/* The skill screen: one layout for every skill, dressed by its category style
   (kit/styles.css). Tokens only (kit/tokens.css): no literal colours, gaps,
   radii or durations here. */

:root { --r-card: var(--r-lg); --r-ctl: var(--r-md); }
html, body { background: var(--ground); }
body { min-height: 100vh; padding-inline: var(--s4); padding-block: var(--s5) var(--s7); overflow-x: hidden; }
.belt-ambient { position: fixed; }

.skill-app { max-width: 480px; margin: 0 auto; display: grid; gap: var(--s4); position: relative; }
.screen { position: relative; overflow: hidden; isolation: isolate; background: var(--surface); border: var(--border); border-radius: var(--r-card); box-shadow: var(--shadow); padding: var(--s5) var(--s4); display: grid; gap: var(--s4); align-content: start; min-height: min(640px, calc(100vh - 80px)); }
.screen::before { content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none; }
.screen::after { content: ""; position: absolute; inset: -30% -30% auto auto; width: 70%; height: 60%; z-index: -1; pointer-events: none; background: radial-gradient(closest-side, var(--belt), transparent); opacity: var(--belt-glow-alpha); }

.top { display: flex; justify-content: space-between; align-items: center; gap: var(--s2); min-height: 32px; }
.back { width: 32px; height: 32px; border-radius: var(--r-full); border: var(--border); background: var(--surface-2); color: var(--ink-2); display: grid; place-items: center; font: inherit; cursor: pointer; text-decoration: none; }
.back:hover { color: var(--ink); border-color: var(--rule-2); }
.cat { font-size: var(--t-xs); color: var(--belt-ink); letter-spacing: .04em; }
.prog { display: flex; gap: 3px; flex-wrap: wrap; justify-content: flex-end; max-width: 60%; }
.prog i { width: 12px; height: 6px; border-radius: var(--r-full); background: var(--rule-2); transition: background var(--motion-fast) var(--ease); }
.prog i.on { background: var(--belt); box-shadow: 0 0 0 1px var(--belt-ink) inset; }
.prog i.miss { background: var(--rule-2); box-shadow: 0 0 0 1px var(--crit) inset; }

.title { display: grid; gap: var(--s1); }
.title h1 { font-size: var(--t-xl); }
/* PASSED stamped on the title of a passed skill (Luke, 10 Oct), instead of a big tick */
.title h1 .stamp.on-title { position: relative; right: auto; bottom: auto; display: inline-block; margin-left: var(--s3); vertical-align: middle; font-size: var(--t-md); line-height: 1.2; text-transform: uppercase; letter-spacing: .06em; font-weight: 700; }
.title p { color: var(--ink-2); font-size: var(--t-sm); }
.stage { background: var(--surface-2); border: var(--border); border-radius: var(--r-card); padding: var(--s4); display: grid; gap: var(--s3); position: relative; overflow: hidden; }

.btn { font: 600 var(--t-base) var(--font-display); letter-spacing: .01em; border-radius: var(--r-ctl); padding: var(--s3) var(--s5); cursor: pointer; border: var(--border); background: var(--surface-2); color: var(--ink); transition: border-color var(--motion-fast) var(--ease), transform var(--motion-fast) var(--ease); }
.btn:hover:not(:disabled) { border-color: var(--belt-ink); }
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: .4; cursor: default; transform: none; }
.btn.primary { background: var(--belt-ink); border-color: var(--belt-ink); color: var(--ground); }
:root[data-belt="white"] .btn.primary { color: var(--ground); }
:root[data-dojo-theme="dojo-light"] .btn.primary { color: var(--surface); }
.actions { display: grid; gap: var(--s2); margin-top: auto; }

.big { font: 600 var(--t-3xl)/1 var(--font-display); }
.muted { color: var(--muted); }
.lede { color: var(--ink-2); }
.facts { display: flex; flex-wrap: wrap; gap: var(--s2); }
.fact { background: var(--surface-2); border: var(--border); border-radius: var(--r-ctl); padding: var(--s2) var(--s3); display: grid; gap: 2px; min-width: 96px; }
.fact b { font: 600 var(--t-lg) var(--font-num); font-variant-numeric: tabular-nums; }
.fact span { font-size: var(--t-xs); color: var(--muted); }
.list { margin: 0; padding-left: 1.2em; display: grid; gap: var(--s1); color: var(--ink-2); }
.tick { width: 72px; height: 72px; border-radius: var(--r-full); border: 2px solid var(--belt-ink); display: grid; place-items: center; color: var(--belt-ink); font: 600 var(--t-2xl) var(--font-display); }

/* quick-fire: the timed number-pad game (games/quick-fire.js) */
.qf-board { display: grid; grid-template-columns: 1fr auto; align-items: end; gap: var(--s3); padding-bottom: var(--s2); border-bottom: 2px solid var(--belt-ink); }
.qf-sum { font: 600 2.75rem/1.05 var(--font-display); font-variant-numeric: tabular-nums; }
.qf-sum .q { color: var(--belt-ink); }
.qf-entry { font: 600 var(--t-2xl) var(--font-num); font-variant-numeric: tabular-nums; min-width: 2.5ch; text-align: right; color: var(--belt-ink); }
.qf-pad { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s2); }
.qf-pad button { font: 600 var(--t-lg) var(--font-num); font-variant-numeric: tabular-nums; padding: var(--s3) 0; background: var(--surface); border: var(--border); border-radius: var(--r-ctl); color: var(--ink); cursor: pointer; touch-action: manipulation; }
.qf-pad button:hover { border-color: var(--rule-2); }
.qf-pad button.go { background: var(--belt-ink); border-color: var(--belt-ink); color: var(--ground); }
:root[data-dojo-theme="dojo-light"] .qf-pad button.go { color: var(--surface); }
.qf-flash { min-height: 1.6em; font-size: var(--t-sm); color: var(--ink-2); }
.qf-flash.good { color: var(--good); }
.qf-flash.not { color: var(--crit); }

