/* =============================================================================
   ТУРБО / TOURBO — component kit
   Consumes tokens.css only. No literal colours, sizes or fonts below.
   Every block is documented: what it is, and the markup it expects.
   ============================================================================= */

/* ---------- 0. RESET / BASE ------------------------------------------------ */

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--c-bg);
  color: var(--c-text);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  font-weight: var(--fw-body);
  line-height: var(--lh-body);
  overflow-wrap: anywhere;
}

/* Selection, drag and the long-press callout are BROWSER furniture. On a phone
   they are what gives a web app away: a long press on a card offers to copy it,
   a press on the logo offers to save the image, and a stray drag paints half the
   screen blue. Off by default, and switched back ON below for the places where
   selecting text is the whole point. */
body {
  -webkit-user-select: none; user-select: none;
  -webkit-touch-callout: none;
  /* The grey flash Chrome/Safari paint over a tapped element. The app draws its
     own :active states (the plate shifts under the press), so the default only
     muddies them. */
  -webkit-tap-highlight-color: transparent;
}

/* Anything a member types into, and anything they might legitimately need to
   copy — a booking number, a receipt line, an address. Denying selection here
   is not "app-like", it is just broken. */
input, textarea, select, [contenteditable], .t-num, .t-mono, .ticket__stub, .list__val {
  -webkit-user-select: text; user-select: text;
  -webkit-touch-callout: default;
}

/* A dragged image leaves a ghost and starts a drag-and-drop the app has no use
   for; `draggable={false}` in the markup covers the rest. */
img { -webkit-user-drag: none; user-drag: none; }

h1, h2, h3, h4 { margin: 0; font-weight: var(--fw-strong); line-height: var(--lh-head); }
p { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
a { color: inherit; text-decoration: none; }
button, input, select, textarea { font: inherit; color: inherit; }
img, svg { display: block; max-width: 100%; }

:focus-visible {
  outline: var(--rule) solid var(--c-focus);
  outline-offset: 2px;
}

/* ---------- 1. TYPOGRAPHY UTILITIES ---------------------------------------- */

/* .t-display — condensed uppercase poster voice. Headings, labels, numbers only.
   Never wrap a paragraph in this. */
.t-display {
  font-family: var(--font-display);
  font-weight: var(--fw-strong);
  text-transform: uppercase;
  line-height: var(--lh-head);
  letter-spacing: var(--ls-head);
}
/* .t-label — micro letterspaced caps: rail labels, badge text, table heads. */
.t-label {
  font-family: var(--font-display);
  font-weight: var(--fw-strong);
  text-transform: uppercase;
  font-size: var(--fs-micro);
  letter-spacing: var(--ls-label);
  line-height: 1.1;
}
/* .t-num — tabular figures. Any number that lines up in a column. */
.t-num { font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1; }
/* .t-mono — ticket data: ids, receipts, countdown digits. */
.t-mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }

.t-muted { color: var(--c-text-muted); }
.t-meta  { color: var(--c-text-meta); font-size: var(--fs-sm); }
.t-red   { color: var(--c-red); }
.t-cyan  { color: var(--c-cyan); }

/* .rail-label — vertical micro label running up the side of a block.
   Rotation is allowed here: it is ornament/label, never body copy. */
.rail-label {
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  font-family: var(--font-display);
  font-weight: var(--fw-strong);
  text-transform: uppercase;
  font-size: var(--fs-micro);
  letter-spacing: var(--ls-label);
  white-space: nowrap;
}

/* ---------- 2. RULES, DIVIDERS, ORNAMENT ----------------------------------- */

/* .rule — horizontal ink rule. Weight modifiers carry hierarchy. */
.rule { border: 0; border-top: var(--rule) solid var(--c-line); margin: 0; }
.rule--hair  { border-top-width: var(--rule-hair); border-top-color: var(--c-line-soft); }
.rule--thick { border-top-width: var(--rule-thick); }
.rule--heavy { border-top-width: var(--rule-heavy); }
.rule--red   { border-top-color: var(--c-red); }
/* .rule--double — the constructivist double bar (thick over hair). */
.rule--double {
  border-top: var(--rule-thick) solid var(--c-line);
  border-bottom: var(--rule-hair) solid var(--c-line);
  height: var(--sp-1);
}

/* .divider — labelled section break used between mockup variants on one page. */
.divider {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin: var(--sp-10) 0 var(--sp-5);
  color: var(--c-ink-2);
}
.divider::before, .divider::after {
  content: ""; flex: 1; height: var(--rule); background: var(--c-line-soft);
}
.divider > span {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  text-transform: uppercase; font-size: var(--fs-xs); letter-spacing: var(--ls-label);
}

/* .plaque — ink block with an offset red plate: the mis-registration signature. */
.plaque {
  position: relative;
  background: var(--c-ink);
  color: var(--c-white);
  box-shadow: var(--plate-red);
}
.plaque--red   { background: var(--c-red);   box-shadow: var(--plate); }
.plaque--cyan  { background: var(--c-cyan);  box-shadow: var(--plate); }
.plaque--ochre { background: var(--c-ochre); color: var(--c-ink); box-shadow: var(--plate); }

/* .cut-br / .cut-tr — the shared diagonal corner cut. Keeps all angles parallel. */
.cut-br { clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--cut)), calc(100% - var(--cut)) 100%, 0 100%); }
.cut-tr { clip-path: polygon(0 0, calc(100% - var(--cut)) 0, 100% var(--cut), 100% 100%, 0 100%); }
.cut-bl { clip-path: polygon(0 0, 100% 0, 100% 100%, var(--cut) 100%, 0 calc(100% - var(--cut))); }

/* .seal — starburst price/promo seal. Put a number inside. */
.seal {
  position: relative;
  display: grid; place-items: center;
  width: 84px; aspect-ratio: 1; border-radius: var(--r-seal);
  background: var(--c-ochre);
  color: var(--c-ink);
  text-align: center;
  z-index: var(--z-raised);
}
.seal::before {
  content: ""; position: absolute; inset: -9px; border-radius: var(--r-seal);
  background: conic-gradient(from 0deg, var(--c-ochre) 0 6deg, transparent 6deg 18deg);
  z-index: -1;
}
.seal--red { background: var(--c-red); color: var(--c-white); }
.seal--red::before { background: conic-gradient(from 0deg, var(--c-red) 0 6deg, transparent 6deg 18deg); }

/* .stamp — rotated outline stamp laid over a card (sold out, cancelled, paid). */
.stamp {
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%) rotate(-11deg);
  padding: var(--sp-1) var(--sp-3);
  border: var(--rule-thick) solid currentColor;
  color: var(--c-red);
  font-family: var(--font-display); font-weight: var(--fw-strong);
  text-transform: uppercase; font-size: var(--fs-h1); letter-spacing: var(--ls-head);
  opacity: 0.9;
  z-index: var(--z-raised);
  pointer-events: none;
  white-space: nowrap;
}
.stamp[data-tone="muted"]   { color: var(--c-ink-3); }
.stamp[data-tone="positive"]{ color: var(--c-green); }

/* .ticket-edge — perforated print edge for receipts and payment blocks. */
.ticket-edge { position: relative; }
.ticket-edge::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: calc(var(--sp-2) * -1);
  height: var(--sp-2);
  background: radial-gradient(circle at 6px 0, var(--c-bg) 5px, transparent 5.5px);
  background-size: 12px var(--sp-2);
}

/* ---------- 3. APP SHELL --------------------------------------------------- */

/* .app — the phone column. Centred, capped, poster margins grow on tablet.
   Markup: <div class="app tex-grain"> [statusbar] [appbar] <main class="app__main"> … */
.app {
  position: relative;
  width: 100%;
  max-width: var(--shell-max);
  margin-inline: auto;
  min-height: 100vh;
  min-height: 100dvh;
  background: var(--c-bg);
  display: flex;
  flex-direction: column;
  border-inline: var(--rule) solid var(--c-line);
  /* .tabbar is FIXED, so it covers whatever the page ends with. Reserve its
     height (58px item + 4px top rule) plus the home-indicator inset, plus a
     gutter so the last card clears the bar instead of touching it. Without
     this the final card cannot be scrolled out from under the nav. */
  padding-bottom: calc(62px + env(safe-area-inset-bottom, 0) + var(--sp-6));
}
@media (max-width: 460px) { .app { border-inline: 0; } }

/* .fill-center — claims the page's leftover height and centres its content in
   it. For TERMINAL screens (a payment result) where nothing follows the
   message, so the eye lands on the outcome and its one action rather than on
   empty paper below them. `.app` is the flex column this grows inside. */
.fill-center {
  flex: 1;
  display: flex; flex-direction: column; justify-content: center;
  gap: var(--sp-4);
}
/* Tablet and up: the column reads as a printed sheet lying on darker stock. */
@media (min-width: 600px) {
  body { background: var(--c-paper-2); }
  .app { border-inline-width: var(--rule-thick); }
}

/* .statusbar — the OS strip. Cosmetic in mockups; keeps the header off the notch. */
.statusbar {
  height: 44px;
  flex: none;
  display: flex; align-items: center; justify-content: space-between;
  padding: 0 var(--gutter);
  background: var(--c-ink);
  color: var(--c-white);
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  letter-spacing: 0.04em;
}
.statusbar__dots { display: flex; gap: 3px; align-items: flex-end; }
.statusbar__dots i { display: block; width: 3px; background: var(--c-white); }
.statusbar__dots i:nth-child(1) { height: 5px; }
.statusbar__dots i:nth-child(2) { height: 8px; }
.statusbar__dots i:nth-child(3) { height: 11px; }
.statusbar__dots i:nth-child(4) { height: 14px; opacity: 0.4; }

/* .appbar — sticky header carrying the brand mark and up to two actions.
   The mockups sat it under a fake 44px .statusbar; the real app clears the notch
   with the actual inset instead, which is 0 wherever there isn't one. */
.appbar {
  position: sticky; top: 0; z-index: var(--z-sticky);
  flex: none;
  display: flex; align-items: stretch; justify-content: space-between; gap: var(--sp-3);
  background: var(--c-paper);
  border-bottom: var(--rule-heavy) solid var(--c-line);
  padding: calc(var(--sp-2) + env(safe-area-inset-top, 0)) var(--gutter) var(--sp-2);
}

/* .appbar__me — the signed-in member, as a MONOGRAM. The full name fought the
   club's plaque for the bar's one row and had to be capped, wrapped or clipped
   however it was tried; a fixed 32px square simply never competes. The name
   survives as the link's accessible label and title.
   `margin-inline-start: auto` keeps it against the right edge when a screen
   also puts its own action up there. */
/* Two classes on purpose: `.monogram` is declared further down the sheet, so a
   single-class rule here loses the size to it. */
.monogram.appbar__me {
  align-self: center; margin-inline-start: auto;
  width: 32px; height: 32px;
  font-size: var(--fs-sm);
  text-decoration: none;
  box-shadow: var(--misreg-x) var(--misreg-y) 0 0 var(--c-red);
}

/* .brand — club mark: logo, red plaque with the club's name, latin sub-line. */
.brand { display: flex; align-items: center; gap: var(--sp-2); min-height: var(--tap-min); min-width: 0; }
/* .brand__logo — square, never shrinks: the plaque beside it is the part that
   gives ground when a club has a long name. */
.brand__logo { flex: none; width: 34px; height: 34px; object-fit: contain; }
.brand__mark {
  position: relative;
  background: var(--c-red);
  color: var(--c-white);
  padding: 2px var(--sp-2) 3px;
  font-family: var(--font-display); font-weight: var(--fw-strong);
  line-height: 1; letter-spacing: 0.01em;
  text-transform: uppercase;
  box-shadow: 3px 3px 0 0 var(--c-ink);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 7px), calc(100% - 7px) 100%, 0 100%);
  /* A club name is longer than «ТУРБО» and its length is the admin's choice, so
     the plaque scales down on narrow screens and clips rather than wrapping. */
  font-size: clamp(20px, 6.4vw, 27px);
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.brand__sub {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-micro); letter-spacing: var(--ls-label);
  text-transform: uppercase; color: var(--c-ink-2);
  writing-mode: vertical-rl; line-height: 1;
}

/* .iconbtn — square 44px action in the header (filters, back, close). */
.iconbtn {
  min-width: var(--tap-min); min-height: var(--tap-min);
  display: inline-grid; place-items: center;
  background: transparent;
  border: var(--rule) solid var(--c-line);
  border-radius: var(--r-0);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.iconbtn:active { background: var(--c-ink); color: var(--c-white); }
.iconbtn--bare { border-color: transparent; }

/* .app__main — scroll area. Bottom padding clears the tab bar. */
.app__main {
  flex: 1 1 auto;
  padding-bottom: calc(var(--sp-16) + var(--sp-6));
}
.app__main--noTabbar { padding-bottom: var(--sp-10); }

/* .page-head — the screen title block: giant condensed title + rule + kicker. */
.page-head { position: relative; overflow: hidden; padding: var(--sp-5) var(--gutter) var(--sp-4); }
/* .page-head__orn — halftone disc bleeding off the top-right of the masthead.
   Pure ornament: no text, no meaning, never intercepts taps. */
.page-head__orn {
  position: absolute; top: -34px; right: -34px;
  width: 132px; height: 132px; border-radius: var(--r-seal);
  color: var(--c-red);
  pointer-events: none;
  opacity: 0.55;
  overflow: hidden;
}
.page-head__kicker {
  display: flex; align-items: center; gap: var(--sp-2);
  color: var(--c-ink-2); margin-bottom: var(--sp-2);
}
.page-head__kicker::after { content: ""; flex: 1; height: var(--rule); background: var(--c-line-soft); }
/* .page-head__row — the title line, with room for a screen-level action on the
   right. `position: relative` is load-bearing: `.page-head__orn` is absolutely
   positioned and would otherwise paint its halftone dots OVER the action. */
.page-head__row {
  position: relative;
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--sp-3);
}
.page-head__title {
  min-width: 0;
  font-family: var(--font-display); font-size: var(--fs-d1);
  text-transform: uppercase; letter-spacing: var(--ls-head);
  line-height: var(--lh-head);
}
.page-head__title em { font-style: normal; color: var(--c-red); }
.page-head__rule { margin-top: var(--sp-2); border-top: var(--rule-thick) solid var(--c-red); }

/* .section — generic padded block inside the scroll area. */
.section { padding: var(--sp-5) var(--gutter); }
.section--flush { padding-inline: 0; }
.section__head {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-3);
  margin-bottom: var(--sp-3);
}
.section__title {
  font-family: var(--font-display); font-size: var(--fs-h1);
  text-transform: uppercase; letter-spacing: var(--ls-head);
}

/* ---------- 4. BOTTOM TAB BAR ---------------------------------------------- */

/* .tabbar — fixed 4-item navigation. Active item is a red plaque.
   Markup: <nav class="tabbar"><a class="tabbar__item is-active">[svg]<span>…</span></a>… */
.tabbar {
  position: fixed; left: 50%; transform: translateX(-50%); bottom: 0;
  width: 100%; max-width: var(--shell-max);
  z-index: var(--z-tabbar);
  display: grid; grid-template-columns: repeat(4, 1fr);
  background: var(--c-ink);
  border-top: var(--rule-thick) solid var(--c-red);
  padding-bottom: env(safe-area-inset-bottom, 0);
}
.tabbar__item {
  min-height: 58px;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 3px;
  color: var(--c-paper-3);
  padding: var(--sp-2) 2px;
  border-right: var(--rule-hair) solid rgba(241, 231, 211, 0.18);
  transition: color var(--dur-fast) var(--ease-out), background var(--dur-fast) var(--ease-out);
}
.tabbar__item:last-child { border-right: 0; }
.tabbar__item svg { width: 22px; height: 22px; stroke: currentColor; fill: none; stroke-width: 2; }
.tabbar__item span {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-micro); letter-spacing: 0.06em; text-transform: uppercase;
  text-align: center; line-height: 1.05;
}
.tabbar__item.is-active { background: var(--c-red); color: var(--c-white); }
.tabbar__item:active { background: var(--c-red-deep); color: var(--c-white); }

/* ---------- 5. BUTTONS ------------------------------------------------------ */

/* .btn — base. Variants: --primary --secondary --ghost. Modifier: --block, --lg.
   Disabled = [disabled] or .is-disabled (striped, no plate). */
.btn {
  position: relative;
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
  min-height: var(--tap-min);
  padding: var(--sp-3) var(--sp-5);
  border: var(--rule) solid var(--c-line);
  border-radius: var(--r-0);
  background: var(--c-surface);
  color: var(--c-ink);
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h2); text-transform: uppercase; letter-spacing: 0.04em;
  cursor: pointer;
  box-shadow: var(--plate);
  transition: transform var(--dur-fast) var(--ease-step),
              box-shadow var(--dur-fast) var(--ease-step),
              background var(--dur-fast) var(--ease-out);
}
.btn:active { transform: translate(var(--misreg-x), var(--misreg-y)); box-shadow: none; }

.btn--primary { background: var(--c-red); color: var(--c-white); border-color: var(--c-ink); }
.btn--primary:hover { background: var(--c-red-deep); }

.btn--secondary { background: var(--c-surface); color: var(--c-ink); }
.btn--secondary:hover { background: var(--c-paper-2); }

