/* ==========================================================================
   GLOW · components.css — every shared component of the Atelier system
   (design/DESIGN.md = concepts/atelier + mandatory amendments A–G).

   Owner: "components". All values come from tokens.css (colours, type scale, weights, tracking,
   spacing, durations, z-index, --col). base.css already gives: the reset, [hidden] → display:none,
   the global 2 px focus ring, the .sheet--invert token remap and the desktop grayscale filter.
   Nothing here knows the theme except the theme-toggle disc.

   Rules kept everywhere below
   · Strict monochrome: every value is R = G = B (alpha only on the busy track).
   · No text under 11 px. Caps micro-labels: 11 px, 500–600, tracking ≤ .1em.
     Sentences (notes, fine print, toasts, field errors) are sentence case, 13–14 px.
   · Serif weights are fixed: 300 ≥ 48 px, 340 for 24–47 px, 400 for ≤ 23 px.
   · Controls (chips, segments, inputs, checkboxes) have 1 px --line-2 borders (≥ 3:1);
     hairlines (--hair) are separators only.
   · 44 px targets: smaller visible parts grow their hit area with ::after.
   · No backdrop-filter, no blurred shadows, no glows. Motion = transform / opacity / clip-path.

   Markup contracts: see design/notes/components.md and design/gallery.html (every component, both themes).

   Sections
    0  resets                 8  fields                  16 states (empty / error)
    1  primitives             9  checkbox · radio        17 skeleton · status line
    2  header                10  marks: kind ev prio     18 sheet (+ split, invert)
    3  tab bar               11  metric tape             19 toast
    4  page heads            12  tip rows · redaction    20 focus · figures · motion
    5  buttons               13  legend
    6  chips                 14  spec cite dropcap caution panel group
    7  segmented             15  premium gate
   ========================================================================== */


/* ---------- 0. Resets (zero specificity: any component rule wins) -------- */

:where(.tab, .plan, .theme-toggle, .chip, .seg > button, .sheet__close, .btn, .check__x, .toast__action) {
  -webkit-appearance: none; appearance: none;
  margin: 0; padding: 0; border: 0; border-radius: 0;
  background: none; color: inherit;
  font: inherit; letter-spacing: inherit; text-transform: inherit; text-align: inherit;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}
:where(.tips-list, .gate__list, .group ul, .legend, .spec) { margin: 0; padding: 0; list-style: none; }


/* ---------- 1. Primitives ------------------------------------------------- */

/* Icons (icons.js) are inline SVG in currentColor; their size comes from the attributes. */
.ico { display: inline-block; flex: none; vertical-align: middle; overflow: visible; }
.ico--s { width: 14px; height: 14px; }
.ico--m { width: 18px; height: 18px; }

/* Serif numerals: proportional lining figures. Tabular figures on demand: .tnum. */
.num {
  font-family: var(--serif);
  font-weight: var(--fw-serif-display);
  font-variant-numeric: lining-nums proportional-nums;
}
.tnum { font-variant-numeric: lining-nums tabular-nums; }

.body { font: var(--fw-regular) var(--fs-body)/var(--lh-body) var(--sans); color: var(--ink-2); }

/* Caps micro-label; <b> inside = the emphasised value («13 из 14»). */
.label {
  font: var(--fw-medium) var(--fs-micro)/1.25 var(--sans);
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--ink-3);
}
.label b, .label strong { font-weight: var(--fw-semibold); color: var(--ink); }

/* Fine print: sentence case, never caps (A1). */
.fine { font: var(--fw-regular) var(--fs-note)/1.5 var(--sans); color: var(--ink-3); text-wrap: pretty; }

/* Roman numerals of benefit lists (I, II, III, IV). */
.rn { font: var(--fw-serif-text) 15px/1 var(--serif); letter-spacing: .04em; color: var(--ink-3); }


/* ---------- 2. Header ----------------------------------------------------- */
/* <header class="hdr [hdr--bare]"> button.plan#premiumBadge[data-plan] · .wordmark · button.theme-toggle
   Fixed and opaque, one hairline under it; on wide windows the parts stay inside --col. */

.hdr {
  position: fixed; z-index: var(--z-chrome);
  top: 0; left: 0; right: 0;
  height: calc(var(--hdr-h) + var(--sat));
  padding-top: var(--sat);
  /* Symmetric padding so the 1fr · auto · 1fr grid puts the wordmark on the screen axis; the toggle
     still ends 10 px from the edge through its negative margin. */
  padding-left: max(var(--gutter), (100% - var(--col)) / 2 + var(--gutter));
  padding-right: max(var(--gutter), (100% - var(--col)) / 2 + var(--gutter));
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  background: var(--bg);
  color: var(--ink);
  border-bottom: var(--hair) solid var(--rule-2);
}
.hdr--bare { border-bottom-color: transparent; }

.wordmark {
  justify-self: center;
  margin: 0;
  font: var(--fw-serif-text) var(--fs-wordmark)/1 var(--serif);
  letter-spacing: var(--tr-wordmark);
  padding-left: var(--tr-wordmark);       /* re-centre the trailing tracking */
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--ink);
  -webkit-user-select: none; user-select: none;
}
.hdr--bare .wordmark { visibility: hidden; }

