/* timfx.css - единый источник стиля timfx.dev для страниц вне сайта.
 * Сайт (Next.js) живёт на Tailwind и motion; здесь тот же язык в чистом CSS:
 * артефакты на data.timfx.dev/a/, промо плагинов, страницы для aescripts.
 * Числа сняты с кода сайта; описание - skills/timfx-style/SKILL.md.
 * Раздаётся как https://data.timfx.dev/a/_/timfx.css. */

/* ---------- 1. Токены: шкала zinc, светлая и тёмная тема ---------- */
:root {
  --tfx-bg: #ffffff;        /* white */
  --tfx-surface: #fafafa;   /* zinc-50 */
  --tfx-chip: #f4f4f5;      /* zinc-100 */
  --tfx-ring: rgba(228, 228, 231, .5); /* zinc-200/50 */
  --tfx-line: #e4e4e7;      /* zinc-200 */
  --tfx-ink: #18181b;       /* zinc-900 */
  --tfx-ink-2: #52525b;     /* zinc-600 */
  --tfx-muted: #71717a;     /* zinc-500 */
  --tfx-inv-bg: #18181b;    /* кнопка: zinc-900 */
  --tfx-inv-ink: #fafafa;
  --tfx-font: "Geist", ui-sans-serif, system-ui, -apple-system, sans-serif;
  --tfx-mono: "Geist Mono", ui-monospace, "SF Mono", Menlo, monospace;
  --tfx-col: 640px;         /* max-w-screen-sm */
  /* Скругления одним набором, чтобы «квадратность» правилась в одном месте.
   * Умолчания - как на сайте (rounded-2xl / xl / lg). Страница или слой,
   * которому нужна другая форма, переопределяет эти четыре переменные
   * и не трогает правила ниже: так живёт web/timkit.css в tim-kit. */
  --tfx-r: 16px;            /* карточка, панель, таблица, pre */
  --tfx-r-sm: 12px;         /* поле ввода, рамка картинки, мелкая плашка */
  --tfx-r-xs: 8px;          /* ссылка-плашка */
  --tfx-r-code: 6px;        /* код в строке */
  --tfx-r-pill: 999px;      /* кнопка, чип, кружок - форма, а не скругление */
  --tfx-ease: cubic-bezier(.2, .7, .2, 1); /* приближение пружины bounce 0 */
  /* Время считается кадрами 30fps: всё движение в окне 300-700 мс, то есть 8 или 16 кадров.
   * Короткое (появление одного элемента) - 8 кадров, длинное (раскрытие, зум) - 16.
   * Каскад появления целиком укладывается в 16 кадров: 8 на элемент + шаг 1.5 кадра. */
  --tfx-dur: .267s;         /* 8 кадров: появление элемента */
  --tfx-long: .533s;        /* 16 кадров: раскрытие картинки, зум, проявление */
  --tfx-step: .05s;         /* 1.5 кадра между элементами каскада */
  color-scheme: light dark;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --tfx-bg: #09090b; --tfx-surface: #18181b; --tfx-chip: #27272a;
    --tfx-ring: rgba(39, 39, 42, .5); --tfx-line: #27272a;
    --tfx-ink: #fafafa; --tfx-ink-2: #a1a1aa; --tfx-muted: #71717a;
    --tfx-inv-bg: #fafafa; --tfx-inv-ink: #09090b;
    --tfx-skel-a: #1f1f22; --tfx-skel-b: rgba(255,255,255,.07);
  }
}
:root[data-theme="dark"] {
  --tfx-bg: #09090b; --tfx-surface: #18181b; --tfx-chip: #27272a;
  --tfx-ring: rgba(39, 39, 42, .5); --tfx-line: #27272a;
  --tfx-ink: #fafafa; --tfx-ink-2: #a1a1aa; --tfx-muted: #71717a;
  --tfx-inv-bg: #fafafa; --tfx-inv-ink: #09090b;
  --tfx-skel-a: #1f1f22; --tfx-skel-b: rgba(255,255,255,.07);
}