.btn--ghost {
  background: transparent; border-color: transparent; box-shadow: none;
  color: var(--c-ink-2); font-size: var(--fs-body);
  text-decoration: underline; text-underline-offset: 4px; text-decoration-thickness: 2px;
}
.btn--ghost:active { transform: none; }
.btn--ghost:hover { color: var(--c-red); }

.btn--lg { min-height: 56px; font-size: var(--fs-h1); padding-inline: var(--sp-6); }
.btn--block { display: flex; width: 100%; }
/* .btn--sm — компактное действие внутри строки ленты. Сохраняет 44px по высоте. */
.btn--sm {
  min-height: var(--tap-min);
  padding: var(--sp-2) var(--sp-3);
  font-size: var(--fs-xs); letter-spacing: var(--ls-label);
  box-shadow: none;
  border-width: var(--rule);
}

.btn[disabled], .btn.is-disabled {
  cursor: not-allowed;
  background: var(--c-paper-3);
  color: var(--c-ink-3);
  border-color: var(--c-ink-3);
  box-shadow: none;
  transform: none;
  background-image: repeating-linear-gradient(-45deg,
    rgba(23,19,14,0.10) 0 6px, transparent 6px 14px);
}
/* Выключенная ссылка-действие не должна становиться полосатой плашкой. */
.btn--ghost[disabled] {
  background: transparent; background-image: none; border-color: transparent;
  color: var(--c-ink-3); text-decoration: none;
}

/* .btn-row — sticky action footer above the tab bar. */
.btn-row {
  position: sticky; bottom: 0;
  display: grid; gap: var(--sp-3);
  padding: var(--sp-4) var(--gutter) var(--sp-5);
  background: var(--c-paper);
  border-top: var(--rule-thick) solid var(--c-line);
}
.btn-row--split { grid-template-columns: auto 1fr; align-items: center; }
/* .btn-row--flow — the same stack of actions, but ending a finished screen
   rather than hovering over a scrolling one. A terminal result (paid, refused)
   has nothing below it to keep reachable, and a sticky bar there just floats a
   rule across the page. */
.btn-row--flow {
  position: static;
  padding-inline: 0; padding-bottom: 0;
  border-top: 0; background: transparent;
}
/* Inside a callout the row only gets the content column, and `auto 1fr` left so
   little for the confirm button that «ОТМЕНИТЬ» broke mid-word. Two equal
   columns, and the label never breaks. */
.callout .btn-row--split { grid-template-columns: 1fr 1fr; }
.callout .btn-row--split .btn { white-space: nowrap; padding-inline: var(--sp-2); }

/* ---------- 6. CHIPS, FILTER PILLS, SEGMENTED CONTROL ---------------------- */

/* .chiprow — horizontally scrolling filter bar. Sticky under the header. */
.chiprow {
  display: flex; gap: var(--sp-2);
  overflow-x: auto;
  padding: var(--sp-2) var(--gutter);
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.chiprow::-webkit-scrollbar { display: none; }
.chiprow--sticky {
  position: sticky; top: 68px; z-index: var(--z-sticky);
  background: var(--c-paper);
  border-bottom: var(--rule) solid var(--c-line);
}

/* .chip — filter pill. .is-on = selected (ink fill). [disabled] = struck out. */
.chip {
  flex: none;
  display: inline-flex; align-items: center; gap: var(--sp-2);
  min-height: var(--tap-min); padding: var(--sp-1) var(--sp-3);
  border: var(--rule) solid var(--c-line);
  background: transparent; color: var(--c-ink);
  border-radius: var(--r-0);
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-xs); letter-spacing: var(--ls-label); text-transform: uppercase;
  cursor: pointer; white-space: nowrap;
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.chip.is-on { background: var(--c-ink); color: var(--c-white); }
.chip.is-on.chip--accent { background: var(--c-red); border-color: var(--c-red); }
.chip__count {
  font-variant-numeric: tabular-nums;
  padding: 0 4px; background: var(--c-red); color: var(--c-white);
}
.chip.is-on .chip__count { background: var(--c-white); color: var(--c-ink); }
.chip[disabled] { opacity: 0.45; cursor: not-allowed; text-decoration: line-through; }
/* .chip.is-in — a day inside the selected date range (endpoints stay .is-on). */
.chip.is-in { background: var(--c-paper-3); border-color: var(--c-ink-2); }
/* .chiprow__sep — vertical rule splitting filter groups inside the scroller. */
.chiprow__sep { flex: none; width: var(--rule); background: var(--c-line-soft); margin-inline: var(--sp-1); }
/* Chips have a 36px box but sit in a 44px-tall scroller — the row is the target. */
.chiprow .chip { align-self: center; }
/* .chiprow--wrap / --flush — chips inside a sheet or a form wrap instead of scrolling. */
.chiprow--wrap  { flex-wrap: wrap; overflow-x: visible; }
.chiprow--flush { padding-inline: 0; }

/* .seg — two/three-way view switch (список · неделя · месяц). */
.seg {
  display: inline-grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  border: var(--rule) solid var(--c-line);
  background: var(--c-surface);
  width: 100%;
}
/* .seg__price — the club's price for that length, under its label. Two lines in
   a segment, because the number is the whole reason to choose one over another. */
.seg__price {
  display: block; margin-top: 2px;
  font-size: var(--fs-micro); letter-spacing: 0; font-style: normal;
  opacity: 0.75;
}

.seg__btn {
  min-height: var(--tap-min);
  padding: var(--sp-2) var(--sp-3);
  background: transparent; border: 0;
  border-right: var(--rule) solid var(--c-line);
  font-family: var(--font-display); font-weight: var(--fw-strong);
  text-transform: uppercase; font-size: var(--fs-xs); letter-spacing: var(--ls-label);
  cursor: pointer;
}
.seg__btn:last-child { border-right: 0; }
.seg__btn.is-on { background: var(--c-ink); color: var(--c-white); }
.seg__btn .t-num { margin-left: var(--sp-1); opacity: 0.75; }

/* .segbar — липкая полка под шапкой для сегментированного переключателя.
   Sticky stack: .appbar 0–68px, .segbar 68–128px. Markup: .segbar > .seg. */
.segbar {
  position: sticky; top: 68px; z-index: var(--z-sticky);
  padding: var(--sp-2) var(--gutter);
  background: var(--c-paper);
  border-bottom: var(--rule) solid var(--c-line);
}

/* .tapchiprow / .tapchip — крупная версия .chip для полевых экранов: цель 56px,
   строка не прокручивается (замёрзшая рука не попадает в скролл). */
.tapchiprow { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--sp-2); }
.tapchip {
  min-height: 56px;
  display: flex; align-items: center; justify-content: center; gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-3);
  background: var(--c-surface);
  border: var(--rule) solid var(--c-line);
  color: var(--c-ink);
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h2); text-transform: uppercase; letter-spacing: 0.04em;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.tapchip.is-on { background: var(--c-ink); color: var(--c-white); box-shadow: var(--plate-red); }

/* .tapseg — двухкнопочный переключатель на всю ширину строки («был» / «не пришёл»).
   Кнопки 56px, залитое состояние читается издалека и в перчатке.
   Markup: .tapseg > button.tapseg__btn[data-id][data-tone][.is-on] */
.tapseg {
  grid-column: 1 / -1;
  display: grid; grid-template-columns: 1fr 1fr;
  border: var(--rule) solid var(--c-line);
}
.tapseg__btn {
  min-height: 56px;
  padding: var(--sp-2) var(--sp-3);
  background: var(--c-surface); border: 0;
  border-right: var(--rule) solid var(--c-line);
  color: var(--c-ink);
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h2); text-transform: uppercase; letter-spacing: 0.04em;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.tapseg__btn:last-child { border-right: 0; }
.tapseg__btn.is-on { background: var(--c-ink); color: var(--c-white); }
.tapseg__btn.is-on[data-tone="positive"] { background: var(--c-green); }
.tapseg__btn.is-on[data-tone="danger"]   { background: var(--c-red); }

/* .sortbar — sort control + result count, sits under the filter row. */
.sortbar {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
  padding: var(--sp-2) var(--gutter);
  border-bottom: var(--rule) solid var(--c-line);
  background: var(--c-paper-2);
}
/* Three items share this line — count, availability checkbox, sort. */
.sortbar__count { flex: none; }
/* The sort control must not wrap to two lines when the checkbox joins the row. */
.sortbar__btn { white-space: nowrap; }

/* .checkfilter — a checkbox sized for a dense toolbar. Same square-and-red-fill
   mark as .radio-row, because a sliding track reads as a settings control and
   this is a filter sitting next to a count and a sort. */
.checkfilter {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  min-width: 0; cursor: pointer;
}
.checkfilter input { position: absolute; opacity: 0; pointer-events: none; }
.checkfilter__mark {
  flex: none; width: 18px; height: 18px;
  border: var(--rule) solid var(--c-ink);
  background: var(--c-surface);
  display: grid; place-items: center;
}
.checkfilter__mark::after {
  content: ""; width: 10px; height: 10px; background: var(--c-red);
  transform: scale(0); transition: transform var(--dur-fast) var(--ease-step);
}
.checkfilter:has(input:checked) .checkfilter__mark::after { transform: scale(1); }
.checkfilter .t-label { white-space: nowrap; }

.sortbar__btn {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  min-height: var(--tap-min); padding: 0 var(--sp-2);
  background: transparent; border: 0; cursor: pointer;
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-xs); letter-spacing: var(--ls-label); text-transform: uppercase;
}
.sortbar__btn::after {
  content: ""; width: 0; height: 0;
  border-left: 5px solid transparent; border-right: 5px solid transparent;
  border-top: 6px solid currentColor;
}

/* ---------- 7. DAY BAND (schedule grouping) -------------------------------- */

/* .dayband — sticky ink bar opening a day group. Giant date + halftone tail. */
/* Sticky stack: .appbar owns 0–68px, and a band sticks directly under it.
   Only a screen that ALSO carries a sticky filter chiprow (68–130px) needs to
   start lower, and it says so with .dayband--stacked — the default used to be
   the stacked offset, which left a 62px gap on every screen without a chiprow
   («Мои тренировки», «Достижения», уведомления, слоты тренера). */
.dayband {
  position: sticky; top: 68px; z-index: 15;
  display: flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-2) var(--gutter);
  background: var(--c-ink); color: var(--c-white);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 10px), calc(100% - 22px) 100%, 0 100%);
  overflow: hidden;
}
.dayband__num {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-d2); line-height: var(--lh-tight);
  font-variant-numeric: tabular-nums;
  color: var(--c-white);
}
.dayband__mo {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-xs); letter-spacing: var(--ls-label); text-transform: uppercase;
  color: var(--c-ochre);
}
.dayband__wd {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-xs); letter-spacing: var(--ls-label); text-transform: uppercase;
  color: var(--c-paper-3);
}
.dayband__tail { flex: 1; align-self: stretch; position: relative; color: var(--c-red); min-width: 24px; }
.dayband__count {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-micro); letter-spacing: var(--ls-label); text-transform: uppercase;
  color: var(--c-white); padding-right: var(--sp-4);
}
.dayband--today { background: var(--c-red); }
/* .dayband--stacked — the schedule, where a filter chiprow sits between the app
   bar and the list. Nothing else stacks two sticky bars. */
.dayband--stacked { top: 130px; }
/* .dayband--toggle — a band that folds a section. It is a <button>, so it needs
   the same reset as button.list__row, plus room for the chevron on the right. */
button.dayband--toggle {
  width: 100%; font: inherit; color: var(--c-white); text-align: left;
  border: 0; cursor: pointer;
}
.dayband__chev { margin-left: auto; padding-left: var(--sp-3); font-size: var(--fs-micro); }
.dayband--today .dayband__mo { color: var(--c-white); }
.dayband--today .dayband__wd { color: var(--c-paper-2); }
.dayband--today .dayband__tail { color: var(--c-ink); }

/* ---------- 8. SESSION CARD ------------------------------------------------ */

/* .card — generic bordered panel with an offset ink plate. */
.card {
  position: relative;
  background: var(--c-surface);
  border: var(--rule) solid var(--c-line);
  box-shadow: var(--plate);
}

/* .card--session — schedule row.
   Grid: [time rail | body | spots score] + full-width footer strip.
   Modifiers: .is-personal (red rail), .is-soldout, .is-cancelled. */
.card--session {
  display: grid;
  grid-template-columns: 64px 1fr auto;
  grid-template-areas: "time body score" "foot foot foot";
  align-items: start;
  margin-bottom: var(--sp-4);
  min-height: 108px;
  transition: transform var(--dur-fast) var(--ease-step), box-shadow var(--dur-fast) var(--ease-step);
}
.card--session:active { transform: translate(var(--misreg-x), var(--misreg-y)); box-shadow: none; }

.card__time {
  grid-area: time;
  align-self: stretch;
  padding: var(--sp-3) var(--sp-2);
  border-right: var(--rule) solid var(--c-line);
  background: var(--c-ink); color: var(--c-white);
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  text-align: center;
}
/* .card__where — the venue on a booking's header strip. It is the one part of
   the line allowed to give ground: the time and date are short and fixed, so a
   long venue truncates instead of pushing the calendar button off the card. */
.card__where {
  flex: 1; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.card__time b {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h1); line-height: 1; font-variant-numeric: tabular-nums;
  white-space: nowrap; letter-spacing: -0.02em;
}
.card__time span {
  font-family: var(--font-display); font-size: var(--fs-micro);
  letter-spacing: 0.04em; text-transform: uppercase; color: var(--c-paper-3);
  white-space: nowrap;
}
.card__time i {
  display: block; width: 100%; height: var(--rule-hair);
  background: var(--c-red); margin: 2px 0;
}
.is-personal .card__time { background: var(--c-red); }
.is-personal .card__time i { background: var(--c-ink); }

.card__body { grid-area: body; padding: var(--sp-3); min-width: 0; }
.card__kicker { display: flex; flex-wrap: wrap; gap: 3px; margin-bottom: var(--sp-2); }
.card__kicker .tag { letter-spacing: 0.06em; padding-inline: 5px; }
.card__title {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h1); text-transform: uppercase; letter-spacing: var(--ls-head);
  line-height: var(--lh-head);
}
.card__meta { font-size: var(--fs-sm); color: var(--c-ink-2); margin-top: 2px; }
.card__coach { display: flex; align-items: center; gap: var(--sp-2); margin-top: var(--sp-2); }

.card__score {
  grid-area: score;
  border-left: var(--rule-hair) solid var(--c-line-soft);
  padding: var(--sp-3) var(--sp-3) var(--sp-2) var(--sp-3);
  text-align: right;
  min-width: 72px;
}

.card__foot {
  grid-area: foot;
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  border-top: var(--rule) solid var(--c-line);
  background: var(--c-paper-2);
}
.card__price {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h2); font-variant-numeric: tabular-nums;
}
.card__note { font-size: var(--fs-xs); color: var(--c-ink-2); text-align: right; }

/* The sold-out stamp sits low-left, clear of the score column. */
.card--session .stamp { top: 64%; left: 44%; font-size: var(--fs-h2); }
.card--session.is-soldout { box-shadow: none; background: var(--c-paper-2); }
.card--session.is-soldout .card__body,
.card--session.is-soldout .card__score { opacity: 0.6; }
.card--session.is-soldout .card__foot { background: var(--c-paper-3); }
.card--session.is-cancelled { border-color: var(--c-ink-3); box-shadow: none; }
.card--session.is-cancelled .card__title { text-decoration: line-through; }

/* .sessionrow — a schedule card plus the actions that belong to it. The card is
   one big link, so anything clickable has to live beside it, not inside. */
.sessionrow { margin-bottom: var(--sp-4); }
.sessionrow .card--session { margin-bottom: 0; }

/* .quickbook — «Запись по пакету» under a schedule card: one tap, no payment.
   Reads as part of the card above it, hence the shared edges and no top rule. */
.quickbook {
  display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-2) var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  border: var(--rule) solid var(--c-line); border-top: 0;
  background: var(--c-paper-2);
}
/* Booked already: an ink strip reads as a mark on the row, not an offer. */
.quickbook--mine { background: var(--c-ink); color: var(--c-white); border-color: var(--c-ink); }
.quickbook--mine .btn--ghost { color: var(--c-paper-2); }
/* A hold can still be LOST, so it gets the warning colour rather than the calm one. */
.quickbook--hold { background: var(--c-ochre); border-color: var(--c-ink); }
.quickbook--hold .quickbook__mark { background: var(--c-ink); }
.quickbook__mark {
  display: grid; place-items: center; flex: none;
  width: 20px; height: 20px;
  background: var(--tone-positive); color: var(--c-white);
  font-size: var(--fs-xs); font-weight: var(--fw-strong);
}
.quickbook .t-meta { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.quickbook .btn--ghost { margin-inline-start: auto; }

/* .card--booking — строка записи в кабинете. Stacked, not gridded: a header
   strip, the body, then a footer that is also the card's action strip.
   Markup: <article class="card card--booking">
             <div class="card__rail">[b][i][span]…<a class="iconbtn"></div>
             <div class="card__body">… <div class="card__foot">[кнопка][номер] */
/* .booking — one card and everything welded to it. */
.booking { margin-bottom: var(--sp-5); }

/* Title and status share a line; the badge keeps its width and the name takes
   the rest, wrapping under itself rather than under the badge. */
.card--booking .card__head {
  display: flex; align-items: flex-start; gap: var(--sp-3);
}
.card--booking .card__head .card__title { flex: 1; min-width: 0; }
.card--booking .card__badges {
  flex: none; display: flex; flex-wrap: wrap; justify-content: flex-end;
  gap: var(--sp-2); max-width: 45%;
}
.card--booking .card__note { max-width: 62%; margin-inline-start: auto; }

/* .card__rail — WHEN · WHERE on one line, with the calendar action at its end.
   The old rail stacked them and spent three lines saying what reads as one
   phrase. `i` is the red tick between parts. */
.card__rail {
  display: flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-3);
  background: var(--c-ink); color: var(--c-white);
  border-bottom: var(--rule) solid var(--c-line);
}
.card__rail b {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h2); line-height: 1; font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em; white-space: nowrap;
}
.card__rail span {
  font-family: var(--font-display); font-size: var(--fs-micro);
  letter-spacing: 0.04em; text-transform: uppercase; color: var(--c-paper-3);
  white-space: nowrap;
}
.card__rail i {
  flex: none; width: var(--rule-hair); height: 12px; background: var(--c-red);
}
.is-personal .card__rail { background: var(--c-red); }
.is-personal .card__rail i { background: var(--c-ink); }