/* Plan badge: FREE = outline, PREMIUM = inverted (no emoji). 24 px visible, 44 px target. */
.plan,
#premiumBadge {
  justify-self: start;
  position: relative;
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  height: 24px;
  padding: 2px 8px 0;                       /* caps sit 1 px high at line-height 1: optical centre */
  border: var(--stroke) solid var(--ink);
  background: transparent;
  color: var(--ink);
  font: var(--fw-semibold) var(--fs-micro)/1 var(--sans);
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  white-space: nowrap;
  transition:
    background-color var(--d-state) var(--e-std),
    color var(--d-state) var(--e-std),
    transform var(--d-press) var(--e-std);
}
.plan::after,
#premiumBadge::after { content: ""; position: absolute; inset: -11px -10px; }
.plan:active,
#premiumBadge:active { transform: scale(.96); }
:is(.plan, #premiumBadge):is([data-plan="premium"], .is-premium, .premium) {   /* still a button → Профиль */
  background: var(--ink);
  color: var(--bg);
}
:is(.plan, #premiumBadge) svg { width: 11px; height: 11px; }

/* Theme toggle: the half-filled disc turns 180° with the theme. */
.theme-toggle {
  justify-self: end;
  position: relative;
  margin-right: calc(10px - var(--gutter));  /* box ends 10 px from the edge, as before */
  width: var(--target); height: var(--target);
  display: grid; place-items: center;
  color: var(--ink);
}
.theme-toggle svg {
  width: 20px; height: 20px;
  transition: transform 640ms var(--e-io);
}
[data-theme="dark"] .theme-toggle svg { transform: rotate(180deg); }
.theme-toggle:active svg { scale: .9; }

/* Outlined caps tag «PREMIUM» (kicker end, row meta, gate). */
.tag-premium {
  display: inline-flex; align-items: center;
  height: 20px;
  padding: 0 6px;
  border: var(--stroke) solid currentColor;
  color: var(--ink);
  font: var(--fw-semibold) var(--fs-micro)/1 var(--sans);
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  white-space: nowrap;
}


/* ---------- 3. Tab bar ---------------------------------------------------- */
/* <nav class="tabbar"> 5 × <button class="tab" data-tab aria-current="page">svg + span.tab__label</button>
   Active = filled icon (icons.js fill:true, or --ico-fill) + ink label 600 + 24×2 marker on ::before.
   The runtime slides that marker between tabs with WAAPI, so its transform has no CSS transition. */

.tabbar {
  position: fixed; z-index: var(--z-chrome);
  left: 0; right: 0; bottom: 0;
  height: calc(var(--tab-h) + var(--sab));
  padding-bottom: var(--sab);
  padding-inline: max(0px, (100% - var(--col)) / 2);
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  background: var(--bg);
  border-top: var(--hair) solid var(--rule-2);
}
.tabbar::before {          /* content dissolves under the bar instead of being sliced — not a blur */
  content: "";
  position: absolute; left: 0; right: 0; bottom: 100%;
  height: var(--fade-h);
  /* The fade covers the border edge (Chromium rounds a .5 px border up to 1 px, WebKit keeps it), so it
     redraws the top hairline itself as its last --hair: the rail under the 24×2 marker stays visible and
     equally thin in both engines. A 1 px tile with a hard stop — Chromium drops a .5 px tile. */
  background:
    linear-gradient(to bottom, transparent calc(100% - var(--hair)), var(--rule-2) 0) left bottom / 100% 1px no-repeat,
    linear-gradient(to bottom, transparent, var(--bg));
  pointer-events: none;
}
.tab {
  position: relative;
  min-width: 0;
  height: var(--tab-h);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 7px;
  color: var(--ink-3);
  font: var(--fw-medium) var(--fs-micro)/1 var(--sans);
  letter-spacing: .06em;
  text-transform: uppercase;
  white-space: nowrap;
  transition: color var(--d-fade) var(--e-std);
}
.tab svg { width: 22px; height: 22px; flex: none; transition: scale var(--d-press) var(--e-std); }
.tab__label { display: block; max-width: 100%; overflow: hidden; }
.tab:is([aria-current="page"], .is-active, [aria-selected="true"]) {
  color: var(--ink);
  font-weight: var(--fw-semibold);
  --ico-fill: currentColor;
  --ico-knock: var(--bg);
}
.tab:is([aria-current="page"], .is-active, [aria-selected="true"])::before {
  content: "";
  position: absolute; top: -1px; left: 50%;
  width: 24px; height: var(--bar);
  margin-left: -12px;
  background: var(--ink);
}
.tab:active svg { scale: .9; }
@media (hover: hover) {
  .tab:not([aria-current="page"]):not(.is-active):hover { color: var(--ink-2); }
}


/* ---------- 4. Page heads ------------------------------------------------- */
/* .page-head > p.kicker(.no · i.dash · span · span.end) + h1.page-title (may hold <em>).
   .sec-head > h2.sec-title (<em>, <sup>*</sup>) + .sec-meta (span.num<small>/8</small> + span.label | a button). */

.page-head { padding: 20px var(--gutter) 0; }

.kicker {
  display: flex; align-items: center; flex-wrap: wrap;
  column-gap: 10px; row-gap: 6px;
  min-height: 16px;
  margin: 0;
  font: var(--fw-medium) var(--fs-micro)/1.2 var(--sans);
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--ink-3);
}
.kicker .no {
  font: var(--fw-serif-text) 14px/1 var(--serif);
  letter-spacing: .01em;
  text-transform: none;
  color: var(--ink);
  white-space: nowrap;
}
.kicker .dash {
  flex: none;
  width: 22px; height: 0;
  border-top: var(--hair) solid var(--ink-3);
}
/* .end has the kicker's own line height: a taller child (tag 20 px, a 28 px goal button) hangs over
   it symmetrically instead of pushing the kicker — and the whole screen — down. */
.kicker .end { margin-left: auto; display: inline-flex; align-items: center; gap: 8px; height: 16px; }
.kicker b, .kicker strong { font-weight: var(--fw-semibold); color: var(--ink); }

.page-title {
  margin: 12px 0 0;
  font: var(--fw-serif-display) var(--fs-title-xl)/.92 var(--serif);
  letter-spacing: var(--tr-display);
  color: var(--ink);
  text-wrap: balance;
  overflow-wrap: break-word;
}
.page-title em { font-style: italic; }
@media (max-width: 383.98px) { .page-title { font-weight: var(--fw-serif-title); } }   /* < 48 px → 340 */

.sec-head {
  display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between;
  column-gap: 16px; row-gap: 8px;
  padding: 22px var(--gutter) 12px;
}
.sec-title {
  min-width: 0;
  margin: 0;
  font: var(--fw-serif-title) var(--fs-title-l)/1 var(--serif);
  letter-spacing: -.025em;
  color: var(--ink);
  text-wrap: balance;
}
.sec-title em { font-style: italic; }
.sec-title sup {
  font-size: .5em; line-height: 0;
  vertical-align: .9em;
  margin-left: 2px;
  color: var(--ink-2);
}
.sec-meta {
  display: flex; align-items: baseline; gap: 8px;
  margin: 0 0 0 auto;
  padding-bottom: 2px;
  white-space: nowrap;
}
.sec-meta .num {
  font-weight: var(--fw-serif-title);
  font-size: 28px;
  line-height: .9;
  letter-spacing: -.01em;
  color: var(--ink);
}
.sec-meta .num small { font-size: .6em; color: var(--ink-3); margin-left: 1px; }
.sec-meta .label { line-height: 1; }
:where(.sheet, .gate, .panel, .group, .state) .sec-head,
.sec-head--flush { padding-left: 0; padding-right: 0; }


/* ---------- 5. Buttons ---------------------------------------------------- */
/* .btn + --primary | --secondary | --ghost | --small, --block. Icon svg + <span>label</span>.
   Long labels wrap (balanced) inside the same 54 px minimum. Busy: .is-busy or [aria-busy="true"]. */

.btn {
  position: relative;
  display: inline-flex; align-items: center; justify-content: center; gap: 12px;
  min-height: var(--btn-h);
  padding: 12px 22px;
  border: var(--stroke) solid transparent;
  background: transparent;
  color: var(--ink);
  font: var(--fw-medium) var(--fs-button)/1.3 var(--sans);
  letter-spacing: var(--tr-button);
  text-transform: uppercase;
  text-align: center;
  text-decoration: none;
  text-wrap: balance;
  -webkit-user-select: none; user-select: none;
  transition:
    transform var(--d-press) var(--e-std),
    background-color var(--d-press) var(--e-std),
    border-color var(--d-press) var(--e-std),
    color var(--d-press) var(--e-std),
    text-decoration-color var(--d-press) var(--e-std);
}
.btn > svg { flex: none; }
.btn > span { min-width: 0; }
.btn .star { margin-left: -6px; }           /* «250 ★»: the star stays with its figure */
.btn--block { display: flex; width: 100%; }

.btn--primary { background: var(--ink); border-color: var(--ink); color: var(--bg); }
.btn--primary:active { background: var(--ink-2); border-color: var(--ink-2); }

.btn--secondary { border-color: var(--ink); }
.btn--secondary:active { background: var(--bg-2); }

.btn--ghost {
  min-height: var(--target);
  padding: 8px 2px;
  gap: 8px;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 6px;
}
.btn--ghost:active { text-decoration-color: transparent; }

.btn--small {
  min-height: var(--btn-h-s);
  padding: 6px 12px;
  gap: 8px;
  border-color: var(--ink);
  font-size: var(--fs-micro);
  letter-spacing: var(--tr-caps);
}
.btn--small::after { content: ""; position: absolute; inset: -6px -2px; }
.btn--small:active { background: var(--bg-2); }
.btn--small.btn--primary:active { background: var(--ink-2); }
.btn--small svg { width: 12px; height: 12px; }

.btn:active { transform: scale(.985); }

.btn:disabled,
.btn[aria-disabled="true"] {
  background: var(--bg-3);
  border-color: var(--bg-3);
  color: var(--ink-3);
  cursor: default;
  transform: none;
}
.btn--ghost:is(:disabled, [aria-disabled="true"]) { background: none; border-color: transparent; }
.btn--secondary:is(:disabled, [aria-disabled="true"]) { background: none; border-color: var(--line-2); }

/* Busy keeps its own look (a working button is not a disabled one) and runs a 1 px bar. */
.btn:is(.is-busy, [aria-busy="true"]) { pointer-events: none; transform: none; }
.btn--primary:is(.is-busy, [aria-busy="true"]):disabled { background: var(--ink); border-color: var(--ink); color: var(--bg); }
.btn--secondary:is(.is-busy, [aria-busy="true"]):disabled { background: none; border-color: var(--ink); color: var(--ink); }
/* ::before = the grey track, ::after = the ink segment. The segment moves by transform only
   (composited): it grows out of the left end, slides, and shrinks into the right end, so it never
   leaves the track and needs no clipping. */