/* dots: hands-on pictures for training (games/dots.js) */
.mani { justify-items: center; text-align: center; }
.mani-row { display: flex; align-items: center; justify-content: center; gap: var(--s2); flex-wrap: wrap; }
.mani-op { font: 600 var(--t-xl) var(--font-display); color: var(--muted); }
.mani-label { font-size: var(--t-xs); color: var(--muted); width: 100%; }
.mani-say { color: var(--ink-2); min-height: 1.6em; }
.mani .btn { justify-self: stretch; }
.tf { display: grid; grid-template-columns: repeat(5, 26px); grid-auto-rows: 26px; gap: 3px; padding: var(--s2); border: 1px solid var(--rule-2); border-radius: var(--r-ctl); background: var(--surface); transition: border-color var(--motion-fast) var(--ease), box-shadow var(--motion-fast) var(--ease); }
.tf .cell { display: grid; place-items: center; border-radius: var(--r-full); box-shadow: 0 0 0 1px var(--rule) inset; }
.tf.pile, .tf.fixed { background: transparent; border-style: dashed; }
.tf.target { border-color: var(--belt-ink); }
.tf.over { box-shadow: 0 0 0 2px var(--belt-ink); }
.tf.good, .cut.good { border-color: var(--good); box-shadow: 0 0 0 2px var(--good); }
.dot { display: block; width: 20px; height: 20px; border-radius: var(--r-full); background: var(--belt); box-shadow: 0 0 0 1px var(--belt-ink) inset; cursor: grab; touch-action: none; }
.tf.fixed .dot { cursor: default; }
.dot.lifting { opacity: .3; }
.dot-ghost { position: fixed; z-index: 60; pointer-events: none; width: 26px; height: 26px; border-radius: var(--r-full); background: var(--belt); box-shadow: 0 0 0 2px var(--belt-ink), var(--shadow); }
.mani.done .dot { cursor: default; }

.cut-wrap { max-width: 100%; overflow-x: auto; padding-block: 16px 8px; } /* room for the line's handle, or it scrolls */
.cut { position: relative; border: 1px dashed var(--rule-2); border-radius: var(--r-ctl); cursor: ew-resize; touch-action: none; }
.cut .dot { position: absolute; width: var(--dot, 22px); height: var(--dot, 22px); cursor: inherit; }
.cut .dot.side-l { background: color-mix(in srgb, var(--belt) 45%, var(--surface)); }
.cut-line { position: absolute; top: -6px; bottom: -6px; width: 3px; margin-left: -1.5px; background: var(--belt-ink); border-radius: var(--r-full); pointer-events: none; transition: left var(--motion-fast) var(--ease); }
.cut-line::before { content: ""; position: absolute; top: -6px; left: -6px; width: 15px; height: 15px; border-radius: var(--r-full); background: var(--belt-ink); }
.cut-count { display: flex; gap: var(--s3); align-items: baseline; font: 600 var(--t-xl) var(--font-num); font-variant-numeric: tabular-nums; }
.cut-count span { color: var(--muted); }

/* drill: one chip per fact, pips fill with the streak (games/drill.js) */
.chips { display: flex; flex-wrap: wrap; gap: var(--s1); }
.chip { display: inline-flex; flex-direction: column; align-items: center; gap: 3px; padding: var(--s1) var(--s2); border: var(--border); border-radius: var(--r-sm); font-size: var(--t-xs); color: var(--muted); background: var(--surface); }
.chip.on { color: var(--ink); border-color: var(--rule-2); }
.chip.got { color: var(--ink); border-color: var(--good); }
.chip .pips { display: flex; gap: 2px; }
.chip .pips i { width: 8px; height: 4px; border-radius: var(--r-full); background: var(--rule-2); }
.chip .pips i.on { background: var(--belt); }
.chip.got .pips i { background: var(--good); }

/* test-boy bar: only ever on localhost or ?test */
.devbar { position: fixed; left: var(--s2); bottom: var(--s2); z-index: 20; display: flex; gap: var(--s2); align-items: center; flex-wrap: wrap; background: var(--surface); border: 1px dashed var(--warn); border-radius: var(--r-ctl); padding: var(--s1) var(--s2); font-size: var(--t-xs); color: var(--warn); max-width: calc(100vw - var(--s4)); }
.devbar button { font: inherit; color: var(--ink-2); background: var(--surface-2); border: var(--border); border-radius: var(--r-sm); padding: 2px var(--s2); cursor: pointer; }

/* rewards bar: level, combo, coins, days this week (kit/kit.js hudHtml).
   Coins use --warn: there is no coin/gold token yet (gap raised 10 Oct). */
.hud { position: relative; display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap; font-size: var(--t-xs); color: var(--muted); padding-bottom: var(--s2); border-bottom: 1px solid var(--rule); }
.hud b { font: 600 var(--t-sm) var(--font-num); font-variant-numeric: tabular-nums; color: var(--ink); }
.hud-lv { display: inline-flex; align-items: center; gap: var(--s2); }
.hud-bar { width: 64px; height: 4px; border-radius: var(--r-full); background: var(--rule-2); overflow: hidden; }
.hud-bar i { display: block; height: 100%; background: var(--belt); transition: width var(--motion-base) var(--ease); }
.hud-coins { display: inline-flex; align-items: center; gap: var(--s1); margin-left: auto; }
/* (the old CSS coin dot was replaced by the coin pile art, kit/art.js) */
.hud-week.met { color: var(--good); }
.hud-combo { padding: 2px var(--s2); border-radius: var(--r-full); border: 1px solid var(--rule-2); color: var(--ink-2); transition: border-color var(--motion-fast) var(--ease), box-shadow var(--motion-fast) var(--ease); }
.hud-combo.t2, .hud-combo.t3, .hud-combo.t4 { border-color: var(--belt-ink); color: var(--ink); }
.hud-combo.t3, .hud-combo.t4 { box-shadow: 0 0 var(--s3) color-mix(in srgb, var(--belt) 55%, transparent); }
.hud-pop { position: absolute; right: 0; top: calc(100% + var(--s1)); font: 600 var(--t-sm) var(--font-num); color: var(--belt-ink); opacity: 0; pointer-events: none; z-index: 5; }
.hud-pop.show { animation: hud-pop 1100ms var(--ease) both; }
@keyframes hud-pop { 0% { opacity: 0; transform: translateY(4px); } 15%, 75% { opacity: 1; transform: none; } 100% { opacity: 0; transform: translateY(-4px); } }
@media (prefers-reduced-motion: reduce) { .hud-pop.show { animation: none; opacity: 1; } }