/* .iconbtn — a square action inside a dark strip. Icon-only, so every use MUST
   carry an aria-label. */
.iconbtn {
  flex: none; margin-inline-start: auto;
  display: grid; place-items: center;
  width: 30px; height: 30px;
  border: var(--rule) solid currentColor;
  color: inherit; background: transparent;
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.iconbtn svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 2; }
.iconbtn:active { background: var(--c-white); color: var(--c-ink); }
/* .iconbtn--red — a screen's own quick action, standing beside a display-size
   title rather than inside a dark strip. Same red/ink pairing as .btn--primary. */
.iconbtn--red {
  background: var(--c-red); color: var(--c-white); border-color: var(--c-ink);
}
.iconbtn--red:active { background: var(--c-red-deep); color: var(--c-white); }

/* .monogram — coach avatar substitute (no remote images allowed). Initials plate. */
.monogram {
  flex: none;
  width: 26px; height: 26px;
  display: grid; place-items: center;
  background: var(--c-ink); color: var(--c-white);
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-xs); letter-spacing: 0.02em;
}
.monogram--lg { width: 56px; height: 56px; font-size: var(--fs-h1); box-shadow: var(--plate-red); }
.monogram--red { background: var(--c-red); }

/* ---------- 9. SCOREBOARD NUMERALS ----------------------------------------- */

/* .score — the hero number. Wrap: <div class="score"><b>4</b><span>МЕСТ</span></div>
   Modifiers: --xl (screen hero), --danger/--progress/--muted for tone. */
.score { display: flex; flex-direction: column; align-items: flex-end; line-height: 1; }
.score > b {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-score); line-height: var(--lh-tight);
  letter-spacing: var(--ls-score);
  font-variant-numeric: tabular-nums;
  color: var(--c-ink);
  /* Табло не переносится: ЧЧ:ММ:СС должно стоять одной строкой. */
  white-space: nowrap;
}
.score > span {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-micro); letter-spacing: var(--ls-label); text-transform: uppercase;
  color: var(--c-ink-2); margin-top: 2px; white-space: nowrap;
}
.score--sm > b { font-size: 40px; }
.score--xl > b { font-size: var(--fs-score-l); }
.score--danger  > b { color: var(--c-red); }
.score--progress> b { color: var(--c-cyan); }
.score--muted   > b { color: var(--c-ink-3); }
.score--muted   > span { color: var(--c-ink-3); }
.score--center { align-items: center; }

/* .score-plate — scoreboard set into an ink plaque (detail screens). */
.score-plate {
  display: flex; align-items: center; gap: var(--sp-3);
  background: var(--c-ink); color: var(--c-white);
  padding: var(--sp-3) var(--sp-4);
  box-shadow: var(--plate-red);
}
.score-plate .score > b { color: var(--c-white); }
.score-plate .score > span { color: var(--c-paper-3); }
.score-plate .score--danger > b { color: var(--c-red); }
/* Табло стоит без flex-basis, поэтому длинный текст в соседнем .grow сжимал его
   и рвал число на строки. Табло не сжимается. */
.score-plate > .score { flex: none; }

/* .tickbar — счётная шкала печатными штрихами: ряд равных ячеек по бумажной
   подложке. Одна геометрия на все счётные шкалы кита; вариант задаёт шаг, высоту,
   цвет канта (--tick-gap --tick-h --tick-edge) и семантику залитой ячейки.
   Markup: <div class="tickbar tickbar--capacity"><i class="is-taken"></i>…

   Варианты:
   --capacity  занятость: закрашено = ЗАНЯТО (обратная семантика);
   --progress  выполнение: закрашено = СДЕЛАНО (недельная норма, ступень ветки);
   --ladder    пройденные ступени сезона + новая;
   --rpe       шкала нагрузки 1–10, верхние ступени кумачом;
   --fixed     ячейки фиксированной ширины, а не в долю (ступени ветки). */
.tickbar {
  display: flex; align-items: stretch;
  gap: var(--tick-gap, 3px);
  height: var(--tick-h, 16px);
}
.tickbar > i {
  flex: 1; min-width: 0;
  background: var(--c-paper-3);
  border: var(--rule-hair) solid var(--tick-edge, transparent);
}

/* --capacity — места: занято ink, свободно кумачом. Канта нет. */
.tickbar--capacity { --tick-h: 14px; }
.tickbar--capacity > i.is-taken { background: var(--c-ink); }
.tickbar--capacity > i.is-free  { background: var(--c-red); }
/* On an ink plaque the taken ticks flip to paper so they stay visible. */
.score-plate .tickbar--capacity > i.is-taken { background: var(--c-paper-3); }

/* --progress — сделано кумачом, заморожено холодным. */
.tickbar--progress { --tick-edge: var(--c-ink-3); }
.tickbar--progress > i.is-on     { background: var(--c-red);  border-color: var(--c-red); }
.tickbar--progress > i.is-frozen { background: var(--c-cyan); border-color: var(--c-cyan); }

/* --ladder — пройденные ступени ink, только что взятая — кумачом. */
.tickbar--ladder { --tick-h: 22px; --tick-edge: var(--c-line-soft); }
.tickbar--ladder > i.is-done { background: var(--c-ink); border-color: var(--c-ink); }
.tickbar--ladder > i.is-new  { background: var(--c-red); border-color: var(--c-red); }

/* --rpe — субъективная нагрузка по отметке тренера, пик кумачом. */
.tickbar--rpe { --tick-gap: 2px; --tick-h: 22px; --tick-edge: var(--c-ink-3); }
.tickbar--rpe > i.is-on   { background: var(--c-ink); border-color: var(--c-ink); }
.tickbar--rpe > i.is-peak { background: var(--c-red); border-color: var(--c-red); }

/* --fixed — ячейки не тянутся: короткая шкала из трёх ступеней читается как
   штрихи, а не как полоса во всю ширину. */
.tickbar--fixed { --tick-edge: var(--c-ink-3); height: auto; }
.tickbar--fixed > i { flex: none; width: 16px; height: 16px; }
.tickbar--fixed > i.is-on { background: var(--c-ink); border-color: var(--c-ink); }

/* .prog — доля для больших чисел (очки). Ширина заливки приходит полем percent
   через инлайновую переменную --v. Markup: .prog[style="--v:78%"] > i */
.prog { position: relative; height: 16px; background: var(--c-paper-3); border: var(--rule-hair) solid var(--c-ink-3); }
.prog > i { position: absolute; top: 0; bottom: 0; left: 0; width: var(--v, 0%); background: var(--c-red); }
.score-plate .prog { background: rgba(241, 231, 211, 0.25); border-color: var(--c-paper-3); }

/* ---------- 10. TAGS & STATUS BADGES --------------------------------------- */

/* .tag — inline micro label (level, format, session type). --fill for emphasis. */
.tag {
  display: inline-flex; align-items: center;
  padding: 2px var(--sp-2) 3px;
  border: var(--rule-hair) solid var(--c-ink-2);
  color: var(--c-ink-2);
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-micro); letter-spacing: var(--ls-label); text-transform: uppercase;
  line-height: 1.2;
}
.tag--fill { background: var(--c-ink); color: var(--c-white); border-color: var(--c-ink); }
.tag--accent { background: var(--c-red); color: var(--c-white); border-color: var(--c-red); }
.tag--ochre { background: var(--c-ochre); color: var(--c-ink); border-color: var(--c-ochre); }
/* .tag.is-off — канал выключен на уровне аккаунта: подпись зачёркнута. */
.tag.is-off { text-decoration: line-through; color: var(--c-ink-3); border-color: var(--c-ink-3); }

/* .badge — booking / payment status. Tone comes from data: <span class="badge"
   data-tone="progress">. Tones: neutral positive progress warning danger muted. */
.badge {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  padding: 3px var(--sp-2) 4px;
  border: var(--rule) solid currentColor;
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-xs); letter-spacing: var(--ls-label); text-transform: uppercase;
  line-height: 1.15;
  color: var(--tone-neutral);
  background: var(--c-surface);
}
.badge::before { content: ""; width: 8px; height: 8px; background: currentColor; flex: none; }
.badge[data-tone="positive"] { color: var(--tone-positive); }
.badge[data-tone="progress"] { color: var(--tone-progress); }
.badge[data-tone="warning"]  { color: var(--tone-warning); }
.badge[data-tone="danger"]   { color: var(--tone-danger); }
.badge[data-tone="muted"]    { color: var(--tone-muted); }
/* .badge--solid — knocked out, for hero placement. */
.badge--solid { background: currentColor; border-color: currentColor; }
.badge--solid span { color: var(--c-white); }
.badge--solid::before { background: var(--c-white); }
/* Pulsing dot for "waiting for the server" states. */
.badge--live::before { animation: badgeBlink 1.1s var(--ease-step) infinite; }
@keyframes badgeBlink { 0%, 60% { opacity: 1; } 61%, 100% { opacity: 0.15; } }

/* ---------- 11. COACH CARD ------------------------------------------------- */

/* .coach — coach row/card. .is-locked marks a стажёр who cannot take personals. */
.coach {
  position: relative;
  display: grid; grid-template-columns: auto 1fr auto; gap: var(--sp-3);
  align-items: center;
  padding: var(--sp-3);
  background: var(--c-surface);
  border: var(--rule) solid var(--c-line);
  box-shadow: var(--plate);
  margin-bottom: var(--sp-4);
  min-height: 84px;
}
.coach__name {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h2); text-transform: uppercase; letter-spacing: var(--ls-head);
}
.coach__role { font-size: var(--fs-sm); color: var(--c-ink-2); }
.coach__quals { display: flex; flex-wrap: wrap; gap: var(--sp-1); margin-top: var(--sp-2); }
.coach.is-locked { box-shadow: none; background: var(--c-paper-2); }
.coach.is-locked .monogram { background: var(--c-ink-3); }

/* .coach__detail — quals + specialisation get the full card width under the head. */
.coach__detail { grid-column: 1 / -1; }
.coach__spec { font-size: var(--fs-sm); color: var(--c-ink-2); margin-top: var(--sp-2); }
/* .coach__foot — footer strip: next free slot on the left, action on the right. */
.coach__foot {
  grid-column: 1 / -1;
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--sp-3); flex-wrap: wrap;
  margin: var(--sp-3) calc(var(--sp-3) * -1) calc(var(--sp-3) * -1);
  padding: var(--sp-2) var(--sp-3);
  border-top: var(--rule) solid var(--c-line);
  background: var(--c-paper-2);
}
.coach__slot {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h2); font-variant-numeric: tabular-nums; text-transform: uppercase;
}
/* .coach__lock — band explaining why a стажёр takes no personal sessions. */
.coach__lock {
  grid-column: 1 / -1;
  display: grid; grid-template-columns: auto 1fr; gap: var(--sp-3); align-items: center;
  margin: var(--sp-3) calc(var(--sp-3) * -1) 0;
  padding: var(--sp-3);
  background: var(--c-red-wash);
  border-top: var(--rule) solid var(--c-red);
  font-size: var(--fs-sm);
}
.coach__lock b {
  font-family: var(--font-display); font-size: var(--fs-h1); line-height: 1; color: var(--c-red);
}
.coach.is-locked .coach__foot { background: var(--c-paper-3); }

/* ---------- 12. LISTS & KEY-VALUE ROWS ------------------------------------- */

/* .list — bordered stack with hairline dividers. Rows are min 44px. */
.list { border-block: var(--rule) solid var(--c-line); background: var(--c-surface); }
.list__row {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
  min-height: var(--tap-min);
  padding: var(--sp-3) var(--sp-4);
  border-bottom: var(--rule-hair) solid var(--c-line-soft);
}
.list__row:last-child { border-bottom: 0; }
/* A row that ACTS rather than navigates. A <button> brings its own UA styling —
   auto width, centred text, its own font — so it must be told to behave like
   the anchors beside it. */
button.list__row {
  width: 100%; font: inherit; color: inherit; text-align: left;
  background: none; border: 0; border-bottom: var(--rule-hair) solid var(--c-line-soft);
  cursor: pointer;
}
button.list__row:last-child { border-bottom: 0; }
.list__key {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-xs); letter-spacing: var(--ls-label); text-transform: uppercase;
  color: var(--c-ink-2); flex: none;
}
.list__val { text-align: right; font-size: var(--fs-body); }
.list__val strong { font-family: var(--font-display); font-size: var(--fs-h2); }

/* .callout — admin-authored rule text (cancellation window, package terms). */
.callout {
  display: grid; grid-template-columns: auto 1fr; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  background: var(--c-paper-2);
  border-left: var(--rule-thick) solid var(--c-ink);
  font-size: var(--fs-sm);
}
.callout--warn { border-left-color: var(--c-ochre); background: var(--c-paper-2); }
.callout--danger { border-left-color: var(--c-red); background: var(--c-red-wash); }
.callout__mark {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h1); line-height: 1; font-variant-numeric: tabular-nums;
}

/* .setting-row — строка настройки: заголовок + пояснение слева, переключатель справа.
   .is-off — выключено участником, .is-locked — выключено на уровне аккаунта. */
.setting-row {
  display: grid; grid-template-columns: 1fr auto; gap: var(--sp-3);
  align-items: center;
  min-height: var(--tap-min);
  padding: var(--sp-3) var(--sp-4);
  background: var(--c-surface);
  border-bottom: var(--rule-hair) solid var(--c-line-soft);
}
.setting-row:last-child { border-bottom: 0; }
.setting-row__title {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h2); text-transform: uppercase; letter-spacing: var(--ls-head);
}
.setting-row__note { font-size: var(--fs-sm); color: var(--c-ink-2); }
.setting-row__chan { display: flex; flex-wrap: wrap; gap: var(--sp-1); margin-top: var(--sp-2); }
.setting-row.is-off { background: var(--c-paper-2); }
.setting-row.is-off .setting-row__title { color: var(--c-ink-3); }
.setting-row.is-locked { background: var(--c-paper-2); }

/* .optin — блок согласия: заголовок, сухое пояснение и переключатель.
   Ничего не подталкивает: выключенное состояние выглядит нормальным. */
.optin {
  display: grid; grid-template-columns: 1fr auto; gap: var(--sp-3);
  align-items: center;
  padding: var(--sp-3) var(--sp-4);
  background: var(--c-paper-2);
  border-left: var(--rule-thick) solid var(--c-ink-3);
}
.optin.is-on { background: var(--c-surface); border-left-color: var(--c-red); }
.optin__title {
  display: block;
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h2); text-transform: uppercase; letter-spacing: var(--ls-head);
}
.optin__hint { display: block; font-size: var(--fs-sm); color: var(--c-ink-2); margin-top: 2px; }

/* ---------- 13. HERO (detail screens) -------------------------------------- */

/* .hero — session/coach detail masthead: ink field, halftone, diagonal cut. */
.hero {
  position: relative;
  background: var(--c-ink); color: var(--c-white);
  padding: var(--sp-5) var(--gutter) var(--sp-6);
  overflow: hidden;
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 26px), 0 100%);
}
.hero--red { background: var(--c-red); }
.hero__dots {
  position: absolute; inset: 0;
  color: var(--c-red);
  opacity: 0.5;
  pointer-events: none;
}
.hero--red .hero__dots { color: var(--c-ink); opacity: 0.35; }
.hero__kicker { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-bottom: var(--sp-3); position: relative; }
/* Outline tags on the dark hero need paper-weight ink to stay AA. */
.hero .tag { border-color: var(--c-paper-3); color: var(--c-paper-2); }
.hero__title {
  position: relative;
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-d1); line-height: var(--lh-head);
  text-transform: uppercase; letter-spacing: var(--ls-head);
}
.hero__sub { position: relative; margin-top: var(--sp-2); color: var(--c-paper-2); font-size: var(--fs-sm); }
/* .hero__sub--row — a fact that is also a control: the phone and the «Изменить»
   that changes it share one line. The ghost button is drawn for paper, so on the
   ink field it takes paper weight — same move as the install bar. */
.hero__sub--row { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.hero .btn--ghost { color: var(--c-paper-2); }
.hero .btn--ghost:hover { color: var(--c-white); }
.hero__strip {
  position: relative;
  display: flex; gap: var(--sp-4); margin-top: var(--sp-4);
  padding-top: var(--sp-3); border-top: var(--rule) solid rgba(241,231,211,0.3);
}
.hero__fact b {
  display: block;
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h1); line-height: 1; font-variant-numeric: tabular-nums;
}
.hero__fact span {
  font-family: var(--font-display); font-size: var(--fs-micro);
  letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--c-paper-3);
}

