/* Batch kids card player — mobile-first (390px), file:// safe */
* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
:root {
  --bg: #FFF6E9; --card: #FFFFFF; --ink: #3A3042; --sub: #8a7a64;
  --coral: #FF6B6B; --teal: #2EC4B6; --sun: #FFBF3F; --purple: #9B5DE5;
  --blue: #4CC9F0; --green: #57CC99; --soft: #F2E4CE;
  --good: #57CC99; --good-deep: #3da877; --bad: #FF6B6B; --bad-deep: #d94f4f;
  --radius: 20px;
}
html, body { margin: 0; padding: 0; background: var(--bg); color: var(--ink);
  font-family: ui-rounded, -apple-system, "Segoe UI", system-ui, sans-serif;
  /* no sideways drift / rubber-banding on phones (ported from reps duo.css, 2026-07-16) */
  overflow-x: hidden; overscroll-behavior: none; }
#app { max-width: 480px; margin: 0 auto; min-height: 100dvh; display: flex; flex-direction: column;
  padding: env(safe-area-inset-top) 0 env(safe-area-inset-bottom); overflow-x: hidden; }

/* ---------- "Easy letters" — dyslexia-friendly font (⚙️ Settings toggle) ----------
   Self-hosted OpenDyslexic (assets/fonts/, precached — offline/file:// safe).
   html.dys-font is set by fx.js from the saved batch_dysfont pref. Diagram SVG
   text stays on the standard stack so hand-tuned diagram layouts never shift. */
@font-face { font-family: "OpenDyslexic"; src: url("../assets/fonts/OpenDyslexic-Regular.woff") format("woff");
  font-weight: 400 700; font-style: normal; font-display: swap; }
@font-face { font-family: "OpenDyslexic"; src: url("../assets/fonts/OpenDyslexic-Bold.woff") format("woff");
  font-weight: 800 900; font-style: normal; font-display: swap; }
html.dys-font, html.dys-font body { font-family: "OpenDyslexic", ui-rounded, -apple-system, "Segoe UI", system-ui, sans-serif;
  letter-spacing: .01em; }
html.dys-font .visual svg text, html.dys-font svg text {
  font-family: ui-rounded, -apple-system, "Segoe UI", system-ui, sans-serif; }

button { font: inherit; color: inherit; cursor: pointer; border: 0; background: none; }
button:disabled { opacity: .45; cursor: default; }
img, svg { max-width: 100%; display: block; }

/* Screen-reader-only text stays available without affecting the 390px layout. */
.sr-only { position: absolute !important; width: 1px !important; height: 1px !important;
  padding: 0 !important; margin: -1px !important; overflow: hidden !important;
  clip: rect(0, 0, 0, 0) !important; white-space: nowrap !important; border: 0 !important; }