.btn:is(.is-busy, [aria-busy="true"])::before,
.btn:is(.is-busy, [aria-busy="true"])::after {
  content: "";
  position: absolute; top: auto; left: 22px; right: 22px; bottom: 11px;
  height: 1px;
  pointer-events: none;
}
.btn:is(.is-busy, [aria-busy="true"])::before { background: rgba(128, 128, 128, .42); }
.btn:is(.is-busy, [aria-busy="true"])::after {
  background: currentColor;
  transform-origin: 0 50%;
  transform: scaleX(0);
  animation: btn-busy 1.4s linear infinite;
}
.btn--small:is(.is-busy, [aria-busy="true"])::before,
.btn--small:is(.is-busy, [aria-busy="true"])::after { left: 12px; right: 12px; bottom: 4px; }
.btn--ghost:is(.is-busy, [aria-busy="true"])::before,
.btn--ghost:is(.is-busy, [aria-busy="true"])::after { left: 2px; right: 2px; bottom: 4px; }
@keyframes btn-busy {
  0%   { transform: translateX(0)    scaleX(0); }
  22%  { transform: translateX(0)    scaleX(.3); }
  78%  { transform: translateX(70%)  scaleX(.3); }
  100% { transform: translateX(100%) scaleX(0); }
}

/* Done (the runtime's quiet confirmation after «Добавить в рутину» → «✓ В рутине»):
   not a greyed-out disabled block — an outline with ink text, no longer inviting a tap. */
.btn.is-done,
.btn.is-done:is(:disabled, [aria-disabled="true"]) {
  background: transparent;
  border-color: var(--line-2);
  color: var(--ink);
  cursor: default;
  transform: none;
}
.btn--ghost.is-done { border-color: transparent; text-decoration: none; }

@media (hover: hover) {
  .btn--secondary:not(:disabled):hover,
  .btn--small:not(.btn--primary):not(:disabled):hover { background: var(--bg-2); }
  .btn--ghost:not(:disabled):hover { text-decoration-thickness: 2px; }
}


/* ---------- 6. Chips ------------------------------------------------------ */
/* Filter row .chips (one line, scrolls, cut by the screen edge) > button.chip[aria-pressed]
   (+ <span class="chip__n">42</span> count). Divider between kinds and categories:
   <span class="chips__div" aria-hidden="true"></span>. Multi-select: .chips--wrap, the chosen chip shows
   svg.tick (icons.js check 12) — or a drawn tick when the chip has no svg. */

.chips {
  display: flex; align-items: center; gap: 6px;
  margin: 0;
  padding: 5px var(--gutter);               /* room for the 44 px hit areas inside the scroller */
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-padding-inline: var(--gutter);
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.chips::-webkit-scrollbar { display: none; }

.chip {
  position: relative;
  flex: none;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  height: var(--chip-h);
  padding: 0 14px;
  border: var(--stroke) solid var(--line-2);
  background: transparent;
  color: var(--ink-2);
  font: var(--fw-medium) var(--fs-micro)/1 var(--sans);
  letter-spacing: .08em;
  text-transform: uppercase;
  white-space: nowrap;
  transition:
    background-color var(--d-state) var(--e-std),
    border-color var(--d-state) var(--e-std),
    color var(--d-state) var(--e-std);
}
.chip::after { content: ""; position: absolute; inset: -5px -1px; }
.chip:active { background: var(--bg-2); }
.chip:is([aria-pressed="true"], [aria-checked="true"], [aria-selected="true"], .is-on) {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--bg);
}
.chip:is([aria-pressed="true"], [aria-checked="true"], [aria-selected="true"], .is-on):active {
  background: var(--ink-2);
  border-color: var(--ink-2);
}
.chip__n {
  letter-spacing: .02em;
  color: var(--ink-3);
  font-variant-numeric: lining-nums tabular-nums;
  transition: color var(--d-state) var(--e-std), opacity var(--d-state) var(--e-std);
}
.chip:is([aria-pressed="true"], [aria-checked="true"], [aria-selected="true"], .is-on) .chip__n { color: inherit; opacity: .66; }

.chips__div {
  flex: none;
  align-self: center;
  width: 1px; height: 22px;
  margin: 0 5px;
  background: var(--ink-4);
}

.chips--wrap { flex-wrap: wrap; gap: 8px; padding: 0; overflow: visible; }
/* The tick takes the room of the side padding, so a chosen chip keeps its width and a tap never
   re-flows the rows under the finger: svg tick 12 − 2 + 8 gap = 18 px (23 → 14 each side);
   the drawn tick 9 − 2 + 8 = 15 px (21.5 → 14). */
.chips--wrap .chip { padding: 0 23px; }
.chips--wrap .chip:not(:has(svg)) { padding: 0 21.5px; }
.chips--wrap .chip:is([aria-pressed="true"], [aria-checked="true"], .is-on) { padding: 0 14px; }
.chip :is(.tick, .chip__tick) { display: none; flex: none; width: 12px; height: 12px; margin-left: -2px; }
.chip:is([aria-pressed="true"], [aria-checked="true"], .is-on) :is(.tick, .chip__tick) { display: inline-block; }
.chips--wrap .chip:is([aria-pressed="true"], [aria-checked="true"], .is-on):not(:has(svg))::before {
  content: "";
  flex: none;
  width: 9px; height: 5px;
  margin: -3px 0 0 -2px;
  border-left: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(-45deg);
}
@media (hover: hover) {
  .chip:not([aria-pressed="true"]):not(.is-on):hover { border-color: var(--ink); color: var(--ink); }
}


/* ---------- 7. Segmented control ----------------------------------------- */
/* .seg > button[aria-pressed] (or label > input[type=radio] + span). Equal cells, 44 px. */

.seg {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  border: var(--stroke) solid var(--line-2);
}
.seg > button,
.seg > label,
.seg > .seg__opt {
  position: relative;
  display: flex; align-items: center; justify-content: center;
  min-width: 0;
  min-height: var(--control-h);
  padding: 6px 4px;
  background: transparent;
  color: var(--ink-2);
  font: var(--fw-medium) var(--fs-micro)/1.2 var(--sans);
  letter-spacing: .07em;
  text-transform: uppercase;
  text-align: center;
  cursor: pointer;
  transition: background-color var(--d-state) var(--e-std), color var(--d-state) var(--e-std);
}
.seg > * + * { border-left: var(--stroke) solid var(--line-2); }
.seg > :active { background: var(--bg-2); }
.seg > :is([aria-pressed="true"], [aria-checked="true"], .is-on),
.seg > label:has(input:checked) {
  z-index: 1;
  background: var(--ink);
  border-left-color: var(--ink);
  color: var(--bg);
  box-shadow: 0 0 0 var(--stroke) var(--ink);     /* covers the frame and the dividers beside it */
}
.seg input { position: absolute; opacity: 0; width: 1px; height: 1px; pointer-events: none; }
.seg > label:has(input:focus-visible) { outline: 2px solid var(--ink); outline-offset: 2px; z-index: 2; }
@media (hover: hover) {
  .seg > :not([aria-pressed="true"]):not(.is-on):hover { color: var(--ink); }
}


/* ---------- 8. Fields ----------------------------------------------------- */
/* label.fld[.fld--num|.fld--select][.is-error] > span.fld__label + span.fld__box(input|select
   [+ svg.fld__chev] [+ span.fld__unit]) [+ .fld__msg(svg + span)]. Underline only — no boxes.
   Numbers: type=text inputmode=decimal («79,6» and «79.6»), serif figures.
   .field: a bare underlined input (routine «Свой пункт…»). */