/* ---------- 14. COUNTDOWN --------------------------------------------------- */

/* .countdown — the 15-minute hold. Big mono digits, cyan plate, vertical label.
   .is-urgent flips the plate to red for the last minutes. */
.countdown {
  position: relative;
  display: grid; grid-template-columns: auto 1fr;
  gap: var(--sp-3);
  align-items: center;
  background: var(--c-ink); color: var(--c-white);
  padding: var(--sp-4);
  box-shadow: var(--misreg-x) var(--misreg-y) 0 0 var(--c-cyan);
}
.countdown__rail {
  align-self: stretch;
  display: grid; place-items: center;
  background: var(--c-cyan); color: var(--c-white);
  padding: var(--sp-2) 4px;
}
.countdown__body { text-align: center; }
.countdown__digits {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-score-l); line-height: var(--lh-tight);
  letter-spacing: var(--ls-score);
  font-variant-numeric: tabular-nums;
}
.countdown__digits .sep { animation: colonBlink 1s var(--ease-step) infinite; }
@keyframes colonBlink { 0%, 55% { opacity: 1; } 56%, 100% { opacity: 0.2; } }
.countdown__cap {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-micro); letter-spacing: var(--ls-label); text-transform: uppercase;
  color: var(--c-paper-3);
}
.countdown.is-urgent { box-shadow: var(--misreg-x) var(--misreg-y) 0 0 var(--c-red); }
.countdown.is-urgent .countdown__rail { background: var(--c-red); }
.countdown.is-urgent .countdown__digits { color: var(--c-red); }
/* .countdown__track — depleting tick row under the digits. */
.countdown__track { display: flex; gap: 2px; margin-top: var(--sp-3); }
.countdown__track i { flex: 1; height: 6px; background: var(--c-cyan); }
.countdown__track i.is-spent { background: rgba(241,231,211,0.22); }
.countdown.is-urgent .countdown__track i { background: var(--c-red); }

/* ---------- 15. FORM CONTROLS ---------------------------------------------- */

.field { display: block; margin-bottom: var(--sp-4); }
.field__label {
  display: block; margin-bottom: var(--sp-2);
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-xs); letter-spacing: var(--ls-label); text-transform: uppercase;
  color: var(--c-ink-2);
}
.field__hint { margin-top: var(--sp-2); font-size: var(--fs-sm); color: var(--c-ink-3); }
.field__hint--error { color: var(--c-red); }

/* .input / .textarea / .select — flat, square, 2px ink border, no radius. */
.input, .textarea, .select {
  width: 100%;
  min-height: var(--tap-min);
  padding: var(--sp-3);
  background: var(--c-surface);
  border: var(--rule) solid var(--c-line);
  border-radius: var(--r-0);
  font-size: var(--fs-body);
  transition: box-shadow var(--dur-fast) var(--ease-out);
}
.input:focus, .textarea:focus, .select:focus { outline: 0; box-shadow: var(--plate-cyan); }
.textarea { min-height: 96px; resize: vertical; line-height: var(--lh-body); }
.select {
  appearance: none;
  padding-right: var(--sp-8);
  background-image:
    linear-gradient(45deg, transparent 50%, var(--c-ink) 50%),
    linear-gradient(135deg, var(--c-ink) 50%, transparent 50%);
  background-position: right 20px center, right 14px center;
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
}
.input[disabled], .textarea[disabled], .select[disabled] {
  background: var(--c-paper-3); color: var(--c-ink-3); cursor: not-allowed;
}

/* .radio-list — payment method chooser. Each row: label > input + content.
   .is-disabled rows keep their reason text visible. */
.radio-list { display: grid; gap: var(--sp-3); }
.radio-row {
  position: relative;
  display: grid; grid-template-columns: auto 1fr auto; gap: var(--sp-3);
  align-items: center;
  min-height: 64px;
  padding: var(--sp-3) var(--sp-4);
  background: var(--c-surface);
  border: var(--rule) solid var(--c-line);
  cursor: pointer;
  transition: box-shadow var(--dur-fast) var(--ease-out), background var(--dur-fast) var(--ease-out);
}
.radio-row input { position: absolute; opacity: 0; pointer-events: none; }
/* The mark is a square that gets an ink cross-fill when checked. */
.radio-row__mark {
  flex: none; width: 24px; height: 24px;
  border: var(--rule) solid var(--c-ink);
  display: grid; place-items: center;
}
.radio-row__mark::after {
  content: ""; width: 12px; height: 12px; background: var(--c-red); transform: scale(0);
  transition: transform var(--dur-fast) var(--ease-step);
}
.radio-row:has(input:checked) { box-shadow: var(--plate-red); background: var(--c-white); }
.radio-row:has(input:checked) .radio-row__mark::after { transform: scale(1); }
.radio-row:has(input:focus-visible) { outline: var(--rule) solid var(--c-focus); outline-offset: 2px; }
.radio-row__title {
  display: block;
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h2); text-transform: uppercase; letter-spacing: var(--ls-head);
}
.radio-row__note { display: block; font-size: var(--fs-sm); color: var(--c-ink-2); margin-top: 2px; }
.radio-row .score, .radio-row .card__price, .radio-row .tag { justify-self: end; text-align: right; }
.radio-row.is-disabled {
  cursor: not-allowed; background: var(--c-paper-2); color: var(--c-ink-3);
  border-color: var(--c-ink-3); box-shadow: none;
  background-image: repeating-linear-gradient(-45deg, rgba(23,19,14,0.07) 0 6px, transparent 6px 14px);
}
.radio-row.is-disabled .radio-row__mark { border-color: var(--c-ink-3); }
.radio-row.is-disabled .radio-row__note { color: var(--c-red); }

/* .switch — freeze/transfer toggles. Square knob, no rounding. */
.switch { display: inline-flex; align-items: center; gap: var(--sp-3); min-height: var(--tap-min); cursor: pointer; }
.switch input { position: absolute; opacity: 0; }
.switch__track {
  width: 52px; height: 28px; background: var(--c-paper-3);
  border: var(--rule) solid var(--c-ink); position: relative;
}
.switch__track::after {
  content: ""; position: absolute; top: 2px; left: 2px; width: 20px; height: 20px;
  background: var(--c-ink);
  transition: transform var(--dur) var(--ease-step), background var(--dur) var(--ease-out);
}
.switch input:checked + .switch__track { background: var(--c-red); }
.switch input:checked + .switch__track::after { transform: translateX(24px); background: var(--c-white); }
/* Недоступный переключатель должен читаться как выключенный, а не как «выкл». */
.switch:has(input:disabled) { cursor: not-allowed; color: var(--c-ink-3); }
.switch input:disabled + .switch__track { opacity: 0.45; }
.switch.is-locked { cursor: not-allowed; }

/* .code — ввод одноразового кода. Один настоящий input лежит поверх ячеек,
   ячейки только рисуют цифры. Никаких паролей и токенов в UI.
   Markup: .code > input.code__input + .code__cell × 6 */
.code { position: relative; display: grid; grid-template-columns: repeat(6, 1fr); gap: var(--sp-2); }
.code__input {
  position: absolute; inset: 0; width: 100%; height: 100%;
  opacity: 0; z-index: var(--z-raised);
  border: 0; background: transparent;
}
.code__cell {
  min-height: 56px;
  display: grid; place-items: center;
  background: var(--c-surface);
  border: var(--rule) solid var(--c-line);
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h1); font-variant-numeric: tabular-nums;
}
.code__cell.is-filled { box-shadow: var(--plate); }
.code.is-focus .code__cell.is-next { box-shadow: var(--plate-cyan); border-color: var(--c-cyan); }
.code.is-error .code__cell { border-color: var(--c-red); background: var(--c-red-wash); box-shadow: none; }

/* .stepper — число с двумя крупными кнопками: правка подставленного значения без
   набора с клавиатуры. Поле остаётся вводимым для редких больших правок.
   Markup: .stepper > button.stepper__btn + input.stepper__val + button.stepper__btn */
.stepper {
  display: grid; grid-template-columns: 64px 1fr 64px;
  border: var(--rule) solid var(--c-line);
  background: var(--c-surface);
}
.stepper__btn {
  min-height: 64px;
  background: var(--c-ink); color: var(--c-white); border: 0;
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-d2); line-height: 1; cursor: pointer;
}
.stepper__btn:active { background: var(--c-red); }
.stepper__val {
  min-width: 0;
  border: 0; background: transparent;
  text-align: center;
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-score); line-height: 1; letter-spacing: var(--ls-score);
  font-variant-numeric: tabular-nums;
}
.stepper__val:focus { outline: 0; background: var(--c-paper-2); }

/* .rpe — ввод субъективной нагрузки 1–10 двумя рядами по пять: каждая цель 56px,
   заливка растёт вместе с числом (шкала, а не набор кнопок). Показ той же шкалы
   участнику — .tickbar--rpe. Markup: .rpe > button.rpe__btn[.is-on|.is-under] */
.rpe { display: grid; grid-template-columns: repeat(5, 1fr); gap: 2px; }
.rpe__btn {
  min-height: 56px;
  background: var(--c-surface);
  border: var(--rule-hair) solid var(--c-line);
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h2); font-variant-numeric: tabular-nums;
  cursor: pointer;
}
.rpe__btn.is-on { background: var(--c-ink); color: var(--c-white); }
.rpe__btn.is-under { background: var(--c-paper-3); }

/* Закрытая тренировка: элементы ввода гаснут, но отметки остаются читаемыми. */
.tapseg__btn[disabled], .tapchip[disabled],
.rpe__btn[disabled], .stepper__btn[disabled] { cursor: not-allowed; opacity: 0.5; }
.stepper__val[disabled], .input[disabled] { color: var(--c-ink-3); }

/* .slotgrid / .slot — сетка временных плашек для персональной тренировки.
   States: free (default), .is-on (chosen), [disabled] (taken / held). */
/* .install — «поставьте приложение на домашний экран». A full sheet, because it
   is asked once and answered once; «Продолжить в браузере» is the way past it
   and is deliberately quiet, not hidden. */
.install {
  position: fixed; inset: 0; z-index: var(--z-modal, 90);
  overflow-y: auto; overscroll-behavior: contain;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--sp-4);
  padding: var(--sp-8) var(--gutter) calc(var(--sp-8) + env(safe-area-inset-bottom, 0));
  background: var(--c-paper);
  text-align: center;
}
/* Quieter than a hero's dots: this screen is mostly small text, and the raster
   competes with it at the density the posters use. */
.install__dots { position: absolute; inset: 0; color: var(--c-red); opacity: 0.10; pointer-events: none; }
.install__icon {
  position: relative; flex: none;
  width: 112px; height: 112px;
  border: var(--rule) solid var(--c-line);
  box-shadow: var(--plate);
}
.install__title {
  position: relative;
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-d2); line-height: 1; letter-spacing: var(--ls-head);
  text-transform: uppercase;
}
.install__line { position: relative; max-width: 30ch; font-size: var(--fs-sm); color: var(--c-ink-2); }
.install .btn { position: relative; }

/* Numbered because the order matters — this is a route through someone else's
   UI, not a list of features. */
.install__steps {
  position: relative; text-align: left;
  display: grid; gap: var(--sp-2);
  margin: 0; padding-inline-start: var(--sp-6);
  font-size: var(--fs-sm);
}
.install__steps li { list-style: decimal; }
.install__glyph {
  display: inline-block; vertical-align: -4px;
  width: 18px; height: 18px;
  stroke: currentColor; fill: none; stroke-width: 2;
}
.install__note {
  position: relative; max-width: 32ch;
  font-size: var(--fs-xs); color: var(--c-ink-2);
}
.install__skip { margin-top: var(--sp-2); }

/* .install--bar — «уже установлено». A strip above the tab bar rather than a
   screen, because there is no action behind it: a browser cannot open an
   installed app, so this is information, not an offer. */
.install--bar {
  inset: auto 0 0 0;
  flex-direction: row; align-items: center; justify-content: space-between;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--gutter);
  padding-bottom: calc(var(--sp-3) + 62px + env(safe-area-inset-bottom, 0));
  background: var(--c-ink); color: var(--c-white);
  text-align: left; font-size: var(--fs-xs);
}
.install--bar .btn--ghost { color: var(--c-paper-2); flex: none; }

/* .starbtn — favourite toggle on a coach card. Sits ON the name line, so it
   reads as a property of the coach rather than another action in the footer. */
.starbtn {
  border: 0; background: transparent; cursor: pointer;
  padding: 0 var(--sp-1); margin-inline-start: var(--sp-2);
  font-size: var(--fs-h1); line-height: 1; color: var(--c-ink-3);
  transition: color var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-step);
}
.starbtn.is-on { color: var(--c-ochre); }
.starbtn:active { transform: scale(0.88); }
.starbtn[disabled] { opacity: 0.5; cursor: wait; }

/* .coach__seen — «you have trained with them», derived from bookings. Quiet:
   it explains the order, it is not a badge to collect. */
.coach__seen {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-micro); letter-spacing: var(--ls-label); text-transform: uppercase;
  color: var(--c-green);
}

/* .dayhead — the vertical date plate with its weekday beside it. The rotated
   number is compact but unreadable at a glance; the horizontal line is what a
   member actually reads to find «завтра». */
.dayhead { display: flex; align-items: center; gap: var(--sp-3); margin-bottom: var(--sp-2); }
.dayhead .rail-label { margin: 0; }
.dayhead__wd {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-sm); letter-spacing: var(--ls-label); text-transform: uppercase;
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dayhead__wd i { font-style: normal; color: var(--c-ink-2); }

.slotgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: var(--sp-2); }
.slot {
  min-height: 56px;
  display: flex; flex-direction: column; align-items: flex-start; justify-content: center; gap: 2px;
  padding: var(--sp-2) var(--sp-3);
  background: var(--c-surface);
  border: var(--rule) solid var(--c-line);
  border-radius: var(--r-0);
  cursor: pointer; text-align: left;
  transition: transform var(--dur-fast) var(--ease-step), box-shadow var(--dur-fast) var(--ease-step);
}
.slot b {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h2); line-height: 1; font-variant-numeric: tabular-nums;
}
.slot span {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-micro); letter-spacing: var(--ls-label); text-transform: uppercase;
  color: var(--c-ink-2);
}
.slot.is-on { background: var(--c-ink); color: var(--c-white); box-shadow: var(--plate-red); }
.slot.is-on span { color: var(--c-paper-3); }
.slot[disabled] {
  cursor: not-allowed; color: var(--c-ink-3);
  border-color: var(--c-ink-3); background: var(--c-paper-2);
  background-image: repeating-linear-gradient(-45deg, rgba(23,19,14,0.10) 0 6px, transparent 6px 14px);
}
.slot[disabled] b { text-decoration: line-through; }
.slot[disabled] span { color: var(--c-ink-3); }

/* ---------- 16. SHEET / MODAL ---------------------------------------------- */

/* .scrim — ink wash behind a sheet. Add .is-open to show. */
.scrim {
  position: fixed; inset: 0; z-index: var(--z-scrim);
  background: rgba(23, 19, 14, 0.72);
  opacity: 0; visibility: hidden;
  transition: opacity var(--dur) var(--ease-out), visibility var(--dur);
}
.scrim.is-open { opacity: 1; visibility: visible; }

/* .sheet — bottom sheet. Add .is-open to slide up. .sheet--modal centres it. */
.sheet {
  position: fixed; left: 50%; bottom: 0; transform: translate(-50%, 100%);
  width: 100%; max-width: var(--shell-max);
  z-index: var(--z-sheet);
  background: var(--c-paper);
  border-top: var(--rule-heavy) solid var(--c-red);
  transition: transform var(--dur-slow) var(--ease-out);
  max-height: 86vh; overflow-y: auto;
}
.sheet.is-open { transform: translate(-50%, 0); }
.sheet__head {
  position: sticky; top: 0;
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
  padding: var(--sp-4) var(--gutter);
  background: var(--c-paper);
  border-bottom: var(--rule) solid var(--c-line);
}
.sheet__title {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-d2); text-transform: uppercase; letter-spacing: var(--ls-head);
}
.sheet__body { padding: var(--sp-4) var(--gutter) var(--sp-6); }
.sheet--modal {
  bottom: auto; top: 50%; transform: translate(-50%, -40%);
  max-width: calc(var(--shell-max) - var(--sp-8));
  border: var(--rule-thick) solid var(--c-ink);
  box-shadow: 6px 6px 0 0 var(--c-red);
  opacity: 0; visibility: hidden;
}
.sheet--modal.is-open { transform: translate(-50%, -50%); opacity: 1; visibility: visible; }

/* .sheet--full — полноэкранный лист фильтров: шапка и подвал закреплены, скроллит
   только список фасетов. Останавливается под шапкой, чтобы расписание оставалось
   видно сверху. */