/* ---------- chunky button ---------- */
.btn { display: block; width: 100%; padding: 16px 20px; border-radius: 18px;
  font-size: 19px; font-weight: 800; text-align: center; color: #fff;
  background: var(--teal); box-shadow: 0 5px 0 #1e9c8f; transition: transform .06s, box-shadow .06s; }
.btn:active:not(:disabled) { transform: translateY(4px); box-shadow: 0 1px 0 #1e9c8f; }
.btn.alt { background: var(--purple); box-shadow: 0 5px 0 #7a41c4; }
.btn.good { background: var(--good); box-shadow: 0 5px 0 var(--good-deep); }
.btn.bad { background: var(--bad); box-shadow: 0 5px 0 var(--bad-deep); }

/* ---------- home ---------- */
.home { padding: 20px 18px 40px; }
.topbar { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 14px; }
.brand { font-size: 22px; font-weight: 900; letter-spacing: -.5px; flex-shrink: 0; }
.brand em { color: var(--coral); font-style: normal; }
/* topbar right cluster can grow (avatar + ring + ⚙️ + 🏆 + streak + XP); on a narrow
   phone it scrolls horizontally instead of squishing. min-width:0 lets this flex item
   shrink below its content so overflow-x engages (else .topbar overflows the viewport). */
.stats { display: flex; gap: 10px; min-width: 0; overflow-x: auto; -webkit-overflow-scrolling: touch;
  scrollbar-width: none; }
.stats::-webkit-scrollbar { display: none; }
.stats > * { flex-shrink: 0; }
.pill { background: #fff; border-radius: 999px; padding: 7px 13px; font-weight: 800; font-size: 14px;
  box-shadow: 0 2px 0 var(--soft); flex-shrink: 0; white-space: nowrap; }
.coursehead { background: linear-gradient(135deg, #FFE3C2, #FFD6D6); border-radius: 24px;
  padding: 20px; margin-bottom: 26px; }
.coursehead .kicker { font-size: 13px; font-weight: 800; color: var(--coral); text-transform: uppercase; letter-spacing: 1px; }
.coursehead h1 { margin: 4px 0 6px; font-size: 28px; }
.coursehead p { margin: 0; color: var(--sub); font-weight: 600; }
.coverart { border-radius: 18px; margin-top: 14px; }

/* ---------- Learn home: "Up next" hero (v178, 2026-09-15) ----------
   One lesson, one Start. Design: canvas "Batch Kids Home Redesign" → B1 Tall Hero. */
.continue-card { width: 100%; background: var(--card); border-radius: 24px; overflow: hidden;
  box-shadow: 0 6px 0 var(--soft); margin-bottom: 14px; text-align: left; cursor: pointer; }
.hero-art { position: relative; height: 196px; background: var(--soft); }
.hero-cover { width: 100%; height: 196px; object-fit: cover; object-position: center 8%; }
.hero-kicker { position: absolute; left: 14px; top: 14px; background: var(--purple); color: #fff;
  font-size: 11px; font-weight: 900; letter-spacing: .8px; text-transform: uppercase;
  padding: 6px 10px; border-radius: 999px; box-shadow: 0 2px 0 #7a41c4; }
.hero-body { padding: 16px 18px 18px; display: flex; flex-direction: column; gap: 10px; }
.hero-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.hero-title { margin: 0; font-size: 30px; font-weight: 900; letter-spacing: -.5px; line-height: 1.05;
  min-width: 0; overflow-wrap: anywhere; }
.hero-sub { margin: 0; color: var(--sub); font-weight: 700; }
.hero-unit { flex: none; background: var(--bg); color: var(--sub); border-radius: 999px; padding: 7px 11px;
  font-size: 12px; font-weight: 800; min-height: 34px; white-space: nowrap; box-shadow: 0 2px 0 var(--soft); }
.hero-unit:active { transform: translateY(2px); box-shadow: none; }
.hero-meta { display: flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 800; color: var(--sub); flex-wrap: wrap; }
.hero-meta .hero-unit { margin-left: auto; }
.hero-dots { display: flex; gap: 5px; }
.hero-dot { width: 12px; height: 12px; border-radius: 50%; background: var(--soft); display: block; }
.hero-dot.done { background: var(--sun); }
.hero-dot.here { background: var(--teal); box-shadow: 0 0 0 3px rgba(46,196,182,.25); }
.continue-go { display: flex; align-items: center; justify-content: center; gap: 10px;
  padding: 18px 20px; font-size: 21px; margin-top: 4px; order: 9; }
.hero-play { font-size: 17px; line-height: 1; }
.hero-done .hero-body { gap: 12px; }

/* Today row — quests · review · level */
.today-row { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; margin-bottom: 14px; }
.today-tile { display: flex; flex-direction: column; gap: 4px; text-align: left; min-height: 64px;
  background: var(--card); border-radius: 16px; padding: 12px 12px 10px; box-shadow: 0 3px 0 var(--soft); }
.today-tile:active:not(:disabled) { transform: translateY(2px); box-shadow: 0 1px 0 var(--soft); }
.today-tile:disabled { opacity: 1; cursor: default; }
.today-tile.review { background: linear-gradient(135deg, #F3E8FF, #E8F4FF); box-shadow: 0 3px 0 #d7d0e7; }
.today-tile.review:active:not(:disabled) { box-shadow: 0 1px 0 #d7d0e7; }
.tt-kicker { font-size: 11px; font-weight: 900; color: var(--sub); text-transform: uppercase; letter-spacing: .6px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.today-tile.quests .tt-kicker { color: var(--bad-deep); }
.today-tile.review .tt-kicker, .today-tile.review .tt-sub { color: #7a41c4; }
.today-tile.level .tt-kicker { color: #b8830f; }
.tt-num { font-size: 18px; font-weight: 900; line-height: 1.1; }
.tt-num small { color: var(--sub); font-size: 13px; font-weight: 800; }
.tt-sub { font-size: 11px; font-weight: 800; }
.tt-bar { display: block; width: 100%; height: 5px; background: var(--soft); border-radius: 999px; overflow: hidden; margin-top: 2px; }
.tt-bar i { display: block; height: 100%; border-radius: 999px; background: linear-gradient(90deg, var(--sun), var(--coral)); }
.today-tile.quests .tt-bar i { background: linear-gradient(90deg, var(--coral), var(--sun)); }

.browse-row { display: flex; justify-content: center; margin: 4px 0 8px; }
.browse-btn { background: #fff; color: var(--ink); border-radius: 999px; padding: 11px 18px; font-size: 14px;
  font-weight: 800; box-shadow: 0 3px 0 var(--soft); min-height: 44px; }
.browse-btn:active { transform: translateY(2px); box-shadow: 0 1px 0 var(--soft); }

/* tab screens (Quests / Rewards / catalogue) */
.tab-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin: 2px 4px 14px; }
.tab-title { margin: 0; font-size: 24px; font-weight: 900; letter-spacing: -.4px; flex: 1; }
.browse .tab-head { justify-content: flex-start; }
.browse .tab-head .backbtn { margin-bottom: 0; }
.rewards-more { margin-top: 18px; }
.me-tools { margin-left: auto; display: flex; gap: 8px; align-items: center; }

/* ---------- tab bar (fixed, bottom) ----------
   Fixed rather than sticky so it never depends on a screen's own height; every
   screen that renders it carries `has-tabs` so its last row clears the bar. */
.has-tabs { padding-bottom: calc(96px + env(safe-area-inset-bottom)); }
.profile.has-tabs { padding-bottom: 0; }
.profile.has-tabs .pscroll { padding-bottom: calc(100px + env(safe-area-inset-bottom)); }
.tabbar { position: fixed; left: 50%; bottom: 0; transform: translateX(-50%); width: 100%; max-width: 480px;
  z-index: 50; background: #fff; box-shadow: 0 -3px 0 var(--soft);
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 4px;
  padding: 6px 8px calc(10px + env(safe-area-inset-bottom)); }
.tab { position: relative; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 3px; min-height: 58px; border-radius: 16px; color: var(--sub); font-size: 11px; font-weight: 900; }
.tab svg { width: 28px; height: 28px; display: block; }
.tab.on { color: #1e9c8f; background: #E8FBF6; }
.tab.on[data-tab="quests"] { color: #d94f4f; background: #FFE9E9; }
.tab.on[data-tab="rewards"] { color: #b8830f; background: #FFF3D6; }
.tab.on[data-tab="me"] { color: #2aa9d1; background: #E4F6FC; }
.tab:active { transform: translateY(1px); }
.tab-badge { position: absolute; top: 4px; right: 16px; background: var(--coral); color: #fff; font-size: 10px;
  font-weight: 900; border-radius: 999px; padding: 1px 6px; box-shadow: 0 1px 0 #d94f4f; }
@media (min-width: 700px) { .tabbar { max-width: 600px; } }
@media (min-width: 1000px) { .tabbar { max-width: 640px; } }

/* unit list (multi-course home) */
.units { display: flex; flex-direction: column; gap: 16px; }
.unitcard { display: block; width: 100%; background: var(--card); border-radius: 24px;
  padding: 0; text-align: left; box-shadow: 0 5px 0 var(--soft); overflow: hidden;
  transition: transform .06s, box-shadow .06s; }
.unitcard:active:not(:disabled) { transform: translateY(3px); box-shadow: 0 2px 0 var(--soft); }
.unitcard.locked { opacity: .55; }
.unit-cover { width: 100%; height: 110px; object-fit: cover; }
.unit-row { display: flex; align-items: center; gap: 12px; padding: 14px 16px; }
.unit-meta { flex: 1; min-width: 0; }
.unit-kicker { font-size: 16px; font-weight: 900; color: var(--ink); }
.unit-tagline { margin: 3px 0 6px; font-size: 14px; color: var(--sub); font-weight: 600; line-height: 1.3; }
.unit-progress { font-size: 13px; font-weight: 800; color: var(--teal); }
.unitcard.locked .unit-progress { color: #b3a488; }
.unit-badge { width: 44px; height: 44px; min-width: 44px; border-radius: 50%; display: grid;
  place-items: center; font-size: 22px; font-weight: 900; }
.unit-badge.done { background: var(--sun); color: #fff; box-shadow: 0 4px 0 #d99c12; }
.unit-badge.locked { background: var(--soft); }

/* back to units */
.backbtn { display: inline-block; margin-bottom: 8px; padding: 7px 14px; border-radius: 999px;
  background: #fff; font-size: 14px; font-weight: 800; color: var(--ink);
  box-shadow: 0 3px 0 var(--soft); }
.backbtn:active { transform: translateY(2px); box-shadow: 0 1px 0 var(--soft); }

/* lesson path */
.path { display: flex; flex-direction: column; gap: 14px; }
.node { display: flex; align-items: center; gap: 14px; background: var(--card);
  border-radius: var(--radius); padding: 14px 16px; box-shadow: 0 4px 0 var(--soft);
  text-align: left; width: 100%; }
.node:nth-child(odd) { margin-left: 14px; }
.node:nth-child(even) { margin-right: 14px; }
.node .bubble { width: 54px; height: 54px; min-width: 54px; border-radius: 50%; display: grid; place-items: center;
  font-size: 24px; font-weight: 900; color: #fff; background: var(--teal); box-shadow: 0 4px 0 #1e9c8f; }
.node.locked { opacity: .55; }
.node.locked .bubble { background: #cdbfa8; box-shadow: 0 4px 0 #b3a488; }
.node.done .bubble { background: var(--sun); box-shadow: 0 4px 0 #d99c12; }
.trackhead { margin: 22px 4px 2px; }
.trackhead:first-child { margin-top: 4px; }
.trackhead-row { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.trackhead-title { font-size: 20px; font-weight: 900; letter-spacing: -.3px; }
.trackhead-count { font-size: 13px; font-weight: 800; color: var(--sub); flex: none; }
.trackhead.done .trackhead-count { color: var(--good-deep); }
.trackbar { height: 6px; background: var(--soft); border-radius: 999px; overflow: hidden; margin-top: 6px; }
.trackbar > i { display: block; height: 100%; border-radius: 999px;
  background: linear-gradient(90deg, var(--teal), var(--sun)); }
.trackhead.done .trackbar > i { background: linear-gradient(90deg, var(--good), var(--good-deep)); }
@media (prefers-reduced-motion: no-preference) {
  .trackbar > i { transition: width .7s cubic-bezier(.22,1,.36,1); }
}
.node.review { background: linear-gradient(135deg, #F3E8FF, #E8F4FF); margin-bottom: 14px; }
.node.review .bubble { background: var(--purple); box-shadow: 0 4px 0 #7a41c4; font-size: 22px; }
.node .meta { flex: 1; min-width: 0; }
.node .meta h3 { margin: 0 0 2px; font-size: 17px; }
.node .meta span { font-size: 13px; color: var(--sub); font-weight: 700; }
.node .node-thumb { width: 48px; height: 48px; min-width: 48px; border-radius: 12px; object-fit: cover;
  box-shadow: 0 2px 0 var(--soft); margin-left: 2px; }
.node.locked .node-thumb { filter: grayscale(.55); opacity: .7; }

/* ---------- player ---------- */
/* Fill #app's content box (which already subtracts the safe-area insets) instead of
   forcing a 2nd full 100dvh — that double-count pushed the footer/Continue button below
   the home-indicator on notched iPhones (reported on iPhone 16 Pro 2026-06-19). */
.player { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }
.phead { display: flex; align-items: center; gap: 12px; padding: 14px 16px 6px; }
.quit { font-size: 26px; font-weight: 900; color: #c9b793; padding: 4px 8px; }
.progress { flex: 1; height: 16px; background: var(--soft); border-radius: 999px; overflow: hidden; }
.progress i { display: block; height: 100%; background: var(--green); border-radius: 999px;
  transition: width .35s cubic-bezier(.22,1.2,.36,1); }
.stage { flex: 1; padding: 14px 18px 12px; display: flex; flex-direction: column; gap: 14px; overflow-y: auto; }
.prompt { font-size: 21px; font-weight: 800; margin: 0; line-height: 1.3; }
.concept-text { font-size: 22px; font-weight: 700; line-height: 1.35; margin: 0; }
.visual { background: var(--card); border-radius: var(--radius); box-shadow: 0 4px 0 var(--soft); overflow: hidden; }
.visual img { width: 100%; }
/* Portrait diagrams (viewBox 560xH, H≈560–700) render at natural height so they fill the
   phone screen instead of a letterboxed landscape band. max-height is a safety valve only —
   it must stay ABOVE the tallest conforming portrait diagram at the #app content width
   (480px → ~700-tall svg ≈ 555px) so it never actually clamps (a clamped svg letterboxes
   narrower than its .tapwrap and shifts tap_diagram % hotspots off-target). */
.visual svg { width: 100%; height: auto; max-height: 720px; display: block; }

/* choice */
.opts { display: flex; flex-direction: column; gap: 12px; }
.opt { background: var(--card); border: 3px solid var(--soft); border-radius: 18px; padding: 15px 16px;
  font-size: 18px; font-weight: 700; text-align: left; box-shadow: 0 4px 0 var(--soft); }
.opt.sel { border-color: var(--blue); background: #ECFAFF; box-shadow: 0 4px 0 var(--blue); }
.opt.correct { border-color: var(--good); background: #EAFBF2; box-shadow: 0 4px 0 var(--good); }
.opt.wrong { border-color: var(--bad); background: #FFF0F0; box-shadow: 0 4px 0 var(--bad); }

/* sort */
.sort-item { background: var(--card); border-radius: 22px; padding: 26px 18px; text-align: center;
  box-shadow: 0 5px 0 var(--soft); font-size: 22px; font-weight: 800; }
.sort-item .icon { font-size: 56px; display: block; margin-bottom: 8px; }
.sort-count { text-align: center; color: var(--sub); font-weight: 800; font-size: 14px; }
.buckets { display: flex; gap: 12px; }
.bucket { flex: 1; padding: 18px 10px; border-radius: 18px; font-size: 17px; font-weight: 800; color: #fff; }
.bucket.b0 { background: var(--teal); box-shadow: 0 5px 0 #1e9c8f; }
.bucket.b1 { background: var(--purple); box-shadow: 0 5px 0 #7a41c4; }
.bucket:active { transform: translateY(4px); box-shadow: none; }
.sort-item.flash-good { outline: 4px solid var(--good); }
.sort-item.flash-bad { outline: 4px solid var(--bad); animation: shake .35s; }
@keyframes shake { 25% { transform: translateX(-7px);} 75% { transform: translateX(7px);} }

/* fill blank */
.sentence { font-size: 23px; font-weight: 800; line-height: 1.5; }
.gap { display: inline-block; min-width: 110px; border-bottom: 4px dashed #cdbfa8; text-align: center;
  color: var(--purple); padding: 0 6px; }
.bank { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; }
.chip { background: var(--card); border-radius: 14px; padding: 12px 18px; font-size: 18px; font-weight: 800;
  box-shadow: 0 4px 0 var(--soft); border: 3px solid var(--soft); }
.chip.used { opacity: .35; }

/* tap diagram */
.tapwrap { position: relative; }
.hotspot { position: absolute; background: transparent; border-radius: 12px; }
.hotspot.reveal-good { outline: 4px solid var(--good); background: rgba(87,204,153,.18); }
.hotspot.reveal-bad { outline: 4px solid var(--bad); background: rgba(255,107,107,.18); }

/* order steps */
.slots { display: flex; flex-direction: column; gap: 10px; min-height: 60px; }
.slot { background: #FFFDF8; border: 3px dashed #e3d5bb; border-radius: 16px; padding: 13px 14px;
  font-size: 17px; font-weight: 800; display: flex; gap: 10px; align-items: center; }
.slot .num { width: 28px; height: 28px; min-width: 28px; border-radius: 50%; background: var(--sun); color: #fff;
  display: grid; place-items: center; font-size: 15px; }
.pool { display: flex; flex-direction: column; gap: 10px; }

/* speed round */
.speedtrack { height: 14px; background: var(--soft); border-radius: 999px; overflow: hidden; }
.speedfill { display: block; height: 100%; width: 100%; background: var(--sun); border-radius: 999px;
  transition-property: width; transition-timing-function: linear; }
.speed-card { background: var(--card); border-radius: 22px; padding: 30px 18px; text-align: center;
  box-shadow: 0 5px 0 var(--soft); font-size: 22px; font-weight: 800; line-height: 1.35; }
.speed-card.flash-good { outline: 4px solid var(--good); }
.speed-card.flash-bad { outline: 4px solid var(--bad); animation: shake .35s; }
.tfrow { display: flex; gap: 12px; }
.tf { flex: 1; padding: 20px 10px; border-radius: 18px; font-size: 20px; font-weight: 900; color: #fff; }
.tf-true { background: var(--teal); box-shadow: 0 5px 0 #1e9c8f; }
.tf-false { background: var(--coral); box-shadow: 0 5px 0 var(--bad-deep); }
.tf:active { transform: translateY(4px); box-shadow: none; }
.tf.tf-reveal { outline: 4px solid var(--sun); }

/* feedback sheet + footer */
.pfoot { padding: 12px 18px calc(16px + env(safe-area-inset-bottom)); background: var(--bg); }
.sheet { border-radius: 22px 22px 0 0; padding: 16px 18px 8px; margin: 0 -18px 10px; }
.sheet.good { background: #EAFBF2; color: var(--good-deep); }
.sheet.bad { background: #FFF0F0; color: var(--bad-deep); }
.sheet h4 { margin: 0 0 4px; font-size: 20px; }
.sheet p { margin: 0 0 8px; font-weight: 700; color: inherit; }

/* win screen */
.winwrap { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center;
  text-align: center; padding: 30px 26px; gap: 12px; }
.winwrap .burst { font-size: 84px; animation: pop .5s cubic-bezier(.22,1.6,.36,1); }
@keyframes pop { from { transform: scale(.2); } }
.winwrap h2 { margin: 0; font-size: 30px; }
.winwrap .recap { color: var(--sub); font-weight: 700; font-size: 18px; line-height: 1.4; }
.xpchip { background: var(--sun); color: #fff; font-weight: 900; font-size: 22px;
  padding: 12px 26px; border-radius: 999px; box-shadow: 0 5px 0 #d99c12; }

@media (min-width: 500px) {
  #app { border-left: 1px solid #f0e3cc; border-right: 1px solid #f0e3cc; }
}

/* ============================================================
   JUICE / ENGAGEMENT LAYER — 2026-06-18
   Animations + mascot + combo toast + badges + daily ring.
   All motion is gated behind prefers-reduced-motion.
   ============================================================ */

/* topbar: sound toggle + daily-goal ring */
.stats { align-items: center; }
.sound-toggle { background: #fff; border-radius: 999px; padding: 7px 11px; font-size: 16px;
  line-height: 1; box-shadow: 0 2px 0 var(--soft); flex-shrink: 0; }
.sound-toggle:active { transform: translateY(2px); box-shadow: none; }
.daily-ring { position: relative; width: 40px; height: 40px; flex-shrink: 0; }
.daily-ring svg { width: 40px; height: 40px; display: block; transform: rotate(-90deg); }
.daily-ring .dr-num { position: absolute; inset: 0; display: grid; place-items: center;
  font-size: 13px; font-weight: 900; color: var(--ink); }
.daily-ring.hit .dr-num { color: var(--good-deep); }
.dr-arc { transition: stroke-dashoffset .6s cubic-bezier(.22,1,.36,1); }

/* badges shelf on home */
.shelf-head { font-size: 14px; font-weight: 900; color: var(--sub); margin: 2px 4px 6px;
  text-transform: uppercase; letter-spacing: .5px; }
.badges-shelf { display: flex; gap: 10px; overflow-x: auto; padding: 2px 2px 10px;
  margin-bottom: 12px; -webkit-overflow-scrolling: touch; }
.badge-chip { min-width: 66px; height: 70px; border-radius: 18px; background: #fff;
  box-shadow: 0 3px 0 var(--soft); display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 3px; }
.badge-chip .bi { font-size: 28px; line-height: 1; }
.badge-chip .bl { font-size: 9px; font-weight: 800; color: var(--sub); text-align: center;
  padding: 0 4px; line-height: 1.1; }
.badge-chip.locked { background: #FBF4E6; box-shadow: 0 3px 0 #ece0c8; }
.badge-chip.locked .bi { filter: grayscale(1); opacity: .35; }
.badge-chip.locked .bl { opacity: .5; }

/* sticker book shelf on home — a collection that reveals as lifetime XP climbs.
   Mirrors the badges shelf but with a teal tint so the two read as distinct. */
.stickers-shelf { display: flex; gap: 10px; overflow-x: auto; padding: 2px 2px 10px;
  margin-bottom: 12px; -webkit-overflow-scrolling: touch; }
.sticker-chip { min-width: 66px; height: 70px; border-radius: 18px; background: #F1FBF8;
  box-shadow: 0 3px 0 #c8ece5; display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 3px; cursor: pointer; }
.sticker-chip .si { font-size: 28px; line-height: 1; }
.sticker-chip .sl { font-size: 9px; font-weight: 800; color: var(--sub); text-align: center;
  padding: 0 4px; line-height: 1.1; }
.sticker-chip.locked { background: #FBF4E6; box-shadow: 0 3px 0 #ece0c8; }
.sticker-chip.locked .si { opacity: .5; }
.sticker-chip.locked .sl { opacity: .55; letter-spacing: 1px; }
.sticker-chip:active { transform: scale(.94); }

/* mascot "Batch" (PNG stickers from fx.js mascot(); .bo class kept for sizing) */
.bo { width: 100%; height: auto; display: block; }
.win-mascot { width: 138px; margin: 0 auto; }
.sheet-help { font-size: 13.5px; background: rgba(255,255,255,.65); border-radius: 12px;
  padding: 6px 10px; margin-top: 6px !important; }
/* C2 guess-first — a pretest guess is neither right nor wrong, so it gets its own
   neutral-positive sheet instead of the red "bad" one. Purple = the app's thinking
   colour, already used for levels. */
.sheet.guess { background: #F4EDFC; color: #5B3E8A; }
.sheet.guess h4 { color: #5B3E8A; }
/* C1 whyNot — the per-wrong-option explanation. Same shape as .sheet-help so the
   feedback sheet reads as one voice, with a slightly stronger fill because this
   line is the specific reason the kid's own answer missed. */
.sheet-why { font-size: 13.5px; background: rgba(255,255,255,.8); border-radius: 12px;
  padding: 6px 10px; margin-top: 6px !important; font-weight: 700; line-height: 1.4; }
.sheet-mascot { width: 56px; min-width: 56px; }
.sheet.with-mascot { display: flex; align-items: center; gap: 12px; }
.sheet.with-mascot .sheet-body { flex: 1; min-width: 0; }
.sheet.with-mascot h4, .sheet.with-mascot p { margin: 0; }

/* combo + badge toast (transient, centred) */
.fx-toast { position: fixed; left: 50%; top: 16%; z-index: 9999; white-space: nowrap;
  transform: translate(-50%, -10px) scale(.85); opacity: 0; pointer-events: none;
  background: #fff; color: var(--ink); font-weight: 900; font-size: 21px;
  padding: 12px 22px; border-radius: 999px; box-shadow: 0 6px 0 rgba(58,48,66,.12);
  transition: opacity .22s ease, transform .3s cubic-bezier(.22,1.7,.36,1); }
.fx-toast.show { opacity: 1; transform: translate(-50%, 0) scale(1); }
.fx-toast.combo { background: #FFF0DE; color: #c9622e; }
.fx-toast.badge { background: #F1E8FF; color: var(--purple); font-size: 18px;
  display: flex; align-items: center; gap: 10px; }
.fx-toast.badge .bi { font-size: 30px; }
.fx-toast .small { display: block; font-size: 12px; font-weight: 800; opacity: .8; }

/* win-screen badge unlock row */
.badge-unlock { display: flex; align-items: center; gap: 12px; background: #F6EEFF;
  border-radius: 18px; padding: 12px 16px; box-shadow: 0 4px 0 #e3d2ff; width: 100%;
  max-width: 320px; }
.badge-unlock .bi { font-size: 34px; }
.badge-unlock .bt { font-size: 11px; font-weight: 900; color: var(--purple); text-transform: uppercase; letter-spacing: .5px; }
.badge-unlock .bn { font-size: 16px; font-weight: 900; color: var(--ink); }
/* sticker reveal row — a teal variant of the badge-unlock row */
.sticker-unlock { background: #E8FBF6; box-shadow: 0 4px 0 #c4ebe2; }
.sticker-unlock .bt { color: #1f9e90; }

/* ---- animations (gated on motion preference) ---- */
@media (prefers-reduced-motion: no-preference) {
  .stage { animation: cardIn .34s cubic-bezier(.22,1,.36,1); }
  @keyframes cardIn { from { opacity: 0; transform: translateY(16px); } }

  .sheet { animation: sheetUp .3s cubic-bezier(.22,1.3,.36,1); }
  @keyframes sheetUp { from { opacity: 0; transform: translateY(22px); } }

  .opt.correct { animation: optPop .5s cubic-bezier(.22,1.6,.36,1); }
  @keyframes optPop { 0% { transform: scale(1); } 38% { transform: scale(1.045); } 100% { transform: scale(1); } }

  .reveal-good { animation: goodPing .5s ease; }
  @keyframes goodPing { 0% { transform: scale(1); } 40% { transform: scale(1.12); } 100% { transform: scale(1); } }

  .xpchip { animation: pop .5s cubic-bezier(.22,1.6,.36,1); }

  .fx-pop { animation: fxPop .52s cubic-bezier(.22,1.7,.36,1); }
  @keyframes fxPop { 0% { transform: scale(1); } 34% { transform: scale(1.2); } 100% { transform: scale(1); } }

  .badge-unlock { animation: pop .45s cubic-bezier(.22,1.6,.36,1); }

  .win-mascot { animation: boBob 2.6s ease-in-out infinite; }
  @keyframes boBob { 0%,100% { transform: translateY(0) rotate(-2deg); } 50% { transform: translateY(-7px) rotate(2deg); } }

  /* Batch is a sticker, not an SVG with arms: the cheer mood gets a little hop instead. */
  .bo-cheer { animation: boHop .9s ease-in-out infinite; transform-origin: 50% 100%; }
  @keyframes boHop { 0%,100% { transform: translateY(0) scale(1); } 40% { transform: translateY(-6px) scale(1.03); } }

  .badge-chip.justunlocked { animation: fxPop .6s cubic-bezier(.22,1.7,.36,1); }
}

@media (prefers-reduced-motion: reduce) {
  .fx-toast { transition: opacity .2s ease; }
}

/* ---- progression / levels ---- */
.levelbar { background: #fff; border-radius: 16px; box-shadow: 0 3px 0 var(--soft);
  padding: 10px 14px; margin-bottom: 14px; }
.lb-row { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: 7px; }
.lb-title { font-size: 14px; font-weight: 900; color: var(--ink); }
.lb-next { font-size: 12px; font-weight: 800; color: var(--sub); }
.lb-track { height: 12px; background: var(--soft); border-radius: 999px; overflow: hidden; }
.lb-track i { display: block; height: 100%; border-radius: 999px;
  background: linear-gradient(90deg, var(--sun), var(--coral)); width: 0; }
@media (prefers-reduced-motion: no-preference) {
  .lb-track i { transition: width .7s cubic-bezier(.22,1,.36,1); }
}
/* level-up banner on the win screen */
.levelup { display: flex; align-items: center; gap: 12px; width: 100%; max-width: 320px;
  background: linear-gradient(135deg, #FFE9B8, #FFD0A8); border-radius: 18px;
  padding: 12px 16px; box-shadow: 0 4px 0 #e9c483; }
.levelup .lu-ic { font-size: 38px; }
.levelup .lu-k { font-size: 11px; font-weight: 900; color: #b9701a; text-transform: uppercase; letter-spacing: .5px; }
.levelup .lu-n { font-size: 18px; font-weight: 900; color: var(--ink); }
@media (prefers-reduced-motion: no-preference) {
  .levelup { animation: pop .5s cubic-bezier(.22,1.6,.36,1); }
}

/* finishing a whole track — bigger, rainbow celebration banner */
.trackdone { display: flex; align-items: center; gap: 14px; width: 100%; max-width: 340px;
  background: linear-gradient(135deg, #FFE9B8, #FFD0E0, #E8D6FF); border-radius: 20px;
  padding: 14px 16px; box-shadow: 0 5px 0 #e6c79a; }
.trackdone .td-ic { font-size: 46px; line-height: 1; }
.trackdone .td-k { font-size: 11px; font-weight: 900; color: var(--purple); text-transform: uppercase; letter-spacing: .6px; }
.trackdone .td-n { font-size: 21px; font-weight: 900; color: var(--ink); }
.trackdone .td-sub { font-size: 13px; font-weight: 800; color: var(--sub); }
@media (prefers-reduced-motion: no-preference) {
  .trackdone { animation: pop .55s cubic-bezier(.22,1.6,.36,1); }
}

/* ---- match_pairs card ---- */
.matchwrap { display: flex; gap: 12px; }
.mp-col { flex: 1; display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.mp-tile { background: var(--card); border: 3px solid var(--soft); border-radius: 16px;
  padding: 14px 8px; font-size: 17px; font-weight: 800; text-align: center;
  box-shadow: 0 4px 0 var(--soft); }
.mp-tile .mp-ic { display: block; font-size: 30px; margin-bottom: 3px; }
.mp-tile:active:not(:disabled) { transform: translateY(3px); box-shadow: 0 1px 0 var(--soft); }
.mp-left.sel { border-color: var(--blue); background: #ECFAFF; box-shadow: 0 4px 0 var(--blue); }
.mp-tile.matched { border-color: var(--good); background: #EAFBF2; box-shadow: 0 4px 0 var(--good); opacity: 1; }
.mp-tile.matched:disabled { opacity: .9; }
.mp-shake { animation: shake .35s; }

/* ---- read-aloud button + settings sheet ---- */
.readaloud { display: inline-flex; align-items: center; justify-content: center;
  width: 46px; height: 46px; border-radius: 50%; background: #fff; font-size: 22px;
  box-shadow: 0 3px 0 var(--soft); }
.readaloud:active { transform: translateY(2px); box-shadow: 0 1px 0 var(--soft); }
.settings-overlay { position: fixed; inset: 0; z-index: 9999; background: rgba(58,48,66,.45);
  display: flex; align-items: center; justify-content: center; padding: 24px; }
.settings-card { background: var(--bg); border-radius: 24px; padding: 20px; width: 100%;
  max-width: 340px; box-shadow: 0 10px 0 rgba(0,0,0,.12); }
.settings-card h3 { margin: 2px 0 14px; font-size: 22px; }
.setrow { display: flex; align-items: center; justify-content: space-between; width: 100%;
  background: #fff; border-radius: 16px; padding: 14px 16px; margin-bottom: 10px;
  font-size: 17px; font-weight: 800; box-shadow: 0 3px 0 var(--soft); }
.toggle { width: 52px; height: 30px; border-radius: 999px; background: #d9ccb4; position: relative; flex: none; }
.toggle i { position: absolute; top: 3px; left: 3px; width: 24px; height: 24px; border-radius: 50%;
  background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,.2); }
.toggle.on { background: var(--good); }
.toggle.on i { left: 25px; }
.settings-card .btn { margin-top: 6px; }
@media (prefers-reduced-motion: no-preference) {
  .toggle, .toggle i { transition: background .18s, left .18s; }
  .settings-card { animation: pop .35s cubic-bezier(.22,1.5,.36,1); }
}

/* ---- badge detail sheet (tap a badge chip on a home shelf) ---- */
.badge-chip[data-badge] { cursor: pointer; }
.badge-card { text-align: center; max-width: 320px; }
.badge-card .badge-hero { font-size: 76px; line-height: 1; margin: 4px 0 2px; }
.badge-card .badge-hero.locked { filter: grayscale(1); opacity: .4; }
.badge-card .badge-name { margin: 6px 0 10px; font-size: 22px; }
.badge-status { display: inline-block; font-weight: 900; font-size: 14px; border-radius: 999px;
  padding: 5px 14px; margin-bottom: 12px; }
.badge-status.earned { background: rgba(87,204,153,.20); color: #2F8F5B; }
.badge-status.locked { background: #F1E7D4; color: #8a7c60; }
.badge-card .badge-how { color: var(--sub); font-size: 15px; font-weight: 700; line-height: 1.4;
  margin: 0 4px 16px; }
@media (prefers-reduced-motion: no-preference) {
  .badge-chip[data-badge]:active { transform: scale(.94); }
}

/* ---- home greeter (Batch) + perfect-lesson star ---- */
.greeter { display: flex; align-items: center; gap: 12px; margin-bottom: 16px; }
.greeter-bo { width: 60px; min-width: 60px; }
.greeter-bubble { position: relative; flex: 1; background: #fff; border-radius: 16px;
  padding: 12px 14px; font-size: 15px; font-weight: 800; color: var(--ink); box-shadow: 0 3px 0 var(--soft); }
.greeter-bubble::before { content: ""; position: absolute; left: -7px; top: 22px;
  border: 8px solid transparent; border-right-color: #fff; border-left: 0; }
.node.perfect .bubble { box-shadow: 0 4px 0 #d99c12; }
@media (prefers-reduced-motion: no-preference) {
  .greeter-bo { animation: boBob 3s ease-in-out infinite; }
}

/* ---- home streak chain ("don't break the chain") ---- */
.chaincard { background: #fff; border-radius: 16px; box-shadow: 0 3px 0 var(--soft);
  padding: 12px 14px; margin-bottom: 16px; }
.chaincard.risk { background: linear-gradient(135deg, #FFF1D9, #FFE2D0); box-shadow: 0 3px 0 #f0cda0; }
.chain-head { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.chain-flame { font-size: 26px; line-height: 1; flex: none; }
.chain-title { font-size: 15px; font-weight: 900; color: var(--ink); }
.chain-sub { font-size: 12.5px; font-weight: 800; color: var(--sub); }
.chaincard.risk .chain-sub { color: #c0682f; }
.chain-links { display: flex; justify-content: space-between; gap: 4px; }
.chain-link { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 3px; min-width: 0; }
.cl-dot { width: 30px; height: 30px; border-radius: 50%; display: flex; align-items: center;
  justify-content: center; font-size: 15px; background: var(--soft); color: transparent; }
.chain-link.lit .cl-dot { background: linear-gradient(135deg, var(--sun), var(--coral)); }
.chain-link.lit.froze .cl-dot { background: linear-gradient(135deg, #CFEFFF, #7EC8F2); }
.chain-link.today .cl-dot { box-shadow: 0 0 0 3px rgba(255,191,63,.45); }
.chain-link.target .cl-dot { background: #fff; border: 2px dashed var(--coral);
  color: var(--coral); font-weight: 900; }
.cl-day { font-size: 10px; font-weight: 800; color: var(--sub); }
@media (prefers-reduced-motion: no-preference) {
  .chaincard.risk .chain-link.target .cl-dot { animation: chainPulse 1.4s ease-in-out infinite; }
}
@keyframes chainPulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.12); } }

/* ---- first-run onboarding (Batch intro) ---- */
.onboard-overlay { position: fixed; inset: 0; z-index: 10000; background: rgba(58,48,66,.55);
  display: flex; align-items: center; justify-content: center; padding: 24px; }
.onboard-card { position: relative; background: var(--bg); border-radius: 26px;
  padding: 26px 22px 22px; width: 100%; max-width: 340px; text-align: center;
  box-shadow: 0 12px 0 rgba(0,0,0,.14); }
.onb-skip { position: absolute; top: 12px; right: 12px; font-size: 14px; font-weight: 800;
  color: var(--sub); padding: 6px 9px; border-radius: 12px; }
.onb-bo { width: 122px; margin: 2px auto 4px; }
.onb-bo .emoji { font-size: 84px; line-height: 1; }
.onb-title { margin: 4px 0 6px; font-size: 24px; font-weight: 900; color: var(--ink); }
.onb-body { margin: 0 6px 16px; font-size: 16px; font-weight: 700; line-height: 1.45; color: var(--ink); }
.onb-dots { display: flex; justify-content: center; gap: 7px; margin-bottom: 18px; }
.onb-dots i { width: 9px; height: 9px; border-radius: 50%; background: var(--soft); display: block; }
.onb-dots i.on { background: var(--coral); transform: scale(1.18); }
@media (prefers-reduced-motion: no-preference) {
  .onboard-card { animation: onbPop .4s cubic-bezier(.22,1.5,.36,1); }
  @keyframes onbPop { from { opacity: 0; transform: translateY(14px) scale(.96); } }
  .onb-bo { animation: boBob 3s ease-in-out infinite; }
  .onb-dots i { transition: background .2s, transform .2s; }
}

/* ============ accounts gate (js/auth-ui.js) — optional, gate OFF by default ============ */
.authwrap { flex: 1 1 auto; min-height: 0; display: flex; align-items: center; justify-content: center;
  padding: 22px 16px; }
.authcard { width: 100%; max-width: 420px; background: var(--card); border-radius: 26px;
  padding: 22px 20px 24px; box-shadow: 0 6px 0 var(--soft); }
.authlogo { display: flex; justify-content: center; margin-bottom: 6px; }
.authlogo svg, .authlogo img { width: 104px; height: 104px; }
.auth-emoji { font-size: 78px; line-height: 1; }
.authtitle { margin: 4px 0 4px; text-align: center; font-size: 27px; font-weight: 900; }
.authsub { margin: 0 0 16px; text-align: center; color: var(--sub); font-weight: 700; line-height: 1.4; }
.authtabs { display: flex; gap: 8px; background: var(--soft); border-radius: 999px; padding: 5px; margin-bottom: 16px; }
.authtab { flex: 1; padding: 10px; border-radius: 999px; font-weight: 800; color: var(--sub); }
.authtab.on { background: #fff; color: var(--ink); box-shadow: 0 2px 0 rgba(0,0,0,.06); }
.authform { display: flex; flex-direction: column; }
.authlabel { font-size: 13px; font-weight: 800; color: var(--sub); margin: 10px 2px 5px; }
.authlabel small { font-weight: 700; opacity: .8; }
.authinput { width: 100%; padding: 14px 15px; border-radius: 14px; font-size: 16px; font-weight: 700;
  color: var(--ink); background: #FFFDF8; border: 2px solid var(--soft); }
.authinput:focus { outline: none; border-color: var(--teal); }
.authcheck { display: flex; gap: 9px; align-items: flex-start; margin: 12px 2px 2px;
  font-size: 13px; font-weight: 700; color: var(--ink); line-height: 1.35; }
.authcheck input { width: 20px; height: 20px; margin-top: 1px; accent-color: var(--teal); }
.autherr { color: var(--bad-deep); font-weight: 800; font-size: 14px; min-height: 18px; margin: 8px 2px 0; }
.authbtn { display: block; width: 100%; margin-top: 14px; padding: 15px 18px; border-radius: 16px;
  font-size: 18px; font-weight: 800; text-align: center; color: #fff;
  background: var(--teal); box-shadow: 0 5px 0 #1e9c8f; transition: transform .06s, box-shadow .06s; }
.authbtn:active:not(:disabled) { transform: translateY(4px); box-shadow: 0 1px 0 #1e9c8f; }
.authbtn.google { background: #fff; color: var(--ink); border: 2px solid var(--soft); box-shadow: 0 5px 0 var(--soft); }
.authbtn.google:active:not(:disabled) { transform: translateY(4px); box-shadow: 0 1px 0 var(--soft); }
.authdivider { display: flex; align-items: center; gap: 10px; margin: 16px 2px 4px; color: var(--sub); font-weight: 800; font-size: 13px; }
.authdivider::before, .authdivider::after { content: ""; flex: 1; height: 2px; background: var(--soft); }
.authnote { margin: 14px 4px 0; text-align: center; font-size: 12.5px; font-weight: 700; color: var(--sub); line-height: 1.4; }
.authrowtop { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; gap: 10px; }
.authmini { font-size: 12.5px; font-weight: 800; color: var(--sub); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.authlink { font-size: 13px; font-weight: 800; color: var(--coral); padding: 4px 6px; }

/* kid list + add player */
.kidlist { display: flex; flex-direction: column; gap: 10px; margin-bottom: 16px; }
.kidrow { display: flex; align-items: center; gap: 12px; width: 100%; text-align: left;
  background: #FFFDF8; border: 2px solid var(--soft); border-radius: 18px; padding: 12px 14px;
  transition: transform .06s, box-shadow .06s; box-shadow: 0 4px 0 var(--soft); }
.kidrow:active { transform: translateY(3px); box-shadow: 0 1px 0 var(--soft); }
.kidav { font-size: 34px; line-height: 1; }
.kidname { flex: 1; font-weight: 900; font-size: 17px; display: flex; flex-direction: column; }
.kidname small { font-weight: 700; font-size: 12.5px; color: var(--sub); }
.kidxp { font-weight: 800; font-size: 13px; color: var(--teal); }
.addkid { border-top: 2px dashed var(--soft); margin-top: 4px; padding-top: 10px; }
.avatarpick { display: grid; grid-template-columns: repeat(8, 1fr); gap: 6px; }
.avataropt { font-size: 25px; padding: 6px 0; border-radius: 12px; background: #FFFDF8; border: 2px solid transparent; }
.avataropt.sel { border-color: var(--teal); background: #E8FAF7; }
.unamecheck { min-height: 18px; font-size: 13px; font-weight: 800; margin: 6px 2px 0; color: var(--sub); }
.unamecheck.ok { color: var(--good-deep); }
.unamecheck.bad { color: var(--bad-deep); }

/* who's playing */
.pickgrid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.pickbtn { display: flex; flex-direction: column; align-items: center; gap: 4px; padding: 18px 10px;
  background: #FFFDF8; border: 2px solid var(--soft); border-radius: 20px; box-shadow: 0 5px 0 var(--soft);
  transition: transform .06s, box-shadow .06s; }
.pickbtn:active { transform: translateY(4px); box-shadow: 0 1px 0 var(--soft); }
.pickav { font-size: 46px; line-height: 1; }
.pickname { font-weight: 900; font-size: 16px; }
.pickxp { font-weight: 800; font-size: 12.5px; color: var(--teal); }
.pickbtn.add { justify-content: center; color: var(--sub); border-style: dashed; }
.pickbtn.add .pickav { color: var(--teal); }

/* leaderboard (Phase 2) */
.lbwrap { padding: 18px 16px 40px; }
.lbhead { margin-bottom: 14px; }
.lbtitle { margin: 8px 0 2px; font-size: 26px; font-weight: 900; }
.lbsub { margin: 0; color: var(--sub); font-weight: 700; font-size: 14px; }
/* Season Complete re-entry (Sprint T1) — only rendered in the tournament's
   `ended` phase, and only when js/season-end.js is loaded. */
.lbseason { margin: 10px 0 0; width: 100%; padding: 12px 10px; border: 0; border-radius: 16px;
  background: var(--purple, #9B5DE5); color: #fff; font-weight: 800; font-size: 15px; }
.lblist { display: flex; flex-direction: column; gap: 8px; }
.lbmsg { color: var(--sub); font-weight: 700; text-align: center; padding: 24px 8px; }
.lbrow { display: flex; align-items: center; gap: 12px; background: var(--card); border-radius: 16px;
  padding: 12px 14px; box-shadow: 0 3px 0 var(--soft); }
.lbrow.me { background: #E8FAF7; box-shadow: 0 3px 0 #b8ece4; }
.lbrank { width: 34px; font-weight: 900; font-size: 17px; text-align: center; }
.lbav { font-size: 28px; line-height: 1; }
.lbname { flex: 1; font-weight: 800; font-size: 16px; }
.lbname small { color: var(--teal); font-weight: 800; font-size: 12px; }
.lbxp { font-weight: 900; font-size: 15px; color: var(--teal); }
.lbxp::after { content: " XP"; font-size: 11px; font-weight: 800; color: var(--sub); }

/* ---- summer tournament (Phase 3) — optional layer, only shows when accounts are ON ---- */
.tbanner { border-radius: 18px; padding: 13px 15px; margin-bottom: 14px; box-shadow: 0 4px 0 var(--soft); }
.tbanner.t-pre { background: linear-gradient(135deg, #FFF1D9, #FFE2D0); box-shadow: 0 4px 0 #f0cda0; }
.tbanner.t-active { background: linear-gradient(135deg, #FFEDD6, #FFE0E0); box-shadow: 0 4px 0 #f3c9b0; }
.tbanner.t-ended { background: linear-gradient(135deg, #EDE3FF, #E0F4FF); box-shadow: 0 4px 0 #cdb9ee; }
.tbmsg { font-size: 15px; font-weight: 800; color: var(--ink); line-height: 1.3; }
.tbmsg b { font-weight: 900; }
.tbar { height: 9px; border-radius: 999px; background: rgba(255,255,255,.65); margin: 9px 0 2px; overflow: hidden; }
.tbar i { display: block; height: 100%; border-radius: 999px; background: linear-gradient(90deg, var(--sun), var(--coral)); }
.tbinfo { display: inline-block; margin-top: 8px; font-size: 13px; font-weight: 900; color: var(--purple);
  background: rgba(255,255,255,.7); border-radius: 999px; padding: 5px 12px; box-shadow: 0 2px 0 var(--soft); }
.tbinfo:active { transform: translateY(2px); box-shadow: 0 1px 0 var(--soft); }

/* top-3 podium (shown on the final/ended board) */
.podium { display: grid; grid-template-columns: 1fr 1fr 1fr; align-items: end; gap: 8px; margin-bottom: 16px; }
.pod { background: var(--card); border-radius: 16px 16px 0 0; padding: 12px 6px 8px; text-align: center; box-shadow: 0 3px 0 var(--soft); }
.pod-1 { padding-top: 22px; background: linear-gradient(180deg, #FFF6DD, #fff); box-shadow: 0 3px 0 #f0d999; }
.pod-2 { padding-top: 14px; }
.pod-3 { padding-top: 8px; }
.pod-empty { background: #FBF4E6; box-shadow: none; min-height: 84px; }
.podmedal { font-size: 26px; line-height: 1; }
.podav { font-size: 34px; line-height: 1; margin: 2px 0; }
.podname { font-weight: 900; font-size: 14px; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.podxp { font-weight: 800; font-size: 12px; color: var(--teal); }
.podbase { margin-top: 6px; font-weight: 900; font-size: 16px; color: var(--sub); }

/* rules / prize sheet (mirrors the settings + onboarding overlays) */
.tsheet-overlay { position: fixed; inset: 0; z-index: 9999; background: rgba(58,48,66,.45);
  display: flex; align-items: center; justify-content: center; padding: 24px; }
.tsheet-card { position: relative; background: var(--bg); border-radius: 24px; padding: 22px 20px 18px;
  width: 100%; max-width: 340px; box-shadow: 0 10px 0 rgba(0,0,0,.12); }
.trules h2 { margin: 2px 0 10px; font-size: 22px; }
.trintro { margin: 0 0 12px; font-size: 15px; font-weight: 700; color: var(--ink); line-height: 1.4; }
.trlist { list-style: none; padding: 0; margin: 0 0 16px; display: flex; flex-direction: column; gap: 9px; }
.trlist li { background: #fff; border-radius: 14px; padding: 10px 13px; font-size: 14.5px; font-weight: 700;
  color: var(--ink); line-height: 1.35; box-shadow: 0 3px 0 var(--soft); }
.trlist li b { font-weight: 900; }
.trclose { width: 100%; padding: 13px; border-radius: 16px; background: var(--coral); color: #fff;
  font-size: 17px; font-weight: 900; box-shadow: 0 4px 0 #d94f4f; }
.trclose:active { transform: translateY(3px); box-shadow: 0 1px 0 #d94f4f; }
@media (prefers-reduced-motion: no-preference) {
  .tsheet-card { animation: pop .35s cubic-bezier(.22,1.5,.36,1); }
}

/* ============================================================
   ✨ PREMIUM POLISH PASS — 2026-06-24 (launch eve)
   Depth, tactile finish, and entrance motion. All additive +
   file://-safe (gradients/shadows only, no url()/defs) and gated
   on prefers-reduced-motion. Sits at the END of the cascade so it
   refines the base styles above without changing any markup.
   Nothing here touches scoring/auth/gate — pure visual lift.
   ============================================================ */

/* --- ambient background glow: soft brand-colour light warms the flat
   cream so the app reads as dimensional. Painted into the body background
   itself (a fixed pseudo-layer hides behind the body canvas), attachment
   fixed so it stays put while the page scrolls. --- */
body {
  background:
    radial-gradient(760px 500px at 6% -8%, rgba(255,191,63,.32), transparent 60%),
    radial-gradient(700px 540px at 106% 3%, rgba(255,107,107,.24), transparent 60%),
    radial-gradient(860px 640px at 50% 114%, rgba(155,93,229,.18), transparent 64%),
    var(--bg);
  background-attachment: fixed;
}

/* --- floating surfaces: keep the chunky hard offset shadow, add a soft
   ambient shadow beneath so cards lift off the page (premium depth). --- */
.unitcard { box-shadow: 0 5px 0 var(--soft), 0 18px 30px -12px rgba(58,48,66,.32); }
.unitcard:active:not(:disabled) { box-shadow: 0 2px 0 var(--soft), 0 9px 16px -9px rgba(58,48,66,.30); }
.coursehead { box-shadow: 0 18px 34px -16px rgba(255,107,107,.48); }
.levelbar, .chaincard, .greeter-bubble,
.badge-chip, .sticker-chip { box-shadow: 0 3px 0 var(--soft), 0 11px 20px -10px rgba(58,48,66,.22); }
.node { box-shadow: 0 4px 0 var(--soft), 0 14px 22px -12px rgba(58,48,66,.24); }
.visual { box-shadow: 0 4px 0 var(--soft), 0 16px 26px -14px rgba(58,48,66,.20); }
.pill, .sound-toggle { box-shadow: 0 2px 0 var(--soft), 0 6px 12px -6px rgba(58,48,66,.22); }

/* --- glossy candy buttons: a top inner highlight turns the flat colour
   into a rounded, 3-D sheen; the hard colour shadow underneath stays. --- */
.btn { box-shadow: inset 0 2px 0 rgba(255,255,255,.32), 0 5px 0 #1e9c8f; }
.btn.alt { box-shadow: inset 0 2px 0 rgba(255,255,255,.32), 0 5px 0 #7a41c4; }
.btn.good { box-shadow: inset 0 2px 0 rgba(255,255,255,.32), 0 5px 0 var(--good-deep); }
.btn.bad { box-shadow: inset 0 2px 0 rgba(255,255,255,.32), 0 5px 0 var(--bad-deep); }
.btn:active:not(:disabled) { box-shadow: inset 0 2px 0 rgba(255,255,255,.22), 0 1px 0 #1e9c8f; }
.tf-true { box-shadow: inset 0 2px 0 rgba(255,255,255,.32), 0 5px 0 #1e9c8f; }
.tf-false { box-shadow: inset 0 2px 0 rgba(255,255,255,.32), 0 5px 0 var(--bad-deep); }
.bucket.b0 { box-shadow: inset 0 2px 0 rgba(255,255,255,.30), 0 5px 0 #1e9c8f; }
.bucket.b1 { box-shadow: inset 0 2px 0 rgba(255,255,255,.30), 0 5px 0 #7a41c4; }

/* --- tactile press: tappable tiles that lacked it now give under the finger --- */
@media (prefers-reduced-motion: no-preference) {
  .opt, .chip, .node, .backbtn { transition: transform .08s ease, box-shadow .08s ease; }
  .opt:active:not(.correct):not(.wrong):not(.sel) { transform: translateY(3px); box-shadow: 0 1px 0 var(--soft); }
  .chip:not(.used):active { transform: translateY(2px) scale(.97); box-shadow: 0 1px 0 var(--soft); }
  .node:not(.locked):active { transform: translateY(3px); }
  .node:not(.locked):active .bubble { transform: translateY(1px); }
}

/* --- entrance cascade: home blocks + cards rise in with a quick, gentle
   stagger. Resting state is fully visible (opacity 1, no transform), and
   the whole thing is gated — so content never hides if motion is reduced
   or the animation is skipped. Fast (<.7s total) so re-renders stay snappy. --- */
@media (prefers-reduced-motion: no-preference) {
  @keyframes riseIn { from { opacity: 0; transform: translateY(15px); } }
  .home > *:not(.topbar):not(.units):not(.path) { animation: riseIn .5s cubic-bezier(.22,1,.36,1) both; }
  .home > *:nth-child(2) { animation-delay: .03s; }
  .home > *:nth-child(3) { animation-delay: .07s; }
  .home > *:nth-child(4) { animation-delay: .11s; }
  .home > *:nth-child(n+5) { animation-delay: .15s; }

  .units .unitcard, .path .node { animation: riseIn .48s cubic-bezier(.22,1,.36,1) both; }
  .units .unitcard:nth-child(2), .path .node:nth-child(2) { animation-delay: .05s; }
  .units .unitcard:nth-child(3), .path .node:nth-child(3) { animation-delay: .10s; }
  .units .unitcard:nth-child(4), .path .node:nth-child(4) { animation-delay: .15s; }
  .units .unitcard:nth-child(5), .path .node:nth-child(5) { animation-delay: .20s; }
  .units .unitcard:nth-child(n+6), .path .node:nth-child(n+6) { animation-delay: .24s; }
}

/* ============================================================
   ✨ PREMIUM POLISH — hero + celebration (2026-06-24)
   The payoff moments: the win screen and the lesson hero. Pure
   CSS, file://-safe, gated. No markup / scoring changes.
   ============================================================ */

/* --- WIN SCREEN: a warm sun-halo + a one-shot ring burst behind Bo turns
   "lesson complete" into a real moment (the confetti + bobbing mascot were
   already there). Content sits above the glow via z-index. --- */
.winwrap { position: relative; overflow: hidden; }
.winwrap > * { position: relative; z-index: 1; }
.winwrap::before {
  content: ""; position: absolute; left: 50%; top: 33%; z-index: 0;
  width: 540px; height: 540px; transform: translate(-50%, -50%); pointer-events: none;
  background: radial-gradient(circle, rgba(255,191,63,.42), rgba(255,191,63,.12) 42%, transparent 68%);
}
@media (prefers-reduced-motion: no-preference) {
  .winwrap::after {
    content: ""; position: absolute; left: 50%; top: 33%; z-index: 0;
    width: 180px; height: 180px; border-radius: 50%; pointer-events: none;
    transform: translate(-50%, -50%); border: 6px solid rgba(255,191,63,.6);
    animation: winRing 1.15s cubic-bezier(.22,1,.36,1) forwards;
  }
  @keyframes winRing {
    0% { transform: translate(-50%,-50%) scale(.35); opacity: .9; }
    100% { transform: translate(-50%,-50%) scale(2.6); opacity: 0; }
  }
}
/* golden glow under the XP chip */
.xpchip { box-shadow: inset 0 2px 0 rgba(255,255,255,.35), 0 5px 0 #d99c12, 0 12px 24px -6px rgba(255,191,63,.55); }

/* --- LESSON HERO: a soft top sheen + light inner edge gives the coursehead
   gradient banner a glossy, dimensional finish. --- */
.coursehead { position: relative; overflow: hidden;
  box-shadow: inset 0 1px 0 rgba(255,255,255,.55), 0 18px 34px -16px rgba(255,107,107,.48); }
.coursehead::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(120% 80% at 0% 0%, rgba(255,255,255,.40), transparent 55%);
}
.coursehead > * { position: relative; }

/* --- PROGRESS BAR (in-lesson): brand-gradient fill + soft inner highlight so
   forward motion feels rewarding. --- */
.progress i {
  background: linear-gradient(90deg, var(--good), var(--teal));
  box-shadow: inset 0 2px 0 rgba(255,255,255,.35);
}
/* level + track + tournament fill bars get the same glossy inner light */
.lb-track i, .trackbar > i, .tbar i { box-shadow: inset 0 1px 0 rgba(255,255,255,.45); }

/* --- FEEDBACK SHEET: a clearer correct/again identity — accent edge + pop --- */
.sheet.good { box-shadow: inset 4px 0 0 var(--good); }
.sheet.bad { box-shadow: inset 4px 0 0 var(--bad); }

/* --- DAILY-GOAL RING + topbar level pill: a touch of lift --- */
.daily-ring { filter: drop-shadow(0 4px 8px rgba(58,48,66,.16)); }

/* ============================================================
   ✨ PREMIUM POLISH — living details (2026-06-24)
   Small touches that make the app feel alive + cohesive. Pure
   CSS, file://-safe, gated. No markup / scoring changes.
   ============================================================ */

/* --- progress glimmer: a slow, subtle highlight glints across the home
   level bar so progress feels alive (signature touch; level bar only, to
   stay focused). Gated; the fill clips it. --- */
@media (prefers-reduced-motion: no-preference) {
  .lb-track i { position: relative; overflow: hidden; }
  .lb-track i::after {
    content: ""; position: absolute; top: 0; left: -45%; width: 45%; height: 100%;
    background: linear-gradient(90deg, transparent, rgba(255,255,255,.6), transparent);
    animation: barSheen 3.8s ease-in-out infinite; animation-delay: 1s;
  }
  @keyframes barSheen { 0% { left: -45%; } 50%, 100% { left: 130%; } }
}

/* --- brand wordmark: a hair more presence so the logo reads premium --- */
.brand { text-shadow: 0 1px 0 rgba(255,255,255,.6); }
.brand em { text-shadow: 0 2px 8px rgba(255,107,107,.30); }

/* --- gated launch UI (accounts ON): give the auth + tournament buttons the
   same glossy candy finish as the rest of the app so the signed-in flow
   feels of-a-piece. Additive; only visible when the gate is flipped. --- */
.authbtn { box-shadow: inset 0 2px 0 rgba(255,255,255,.30), 0 5px 0 #1e9c8f; }
.authbtn.google { box-shadow: inset 0 2px 0 rgba(255,255,255,.7), 0 5px 0 var(--soft); }
.authbtn:active:not(:disabled) { box-shadow: inset 0 2px 0 rgba(255,255,255,.22), 0 1px 0 #1e9c8f; }
.trclose { box-shadow: inset 0 2px 0 rgba(255,255,255,.30), 0 4px 0 #d94f4f; }
.kidrow, .pickbtn { box-shadow: 0 4px 0 var(--soft), 0 10px 18px -12px rgba(58,48,66,.20); }
.lbrow.me { box-shadow: 0 3px 0 #b8ece4, 0 10px 18px -10px rgba(46,196,182,.35); }

/* --- podium top step gets a soft golden glow (final leaderboard moment) --- */
.pod-1 { box-shadow: 0 3px 0 #f0d999, 0 14px 26px -10px rgba(255,191,63,.55); }

/* ============================================================
   ✨ PREMIUM POLISH — refinements (2026-06-24)
   Tidies the rough edges. Pure CSS, file://-safe, gated where
   it animates. No markup / scoring changes.
   ============================================================ */

/* --- scroll affordance: the badge + sticker shelves overflow horizontally
   and were hard-cutting mid-item. A soft right-edge fade signals "scroll for
   more" and reads as a premium finish. (Topbar .stats is intentionally NOT
   faded — its right-most XP pill must stay crisp.) --- */
.badges-shelf, .stickers-shelf {
  -webkit-mask-image: linear-gradient(90deg, #000 90%, transparent 100%);
          mask-image: linear-gradient(90deg, #000 90%, transparent 100%);
}

/* --- celebration banners: a warm glow makes level-up + track-complete feel
   like the big moments they are. --- */
.levelup { box-shadow: 0 4px 0 #e9c483, 0 14px 28px -8px rgba(255,191,63,.50); }
.trackdone { box-shadow: 0 5px 0 #e6c79a, 0 16px 32px -8px rgba(155,93,229,.42); }
.badge-unlock { box-shadow: 0 4px 0 #e3d2ff, 0 13px 24px -10px rgba(155,93,229,.35); }
.sticker-unlock { box-shadow: 0 4px 0 #c4ebe2, 0 13px 24px -10px rgba(46,196,182,.35); }

/* --- greeter Bo: a touch larger for friendly presence on the home --- */
.greeter-bo { width: 66px; min-width: 66px; }

/* --- desktop niceties: a gentle hover-lift on the collectible chips (no-op
   on touch; keeps the playful tactility on the wider/installed view) --- */
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .badge-chip, .sticker-chip, .unitcard, .node { transition: transform .12s ease, box-shadow .12s ease; }
  .badge-chip:hover, .sticker-chip:hover { transform: translateY(-2px); }
}

/* ============================================================
   📱→📲 TABLET / iPad RESPONSIVE PASS — 2026-06-25
   The app is mobile-first (≤480px column). On an iPad that column
   was small and stranded in whitespace. Keep the single-column flow
   (ideal reading width + the lesson-player layout) but WIDEN the
   column and SCALE UP type, art, and tap targets so it fills the
   screen and is comfortable at arm's length. Additive, at the end
   of the cascade; nothing here changes markup, scoring, or motion
   gating. Diagrams/cards/buttons are width:100%, so they grow with
   the wider column automatically — this just tunes proportions.
   ============================================================ */

/* iPad portrait (768) and up — also big phones in landscape */
@media (min-width: 700px) {
  #app { max-width: 600px; }

  /* home */
  .home { padding: 30px 26px 56px; }
  .brand { font-size: 27px; }
  .pill { font-size: 15px; padding: 8px 15px; }
  .coursehead { padding: 26px; }
  .coursehead h1 { font-size: 34px; }
  .coursehead p { font-size: 17px; }
  .unit-cover { height: 150px; }
  .unit-kicker { font-size: 19px; }
  .unit-tagline { font-size: 16px; }
  .unit-progress { font-size: 14px; }
  .greeter-bo { width: 78px; min-width: 78px; }
  .greeter-bubble { font-size: 17px; }
  .shelf-head { font-size: 15px; }

  /* lesson path */
  .trackhead-title { font-size: 23px; }
  .node { padding: 16px 18px; }
  .node .meta h3 { font-size: 19px; }
  .node .meta span { font-size: 14px; }
  .node .bubble { width: 62px; height: 62px; min-width: 62px; font-size: 28px; }

  /* player — bigger prompts, art, options, buttons */
  .phead { padding: 16px 22px 8px; }
  .stage { padding: 18px 26px 14px; gap: 18px; }
  .prompt { font-size: 24px; }
  .concept-text { font-size: 25px; line-height: 1.4; }
  .opt { font-size: 20px; padding: 18px 20px; }
  .sentence { font-size: 26px; }
  .chip { font-size: 20px; padding: 14px 22px; }
  .bucket { font-size: 19px; padding: 22px 12px; }
  .sort-item { font-size: 24px; padding: 30px 20px; }
  .speed-card { font-size: 24px; padding: 34px 20px; }
  .tf { font-size: 22px; padding: 24px 12px; }
  .btn, .authbtn { font-size: 21px; padding: 18px 22px; }
  .pfoot { padding: 14px 26px calc(18px + env(safe-area-inset-bottom)); }
  .sheet h4 { font-size: 22px; }

  /* win screen */
  .winwrap h2 { font-size: 34px; }
  .winwrap .recap { font-size: 19px; }
  .win-mascot { width: 168px; }
  .xpchip { font-size: 25px; padding: 14px 30px; }

  /* accounts / leaderboard / tournament */
  .authcard { max-width: 460px; padding: 26px 24px 28px; }
  .authtitle { font-size: 29px; }
  .lbtitle { font-size: 29px; }
  .lbname { font-size: 17px; }
  .lbav { font-size: 30px; }
}

/* larger iPads / landscape (≥1000px): a touch more room + bigger art */
@media (min-width: 1000px) {
  #app { max-width: 640px; }
  .unit-cover { height: 170px; }
  .coursehead h1 { font-size: 36px; }
}

/* ── a11y: visible keyboard focus (2026-07-09) ─────────────────────────── */
button:focus-visible, a:focus-visible, [tabindex]:focus-visible {
  outline: 3px solid #9B5DE5;
  outline-offset: 2px;
}
button:focus:not(:focus-visible) { outline: none; }

/* ── auth welcome: parent links row (About · Privacy · Contact, 2026-07-10) ── */
.authlinks { display: flex; justify-content: center; gap: 8px; margin-top: 14px;
  font-size: 12.5px; font-weight: 800; }
.authlinks a { color: var(--sub); text-decoration: none; }
.authlinks a:hover, .authlinks a:focus-visible { color: var(--coral); text-decoration: underline; }
.authlinks span { color: var(--soft); }

/* ── new-version update pill (2026-07-10) ─────────────────────────────────── */
.update-pill { display: block; width: calc(100% - 8px); margin: 10px auto 2px; padding: 12px 16px;
  border: none; border-radius: 14px; background: var(--purple); color: #fff;
  font-family: inherit; font-size: 14.5px; font-weight: 800; cursor: pointer;
  box-shadow: 0 4px 0 #7a41c4; -webkit-tap-highlight-color: transparent; }
.update-pill:active { transform: translateY(3px); box-shadow: 0 1px 0 #7a41c4; }

/* settings sheet: build version line (2026-07-10) */
.settings-version { margin: 12px 0 0; text-align: center; font-size: 11.5px; font-weight: 700; color: var(--sub); opacity: .7; }

/* profile: info-page links row (2026-07-10) */
.pf-links { margin: 18px 0 8px; padding-bottom: 12px; }

/* ── Batch Lab launcher card (2026-07-10 — these styles were missing entirely:
     the .labcard rendered as bare text at the bottom of the units screen) ──── */
.labzone { margin: 18px 4px 8px; }
.labcard { display: flex; align-items: center; gap: 12px; width: 100%;
  background: linear-gradient(135deg, #F3E8FF, #E2F6F1); border: none; border-radius: 18px;
  padding: 14px 16px; box-shadow: 0 4px 0 #d9c7f0; cursor: pointer; text-align: left;
  font-family: inherit; -webkit-tap-highlight-color: transparent; }
.labcard:active { transform: translateY(2px); box-shadow: 0 1px 0 #d9c7f0; }
.lab-ic { font-size: 30px; line-height: 1; flex-shrink: 0; }
.lab-meta { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.lab-meta b { font-size: 16px; font-weight: 900; color: var(--ink); }
.lab-meta span { font-size: 12.5px; font-weight: 700; color: var(--sub); }
.lab-go { font-size: 13px; font-weight: 900; color: var(--purple); flex-shrink: 0; }

/* ---------- auth loading splash (gate-on boot, added 2026-07-11 v68) ----------
   Shown by auth-ui.js while supabase-js loads from the CDN — replaces the blank
   screen kids saw for up to ~6s on slow connections. Motion-gated. */
.authloading { text-align: center; }
.authdots i { display: inline-block; font-style: normal; opacity: .3; }
@media (prefers-reduced-motion: no-preference) {
  .authdots i { animation: authdot 1.2s infinite; }
  .authdots i:nth-child(2) { animation-delay: .2s; }
  .authdots i:nth-child(3) { animation-delay: .4s; }
}
@keyframes authdot { 0%, 60%, 100% { opacity: .25; } 30% { opacity: 1; } }
