/* Ekwaa design system.
   Palette reasoning, recorded for the owner: Ebonyi is red earth, yam barns,
   palm groves and Abakaliki rice. The brand colour is fired clay terracotta,
   the accent is brass ochre, the ground is warm ivory like sun dried raffia,
   and deep palm green appears only as a quiet secondary. No flat colour: a
   woven raffia texture sits under every surface at low opacity, cards carry
   real shadow, and display type is a crafted serif. Primary actions are light
   (brass) with dark ink text, per the house law. */
:root {
  --ground: #faf5ec;
  --ground-2: #f3ead9;
  --card: #fffdf8;
  --ink: #2a2118;
  --ink-soft: #6b5d4c;
  --clay: #b0512a;
  --clay-deep: #8d3e1f;
  --brass: #e9c886;
  --brass-deep: #c29a5b;
  --brass-text: #8a6414;
  --palm: #2e4b3f;
  --line: #e3d7c3;
  --good: #2e7d4f;
  --bad: #b3372a;
  --radius: 18px;
  --shadow: 0 2px 6px rgba(74, 52, 25, 0.08), 0 14px 34px rgba(74, 52, 25, 0.10);
}
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  font-family: "Public Sans", system-ui, -apple-system, sans-serif;
  color: var(--ink);
  background:
    radial-gradient(1200px 500px at 85% -10%, rgba(233, 200, 134, 0.35), transparent 60%),
    repeating-linear-gradient(45deg, rgba(141, 62, 31, 0.025) 0 10px, transparent 10px 20px),
    repeating-linear-gradient(-45deg, rgba(141, 62, 31, 0.025) 0 10px, transparent 10px 20px),
    var(--ground);
  min-height: 100dvh;
}
h1, h2, h3, .display { font-family: "Fraunces", Georgia, serif; }
button { font: inherit; cursor: pointer; }
.wrap { max-width: 760px; margin: 0 auto; padding: 20px 18px 90px; }
.topbar { display: flex; align-items: center; justify-content: space-between; padding: 16px 18px 0; max-width: 760px; margin: 0 auto; }
.brand-lockup { display: flex; align-items: center; gap: 10px; }
.brand-mark { display: block; flex: none; width: 32px; height: 32px; }
.wordmark { font-family: "Fraunces", Georgia, serif; font-weight: 700; font-size: 26px; letter-spacing: 0.2px; }
.wordmark .dot { color: var(--clay); }
.chip { display: inline-flex; align-items: center; gap: 6px; background: var(--card); border: 1px solid var(--line); border-radius: 999px; padding: 7px 12px; font-size: 13px; font-weight: 600; box-shadow: 0 1px 3px rgba(74,52,25,0.06); }
.card { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow); padding: 20px; }
.btn { border: 0; border-radius: 14px; min-height: 48px; padding: 12px 20px; font-weight: 700; font-size: 16px; display: inline-flex; align-items: center; justify-content: center; gap: 8px; }
.btn-primary { background: linear-gradient(180deg, #f2d79b, var(--brass)); color: #3a2a10; box-shadow: inset 0 1px 0 rgba(255,255,255,0.6), 0 6px 16px rgba(194,154,91,0.35); }
.btn-primary:active { transform: translateY(1px); }
.btn-dark { background: var(--ink); color: #fdf8ee; }
.btn-ghost { background: transparent; border: 1.5px solid var(--line); color: var(--ink); }
.btn-clay { background: var(--clay-deep); color: #fff7ea; }
.btn[disabled] { opacity: 0.45; cursor: default; }
.tap { min-height: 48px; }
input[type="text"] { width: 100%; min-height: 52px; border: 1.5px solid var(--line); border-radius: 14px; padding: 12px 14px; font-size: 18px; background: #fff; color: var(--ink); }
input[type="text"]:focus { outline: 2px solid var(--brass-deep); border-color: transparent; }
.field-label { font-size: 13px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-soft); margin: 18px 0 8px; }
.variety-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
@media (max-width: 560px) { .variety-grid { grid-template-columns: 1fr; } }
.variety { text-align: left; background: var(--card); border: 2px solid var(--line); border-radius: var(--radius); padding: 16px; min-height: 48px; }
.variety.selected { border-color: var(--clay); background: #fdf3e7; }
.variety .vname { font-family: "Fraunces", Georgia, serif; font-size: 21px; font-weight: 700; }
.status-live { color: var(--good); font-weight: 700; font-size: 12px; letter-spacing: 0.05em; text-transform: uppercase; }
.status-open { color: var(--brass-text); font-weight: 700; font-size: 12px; letter-spacing: 0.05em; text-transform: uppercase; }
.reason-row { display: flex; flex-wrap: wrap; gap: 8px; }
.reason { border: 1.5px solid var(--line); background: var(--card); border-radius: 999px; padding: 10px 14px; min-height: 44px; font-weight: 600; }
.reason.selected { background: var(--palm); color: #f2efe4; border-color: var(--palm); }
.unit { margin-top: 26px; }
.unit-head { display: flex; align-items: baseline; gap: 10px; }
.unit-head h2 { margin: 0; font-size: 24px; }
.lesson { display: flex; align-items: center; gap: 14px; width: 100%; text-align: left; margin-top: 12px; background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); padding: 16px; box-shadow: 0 1px 4px rgba(74,52,25,0.05); min-height: 64px; }
.lesson .num { width: 44px; height: 44px; border-radius: 50%; background: var(--ground-2); border: 1.5px solid var(--brass-deep); display: flex; align-items: center; justify-content: center; font-family: "Fraunces", Georgia, serif; font-weight: 700; flex: none; }
.lesson.done .num { background: var(--palm); color: #f2efe4; border-color: var(--palm); }
.lesson .lt { font-weight: 700; font-size: 17px; }
.planned { border: 1.5px dashed var(--line); border-radius: var(--radius); padding: 16px; margin-top: 12px; color: var(--ink-soft); background: rgba(255,253,248,0.6); }
.progress-track { height: 10px; background: var(--ground-2); border-radius: 999px; overflow: hidden; }
.progress-fill { height: 100%; background: linear-gradient(90deg, var(--brass-deep), var(--clay)); border-radius: 999px; }
.exercise-word { font-family: "Fraunces", Georgia, serif; font-size: 44px; font-weight: 700; text-align: center; margin: 18px 0 4px; }
.exercise-sub { text-align: center; color: var(--ink-soft); margin: 0 0 8px; }
.choices { display: grid; gap: 10px; margin-top: 16px; }
.choice { min-height: 52px; border: 1.5px solid var(--line); background: #fff; border-radius: 14px; padding: 12px 16px; font-size: 18px; font-weight: 600; text-align: left; }
.choice.correct { border-color: var(--good); background: #eef7f1; }
.choice.wrong { border-color: var(--bad); background: #faefec; }
.grades { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; margin-top: 14px; }
.grade { min-height: 56px; border-radius: 14px; border: 1.5px solid var(--line); background: #fff; font-weight: 700; padding: 6px 4px; }
.grade small { display: block; font-weight: 600; color: var(--ink-soft); font-size: 12px; margin-top: 2px; }
.grade.g1 { border-color: #d9a49a; } .grade.g4 { border-color: var(--brass-deep); background: #fdf6e3; }
.tokens { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }
.token { min-height: 44px; padding: 8px 14px; border-radius: 12px; border: 1.5px solid var(--brass-deep); background: #fdf6e3; font-size: 18px; font-weight: 700; }
.token.used { opacity: 0.3; }
.built { min-height: 56px; border: 1.5px dashed var(--line); border-radius: 14px; padding: 10px 14px; font-size: 20px; font-family: "Fraunces", Georgia, serif; }
.pair-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 14px; }
.pair { min-height: 52px; border-radius: 14px; border: 1.5px solid var(--line); background: #fff; font-weight: 700; font-size: 17px; padding: 10px; }
.pair.sel { border-color: var(--clay); background: #fdf3e7; }
.pair.done { opacity: 0.35; }
.feedback { border-radius: 14px; padding: 14px 16px; margin-top: 16px; font-weight: 600; }
.feedback.good { background: #eef7f1; border: 1px solid #bfe0cc; }
.feedback.bad { background: #faefec; border: 1px solid #ecc4bc; }
.tabbar { position: fixed; bottom: 0; left: 0; right: 0; background: rgba(255, 253, 248, 0.95); backdrop-filter: blur(8px); border-top: 1px solid var(--line); display: flex; justify-content: center; gap: 6px; padding: 8px 10px calc(8px + env(safe-area-inset-bottom)); z-index: 20; }
.tabbar button { min-height: 48px; min-width: 84px; border: 0; background: transparent; border-radius: 12px; font-weight: 700; color: var(--ink-soft); }
.tabbar button.active { background: var(--ground-2); color: var(--ink); }
.small { font-size: 13px; color: var(--ink-soft); }
.hero-band { background: linear-gradient(135deg, #3a2a1c, #59331e 55%, #8d3e1f); color: #fdf3e3; border-radius: 22px; padding: 26px 22px; box-shadow: var(--shadow); position: relative; overflow: hidden; }
.hero-band::after { content: ""; position: absolute; inset: 0; background: repeating-linear-gradient(45deg, rgba(233,200,134,0.06) 0 12px, transparent 12px 24px); pointer-events: none; }
.hero-band h1 { margin: 0 0 6px; font-size: 32px; }
.streak-flame { font-size: 20px; }
audio { width: 100%; margin-top: 8px; }
.owner-entry { border: 1px solid var(--line); border-radius: 14px; padding: 14px; margin-top: 10px; background: #fff; }
.badge { display: inline-block; font-size: 11px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; border-radius: 999px; padding: 3px 8px; }
.badge.verified { background: #e3f2e7; color: #256b43; }
.badge.held { background: #f7e8cd; color: #7d5a10; }
.badge.noaudio { background: #f3e7e0; color: var(--clay-deep); }
.banner { background: #fdf3e7; border: 1px solid var(--brass-deep); border-radius: 12px; padding: 10px 14px; font-weight: 600; margin-top: 12px; }

/* Phase 2: family, kids, weekly, conversation, worklist */
select { font: inherit; font-weight: 600; min-height: 44px; border: 1.5px solid var(--line); border-radius: 12px; padding: 8px 12px; background: var(--card); color: var(--ink); }
input[type="password"] { font: inherit; width: 100%; border: 1.5px solid var(--line); border-radius: 12px; padding: 12px 14px; background: var(--card); color: var(--ink); box-sizing: border-box; }
.check-row { display: flex; gap: 10px; align-items: flex-start; margin-top: 14px; font-size: 14px; font-weight: 600; line-height: 1.45; }
.check-row input { width: 22px; height: 22px; margin-top: 1px; flex: none; accent-color: var(--clay); }
.week-card { margin-top: 16px; padding: 16px; }
.week-stars { font-size: 30px; letter-spacing: 4px; color: var(--brass-deep); }
.milestone-chip { background: #fdf6e3; border-color: var(--brass-deep); }
.culture-note { margin-top: 10px; border-left: 3px solid var(--brass-deep); background: #fdf6e3; border-radius: 0 10px 10px 0; padding: 10px 12px; font-size: 14px; line-height: 1.5; }
.convo-card { margin-top: 16px; padding: 18px; }
.word-chip { background: #fdf6e3; border-color: var(--brass-deep); }
.word-chip.plain { background: var(--card); }
.needs-list { margin: 8px 0 0; padding-left: 20px; font-size: 14px; line-height: 1.55; }
.worklist-unit .owner-entry { background: #fffdf8; }
/* Kids mode: bigger targets, warmer scale, nothing under 44px anywhere. */
.kids .lesson { min-height: 88px; padding: 20px; }
.kids .lesson .num { width: 56px; height: 56px; font-size: 20px; }
.kids .lesson .lt { font-size: 20px; }
.kids .choice, .kids .pair { min-height: 64px; font-size: 20px; }
.kids .exercise-word { font-size: 52px; }
.kids .exercise-sub { font-size: 17px; }
.kids .btn { min-height: 56px; font-size: 18px; }
.kids .token { min-height: 52px; font-size: 20px; padding: 10px 16px; }
.kids h2 { font-size: 27px; }

/* Phase 3 usefulness and panel looks pass: dictionary, source lines, home
   clarity, conversation practice, Together, ranked worklist, print cards.
   New classes only. Small text uses dark ink tokens for AA contrast. */
.source-line { font-size: 13px; color: var(--ink-soft); margin-top: 4px; }
.tone-slot { font-size: 13px; color: var(--ink-soft); margin-top: 2px; }
.home-clarity { margin-top: 16px; padding: 16px; }
.dict-tabs { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 16px; }
.dict-theme { margin: 20px 0 4px; font-size: 19px; }
.dict-row { padding: 0; overflow: hidden; }
.dict-main { display: block; width: 100%; text-align: left; background: transparent; border: 0; padding: 14px; min-height: 48px; color: var(--ink); }
.dict-form { display: block; font-family: "Fraunces", Georgia, serif; font-size: 22px; font-weight: 700; }
.dict-gloss { display: block; margin-top: 2px; font-size: 16px; }
.dict-detail { padding: 0 14px 14px; }
.dict-citation { color: var(--ink); line-height: 1.5; }
.dict-note { color: var(--ink-soft); line-height: 1.5; margin-top: 6px; }
.together-card { background: var(--card); }
.ranked-needs { background: var(--card); }
.print-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-top: 16px; }
.print-card { border: 1.5px dashed var(--ink-soft); border-radius: 12px; padding: 16px; background: var(--card); break-inside: avoid; }
.print-form { font-family: "Fraunces", Georgia, serif; font-size: 26px; font-weight: 700; }
.print-gloss { font-size: 16px; margin-top: 2px; }
@media print {
  .tabbar, .print-actions, .topbar { display: none; }
  .print-grid { grid-template-columns: 1fr 1fr; }
  body { background: #fff; }
}

/* Phase 4 stand out polish pass, panel BC1, BC2, BC6 and BC10.
   Raffia texture is untouched in this pass: same opacities as above,
   and it never sits over lesson forms at a higher opacity (BC3).
   Small brass text now uses the brass text token defined in :root;
   brass deep remains in place for borders, tracks and large display. */
.variety-names { margin: 10px 2px 0; }
.runner-card { padding: 16px; }
.runner-card .exercise-word { margin: 8px 0 4px; }
.runner-card .tone-slot { text-align: center; }
.lesson-dock { position: fixed; left: 0; right: 0; bottom: 0; z-index: 30; display: flex; justify-content: center; padding: 10px 18px calc(10px + env(safe-area-inset-bottom)); background: var(--card); border-top: 1px solid var(--line); box-shadow: 0 -6px 18px rgba(74, 52, 25, 0.08); }
.lesson-dock .btn { width: 100%; max-width: 724px; }
.why-summary { min-height: 44px; display: flex; align-items: center; font-weight: 700; cursor: pointer; }
.why-claim { margin: 0 0 6px; }
@keyframes ekwaa-rise { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
.hero-band, .card { animation: ekwaa-rise 0.45s ease-out; }
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; scroll-behavior: auto !important; }
}

/* LIVELINESS: Ekwaa motion system.
   Motion uses transform and opacity only, plus stroke dashoffset on
   the goal ring and the width transition already used for progress.
   Every animation below has a still equivalent in the reduced motion
   block at the end of this section. Curves are calm ease out curves,
   never bouncy, and colours come only from the existing palette. */
:root {
  --dur-fast: 160ms;
  --dur-med: 320ms;
  --dur-slow: 560ms;
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-soft: cubic-bezier(0.33, 1, 0.68, 1);
}
@keyframes motion-rise { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
@keyframes lesson-pulse { 0% { opacity: 0.55; transform: scale(1); } 70% { opacity: 0; transform: scale(1.45); } 100% { opacity: 0; transform: scale(1.45); } }
@keyframes lesson-settle { from { transform: scale(1.08); } to { transform: scale(1); } }
@keyframes char-blink { 0%, 92%, 100% { transform: scaleY(1); } 95% { transform: scaleY(0.12); } }
@keyframes char-wave { 0%, 20%, 100% { transform: rotate(0deg); } 7% { transform: rotate(9deg); } 13% { transform: rotate(3deg); } }
@keyframes char-bob { from { transform: translateY(0); } to { transform: translateY(-3px); } }
@keyframes confetti-fall { 0% { opacity: 0; transform: translate3d(0, -14px, 0) rotate(0deg); } 12% { opacity: 1; } 55% { transform: translate3d(var(--confetti-drift, 12px), 170px, 0) rotate(150deg); } 100% { opacity: 0.85; transform: translate3d(calc(var(--confetti-drift, 12px) * -0.5), 340px, 0) rotate(300deg); } }
@keyframes tile-shake { 0%, 100% { transform: translateX(0); } 25% { transform: translateX(-4px); } 75% { transform: translateX(4px); } }
@keyframes feedback-pop { from { opacity: 0.6; transform: scale(0.97); } to { opacity: 1; transform: scale(1); } }
@keyframes word-pop { 0% { opacity: 0.5; transform: scale(0.94); } 60% { opacity: 1; transform: scale(1.03); } 100% { opacity: 1; transform: scale(1); } }
@keyframes skeleton-pulse { from { opacity: 0.55; } to { opacity: 1; } }
.step-enter { animation: motion-rise var(--dur-med) var(--ease-out) both; }
.card-enter { animation: motion-rise var(--dur-slow) var(--ease-out) both; }
.progress-fill { transition: width var(--dur-med) var(--ease-out); }
.lesson.current .num { position: relative; }
.lesson.current .num::after { content: ""; position: absolute; inset: -4px; border-radius: 50%; border: 2px solid var(--brass-deep); opacity: 0; pointer-events: none; animation: lesson-pulse 2.4s var(--ease-out) infinite; }
.lesson.done .num { animation: lesson-settle var(--dur-med) var(--ease-out); }
.path-guide { display: inline-flex; align-items: center; gap: 6px; background: var(--card); border: 1px solid var(--brass-deep); border-radius: 999px; padding: 5px 10px; font-size: 12px; font-weight: 700; color: var(--ink); box-shadow: 0 1px 3px rgba(74, 52, 25, 0.08); }
.path-guide::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: var(--clay); flex: none; }
.guide-row { display: flex; align-items: center; gap: 14px; }
.char { display: block; flex: none; }
.char-eyes { transform-origin: center; transform-box: fill-box; animation: char-blink 5.6s var(--ease-soft) infinite; }
.char-arm-wave { transform-origin: 20% 85%; transform-box: fill-box; animation: char-wave 5s var(--ease-soft) infinite; }
.char-bob { transform-box: fill-box; animation: char-bob 3.6s var(--ease-soft) infinite alternate; }
.confetti { position: absolute; inset: 0; pointer-events: none; overflow: hidden; z-index: 5; }
.confetti i { position: absolute; top: 0; left: var(--confetti-left, 50%); width: 8px; height: 12px; border-radius: 2px; opacity: 0; animation: confetti-fall var(--confetti-dur, 2800ms) var(--ease-soft) var(--confetti-delay, 0s) both; }
.confetti i:nth-child(4n+1) { background: var(--clay); }
.confetti i:nth-child(4n+2) { background: var(--brass); }
.confetti i:nth-child(4n+3) { background: var(--palm); }
.confetti i:nth-child(4n) { background: var(--brass-deep); }
.goal-ring { position: relative; width: 96px; height: 96px; flex: none; background: var(--ground); border-radius: 50%; }
.goal-ring svg { display: block; width: 100%; height: 100%; transform: rotate(-90deg); }
.goal-ring .ring-track { fill: none; stroke: var(--palm); stroke-width: 8; opacity: 0.18; }
.goal-ring .ring-fill { fill: none; stroke: var(--brass); stroke-width: 8; stroke-linecap: round; stroke-dashoffset: var(--ring-offset, 0); transition: stroke-dashoffset var(--dur-slow) var(--ease-out); }
.shelf-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(138px, 1fr)); gap: 12px; margin-top: 16px; }
.word-card { background: var(--card); border: 1.5px solid var(--line); border-radius: 14px; padding: 14px; min-height: 72px; }
.word-card.collected { background: #fdf6e3; border-color: var(--brass-deep); box-shadow: 0 1px 4px rgba(74, 52, 25, 0.06); }
.word-card.new { animation: word-pop var(--dur-med) var(--ease-out) both; }
.fade-card { background: #fdf3e7; border: 1px solid var(--brass-deep); border-radius: 12px; padding: 10px 14px; font-weight: 600; margin-top: 12px; animation: motion-rise var(--dur-med) var(--ease-out) both; }
.challenge-bar { height: 10px; background: var(--ground-2); border-radius: 999px; overflow: hidden; margin-top: 12px; }
.challenge-fill, .challenge-bar-fill { display: block; height: 100%; background: var(--palm); border-radius: 999px; transition: width var(--dur-med) var(--ease-out); }
.play-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 12px; margin-top: 16px; }
.play-card { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow); padding: 16px; transition: transform var(--dur-fast) var(--ease-out); }
.play-card:hover { transform: translateY(-2px); }
.match-tile { min-height: 52px; border-radius: 14px; border: 1.5px solid var(--line); background: #fff; font-weight: 700; font-size: 17px; padding: 10px; transition: transform var(--dur-fast) var(--ease-out), opacity var(--dur-fast) var(--ease-out); }
.match-tile.sel { border-color: var(--clay); background: #fdf3e7; }
.match-tile.matched { border-color: var(--palm); background: #eef7f1; color: var(--palm); }
.match-tile.wrong { animation: tile-shake var(--dur-med) var(--ease-out); }
.tone-opt { min-height: 52px; border: 1.5px solid var(--line); background: #fff; border-radius: 14px; padding: 12px 16px; font-size: 18px; font-weight: 600; text-align: left; transition: transform var(--dur-fast) var(--ease-out), opacity var(--dur-fast) var(--ease-out); }
.tone-opt:hover { transform: translateY(-1px); }
.tone-opt.sel { border-color: var(--clay); background: #fdf3e7; }
.build-bank { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; padding: 12px; border: 1.5px dashed var(--line); border-radius: 14px; background: rgba(255, 253, 248, 0.6); }
.skeleton { background: var(--ground-2); border-radius: 12px; min-height: 20px; animation: skeleton-pulse 1.5s var(--ease-soft) infinite alternate; }
.pop-in { animation: feedback-pop var(--dur-fast) var(--ease-out) both; }
.feedback.good.pop-in { animation: feedback-pop var(--dur-fast) var(--ease-out) both; }
@media (prefers-reduced-motion: reduce) {
  .step-enter, .card-enter, .fade-card, .lesson.done .num, .lesson.current .num::after, .char-eyes, .char-arm-wave, .char-bob, .confetti i, .match-tile.wrong, .word-card.new, .skeleton, .pop-in, .feedback.good.pop-in { animation: none !important; }
  .progress-fill, .challenge-fill, .challenge-bar-fill, .goal-ring .ring-fill, .play-card, .match-tile, .tone-opt { transition: none !important; }
  .play-card:hover, .tone-opt:hover { transform: none; }
  .lesson.current .num::after { display: none; }
  .confetti { display: none !important; }
}

/* CEREMONY: level complete variants, combo warmth, kind correction.
   Integration picks one celebration treatment per completion moment:
   confetti from the LIVELINESS section is variant one, rise below is
   variant two, and glow below is variant three. All three use only
   transform and opacity, and all three are disabled under the reduced
   motion block at the end of this section, with the card simply
   present and still. */
@keyframes celebrate-rise { from { opacity: 0; transform: scale(0.96); } to { opacity: 1; transform: scale(1); } }
@keyframes celebrate-glow-pulse { 0% { opacity: 0; } 45% { opacity: 0.9; } 100% { opacity: 0.55; } }
@keyframes combo-pop { from { opacity: 0; transform: scale(0.94); } to { opacity: 1; transform: scale(1); } }
@keyframes gentle-nudge { 0% { transform: translateX(0); } 45% { transform: translateX(4px); } 100% { transform: translateX(0); } }
.celebrate-rise { animation: celebrate-rise var(--dur-slow) var(--ease-out) both; }
.celebrate-glow { position: relative; isolation: isolate; }
.celebrate-glow::before { content: ""; position: absolute; inset: -18px; border-radius: 50%; background: radial-gradient(circle, rgba(233, 200, 134, 0.55) 0%, rgba(233, 200, 134, 0) 68%); opacity: 0; pointer-events: none; z-index: -1; animation: celebrate-glow-pulse 1.8s var(--ease-soft) both; }
.celebrate-glow .glow { position: absolute; inset: -18px; border-radius: 50%; background: radial-gradient(circle, rgba(233, 200, 134, 0.55) 0%, rgba(233, 200, 134, 0) 68%); opacity: 0; pointer-events: none; z-index: -1; animation: celebrate-glow-pulse 1.8s var(--ease-soft) both; }
.combo-note { display: inline-flex; align-items: center; gap: 6px; background: #fdf6e3; border: 1px solid var(--brass-deep); border-radius: 999px; padding: 7px 12px; font-size: 13px; font-weight: 700; color: var(--ink); animation: combo-pop var(--dur-med) var(--ease-out) both; }
.gentle-nudge { animation: gentle-nudge var(--dur-med) var(--ease-soft) both; }
@media (prefers-reduced-motion: reduce) {
  .celebrate-rise, .combo-note, .gentle-nudge { animation: none !important; }
  .celebrate-glow::before, .celebrate-glow .glow, .glow { animation: none !important; opacity: 0 !important; }
}

/* UNIT IDENTITY: per unit background treatments, design 09 Tone Bars.
   The sanctioned pattern is abstract geometry only: three horizontal
   bars of falling length with a stepped dot column stepping down
   beside them, a falling tone contour drawn as pure typographic
   rhythm. Never pictorial, never rising. Integration puts unit-scene
   plus one of unit-id-1 through unit-id-9 on a journey path unit
   container, and lesson-scene plus the same unit-id class on a lesson
   screen header. The slot follows the unit order within its variety,
   so a slot means the same journey position in every variety; the
   data side of this system lives in units.ts as UNIT_IDENTITY.
   Recipe per slot: a tint from the locked palette on the container,
   then a pattern on the ::before layer built from three repeating
   linear gradients. Layer one is the bar rhythm, layer two is the
   stepped dot column tiled to the bar rows, and layer three is a
   faint diagonal step that shortens the bars as they fall. Pattern
   colours are rgba forms of palm, clay deep and brass deep only, at
   alpha 0.05 to 0.10, far below text contrast, and the pattern sits
   on section containers behind content, never under small text at
   strength. Slots vary by tint, bar angle, bar thickness and spacing
   cadence (wide, even, clustered), and consecutive slots always
   differ in tint. The drift below moves background-position only, on
   the pattern layer only, as the one sanctioned moving background. */
@keyframes scene-drift { from { background-position: 0 0, 0 0, 0 0; } to { background-position: 6px 3px, -4px 6px, 3px -5px; } }
.unit-scene { position: relative; border-radius: var(--radius); overflow: hidden; }
.unit-scene::before { content: ""; position: absolute; inset: 0; pointer-events: none; animation: scene-drift 28s ease-in-out infinite alternate; }
.unit-scene > * { position: relative; z-index: 1; }
.lesson-scene { position: relative; border-radius: var(--radius); overflow: hidden; }
.lesson-scene::before { content: ""; position: absolute; inset: 0; pointer-events: none; opacity: 0.5; animation: scene-drift 32s ease-in-out infinite alternate; }
.lesson-scene > * { position: relative; z-index: 1; }
.unit-id-1::before, .unit-id-2::before, .unit-id-3::before, .unit-id-4::before, .unit-id-5::before, .unit-id-6::before, .unit-id-7::before, .unit-id-8::before, .unit-id-9::before { content: ""; position: absolute; inset: 0; pointer-events: none; animation: scene-drift 28s ease-in-out infinite alternate; }
.lesson-scene.unit-id-1::before, .lesson-scene.unit-id-2::before, .lesson-scene.unit-id-3::before, .lesson-scene.unit-id-4::before, .lesson-scene.unit-id-5::before, .lesson-scene.unit-id-6::before, .lesson-scene.unit-id-7::before, .lesson-scene.unit-id-8::before, .lesson-scene.unit-id-9::before { animation: scene-drift 32s ease-in-out infinite alternate; }
.unit-id-1 { background-color: #fdf3e7; }
.unit-id-1::before { background-image: repeating-linear-gradient(0deg, rgba(141, 62, 31, 0.08) 0 7px, transparent 7px 46px), repeating-linear-gradient(90deg, rgba(194, 154, 91, 0.07) 0 5px, transparent 5px 30px), repeating-linear-gradient(135deg, rgba(141, 62, 31, 0.05) 0 12px, transparent 12px 38px); background-size: 100% 100%, 30px 46px, 100% 100%; }
.unit-id-2 { background-color: #fdf6e3; }
.unit-id-2::before { background-image: repeating-linear-gradient(90deg, rgba(194, 154, 91, 0.09) 0 6px, transparent 6px 34px), repeating-linear-gradient(0deg, rgba(46, 75, 63, 0.06) 0 5px, transparent 5px 34px), repeating-linear-gradient(45deg, rgba(194, 154, 91, 0.05) 0 10px, transparent 10px 30px); background-size: 100% 100%, 34px 34px, 100% 100%; }
.unit-id-3 { background-color: #eef7f1; }
.unit-id-3::before { background-image: repeating-linear-gradient(45deg, rgba(46, 75, 63, 0.07) 0 5px, transparent 5px 22px), repeating-linear-gradient(90deg, rgba(46, 75, 63, 0.08) 0 4px, transparent 4px 18px), repeating-linear-gradient(0deg, rgba(194, 154, 91, 0.06) 0 4px, transparent 4px 22px); background-size: 100% 100%, 18px 22px, 100% 100%; }
.unit-id-4 { background-color: #f3ead9; }
.unit-id-4::before { background-image: repeating-linear-gradient(0deg, rgba(194, 154, 91, 0.10) 0 8px, transparent 8px 52px), repeating-linear-gradient(90deg, rgba(141, 62, 31, 0.06) 0 5px, transparent 5px 36px), repeating-linear-gradient(135deg, rgba(194, 154, 91, 0.05) 0 14px, transparent 14px 44px); background-size: 100% 100%, 36px 52px, 100% 100%; }
.unit-id-5 { background-color: #faefec; }
.unit-id-5::before { background-image: repeating-linear-gradient(135deg, rgba(141, 62, 31, 0.06) 0 6px, transparent 6px 40px), repeating-linear-gradient(90deg, rgba(141, 62, 31, 0.05) 0 4px, transparent 4px 32px), repeating-linear-gradient(0deg, rgba(194, 154, 91, 0.05) 0 5px, transparent 5px 40px); background-size: 100% 100%, 32px 40px, 100% 100%; }
.unit-id-6 { background-color: #fdf3e3; }
.unit-id-6::before { background-image: repeating-linear-gradient(90deg, rgba(46, 75, 63, 0.07) 0 4px, transparent 4px 20px), repeating-linear-gradient(0deg, rgba(194, 154, 91, 0.07) 0 4px, transparent 4px 20px), repeating-linear-gradient(45deg, rgba(46, 75, 63, 0.05) 0 8px, transparent 8px 24px); background-size: 100% 100%, 20px 20px, 100% 100%; }
.unit-id-7 { background-color: #e3f2e7; }
.unit-id-7::before { background-image: repeating-linear-gradient(0deg, rgba(46, 75, 63, 0.08) 0 6px, transparent 6px 36px), repeating-linear-gradient(90deg, rgba(46, 75, 63, 0.06) 0 5px, transparent 5px 28px), repeating-linear-gradient(135deg, rgba(194, 154, 91, 0.05) 0 10px, transparent 10px 32px); background-size: 100% 100%, 28px 36px, 100% 100%; }
.unit-id-8 { background-color: #f2efe4; }
.unit-id-8::before { background-image: repeating-linear-gradient(90deg, rgba(194, 154, 91, 0.08) 0 7px, transparent 7px 48px), repeating-linear-gradient(0deg, rgba(46, 75, 63, 0.06) 0 5px, transparent 5px 30px), repeating-linear-gradient(45deg, rgba(141, 62, 31, 0.05) 0 12px, transparent 12px 40px); background-size: 100% 100%, 34px 48px, 100% 100%; }
.unit-id-9 { background-color: #fdf8ee; }
.unit-id-9::before { background-image: repeating-linear-gradient(45deg, rgba(141, 62, 31, 0.07) 0 5px, transparent 5px 24px), repeating-linear-gradient(90deg, rgba(194, 154, 91, 0.08) 0 4px, transparent 4px 20px), repeating-linear-gradient(0deg, rgba(141, 62, 31, 0.05) 0 5px, transparent 5px 24px); background-size: 100% 100%, 20px 24px, 100% 100%; }
@media (prefers-reduced-motion: reduce) {
  .unit-scene::before, .lesson-scene::before, .unit-id-1::before, .unit-id-2::before, .unit-id-3::before, .unit-id-4::before, .unit-id-5::before, .unit-id-6::before, .unit-id-7::before, .unit-id-8::before, .unit-id-9::before { animation: none !important; }
}

/* DANCE: the celebration dance, panel ruled 2026-11-11, with the
   owner reported defect fix of the same day. The guide in the
   celebrate pose dances on the completion cards only: hops, the
   raised arm swinging, the sparkles breathing, 2.4 seconds, exactly
   once, then still. Transform only, on the existing character hooks.
   It is abstract joy and is never named or framed as a traditional
   dance. Under reduced motion it never plays; the still celebrate
   pose carries the moment. The motion off switch (html.motion-off,
   set from the Home toggle) stills every animation in the app.
   Defect fix: the first build set no transform origin on the bob or
   the arm, so rotations pivoted around the SVG canvas origin and
   the figure read as sliding sideways instead of dancing, and the
   9px hops were too small to read. The bob now pivots at the feet
   (transform-box fill-box, origin 50% 100%) and the arm pivots at
   the shoulder: the arm group box in characters.ts runs from the
   shoulder joint near its lower left, so origin 20% 85% sits on
   that joint for both Sunny and Junior, whose arm geometry is the
   same. The hops rise about 17px and land on the beats of the
   104 BPM celebration groove: at 2.4 seconds the beats fall near
   24, 48 and 72 percent, which is where the landings sit, with a
   small squash at each landing. */
@keyframes char-dance { 0% { transform: translateY(0) rotate(0deg) scale(1, 1); } 12% { transform: translateY(-17px) rotate(-5deg) scale(0.99, 1.01); } 24% { transform: translateY(0) rotate(0deg) scale(1.02, 0.97); } 36% { transform: translateY(-17px) rotate(5deg) scale(0.99, 1.01); } 48% { transform: translateY(0) rotate(0deg) scale(1.02, 0.97); } 60% { transform: translateY(-11px) rotate(-3deg) scale(1, 1); } 72% { transform: translateY(0) rotate(0deg) scale(1.01, 0.98); } 84% { transform: translateY(-4px) rotate(0deg) scale(1, 1); } 100% { transform: translateY(0) rotate(0deg) scale(1, 1); } }
@keyframes char-dance-arm { 0%, 100% { transform: rotate(0deg); } 12% { transform: rotate(20deg); } 24% { transform: rotate(4deg); } 36% { transform: rotate(-22deg); } 48% { transform: rotate(-4deg); } 60% { transform: rotate(14deg); } 72% { transform: rotate(0deg); } }
@keyframes char-dance-sparkle { 0%, 100% { transform: scale(1); } 30% { transform: scale(1.25); } 60% { transform: scale(0.95); } }
.guide-dance .char-bob { transform-box: fill-box; transform-origin: 50% 100%; animation: char-dance 2.4s var(--ease-soft) 1 both; }
.guide-dance .char-arm-wave { transform-box: fill-box; transform-origin: 20% 85%; animation: char-dance-arm 2.4s var(--ease-soft) 1 both; }
.guide-dance .char-sparkles { transform-origin: center; transform-box: fill-box; animation: char-dance-sparkle 2.4s var(--ease-soft) 1 both; }
@media (prefers-reduced-motion: reduce) {
  .guide-dance .char-bob, .guide-dance .char-arm-wave, .guide-dance .char-sparkles { animation: none !important; }
}
html.motion-off *, html.motion-off *::before, html.motion-off *::after { animation: none !important; transition: none !important; }
html.motion-off .confetti { display: none !important; }

/* ART: dictionary illustrations and the speak step settle, panel ruled
   2026-11-11 (expert-verdicts-dictionary-art-2026-11-11.md). Pictures
   sit beside the word, never above the tone marks in importance: the
   dictionary row carries a small thumb, the detail and the speak card
   carry the drawing at reading size. The settle is the one micro life
   allowed: a single gentle arrival on the speak card, transform and
   opacity only, under a second, never a loop. Reduced motion and the
   motion off switch (html.motion-off, above) still it completely. */
.dict-main.has-illus { display: flex; align-items: center; gap: 12px; }
.dict-thumb { flex: none; line-height: 0; }
.dict-text { flex: 1; min-width: 0; }
.dict-illus { text-align: center; margin: 8px 0 2px; line-height: 0; }
.illus { border-radius: 50%; }
.illus-settle { text-align: center; line-height: 0; animation: illus-settle 0.7s var(--ease-soft) 1 both; }
@keyframes illus-settle { from { opacity: 0; transform: scale(0.92); } to { opacity: 1; transform: scale(1); } }
@media (prefers-reduced-motion: reduce) {
  .illus-settle { animation: none !important; }
}
/* LOCK: progression locking, panel ruled 2026-11-11. A locked lesson
   stays visible on the path (learners can see what is ahead) and says
   plainly what opens it. Completed lessons never lock. */
.lesson.locked { opacity: 0.55; }
.lock-hint { margin: 6px 2px 2px; }