.fld { position: relative; display: block; min-width: 0; padding-top: 18px; }
.fld__label {
  display: block;
  font: var(--fw-medium) var(--fs-micro)/1.25 var(--sans);
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--ink-3);
}
.fld__box {
  position: relative;
  display: flex; align-items: center; gap: 10px;
  border-bottom: var(--stroke) solid var(--line-2);
  transition: border-color var(--d-row) var(--e-std), box-shadow var(--d-row) var(--e-std);
}
.fld__box:focus-within,
.fld.is-focus .fld__box {
  border-bottom-color: var(--ink);
  box-shadow: 0 1px 0 var(--ink);           /* reads as 2 px without moving the layout */
}
.fld input,
.fld select,
.fld textarea {
  flex: 1;
  width: 100%; min-width: 0;
  height: var(--field-h);
  margin: 0; padding: 0;
  border: 0; border-radius: 0;
  background: transparent;
  color: var(--ink);
  font: var(--fw-regular) var(--fs-input)/1.2 var(--sans);   /* 16 px: iOS does not zoom on focus */
  outline: none;
  -webkit-appearance: none; appearance: none;
  caret-color: var(--ink);
  text-overflow: ellipsis;
}
.fld textarea { height: auto; min-height: 96px; padding: 12px 0; resize: vertical; line-height: 1.45; }
.fld--num input {
  height: 54px;
  font: var(--fw-serif-title) var(--fs-num-m)/1 var(--serif);
  letter-spacing: -.01em;
}
.fld input::placeholder,
.fld textarea::placeholder { color: var(--ink-3); opacity: 1; }
.fld--num input::placeholder { font: italic var(--fw-serif-title) 25px/1 var(--serif); letter-spacing: 0; }

.fld__unit {
  flex: none;
  font: var(--fw-medium) var(--fs-micro)/1 var(--sans);
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--ink-3);
  white-space: nowrap;
}

/* select: native list + chevron (svg.fld__chev from icons.js; a drawn one when it is absent) */
.fld select { padding-right: 30px; text-overflow: ellipsis; }
.fld select option { background: var(--bg); color: var(--ink); }
.fld__chev {
  position: absolute; right: 0; top: 50%;
  width: 16px; height: 16px;
  margin-top: -8px;
  color: var(--ink-2);
  pointer-events: none;
}
.fld__box:has(> select):not(:has(> .fld__chev))::after {
  content: "";
  position: absolute; right: 0; top: 50%;
  width: 16px; height: 16px; margin-top: -8px;
  background: var(--ink-2);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.4' stroke-linecap='square'%3E%3Cpath d='M6 9.5l6 6 6-6'/%3E%3C/svg%3E") center / contain no-repeat;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.4' stroke-linecap='square'%3E%3Cpath d='M6 9.5l6 6 6-6'/%3E%3C/svg%3E") center / contain no-repeat;
  pointer-events: none;
}

/* error: shown by thickness and words, never by colour */
.fld.is-error .fld__box,
.fld__box:has([aria-invalid="true"]) {
  border-bottom: var(--bar) solid var(--ink);
  margin-bottom: -1px;
  box-shadow: none;
}
.fld__msg {
  display: flex; align-items: flex-start; gap: 8px;
  margin: 9px 0 0;
  font: var(--fw-regular) var(--fs-note)/1.4 var(--sans);
  color: var(--ink);
  letter-spacing: 0;
  text-transform: none;
}
.fld__msg svg { flex: none; width: 14px; height: 14px; margin-top: 2px; }
.fld__hint { margin: 9px 0 0; font: var(--fw-regular) var(--fs-note)/1.45 var(--sans); color: var(--ink-3); }

.field {
  display: block;
  width: 100%; min-width: 0;
  height: 48px;
  margin: 0; padding: 0 2px;
  border: 0; border-bottom: var(--stroke) solid var(--line-2); border-radius: 0;
  background: transparent;
  color: var(--ink);
  font: var(--fw-regular) var(--fs-input)/1.2 var(--sans);
  outline: none;
  text-overflow: ellipsis;
  -webkit-appearance: none; appearance: none;
  transition: border-color var(--d-row) var(--e-std), box-shadow var(--d-row) var(--e-std);
}
.field:focus { border-bottom-color: var(--ink); box-shadow: 0 1px 0 var(--ink); }
.field::placeholder { color: var(--ink-3); opacity: 1; }


/* ---------- 9. Checkbox · radio · option row ----------------------------- */
/* Checkbox row — native input, drawn box, the whole row is the target:
     <label class="check"><input type="checkbox"><span class="box"></span><span class="check__t">…</span></label>
   or a list item with its own delete button:
     <li class="check [is-done]"><label class="check__l"><input …><span class="box"></span>
       <span class="check__t">…</span></label><button class="check__x" aria-label="Убрать">svg</button></li>
   Radio: input[type=radio] + span.radio anywhere after it in the same label. */

.check {
  position: relative;
  display: flex; align-items: center;
  min-height: 48px;
  border-bottom: var(--hair) solid var(--rule-2);
}
label.check,
.check__l {
  flex: 1;
  display: flex; align-items: center; gap: 14px;
  min-width: 0;
  min-height: 48px;
  cursor: pointer;
}
.check input[type="checkbox"],
.option input {
  position: absolute;
  opacity: 0;
  width: 1px; height: 1px;
  margin: 0;
  pointer-events: none;
}
.box {
  position: relative;
  flex: none;
  width: 22px; height: 22px;
  display: grid; place-items: center;
  border: var(--stroke) solid var(--line-2);
  background: transparent;
  color: var(--bg);
  transition:
    background-color var(--d-fade) var(--e-std),
    border-color var(--d-fade) var(--e-std),
    transform var(--d-press) var(--e-std);
}
.box::after {                       /* the drawn tick, drawn left → right when checked */
  content: "";
  width: 11px; height: 6px;
  margin-top: -3px;
  border-left: 1.6px solid currentColor;
  border-bottom: 1.6px solid currentColor;
  transform: rotate(-45deg);
  clip-path: inset(0 100% 0 0);
  transition: clip-path 240ms var(--e-out) 40ms;
}
.box:has(svg)::after { display: none; }
.box svg { width: 14px; height: 14px; opacity: 0; transition: opacity var(--d-fade) var(--e-std); }
:is(input:checked + .box, .box.is-on, [aria-checked="true"] > .box) { background: var(--ink); border-color: var(--ink); }
:is(input:checked + .box, .box.is-on, [aria-checked="true"] > .box)::after { clip-path: inset(0 0 0 0); }
:is(input:checked + .box, .box.is-on, [aria-checked="true"] > .box) svg { opacity: 1; }
label:active > .box { transform: scale(.92); }

.check__t {
  flex: 1; min-width: 0;
  padding: 12px 0;
  font: var(--fw-regular) var(--fs-body)/1.4 var(--sans);
  color: var(--ink);
  overflow-wrap: break-word;
  transition: color var(--d-fade) var(--e-std);
}
/* Done = muted text + a strike. The strike is an underline raised to strike height: an underline is
   painted UNDER the glyphs (line-through is painted over them), so the range dash in «7–9 ч» or
   «1,6–2,2 г/кг» stays visible on the lighter line instead of merging into it. */
.check:is(.is-done, :has(input:checked)) .check__t {
  color: var(--ink-3);
  text-decoration-line: underline;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--ink-4);
  text-underline-offset: -.38em;
  -webkit-text-decoration-skip: none;
  text-decoration-skip-ink: none;
}
.check__x {
  flex: none;
  width: var(--target); height: var(--target);
  margin-right: -12px;
  display: grid; place-items: center;
  color: var(--ink-3);
}
.check__x svg { width: 16px; height: 16px; }
.check__x:active { color: var(--ink); }