.sheet--full {
  top: calc(var(--sp-16) + var(--sp-6));
  bottom: 0; max-height: none; overflow: hidden;
  display: flex; flex-direction: column;
}
.sheet--full .sheet__head { flex: none; }
.sheet--full .sheet__body { flex: 1 1 auto; overflow-y: auto; -webkit-overflow-scrolling: touch; }
/* .sheet__foot — the sheet's own action bar; same anatomy as .btn-row. */
.sheet__foot {
  flex: none;
  display: grid; gap: var(--sp-2);
  padding: var(--sp-3) var(--gutter) var(--sp-4);
  background: var(--c-paper);
  border-top: var(--rule-thick) solid var(--c-line);
}
/* .facet__head — label + live selection count above a facet's chips. */
.facet { padding-top: var(--sp-5); }
.facet + .facet { border-top: var(--rule-hair) solid var(--c-line-soft); }
.facet__head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-3); }
/* Page behind a full-height sheet must not scroll. */
body.is-sheet-locked { overflow: hidden; }

/* ---------- 17. STATE BLOCKS (empty / loading / error) --------------------- */

/* .state — centred block with an ornament mark, title, copy and one action. */
.state {
  display: grid; justify-items: center; gap: var(--sp-3);
  text-align: center;
  padding: var(--sp-10) var(--gutter);
}
.state__mark {
  position: relative;
  width: 96px; height: 96px;
  display: grid; place-items: center;
  border: var(--rule-thick) solid var(--c-ink);
  color: var(--c-ink);
  background: var(--c-paper-2);
}
.state__mark span {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: 44px; line-height: 1;
}
.state__mark--danger { border-color: var(--c-red); color: var(--c-red); background: var(--c-red-wash); }
.state__title {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-d2); text-transform: uppercase; letter-spacing: var(--ls-head);
}
.state__text { font-size: var(--fs-sm); color: var(--c-ink-2); max-width: 30ch; }

/* .skel — loading placeholder bars. Mechanical step-shimmer, not a soft pulse. */
.skel { background: var(--c-paper-3); position: relative; overflow: hidden; }
.skel::after {
  content: ""; position: absolute; inset: 0;
  background: repeating-linear-gradient(-45deg,
    rgba(23,19,14,0.10) 0 8px, transparent 8px 16px);
  animation: skelMove 900ms var(--ease-step) infinite;
}
@keyframes skelMove { from { transform: translateX(0); } to { transform: translateX(22px); } }
.skel--line { height: 14px; margin-bottom: var(--sp-2); }
.skel--title { height: 26px; width: 62%; margin-bottom: var(--sp-3); }
.skel--block { height: 108px; margin-bottom: var(--sp-4); }

/* .actionbar — a decision that must stay reachable. Parked ABOVE .tabbar (which
   is fixed and would otherwise cover it), so picking a slot at the top of a long
   list still leaves the confirm button on screen. Same clearance arithmetic as
   .app's bottom padding: 58px item + 4px rule + the home-indicator inset. */
.actionbar {
  position: fixed; left: 50%; transform: translateX(-50%);
  bottom: calc(62px + env(safe-area-inset-bottom, 0));
  width: 100%; max-width: var(--shell-max);
  z-index: var(--z-tabbar);
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
  padding: var(--sp-3) var(--gutter);
  background: var(--c-paper);
  border-top: var(--rule-heavy) solid var(--c-red);
}

/* ---------- 18. TOAST / NOTIFICATION ROW ----------------------------------- */

/* .toast — fixed confirmation row. .notif-row — the same anatomy inside a feed. */
.toast {
  position: fixed; left: 50%; transform: translateX(-50%);
  bottom: calc(58px + var(--sp-4));
  width: calc(100% - var(--gutter) * 2); max-width: calc(var(--shell-max) - var(--gutter) * 2);
  z-index: var(--z-toast);
  display: grid; grid-template-columns: auto 1fr auto; gap: var(--sp-3); align-items: center;
  padding: var(--sp-3);
  background: var(--c-ink); color: var(--c-white);
  border-left: var(--rule-heavy) solid var(--c-red);
  box-shadow: var(--plate-red);
}
.toast[data-tone="positive"] { border-left-color: var(--c-green); }
.toast[data-tone="progress"] { border-left-color: var(--c-cyan); }
.toast__title {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h2); text-transform: uppercase; letter-spacing: var(--ls-head);
}
.toast__text { font-size: var(--fs-sm); color: var(--c-paper-2); }

.notif-row {
  display: grid; grid-template-columns: auto 1fr auto; gap: var(--sp-3);
  padding: var(--sp-3) var(--gutter);
  border-bottom: var(--rule-hair) solid var(--c-line-soft);
  background: var(--c-surface);
  align-items: start;
}
.notif-row.is-unread { background: var(--c-paper-2); border-left: var(--rule-thick) solid var(--c-red); }
.notif-row__mark { width: 10px; height: 10px; margin-top: 6px; background: var(--c-ink); }
.notif-row[data-tone="positive"] .notif-row__mark { background: var(--c-green); }
.notif-row[data-tone="progress"] .notif-row__mark { background: var(--c-cyan); }
.notif-row[data-tone="danger"] .notif-row__mark { background: var(--c-red); }
.notif-row[data-tone="warning"] .notif-row__mark { background: var(--tone-warning); }
.notif-row[data-tone="muted"]   .notif-row__mark { background: var(--tone-muted); }
.notif-row__title { font-family: var(--font-display); font-weight: var(--fw-strong); font-size: var(--fs-h2); text-transform: uppercase; }
.notif-row__text { font-size: var(--fs-sm); color: var(--c-ink-2); }
.notif-row__time { font-family: var(--font-mono); font-size: var(--fs-micro); color: var(--c-ink-3); white-space: nowrap; }
/* .notif-row__body / __act — вторая строка внутри уведомления: теги каналов и действие. */
.notif-row__body { min-width: 0; }
.notif-row__act { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-top: var(--sp-2); }
.notif-row.is-read .notif-row__title { color: var(--c-ink-2); }

/* ---------- 19. WEEK STRIP & MONTH GRID ------------------------------------ */

/* .weekstrip — 7 day buttons, scrollable. .is-on = selected day. */
.weekstrip { display: grid; grid-template-columns: repeat(7, 1fr); border: var(--rule) solid var(--c-line); background: var(--c-surface); }
.weekstrip__day {
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  min-height: 62px; padding: var(--sp-2) 2px;
  border-right: var(--rule-hair) solid var(--c-line-soft);
  background: transparent; cursor: pointer;
}
.weekstrip__day:last-child { border-right: 0; }
.weekstrip__wd { font-family: var(--font-display); font-size: var(--fs-micro); letter-spacing: 0.06em; text-transform: uppercase; color: var(--c-ink-3); }
.weekstrip__num { font-family: var(--font-display); font-weight: var(--fw-strong); font-size: var(--fs-h2); font-variant-numeric: tabular-nums; line-height: 1; }
.weekstrip__dot { width: 100%; height: 4px; background: var(--c-paper-3); }
.weekstrip__dot.has-free { background: var(--c-red); }
.weekstrip__dot.is-full { background: var(--c-ink-3); }
.weekstrip__day.is-on { background: var(--c-ink); color: var(--c-white); }
.weekstrip__day.is-on .weekstrip__wd { color: var(--c-paper-3); }

/* .monthgrid — 7-column calendar. Cells carry a load bar, not a dot cloud. */
.monthgrid { display: grid; grid-template-columns: repeat(7, 1fr); border-top: var(--rule) solid var(--c-line); border-left: var(--rule) solid var(--c-line); }
.monthgrid__head, .monthgrid__cell {
  border-right: var(--rule-hair) solid var(--c-line-soft);
  border-bottom: var(--rule-hair) solid var(--c-line-soft);
  min-height: 52px;
  padding: 3px;
  background: var(--c-surface);
}
.monthgrid__head { min-height: 26px; display: grid; place-items: center; background: var(--c-ink); color: var(--c-white); }
.monthgrid__head span { font-family: var(--font-display); font-size: var(--fs-micro); letter-spacing: 0.06em; text-transform: uppercase; }
.monthgrid__cell { display: flex; flex-direction: column; justify-content: space-between; cursor: pointer; }
.monthgrid__cell b { font-family: var(--font-display); font-weight: var(--fw-strong); font-size: var(--fs-sm); font-variant-numeric: tabular-nums; }
.monthgrid__cell.is-out { background: var(--c-paper-2); color: var(--c-ink-3); }   /* вне месяца */
.monthgrid__cell.is-empty { background: var(--c-paper-2); color: var(--c-ink-2); }  /* нет тренировок */
.monthgrid__cell.is-on { background: var(--c-red); color: var(--c-white); }
/* .is-past — a day already gone. The grid keeps it for orientation but mutes it
   so the eye skips ahead. Distinct from .is-out, which is a neighbouring
   month's filler cell. */
.monthgrid__cell.is-past { background: var(--c-paper-2); color: var(--c-ink-3); }
.monthgrid__cell.is-past b { opacity: 0.5; }
/* Today reuses .is-on's red — the same "you are here" the dayband uses. */
.monthgrid__cell.is-on b { color: var(--c-white); }
/* .daymark — «you are booked this day». Rendered INVISIBLY inside the day cell;
   the cell itself goes green through `:has()`. Green is the app's only
   non-poster hue and already means confirmed/paid, so it keeps that meaning
   here. The element carries the words for screen readers — the colour alone is
   not information. */
.monthgrid__cell:has(.daymark),
.weekstrip__day:has(.daymark) {
  background: var(--c-green); color: var(--c-white);
}
.monthgrid__cell:has(.daymark) b { color: var(--c-white); opacity: 1; }
.monthgrid__cell:has(.daymark) .monthgrid__load i { background: var(--c-white); }
.monthgrid__cell:has(.daymark) .monthgrid__load i.is-full { background: var(--c-paper-3); }
.weekstrip__day:has(.daymark) i,
.weekstrip__day:has(.daymark) u { color: var(--c-paper-2); }
/* Today keeps its own plate — «you are here» outranks «you are booked» — but
   takes a green rule so the day is still marked. */
.monthgrid__cell.is-on:has(.daymark),
.weekstrip__day.is-on:has(.daymark) {
  background: var(--c-red); color: var(--c-white);
  box-shadow: inset 0 -4px 0 0 color-mix(in srgb, var(--c-green) 45%, white);
}
.weekstrip__day.is-on:has(.daymark) { background: var(--c-ink); }

.monthgrid__load { display: flex; gap: 2px; }
.monthgrid__load i { flex: 1; height: 5px; background: var(--c-ink); }
.monthgrid__load i.is-full { background: var(--c-paper-3); }
.monthgrid__cell.is-on .monthgrid__load i { background: var(--c-white); }

/* ---------- 20. PAYMENT RESULT & TICKET ------------------------------------ */

/* .statuspair — платёж и бронирование живут отдельно, поэтому статусы стоят рядом
   двумя равными колонками. Markup: .statuspair > div > (.t-label + .badge). */
.statuspair {
  display: grid; grid-template-columns: 1fr 1fr;
  border: var(--rule) solid var(--c-line);
  background: var(--c-surface);
  box-shadow: var(--plate);
}
.statuspair > div {
  min-width: 0;
  padding: var(--sp-3) var(--sp-2);
  border-right: var(--rule) solid var(--c-line);
  display: grid; gap: var(--sp-2); align-content: start; justify-items: start;
}
.statuspair > div:last-child { border-right: 0; }
/* Длинные статусы («Возврат инициирован») должны влезать в половину колонки. */
.statuspair .badge { max-width: 100%; font-size: var(--fs-micro); }

/* .wait-bar — неопределённое ожидание вебхука: марширующая косая заливка cyan.
   Markup: <div class="wait-bar"><i></i></div>. При reduced-motion остаётся статичной. */
.wait-bar {
  position: relative; height: 14px; overflow: hidden;
  border: var(--rule) solid var(--c-line);
  background: var(--c-paper-2);
}
.wait-bar > i {
  position: absolute; inset: 0 -32px;
  background-image: repeating-linear-gradient(-45deg,
    var(--c-cyan) 0 8px, transparent 8px 16px);
  animation: waitMarch 700ms linear infinite;
}
@keyframes waitMarch { from { transform: translateX(0); } to { transform: translateX(22.7px); } }

/* .ticket — билет-подтверждение: ink-шапка, слаб-цифры, перфорация и корешок с
   данными чека. Markup: .ticket > .ticket__head + .ticket__body + .ticket__perf +
   .ticket__stub, плюс абсолютная .seal. */
.ticket { position: relative; background: var(--c-surface); border: var(--rule) solid var(--c-line); box-shadow: var(--plate-red); }
.ticket__head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-4);
  background: var(--c-ink); color: var(--c-white);
}
.ticket__head .t-mono { font-size: var(--fs-xs); color: var(--c-paper-2); }
.ticket__body { padding: var(--sp-4); padding-right: var(--sp-6); }
.ticket__nums { display: flex; flex-wrap: wrap; gap: var(--sp-5); margin: var(--sp-3) 0; }
.ticket__num b {
  display: block;
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-d1); line-height: var(--lh-tight); letter-spacing: var(--ls-score);
  font-variant-numeric: tabular-nums;
}
.ticket__num span {
  display: block; margin-top: var(--sp-1);
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-micro); letter-spacing: var(--ls-label); text-transform: uppercase;
  color: var(--c-ink-2);
}
.ticket__perf {
  position: relative; height: var(--sp-5);
  border-block: var(--rule-hair) solid var(--c-line-soft);
  background-image: radial-gradient(circle at 9px 50%, var(--c-bg) 3.5px, transparent 4px);
  background-size: 18px 100%;
}
.ticket__perf::before, .ticket__perf::after {
  content: ""; position: absolute; top: 50%; transform: translateY(-50%);
  width: 20px; height: 20px; border-radius: var(--r-seal); background: var(--c-bg);
}
.ticket__perf::before { left: -11px; }
.ticket__perf::after  { right: -11px; }
.ticket__stub { padding: var(--sp-3) var(--sp-4); background: var(--c-paper-2); }
.ticket__stub .list__row { padding-inline: 0; border-bottom-color: var(--c-paper-3); }

/* ---------- 21. STEP TRACKER ------------------------------------------------ */

/* .steps — four-stage tracker for the request → proposal → confirm → pay loop.
   Markup: <ol class="steps"><li class="steps__item is-done"><b class="steps__n">1</b>
           <span class="steps__cap">Запрос</span></li>… */
.steps { display: flex; gap: var(--sp-1); }
.steps__item { flex: 1; display: grid; justify-items: center; gap: var(--sp-1); text-align: center; }
.steps__n {
  width: 32px; height: 32px; display: grid; place-items: center;
  border: var(--rule) solid var(--c-ink-3); color: var(--c-ink-3);
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-body); line-height: 1; font-variant-numeric: tabular-nums;
}
.steps__cap {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-micro); letter-spacing: var(--ls-label); text-transform: uppercase;
  color: var(--c-ink-3); line-height: 1.15;
}
.steps__item.is-done .steps__n { background: var(--c-ink); border-color: var(--c-ink); color: var(--c-white); }
.steps__item.is-done .steps__cap { color: var(--c-ink-2); }
.steps__item.is-now .steps__n { background: var(--c-red); border-color: var(--c-red); color: var(--c-white); box-shadow: var(--plate); }
.steps__item.is-now .steps__cap { color: var(--c-ink); }

/* ---------- 22. PACKAGE CARD ------------------------------------------------ */

/* .pkg — карточка пакета. Markup:
   .card.pkg > .pkg__head (плашка: название + статус)
             > .pkg__body (табло остатка, шкала, срок)
             > .list      (правила пакета)
             > .pkg__foot (заморозка и передача)
   Модификатор .is-off — израсходованный или истёкший пакет. */
.pkg { margin-bottom: var(--sp-6); }
.pkg__head {
  display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  background: var(--c-ink); color: var(--c-white);
}
.pkg__title {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-d2); text-transform: uppercase; letter-spacing: var(--ls-head);
  line-height: var(--lh-head);
  overflow-wrap: normal;           /* название пакета не рвём посреди слова */
}
.pkg__sub { font-size: var(--fs-sm); color: var(--c-paper-3); }
/* Бейдж статуса на чёрной плашке остаётся на бумажной подложке — иначе
   тон-цвет теряет контраст на ink. */
.pkg__head .badge { flex: none; }
.pkg__body { display: grid; grid-template-columns: auto 1fr; gap: var(--sp-4); padding: var(--sp-4); }
.pkg__body > * { min-width: 0; }
.pkg__types { display: flex; flex-wrap: wrap; gap: var(--sp-1); margin-top: var(--sp-2); }
/* .pkg__rules — правила пакета словами клуба: подпись слева, текст слева же,
   иначе длинные предложения ломают выключку в .list. */
.pkg__rules { display: grid; gap: var(--sp-3); padding: var(--sp-4); border-top: var(--rule) solid var(--c-line); }
.pkg__rules > div { display: grid; grid-template-columns: 84px 1fr; gap: var(--sp-3); align-items: baseline; }
.pkg__rules p { font-size: var(--fs-sm); }
.pkg__foot {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
  flex-wrap: wrap;
  padding: var(--sp-3) var(--sp-4);
  border-top: var(--rule) solid var(--c-line);
  background: var(--c-paper-2);
}
/* .pkg__foot--end — for a foot holding only the commit action («Купить»), which
   belongs at the trailing edge. `space-between` alone leaves a lone child on the
   left, where a buy button reads like an afterthought. */
.pkg__foot--end { justify-content: flex-end; }
.pkg.is-off { box-shadow: none; background: var(--c-paper-2); }
.pkg.is-off .pkg__head { background: var(--c-ink-2); }
.pkg.is-off .score > b { color: var(--c-ink-3); }

