/* ============================================================
   Flow Training SPA — theme tokens + shared component styles
   Token layer ported VERBATIM from the DROPS-gold design handoff
   (.planning/design/handoff-flow-training/tokens.css :root L11-89);
   the upstream Google-Fonts CDN import is intentionally NOT
   shipped — fonts are self-hosted (plan 02/03).
   Component CSS carried from the winning sketches
     (001-selection-grid A + 002-approach-screen A), re-pointed onto
   the DROPS-gold tokens (night + gold). No legacy brand tokens remain;
   the SDK runtime viewport var (set by telegram-web-app.js) is kept verbatim.
   ============================================================ */

/* ============================================================
   @font-face — self-hosted subset webfonts (plan 32-03 wiring)
   Real faces (one per committed weight) are CLEAN: NO size-adjust /
   ascent-override / descent-override / line-gap-override. The metric
   overrides live ONLY on the two fallback faces below. Subset woff2
   produced by scripts/subset_flow_fonts.sh (plan 32-02); served from
   /static/fonts/ (hard-cacheable; NOT /static/flow/ no-cache middleware).
   unicode-range = D-05 subset (Cyrillic + Latin + digits + punctuation).
   ============================================================ */
@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/static/fonts/manrope-400.woff2') format('woff2');
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+2000-206F,U+2070-209F,U+20A0-20BF,U+2122,U+2191,U+2193,U+2212,U+0400-045F,U+0490-0491,U+04B0-04B1;
}
@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/static/fonts/manrope-600.woff2') format('woff2');
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+2000-206F,U+2070-209F,U+20A0-20BF,U+2122,U+2191,U+2193,U+2212,U+0400-045F,U+0490-0491,U+04B0-04B1;
}
@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/static/fonts/manrope-700.woff2') format('woff2');
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+2000-206F,U+2070-209F,U+20A0-20BF,U+2122,U+2191,U+2193,U+2212,U+0400-045F,U+0490-0491,U+04B0-04B1;
}
@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url('/static/fonts/manrope-800.woff2') format('woff2');
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+2000-206F,U+2070-209F,U+20A0-20BF,U+2122,U+2191,U+2193,U+2212,U+0400-045F,U+0490-0491,U+04B0-04B1;
}
@font-face {
  font-family: 'Cormorant Garamond';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/static/fonts/cormorant-600.woff2') format('woff2');
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+2000-206F,U+2070-209F,U+20A0-20BF,U+2122,U+2191,U+2193,U+2212,U+0400-045F,U+0490-0491,U+04B0-04B1;
}
@font-face {
  font-family: 'Cormorant Garamond';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/static/fonts/cormorant-700.woff2') format('woff2');
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+2000-206F,U+2070-209F,U+20A0-20BF,U+2122,U+2191,U+2193,U+2212,U+0400-045F,U+0490-0491,U+04B0-04B1;
}

/* ===== Metric-matched fallback @font-face descriptors ===== */
/* Derived deterministically by scripts/derive_font_overrides.py   */
/* from the real subset woff2 files. Override descriptors go ONLY  */
/* on these fallback faces — never on the real webfont faces.      */
@font-face {
  font-family: 'Manrope Fallback';
  src: local('Arial');
  size-adjust: 132.531%;
  ascent-override: 80.434%;
  descent-override: 22.6362%;
  line-gap-override: 0%;
}
@font-face {
  font-family: 'Cormorant Fallback';
  src: local('Times New Roman');
  size-adjust: 133.747%;
  ascent-override: 69.0857%;
  descent-override: 21.4584%;
  line-gap-override: 0%;
}