.radio {
  position: relative;
  flex: none;
  width: 22px; height: 22px;
  border-radius: var(--radius-round);        /* a circle is the radio's own form */
  border: var(--stroke) solid var(--line-2);
  display: grid; place-items: center;
  transition: border-color var(--d-state) var(--e-std);
}
.radio::after {
  content: "";
  width: 10px; height: 10px;
  border-radius: var(--radius-round);
  background: var(--ink);
  transform: scale(0);
  transition: transform var(--d-radio) var(--e-out);
}
:is(input:checked ~ .radio, .radio.is-on, [aria-checked="true"] .radio) { border-color: var(--ink); }
:is(input:checked ~ .radio, .radio.is-on, [aria-checked="true"] .radio)::after { transform: scale(1); }

input:focus-visible + .box,
input:focus-visible ~ .radio { outline: 2px solid var(--ink); outline-offset: 2px; }

/* Option row (answers): numeral · serif answer · radio; the chosen answer turns ink + italic. */
.option {
  position: relative;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  min-height: 56px;
  border-top: var(--hair) solid var(--rule-2);
  cursor: pointer;
}
.option:last-of-type { border-bottom: var(--hair) solid var(--rule-2); }
.option__l { display: flex; align-items: baseline; min-width: 0; }
.option__no { flex: none; width: 30px; font: var(--fw-serif-text) 13px/1 var(--serif); color: var(--ink-3); }
.option__text {
  font: var(--fw-serif-text) var(--fs-item-l)/1.15 var(--serif);
  letter-spacing: var(--tr-item);
  color: var(--ink-2);
  transition: color var(--d-state) var(--e-std);
}
.option:has(input:checked) .option__text { color: var(--ink); font-style: italic; }


/* ---------- 10. Marks: one form per dimension ---------------------------- */
/* kind = circle (soft ○ · hard ● · myth ⊘) · evidence = squares (A ■■■ · B ■■□ · C ■□□) ·
   priority = diamond (high ◆ · medium half · low ◇) · direction tag «↑ ВЫШЕ ОРИЕНТИРА». */

.kind {
  position: relative;
  display: inline-block;
  flex: none;
  width: 7px; height: 7px;
  border-radius: var(--radius-round);
  border: var(--stroke) solid currentColor;
  vertical-align: middle;
}
.kind--hard { background: currentColor; }
.kind--myth::after {
  content: "";
  position: absolute; left: 50%; top: 50%;
  width: 1px; height: 11px;
  margin: -5.5px 0 0 -.5px;
  background: currentColor;
  transform: rotate(45deg);
}

.ev {
  display: inline-flex; align-items: center; gap: 3px;
  color: var(--ink);
  white-space: nowrap;
  vertical-align: middle;
}
.ev b {
  margin-right: 5px;
  font: var(--fw-semibold) var(--fs-micro)/1 var(--sans);
  letter-spacing: .02em;
}
.ev i { flex: none; width: 6px; height: 6px; border: var(--stroke) solid currentColor; }
.ev[data-ev="A"] i,
.ev[data-ev="B"] i:nth-of-type(-n+2),
.ev[data-ev="C"] i:first-of-type { background: currentColor; }
.ev__label { font: var(--fw-regular) var(--fs-body-s)/1.35 var(--sans); color: var(--ink); }

.prio {
  display: inline-flex; align-items: center; gap: 8px;
  font: var(--fw-medium) var(--fs-micro)/1 var(--sans);
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--ink);
  white-space: nowrap;
}
.prio i { flex: none; width: 8px; height: 8px; border: var(--stroke) solid currentColor; transform: rotate(45deg); }
.prio[data-prio="high"] i { background: currentColor; }
.prio[data-prio="medium"] i { background: linear-gradient(to top right, currentColor 50%, transparent 50%); }
.prio[data-prio="low"] { color: var(--ink-2); }

/* Out-of-range tag — ui.tape appends <p class="tag-dir" data-dir="high|low">↑ ВЫШЕ ОРИЕНТИРА</p>.
   The arrows are Geist glyphs: the tag stays in the sans. */
.tag-dir {
  display: flex; align-items: center; gap: 6px;
  width: max-content; max-width: 100%;
  height: 22px;
  margin: 0;
  padding: 0 7px;
  border: var(--stroke) solid var(--ink);
  color: var(--ink);
  font: var(--fw-semibold) var(--fs-micro)/1 var(--sans);
  letter-spacing: .08em;
  text-transform: uppercase;
  white-space: nowrap;
}
.tag-dir svg { flex: none; width: 11px; height: 11px; }
.tape + .tag-dir { margin: 10px 0 0 var(--col-no); }


/* ---------- 11. Metric tape — the tailor's ruler -------------------------- */
/* <div class="tape [is-out]" data-state="in|low|high" style="--lo:%;--hi:%;--v:%">
     span.tape__range · span.tape__mark[.tape__mark--out] · span.tape__lab(left:%) × 2</div>
   Filled caret = in range; hollow caret = out of range (+ .tag-dir and the note sentence under it). */

.tape {
  --tick: var(--ink-4);
  position: relative;
  height: 28px;
  margin: 8px 0 0 var(--col-no);
}
.tape::before {            /* axis + 20 ruler ticks, every 5th taller, an end stop */
  content: "";
  position: absolute; left: 0; right: 0; top: 7px;
  height: 7px;
  background:
    linear-gradient(var(--tick), var(--tick)) 0 0 / 100% var(--hair) no-repeat,
    linear-gradient(to right, var(--tick) var(--hair), transparent var(--hair)) 0 0 / 5% 3px repeat-x,
    linear-gradient(to right, var(--tick) 1px, transparent 1px) 0 0 / 25% 6px repeat-x;
  border-right: 1px solid var(--tick);
}
.tape__range {
  position: absolute; top: 6px;
  left: var(--lo); width: calc(var(--hi) - var(--lo));
  height: var(--range);
  background: var(--ink);
}
.tape__range::before,
.tape__range::after {
  content: "";
  position: absolute; top: -3px;
  width: 1px; height: 9px;
  background: var(--ink);
}
.tape__range::before { left: 0; }
.tape__range::after { right: 0; }
.tape__mark {
  position: absolute; top: -3px; left: var(--v);
  width: 0; height: 0;
}
.tape__mark::before {        /* the caret */
  content: "";
  position: absolute; left: -5px; top: 0;
  width: 10px; height: 8px;
  background: var(--ink);
  clip-path: polygon(0 0, 100% 0, 50% 100%);
}
.tape__mark::after {         /* the needle */
  content: "";
  position: absolute; left: 0; top: 8px;
  width: 1px; height: 11px;
  background: var(--ink);
}
:is(.tape.is-out, .tape[data-state="low"], .tape[data-state="high"]) .tape__mark::before,
.tape__mark--out::before {
  clip-path: path(evenodd, "M0 0H10L5 8Z M2.25 1.05H7.75L5 5.45Z");     /* hollow */
}
.tape__lab {
  position: absolute; top: 17px;
  transform: translateX(-50%);
  font: var(--fw-regular) var(--fs-micro)/1 var(--sans);
  color: var(--ink-3);
  font-variant-numeric: lining-nums tabular-nums;
  white-space: nowrap;
}


/* ---------- 12. Tip rows · redaction ------------------------------------- */
/* .tips-list > div.tip[role=button][data-tip | data-locked] (ui.tipRow):
     .tip__no · .tip__body(.tip__title [.redact] .tip__meta) · .tip__side(svg.tip__chev | svg.tip__lock, .ev)
   The chevron says the row opens; a locked row keeps title, kind and evidence, its text is redacted. */

.tips-list { border-bottom: var(--hair) solid var(--rule-2); }