/* .usage-strip — лента движения занятий по пакету: горизонтальный скролл ячеек
   ±1 с датой. Markup: .usage-strip > .usage-strip__cell[data-tone] > b + span + em. */
.usage-strip { display: flex; gap: var(--sp-2); max-width: 100%; overflow-x: auto; padding-bottom: var(--sp-2); scrollbar-width: none; }
.usage-strip::-webkit-scrollbar { display: none; }
.usage-strip__cell {
  flex: none; width: 128px;
  padding: var(--sp-2);
  background: var(--c-surface);
  border: var(--rule-hair) solid var(--c-line);
  border-left: var(--rule-thick) solid var(--c-ink);
}
.usage-strip__cell b {
  display: block;
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h1); line-height: 1; font-variant-numeric: tabular-nums;
}
.usage-strip__cell span {
  display: block; margin-top: 2px;
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-micro); letter-spacing: var(--ls-label); text-transform: uppercase;
  color: var(--c-ink-2);
}
.usage-strip__cell em { display: block; margin-top: 2px; font-style: normal; font-size: var(--fs-xs); color: var(--c-ink-3); }
.usage-strip__cell[data-tone="positive"] { border-left-color: var(--c-green); }
.usage-strip__cell[data-tone="positive"] b { color: var(--c-green); }
.usage-strip__cell[data-tone="progress"] { border-left-color: var(--c-cyan); }
.usage-strip__cell[data-tone="progress"] b { color: var(--c-cyan); }

/* ---------- 23. BOOKING HISTORY & RESCHEDULE -------------------------------- */

/* .timeline — история изменений записи. Rail + dot per row; tone comes from data. */
.timeline { position: relative; padding-left: var(--sp-6); }
.timeline::before {
  content: ""; position: absolute; left: 5px; top: 8px; bottom: 8px;
  width: var(--rule); background: var(--c-line-soft);
}
.timeline__row { position: relative; padding-bottom: var(--sp-4); }
.timeline__row:last-child { padding-bottom: 0; }
.timeline__dot {
  position: absolute; left: calc(var(--sp-6) * -1); top: 4px; width: 12px; height: 12px;
  background: var(--tone-neutral);
}
.timeline__row[data-tone="positive"] .timeline__dot { background: var(--tone-positive); }
.timeline__row[data-tone="progress"] .timeline__dot { background: var(--tone-progress); }
.timeline__row[data-tone="warning"]  .timeline__dot { background: var(--tone-warning); }
.timeline__row[data-tone="danger"]   .timeline__dot { background: var(--tone-danger); }
.timeline__row[data-tone="muted"]    .timeline__dot { background: var(--c-paper-3); border: var(--rule) solid var(--c-ink-3); }
.timeline__time {
  font-family: var(--font-mono); font-size: var(--fs-micro);
  color: var(--c-ink-3); letter-spacing: 0.02em;
}
.timeline__title {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h2); text-transform: uppercase; letter-spacing: var(--ls-head);
}
.timeline__text { font-size: var(--fs-sm); color: var(--c-ink-2); }
.timeline__row.is-pending .timeline__title { color: var(--c-ink-3); }

/* .swap — старое время → новое время. Old cell is struck and paper-toned. */
.swap { display: grid; grid-template-columns: 1fr auto 1fr; gap: var(--sp-2); align-items: stretch; }
.swap__cell {
  background: var(--c-surface); border: var(--rule) solid var(--c-line);
  padding: var(--sp-3); min-width: 0;
}
.swap__cell.is-old {
  background: var(--c-paper-2); color: var(--c-ink-2); border-color: var(--c-ink-3);
}
.swap__cell.is-old .swap__time { text-decoration: line-through; }
.swap__cell.is-new { box-shadow: var(--plate-red); }
.swap__cell.is-empty { background: var(--c-paper-2); border-style: dashed; box-shadow: none; }
.swap__time {
  display: block;
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h1); line-height: 1; font-variant-numeric: tabular-nums;
}
.swap__day {
  display: block; margin-top: 2px;
  font-family: var(--font-display); font-size: var(--fs-micro);
  letter-spacing: var(--ls-label); text-transform: uppercase; color: inherit;
}
.swap__note { display: block; margin-top: var(--sp-2); font-size: var(--fs-xs); color: var(--c-ink-2); }
.swap__arrow {
  align-self: center;
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h1); color: var(--c-red);
}

/* ---------- 24. SIGN-IN ----------------------------------------------------- */

/* .entry-head — постерная шапка входа: красное поле, растровая точка, косой срез.
   (Печатный лист годового отчёта — это .poster, §33: другой компонент.) */
.entry-head {
  position: relative; overflow: hidden;
  background: var(--c-red); color: var(--c-white);
  padding: var(--sp-8) var(--gutter) var(--sp-10);
  text-align: center;
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--cut-deep)), 0 100%);
}
.entry-head__dots { position: absolute; inset: 0; color: var(--c-ink); opacity: 0.35; pointer-events: none; }
.entry-head__mark {
  position: relative; display: inline-block;
  background: var(--c-white); color: var(--c-red);
  padding: var(--sp-2) var(--sp-4) var(--sp-3);
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-d1); line-height: 1; letter-spacing: 0.02em; text-transform: uppercase;
  box-shadow: var(--misreg-x) var(--misreg-y) 0 0 var(--c-ink);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--cut)), calc(100% - var(--cut)) 100%, 0 100%);
}
.entry-head__sub {
  position: relative; margin-top: var(--sp-4);
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-xs); letter-spacing: var(--ls-label); text-transform: uppercase;
  color: var(--c-white);
}
.entry-head__line {
  position: relative; margin: var(--sp-4) auto 0; max-width: 26ch;
  font-size: var(--fs-sm); color: var(--c-white);
}
.entry-head__rule { position: relative; margin: var(--sp-5) auto 0; width: 64px;
  border-top: var(--rule-heavy) solid var(--c-ink); }

/* .id-card — карточка участника: монограмма, имя, счёт выездов. */
.id-card {
  position: relative;
  display: grid; grid-template-columns: auto 1fr; gap: var(--sp-4);
  align-items: center;
  padding: var(--sp-4);
  background: var(--c-surface);
  border: var(--rule-thick) solid var(--c-line);
  box-shadow: var(--plate-red);
}
.id-card__name {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-d2); text-transform: uppercase; letter-spacing: var(--ls-head);
  line-height: var(--lh-head);
}
.id-card__meta { font-size: var(--fs-sm); color: var(--c-ink-2); margin-top: 2px; }
.id-card__strip {
  grid-column: 1 / -1;
  display: flex; gap: var(--sp-4); align-items: flex-end;
  padding-top: var(--sp-3); border-top: var(--rule) solid var(--c-line-soft);
}

/* ---------- 25. RANK, LADDER, NORM, STREAK, BRANCH -------------------------- */

/* .rank — плашка ступени клуба: вертикальная рейка с позицией, имя ступени
   набором в d1, растровый круг в углу. Markup:
   .rank > .rank__rail + .rank__body(.rank__cap .rank__name .rank__note) + .rank__orn */
.rank {
  position: relative; overflow: hidden;
  display: grid; grid-template-columns: 34px 1fr;
  background: var(--c-red); color: var(--c-white);
  box-shadow: var(--plate);
}
.rank__rail {
  display: grid; place-items: center;
  background: var(--c-ink); color: var(--c-white);
  padding: var(--sp-3) 0;
}
.rank__body { position: relative; z-index: var(--z-raised); padding: var(--sp-3) var(--sp-4) var(--sp-4); min-width: 0; }
.rank__cap { display: block; color: var(--c-paper-2); }
.rank__name {
  display: block; margin-top: var(--sp-1);
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-d1); line-height: var(--lh-head);
  text-transform: uppercase; letter-spacing: var(--ls-head);
  overflow-wrap: normal;              /* имя ступени не рвём посреди слова */
}
.rank__note { display: block; margin-top: var(--sp-2); font-size: var(--fs-sm); color: var(--c-paper-2); }
.rank__orn {
  position: absolute; right: -26px; top: -26px; width: 108px; height: 108px;
  border-radius: var(--r-seal); color: var(--c-ink); opacity: 0.45;
  pointer-events: none; overflow: hidden;
}
/* Ступень новичка — та же плашка чернилами: достижения ещё нет, но место для него есть. */
.rank--start { background: var(--c-ink); box-shadow: var(--plate-red); }
.rank--start .rank__rail { background: var(--c-red); }
.rank--start .rank__orn { color: var(--c-red); opacity: 0.5; }

/* .ladder — лестница ступеней клуба, шесть строк сверху вниз.
   Markup: ol.ladder > li.ladder__row(.is-past|.is-now|.is-ahead) >
           .ladder__no + .ladder__name + .ladder__state
   (Компактная шкала пройденных ступеней — .tickbar--ladder, §9.) */
.ladder { border: var(--rule) solid var(--c-line); background: var(--c-surface); }
.ladder__row {
  display: grid; grid-template-columns: 30px 1fr auto; gap: var(--sp-3);
  align-items: center;
  min-height: var(--tap-min);
  padding: var(--sp-2) var(--sp-3);
  border-bottom: var(--rule-hair) solid var(--c-line-soft);
}
.ladder__row:last-child { border-bottom: 0; }
.ladder__no {
  display: grid; place-items: center; width: 30px; height: 30px;
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h2); line-height: 1; font-variant-numeric: tabular-nums;
  background: var(--c-paper-3); color: var(--c-ink-2);
}
.ladder__name {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h2); text-transform: uppercase; letter-spacing: var(--ls-head);
  overflow-wrap: normal;
}
.ladder__state { color: var(--c-ink-3); }
.ladder__row.is-past .ladder__no { background: var(--c-ink); color: var(--c-white); }
.ladder__row.is-now { background: var(--c-ink); color: var(--c-white); }
.ladder__row.is-now .ladder__no { background: var(--c-red); color: var(--c-white); }
.ladder__row.is-now .ladder__state { color: var(--c-ochre); }
.ladder__row.is-ahead .ladder__name { color: var(--c-ink-3); }
/* .ladder__cap — подпись под компактной шкалой .tickbar--ladder. */
.ladder__cap {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-3);
  margin-top: var(--sp-2);
}

/* .norm — норматив следующей ступени: несколько условий, у каждого своя шкала.
   Markup: .norm > .norm__row > (.row--between: .t-label + .norm__val) + шкала + .t-meta */
.norm { display: grid; gap: var(--sp-4); }
.norm__row { display: grid; gap: var(--sp-2); }
.norm__val { font-family: var(--font-display); font-weight: var(--fw-strong); font-size: var(--fs-h2); }
.norm__val em { font-style: normal; color: var(--c-ink-3); }

/* .streak — серия недель: табло, шкала текущей недели, управление заморозкой. */
.streak__week { padding: var(--sp-4); display: grid; gap: var(--sp-2); }
.streak__cnt { font-family: var(--font-display); font-weight: var(--fw-strong); font-size: var(--fs-h1); }
.streak__foot {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
  flex-wrap: wrap;
  padding: var(--sp-3) var(--sp-4);
  border-top: var(--rule) solid var(--c-line);
  background: var(--c-paper-2);
}
.streak__foot p { flex-basis: 100%; font-size: var(--fs-sm); color: var(--c-ink-2); }

/* .branch — ветка дисциплины: имя, достигнутая ступень, три штриха, остаток.
   Markup: .branch > .branch__name + .branch__steps + .branch__title + .branch__note
   Разделитель живёт на смежных ветках, а не на каждой строке: список внутри .card
   получает волосяные рули между ветками, а одиночная .card.branch (итог тренировки)
   сохраняет собственную рамку карточки. */
.branch {
  display: grid; grid-template-columns: 1fr auto; gap: var(--sp-1) var(--sp-3);
  align-items: center;
  padding: var(--sp-3) var(--sp-4);
}
.branch + .branch { border-top: var(--rule-hair) solid var(--c-line-soft); }
.branch__name { color: var(--c-ink-2); }
/* Три ступени ветки — штрихи фиксированной ширины, см. .tickbar--fixed. */
.branch__steps { display: flex; gap: 3px; }
.branch__steps i { width: 16px; height: 16px; background: var(--c-paper-3); border: var(--rule-hair) solid var(--c-ink-3); }
.branch__steps i.is-on { background: var(--c-ink); border-color: var(--c-ink); }
.branch__title {
  grid-column: 1 / -1;
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h2); text-transform: uppercase; letter-spacing: var(--ls-head);
}
.branch.is-empty .branch__title { color: var(--c-ink-3); }
.branch__note { grid-column: 1 / -1; font-size: var(--fs-sm); color: var(--c-ink-2); }

/* ---------- 26. BADGES ------------------------------------------------------ */

/* .pin — эмалевый значок клуба: плоская геометрия, ободок чернилами, смещённая
   плашка. Тон приходит полем tone, никогда не выводится из id.
   Markup: <span class="pin" data-tone="ochre"><svg …></span>
           <span class="pin"><b class="pin__num">10</b></span>
   Модификаторы: --lg --xl · .is-locked — значок не получен. */
.pin {
  --pin-fill: var(--c-red);
  position: relative; flex: none;
  width: 64px; aspect-ratio: 1; border-radius: var(--r-seal);
  display: grid; place-items: center;
  background: var(--pin-fill); color: var(--c-white);
  border: var(--rule) solid var(--c-ink);
  box-shadow: 2px 2px 0 0 var(--c-ink);
}
.pin svg { width: 58%; height: 58%; fill: currentColor; }
.pin__num {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: 25px; line-height: 1; letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
}
.pin[data-tone="ink"]   { --pin-fill: var(--c-ink);   color: var(--c-white); }
.pin[data-tone="ochre"] { --pin-fill: var(--c-ochre); color: var(--c-ink); }
.pin[data-tone="cyan"]  { --pin-fill: var(--c-cyan);  color: var(--c-white); }
/* Не полученный — бумажная заготовка без эмали, перечёркнутая по общему углу. */
.pin.is-locked { --pin-fill: var(--c-paper-3); color: var(--c-ink-3); border-color: var(--c-ink-3); box-shadow: none; }
.pin.is-locked::after {
  content: ""; position: absolute; left: -4px; right: -4px; top: 50%;
  height: 3px; margin-top: -1.5px; background: var(--c-ink-3);
  transform: rotate(-45deg);
}
.pin--lg { width: 96px; border-width: var(--rule-thick); }
.pin--lg .pin__num { font-size: 38px; }
.pin--xl { width: 124px; border-width: var(--rule-thick); box-shadow: 4px 4px 0 0 var(--c-ink); }
.pin--xl .pin__num { font-size: 50px; }
/* Лучевая насечка — только на крупных значках, где она читается. */
.pin--lg::before, .pin--xl::before {
  content: ""; position: absolute; inset: -14px; border-radius: var(--r-seal);
  background: repeating-conic-gradient(from 0deg, var(--pin-fill) 0 5deg, transparent 5deg 20deg);
  -webkit-mask-image: radial-gradient(circle closest-side, transparent 0 84%, #000 84%);
          mask-image: radial-gradient(circle closest-side, transparent 0 84%, #000 84%);
  pointer-events: none;
}

/* .pin-grid — сетка значков по три в ряд. Ячейка — кнопка, открывает лист значка.
   Markup: .pin-grid > button.pin-cell > .pin + .pin-cell__name + .pin-cell__meta */
.pin-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--sp-5) var(--sp-2); align-items: stretch; }
.pin-cell {
  display: grid; grid-template-rows: auto 1fr auto;
  justify-items: center; gap: var(--sp-2);
  min-height: var(--tap-min);
  padding: var(--sp-2) 2px;
  background: transparent; border: 0; cursor: pointer; text-align: center;
}
.pin-cell:active .pin { transform: translate(2px, 2px); box-shadow: none; }
.pin-cell__name {
  align-self: start;
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-xs); letter-spacing: 0.04em; text-transform: uppercase;
  line-height: 1.15; overflow-wrap: normal;
}
/* Закрытый значок гасим цветом и перечёркнутой заготовкой, но не именем:
   двойное зачёркивание уже нечитаемо. */
.pin-cell.is-locked .pin-cell__name { color: var(--c-ink-2); }
.pin-cell__meta { font-family: var(--font-mono); font-size: var(--fs-micro); color: var(--c-ink-2); line-height: 1.2; }

/* .shelf — горизонтальная полка значков; последняя ячейка ведёт на полную сетку.
   Markup: .shelf > .shelf__cell(> .pin + span) … + .shelf__more */
.shelf { display: flex; gap: var(--sp-3); overflow-x: auto; padding-bottom: var(--sp-2); scrollbar-width: none; }
.shelf::-webkit-scrollbar { display: none; }
.shelf__cell { flex: none; width: 84px; display: grid; justify-items: center; gap: var(--sp-2); text-align: center; }
.shelf__cell span {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-micro); letter-spacing: 0.04em; text-transform: uppercase;
  line-height: 1.15; color: var(--c-ink-2); overflow-wrap: normal;
}
.shelf__more {
  flex: none; width: 84px; min-height: 64px;
  display: grid; place-items: center; align-content: center; gap: var(--sp-1);
  border: var(--rule) solid var(--c-line); background: var(--c-paper-2);
  text-align: center; padding: var(--sp-2);
}
.shelf__more b { font-family: var(--font-display); font-weight: var(--fw-strong); font-size: var(--fs-h1); line-height: 1; }