/* dots that he adds (ring) next to dots that were given (solid) */
.dot.alt { background: transparent; box-shadow: 0 0 0 3px var(--belt-ink) inset; }
.dot.given { cursor: default; }
.mani-count { font: 600 var(--t-xl) var(--font-num); font-variant-numeric: tabular-nums; }
.mani-count .hl { color: var(--belt-ink); }

/* progress bar with clear gaps (Luke, 10 Oct): one segment per step or fact */
.seg-bar { display: flex; gap: var(--s1); }
.seg-bar span { flex: 1; height: 8px; border-radius: var(--r-full); background: var(--rule-2); overflow: hidden; }
.seg-bar i { display: block; height: 100%; background: var(--belt); transition: width var(--motion-base) var(--ease); }

/* the typing box sits in the sum where the answer goes (no number pad) */
.qf-in { font: inherit; color: var(--belt-ink); background: transparent; border: 0; border-bottom: 3px solid var(--belt-ink); width: 2.2ch; padding: 0; text-align: center; caret-color: var(--belt-ink); }
.qf-in:focus { outline: none; border-bottom-color: var(--ink); }
.qf-board { border-bottom: 0; }
.qf-board.good .qf-sum, .qf-board.good .q { color: var(--good); }
.qf-board.not .q { color: var(--crit); }
.stage.qf { padding-block: var(--s6); }

/* tap dots (games/dots.js): the question on top, fixed; his sum under it,
   following his taps (red while it's off, green once right); then the dots */
.tap-game { justify-items: center; gap: var(--s3); }
.mani-sum { font: 600 var(--t-2xl) var(--font-display); font-variant-numeric: tabular-nums; }
.mani-sum .q { color: var(--belt-ink); }
.tap-game.good .mani-sum .q, .choice-game.good .mani-sum .q { color: var(--good); }
.mani-live { font: 600 var(--t-xl) var(--font-num); font-variant-numeric: tabular-nums; color: var(--ink-2); min-height: 1.4em; margin-bottom: var(--s2); }
.mani-live.off { color: var(--crit); }
.tap-game.good .mani-live { color: var(--good); }
.tap-game .cut { cursor: ew-resize; touch-action: none; } /* drag the line, or tap where it goes */

/* choice tiles (games/choice.js, from Fact Dojo): a wrong tile wobbles and fades where it stood */
.choice-game { justify-items: center; gap: var(--s5); }
.choice-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 96px)); gap: var(--s2); }
.choice-tile { font: 600 var(--t-xl) var(--font-display); font-variant-numeric: tabular-nums; color: var(--ink); background: var(--surface-2); border: 2px solid var(--rule-2); border-radius: var(--r-ctl); padding: var(--s3) var(--s2); cursor: pointer; touch-action: manipulation; transition: opacity var(--motion-base) var(--ease), transform var(--motion-base) var(--ease), border-color var(--motion-fast) var(--ease); }
@media (hover: hover) { .choice-tile:not(:disabled):hover { border-color: var(--belt-ink); } }
.choice-tile.wobble { border-color: var(--crit); animation: choice-wobble 300ms var(--ease); }
.choice-tile.gone { opacity: 0; transform: scale(.85); pointer-events: none; }
.choice-tile.right { border-color: var(--good); color: var(--good); box-shadow: 0 0 0 2px var(--good); }
.choice-game.done .choice-tile { cursor: default; }
@keyframes choice-wobble { 0%, 100% { transform: translateX(0); } 25% { transform: translateX(-6px); } 75% { transform: translateX(6px); } }
@media (prefers-reduced-motion: reduce) { .choice-tile.wobble { animation: none; } }

/* small doubles as a mirror (dots.js double): one box, a line down the middle,
   five cells each side; sized to fit a phone */
.mirror { display: flex; align-items: center; gap: var(--s2); padding: var(--s2); border: 1px solid var(--rule-2); border-radius: var(--r-ctl); background: var(--surface); transition: border-color var(--motion-fast) var(--ease), box-shadow var(--motion-fast) var(--ease); }
.mirror.good { border-color: var(--good); box-shadow: 0 0 0 2px var(--good); }
.mirror { --mc: clamp(14px, 5vw, 28px); } /* cell size: shrinks on a phone so the box fits */
.mirror .tf.mirror-side { grid-template-columns: repeat(5, var(--mc)); grid-auto-rows: var(--mc); gap: 2px; padding: 0; border: 0; background: transparent; }
.mirror .tf.mirror-side .dot { width: calc(var(--mc) - 6px); height: calc(var(--mc) - 6px); }
.mirror-line { align-self: stretch; width: 0; border-left: 3px dashed var(--belt-ink); margin-block: calc(-1 * var(--s1)); }

.tf.tap { grid-template-columns: repeat(5, 36px); grid-auto-rows: 36px; gap: var(--s1); }
.tf.tap .cell { font: inherit; padding: 0; border: 0; background: transparent; cursor: pointer; touch-action: manipulation; }
@media (hover: hover) { .tf.tap button.cell:hover { box-shadow: 0 0 0 1px var(--belt-ink) inset; } }
.tap-game .mani-row { flex-direction: column; gap: var(--s2); }
.tf.tap .dot { width: 28px; height: 28px; cursor: inherit; }
.dot.out { background: transparent; box-shadow: 0 0 0 1px var(--rule-2) inset; opacity: .5; }
.tap-game.done .cell, .tf.tap.fixed .cell { cursor: default; }
@media (hover: hover) { .tf.tap.fixed button.cell:hover { box-shadow: none; } }

/* the facts that got away, on the not-yet screen */
.misses { margin: 0; padding: 0; list-style: none; display: flex; flex-wrap: wrap; gap: var(--s2); }
.misses li { font: 600 var(--t-md) var(--font-num); font-variant-numeric: tabular-nums; padding: var(--s1) var(--s3); border: 1px solid var(--rule-2); border-radius: var(--r-ctl); }