/* ---- DROPS-gold design tokens (ported from handoff tokens.css :root) ---- */
:root {
  /* ---------- Ground (сцена ночью) ---------- */
  --night:        #0D1120;  /* мастер-фон (бренд --db-night) */
  --night-deep:   #070A12;  /* виньетка / провалы глубины */
  --night-spot:   #18213C;  /* тёплое пятно прожектора */

  /* ---------- Surfaces (тактильные карточки DROPS) ---------- */
  --surface:      #161D31;  /* карточка */
  --surface-2:    #1F2842;  /* приподнятая поверхность / hover */
  --surface-3:    #2A3354;  /* нажатая / активная */
  --hairline:     rgba(247,244,255,0.09);
  --hairline-2:   rgba(247,244,255,0.05);

  /* ---------- Gold (единственный акцент: выбор + CTA + прогресс) ---------- */
  --gold:         #F0C060;  /* яркое золото */
  --gold-deep:    #C8952A;  /* тёмное золото — обводки, caps */
  --champagne:    #FDE9A0;  /* верх градиента, искры */
  --gold-grad:    linear-gradient(135deg, #C8952A 0%, #F0C060 52%, #FDE9A0 100%);
  --gold-line:    rgba(240,192,96,0.42);
  --gold-line-2:  rgba(240,192,96,0.18);
  --on-gold:      #14110A;  /* текст на золоте */

  /* ---------- Status (семантика освоения — НЕ убирать) ---------- */
  --st-count:     #FF6B6B;  /* 🔴 под счёт */
  --st-pair:      #FFCC4D;  /* 🟡 в паре */
  --st-music:     #4DD97A;  /* 🟢 в музыку */
  --st-free:      #C08BFF;  /* ✨ свободно */
  --finish:       #4DD97A;  /* завершение тренировки (зелёный, семантика) */

  /* ---------- Foreground ---------- */
  --fg1:      #F7F4FF;                     /* первичный текст (имена всегда читаемы) */
  --fg2:      rgba(247,244,255,0.64);      /* вторичный */
  --fg3:      rgba(247,244,255,0.40);      /* caps / третичный */
  --fg-mute:  rgba(247,244,255,0.26);      /* совсем тихий */

  /* ---------- Radii (DROPS-first: крупнее) ---------- */
  --r-card:  22px;
  --r-img:   18px;
  --r-sm:    14px;
  --r-chip:  11px;
  --r-pill:  999px;

  /* ---------- Spacing ---------- */
  --s-1: 4px;  --s-2: 8px;  --s-3: 12px; --s-4: 16px;
  --s-5: 20px; --s-6: 28px; --s-7: 40px; --s-8: 56px;

  /* ---------- Elevation ---------- */
  --sh-card:  0 10px 30px rgba(0,0,0,0.45), inset 0 0 0 1px var(--hairline-2);
  --sh-pop:   0 22px 55px rgba(0,0,0,0.60);
  --sh-press: 0 4px 14px rgba(0,0,0,0.40);
  --glow:     0 0 28px rgba(240,192,96,0.40);
  --glow-soft:0 0 16px rgba(240,192,96,0.26);
  /* gold selection ring + glow */
  --ring-gold: 0 0 0 2.5px var(--gold), 0 10px 30px rgba(240,192,96,0.30);

  /* ---------- Type ---------- */
  --font-ui:    'Manrope', 'Manrope Fallback', -apple-system, 'SF Pro Text', system-ui, sans-serif;
  --font-serif: 'Cormorant Garamond', 'Cormorant Fallback', Georgia, 'Times New Roman', serif; /* только «дорогие» моменты */

  --t-title:   26px;  /* заголовок экрана (сериф) */
  --t-h:       19px;  /* подзаголовки */
  --t-body:    15px;
  --t-label:   13px;
  --t-caps:    11px;
  --tracking-caps: 0.18em;

  /* ---------- Motion ---------- */
  --ease:        cubic-bezier(0.22, 1, 0.36, 1);   /* default out */
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);/* tactile pop */
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
  --dur-fast: 150ms;
  --dur-base: 260ms;
  --dur-slow: 460ms;

  /* reduced-motion master levers (D-09; flipped to 0 under prefers-reduced-motion — no Tweaks UI) */
  --motion: 1;
  --glow-k: 1;
  /* PERF-01 blur budget lever: full blur at 1, dampened toward 0 under the
     reduced-motion / iOS perf path. backdrop-filter radii read calc(base*var(--blur-k)). */
  --blur-k: 1;

  /* ---------- Project extensions (flow.css-only; not in handoff tokens.css) ---------- */
  --scrim:      rgba(7,10,18,0.60);      /* badge/overlay scrim over media (night-deep alpha) */
  --tint-count: rgba(255,107,107,0.16);  /* placeholder card tint — 🔴 derived from --st-count */
  --tint-pair:  rgba(255,204,77,0.16);   /* 🟡 derived from --st-pair */
  --tint-music: rgba(77,217,122,0.16);   /* 🟢 derived from --st-music */
  --tint-free:  rgba(192,139,255,0.16);  /* ✨ derived from --st-free */
}
* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
html, body { margin: 0; background: var(--night); color: var(--fg1); font-family: var(--font-ui); }

/* ---- screen state machine utility ---- */
.hidden { display: none !important; }
/* Telegram half-expanded mode: 100vh is the FULL WebView height, so anything
   anchored to it sits below the visible fold. --tg-viewport-stable-height (set
   by telegram-web-app.js) tracks the actually visible area. */
.screen { min-height: var(--tg-viewport-stable-height, 100vh); }

/* ============================================================
   Loading / error / empty state boxes
   ============================================================ */
.loading { display: flex; flex-direction: column; align-items: center; justify-content: center;
  min-height: var(--tg-viewport-stable-height, 100vh); gap: 14px; color: var(--fg2); }
/* DROPS-gold service screens: spinner ring reads from --gold-line-2 / --gold so
   the loading/error/empty screens match the reskinned main screens (EDGE-01/02). */
.spinner { width: 34px; height: 34px; border-radius: 50%;
  border: 3px solid var(--gold-line-2); border-top-color: var(--gold);
  box-shadow: var(--glow-soft); animation: spin .9s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }

.state-box { display: flex; flex-direction: column; align-items: center; justify-content: center;
  min-height: var(--tg-viewport-stable-height, 100vh); gap: 16px; padding: 0 32px; text-align: center; }
/* Serif heading on primary fg — on-brand "expensive moment" treatment matching
   the reskinned screen-heads. */
.state-heading { font-family: var(--font-serif); font-size: var(--t-title); font-weight: 700;
  color: var(--fg1); margin: 0; line-height: 1.15; }
.state-body { font-size: var(--t-body); line-height: 1.45; color: var(--fg2); margin: 0; max-width: 360px; }

/* ============================================================
   Shared header / main layout
   ============================================================ */
.flow-header { position: sticky; top: 0; z-index: 20; background: var(--night);
  padding: 14px 14px 10px; border-bottom: 1px solid var(--hairline); }
.flow-header .h-row { display: flex; align-items: baseline; justify-content: space-between; }
.flow-header h1 { font-size: 18px; margin: 0; font-weight: 700; }
.count { font-size: 13px; color: var(--gold); font-weight: 600; }
.count.zero { color: var(--fg2); font-weight: 400; }
.flow-main { padding: 12px 12px 96px; }

/* ============================================================
   Selection / board grid (sketch 001 variant A)
   ============================================================ */
.grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-3); }

/* animated placeholder "gif" (replaced by real <video> in JS) */
.gif { position: relative; overflow: hidden; aspect-ratio: 4/5; border-radius: var(--r-img);
  background: var(--surface-2);
  background-image: linear-gradient(115deg,transparent 30%,var(--hairline) 50%,transparent 70%);
  background-size: 220% 100%; animation: shimmer 2.6s linear infinite; }
@keyframes shimmer { to { background-position: -220% 0; } }
.gif video { width: 100%; height: 100%; object-fit: cover; display: block; }
.gif .play { position: absolute; top: 7px; left: 8px; font-size: 10px; color: var(--fg2); z-index: 2; }

/* selected ✓ badge + accent outline + glow (sketch 001 L53-69) */
.badge { position: absolute; top: 7px; right: 7px; width: 24px; height: 24px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center; font-size: 14px;
  background: var(--scrim); color: transparent; border: 2px solid var(--fg2); transition: var(--dur-fast); }
.sel .badge { background: var(--gold); border-color: var(--gold); color: var(--on-gold); }

