/* ============================================================
   Algorithm Trainer — design tokens
   Copied from the house style already live on jessecubez.com and
   the CFOP Coach (see colors_and_type.css and cfop-coach/index.html).
   Cream paper, hard black strokes, chunky "pop" shadows.

   FONTS: Baloo 2 (display) and Nunito (body), fetched once at build time by
   trainer/build/fonts.mjs (`npm run trainer:fonts`) into trainer/src/fonts/
   and committed -- SIL Open Font License, so shipping the binaries is fine.
   The deployed page never talks to a font CDN at runtime; @font-face below
   points only at local files. Google serves both families as VARIABLE
   fonts, so each is ONE file (baloo2.woff2, nunito.woff2) covering a
   font-weight RANGE rather than one file per weight -- do not split these
   back into per-weight files, that ships byte-identical duplicates and
   makes a phone fetch every one of them. font-display: swap plus a
   system-font fallback in every --font-* token means a missing/slow file
   degrades to a system sans instead of breaking.
   ============================================================ */

/* A class with `display:` set beats the browser's [hidden] rule, so a
   panel someone marks [hidden] can still show up on screen. This has to
   be the very first rule so nothing later can out-specificity it. */
[hidden] { display: none !important; }

@font-face {
  font-family: 'Baloo 2';
  font-style: normal;
  font-weight: 600 800;
  font-display: swap;
  src: url('fonts/baloo2.woff2') format('woff2');
}
@font-face {
  font-family: 'Nunito';
  font-style: normal;
  font-weight: 700 800;
  font-display: swap;
  src: url('fonts/nunito.woff2') format('woff2');
}

:root {
  --bg: #FBF8F1;
  --bg2: #F3EEE2;
  --card: #FFFFFF;
  --ink: #181311;
  --ink-soft: #5b524c;
  --dim: #93887d;
  --line: #EAE1D1;
  --line-2: #ddd0ba;

  --green: #1EB85A;
  --green-d: #14843F;
  --yellow: #FFC61A;
  --red: #F5333B;
  --blue: #1E74E8;
  --orange: #FF7A1F;
  --cyan: #10C2DC;

  --tint-green: #E7F7E1;
  --tint-blue: #E2F0FE;
  --tint-yellow: #FEF7CC;
  --tint-red: #FFE5E5;

  --pop: 3px 3px 0 var(--ink);
  --soft: 0 6px 20px rgba(24, 19, 17, .08);
  --radius: 16px;
  --radius-lg: 22px;
  --radius-pill: 999px;

  /* Brand faces first, system stack as the fallback -- see FONTS note above. */
  --font-display: 'Baloo 2', system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-body: 'Nunito', system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;

  --tabbar-height: 68px;
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  min-height: 100vh;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-weight: 700;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

h1, h2, h3 {
  font-family: var(--font-display);
  color: var(--ink);
  margin: 0 0 8px;
}
h1 { font-size: 26px; font-weight: 800; line-height: 1.15; }
h2 { font-size: 19px; font-weight: 700; }
h3 { font-size: 16px; font-weight: 700; }

p { margin: 0 0 12px; color: var(--ink-soft); }
p:last-child { margin-bottom: 0; }

a { color: inherit; }

button {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 15px;
  cursor: pointer;
  border: none;
  border-radius: var(--radius-pill);
  padding: 12px 20px;
}
/* .primary/.ghost also match <a> -- several screens (teach.js's "Back to
   Learn", review.js's "Back to dashboard", case-view.js's "Learn this
   case") use an anchor styled as a CTA button rather than a real <button>,
   since they navigate via href rather than a click handler. Without this,
   those links render as plain unstyled text -- caught by eye on Task 10's
   case-view.js screenshot, and it silently affected the two earlier links
   too. */
button.primary, a.primary {
  background: var(--green);
  color: #fff;
  border: 2px solid var(--ink);
  box-shadow: var(--pop);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 15px;
  border-radius: var(--radius-pill);
}
button.ghost, a.ghost {
  background: var(--card);
  color: var(--ink);
  border: 2px solid var(--line-2);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 15px;
  border-radius: var(--radius-pill);
}

/* ---------- header ---------- */
.app-header {
  position: sticky;
  top: 0;
  z-index: 20;
  background: rgba(251, 248, 241, .92);
  backdrop-filter: blur(8px);
  border-bottom: 2px solid var(--line);
  padding: 10px 16px;
}
.brand {
  display: flex;
  align-items: center;
  gap: 9px;
  text-decoration: none;
  min-height: 44px;
}
.brand-mark {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 10px;
  background: var(--green);
  color: #fff;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 13px;
  border: 2px solid var(--ink);
}
.brand-name {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 16px;
  color: var(--ink);
}
.brand-name b { color: var(--green); }

/* ---------- main / layout ---------- */
main#app {
  display: block;
  width: 100%;
  max-width: 720px;
  margin: 0 auto;
  padding: 16px 16px calc(var(--tabbar-height) + 24px + env(safe-area-inset-bottom));
}