/* ---------- 2. Страница и текст ---------- */
html { -webkit-text-size-adjust: 100%; }
body.tfx {
  margin: 0; background: var(--tfx-bg); color: var(--tfx-ink);
  font: 16px/1.65 var(--tfx-font); letter-spacing: -.011em;
  -webkit-font-smoothing: antialiased;
}
.tfx-page { max-width: var(--tfx-col); margin: 0 auto; padding: 80px 16px 96px; overflow-wrap: anywhere; }
.tfx-page > * + * { margin-top: 1.1em; }
.tfx-page h1 { font-size: 24px; line-height: 1.25; font-weight: 700; letter-spacing: -.025em; margin: 0; }
.tfx-page .tfx-lede { color: var(--tfx-ink-2); margin-top: .4em; }
.tfx-page h2 { font-size: 18px; font-weight: 600; letter-spacing: -.02em; margin-top: 64px; }
.tfx-page h3 { font-size: 16px; font-weight: 600; margin-top: 40px; }
.tfx-page p, .tfx-page li { color: var(--tfx-ink-2); }
.tfx-page strong { color: var(--tfx-ink); font-weight: 600; }
.tfx-page ul, .tfx-page ol { padding-left: 1.2em; }
.tfx-page li + li { margin-top: .3em; }
/* Чек-лист из markdown (`- [ ]`): квадрат вместо точки, перенос строки под текст, отмеченное приглушено */
.tfx-page li:has(> input[type="checkbox"]) { list-style: none; margin-left: -1.2em; padding-left: 1.5em; text-indent: -1.5em; }
.tfx-page li > input[type="checkbox"] { width: 1em; margin: 0 .5em 0 0; text-indent: 0; vertical-align: -.1em; accent-color: var(--tfx-ink); }
.tfx-page li:has(> input[type="checkbox"]:checked) { color: var(--tfx-muted); }
.tfx-page hr { border: 0; border-top: 1px solid var(--tfx-line); margin: 48px 0; }
.tfx-page blockquote { margin-left: 0; padding-left: 16px; border-left: 2px solid var(--tfx-line); color: var(--tfx-muted); }
.tfx-page code { font: .9em var(--tfx-mono); background: var(--tfx-chip); padding: .1em .35em; border-radius: var(--tfx-r-code); }
.tfx-page pre { background: var(--tfx-surface); border-radius: var(--tfx-r); padding: 16px; overflow-x: auto;
  box-shadow: inset 0 0 0 1px var(--tfx-ring); font-size: 13.5px; line-height: 1.55; }
.tfx-page pre code { background: none; padding: 0; overflow-wrap: normal; }
.tfx-page pre { overflow-wrap: normal; }
.tfx-page td code { word-break: break-all; }
.tfx-page .tfx-table { overflow-x: auto; border-radius: var(--tfx-r); box-shadow: inset 0 0 0 1px var(--tfx-ring); }
.tfx-page table { border-collapse: collapse; width: 100%; font-size: 14px; }
.tfx-page th, .tfx-page td { text-align: left; padding: 10px 12px; min-width: 7em; border-bottom: 1px solid var(--tfx-line); vertical-align: top; }
.tfx-page th { color: var(--tfx-muted); font-weight: 500; }
.tfx-page tr:last-child td { border-bottom: 0; }
.tfx-page img, .tfx-page video { max-width: 100%; display: block; }
/* Размер картинки из .md не известен до загрузки: без этого рамка-скелет высотой 0 */
.tfx-page .tfx-skel:not(.tfx-ready) { aspect-ratio: 16 / 9; }
.tfx-foot { margin-top: 96px; font-size: 12px; color: var(--tfx-muted); }
.tfx-foot a { color: inherit; }

/* Ссылка - плашка со стрелкой, на hover инвертируется (components/ui/link.tsx) */
.tfx-page a { color: var(--tfx-ink); text-decoration: none; background: var(--tfx-chip);
  border-radius: var(--tfx-r-xs); padding: .05em .4em; transition: background-color .2s, color .2s; }
.tfx-page a[href^="http"]::after { content: " \2197"; font-size: .85em; }
.tfx-page a:hover { background: var(--tfx-inv-bg); color: var(--tfx-inv-ink); }
/* Ссылка-картинка - не плашка: иначе фото получает отступы, фон и стрелку,
 * а на hover белую рамку (odnushki, 28 сентября 2026) */
.tfx-page a:has(> img), .tfx-page a:has(> img):hover { padding: 0; background: none; border-radius: 0; }
.tfx-page a[href]:has(> img)::after { content: none; }

/* Карточка: rounded-2xl, p-1, тонкое кольцо внутрь */
.tfx-card { border-radius: var(--tfx-r); padding: 4px; background: var(--tfx-surface); box-shadow: inset 0 0 0 1px var(--tfx-ring); }

/* Кнопка: одна на экран, rounded-full, zinc-900, на hover инверсия */
.tfx-btn { display: inline-flex; align-items: center; gap: 8px; border-radius: var(--tfx-r-pill);
  padding: 8px 16px; font: 500 14px/1 var(--tfx-font); background: var(--tfx-inv-bg); color: var(--tfx-inv-ink);
  text-decoration: none; box-shadow: inset 0 0 0 1px var(--tfx-inv-bg);
  transition: background-color .2s, color .2s; }
.tfx-btn:hover { background: transparent; color: var(--tfx-ink); }
/* Внутри .tfx-page правило ссылки специфичнее кнопки и сжимало её в плашку */
.tfx-page a.tfx-btn { padding: 8px 16px; background: var(--tfx-inv-bg); color: var(--tfx-inv-ink); border-radius: var(--tfx-r-pill); }
.tfx-page a.tfx-btn:hover { background: transparent; color: var(--tfx-ink); }