.tip {
  position: relative;
  display: grid;
  grid-template-columns: var(--col-no) minmax(0, 1fr) auto;
  column-gap: 6px;
  width: 100%;
  padding: 13px var(--gutter) 14px;
  border-top: var(--hair) solid var(--rule-2);
  background: transparent;
  color: var(--ink);
  text-align: left;
  text-decoration: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background-color var(--d-row) var(--e-std);
}
.tip:active { background: var(--bg-2); }
.tip:focus-visible { outline: 2px solid var(--ink); outline-offset: -4px; }
.tip__no {
  font: var(--fw-serif-text) 13px/23px var(--serif);
  color: var(--ink-3);
  font-variant-numeric: lining-nums tabular-nums;
}
.tip__body { display: block; min-width: 0; }
.tip__title {
  display: block;
  font: var(--fw-serif-text) var(--fs-item)/1.22 var(--serif);
  letter-spacing: var(--tr-item);
  color: var(--ink);
  text-wrap: pretty;
  overflow-wrap: break-word;
}
.tip__meta {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: 6px 8px;
  margin-top: 7px;
  font: var(--fw-medium) var(--fs-micro)/1.2 var(--sans);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.tip__side {
  display: flex; flex-direction: column; align-items: flex-end; justify-content: space-between;
  gap: 10px;
  min-width: 16px;
  padding: 4px 0 1px;
}
.tip__side > svg { flex: none; width: 16px; height: 16px; color: var(--ink-3); }
.tip__side > .tip__lock { width: 14px; height: 14px; margin-top: 1px; color: var(--ink); }
.tip--myth .tip__title { font-style: italic; }
.tip--locked .tip__title { color: var(--ink-2); }
.tip .redact { margin: 10px 0 2px; }
@media (hover: hover) { .tip:hover { background: var(--bg-2); } }

/* Redaction bars: 8 px ink, varied widths (ui.redact writes them inline; these are the defaults). */
.redact { display: grid; gap: 6px; }
.redact > i,
.redact > span {
  display: block;
  height: 8px;
  width: 100%;
  background: var(--ink);
}
.redact > :nth-child(1) { width: 92%; }
.redact > :nth-child(2) { width: 74%; }
.redact > :nth-child(3) { width: 46%; }


/* ---------- 13. Legend ----------------------------------------------------- */
/* Evidence: <dl class="legend"><div><dt>.ev</dt><dd>Сильные: РКИ, метаанализы</dd></div> × 3</dl>.
   Inline (routine history): <p class="legend legend--inline"><span><i class="sw sw--full"></i>всё</span>…</p> */

.legend {
  display: grid;
  grid-template-columns: repeat(3, minmax(min-content, 1fr));   /* equal, unless a word needs more */
  margin: 18px var(--gutter) 14px;
  border-top: var(--hair) solid var(--rule);
  border-bottom: var(--hair) solid var(--rule-2);
}
.legend > div { min-width: 0; padding: 11px 8px 12px 0; }
.legend > div + div { padding-left: 12px; border-left: var(--hair) solid var(--rule-2); }
.legend dt { display: flex; align-items: center; gap: 8px; min-height: 12px; }
.legend dd {
  margin: 9px 0 0;
  font: var(--fw-regular) var(--fs-note)/1.38 var(--sans);
  color: var(--ink-2);
  overflow-wrap: break-word;
  hyphens: auto;
}
@media (min-width: 400px) { .legend dd { font-size: var(--fs-body-s); } }
@media (max-width: 374.98px) {
  .legend > div { padding-right: 6px; }
  .legend > div + div { padding-left: 10px; }
}

.legend--inline {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: 6px 18px;
  margin: 12px 0 0;
  border: 0;
  font: var(--fw-regular) var(--fs-note)/1.3 var(--sans);
  color: var(--ink-2);
}
.legend--inline > * { display: inline-flex; align-items: center; gap: 8px; padding: 0; border: 0; }
.legend--inline > * + * { padding-left: 0; border-left: 0; }
.sw { flex: none; width: 11px; height: 11px; border: var(--stroke) solid var(--ink); }
.sw--full { background: var(--ink); }
.sw--part { background: linear-gradient(to top, var(--ink) 50%, transparent 50%); }
.sw--none { border-color: var(--ink-3); }


/* ---------- 14. Spec · cite · dropcap · caution · panel · group ---------- */

/* Spec table, as a composition list on a fashion-house site: caps key | value.
   <dl class="spec"><div><dt>Тип</dt><dd>…</dd></div>…</dl> — keys share one column. */
.spec {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  margin-top: 16px;
  border-top: var(--hair) solid var(--rule);
}
.spec > div { display: contents; }
.spec dt,
.spec dd {
  display: flex; align-items: flex-start;
  min-height: 40px;
  border-bottom: var(--hair) solid var(--rule-2);
}
.spec dt {
  padding: 14px 14px 10px 0;              /* its caps line sits on the value's first baseline */
  font: var(--fw-medium) var(--fs-micro)/1.2 var(--sans);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.spec dd {
  gap: 10px;
  margin: 0;
  padding: 10px 0;
  font: var(--fw-regular) var(--fs-body-s)/1.35 var(--sans);
  color: var(--ink);
}
/* one line of facts under a citation: «РКИ · n = 903» */
.spec--line {
  display: flex; flex-wrap: wrap; align-items: baseline;
  gap: 4px 16px;
  margin-top: 10px;
  border: 0;
  font: var(--fw-regular) var(--fs-note)/1.4 var(--sans);
  color: var(--ink-2);
}
.spec dd > * { min-width: 0; }
.spec dd > .ev { margin-top: 4px; }
.spec dd > .kind { margin-top: 6px; }
.spec--line > div { display: inline-flex; align-items: baseline; gap: 7px; }
.spec--line :is(dt, dd) { display: inline; min-height: 0; padding: 0; border: 0; font: inherit; }
.spec--line dt { font-size: var(--fs-micro); letter-spacing: .08em; text-transform: uppercase; color: var(--ink-3); }

/* Source: italic serif under a vertical ink line, as a magazine credit. */
.cite {
  margin: 12px 0 0;
  padding: 1px 0 2px 14px;
  border-left: 1px solid var(--ink);
  font: italic var(--fw-serif-text) var(--fs-body-s)/1.5 var(--serif);
  color: var(--ink-2);
  overflow-wrap: break-word;
}
.cite b, .cite strong { font-style: normal; font-weight: var(--fw-serif-text); color: var(--ink); }

/* Drop cap in «Почему это работает». */
.dropcap::first-letter {
  float: left;
  margin: 6px 9px 0 -1px;
  font: var(--fw-serif-display) 48px/.8 var(--serif);
  color: var(--ink);
}

/* Caution: a ruled box with a hatched spine — a pattern, never a colour. */
.caution {
  position: relative;
  padding: 12px 14px 13px 26px;
  border: 1px solid var(--rule);
  font: var(--fw-regular) var(--fs-body-s)/1.45 var(--sans);
  color: var(--ink);
}
.caution::before {
  content: "";
  position: absolute; left: 0; top: 0; bottom: 0;
  width: 10px;
  border-right: 1px solid var(--rule);
  background: repeating-linear-gradient(-45deg, var(--ink) 0 1px, transparent 1px 4px);
}
.caution b,
.caution strong {
  display: flex; align-items: center; gap: 8px;
  margin-bottom: 5px;
  font: var(--fw-semibold) var(--fs-micro)/1.25 var(--sans);
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--ink);
}
.caution b svg { flex: none; width: 14px; height: 14px; }
.caution p + p { margin-top: 10px; }

/* Panel: the one filled surface (≤ 1 per screen). */
.panel { background: var(--bg-2); padding: 18px 18px 20px; }

/* Group: a ruled list section — head (label + count) on a strong rule, rows on hairlines. */
.group { margin: 22px var(--gutter) 0; }
.group__head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  padding-bottom: 9px;
  border-bottom: var(--hair) solid var(--rule);
}
.group__sub {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  padding: 16px 0 8px;
  border-bottom: var(--hair) solid var(--rule-2);
}


/* ---------- 15. Premium gate ---------------------------------------------- */
/* (ui.premiumGate) .page-head(kicker … span.end > .tag-premium) → .gate: p.gate__ghost · p.gate__lead ·
   ol.gate__list > li(span.rn + span) · .btn--primary[data-paywall] · p.gate__fine. */