.wrap { width: 100%; }

.panel {
  background: var(--card);
  border: 2px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 18px;
  margin-bottom: 14px;
  box-shadow: var(--soft);
}
.panel.placeholder p.hint {
  font-size: 13px;
  color: var(--dim);
  font-weight: 600;
}

/* ---------- bottom tab bar ----------
   Four targets: Learn, Review, Map, Settings. Each anchor is a flex column
   at least 44px tall for a comfortable thumb target, laid out with flex:1
   so the bar never needs a width wider than the viewport. */
nav.tabbar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 20;
  display: flex;
  height: var(--tabbar-height);
  padding-bottom: env(safe-area-inset-bottom);
  background: var(--card);
  border-top: 2px solid var(--line);
}
nav.tabbar a {
  flex: 1 1 0;
  min-width: 0;
  min-height: 44px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  text-decoration: none;
  color: var(--dim);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 11px;
}
nav.tabbar a .ti { font-size: 20px; line-height: 1; filter: saturate(.2) opacity(.65); }
nav.tabbar a .tl { white-space: nowrap; }
nav.tabbar a.active { color: var(--ink); background: var(--bg2); }
nav.tabbar a.active .ti { filter: none; }

/* ============================================================
   Teach screen — the five-step learn loop
   ============================================================ */
.teach-shell { display: flex; flex-direction: column; gap: 14px; }

.progress-dots {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 6px 0 2px;
}
/* The dot itself stays small (14px, 18px when active) -- that is the
   intended visual weight for a secondary step indicator. But "done" and
   "active" dots are REAL navigation (teach.js wires a click handler to
   every reachable one), so the tappable button is enlarged to the phone
   minimum via a transparent 32x32 hit box with the visible dot drawn as a
   centered ::after -- same picture, a thumb can actually land on it.
   Caught by trainer/test/browser.test.mjs's "no tap target under 32px"
   check (Task 11), which measured 14px here before this fix. */
.progress-dots button {
  padding: 0;
  border: none;
  background: transparent;
  width: 32px;
  height: 32px;
  min-width: 32px;
  min-height: 32px;
  border-radius: 50%;
  cursor: default;
  display: flex;
  align-items: center;
  justify-content: center;
}
.progress-dots button::after {
  content: "";
  box-sizing: border-box;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: 2px solid var(--line-2);
  background: var(--card);
}
.progress-dots button.reachable { cursor: pointer; }
.progress-dots button.done::after { background: var(--green); border-color: var(--ink); }
.progress-dots button.active::after { background: var(--yellow); border-color: var(--ink); width: 18px; height: 18px; }

.step-label {
  text-align: center;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 13px;
  color: var(--dim);
  text-transform: uppercase;
  letter-spacing: .04em;
  margin: -6px 0 4px;
}

.case-name-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  flex-wrap: wrap;
}
.case-name-row .case-tag {
  font-size: 12px;
  font-weight: 700;
  color: var(--dim);
  text-transform: uppercase;
  letter-spacing: .04em;
}

.case-svg-wrap { display: flex; justify-content: center; padding: 10px 0 4px; }
.case-svg-wrap svg { width: 168px; height: 168px; max-width: 100%; }