.card { position: relative; cursor: pointer; transition: transform var(--dur-fast); }
.card:active { transform: scale(.97); }
.card .meta { display: flex; align-items: center; gap: 6px; padding: 7px 2px 0; }
.card .meta .nm { font-size: 13px; line-height: 1.25; overflow: hidden; }
.sel .gif { outline: 2.5px solid var(--gold); outline-offset: -2px; }
.card.sel { box-shadow: var(--ring-gold);
  border-radius: var(--r-card); }

/* passive status section dividers (D-05; NO interactive filter chips) */
.status-section { margin-top: 18px; }
.status-section:first-child { margin-top: 0; }
.section-head { display: flex; align-items: center; gap: 6px; padding: 6px 2px;
  font-size: 13px; color: var(--fg2); font-weight: 600; }

/* board per-session counter (D-10) */
.hit-count { font-size: 13px; color: var(--fg2); padding: 2px 2px 0; }

/* ============================================================
   Status-color classes — dots / placeholder tints
   ============================================================ */
.st-dot { width: 9px; height: 9px; border-radius: 50%; display: inline-block; flex: 0 0 auto; }
.st-raw { color: var(--st-count); }
.st-controlled { color: var(--st-pair); }
.st-indance { color: var(--st-music); }
.st-mastered { color: var(--st-free); }
.st-raw .st-dot, .st-dot.st-raw { background: var(--st-count); }
.st-controlled .st-dot, .st-dot.st-controlled { background: var(--st-pair); }
.st-indance .st-dot, .st-dot.st-indance { background: var(--st-music); }
.st-mastered .st-dot, .st-dot.st-mastered { background: var(--st-free); }
/* placeholder card tint (no-preview figure, D-08) */
.gif.placeholder.st-raw { background: var(--tint-count); }
.gif.placeholder.st-controlled { background: var(--tint-pair); }
.gif.placeholder.st-indance { background: var(--tint-music); }
.gif.placeholder.st-mastered { background: var(--tint-free); }

/* ============================================================
   Approach carousel (sketch 002 winner A)
   ============================================================ */
.approach-hdr { text-align: center; padding: 16px 14px 6px; }
.approach-lbl { font-size: 15px; color: var(--fg2); display: block; }
.approach-lbl b { color: var(--fg1); }

/* Stopwatch display — bigger than sketch A's compact line (refinement 1) */
.stopwatch { display: block; font-size: 46px; font-weight: 800; line-height: 1;
  font-variant-numeric: tabular-nums; color: var(--fg1); margin-top: 4px; }

.viewport { overflow: hidden; width: 100%; position: relative; touch-action: pan-y; height: calc(var(--tg-viewport-stable-height, 100vh) * 0.6); }
.track { display: flex; height: 100%;
  transition: transform var(--dur-base) var(--ease); will-change: transform; }
.slide { flex: 0 0 78%; height: 100%; padding: 0 1.6%; box-sizing: border-box; }
.slide .gif { height: 100%; width: 100%; aspect-ratio: auto; transition: opacity .3s, transform .3s; }
.slide:not(.cur) .gif { opacity: .4; transform: scale(.93); }
.slide.cur .gif { opacity: 1; }

/* "⬅ сейчас" now-badge on the auto-picked figure (D-05) */
.now-badge { position: absolute; top: 9px; right: 9px; z-index: 3;
  background: var(--gold); color: var(--on-gold); font-size: 11px; font-weight: 600;
  padding: 3px 9px; border-radius: 20px; }

/* current figure name + dots */
.fig-name { text-align: center; font-size: 19px; font-weight: 700; padding: 12px 14px 0; }
.nm-emo { font-size: 18px; }
.now-tag { font-size: 12px; color: var(--gold); font-weight: 600; white-space: nowrap; }
.dots { display: flex; gap: 7px; justify-content: center; padding: 10px 0; }
.dots span { position: relative; width: 7px; height: 7px; border-radius: 50%;
  background: var(--surface-3); cursor: pointer; transition: .2s; }
/* extend dot tap target to ≥44px without changing the visual */
.dots span::after { content: ""; position: absolute; top: 50%; left: 50%;
  width: 44px; height: 44px; transform: translate(-50%, -50%); }
.dots span.on { background: var(--gold); transform: scale(1.3); }

/* ============================================================
   Recall (Tinder swipe)
   ============================================================ */
/* Phase 33 SKIN-04 — RECALL chrome (prototype section[data-screen="recall"]).
   Ported from the handoff prototype (styles.css L150-182) onto DROPS tokens:
   gold legend + counter, full-bleed Tinder deck, the THREE-button action row
   (no/undo/yes), and a reskinned summary ring. The fixed position:fixed undo
   rule that leaked a `↩` arrow onto the approach screen is REMOVED — undo now
   lives in the in-row `.rbtn.undo`. The Phase-32 swipe physics (recall.js
   attachSwipe) + the `.recall-card`/`.tint`/`.stamp` rules below are untouched
   (Gate 5: .recall-card keeps touch-action:none). Wording stays «встроил/не
   встроил» (Phase-34 owns «Помню/Забыл»). */
#screen-recall { display: flex; flex-direction: column;
  min-height: var(--tg-viewport-stable-height, 100vh); overflow: hidden; }

/* Directional legend + gold progress counter (replaces the old .recall-hint). */
.recall-legend { display: flex; justify-content: space-between; align-items: center;
  padding: 16px 18px 10px; flex: none; }
.recall-legend .leg { display: flex; align-items: center; gap: 7px;
  font-size: 12.5px; font-weight: 700; }
.recall-legend .leg.no { color: var(--st-count); }
.recall-legend .leg.yes { color: var(--st-music); }
.recall-counter { font-size: var(--t-label); font-weight: 700; color: var(--fg2);
  font-variant-numeric: tabular-nums; }
.recall-counter b { color: var(--gold); }

/* Deck region grows to fill; the card hosts (.recall-stack) is the prototype .deck.
   VIEW-01: flex:1 absorbs leftover space, but the floor is a clamped min-height
   (not a rigid 0.5×stable) so on a half-collapsed (short) viewport the stack YIELDS
   height to the always-laid-out .recall-actions (flex:none) + .dock «Готово» rather
   than pushing them below the fold. position:relative is preserved so the absolutely
   positioned .tint/.stamp (inset:0) + .recall-card swipe contract resolve against it
   (Gate 5). min-height clamp keeps the deck meaningfully tall on roomy viewports. */