/* .half / .half-head — две половины полки значков. Серьёзная набрана чернилами на
   бумаге, ироничная — охрой на второй бумаге: половины не путаются даже боковым
   зрением. Markup: section.half(.half--ironic) > .half-head(__title __note) > … */
.half { padding-bottom: var(--sp-6); }
.half--ironic { background: var(--c-paper-2); border-top: var(--rule-heavy) solid var(--c-ochre); }
.half-head {
  padding: var(--sp-4) var(--gutter) var(--sp-5);
  background: var(--c-ink); color: var(--c-white);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--cut)), 0 100%);
}
.half-head__title {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-d2); text-transform: uppercase; letter-spacing: var(--ls-head);
}
.half-head__note { margin-top: var(--sp-2); font-size: var(--fs-sm); color: var(--c-paper-2); max-width: 38ch; }
.half--ironic .half-head { background: var(--c-ochre); color: var(--c-ink); }
.half--ironic .half-head__note { color: var(--c-ink); }

/* .group-head — подпись группы внутри половины: название и счёт полученных. */
.group-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-3);
  margin: var(--sp-6) 0 var(--sp-3);
  border-bottom: var(--rule) solid var(--c-line);
  padding-bottom: var(--sp-2);
}
.group-head h3 {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h1); text-transform: uppercase; letter-spacing: var(--ls-head);
}

/* .newbadge — кульминация экрана итога: полученный значок во весь блок, кумачовое
   поле, растр и диагональный срез. Markup:
   .newbadge > .newbadge__dots + .newbadge__rail + .pin--xl + __name + __desc + __meta */
.newbadge {
  position: relative; overflow: hidden;
  display: grid; justify-items: center; gap: var(--sp-3);
  text-align: center;
  padding: var(--sp-6) var(--gutter) var(--sp-8) calc(var(--gutter) + var(--sp-6));
  background: var(--c-red); color: var(--c-white);
  box-shadow: var(--plate);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--cut-deep)), 0 100%);
}
.newbadge__dots { position: absolute; inset: 0; z-index: 0; color: var(--c-ink); opacity: 0.35; pointer-events: none; }
.newbadge__rail {
  position: absolute; left: var(--sp-3); top: var(--sp-5); z-index: 2;
  color: var(--c-white);
}
.newbadge .pin,
.newbadge__name, .newbadge__desc, .newbadge__meta { position: relative; z-index: 1; }
.newbadge__name {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-d1); line-height: var(--lh-head);
  text-transform: uppercase; letter-spacing: var(--ls-head);
  overflow-wrap: normal;
}
.newbadge__desc { font-size: var(--fs-sm); color: var(--c-white); max-width: 32ch; }
.newbadge__meta { color: var(--c-paper-2); }

/* .enamel — эмалевый значок сезонной награды: восьмиугольная плашка с ink-обводкой
   и внутренним кольцом, рисуется inline SVG (никаких внешних картинок). Цвет поля
   приходит полем tone значка, а не его id.
   Markup: .awards > .awards__item > (.enamel > svg) + b + span */
.enamel { width: 68px; height: 68px; flex: none; }
.enamel svg { width: 100%; height: 100%; }
.awards { display: grid; gap: var(--sp-3); }
.awards__item {
  display: grid; grid-template-columns: auto 1fr; gap: var(--sp-3);
  align-items: center;
  padding: var(--sp-3);
  background: var(--c-surface);
  border: var(--rule) solid var(--c-line);
  box-shadow: var(--plate);
}
.awards__item b {
  display: block;
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h2); text-transform: uppercase; letter-spacing: var(--ls-head);
}
.awards__item span { display: block; font-size: var(--fs-sm); color: var(--c-ink-2); margin-top: 2px; }

/* ---------- 27. POINTS ------------------------------------------------------ */

/* .calc — разбор начисления очков. Каждая строка — множитель или слагаемое,
   подытог и итог выделены плашками. Числа приходят полями, вёрстка не считает.
   Markup: .calc > .calc__row(--sub|--total|--mult|--bonus|--zero) > span + .calc__val */
.calc { border: var(--rule) solid var(--c-line); background: var(--c-surface); }
.calc__row {
  display: grid; grid-template-columns: 1fr auto; gap: var(--sp-3);
  align-items: baseline;
  padding: var(--sp-3) var(--sp-4);
  border-bottom: var(--rule-hair) solid var(--c-line-soft);
}
.calc__row > span { font-size: var(--fs-sm); }
.calc__val {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h2); font-variant-numeric: tabular-nums; white-space: nowrap;
}
/* Множитель — холодным цветом: его нельзя спутать со слагаемым. */
.calc__row--mult .calc__val { color: var(--c-cyan); }
.calc__row--zero .calc__val { color: var(--c-ink-3); }
.calc__row--sub { background: var(--c-paper-2); border-top: var(--rule) solid var(--c-line); }
.calc__row--bonus .calc__val { color: var(--c-red); }
.calc__row--total {
  background: var(--c-ink); color: var(--c-white);
  border-bottom: 0; padding: var(--sp-3) var(--sp-4) var(--sp-4);
  align-items: center;
}
.calc__row--total > span { font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-xs); letter-spacing: var(--ls-label); text-transform: uppercase; }
.calc__row--total .calc__val { font-size: var(--fs-d2); color: var(--c-white); }

/* .led — строка леджера очков: причина слева, подписанное движение справа. */
.led__delta {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h2); font-variant-numeric: tabular-nums; color: var(--c-red);
}
.led__reason { font-size: var(--fs-sm); }
.led__date { font-family: var(--font-mono); font-size: var(--fs-micro); color: var(--c-ink-3); }

/* .mark-plate — отметка тренера «отлично отработал»: охряная плашка с очками. */
.mark-plate {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
  flex-wrap: wrap;
  padding: var(--sp-3) var(--sp-4);
}
.mark-plate b {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h2); text-transform: uppercase; letter-spacing: var(--ls-head);
}
.mark-plate span { font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h1); font-variant-numeric: tabular-nums; }

/* ---------- 28. LEADERBOARD ------------------------------------------------- */

/* .honour — «доска почёта»: рамка с толстым кантом, ink-шапка и три места
   передовиков недели. Markup: .honour > .honour__head + .honour__grid > .laureate */
.honour {
  position: relative;
  border: var(--rule-thick) solid var(--c-line);
  background: var(--c-surface);
  box-shadow: var(--plate-red);
}
.honour__head {
  display: flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-3);
  background: var(--c-ink); color: var(--c-white);
  overflow: hidden;
}
.honour__title {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h2); text-transform: uppercase; letter-spacing: var(--ls-head);
  white-space: nowrap;
}
.honour__tail { flex: 1 1 auto; align-self: stretch; min-width: 16px; color: var(--c-red); }
.honour__count {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-micro); letter-spacing: var(--ls-label); text-transform: uppercase;
  color: var(--c-paper-3); white-space: nowrap;
}
.honour__grid { display: grid; grid-template-columns: repeat(3, 1fr); }

/* .laureate — портрет-плашка на доске почёта: место, монограмма, фамилия, очки. */
.laureate {
  display: grid; justify-items: center; gap: var(--sp-1);
  padding: var(--sp-3) var(--sp-2);
  border-right: var(--rule-hair) solid var(--c-line-soft);
  text-align: center;
}
.laureate:last-child { border-right: 0; }
.laureate--first { background: var(--c-paper-2); }
.laureate__place {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-d2); line-height: 1; font-variant-numeric: tabular-nums;
  color: var(--c-ink-3);
}
.laureate--first .laureate__place { color: var(--c-red); }
/* Красная монограмма на красной подложке не читается — первому даём ink-плашку. */
.laureate--first .monogram { box-shadow: var(--plate); }
.laureate__name {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-sm); text-transform: uppercase; letter-spacing: var(--ls-head);
  line-height: var(--lh-head);
}
.laureate__pts {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h2); line-height: 1; font-variant-numeric: tabular-nums;
}
.laureate__cap {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-micro); letter-spacing: var(--ls-label); text-transform: uppercase;
  color: var(--c-ink-2);
}

/* .rank-row — строка рейтинга: рельс места, имя с фактами, очки.
   .is-me — своя строка; .rank-row--pin — своя строка, прижатая к низу экрана. */
.rank-row {
  display: grid; grid-template-columns: 44px 1fr auto;
  align-items: center; gap: var(--sp-3);
  min-height: 56px;
  padding-right: var(--sp-3);
  background: var(--c-surface);
  border-bottom: var(--rule-hair) solid var(--c-line-soft);
}
.rank-row:last-child { border-bottom: 0; }
.rank-row__place {
  align-self: stretch;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1px;
  background: var(--c-paper-2);
  border-right: var(--rule) solid var(--c-line);
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h2); font-variant-numeric: tabular-nums; line-height: 1;
}
.rank-row__delta {
  font-family: var(--font-display); font-size: var(--fs-micro); line-height: 1;
  color: var(--c-ink-2);
}
.rank-row__delta.is-up { color: var(--c-green); }
.rank-row__name {
  display: block;
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h2); text-transform: uppercase; letter-spacing: var(--ls-head);
  line-height: var(--lh-head);
}
.rank-row__meta { display: block; font-size: var(--fs-sm); color: var(--c-ink-2); }
.rank-row__pts {
  text-align: right;
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: 30px; line-height: 1; font-variant-numeric: tabular-nums;
}
.rank-row__pts span {
  display: block;
  font-size: var(--fs-micro); letter-spacing: var(--ls-label); color: var(--c-ink-2);
}
.rank-row.is-me { background: var(--c-red-wash); }
.rank-row.is-me .rank-row__place { background: var(--c-red); color: var(--c-white); }
.rank-row--pin {
  position: sticky; bottom: 0; z-index: var(--z-sticky);
  background: var(--c-ink); color: var(--c-white);
  border-top: var(--rule-thick) solid var(--c-red);
  border-bottom: 0;
}
.rank-row--pin .rank-row__place { background: var(--c-red); color: var(--c-white); border-right-color: var(--c-red-deep); }
.rank-row--pin .rank-row__meta,
.rank-row--pin .rank-row__pts span,
.rank-row--pin .rank-row__delta { color: var(--c-paper-3); }

/* ---------- 29. TEAMS ------------------------------------------------------- */

/* .emblem — эмблема команды. Путь фигуры приходит данными (team.emblemPath),
   экран не выбирает форму по id. --sm для строк таблицы. */
.emblem {
  flex: none;
  width: 56px; height: 56px;
  display: grid; place-items: center;
  background: var(--c-ink); color: var(--c-white);
  box-shadow: var(--plate-red);
}
.emblem svg { width: 68%; height: 68%; }
.emblem--sm { width: 30px; height: 30px; box-shadow: none; }
.emblem--paper { background: var(--c-paper-2); color: var(--c-ink); box-shadow: none; }

/* .team-head — эмблема + название + подпись. Живёт внутри .hero. */
.team-head { position: relative; display: grid; grid-template-columns: auto 1fr; gap: var(--sp-3); align-items: center; }
.team-head__name {
  display: block;
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-d2); text-transform: uppercase; letter-spacing: var(--ls-head);
  line-height: var(--lh-head);
}
.team-head__sub { display: block; font-size: var(--fs-sm); color: var(--c-paper-2); margin-top: 2px; }

/* .mate-row — строка состава: монограмма, имя, норма недели и абсолютный вклад.
   Долей и внутренних мест здесь нет и быть не должно. */
.mate-row {
  display: grid; grid-template-columns: auto 1fr auto; gap: var(--sp-3);
  align-items: center;
  min-height: 64px;
  padding: var(--sp-3);
  background: var(--c-surface);
  border-bottom: var(--rule-hair) solid var(--c-line-soft);
}
.mate-row:last-child { border-bottom: 0; }
.mate-row.is-me { background: var(--c-paper-2); }
.mate-row__name {
  display: block;
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h2); text-transform: uppercase; letter-spacing: var(--ls-head);
  line-height: var(--lh-head);
}
.mate-row__meta { display: block; font-size: var(--fs-sm); color: var(--c-ink-2); }
.mate-row__norm { width: 68px; height: var(--sp-2); margin-top: var(--sp-2); }
.mate-row__pts {
  text-align: right;
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: 30px; line-height: 1; font-variant-numeric: tabular-nums;
}
.mate-row__pts span {
  display: block;
  font-size: var(--fs-micro); letter-spacing: var(--ls-label); text-transform: uppercase;
  color: var(--c-ink-2);
}

/* .tour-row — строка недельной таблицы. Крупное число — СРЕДНЕЕ на участника;
   сумма и состав идут мелко в подписи, чтобы набор людей не выглядел тактикой. */
.tour-row {
  display: grid; grid-template-columns: 40px auto 1fr auto; gap: var(--sp-3);
  align-items: center;
  min-height: 60px;
  padding: var(--sp-2) var(--sp-3) var(--sp-2) 0;
  background: var(--c-surface);
  border-bottom: var(--rule-hair) solid var(--c-line-soft);
}
.tour-row:last-child { border-bottom: 0; }
.tour-row__place {
  align-self: stretch;
  display: grid; place-items: center;
  background: var(--c-paper-2);
  border-right: var(--rule) solid var(--c-line);
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h2); font-variant-numeric: tabular-nums;
}
.tour-row__name {
  display: block;
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h2); text-transform: uppercase; letter-spacing: var(--ls-head);
  line-height: var(--lh-head);
}
.tour-row__meta { display: block; font-size: var(--fs-sm); color: var(--c-ink-2); }
.tour-row__avg {
  text-align: right;
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: 30px; line-height: 1; font-variant-numeric: tabular-nums;
}
.tour-row__avg span {
  display: block;
  font-size: var(--fs-micro); letter-spacing: var(--ls-label); text-transform: uppercase;
  color: var(--c-ink-2);
}
.tour-row.is-mine { background: var(--c-red-wash); }
.tour-row.is-mine .tour-row__place { background: var(--c-red); color: var(--c-white); }

/* .teamcard — команда, которая ищет участников (состояние «вы без команды»). */
.teamcard {
  display: grid; grid-template-columns: auto 1fr auto; gap: var(--sp-3);
  align-items: center;
  padding: var(--sp-3);
  background: var(--c-surface);
  border: var(--rule) solid var(--c-line);
  box-shadow: var(--plate);
  margin-bottom: var(--sp-4);
}
.teamcard__name {
  display: block;
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h2); text-transform: uppercase; letter-spacing: var(--ls-head);
  color: var(--c-red);
}
.teamcard__meta { display: block; font-size: var(--fs-sm); color: var(--c-ink-2); }

/* .teamcard__head — название + девиз одной колонкой, метка мест справа.
   Боковой отступ ТОТ ЖЕ, что у .list__row под ней (--sp-4): без него
   название висит на самом краю плашки, а цифры под ним — с отступом. */
.teamcard__head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
}
/* «4 места» — счётчик, а не фраза: перенос превращает его в две строки
   и ломает выключку шапки. */
.teamcard__head .tag { white-space: nowrap; }

/* .card--form — плашка с формой. У .card своих отступов НЕТ: их дают вложенные
   .list__row и .teamcard__head. Форме их дать некому, поэтому подписи и заголовок
   упираются в край плашки. */
.card--form { padding: var(--sp-4); }

/* Строка списка, которая несёт не только значение, но и действие
   («Состав 2 / 6 · показать»). .list__val сам по себе выровнен по правому
   краю — здесь нужен ряд. */
.list__val--action { display: flex; align-items: center; gap: var(--sp-3); }

/* ---------- 30. CHALLENGE --------------------------------------------------- */

/* .goal / .goalbar — общий прогресс клуба к цели месяца с отметкой графика.
   --fill и --pace приходят данными (percentDone / percentPace), не вёрсткой. */
.goal { position: relative; padding-top: var(--sp-5); }
.goal__pacelab {
  position: absolute; top: 0; left: var(--pace);
  transform: translateX(-50%);
  white-space: nowrap; color: var(--c-ink-2);
}
.goalbar {
  position: relative;
  height: 34px;
  border: var(--rule) solid var(--c-line);
  background: var(--c-paper-2);
}
.goalbar__fill {
  position: absolute; top: 0; bottom: 0; left: 0;
  width: var(--fill);
  background: var(--c-red);
  color: var(--c-red-deep);
}
.goalbar__pace {
  position: absolute; top: calc(var(--sp-2) * -1); bottom: calc(var(--sp-2) * -1);
  left: var(--pace);
  width: var(--rule-thick);
  margin-left: calc(var(--rule-thick) / -2);
  background: var(--c-ink);
}
.goal__scale {
  display: flex; justify-content: space-between; gap: var(--sp-3);
  margin-top: var(--sp-2);
  color: var(--c-ink-2);
}

/* .contrib — лента вкладов. Не рейтинг: порядок по времени последнего вклада,
   числа абсолютные. Горизонтальный скролл, как .chiprow. */
.contrib {
  display: flex; gap: var(--sp-3);
  overflow-x: auto;
  padding: var(--sp-1) var(--gutter) var(--sp-3);
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.contrib::-webkit-scrollbar { display: none; }
.contrib__item {
  flex: none; width: 100px;
  display: grid; justify-items: center; gap: 2px;
  text-align: center;
}
.contrib__km {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h2); line-height: 1; font-variant-numeric: tabular-nums;
  margin-top: var(--sp-2);
}
.contrib__name { font-size: var(--fs-sm); line-height: 1.2; }
.contrib__last {
  font-family: var(--font-display); font-size: var(--fs-micro);
  letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--c-ink-2);
}
.contrib__rest {
  flex: none; width: 120px; align-self: center;
  font-size: var(--fs-sm); color: var(--c-ink-2);
}