/* the hero and the coins (kit/art.js; art from the Dojo Progression Model) */
#screen-host { display: contents; }
.hero-box { position: absolute; top: var(--s5); right: var(--s3); display: grid; gap: var(--s1); justify-items: end; pointer-events: none; z-index: 2; }
.hero-stage { position: relative; width: 76px; height: 90px; }
.hero { position: absolute; left: 50%; bottom: 0; height: 90px; aspect-ratio: 425 / 639; transform: translateX(-50%); background: url(/assets/avatars/hero.png) 0 0 / 200% 100% no-repeat; animation: hero-bob 1.6s steps(1) infinite; transition: filter var(--motion-base) var(--ease); }
@keyframes hero-bob { 0% { background-position: 0 0; } 50%, 100% { background-position: 100% 0; } }
/* the glow grows with the combo; warm colours belong to the fire art */
.hero[data-tier="2"] { filter: drop-shadow(0 0 4px rgba(255, 140, 40, .45)); }
.hero[data-tier="3"] { filter: drop-shadow(0 0 7px rgba(255, 150, 40, .6)); }
.hero[data-tier="4"] { filter: drop-shadow(0 0 10px rgba(255, 190, 70, .75)); }
.hero-fire { position: absolute; left: 50%; bottom: -2px; width: 76px; height: 96px; transform: translateX(-50%); image-rendering: pixelated; }
.coin-pop { position: absolute; left: 50%; top: -8px; width: 32px; height: 32px; margin-left: -16px; background: url(/assets/coins/coin-spin.png) 0 0 / 1600% 100% no-repeat; opacity: 0; }
.coin-pop.go { animation: coin-spin 800ms steps(16, jump-none) 1, coin-rise 800ms var(--ease-out) 1; }
@keyframes coin-spin { to { background-position: 100% 0; } }
@keyframes coin-rise { 0% { opacity: 0; transform: translateY(10px); } 15%, 80% { opacity: 1; } 100% { opacity: 0; transform: translateY(-28px); } }
.coin-pile { width: 24px; height: 24px; object-fit: contain; }
/* keep the top row and the bar clear of the hero */
.skill-app .top, .skill-app .hud { padding-right: 80px; }
.hud-coins { margin-left: 0; }
@media (prefers-reduced-motion: reduce) { .hero { animation: none; } .coin-pop.go { animation: none; } }
.skill-app .hud-pop { right: 80px; }
.skill-app .hud { gap: var(--s2) var(--s3); } .skill-app .hud-bar { width: 44px; }

/* streak under the hero (Luke, 10 Oct): yellow, then orange, then red as it climbs.
   Streak tokens are the yellow, orange and red belt-ink shades; darker in the
   light theme so they still read. (Not in tokens.css yet: gap raised 10 Oct.) */