.recall-stack { position: relative; flex: 1 1 auto;
  min-height: clamp(150px, calc(var(--tg-viewport-stable-height, 100vh) * 0.32), 50vh);
  padding: 16px 14px; }

/* Three-button action row (prototype .recall-actions / .rbtn). Drives the SAME
   decide()/undo() sinks as the swipe — no parallel decision path. */
.recall-actions { flex: none; display: flex; align-items: center; justify-content: center;
  gap: 22px; padding: 4px 0 calc(16px + env(safe-area-inset-bottom)); }
.rbtn { width: 60px; height: 60px; border-radius: 50%; display: grid; place-items: center;
  background: var(--surface); border: 1px solid var(--hairline); box-shadow: var(--sh-card);
  cursor: pointer; transition: transform var(--dur-fast) var(--ease); }
.rbtn:active { transform: scale(.9); }
.rbtn:disabled { opacity: .4; cursor: default; transform: none; }
.rbtn.no { color: var(--st-count); }
.rbtn.yes { color: var(--st-music); width: 68px; height: 68px; }
.rbtn.undo { width: 48px; height: 48px; color: var(--fg2); }

/* Reskinned summary — prototype .recall-sum + gold .sum-ring (the live
   #recall-summary keeps its ID; prototype classes are layered alongside). */
.recall-summary { display: flex; flex-direction: column; align-items: center; justify-content: center;
  flex: 1; min-height: calc(var(--tg-viewport-stable-height, 100vh) * 0.5); gap: 16px; padding: 0 30px; text-align: center; }
.sum-ring { position: relative; width: 172px; height: 172px; margin-bottom: 10px; }
.sum-ring svg { transform: rotate(-90deg); }
.sum-ring .num { position: absolute; inset: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; }
.sum-ring .num b { font-family: var(--font-serif); font-weight: 700; font-size: 56px;
  line-height: .9; color: var(--fg1); font-variant-numeric: tabular-nums; }
.sum-ring .num span { font-size: 12px; font-weight: 700; text-transform: uppercase;
  letter-spacing: var(--tracking-caps); color: var(--fg3); margin-top: 4px; }
.sum-title { font-family: var(--font-serif); font-weight: 700; font-size: 27px; color: var(--fg1); }
.sum-sub { font-size: 14px; color: var(--fg2); margin-top: 6px; }

/* Recall gesture CSS contract (plan 04 buildCard attaches to these exact selectors).
   Cards are class .recall-card + a lifted .gif; stamp wording is owned by JS, not CSS.
   Base card rule ported from the handoff prototype `.rcard` (styles.css:159): without
   it (1) `touch-action: none` is absent so the browser hijacks the horizontal pointer
   drag → swipe glitches/dead on touch; (2) the card is unpositioned so the absolutely
   positioned `.tint`/`.stamp` (inset:0) resolve against `.recall-stack` (½-screen), and
   a partially-dragged red `.tint.no` washes the whole screen. position:relative (not the
   prototype's absolute full-bleed) preserves plan-04's lifted-.gif + .fig-name layout. */
.recall-card { position: relative; touch-action: none; will-change: transform;
  border-radius: var(--r-card); overflow: hidden; background: var(--surface); box-shadow: var(--sh-pop); }
.recall-card .stamp { position: absolute; top: 24px; padding: 8px 16px; border-radius: var(--r-chip);
  font-size: 20px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase;
  border: 3px solid currentColor; opacity: 0; pointer-events: none; z-index: 4; }
.recall-card .stamp.yes { right: 20px; color: var(--st-music); transform: rotate(12deg) scale(1.1); }
.recall-card .stamp.no  { left: 20px;  color: var(--st-count); transform: rotate(-12deg) scale(1.1); }
.recall-card .tint { position: absolute; inset: 0; opacity: 0; pointer-events: none; z-index: 1; }
.recall-card .tint.yes { background: radial-gradient(circle at 70% 50%, rgba(77,217,122,.35), transparent 70%); }
.recall-card .tint.no  { background: radial-gradient(circle at 30% 50%, rgba(255,107,107,.35), transparent 70%); }

/* ============================================================
   Bingo result
   ============================================================ */
.bingo-body { padding: 24px 18px 96px; }

/* ============================================================
   Sticky bottom CTA (sketch 001 L96-102 / 002 L101-107 — locked 16px/700)
   ============================================================ */
.cta-wrap { position: fixed; left: 0; right: 0; z-index: 30;
  top: var(--tg-viewport-stable-height, 100vh); transform: translateY(-100%);
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  background: linear-gradient(to top, var(--night) 70%, transparent); padding: 12px 14px 16px; }
.cta-wrap-stack { gap: 8px; }
.cta { width: 100%; max-width: 404px; padding: 16px; border-radius: var(--r-sm); border: none;
  font-size: 16px; font-weight: 700; background: var(--gold); color: var(--on-gold);
  cursor: pointer; transition: var(--dur-fast); min-height: 44px; }
.cta:active { background: var(--gold-deep); transform: scale(.98); }
.cta:disabled { background: var(--surface-2); color: var(--fg2); cursor: default; transform: none; }
.cta-secondary { background: var(--surface); color: var(--fg1); }
.cta-secondary:active { background: var(--surface-2); }
/* Service-box CTA («Повторить»/«Закрыть»): keep the static/centered max-width
   layout so it sits inside .state-box, but adopt the gold-grad fill + --on-gold
   glyph + pill radius of the main-screen CTAs (EDGE-01/02 reskin). */
.cta-inline { position: static; max-width: 320px; min-width: 200px;
  background: var(--gold-grad); color: var(--on-gold); border-radius: var(--r-pill);
  box-shadow: var(--glow); margin-top: var(--s-2); }
.cta-inline:active { background: var(--gold-deep); box-shadow: var(--glow-soft); transform: scale(.98); }
.cta-green { background: var(--finish); }
.cta-green:active { background: var(--gold-deep); }