.twisty-holder {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 260px;
  background: var(--bg2);
  border: 2px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
}
.twisty-holder twisty-player { width: 100%; height: 260px; }
.twisty-loading { color: var(--dim); font-weight: 700; font-size: 14px; }

.twisty-controls, .step-actions {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: 12px;
}
.twisty-controls button, .step-actions button {
  flex: 1 1 0;
  min-height: 44px;
}

.teach-video { width: 100%; border-radius: var(--radius); margin-bottom: 12px; display: block; }

.trigger-chips { display: flex; flex-wrap: wrap; gap: 8px; margin: 8px 0 14px; }
.trigger-chip {
  background: var(--tint-blue);
  border: 2px solid var(--ink);
  border-radius: var(--radius-pill);
  padding: 8px 14px;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 14px;
  min-height: 32px;
  display: flex;
  align-items: center;
}

.mono-block {
  font-family: "SFMono-Regular", ui-monospace, Menlo, Consolas, monospace;
  font-weight: 700;
  font-size: 18px;
  letter-spacing: .02em;
  background: var(--bg2);
  border: 2px solid var(--line-2);
  border-radius: var(--radius);
  padding: 14px;
  word-break: break-word;
  margin: 8px 0 14px;
}
.mono-block.big { font-size: 22px; text-align: center; }

.recognition-cue {
  background: var(--tint-yellow);
  border: 2px solid var(--ink);
  border-radius: var(--radius);
  padding: 12px 14px;
  font-weight: 700;
  margin-bottom: 14px;
}

.recognize-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
  margin: 10px 0 14px;
}
.recognize-tile {
  background: var(--card);
  border: 2px solid var(--line-2);
  border-radius: var(--radius);
  padding: 10px;
  cursor: pointer;
  min-height: 44px;
}
.recognize-tile svg { width: 100%; height: auto; display: block; }
.recognize-tile.chosen-right { border-color: var(--green); background: var(--tint-green); }
.recognize-tile.chosen-wrong { border-color: var(--red); background: var(--tint-red); }
.recognize-tile.reveal-right { border-color: var(--green); }
.recognize-tile[disabled] { cursor: default; }

.feedback-banner {
  border: 2px solid var(--ink);
  border-radius: var(--radius);
  padding: 12px 14px;
  font-weight: 700;
  margin-bottom: 14px;
}
.feedback-banner.ok { background: var(--tint-green); }
.feedback-banner.soft { background: var(--tint-blue); }
.feedback-banner.bad { background: var(--tint-red); }

.done-panel { text-align: center; padding: 6px 0; }
.done-panel .big-check { font-size: 44px; line-height: 1; margin-bottom: 6px; }

/* ---------- move pad (alg-input.js) ----------
   Every button here is a real tap target: min 44x44px, per Task 8's phone-
   first rule. */
.alg-input .seq {
  font-family: "SFMono-Regular", ui-monospace, Menlo, Consolas, monospace;
  font-weight: 700;
  font-size: 18px;
  letter-spacing: .03em;
  background: var(--ink);
  color: #fff;
  border-radius: var(--radius);
  padding: 14px;
  min-height: 24px;
  margin-bottom: 10px;
  word-break: break-word;
}
.alg-input .seq.empty { color: rgba(255,255,255,.55); font-weight: 600; font-size: 14px; }

.alg-input .pad { display: flex; flex-direction: column; gap: 8px; }
.alg-input .pad-row { display: grid; grid-template-columns: repeat(6, 1fr); gap: 6px; }
.alg-input .pad-row-mod { grid-template-columns: repeat(5, 1fr); }
.alg-input .pad-row-util { grid-template-columns: repeat(2, 1fr); }

.alg-input .pad-btn {
  min-width: 44px;
  min-height: 44px;
  padding: 8px 4px;
  background: var(--card);
  border: 2px solid var(--ink);
  border-radius: 10px;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 17px;
  box-shadow: 2px 2px 0 var(--ink);
}
.alg-input .pad-btn:active { box-shadow: none; transform: translate(2px, 2px); }
.alg-input .pad-btn.toggle-btn { background: var(--card); font-size: 12px; }
.alg-input .pad-btn.toggle-btn.active { background: var(--yellow); }
.alg-input .pad-btn.ghost-btn { background: var(--bg2); font-size: 13px; }