:root { --streak-1: #F4D45E; --streak-2: #F29B4D; --streak-3: #EF6A57; }
:root[data-dojo-theme="dojo-light"] { --streak-1: #8A6D00; --streak-2: #A65412; --streak-3: #97301F; }
.hero-streak[data-tier="1"] { color: var(--streak-1); }
.hero-streak[data-tier="2"] { color: var(--streak-2); }
.hero-streak[data-tier="3"], .hero-streak[data-tier="4"] { color: var(--streak-3); }
.hero-streak[data-tier="4"] { text-shadow: 0 0 var(--s2) color-mix(in srgb, var(--streak-3) 60%, transparent); }

/* after every test: each question, what he typed, the right answer (kit.js reviewHtml) */
/* Boxes grow to fit their sum (Luke, 10 Oct: "half of 18 = 9" ran out of a fixed-width box). */
.review { margin: 0; padding: 0; list-style: none; display: flex; flex-wrap: wrap; gap: var(--s2); }
.review li { flex: 1 0 auto; text-align: center; font: 600 var(--t-md) var(--font-num); font-variant-numeric: tabular-nums; padding: var(--s2) var(--s3); border: 1px solid var(--rule-2); border-radius: var(--r-ctl); background: var(--surface-2); max-width: 100%; overflow-wrap: anywhere; }
/* ...but never wider than the screen: a long one wraps inside its box (Luke, 11 Oct: Equal
   groups' "●●●●● ●●●●●" ran out past the edge). Short ones still sit on one line. */
.review .r-answer { color: var(--good); }
.review .r-miss { border-color: var(--crit); }
.review .r-miss .r-given { color: var(--crit); text-decoration-thickness: 3px; text-decoration-color: var(--crit); }
.review .r-skip { border-style: dashed; }
.review .r-skip .r-given { color: var(--muted); }
.review .r-right { color: var(--ink-2); }
/* under the hero (Luke, 10 Oct): the streak, then his level out of 99 and its bar, one text style */
.hero-stats { display: grid; justify-items: end; gap: 2px; }
.hero-streak, .hero-level { white-space: nowrap; font: 600 var(--t-xs) var(--font-display); letter-spacing: .02em; text-transform: uppercase; font-variant-numeric: tabular-nums; transition: color var(--motion-base) var(--ease); }
.hero-streak[data-tier="0"] { color: var(--ink-2); }
.hero-level { color: var(--ink); }
.hero-bar { width: 76px; height: 4px; border-radius: var(--r-full); background: var(--rule-2); overflow: hidden; }
.hero-bar i { display: block; height: 100%; background: var(--belt); transition: width var(--motion-base) var(--ease); }
.skill-app .title { padding-right: 88px; min-height: 56px; align-content: start; }

/* a test's progress: one continuous bar under the title (Luke, 10 Oct) */
.run-bar { height: 8px; border-radius: var(--r-full); background: var(--rule-2); overflow: hidden; }
.run-bar i { display: block; height: 100%; background: var(--belt); transition: width var(--motion-base) var(--ease); }

/* starting a test: the tick-through pop-up, then the fuzzy 3, 2, 1 */
.gate { position: fixed; inset: 0; z-index: 40; display: grid; place-items: center; padding: var(--s4); background: color-mix(in srgb, var(--ground) 60%, transparent); backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px); }
.gate-box { position: relative; width: min(100%, 380px); background: var(--surface); border: var(--border); border-radius: var(--r-card, var(--r-lg)); box-shadow: var(--shadow); padding: var(--s6) var(--s5) var(--s5); display: grid; gap: var(--s5); }
.gate-x { position: absolute; top: var(--s2); right: var(--s2); width: 32px; height: 32px; border-radius: var(--r-full); border: var(--border); background: var(--surface-2); color: var(--ink-2); font: inherit; cursor: pointer; }
.gate-checks { display: grid; gap: var(--s3); }
.gate-check { display: grid; grid-template-columns: auto 1fr; gap: var(--s3); align-items: center; padding: var(--s3) var(--s4); border: var(--border); border-radius: var(--r-ctl, var(--r-md)); background: var(--surface-2); font-size: var(--t-md); cursor: pointer; transition: border-color var(--motion-fast) var(--ease); }
.gate-check input { width: 24px; height: 24px; margin: 0; accent-color: var(--belt); cursor: pointer; }
.gate-check:has(input:checked) { border-color: var(--belt-ink); }
.countdown { position: fixed; inset: 0; z-index: 50; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--s4); padding: var(--s4); background: color-mix(in srgb, var(--ground) 55%, transparent); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); }
.countdown-n { font-family: var(--font-display); font-weight: 600; font-variant-numeric: tabular-nums; font-size: calc(var(--t-3xl) * 2.2); line-height: 1; color: var(--belt-ink); letter-spacing: -0.04em; animation: count-pop var(--motion-base) var(--ease-out); }
.countdown-msg { font-family: var(--font-display); font-weight: 600; font-size: var(--t-xl); color: var(--ink); text-align: center; max-width: 24ch; }
@keyframes count-pop { from { transform: scale(.7); opacity: 0; } to { transform: scale(1); opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .countdown-n { animation: none; } }
/* stand-in box that opens the phone's number keyboard from the Start tap */
.kb-primer { position: fixed; top: 0; left: 0; width: 1px; height: 1px; opacity: 0; font-size: 16px; border: 0; padding: 0; }

/* the celebration after a pass: cards slap on one at a time (kit.js celebrate) */
.celebrate { position: fixed; inset: 0; z-index: 45; display: grid; place-items: center; padding: var(--s4); background: color-mix(in srgb, var(--ground) 55%, transparent); backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); cursor: pointer; transition: opacity var(--motion-base) var(--ease); }
.celebrate.out { opacity: 0; }
.celebrate.thud .celebrate-stack { animation: thud 180ms var(--ease) 140ms; }
.celebrate-stack { display: grid; gap: var(--s4); justify-items: center; width: min(100%, 360px); }
.slap { width: 100%; display: grid; justify-items: center; gap: var(--s2); padding: var(--s5) var(--s4); background: var(--surface); border: 2px solid var(--belt-ink); border-radius: var(--r-card, var(--r-lg)); box-shadow: var(--shadow), 0 0 var(--s6) color-mix(in srgb, var(--belt) 35%, transparent); text-align: center; animation: slap 240ms cubic-bezier(.3, 1.6, .5, 1) both; }
.slap b { font: 600 var(--t-2xl)/1.1 var(--font-display); font-variant-numeric: tabular-nums; }
.slap-name { position: relative; padding-block: var(--s6); }
.stamp { position: absolute; right: var(--s3); bottom: -10px; transform: rotate(-8deg); padding: 2px var(--s3); border: 3px solid var(--good); border-radius: var(--r-sm); color: var(--good); background: var(--surface); font: 700 var(--t-lg) var(--font-display); text-transform: uppercase; letter-spacing: .06em; }
.slap-level { font: 600 var(--t-md) var(--font-display); text-transform: uppercase; letter-spacing: .02em; color: var(--belt-ink); }
.slap-coins { grid-auto-flow: column; justify-content: center; align-items: center; }
.slap-coins img { width: 56px; height: 56px; object-fit: contain; }
@keyframes slap { 0% { opacity: 0; transform: scale(1.8) rotate(-6deg); } 60% { opacity: 1; transform: scale(.95) rotate(1deg); } 100% { opacity: 1; transform: none; } }
@keyframes thud { 0%, 100% { transform: none; } 30% { transform: translate(-3px, 2px); } 60% { transform: translate(3px, -2px); } }
@media (prefers-reduced-motion: reduce) { .slap, .celebrate.thud .celebrate-stack { animation: none; } }

/* reading, writing and maths as pictures (kit/art.js icon; Luke's art, 10 Oct).
   Three square frames in one strip; .play loops them, holding the first. */
.area-ico { display: inline-block; flex: none; width: 24px; height: 24px; vertical-align: middle; background: 0 0 / 300% 100% no-repeat; }
.area-ico.play { animation: area-play 2.4s steps(1) infinite; }
@keyframes area-play { 0%, 55% { background-position: 0 0; } 70% { background-position: 50% 0; } 85% { background-position: 100% 0; } 100% { background-position: 0 0; } }
@media (prefers-reduced-motion: reduce) { .area-ico.play { animation: none; } }
.hero-level { display: inline-flex; align-items: center; gap: var(--s1); }
.hero-level .area-ico { width: 20px; height: 20px; }
.slap-level { display: inline-flex; align-items: center; gap: var(--s2); }
.slap-level .area-ico { width: 40px; height: 40px; }

/* the skill clock (BBArt.clock): one slice per practice, filling as he goes. On the dojo page
   and beside every skill title. Moved here from shell.css so skill pages have it too. */
.clock { width: 32px; height: 32px; display: block; overflow: visible; }
.clock .rim { fill: none; stroke: var(--rule-2); stroke-width: 2; }
.clock .fill { fill: var(--belt); }
.clock .cut { stroke: var(--rule-2); stroke-width: 1.5; }
.clock .fill + .cut, .clock .cut { stroke-linecap: round; }
.clock .tick { fill: none; stroke: var(--ground); stroke-width: 2.6; stroke-linecap: round; stroke-linejoin: round; }
:root[data-belt="green"] .clock .tick, :root[data-belt="blue"] .clock .tick, :root[data-belt="purple"] .clock .tick, :root[data-belt="brown"] .clock .tick, :root[data-belt="black"] .clock .tick, :root[data-belt="red"] .clock .tick { stroke: var(--ink); }
.clock .lock { fill: none; stroke: var(--muted); stroke-width: 1.6; stroke-linecap: round; }

/* the skill's clock beside the title (Luke, 10 Oct) */
.title-row { display: flex; align-items: center; gap: var(--s3); }
.title-row .clock { flex: none; width: 36px; height: 36px; }
.title-row .clock .rim { stroke: var(--belt-ink); }