/* (Recall direction legend now lives in .recall-legend above — the old
   .recall-hint block was replaced by the prototype legend in SKIN-04.) */

/* ============================================================
   SELECT skeleton CSS contract (plan 05 skeleton.js attaches to these
   exact class names). Reuses the existing shimmer keyframe defined above
   (no new keyframe authored). .sk-media mirrors .gif's 4:5 box so the real
   <video> swap-in causes zero layout shift.
   ============================================================ */
.skel { position: relative; overflow: hidden; background: var(--surface-2);
  background-image: linear-gradient(100deg, transparent 20%, var(--hairline) 38%, var(--gold-line-2) 50%, var(--hairline) 62%, transparent 80%);
  background-size: 220% 100%; background-repeat: no-repeat;
  animation: shimmer 1.25s linear infinite; }
.fig-skel { border-radius: var(--r-card); background: var(--surface); overflow: hidden;
  box-shadow: var(--sh-card); padding-bottom: 14px; }
.sk-media { aspect-ratio: 4/5; border-radius: var(--r-img); }
.sk-line { height: 13px; margin: 12px 14px 0; border-radius: var(--r-sm); width: 72%;
  background: var(--surface-2); }
.skel-sec { display: flex; align-items: center; gap: 6px; padding: 6px 2px; opacity: .6; }
.skel-sec .dot { opacity: .55; }
.skel-sec .lbl { color: var(--fg3); }
.skel-sec .rule { flex: 1; height: 1px; background: var(--hairline); }

/* ============================================================
   Reduced-motion master gate (D-09). --motion:0 alone does NOT stop a
   bare @keyframes — decorative anims must be explicitly halted. Functional
   transitions (swipe fling/snap, screen toggle) intentionally STAY.
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  :root { --motion: 0; --glow-k: 0; --blur-k: 0; }
  .gif, .skel, .timer.tick, .conf { animation: none; }
  /* PERF-01: drop the carousel hero "ken-burns"-style scale/transition on video
     heroes so non-current slides stop animating decode-heavy transforms. */
  .slide .gif { transition: none; }
  .slide:not(.cur) .gif { transform: none; }
}

/* ============================================================
   Phase 33 SKIN-01 — SELECT + shared primitives
   Ported from the DROPS-gold handoff prototype
   (.planning/design/handoff-flow-training/styles.css) re-pointed onto the
   flow.css DROPS tokens (no re-added TG-blue brand token, no hex literals
   beyond the rgba scrim/border alphas the prototype itself uses). The shared bits
   (.screen-head / .dock / .cta-gold / .sec) are reused by screen plans
   02–05; the .fig card is SELECT-specific.
   ============================================================ */

/* SELECT shell: column layout, scroll region grows, dock pinned at bottom.
   Scoped to the id — does NOT touch the global `.screen` state-machine rule.
   `.hidden { display:none !important }` still wins when the screen is off. */
#screen-selection { display: flex; flex-direction: column;
  min-height: var(--tg-viewport-stable-height, 100vh); }
#screen-selection .scroll { flex: 1; overflow-y: auto; overflow-x: hidden;
  padding: 18px 16px 24px; -webkit-overflow-scrolling: touch; scrollbar-width: none; }
#screen-selection .scroll::-webkit-scrollbar { display: none; }

/* Shared screen header — serif title + sub + gold count-chip */
.screen-head { display: flex; align-items: flex-end; justify-content: space-between;
  margin-bottom: 14px; gap: 12px; }
.screen-title { font-family: var(--font-serif); font-weight: 700; font-size: var(--t-title);
  letter-spacing: -.01em; line-height: 1; color: var(--fg1); }
.screen-sub { font-size: 12.5px; color: var(--fg3); font-weight: 600; margin-top: 5px; }
.count-chip { font-size: var(--t-label); font-weight: 700; color: var(--fg3);
  padding: 6px 13px; border-radius: var(--r-pill); background: var(--surface);
  border: 1px solid var(--hairline); white-space: nowrap;
  transition: color var(--dur-base) var(--ease), background var(--dur-base) var(--ease),
    box-shadow var(--dur-base) var(--ease); }
.count-chip.active { color: var(--on-gold); background: var(--gold-grad);
  border-color: transparent; box-shadow: var(--glow-soft); }

/* Shared section divider — dot + caps label + count + gradient hairline.
   Status accents: 🔴 raw reads as a DROPS status dot/label, NOT a red error wall. */
.sec { display: flex; align-items: center; gap: 10px; margin: 22px 2px 12px; }
.sec:first-child { margin-top: 2px; }
.sec .dot { width: 9px; height: 9px; border-radius: 50%; flex: none;
  background: currentColor; box-shadow: 0 0 10px currentColor; }
.sec .lbl { font-size: var(--t-caps); font-weight: 700; text-transform: uppercase;
  letter-spacing: var(--tracking-caps); color: var(--fg2); white-space: nowrap; }
.sec .num { font-size: var(--t-caps); font-weight: 700; color: var(--fg-mute); }
.sec .rule { flex: 1; height: 1px; background: linear-gradient(90deg, var(--hairline), transparent); }
.c-count { color: var(--st-count); }
.c-pair  { color: var(--st-pair); }
.c-music { color: var(--st-music); }
.c-free  { color: var(--st-free); }

/* SELECT figure card — 4:5 video media + corner status marker + gold pick ring */
.fig { position: relative; border-radius: var(--r-card); background: var(--surface);
  overflow: hidden; box-shadow: var(--sh-card); -webkit-tap-highlight-color: transparent;
  cursor: pointer;
  transition: transform var(--dur-fast) var(--ease), box-shadow var(--dur-base) var(--ease); }
.fig:active { transform: scale(.965); }
/* media box holds the lifted factory `.gif` frame (matches its 4:5 ratio) */
.fig .media { position: relative; aspect-ratio: 3/4; overflow: hidden; }
.fig .media .gif { width: 100%; height: 100%; aspect-ratio: auto; border-radius: 0; }
.fig .media::after { content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(to top, rgba(8,10,18,.92) 0%, rgba(8,10,18,.30) 38%, transparent 62%); }
/* Status marker: round dot ONLY (no text label). A small blurred dark disc backs
   the coloured dot so it stays legible over any video frame. */