/* ============================================================
   Progress map -- Task 10

   Fix-round CRITICAL (found by review, reproducible at 390px and 360px, in
   every progress state, on all three set panels): the wordmark badge used
   to be position:absolute over the header, with a hand-guessed
   padding-right reserving room for it. The guess was wrong by ~26px
   (needed ~122px, had 96px) and "mastered" got its last letter swallowed by
   the pill. A guessed pixel reservation is exactly the kind of fix that
   regresses the next time either string's length changes (a longer count
   like "119 of 119 mastered", a translated label, a wider font) -- nobody
   would notice until the next screenshot went out looking broken again.

   Fixed by removing the absolute positioning entirely: the wordmark is now
   a normal flex child sitting IN THE SAME ROW as the text it used to
   overlay (.map-hero-topline for the hero h1, .map-set-meta for each set's
   count), with flex-wrap:wrap and a real gap. Flexbox children cannot
   overlap each other by construction -- if they don't fit on one line, the
   browser wraps them to a new line instead, at any width down to 320px.
   There is no pixel number here to get wrong a second time.
   ============================================================ */
.map-wordmark {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 11px;
  letter-spacing: .02em;
  color: var(--green-d);
  background: var(--tint-green);
  border: 2px solid var(--ink);
  border-radius: var(--radius-pill);
  padding: 4px 10px;
  white-space: nowrap;
  flex-shrink: 0;
}

.map-hero-topline {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 8px 12px;
}
.map-hero-topline h1 { margin: 0; }

.map-counts {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 12px 0;
}
.map-count-row {
  font-family: var(--font-display);
  font-size: 15px;
  color: var(--ink-soft);
}
.map-count-row b { color: var(--ink); font-size: 17px; }

.map-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  padding-top: 10px;
  border-top: 2px solid var(--line);
}
.legend-item {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  font-weight: 700;
  color: var(--ink-soft);
}
.legend-swatch {
  width: 14px;
  height: 14px;
  border-radius: 4px;
  border: 2px solid var(--ink);
  display: inline-block;
}

.map-set-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px 12px;
  flex-wrap: wrap;
  margin-bottom: 12px;
}
.map-set-header h2 { margin: 0; }
.map-set-meta {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-left: auto;
}
.map-set-count {
  font-size: 13px;
  font-weight: 700;
  color: var(--dim);
}

.map-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(32px, 1fr));
  gap: 6px;
}
.map-tile {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 1;
  min-width: 32px;
  min-height: 32px;
  border-radius: 6px;
  border: 2px solid var(--line-2);
  padding: 3px;
  text-decoration: none;
}
.map-tile svg { width: 100%; height: 100%; display: block; }
.map-tile { position: relative; }

/* Shared by both the map tiles AND the legend swatches -- bare classes, not
   scoped to .map-tile, so the legend (which is NOT a .map-tile) still shows
   real colour instead of an empty bordered box.

   The heat-map ladder: progress is carried by the TILE GROUND, not the
   picture inside it (the case pictures are necessarily similar to each
   other -- that's just what OLL/PLL/F2L diagrams look like). An untouched
   case (tile-s0) sits on the same plain ground as any other unfilled panel
   on the site. tile-s1/tile-s2 warm toward brand green in two visible
   steps as the weakest of the three skills climbs, so the grid reads as a
   genuine gradient rather than a flat on/off map. tile-mastered is the one
   tier that gets the SAME "earned" treatment the rest of the site reserves
   for something finished: solid brand green, the hard black stroke, the
   pop shadow, plus a small corner check -- scanning the grid should show
   the shape of your progress from across the room, with nothing to read. */