.gate { padding: 0 var(--gutter); }
.gate__ghost {
  margin: 10px 0 0 -6px;
  font: var(--fw-serif-display) var(--fs-ghost)/.76 var(--serif);
  letter-spacing: var(--tr-hero);
  /* outline numeral: a 2 px stroke under a paper fill leaves a clean 1 px contour outside the glyph —
     the variable font's overlapping contours (the foot of «1») never show through */
  color: var(--bg);
  -webkit-text-stroke: 2px var(--ink-4);
  paint-order: stroke fill;
  white-space: nowrap;
  -webkit-user-select: none; user-select: none;
  pointer-events: none;
}
.gate__lead {
  margin-top: 16px;
  font: italic var(--fw-serif-text) var(--fs-item-l)/1.26 var(--serif);
  letter-spacing: var(--tr-item);
  color: var(--ink);
  text-wrap: pretty;
}
.gate__list { margin-top: 20px; counter-reset: gate; }
.gate__list li {
  display: grid;
  grid-template-columns: 36px minmax(0, 1fr);
  align-items: baseline;
  padding: 12px 0;
  border-bottom: var(--hair) solid var(--rule-2);
  font: var(--fw-regular) var(--fs-body)/1.45 var(--sans);
  color: var(--ink);
  counter-increment: gate;
}
.gate__list li:first-child { border-top: var(--hair) solid var(--rule); }
.gate__list li:not(:has(.rn))::before {
  content: counter(gate, upper-roman);
  font: var(--fw-serif-text) 15px/1 var(--serif);
  letter-spacing: .04em;
  color: var(--ink-3);
}
.gate .btn { margin-top: 22px; }
.gate__fine {
  margin-top: 12px;
  font: var(--fw-regular) var(--fs-note)/1.5 var(--sans);
  color: var(--ink-3);
  text-align: center;
  text-wrap: balance;
}


/* ---------- 16. Empty and error states ----------------------------------- */
/* div.state.state--empty|--error: [p.state__mark(--outline)] → h2.state__title (italic) →
   p.state__body → one .btn (secondary block, or ghost). */

.state { padding: 34px var(--gutter) 8px; }
.state__mark {
  margin: 0 0 0 -4px;
  font: var(--fw-serif-display) clamp(104px, 31vw, 124px)/.78 var(--serif);
  letter-spacing: -.04em;
  color: var(--ink);
  -webkit-user-select: none; user-select: none;
}
.state__mark--outline { color: var(--bg); -webkit-text-stroke: 2px var(--ink-3); paint-order: stroke fill; }
.state__title {
  margin: 0;
  font: italic var(--fw-serif-title) var(--fs-num-m)/1.1 var(--serif);
  letter-spacing: -.015em;
  color: var(--ink);
  text-wrap: balance;
}
.state__mark + .state__title { margin-top: 24px; }
.state__body {
  max-width: 34em;
  margin-top: 12px;
  font: var(--fw-regular) var(--fs-body-s)/1.55 var(--sans);
  color: var(--ink-2);
  text-wrap: pretty;
}
.state .btn { margin-top: 24px; }
.state .btn--ghost { margin-top: 14px; }


/* ---------- 17. Skeleton · status line ----------------------------------- */
/* Skeleton: hairlines and columns stay, text becomes flat bars that breathe (opacity only).
   A reload keeps the old content at .5 instead (.is-stale). .is-paused stops the loops
   (motion.pauseOffscreen). */

.sk {
  display: block;
  background: var(--bg-3);
  animation: sk-breathe 1.4s ease-in-out infinite alternate;
}
.sk--t { height: 16px; width: 70%; margin-top: 4px; }
.sk--m { height: 9px;  width: 34%; margin-top: 11px; }
.sk--n { height: 10px; width: 16px; margin-top: 6px; }
.sk--e { height: 8px;  width: 40px; }
.sk--l { height: 10px; width: 100%; }
@keyframes sk-breathe { from { opacity: .5; } to { opacity: 1; } }

.is-stale { opacity: .5; pointer-events: none; transition: opacity var(--d-fade) var(--e-std); }
:is(.is-paused, .is-paused *) { animation-play-state: paused !important; }

/* Status line (ui.loader): «МОДЕЛЬ АНАЛИЗА ——— ЗАГРУЖАЕТСЯ», a 30 % segment runs on a 1 px track. */
.status {
  display: flex; align-items: center; gap: 12px;
  margin: 14px var(--gutter) 0;
  font: var(--fw-medium) var(--fs-micro)/1.2 var(--sans);
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--ink-2);
  white-space: nowrap;
}
.status > span:not(.status__bar) { overflow: hidden; text-overflow: ellipsis; }
.status__bar {
  position: relative;
  flex: 1;
  min-width: 32px;
  height: 1px;
  background: var(--rule-2);
  overflow: hidden;
}
.status__bar::after {
  content: "";
  position: absolute; top: 0; bottom: 0; left: 0;
  width: 30%;
  background: var(--ink);
  animation: status-run 1.4s var(--e-io) infinite;
}
.status.is-done { color: var(--ink); }
.status.is-done .status__bar::after { width: 100%; animation: none; }
@keyframes status-run { from { transform: translateX(-100%); } to { transform: translateX(340%); } }
:where(.sheet, .panel, .gate, .state) .status { margin-left: 0; margin-right: 0; }


/* ---------- 18. Sheet ----------------------------------------------------- */
/* core.openSheet builds:
     <div id="sheet" hidden>   (.is-open while open, .is-closing while it leaves)
       <div class="scrim"></div>
       <section class="sheet [sheet--split | sheet--invert] [has-foot is-open is-dragging is-expanded]">
         .sheet__grab · .sheet__top(kicker, .sheet__close) · .sheet__body · .sheet__foot[hidden]
   Open: translateY(100%) → none, 520 ms --e-out; close: 320 ms --e-in; the scrim fades with it.
   Drag / re-open / the dropping "ghost" of a replaced sheet are WAAPI (core.js), never CSS transitions.
   Scrim and edge come from tokens: white veil + 1 px ink edge (light), black veil + #2B2B2B edge (dark).
   .sheet-root is the same root for static pages (galleries). */