.fig .marker { position: absolute; top: 10px; left: 10px; z-index: 3;
  width: 22px; height: 22px; border-radius: 50%; display: grid; place-items: center;
  background: var(--scrim);
  backdrop-filter: blur(calc(8px * var(--blur-k)));
  -webkit-backdrop-filter: blur(calc(8px * var(--blur-k)));
  border: 1px solid var(--hairline); }
.fig .marker i { width: 9px; height: 9px; border-radius: 50%; flex: none;
  background: currentColor; box-shadow: 0 0 8px currentColor; }
.fig .check { position: absolute; top: 10px; right: 10px; z-index: 3;
  width: 30px; height: 30px; border-radius: 50%; background: var(--scrim);
  border: 1.5px solid rgba(247,244,255,.45); display: grid; place-items: center; color: var(--fg1);
  transition: transform var(--dur-base) var(--ease-spring), background var(--dur-fast), border-color var(--dur-fast); }
.fig .check svg { width: 16px; height: 16px; opacity: 0; transform: scale(.4);
  transition: opacity var(--dur-fast), transform var(--dur-base) var(--ease-spring); }
.fig .name { position: absolute; left: 12px; right: 12px; bottom: 11px; z-index: 3;
  font-size: 13.5px; font-weight: 700; line-height: 1.2; color: var(--fg1);
  text-shadow: 0 2px 8px rgba(0,0,0,.6); }
/* D-13 source-plan caption on a SELECT card. Lives INSIDE the bottom overlay so
   the overlay grows upward and can never overprint the figure name (the .fig
   bottom-anchored-overlay trap). Caps role, Manrope, secondary tone — never
   serif, never the gold accent. Truncates rather than stacking a long plan name
   over the gif. Emitted by flow/js/selection.js only; the app grids never build
   this node (D-12 keeps plan badges off «Изучаю»/«Освоенные» tiles). */
.fig .name .plan-cap { font-size: var(--t-caps); font-weight: 600;
  letter-spacing: var(--tracking-caps); text-transform: uppercase;
  line-height: 1.2; color: var(--fg2); margin-bottom: var(--s-1);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fig.sel { box-shadow: var(--ring-gold); transform: translateY(-2px); }
.fig.sel .check { background: var(--gold-grad); border-color: transparent;
  color: var(--on-gold); transform: scale(1.08); }
.fig.sel .check svg { opacity: 1; transform: scale(1); }

/* Shared sticky dock + gold CTA. Scope the pill to `.dock .cta` so the global
   `.cta` / `.cta-inline` used by the error & empty state boxes stays intact. */
/* Pinned action bar — solid backing + top hairline + upward shadow so it reads
   clearly as "the start lives here", visually separated from the card grid
   (operator directive 2026-06-14). Scroll carries bottom padding so the last
   card never tucks under this bar. */
.dock { position: relative; z-index: 4;
  padding: 14px 16px calc(16px + env(safe-area-inset-bottom));
  display: flex; flex-direction: column; gap: 9px;
  background: var(--night); border-top: 1px solid var(--hairline);
  box-shadow: 0 -8px 24px rgba(8,10,18,.55); }
.dock .cta { width: 100%; max-width: none; height: 54px; min-height: 54px;
  padding: 0 16px; border-radius: var(--r-pill); font-size: 16px; font-weight: 700;
  letter-spacing: .01em; display: flex; align-items: center; justify-content: center; gap: 8px; }
.cta-gold { background: var(--gold-grad); color: var(--on-gold); box-shadow: var(--glow); }
.cta-gold:active { box-shadow: var(--glow-soft); }
.dock .cta:disabled { background: var(--surface-2); color: var(--fg-mute); box-shadow: none; }
.cta-num { font-variant-numeric: tabular-nums; }

/* ============================================================================
   Phase 33 SKIN-02 — BOARD
   «Твоя доска»: figures as a VERTICAL list (one .brow row under another),
   replacing the old tile grid. Ported from the handoff prototype
   (styles.css L92-102) onto DROPS tokens — the per-session «встроено N×» count
   renders as a big serif gold number. The prototype's cumulative помню/забыл
   swipe-tally block is Phase-34 (needs DB) and is intentionally NOT ported
   here; the decorative pips (no live data) are omitted too.
   Reuses SKIN-01 .screen-head / .dock / .cta-gold as-is.
   ============================================================================ */

/* BOARD shell: column layout, scroll grows, dock pinned. Scoped to the id —
   does NOT touch the global `.screen` state-machine rule. */
#screen-board { display: flex; flex-direction: column;
  min-height: var(--tg-viewport-stable-height, 100vh); }
#screen-board .scroll { flex: 1; overflow-y: auto; overflow-x: hidden;
  padding: 18px 16px 24px; -webkit-overflow-scrolling: touch; scrollbar-width: none; }
#screen-board .scroll::-webkit-scrollbar { display: none; }

/* Vertical figure row */
.brow { display: flex; gap: 14px; align-items: stretch; padding: 12px;
  margin-bottom: 11px; border-radius: var(--r-card); background: var(--surface);
  box-shadow: var(--sh-card); }
.brow .thumb { width: 76px; flex: none; border-radius: var(--r-img);
  overflow: hidden; aspect-ratio: 4/5; position: relative; }
.brow .thumb img,
.brow .thumb video { width: 100%; height: 100%; object-fit: cover; }
.brow .thumb i { position: absolute; top: 7px; left: 7px; width: 9px; height: 9px;
  border-radius: 50%; background: currentColor; box-shadow: 0 0 8px currentColor; }
.brow .info { flex: 1; min-width: 0; display: flex; flex-direction: column;
  justify-content: center; gap: 9px; }
.brow .info .nm { font-size: 14.5px; font-weight: 700; line-height: 1.22; color: var(--fg1); }
/* Baseline-aligned so the big serif number and the «встроено» caption share one
   clean baseline (operator review 2026-06-14: center+line-height:.8 made the caption
   float above the digit and read crooked). */