/* in a test, the clock is the countdown (BBArt.countdown; Luke, 10 Oct): a ring in the belt
   colour that drains as the seconds go, the seconds left inside. Red for the last 10. */
.title-row .timer { width: 44px; height: 44px; }
.timer .rim { stroke: var(--rule-2); stroke-width: 3; }
.title-row .timer .rim { stroke: var(--rule-2); }
.timer .left { fill: none; stroke: var(--belt-ink); stroke-width: 3; stroke-linecap: round; transition: stroke-dashoffset 250ms linear; }
.timer text { fill: var(--ink); font: 600 11px var(--font-num); font-variant-numeric: tabular-nums; }
.timer.low .left { stroke: var(--crit); }
.timer.low text { fill: var(--crit); }
@media (prefers-reduced-motion: reduce) { .timer .left { transition: none; } }

/* lives: Luke's pixel heart art (12 Oct), a three-frame strip that beats on a loop:
   small, medium, large, medium. Every heart in the app uses it. An empty heart is the
   small frame in grey and stays still. */
.hearts { display: inline-flex; gap: 3px; align-items: center; }
.heart { width: 22px; height: 22px; display: block; background: url(/assets/hearts/heart-beat.png) 0 0 / 300% 100% no-repeat; }
.heart.full { animation: heart-beat 1.4s steps(1) infinite; }
@keyframes heart-beat {
  0% { background-position: 0 0; }
  55% { background-position: 50% 0; }
  65% { background-position: 100% 0; }
  80% { background-position: 50% 0; }
  90%, 100% { background-position: 0 0; }
}
.heart.empty { filter: grayscale(1) brightness(.55); opacity: .55; }
.heart.breaking { animation: heart-break 600ms var(--ease) both; }
@keyframes heart-break {
  0% { transform: scale(1); filter: none; opacity: 1; }
  25% { transform: scale(1.35) rotate(-8deg); filter: none; opacity: 1; }
  50% { transform: scale(1.1) rotate(6deg) translateX(-1px); }
  100% { transform: none; filter: grayscale(1) brightness(.55); opacity: .55; }
}
.hud-lives { display: inline-flex; }
.gate-lives { grid-template-columns: auto 1fr auto; }
.gate-lives .hearts .heart { width: 18px; height: 18px; }
@media (prefers-reduced-motion: reduce) { .heart.full, .heart.breaking { animation: none; } }

/* The top of every skill screen (Luke, 11 Oct). The hero about 15% smaller
   (76 tall) in the top right, standing just above the XP line, his streak
   under him. On the left, the coins stacked on his level (the subject's
   picture and "Level 8"). Then the XP line in place of the old rule: "XP /
   XP for the next level" on its left and a very thin line whose fill glows
   softly in his belt colour and fills slowly. */
.hero-box { top: var(--s6); gap: 2px; justify-items: center; }
.hero-stage { width: 64px; height: 76px; }
.hero-box .hero { height: 76px; }
.hero-fire { width: 64px; height: 80px; }
.hero-stats { justify-items: center; }
.skill-app .top { min-height: 32px; align-items: start; padding-right: 0; }
.skill-app .hud { padding-right: 0; padding-bottom: 0; border-bottom: 0; gap: var(--s2) var(--s4); }
.skill-app .hud-pop { right: 0; }
.skill-app .title { padding-right: 0; min-height: 0; }
.hud-stack { display: grid; gap: var(--s1); justify-items: start; }
.hud-stack .hud-coins, .hud-stack .hud-lv { min-height: 24px; }
.hud-stack .area-ico, .hud-stack .coin-pile { width: 24px; height: 24px; }
.hud-lv { display: inline-flex; align-items: center; gap: var(--s1); }
.hud-xp { flex-basis: 100%; display: flex; align-items: center; gap: var(--s3); }
.hud-xp-num { font: 600 var(--t-xs) var(--font-num); font-variant-numeric: tabular-nums; color: var(--ink-2); white-space: nowrap; }
.hud-xp-bar { flex: 1; height: 3px; border-radius: var(--r-full); background: var(--rule); }
.hud-xp-bar i { display: block; height: 100%; border-radius: var(--r-full); background: var(--belt); box-shadow: 0 0 var(--s2) color-mix(in srgb, var(--belt) 70%, transparent), 0 0 2px var(--belt); transition: width var(--motion-reveal) var(--ease-out); }

/* level up (Luke, 11 Oct): one card on the fuzzy screen, stamped like PASSED */
.slap-levelup { position: relative; padding-block: var(--s5) var(--s6); }
.slap-levelup .area-ico { width: 88px; height: 88px; }
.slap-levelup .lu-area { font: 600 var(--t-md) var(--font-display); text-transform: uppercase; letter-spacing: .04em; color: var(--belt-ink); }
.slap-levelup b { font-size: var(--t-3xl); }
.level-up .stamp { border-color: var(--belt-ink); color: var(--belt-ink); }

/* The ✓ beside a number box on a touch screen (kit.js: an iPhone keypad has no Enter). */
.kb-go { display: inline-grid; place-items: center; vertical-align: middle; margin-left: var(--s2); min-width: 44px; min-height: 44px; padding: 0 var(--s3); border: 0; border-radius: var(--r-md); background: var(--belt-ink); color: var(--surface); font: inherit; font-size: var(--t-lg); line-height: 1; cursor: pointer; touch-action: manipulation; }