.tile-s0 { background: var(--bg2); border-color: var(--line-2); }
.tile-s1 { background: #DFF4E4; border-color: #A9D9B4; }
.tile-s2 { background: #A9E2B7; border-color: #4FB868; }
.tile-mastered {
  background: var(--green);
  border: 2px solid var(--ink);
  box-shadow: var(--pop);
}
.map-tile.tile-mastered::after {
  content: "✓";
  position: absolute;
  bottom: 1px;
  right: 1px;
  width: 15px;
  height: 15px;
  border-radius: 50%;
  background: var(--ink);
  color: #fff;
  font-size: 9px;
  font-weight: 800;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
}

.map-tile.gated { opacity: .55; }
.map-tile.gated::before {
  content: "🔒";
  position: absolute;
  top: -2px;
  right: -2px;
  font-size: 11px;
  line-height: 1;
}

/* ---------- hero stat: the one number worth a screenshot ----------
   A CSS conic-gradient ring, not a canvas or SVG chart -- zero extra
   render cost alongside 119 tiles, and it never counts toward the "at most
   2 live 3D contexts" or "zero canvas" rules because it is neither. --pct
   is set inline per render (0-100) and read by calc() inside the gradient
   stop. */
.hero-stat {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  margin: 4px 0 18px;
}
.hero-ring {
  --pct: 0;
  width: 148px;
  height: 148px;
  border-radius: 50%;
  background: conic-gradient(var(--green) calc(var(--pct) * 1%), var(--bg2) 0);
  border: 3px solid var(--ink);
  box-shadow: var(--pop);
  display: flex;
  align-items: center;
  justify-content: center;
}
.hero-ring-inner {
  width: 114px;
  height: 114px;
  border-radius: 50%;
  background: var(--card);
  border: 2px solid var(--line);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}
.hero-number {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 40px;
  line-height: 1;
  color: var(--ink);
}
.hero-of {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 13px;
  color: var(--dim);
  margin-top: 2px;
}
.hero-stat-label {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--ink-soft);
}

/* ============================================================
   Case reference page -- Task 10
   ============================================================ */
.gate-banner {
  background: var(--tint-yellow);
  border: 2px solid var(--ink);
  border-radius: var(--radius);
  padding: 14px;
  margin-bottom: 14px;
}
.gate-banner p { color: var(--ink); font-weight: 700; margin-bottom: 10px; }
.gate-toggle {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 32px;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 14px;
  cursor: pointer;
}
.gate-toggle input { width: 20px; height: 20px; margin: 0; }

.alt-list { display: flex; flex-direction: column; gap: 8px; }
.alt-note { font-family: var(--font-body); font-weight: 700; font-size: 12px; color: var(--dim); margin-top: 4px; }

.mistake-list { margin: 0 0 14px; padding-left: 20px; color: var(--ink-soft); font-weight: 600; }
.mistake-list li { margin-bottom: 8px; }
.mistake-fix { display: block; font-size: 13px; color: var(--dim); margin-top: 2px; }

.strength-bars { display: flex; flex-direction: column; gap: 12px; margin-bottom: 6px; }
.strength-row-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: 4px;
}
.strength-skill { font-family: var(--font-display); font-weight: 700; font-size: 14px; }
.strength-status { font-size: 12px; font-weight: 700; color: var(--dim); }
.strength-track {
  height: 10px;
  border-radius: var(--radius-pill);
  background: var(--bg2);
  border: 2px solid var(--line-2);
  overflow: hidden;
}
.strength-fill {
  height: 100%;
  background: var(--green);
  border-radius: var(--radius-pill);
}

/* ============================================================
   Settings + first run -- Task 10
   ============================================================ */
.pick-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 8px;
  margin: 10px 0 4px;
}
.pick-btn {
  min-height: 44px;
  padding: 10px 12px;
  background: var(--card);
  border: 2px solid var(--ink);
  border-radius: 12px;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 14px;
  box-shadow: 2px 2px 0 var(--ink);
}
.pick-btn:active { box-shadow: none; transform: translate(2px, 2px); }
.pick-btn.active { background: var(--yellow); }
.pick-btn.ghost-btn { background: var(--bg2); grid-column: 1 / -1; }
.pick-btn.small {
  min-height: 44px;
  min-width: 44px;
  padding: 10px 0;
}
.pick-grid.pick-grid-6 { grid-template-columns: repeat(6, 1fr); }