.brow .prog { display: flex; align-items: baseline; gap: 7px; }
.brow .prog .big { font-family: var(--font-serif); font-weight: 700; font-size: 30px;
  line-height: 1; color: var(--gold); font-variant-numeric: tabular-nums;
  transition: color var(--dur-base); }
.brow.zero .prog .big { color: var(--fg-mute); }
.brow .prog .cap { font-size: var(--t-caps); font-weight: 700; text-transform: uppercase;
  letter-spacing: .1em; color: var(--fg3); line-height: 1.2; }

/* Secondary dock button (prototype .cta-ghost) — transparent, hairline border. */
.cta-ghost { background: transparent; color: var(--fg2);
  border: 1px solid var(--hairline); height: 48px; font-size: 15px; }

/* UX-01: tokenized one-step finish confirm (replaces native tg.showConfirm).
   Reads as a deliberate in-app affordance — panel surface, hairline border,
   scrim-tinted question line, gold confirm consistent with .cta-gold + neutral
   cancel. Lives inside the board .dock footprint so it does not fight the
   viewport work. Default hidden via the global .hidden utility. */
.finish-confirm { display: flex; flex-direction: column; gap: 10px;
  padding: 14px; border-radius: var(--r-card);
  background: var(--surface-2); border: 1px solid var(--hairline);
  box-shadow: var(--sh-card); }
.finish-confirm-q { font-size: var(--t-body); font-weight: 600; line-height: 1.3;
  color: var(--fg1); text-align: center; }
.finish-confirm-row { display: flex; gap: 9px; }
.finish-confirm .cta { flex: 1; height: 50px; min-height: 50px; }
.finish-confirm-cancel { background: var(--surface); color: var(--fg1);
  border: 1px solid var(--hairline); }
.finish-confirm-cancel:active { background: var(--surface-3); transform: scale(.98); }
.finish-confirm-yes { background: var(--gold-grad); color: var(--on-gold);
  box-shadow: var(--glow); }
.finish-confirm-yes:active { box-shadow: var(--glow-soft); transform: scale(.98); }

/* ============================================================
   Phase 33 SKIN-03 — APPROACH (гифка-герой + serif stopwatch)
   Ported from the DROPS-gold handoff prototype section[data-screen="set"]
   (.planning/design/handoff-flow-training/styles.css L119-149), re-pointed onto
   the flow.css DROPS tokens. Reuses the SKIN-01 .dock/.cta-gold primitives.
   Server-anchored timing source (approach.js) is untouched — only the
   #approach-time visual becomes the large serif .timer.
   ============================================================ */

/* APPROACH shell: column layout so the carousel grows and the dock pins below.
   The prototype gold-spot background is applied here. Does NOT touch the global
   `.screen` state-machine rule (Gate 5). */
#screen-approach {
  display: flex; flex-direction: column;
  min-height: var(--tg-viewport-stable-height, 100vh);
  background: radial-gradient(70% 38% at 50% 26%,
    rgba(240, 192, 96, calc(.08 * var(--glow-k))) 0%, transparent 70%);
}

.set-top { padding: 14px 16px 4px; text-align: center; flex: none; }
.set-kicker { font-size: var(--t-caps); font-weight: 700; text-transform: uppercase;
  letter-spacing: var(--tracking-caps); color: var(--gold-deep); }
.set-kicker b { color: var(--gold); }

/* The large serif server-anchored stopwatch (#approach-time carries .timer). */
.timer { font-family: var(--font-serif); font-weight: 700;
  font-size: clamp(56px, 18vw, var(--timer-size, 72px)); line-height: .95;
  font-variant-numeric: tabular-nums; letter-spacing: .01em; color: var(--fg1);
  margin-top: 2px; text-shadow: 0 0 30px rgba(240, 192, 96, calc(.28 * var(--glow-k))); }
.timer.tick { animation: tick 420ms var(--ease); }
@keyframes tick { 0% { transform: scale(1); } 22% { transform: scale(1.04); } 100% { transform: scale(1); } }

/* Carousel viewport: the live #approach-track lives inside .viewport; arrows are
   absolutely positioned blur circles over it. We re-scope the SKIN-03 carousel
   here on #screen-approach so the prototype peek layout supersedes the old rules.
   VIEW-01: flex:1 lets the hero absorb leftover space, but a min/max bound tied to
   the stable viewport keeps it meaningfully tall while yielding so the stopwatch,
   arrows and dots never clip on a half-collapsed (short) viewport. min-height
   clamps a sensible floor; max-height caps overgrowth on tall screens. */
#screen-approach .viewport { flex: 1 1 auto; overflow: hidden; width: 100%;
  position: relative; touch-action: pan-y; height: auto;
  min-height: clamp(180px, calc(var(--tg-viewport-stable-height, 100vh) * 0.34), 320px);
  max-height: calc(var(--tg-viewport-stable-height, 100vh) * 0.62);
  display: flex; align-items: center; }
#screen-approach .track { display: flex; align-items: center; height: 100%;
  transition: transform var(--dur-base) var(--ease); will-change: transform; }

.car-arrow { position: absolute; top: 50%; transform: translateY(-50%); z-index: 4;
  width: 40px; height: 40px; border-radius: 50%; display: grid; place-items: center;
  background: rgba(16, 21, 38, .7);
  backdrop-filter: blur(calc(10px * var(--blur-k)));
  -webkit-backdrop-filter: blur(calc(10px * var(--blur-k)));
  border: 1px solid var(--hairline); color: var(--fg1);
  transition: transform var(--dur-fast) var(--ease), background var(--dur-fast); }
.car-arrow:active { transform: translateY(-50%) scale(.88); }
.car-arrow.left { left: 10px; } .car-arrow.right { right: 10px; }

/* Peek slide: 4:5 frame, dimmed side neighbours, gold center ring on the current
   slide. The live `.slide .gif` factory frame fills the slide. */
#screen-approach .slide { flex: 0 0 78%; height: 78%; aspect-ratio: 4 / 5;
  padding: 0 1.6%; box-sizing: border-box; border-radius: var(--r-card);
  position: relative; box-shadow: var(--sh-pop);
  transition: transform var(--dur-base) var(--ease), opacity var(--dur-base) var(--ease),
    filter var(--dur-base) var(--ease); }