/* Demos (kit.js playDemo): the real board answering itself. A pointer, and the keys it presses. */
.demo-host { display: grid; justify-items: center; gap: var(--s3); }
.demo-on .stage { pointer-events: none; }
.demo-pointer { position: fixed; left: 0; top: 0; width: 32px; height: 32px; margin: -3px 0 0 -7px; opacity: 0; transform: translate(50vw, 110vh); transition: transform 600ms var(--ease), opacity 200ms; pointer-events: none; z-index: 60; filter: drop-shadow(0 2px 3px rgba(0,0,0,.45)); }
.demo-pointer svg { width: 100%; height: 100%; fill: #fff; stroke: #111; stroke-width: 1.4; stroke-linejoin: round; }
.demo-pointer.on { opacity: 1; }
.demo-pointer.click { scale: .82; }
.demo-keys { display: grid; place-items: center; min-height: 2.8em; }
.demo-key { display: grid; place-items: center; min-width: 2.4em; height: 2.4em; padding: 0 var(--s2); border: 2px solid var(--rule-2); border-bottom-width: 4px; border-radius: 8px; background: var(--surface-2); font: 700 var(--t-lg) var(--font-body); color: var(--ink); }
@media (prefers-reduced-motion: reduce) { .demo-pointer { transition: opacity 200ms; } }

/* Type it yourself (kit.js typeIt): the blocks are right; he types what they show. */
.type-it .mani-live { visibility: hidden; }
.type-it.ti-help .mani-live { visibility: visible; }
.type-it > :not(.mani-sum):not(.mani-live) { pointer-events: none; }
.ti-in { min-width: 2.2ch; }
/* The sign key (kit.js BB.signKey, 12 Oct): in front of a box whose answer can be
   negative. A faint + in a dashed box; a tap flips it to a solid − in the belt colour. */
.bb-slot { display: inline-flex; align-items: center; gap: .08em; white-space: nowrap; vertical-align: baseline; }
.bb-sign { font: inherit; font-size: .8em; font-weight: 600; line-height: 1; min-width: 1.3em; min-height: 1.3em; padding: 0 .1em;
  color: var(--muted); background: transparent; border: 2px dashed var(--rule-2); border-radius: var(--r-sm);
  cursor: pointer; touch-action: manipulation; transition: color var(--motion-fast) var(--ease), border-color var(--motion-fast) var(--ease); }
.bb-sign.on { color: var(--belt-ink); border-style: solid; border-color: var(--belt-ink); background: color-mix(in srgb, var(--belt) 18%, transparent); }
@media (pointer: coarse) { .bb-sign { min-width: 40px; min-height: 44px; } }
@media (hover: hover) { .bb-sign:not(:disabled):hover { border-color: var(--belt-ink); } }
.bb-sign:focus-visible { outline: 2px solid var(--belt-ink); outline-offset: 2px; }
.bb-sign:disabled { cursor: default; }
.bb-sign:disabled:not(.on) { visibility: hidden; }
.good .bb-sign.on { color: var(--good); border-color: var(--good); background: transparent; }
.not .bb-sign.on { color: var(--crit); border-color: var(--crit); background: transparent; }
.bb-bump { animation: bb-bump 240ms var(--ease); }
@keyframes bb-bump { 40% { filter: brightness(1.4); scale: 1.08; } }
@media (prefers-reduced-motion: reduce) { .bb-bump { animation: none; } }
/* a fraction (kit.js typeFrac): [whole] then top over a line over bottom, boxed as Bu-NU6's pad */
.ti-mix { display: inline-flex; align-items: center; gap: .14em; vertical-align: middle; }
.ti-fr { display: inline-flex; flex-direction: column; align-items: center; gap: .1em; font-size: .62em; }
.ti-mix .ti-in { width: 2.4ch; padding: .04em 0; line-height: 1.1; border: 2px solid var(--belt-ink); border-radius: var(--r-sm); background: color-mix(in srgb, var(--belt) 8%, transparent); }
.ti-mix .ti-in:focus { border-color: var(--ink); }
.ti-mix .ti-in.ti-no { border-color: var(--crit); }
.ti-line { display: block; width: 130%; height: .1em; border-radius: var(--r-full); background: currentColor; }
.ti-no { animation: ti-wobble .35s; border-bottom-color: var(--crit); }
@keyframes ti-wobble { 0%, 100% { transform: none; } 25% { transform: translateX(-4px); } 75% { transform: translateX(4px); } }
@media (prefers-reduced-motion: reduce) { .ti-no { animation: none; } }

/* his outfit (kit/outfit.js, Luke 11 Oct): the shop's character in what he's
   wearing replaces the old hero everywhere. Two poses in one strip: he
   stands, bobbing a touch, and throws a punch for a moment when he changes. */
/* five poses side by side (kit/outfit.js POSE): 0 stand, 1 run stride,
   2 run passing, 3 punch, 4 crouch; each pose is 25% further along */
html[data-outfit] .hero { background-image: var(--hero-img); background-size: 500% 100%; aspect-ratio: 224 / 256; animation: outfit-bob 1.6s steps(1) infinite; }
@keyframes outfit-bob { 0% { background-position: 0 0; } 50%, 100% { background-position: 0 2px; } }
html[data-punch] .hero { animation: none; background-position: 75% 0; }

/* his streak in training moves him (Luke, 12 Oct), top right of a skill:
   Ready stands still · Warming up bobs · On fire bobs quicker ·
   Blazing bobs and punches · Unstoppable runs flat out. A miss puts him back
   to standing; in a test he stands (right or wrong is never shown there). */
html[data-outfit] .hero-box .hero[data-tier="0"] { animation: none; background-position: 0 0; }
html[data-outfit] .hero-box .hero[data-tier="1"] { animation: outfit-bob 1.6s steps(1) infinite; }
html[data-outfit] .hero-box .hero[data-tier="2"] { animation: outfit-bob-quick 0.8s steps(1) infinite; }
html[data-outfit] .hero-box .hero[data-tier="3"] { animation: outfit-bob-punch 1.6s steps(1) infinite; }
html[data-outfit] .hero-box .hero[data-tier="4"] { animation: outfit-run 0.36s steps(1) infinite; }
@keyframes outfit-bob-quick { 0% { background-position: 0 0; } 50%, 100% { background-position: 0 3px; } }
@keyframes outfit-bob-punch {
  0% { background-position: 0 0; } 25% { background-position: 0 3px; }
  50% { background-position: 75% 0; } 70% { background-position: 0 0; }
  85%, 100% { background-position: 0 3px; }
}
@keyframes outfit-run { 0% { background-position: 25% 0; } 50%, 100% { background-position: 50% 0; } }
@media (prefers-reduced-motion: reduce) { html[data-outfit] .hero, html[data-outfit] .hero-box .hero[data-tier] { animation: none; } }

/* scrollbars (Luke, 11 Oct): dark everywhere in the app, light at black belt
   where the lights come on. From the tokens, so it flips with the theme. */
:root { color-scheme: dark; }
:root[data-dojo-theme="dojo-light"] { color-scheme: light; }
* { scrollbar-color: var(--rule-2) var(--ground); scrollbar-width: thin; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: var(--ground); }
::-webkit-scrollbar-thumb { background: var(--rule-2); border-radius: var(--r-full); border: 2px solid var(--ground); }
::-webkit-scrollbar-thumb:hover { background: var(--muted); }
::-webkit-scrollbar-corner { background: var(--ground); }

/* a chest after a pass (kit/chest.js, Luke 11 Oct): shut, shaking open, open;
   the coins, then the outfit as a dark shape that lights up */
.chest-veil { position: fixed; inset: 0; z-index: 46; display: grid; place-items: center; padding: var(--s4); background: color-mix(in srgb, var(--ground) 60%, transparent); backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); transition: opacity var(--motion-base) var(--ease); }
.chest-veil.out { opacity: 0; }
.chest-box { width: min(100%, 360px); display: grid; justify-items: center; gap: var(--s3); padding: var(--s5); background: var(--surface); border: 2px solid var(--belt-ink); border-radius: var(--r-lg); box-shadow: var(--shadow), 0 0 var(--s6) color-mix(in srgb, var(--belt) 30%, transparent); text-align: center; }
.chest-art { width: 160px; height: 160px; object-fit: contain; }
.chest-art.shut { animation: chest-in var(--motion-base) var(--ease-out); }
.chest-art.shake { animation: chest-shake 160ms linear infinite; }
@keyframes chest-in { from { transform: scale(.7); opacity: 0; } to { transform: none; opacity: 1; } }
@keyframes chest-shake { 0%, 100% { transform: rotate(0); } 25% { transform: rotate(-4deg); } 75% { transform: rotate(4deg); } }
.chest-loot { display: grid; justify-items: center; gap: var(--s3); min-height: 8px; }
.chest-coins { position: relative; display: inline-flex; align-items: center; gap: var(--s2); animation: slap 240ms cubic-bezier(.3, 1.6, .5, 1) both; }
.chest-coins img { width: 40px; height: 40px; object-fit: contain; }
.chest-coins b { font: 600 var(--t-2xl) var(--font-display); font-variant-numeric: tabular-nums; }
.chest-spin { position: absolute; left: 4px; top: -28px; width: 32px; height: 32px; background: url(/assets/coins/coin-spin.png) 0 0 / 1600% 100% no-repeat; animation: coin-spin 800ms steps(16, jump-none) 1, coin-rise 800ms var(--ease-out) 1 both; }
.chest-item { position: relative; display: grid; justify-items: center; gap: var(--s1); animation: slap 240ms cubic-bezier(.3, 1.6, .5, 1) both; }
.chest-pic { width: 120px; height: 120px; object-fit: contain; transition: filter var(--motion-slow) var(--ease); }
.chest-pic.sil { filter: brightness(0) opacity(.45); }
.chest-pic.glow { filter: drop-shadow(0 0 var(--s4) color-mix(in srgb, var(--belt) 60%, transparent)); }
.chest-name { font: 600 var(--t-lg) var(--font-display); }
.chest-item .stars { color: var(--ink); } .chest-item .stars .off { color: var(--rule); }
.chest-spare { position: absolute; top: 0; right: -8px; padding: 2px var(--s2); border-radius: var(--r-full); background: var(--surface-2); border: 1px solid var(--rule-2); font: 600 var(--t-xs) var(--font-num); }
.chest-needs { font-size: var(--t-sm); color: var(--ink-2); }
.chest-row { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--s2); }
@media (prefers-reduced-motion: reduce) { .chest-art.shut, .chest-art.shake, .chest-coins, .chest-item, .chest-spin { animation: none; } }
.chest-hearts { display: inline-flex; align-items: center; gap: var(--s2); animation: slap 240ms cubic-bezier(.3, 1.6, .5, 1) both; }
.chest-hearts .heart { width: 32px; height: 32px; }
.chest-hearts b { font: 600 var(--t-xl) var(--font-display); font-variant-numeric: tabular-nums; color: var(--crit); }
@media (prefers-reduced-motion: reduce) { .chest-hearts { animation: none; } }
/* spare hearts from chests, beside his lives on the test bar */
.hud-lives { align-items: center; gap: var(--s1); }
.hud-spare { font: 600 var(--t-xs) var(--font-num); font-variant-numeric: tabular-nums; color: var(--crit); }