/* .stamp--result — печать «цель взята» в углу плаката-итога, а не по центру. */
.stamp--result {
  top: auto; left: auto;
  right: var(--gutter); bottom: var(--sp-6);
  transform: rotate(-11deg);
}
/* На красном поле зелёная печать не проходит по контрасту — выбиваем бумагой. */
.hero--red .stamp--result { color: var(--c-white); }

/* ---------- 31. SEASON WRAP ------------------------------------------------- */

/* .wrapcard — печатная карточка итога: ink-рамка, красная плита, диагональный срез
   и halftone-поле в шапке. Внутри живут только собственные элементы карточки,
   чтобы её можно было целиком отдать в шаринг.
   Markup: .wrapcard > .wrapcard__head (> .wrapcard__kicker .wrapcard__title
   .wrapcard__sub .wrapcard__strip) + .wrapcard__body. */
.wrapcard {
  position: relative;
  background: var(--c-surface);
  border: var(--rule-thick) solid var(--c-line);
  box-shadow: var(--plate-red);
}
.wrapcard__head {
  position: relative;
  overflow: hidden;
  padding: var(--sp-4) var(--sp-4) var(--sp-5);
  background: var(--c-ink);
  color: var(--c-white);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--cut)), 0 100%);
}
.wrapcard__dots {
  position: absolute; inset: 0;
  color: var(--c-red);
  opacity: 0.55;
  pointer-events: none;
}
.wrapcard__kicker {
  position: relative;
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
  color: var(--c-paper-3);
}
.wrapcard__title {
  position: relative;
  margin-top: var(--sp-2);
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-d1); line-height: var(--lh-head);
  text-transform: uppercase; letter-spacing: var(--ls-head);
}
.wrapcard__title em { font-style: normal; color: var(--c-red); }
.wrapcard__sub { position: relative; margin-top: var(--sp-2); color: var(--c-paper-2); font-size: var(--fs-sm); }
/* Сезонный счёт стоит в шапке отдельной строкой фактов: он обнулится, и это
   должно быть видно рядом с датой закрытия, а не в теле карточки. */
.wrapcard__strip {
  position: relative;
  display: flex; flex-wrap: wrap; gap: var(--sp-5);
  margin-top: var(--sp-3); padding-top: var(--sp-3);
  border-top: var(--rule) solid var(--c-ink-3);
}
.wrapcard__fact b {
  display: block;
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h1); line-height: 1; font-variant-numeric: tabular-nums;
}
.wrapcard__fact span {
  display: block;
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-micro); letter-spacing: var(--ls-label); text-transform: uppercase;
  color: var(--c-paper-3);
}
.wrapcard__body { padding: var(--sp-4); }

/* .factgrid — печатная сетка «цифры сезона»: равные ячейки с волосяными рулями.
   Markup: .factgrid > .fact > (b + span). */
.factgrid {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  border: var(--rule) solid var(--c-line);
  background: var(--c-surface);
}
.fact {
  min-width: 0;
  padding: var(--sp-3) var(--sp-2);
  border-right: var(--rule-hair) solid var(--c-line-soft);
  text-align: center;
}
.fact:last-child { border-right: 0; }
.fact > b {
  display: block;
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-d1); line-height: var(--lh-tight); letter-spacing: var(--ls-score);
  font-variant-numeric: tabular-nums;
}
.fact > span {
  display: block; margin-top: var(--sp-2);
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-micro); letter-spacing: var(--ls-label); text-transform: uppercase;
  color: var(--c-ink-2);
}
@media (max-width: 379px) { .fact > b { font-size: var(--fs-d2); } }

/* .ledger — что переносится и что обнуляется. Блок «остаётся» печатается ink-рулём,
   блок «обнулилось» — барьерной штриховкой: разница видна раньше, чем прочитан текст.
   Markup: .ledger > .ledger__col[.is-reset] > (.ledger__head + ul > li). */
.ledger { display: grid; gap: var(--sp-3); }
.ledger__col {
  padding: var(--sp-3) var(--sp-4);
  background: var(--c-surface);
  border: var(--rule) solid var(--c-line);
  border-left-width: var(--rule-heavy);
}
.ledger__col.is-reset {
  border-left-color: var(--c-ink-3);
  background-color: var(--c-paper-2);
  color: var(--c-ink-2);
}
.ledger__head {
  display: flex; align-items: center; gap: var(--sp-2);
  margin-bottom: var(--sp-2);
}
.ledger__col li {
  padding: var(--sp-2) 0;
  border-bottom: var(--rule-hair) solid var(--c-line-soft);
  font-size: var(--fs-sm);
}
.ledger__col li:last-child { border-bottom: 0; }

/* .nextseason — узкая ink-полоса «сезон, который уже идёт»: счёт сезонных очков
   начат заново, поэтому он подаётся отдельно от карточки закрытого сезона. */
.nextseason {
  display: grid; grid-template-columns: 1fr auto; gap: var(--sp-3); align-items: center;
  padding: var(--sp-3) var(--sp-4);
  background: var(--c-ink); color: var(--c-white);
  box-shadow: var(--plate-cyan);
}
.nextseason b {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-d2); line-height: 1; font-variant-numeric: tabular-nums;
  color: var(--c-cyan-bright);
}

/* ---------- 32. YEAR POSTER ------------------------------------------------- */

/* .poster — годовой отчёт как печатный лист: тяжёлая рамка, внутренний волосяной
   контур, приводочные кресты по углам. Всё внутри рамки нарисовано собственными
   элементами постера и красится через локальные переменные (ниже), чтобы лист
   можно было напечатать в другом колорите одним классом. Значения переменных —
   только токены, ни одного нового цвета. (Шапка входа — .entry-head, §24.)

   Markup: .poster > (.poster__reg ×4) + .poster__top + .poster__yearrow
           + .poster__grid + .poster__band + .poster__streak + .poster__pair
           + .poster__scale + .poster__foot */
.poster {
  --pg: var(--c-paper);      /* бумага      */
  --pk: var(--c-ink);        /* краска      */
  --pa: var(--c-red);        /* акцент      */
  --pb: var(--c-ochre);      /* второй цвет */
  --pn: var(--c-white);      /* выворотка на акценте */
  --pm: var(--c-ink-2);      /* приглушённый текст   */

  position: relative;
  isolation: isolate;
  padding: var(--sp-5) var(--sp-4) var(--sp-4);
  background: var(--pg);
  color: var(--pk);
  border: var(--rule-heavy) solid var(--pk);
  box-shadow: var(--misreg-x) var(--misreg-y) 0 0 var(--pa);
  overflow: hidden;
}
/* Ночная печать: тот же лист, другой прогон краски. */
.poster--night {
  --pg: var(--c-ink);
  --pk: var(--c-paper);
  --pa: var(--c-ochre);
  --pb: var(--c-cyan-bright);
  --pn: var(--c-ink);
  --pm: var(--c-paper-3);
  box-shadow: var(--misreg-x) var(--misreg-y) 0 0 var(--c-red);
}
/* Внутренний волосяной контур — «поле» листа. */
.poster::before {
  content: "";
  position: absolute; inset: 5px;
  border: var(--rule-hair) solid var(--pk);
  opacity: 0.45;
  pointer-events: none;
  z-index: 1;
}
/* Приводочный крест — типографская метка совмещения плат; печатается второй
   краской (--pb), как и положено метке приводки. */
.poster__reg {
  position: absolute; width: 14px; height: 14px; z-index: 1; pointer-events: none;
  background-image:
    linear-gradient(var(--pb) 0 0), linear-gradient(var(--pb) 0 0);
  background-size: 100% var(--rule-hair), var(--rule-hair) 100%;
  background-position: 50% 50%, 50% 50%;
  background-repeat: no-repeat;
  opacity: 0.75;
}
.poster__reg--tl { top: 10px; left: 10px; }
.poster__reg--tr { top: 10px; right: 10px; }
.poster__reg--bl { bottom: 10px; left: 10px; }
.poster__reg--br { bottom: 10px; right: 10px; }

.poster__top {
  position: relative; z-index: 2;
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
}
.poster__mark {
  background: var(--pa); color: var(--pn);
  padding: 2px var(--sp-2) 3px;
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: 22px; line-height: 1; letter-spacing: 0.01em; text-transform: uppercase;
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 6px), calc(100% - 6px) 100%, 0 100%);
}
.poster__label {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-micro); letter-spacing: var(--ls-label); text-transform: uppercase;
  color: var(--pm);
}
.poster__yearrow {
  position: relative; z-index: 2;
  display: flex; align-items: flex-end; justify-content: space-between; gap: var(--sp-3);
  margin-top: var(--sp-2);
}
.poster__year {
  flex: none; white-space: nowrap;
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: 82px; line-height: 0.8; letter-spacing: var(--ls-score);
  font-variant-numeric: tabular-nums;
}
.poster__owner {
  min-width: 0;
  text-align: right; font-size: var(--fs-sm); line-height: 1.25; color: var(--pm);
  padding-bottom: var(--sp-1);
}
.poster__owner b {
  display: block;
  font-family: var(--font-display); font-size: var(--fs-h2);
  text-transform: uppercase; letter-spacing: var(--ls-head); color: var(--pk);
}
.poster__rule {
  position: relative; z-index: 2;
  height: var(--rule-thick); background: var(--pa); margin: var(--sp-3) 0 var(--sp-4);
}

/* Сетка главных цифр: одна ячейка печатается акцентом — она и есть заголовок года. */
.poster__grid {
  position: relative; z-index: 2;
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--rule);
  background: var(--pk);
  border: var(--rule) solid var(--pk);
}
.poster__cell {
  min-width: 0;
  padding: var(--sp-3);
  background: var(--pg);
}
.poster__cell--hero { background: var(--pa); color: var(--pn); }
.poster__cell b {
  display: block;
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-d1); line-height: var(--lh-tight); letter-spacing: var(--ls-score);
  font-variant-numeric: tabular-nums;
}
.poster__cell span {
  display: block; margin-top: var(--sp-2);
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-micro); letter-spacing: var(--ls-label); text-transform: uppercase;
  color: var(--pm);
}
.poster__cell--hero span { color: var(--pn); opacity: 0.85; }

/* Полоса «с чего начал → чем закончил»: диагональ между двумя состояниями. */
.poster__band {
  position: relative; z-index: 2;
  display: grid; grid-template-columns: 1fr auto 1fr; gap: var(--sp-2);
  align-items: center;
  margin-top: var(--sp-4);
}
.poster__state { min-width: 0; }
.poster__state span {
  display: block;
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-micro); letter-spacing: var(--ls-label); text-transform: uppercase;
  color: var(--pm);
}
.poster__state b {
  display: block; margin-top: 2px;
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h2); text-transform: uppercase; letter-spacing: var(--ls-head);
}
.poster__state i { display: block; font-style: normal; font-size: var(--fs-sm); color: var(--pm); }
.poster__state--to { text-align: right; }
.poster__arrow {
  width: 26px; height: 14px; flex: none;
  background: var(--pa);
  clip-path: polygon(0 38%, 62% 38%, 62% 0, 100% 50%, 62% 100%, 62% 62%, 0 62%);
}

/* Серия недель: год как 52 деления, серия — залитый отрезок. */
.poster__streak { position: relative; z-index: 2; margin-top: var(--sp-4); }
.poster__ticks { display: flex; gap: 1px; height: 20px; }
.poster__ticks i { flex: 1; background: var(--pk); opacity: 0.22; }
.poster__ticks i.is-on { background: var(--pa); opacity: 1; }
.poster__cap {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-2);
  margin-top: var(--sp-2);
}
.poster__cap b {
  flex: none;
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h2); text-transform: uppercase; letter-spacing: var(--ls-head);
}
.poster__cap span {
  flex: 1; min-width: 0;
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-micro); letter-spacing: var(--ls-label); text-transform: uppercase;
  color: var(--pm); text-align: right;
}

/* Две одиночные строчки-факта в рамках. */
.poster__pair {
  position: relative; z-index: 2;
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-3);
  margin-top: var(--sp-4);
}
.poster__fact { border-top: var(--rule-thick) solid var(--pk); padding-top: var(--sp-2); min-width: 0; }
.poster__fact b {
  display: block;
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-d2); line-height: var(--lh-tight); font-variant-numeric: tabular-nums;
}
.poster__fact span { display: block; margin-top: var(--sp-2); font-size: var(--fs-sm); color: var(--pm); }

/* Масштабная линейка: километры года, приложенные к чему-то человеческому. */
.poster__scale { position: relative; z-index: 2; margin-top: var(--sp-5); }
.poster__ruler {
  position: relative; height: 18px;
  border-top: var(--rule) solid var(--pk);
  border-bottom: 0;
}
.poster__ruler::before, .poster__ruler::after {
  content: ""; position: absolute; top: 0; width: var(--rule); height: 12px; background: var(--pk);
}
.poster__ruler::before { left: 0; }
.poster__ruler::after  { right: 0; }
.poster__km {
  position: absolute; left: 50%; top: -12px; transform: translateX(-50%);
  background: var(--pk); color: var(--pg);
  padding: 2px var(--sp-3) 3px;
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h2); letter-spacing: var(--ls-head); white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.poster__scale b {
  display: block; margin-top: var(--sp-2);
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h1); text-transform: uppercase; letter-spacing: var(--ls-head);
}
.poster__scale span { display: block; font-size: var(--fs-sm); color: var(--pm); }

.poster__foot {
  position: relative; z-index: 2;
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
  margin-top: var(--sp-5); padding-top: var(--sp-2);
  border-top: var(--rule-hair) solid var(--pk);
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-micro); letter-spacing: var(--ls-label); text-transform: uppercase;
  color: var(--pm);
}
/* Печать года — круглая марка в углу листа. */
.poster__seal {
  width: 54px; height: 54px; flex: none;
  border: var(--rule) solid var(--pa); border-radius: var(--r-seal);
  display: grid; place-items: center; text-align: center; line-height: 1;
  color: var(--pa);
}
.poster__seal b { display: block; font-size: var(--fs-h2); font-variant-numeric: tabular-nums; }

@media (max-width: 379px) {
  .poster__year { font-size: 66px; }
  .poster__cell b { font-size: var(--fs-d2); }
}

/* ---------- 33. COACH FIELD SCREEN ----------------------------------------- */

/* Поверхность рассчитана на замёрзшую руку на улице: цели ≥ 56px, контраст
   максимальный, ввода с клавиатуры почти нет. Крупные органы ввода этого экрана —
   .tapseg (§6), .tapchip (§6), .stepper (§15) и .rpe (§15).
   .steplabel — номер шага в ink-квадрате перед заголовком блока. */
.steplabel {
  display: inline-grid; place-items: center;
  width: 26px; height: 26px; flex: none;
  background: var(--c-ink); color: var(--c-white);
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-sm); line-height: 1; font-variant-numeric: tabular-nums;
}

/* .rosterrow — строка участника: монограмма, имя, двухкнопочная отметка присутствия.
   .is-present / .is-noshow красят левый руль строки, чтобы список читался сверху.
   Markup: .rosterrow > .rosterrow__head (> .monogram + .rosterrow__name + .tapseg)
           + .rosterrow__extra */
.rosterrow {
  background: var(--c-surface);
  border: var(--rule) solid var(--c-line);
  border-left-width: var(--rule-heavy);
  border-left-color: var(--c-paper-3);
  margin-bottom: var(--sp-3);
}
.rosterrow.is-present { border-left-color: var(--c-green); }
.rosterrow.is-noshow  { border-left-color: var(--c-red); background: var(--c-paper-2); }
.rosterrow__head {
  display: grid; grid-template-columns: auto 1fr; gap: var(--sp-3);
  align-items: center;
  padding: var(--sp-3);
}
.rosterrow__name {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h2); text-transform: uppercase; letter-spacing: var(--ls-head);
}
.rosterrow__note { font-size: var(--fs-sm); color: var(--c-ink-2); }
.rosterrow__extra {
  grid-column: 1 / -1;
  padding: var(--sp-3);
  border-top: var(--rule) solid var(--c-line);
  background: var(--c-paper-2);
}
.rosterrow.is-noshow .rosterrow__extra { display: none; }

/* .tally — счётчик отметок в липкой шапке блока: сколько строк уже закрыто. */
.tally {
  position: sticky; top: 68px; z-index: var(--z-sticky);
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
  padding: var(--sp-2) var(--gutter);
  background: var(--c-ink); color: var(--c-white);
  border-bottom: var(--rule-thick) solid var(--c-red);
}
.tally b {
  font-family: var(--font-display); font-weight: var(--fw-strong);
  font-size: var(--fs-h1); line-height: 1; font-variant-numeric: tabular-nums;
}

/* ---------- 34. LAYOUT UTILITIES ------------------------------------------- */

.stack   { display: grid; gap: var(--sp-3); }
.stack-4 { display: grid; gap: var(--sp-4); }
.row     { display: flex; align-items: center; gap: var(--sp-2); }
.row--between { justify-content: space-between; }
.row--wrap { flex-wrap: wrap; }
.grow    { flex: 1 1 auto; min-width: 0; }
.pad     { padding-inline: var(--gutter); }
.mt-3 { margin-top: var(--sp-3); }
.mt-4 { margin-top: var(--sp-4); }
.mt-6 { margin-top: var(--sp-6); }
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