#screen-approach .slide .gif { height: 100%; width: 100%; aspect-ratio: auto;
  border-radius: var(--r-card); overflow: hidden;
  transition: opacity var(--dur-base), transform var(--dur-base); }
#screen-approach .slide:not(.cur) { transform: scale(.84); opacity: .40;
  filter: saturate(.7) brightness(.68); }
#screen-approach .slide.cur { box-shadow: var(--sh-pop),
  0 0 0 1px var(--gold-line-2), 0 0 40px rgba(240, 192, 96, calc(.18 * var(--glow-k))); }

/* Figure name + dots footer (live #approach-figname carries .set-name look). */
.set-foot { flex: none; text-align: center; padding: 6px 16px 2px; }
.set-name { display: inline-flex; align-items: center; gap: 9px; justify-content: center;
  font-size: 17px; font-weight: 700; color: var(--fg1); }
.set-name i { width: 10px; height: 10px; border-radius: 50%; box-shadow: 0 0 9px currentColor; }

/* Re-point the live dots (spans) to the prototype's elongated-gold active pill.
   Keep the ≥44px tap target ::after from SKIN-01. */
#screen-approach .dots { display: flex; gap: 7px; justify-content: center; margin-top: 11px; }
#screen-approach .dots span { position: relative; width: 7px; height: 7px;
  border-radius: 50%; background: var(--fg-mute); cursor: pointer;
  transition: all var(--dur-base) var(--ease); }
#screen-approach .dots span.on { background: var(--gold); width: 22px;
  border-radius: 4px; box-shadow: 0 0 10px var(--gold-line); transform: none; }

/* ============================================================
   Phase 33 SKIN-05 — BINGO
   Port of prototype section[data-screen="bingo"] (styles.css L184-208):
   gold badge + large serif DROPS-gold metrics + total-time line, a tagged
   per-figure .brow list (reuses SKIN-02 .brow), and a motion-aware confetti
   finish. All on DROPS tokens — no new hex, no TG-blue brand token. Confetti
   keyframe is halted in the reduced-motion gate above (`.conf`); bingo.js also
   skips spawning under reduced motion (CSS + JS double gate).
   ============================================================ */
#screen-bingo { display: flex; flex-direction: column;
  min-height: var(--tg-viewport-stable-height, 100vh); overflow: hidden; }

/* Hero — gold badge + big serif metrics + total-time line */
.bingo-hero { text-align: center; padding: 34px 24px 10px; flex: none;
  position: relative; z-index: 2; }
.bingo-badge { display: inline-flex; align-items: center; gap: 8px;
  font-size: var(--t-caps); font-weight: 700; text-transform: uppercase;
  letter-spacing: var(--tracking-caps); color: var(--gold-deep); margin-bottom: 20px; }
.gold-text { background: var(--gold-grad); -webkit-background-clip: text;
  background-clip: text; color: transparent; }
.bingo-bigstats { display: flex; align-items: center; justify-content: center; gap: 30px; }
.bigstat { display: flex; flex-direction: column; align-items: center; min-width: 96px; }
.bigstat b { font-family: var(--font-serif); font-weight: 700; font-size: 78px;
  line-height: .86; color: var(--fg1); font-variant-numeric: tabular-nums; }
.bigstat span { font-size: 12px; font-weight: 700; text-transform: uppercase;
  letter-spacing: var(--tracking-caps); color: var(--fg3); margin-top: 10px; }
.bigstat-div { width: 1px; height: 64px;
  background: linear-gradient(to bottom, transparent, var(--gold-line), transparent); }
.bingo-time { font-size: 13px; font-weight: 600; color: var(--fg3);
  margin-top: 20px; letter-spacing: .02em; }
.bingo-time b { font-family: var(--font-serif); font-weight: 700; font-size: 17px;
  color: var(--gold); font-variant-numeric: tabular-nums; }

/* Per-figure tagged list — reuses SKIN-02 .brow rows */
.bingo-list { flex: 1; overflow-y: auto; padding: 18px 16px 8px;
  scrollbar-width: none; position: relative; z-index: 2; }
.bingo-list::-webkit-scrollbar { display: none; }
.bingo-list .brow { align-items: center; }
.b-status { display: flex; align-items: center; gap: 7px; font-size: 12px;
  font-weight: 600; color: var(--fg2); }
.b-status .b-dot { width: 8px; height: 8px; border-radius: 50%;
  background: currentColor; box-shadow: 0 0 8px currentColor; }
.b-tag { align-self: center; font-size: 11px; font-weight: 800;
  text-transform: uppercase; letter-spacing: .06em; padding: 6px 12px;
  border-radius: var(--r-pill); white-space: nowrap; margin-left: auto; }
.b-tag.yes { color: var(--st-music); background: rgba(77,217,122,.14);
  border: 1px solid rgba(77,217,122,.3); }
.b-tag.no { color: var(--st-count); background: rgba(255,107,107,.14);
  border: 1px solid rgba(255,107,107,.3); }
.b-tag.skip { color: var(--fg3); background: var(--surface-2); border: 1px solid var(--hairline); }

/* Confetti finish — motion-aware (keyframe halted under prefers-reduced-motion;
   bingo.js also skips spawning so no DOM churn for reduced-motion users) */
#confetti { position: absolute; inset: 0; z-index: 1; pointer-events: none; overflow: hidden; }
.conf { position: absolute; top: -20px; width: 9px; height: 14px; border-radius: 2px;
  will-change: transform, opacity; animation: confettiFall 2.4s var(--ease) forwards; }
@keyframes confettiFall {
  0%   { transform: translate(0, -20px) rotate(0deg); opacity: 1; }
  100% { transform: translate(var(--conf-x, 0), 900px) rotate(var(--conf-r, 720deg)); opacity: .9; }
}

/* Secondary dock "Закрыть" — prototype .cta-finish (transparent, green-tinted border).
   (Base .cta-finish already lives in SKIN-02; restated here for the BINGO dock context.) */
.bingo-dock .cta-finish { background: transparent; color: var(--finish);
  border: 1px solid rgba(77,217,122,.35); }