/* Значки и чипы. Разметку даёт tools/web/marks.py, касание - /a/_/bdg.js.
 * Цвет - через --c, и закреплён за смыслом словарём, а не выбран под вкус:
 * на всех карточках страницы зелёный значит одно и то же.
 * Значок - свойство предмета: кружок с иконкой, рядом с соседями, подпись
 * в подсказке. Кружки не двигаются никогда: стопка внахлёст раздвигалась
 * при наведении, значок уезжал из-под курсора и подсказка гасла (Костя,
 * 1 октября 2026).
 * Чип - когда и что сделать: пилюля с иконкой и словом, подпись видна сразу. */
.tfx-bdgs { position: relative; display: flex; flex-wrap: wrap; align-items: center; gap: 5px; margin-top: 12px; }
.tfx-bdg { width: 30px; height: 30px; padding: 0; border: 0; border-radius: 50%; background: var(--c); flex: none;
  box-shadow: 0 0 0 2.5px var(--tfx-surface); cursor: pointer; display: grid; place-items: center; transition: box-shadow .2s; }
/* Без transform на самой кнопке: он сделал бы её опорой для подсказки, и подсказка
 * крайнего значка уезжала бы за край экрана - подсказка держится за всю стопку. */
.tfx-bdg:hover, .tfx-bdg.tfx-on { box-shadow: 0 0 0 2.5px var(--tfx-surface), 0 0 0 4.5px var(--c); }
.tfx-bdg svg { width: 16px; height: 16px; fill: none; stroke: #fff; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
.tfx-tip { position: absolute; bottom: calc(100% + 8px); left: 0; width: max-content; max-width: min(260px, 80vw); text-align: left;
  font: 500 12px/1.3 var(--tfx-font); padding: 7px 10px; border-radius: var(--tfx-r-xs); background: var(--tfx-inv-bg); color: var(--tfx-inv-ink);
  opacity: 0; visibility: hidden; transform: translateY(4px); transition: opacity .15s, transform .15s, visibility .15s; z-index: 5; }
.tfx-bdg:hover .tfx-tip, .tfx-bdg.tfx-on .tfx-tip { opacity: 1; visibility: visible; transform: none; }
.tfx-chips { display: inline-flex; flex-wrap: wrap; gap: 4px; vertical-align: 1px; }
.tfx-chip { display: inline-flex; align-items: center; gap: 5px; padding: 3px 9px 3px 7px; border-radius: var(--tfx-r-pill);
  font: 500 12px/1.35 var(--tfx-font); letter-spacing: 0; color: var(--tfx-ink); white-space: nowrap;
  background: color-mix(in srgb, var(--c) 13%, transparent); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--c) 38%, transparent); }