/* XP beside the answer (kit.js xpDrop, Luke 12 Oct): about twice the old size,
   in his streak's colour, drifting up and fading in about a second; to the
   right of the card, never over his work */
.xp-drop { position: fixed; z-index: 30; display: grid; justify-items: start; gap: var(--s1); pointer-events: none; color: var(--belt-ink); animation: xp-drop 1000ms var(--ease-out) both; }
.xp-drop b { font: 700 var(--t-xl)/1 var(--font-display); font-variant-numeric: tabular-nums; text-shadow: 0 2px 8px color-mix(in srgb, var(--ground) 70%, transparent); white-space: nowrap; }
.xp-drop[data-tier="1"] { color: var(--streak-1); }
.xp-drop[data-tier="2"] { color: var(--streak-2); }
.xp-drop[data-tier="3"], .xp-drop[data-tier="4"] { color: var(--streak-3); }
.xp-drop[data-tier="4"] b { text-shadow: 0 0 var(--s3) color-mix(in srgb, var(--streak-3) 60%, transparent); }
.xp-drop-coin { display: inline-flex; align-items: center; gap: var(--s1); font: 700 var(--t-lg)/1 var(--font-num); font-variant-numeric: tabular-nums; color: var(--ink); }
.xp-drop-spin { width: 28px; height: 28px; background: url(/assets/coins/coin-spin.png) 0 0 / 1600% 100% no-repeat; animation: coin-spin 800ms steps(16, jump-none) 1 both; }
@keyframes xp-drop { 0% { opacity: 0; transform: translateY(6px) scale(.9); } 15% { opacity: 1; transform: none; } 70% { opacity: 1; } 100% { opacity: 0; transform: translateY(-24px); } }
@keyframes xp-fade { 0% { opacity: 0; } 15%, 70% { opacity: 1; } 100% { opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .xp-drop { animation-name: xp-fade; } .xp-drop-spin { animation: none; } }