:where(#sheet, .sheet-root) {
  position: fixed; z-index: var(--z-scrim);
  inset: 0;
  pointer-events: none;
}
:where(#sheet, .sheet-root).is-open { pointer-events: auto; }

.scrim {
  position: absolute; inset: 0;
  background: var(--scrim);
  opacity: 0;
  touch-action: none;
  -webkit-tap-highlight-color: transparent;
  transition: opacity var(--d-sheet-out) var(--e-in);
}
:where(#sheet, .sheet-root).is-open .scrim,
.scrim.is-open { opacity: 1; transition: opacity 440ms var(--e-out); }

.sheet {
  position: absolute; z-index: 1;
  left: 0; right: 0; bottom: 0;
  max-width: var(--col);
  max-height: calc(100% - var(--sat) - 24px);
  margin: 0 auto;
  display: flex; flex-direction: column;
  background: var(--sheet);
  color: var(--ink);
  border-top: 1px solid var(--sheet-edge);
  outline: none;
  overflow: hidden;
  overscroll-behavior: contain;
  transform: translateY(100%);
  transition: transform var(--d-sheet-out) var(--e-in), max-height 420ms var(--e-io);
}
:where(#sheet, .sheet-root).is-open .sheet,
.sheet.is-open {
  transform: none;
  transition: transform var(--d-sheet-in) var(--e-out), max-height 420ms var(--e-io);
}
.sheet.is-dragging,
.sheet.is-ghost { transition: none; }

.sheet__grab {
  flex: none;
  width: 32px; height: var(--bar);
  margin: 9px auto 0;
  background: var(--ink-4);
}
.sheet__top {
  flex: none;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  min-height: var(--target);
  margin-top: 2px;
  padding: 0 calc(var(--gutter) - 12px) 0 var(--gutter);
}
.sheet__top > .kicker { flex: 1; min-width: 0; }
.sheet__close {
  flex: none;
  width: var(--target); height: var(--target);
  display: grid; place-items: center;
  color: var(--ink);
  transition: transform var(--d-press) var(--e-std);
}
.sheet__close svg { width: 20px; height: 20px; }
.sheet__close:active { transform: scale(.9); }

.sheet__body {
  flex: 1 1 auto;
  min-height: 0;
  padding: 0 var(--gutter) calc(var(--sab) + 24px);
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.sheet__body::-webkit-scrollbar { display: none; }
.sheet.has-foot > .sheet__body,
.sheet:has(> .sheet__foot:not([hidden])) > .sheet__body { padding-bottom: 24px; }

.sheet__foot {
  flex: none;
  padding: 12px var(--gutter) calc(var(--sab) + 12px);
  border-top: var(--hair) solid var(--rule-2);
  background: var(--sheet);
}
.sheet__note {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 30px;
  margin-top: 4px;
  font: var(--fw-medium) var(--fs-micro)/1.2 var(--sans);
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--ink-3);
}
.sheet__note svg { flex: none; width: 14px; height: 14px; }

/* Long content: the body scrolls under a 40 px fade, the primary action stays pinned in the footer.
   The sheet stops ~200 px from the top (the tapped row stays in view); .is-expanded = full height. */
.sheet--split { max-height: calc(100% - clamp(72px, 24vh, 200px)); }
.sheet--split.is-expanded { max-height: calc(100% - var(--sat) - 12px); }
/* Scrolled text also dissolves at the top: the body reaches 12 px up into the empty lower band of
   .sheet__top (under the kicker and ×, which stay above it) and fades in over those 12 px — the
   content at rest keeps its place, and no half-glyphs hang under «№ 01 — КОЖА». */
.sheet--split > .sheet__top { position: relative; z-index: 1; }
.sheet--split > .sheet__body {
  margin-top: -12px;
  padding-top: 12px;
  padding-bottom: calc(var(--sab) + 44px);
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, rgba(0, 0, 0, .2) 6px, #000 12px, #000 calc(100% - 40px), transparent 100%);
          mask-image: linear-gradient(to bottom, transparent 0, rgba(0, 0, 0, .2) 6px, #000 12px, #000 calc(100% - 40px), transparent 100%);
}
.sheet--split.has-foot > .sheet__body,
.sheet--split:has(> .sheet__foot:not([hidden])) > .sheet__body { padding-bottom: 44px; }

/* The inverted surface (paywall): tokens.css remaps every colour token on .sheet--invert;
   the background, ink and edge here follow the remapped values automatically. */
.sheet--invert { background: var(--sheet); color: var(--ink); }

/* Generic sheet typography (screens may restyle inside their own root). */
.sheet__title {
  margin: 2px 0 0;
  font: var(--fw-serif-title) var(--fs-title-m)/1.04 var(--serif);
  letter-spacing: -.025em;
  color: var(--ink);
  text-wrap: balance;
  overflow-wrap: break-word;
}
:where(.sheet__body) h3 {
  margin: 20px 0 10px;
  font: var(--fw-medium) var(--fs-micro)/1.2 var(--sans);
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--ink-3);
}


/* ---------- 19. Toast ----------------------------------------------------- */
/* <div id="toast" class="toast" role="status" hidden> (.is-open while visible)
     <span class="toast__text">svg + <span>message</span></span> [<button class="toast__action">Ок</button>]
   Inverted bar above the tab bar; while a sheet is open it moves to the top so it never covers the
   pinned action (see the rules at the end). Messages are sentences: sentence case, 14 px (A1). */

.toast {
  position: fixed; z-index: var(--z-toast);
  left: 12px; right: 12px;
  bottom: calc(var(--tab-h) + var(--sab) + 12px);
  max-width: calc(var(--col) - 24px);
  min-height: 48px;
  margin: 0 auto;
  padding: 11px 16px;
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  background: var(--ink);
  color: var(--bg);
  font: var(--fw-medium) var(--fs-body-s)/1.35 var(--sans);
  opacity: 0;
  transform: translateY(12px);
  pointer-events: none;
  transition: opacity var(--d-fade) var(--e-in), transform var(--d-toast) var(--e-in);
}
.toast.is-open {
  opacity: 1;
  transform: none;
  pointer-events: auto;
  transition: opacity var(--d-toast) var(--e-out), transform var(--d-toast) var(--e-out);
}
.toast__text { display: flex; align-items: flex-start; gap: 10px; min-width: 0; overflow-wrap: break-word; }
.toast__text svg { flex: none; width: 14px; height: 14px; margin-top: 2.5px; }
.toast__action {
  position: relative;
  flex: none;
  font: var(--fw-semibold) var(--fs-micro)/1 var(--sans);
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: inherit;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 4px;
}
.toast__action::after { content: ""; position: absolute; inset: -16px -12px; }
/* Over an open sheet: at the very top, over the veiled header. A tall sheet starts lower than the
   toast ends (sheet top ≥ --sat + 24 px; the paywall at 390×740 starts at 65 px), so the toast covers
   neither the pinned action nor, in most cases, the sheet's kicker and ×; a tap hides it anyway.
   In onboarding the primary pair is pinned right above the tab bar: there the toast goes under the
   header. Separate rules, so an engine without :has() still keeps the sibling fallback. */
:where(#sheet.is-open, .sheet-root.is-open) ~ .toast {
  top: calc(var(--sat) + 12px);
  bottom: auto;
  transform: translateY(-12px);
}
body:has(#sheet.is-open) .toast {
  top: calc(var(--sat) + 12px);
  bottom: auto;
  transform: translateY(-12px);
}
body:has(#view[data-screen="onboarding"]):not(:has(#sheet.is-open)) .toast {
  top: calc(var(--sat) + var(--hdr-h) + 12px);
  bottom: auto;
  transform: translateY(-12px);
}
:where(#sheet.is-open, .sheet-root.is-open) ~ .toast.is-open { transform: none; }
body:has(#sheet.is-open) .toast.is-open,
body:has(#view[data-screen="onboarding"]):not(:has(#sheet.is-open)) .toast.is-open { transform: none; }
/* The paywall is the inverted surface: over it the toast inverts against the sheet, not the page
   (a light bar on the black sheet, an ink bar on the paper one) — never the sheet's own colour.
   The 1 px page-ink edge keeps it a distinct bar where it lies over the veiled page instead. */
body:has(#sheet.is-open > .sheet--invert) .toast,
.sheet-root.is-open:has(> .sheet--invert) ~ .toast {
  background: var(--inv-ink);
  color: var(--inv-bg);
  border: 1px solid var(--ink);
}
body:has(#sheet.is-open > .sheet--invert) .toast__action:focus-visible { outline-color: var(--inv-bg); }


/* ---------- 20. Focus · figures · reduced motion ------------------------- */

.tab:focus-visible { outline-offset: -6px; }
.toast__action:focus-visible { outline-color: var(--bg); }
.fld input:focus-visible,
.fld select:focus-visible,
.field:focus-visible { outline: none; }        /* the underline is the focus indicator */

/* A `font:` shorthand resets font-variant-numeric; the serif's default digits are tabular —
   give serif text back its proportional lining figures («SPF 30+», «Сон 7–9 часов», «250»). */
.wordmark, .kicker .no, .page-title, .sec-title, .tip__title, .option__text, .option__no, .rn,
.fld--num input, .cite, .gate__ghost, .gate__lead, .state__mark, .state__title, .sheet__title {
  font-variant-numeric: lining-nums proportional-nums;
}

/* The fixed chrome and the moving surfaces get their own layer up front: smoother sheet / toast
   motion, and grey (never LCD-coloured) text antialiasing on those layers. */
.hdr, .tabbar, .sheet, .toast { will-change: transform; }

@media (prefers-reduced-motion: reduce) {
  .sk { animation: none; opacity: .8; }
  .status__bar::after { animation: none; left: 35%; }
  .btn:is(.is-busy, [aria-busy="true"])::after { animation: none; transform: translateX(35%) scaleX(.3); }
}