.tfx-chip svg { width: 13px; height: 13px; flex: none; fill: none; stroke: var(--c); stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
/* Вопросы с этапами: блок на вопрос, кружки этапов над текстом, легенда
 * словами один раз над списком. Точки списка тут лишние - блок и есть пункт. */
.tfx-legend { display: flex; flex-wrap: wrap; gap: 6px 14px; margin: 12px 0 4px; font: 500 13px/1.3 var(--tfx-font); color: var(--tfx-muted); }
.tfx-key { display: inline-flex; align-items: center; gap: 6px; }
.tfx-key i { width: 20px; height: 20px; border-radius: 50%; background: var(--c); display: grid; place-items: center; flex: none; }
.tfx-key svg { width: 11px; height: 11px; fill: none; stroke: #fff; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
.tfx-qs { display: grid; gap: 8px; margin-top: 12px; }
.tfx-q { border-radius: var(--tfx-r); padding: 12px 14px; background: var(--tfx-surface); box-shadow: inset 0 0 0 1px var(--tfx-ring); }
.tfx-q .tfx-bdgs { margin: 0 0 8px; }
.tfx-q .tfx-bdg { width: 26px; height: 26px; }
.tfx-q .tfx-bdg svg { width: 14px; height: 14px; }
.tfx-q-text { font-weight: 500; color: var(--tfx-ink); }

/* ---------- 3. Движение: EMBED-SAFE ----------
 * Всё от этой строки до конца файла - только CSS, без JS и внешних файлов.
 * Для aescripts и любой страницы, где режут <script>, этот блок копируется
 * внутрь <style> целиком: `tools/web/artifact.py css` печатает его. */

/* Появление без блюра: блюр в анимации - это filter, он пересчитывается каждый кадр
 * вместе со всем, что под ним (у NeuRenamer вдвое срезал частоту кадров), а на
 * глаз за 8 кадров почти не читается. Убран 18 сентября 2026. Тени - box-shadow.
 * Появление: сдвиг вверх 20px + прозрачность за 8 кадров, шаг 1.5 кадра - шесть блоков
 * укладываются в 16 кадров. Номер элемента - переменная --i (0, 1, 2...). */
@keyframes tfx-in { from { opacity: 0; transform: translateY(20px); } }
.tfx-in { animation: tfx-in var(--tfx-dur, .267s) var(--tfx-ease, cubic-bezier(.2,.7,.2,1)) both;
  animation-delay: calc(var(--i, 0) * var(--tfx-step, .05s)); }

/* Мягкое проявление без сдвига - для крупных картинок и видео в чужой вёрстке */
@keyframes tfx-fade { from { opacity: 0; } }
.tfx-fade { animation: tfx-fade var(--tfx-long, .533s) ease-out both; animation-delay: calc(var(--i, 0) * var(--tfx-step, .05s)); }

/* Зум внутренности: рамка стоит, картинка внутри чуть приближается */
.tfx-zoom { overflow: hidden; border-radius: var(--tfx-r-sm); }
.tfx-zoom > img, .tfx-zoom > video, .tfx-zoom > picture {
  display: block; width: 100%; transition: transform var(--tfx-long, .533s) var(--tfx-ease, cubic-bezier(.2,.7,.2,1)); }
.tfx-zoom:hover > img, .tfx-zoom:hover > video, .tfx-zoom:hover > picture { transform: scale(1.04); }

/* Раскрытие картинки внутри рамки: картинка приходит крупнее рамки (scale 1.18 -> 1) и
 * оседает, рамка всё время её обрезает - получается параллакс, а не «вырастание».
 * Заодно выпрямляется лёгкий 3D-доворот. 16 кадров. Рамка - .tfx-frame, она режет края;
 * запаса 1.18 хватает, чтобы при довороте до 15 град в рамке не открывались углы.
 * Углы - --rx --ry (до 12-15 град) и --rz (прокрут, до 4 град); у соседних картинок
 * разные, чтобы не выглядело штамповкой. Анимация перезапускается при каждом показе
 * (display: none -> block), поэтому годится и для превью по наведению без JS. */
.tfx-frame { overflow: hidden; }
@keyframes tfx-open { from { opacity: 0;
  transform: perspective(700px) rotateX(var(--rx, 8deg)) rotateY(var(--ry, -10deg)) rotateZ(var(--rz, 3deg)) scale(1.18); } }
.tfx-open { animation: tfx-open var(--tfx-long, .533s) var(--tfx-ease, cubic-bezier(.2,.7,.2,1)) both; transform-origin: 50% 50%; }

/* Скелет: пока картинка, svg или видео не пришли, рамка переливается - полоса света
 * проходит слева направо за 1 с (как TextShimmerWave на сайте, duration 1) и ждёт
 * четверть секунды. Полоса - ::before под содержимым и едет transform'ом, то есть
 * на компоновщике: не перерисовывает страницу и не тормозит блюр появления.
 * Без JS картинка просто ложится поверх перелива (aescripts). С skel.js рамка
 * получает .tfx-ready, когда всё внутри загрузилось: перелив гаснет, картинка
 * проявляется за 8 кадров. Цвета - --tfx-skel-a (фон) и --tfx-skel-b (блик);
 * .tfx-skel-dark - для тёмных мокапов. */
.tfx-skel { position: relative; isolation: isolate; overflow: hidden; background-color: var(--tfx-skel-a, #f4f4f5); }
.tfx-skel::before { content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background: linear-gradient(100deg, transparent 20%, var(--tfx-skel-b, rgba(255,255,255,.75)) 50%, transparent 80%);
  transform: translateX(-100%); animation: tfx-skel 1.25s linear infinite; }
@keyframes tfx-skel { 80%, 100% { transform: translateX(100%); } }
.tfx-skel-dark { --tfx-skel-a: #1f1f22; --tfx-skel-b: rgba(255,255,255,.07); }
.tfx-skel.tfx-ready::before { content: none; }
.tfx-js .tfx-skel > :is(img, svg, video, picture) { transition: opacity var(--tfx-dur, .267s) ease-out; }
.tfx-js .tfx-skel:not(.tfx-ready) > :is(img, svg, video, picture) { opacity: 0; }

/* Hover цвета - всегда .2s, без скейла самих кнопок */
.tfx-hover { transition: color .2s, background-color .2s, opacity .2s; }

@media (prefers-reduced-motion: reduce) {
  .tfx-in, .tfx-fade, .tfx-open, .tfx-skel::before { animation: none; }
  .tfx-zoom > img, .tfx-zoom > video, .tfx-zoom > picture { transition: none; }
  .tfx-zoom:hover > img, .tfx-zoom:hover > video, .tfx-zoom:hover > picture { transform: none; }
}
