
:root{
  --ground:#E7EDE4;
  --surface:#F2F7EF;
  --surface-2:#D8E2D2;
  --ink:#152018;
  --ink-soft:#48594D;
  --ink-faint:#7B8C7F;
  --rule:#C2CFBD;
  --verdigris:#25664A;
  --verdigris-ink:#174630;
  --verdigris-soft:rgba(37,102,74,.12);
  --porphyry:#8A2F3F;
  --porphyry-soft:rgba(138,47,63,.10);
  /* 🚨 RIGHT IS GREEN ON EVERY THEME. Never move these into THEMES. */
  --laurel:#15803D;
  --laurel-soft:rgba(21,128,61,.12);
  --brass:#7E6A16;
  --band:rgba(158,138,40,.42);
  --word:rgba(158,138,40,.85);
  --on-accent:#F2F7EF;
  --control-bg:#1F5A41;
  --control-ink:#EDF4EA;
  --control-border:#164630;
  --tick-now:#14432E;
  --tick-done:rgba(37,102,74,.30);
  --shadow:0 1px 2px rgba(21,32,24,.07),0 8px 24px rgba(21,32,24,.07);
}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]){
    --ground:#0E1A13;
    --surface:#14251A;
    --surface-2:#1C3123;
    --ink:#E2EBE2;
    --ink-soft:#93A896;
    --ink-faint:#6B8071;
    --rule:#27402F;
    --verdigris:#66C293;
    --verdigris-ink:#93D9B2;
    --verdigris-soft:rgba(102,194,147,.16);
    --porphyry:#E08497;
    --porphyry-soft:rgba(224,132,151,.16);
    --laurel:#4ADE80;
    --laurel-soft:rgba(74,222,128,.16);
    --brass:#D8B355;
    --band:rgba(216,179,85,.42);
    --word:rgba(216,179,85,.85);
    --on-accent:#0B160F;
    --control-bg:#17301F;
    --control-ink:#DCEADF;
    --control-border:#345A41;
    --tick-now:#3F9B69;
    --tick-done:rgba(102,194,147,.28);
    --shadow:0 1px 2px rgba(0,0,0,.35),0 8px 24px rgba(0,0,0,.32);
  }
}
:root[data-theme="dark"]{
  --ground:#0E1A13;
  --surface:#14251A;
  --surface-2:#1C3123;
  --ink:#E2EBE2;
  --ink-soft:#93A896;
  --ink-faint:#6B8071;
  --rule:#27402F;
  --verdigris:#66C293;
  --verdigris-ink:#93D9B2;
  --verdigris-soft:rgba(102,194,147,.16);
  --porphyry:#E08497;
  --porphyry-soft:rgba(224,132,151,.16);
  --laurel:#4ADE80;
  --laurel-soft:rgba(74,222,128,.16);
  --brass:#D8B355;
  --band:rgba(216,179,85,.42);
  --word:rgba(216,179,85,.85);
  --on-accent:#0B160F;
  --control-bg:#17301F;
  --control-ink:#DCEADF;
  --control-border:#345A41;
  --tick-now:#3F9B69;
  --tick-done:rgba(102,194,147,.28);
  --shadow:0 1px 2px rgba(0,0,0,.35),0 8px 24px rgba(0,0,0,.32);
}

*{box-sizing:border-box}
body{
  margin:0;
  background:var(--ground);
  color:var(--ink);
  font-family:"Source Serif 4",Georgia,"Times New Roman",serif;
  font-size:20px;
  line-height:1.85;
  -webkit-text-size-adjust:100%;
}
.wrap{max-width:660px;margin:0 auto;padding:0 22px 96px}

/* ---------- masthead ----------
   🚨 CENTRED, LIKE EVERY OTHER PAGE ON THE SITE. Paul, 2026-09-04: "Kinds of
   Sentences is not centered and also the A Statement, a question, a command i
   would also like in the middle of the page."
   The site pages centre their `.phead` and the lesson masthead never did, so a
   lesson read as a different site the moment you arrived on one.
   🚨 CENTRING TEXT IS NOT CENTRING THE BOX, and this masthead had BOTH of the
   ways to get it wrong that are already written up in CLAUDE.md:
     - `.dek` has a max-width, so text-align alone leaves a 52ch box hard left.
       It needs margin-inline:auto.
     - `.eyebrow` is a FLEX ROW, and a flex row packs its items left however the
       text is aligned. It needs justify-content:center. */
.mast{padding:44px 0 24px;border-bottom:1px solid var(--rule);text-align:center}
.eyebrow{
  font-family:"IBM Plex Sans",system-ui,sans-serif;
  font-size:12px;font-weight:600;line-height:1.4;
  letter-spacing:.16em;text-transform:uppercase;
  color:var(--verdigris);
  display:flex;flex-wrap:wrap;gap:10px;align-items:center;justify-content:center;
}
.eyebrow .dot{width:4px;height:4px;border-radius:50%;background:var(--ink-faint)}
h1{
  font-family:Cinzel,Georgia,serif;
  font-weight:600;font-size:clamp(30px,7vw,44px);line-height:1.15;
  letter-spacing:.01em;text-wrap:balance;
  margin:14px 0 10px;
}
.dek{color:var(--ink-soft);font-size:17px;line-height:1.65;margin:0 auto;max-width:52ch}

/* ---------- teacher notes ----------
   🚨 STYLED ON THIS TEMPLATE'S OWN TOKENS (--surface, --rule, --ink-soft,
   --verdigris), NOT the ns.css names. ns.css is not loaded on a lesson page, so
   a --a or --dim here resolves to nothing and the panel renders unstyled. That
   is exactly what went wrong on the unit strip; it is written down twice now.
   The disclosure arrow points RIGHT closed and DOWN open, matching the integers
   and English panels - Paul, 2026-08-30. */
.ground{border:1px solid var(--rule);border-radius:10px;margin:14px 0 4px;
  background:var(--surface);overflow:hidden}
/* 🚨 THE TITLE IS CENTRED, and the chevron must not push it off centre.
   Paul, 2026-09-04: "just call this box and center the title simply Teacher
   Notes." A flex row with the arrow on margin-left:auto centres the text only
   in the space LEFT OVER, which is not centred at all. So the row is a
   1fr auto 1fr grid: an empty cell, the title, the chevron. The title sits on
   the true centre line whatever the arrow does.
   ⚠️ This is the same trap recorded in CLAUDE.md under the five ways to get
   centring wrong: centring a GROUP is not centring the middle item. */
.ground summary{cursor:pointer;padding:10px 17px;font-weight:700;font-size:15px;list-style:none;
  display:grid;grid-template-columns:1fr auto 1fr;align-items:center;
  text-align:center;color:var(--ink)}
.ground summary::before{content:""}
.ground summary::-webkit-details-marker{display:none}
.ground summary::after{content:"\25B8";justify-self:end;color:var(--ink-faint);
  transition:transform .18s;display:inline-block}
.ground[open] summary::after{transform:rotate(90deg)}
.ground summary:hover{color:var(--verdigris)}
.ground .who{font-size:.72rem;letter-spacing:.09em;text-transform:uppercase;
  color:var(--ink-faint);font-weight:600}
/* The score the parent sees first, at the top of the teacher box. */
.gscore{display:flex;align-items:center;gap:14px;flex-wrap:wrap;
  padding:11px 0 13px;margin-bottom:4px;border-bottom:1px solid var(--rule)}
/* 🚨 SPECIFICITY: `.ground .gbody p` sets a 9px bottom margin and beats a bare
   .gscore-line, so the score sat 4.5px above the button it is meant to line up
   with. Measured on the live page, not guessed: line mid 439, button mid 444.
   Scoped under .gscore so it wins, and the row centres properly. */
.gscore .gscore-line{margin:0;font-weight:600;font-size:14.5px;color:var(--ink);line-height:1.3}
.gscore .gscore-line.good{color:#1c7a4a}
.gscore .gscore-line.part{color:var(--ink-soft, var(--ink))}
.gscore .gscore-line.bad{color:#c0392b}
.gscore .btn{margin-left:auto;font-size:12.5px;padding:6px 12px}
@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]) .gscore .gscore-line.good{color:#5fcf95}
  :root:not([data-theme="light"]) .gscore .gscore-line.bad{color:#ff7a6b}
}
:root[data-theme="dark"] .gscore .gscore-line.good{color:#5fcf95}
:root[data-theme="dark"] .gscore .gscore-line.bad{color:#ff7a6b}
.ground .gbody{padding:2px 17px 18px;border-top:1px solid var(--rule);
  color:var(--ink-soft);font-size:15.5px}
.ground h3{margin:17px 0 5px;font-size:.79rem;letter-spacing:.09em;
  text-transform:uppercase;color:var(--verdigris);font-weight:700}
.ground p{margin:0 0 9px;line-height:1.62;max-width:68ch}
.ground dl{margin:6px 0 0;max-width:68ch}
.ground dt{font-weight:700;color:var(--ink);margin:9px 0 1px}
.ground dd{margin:0;line-height:1.6}

/* ---------- player ---------- */
.player{
  /* Docked to the foot of the screen on every width. Paul, 2026-08-29:
     "i said i want it to be on the bottom of the screen on the pc too and
     follow the screen like on mobile."

     ⚠️ TWO LEFTOVERS FROM THE STICKY VERSION BROKE THIS AND BOTH WERE SILENT:
       margin:22px 0 34px  - a fixed box with bottom:0 AND a bottom margin
                             floats 34px off the floor. It looked like the dock
                             had failed rather than like a margin.
       padding:14px 16px 12px - the shorthand came after padding-bottom and
                             wiped the safe-area inset, so an iPhone home
                             indicator would have sat over the controls.
     Never leave a shorthand after the longhand it overwrites. */
  position:fixed;left:0;right:0;bottom:0;top:auto;z-index:40;
  margin:0;
  max-height:80vh;overflow-y:auto;
  display:flex;flex-direction:column;
  background:var(--surface, var(--panel));
  border:1px solid var(--rule, var(--line));
  border-width:1px 0 0;
  border-radius:0;
  box-shadow:0 -6px 24px rgba(0,0,0,.30);
  padding:8px 16px calc(10px + env(safe-area-inset-bottom, 0px));
  font-family:"IBM Plex Sans",system-ui,sans-serif;
}
/* 🚨 THE THREE READING CONTROLS SIT IN THE MIDDLE. Paul, 2026-08-29. They
   were left-aligned with a spacer shoving the speed selector to the far right,
   which parked the primary action in the corner of a wide screen. Back, Play
   and Forward are one centred cluster; speed sits centred underneath. */
.btn{
  font-family:inherit;font-size:14px;font-weight:500;
  background:transparent;color:var(--ink, var(--fg));
  border:1px solid var(--rule, var(--line));border-radius:3px;
  padding:9px 12px;cursor:pointer;line-height:1;
  display:inline-flex;align-items:center;gap:7px;
  transition:background .15s,border-color .15s,color .15s;
}
.btn:hover{background:var(--surface-2, var(--boxfill));border-color:var(--ink-faint, var(--dim))}
.btn:focus-visible{outline:2px solid var(--verdigris, var(--a));outline-offset:2px}
/* One rule owns the play button. It is a FIXED rectangle - Paul, 2026-08-29:
   "i want it just a small rectangle shape ... it doesnt need to stretch to
   fit" - because a transport button that changes width when you press it never
   feels solid. */
.btn.primary{
  background:var(--verdigris, var(--a));border-color:var(--verdigris, var(--a));
  color:var(--on-accent, #0b160f);font-weight:600;
  flex:0 0 auto;width:56px;min-width:56px;min-height:37px;
  padding:0;justify-content:center;border-radius:6px;border-width:1.5px;
}
.btn.primary:hover{filter:brightness(1.06);background:var(--verdigris, var(--a))}
.btn svg{display:block}
.rate{
  font-family:inherit;font-size:13px;font-weight:500;
  color:var(--control-ink, var(--fg));
  background-color:var(--control-bg, var(--boxfill));
  border:1px solid var(--control-border, var(--boxline));border-radius:3px;
  cursor:pointer;
  /* native arrow sits hard against the right edge, which leaves a gap on a wide
     select. Own arrow, tucked just after the text. */
  -webkit-appearance:none;-moz-appearance:none;appearance:none;
  padding:8px 27px 8px 10px;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5' fill='none' stroke='%23DCEADF' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat:no-repeat;
  background-position:right 9px center;
  background-size:11px 7px;
}
.rate::-ms-expand{display:none}
.rate option{background:var(--control-bg, var(--boxfill));color:var(--control-ink, var(--fg))}
.rate:hover{border-color:var(--verdigris, var(--a))}
.rate:focus-visible{outline:2px solid var(--verdigris, var(--a));outline-offset:2px}

.scrub{
  margin-top:10px;display:flex;gap:2px;height:16px;
  align-items:stretch;cursor:pointer;
  touch-action:none;      /* the bar scrubs; it must not also scroll the page */
}
.tick{flex:1;background:var(--surface-2, var(--boxfill));border-radius:1px;
  transition:background .14s ease;pointer-events:auto}
.tick.done{background:var(--tick-done, var(--accent-soft))}
.tick.now{background:var(--tick-now, var(--a))}
@media (hover:hover){ .tick:hover{background:var(--verdigris-soft, var(--accent-soft))} }
/* while scrubbing, kill the transitions entirely: a drag should track the
   finger exactly, not chase it 140ms behind */
.scrub.dragging .tick{transition:none}
.pcontrols{
  display:flex;align-items:center;justify-content:center;
  gap:10px;flex-wrap:wrap;
}
/* 🚨 display:contents, so on a wide screen this wrapper is not a box at all and
   the bar lays out exactly as it did before it existed. It becomes a real row
   only inside the phone media query. */
.pfoot{display:contents}

/* ── THE SETTINGS ROWS AND THEIR DIALOG ──────────────────────────────────
   Paul, 2026-09-15: "right now on mobile i have the settings open and it takes
   up a massive amount of screen ... if they press speed they get a mobile popup
   of the speed to select instead of the box to show ... the box will be centered
   on the screen ... this would shrink it all down removing the option for text
   boxes." The same on a PC, because one centred box is easier to read than four
   controls crammed onto a bar. */
/* 🚨 A GRID, SO EVERY PILL IS THE SAME SIZE. Paul: "make all the pills the
   same size." flex with flex:1 1 auto sized each one to its own text, so Speed
   came out narrow and Voice API key wide. Equal columns cannot do that. */
.setrows{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;padding:6px 0 2px}
@media (min-width: 621px){ .setrows{grid-template-columns:repeat(4,minmax(0,1fr))} }
.setbtn{
  min-width:0;display:flex;align-items:center;justify-content:space-between;
  gap:10px;font:inherit;font-size:13px;cursor:pointer;
  background:var(--surface-2, var(--boxfill));color:var(--ink, var(--fg));
  border:1px solid var(--rule, var(--line));border-radius:6px;padding:5px 10px;min-height:32px;
}
.setbtn:hover{border-color:var(--verdigris, var(--a))}
.setbtn .lbl{color:var(--ink-soft, var(--dim));white-space:nowrap}
.setbtn .val{font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

/* 🚨 CENTRED, AND NOT DOCKED TO THE PLAYER. A settings box that grows out of the
   bar is what was eating the screen; this one sits in the middle and the bar
   stays the height it was. */
dialog.pdlg{
  border:1px solid var(--rule, var(--line));border-radius:10px;padding:0;
  background:var(--surface, var(--panel));color:var(--ink, var(--fg));
  width:min(420px, calc(100vw - 32px));max-height:min(80vh, 560px);
  /* 🚨 CENTRE IT EXPLICITLY. A <dialog> defaults to margin:auto, which UA sheets
     and an inherited margin can both defeat - it was drawing off-centre. Fixed
     positioning with a translate cannot be argued with. */
  position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);margin:0;
}
dialog.pdlg::backdrop{background:rgba(0,0,0,.55)}
.pdlg-head{
  display:flex;align-items:center;justify-content:space-between;gap:12px;
  padding:13px 14px;border-bottom:1px solid var(--rule, var(--line));
}
.pdlg-head h3{margin:0;font-size:1rem;letter-spacing:.01em}
.pdlg-x{
  background:transparent;border:1px solid var(--rule, var(--line));border-radius:6px;
  color:inherit;cursor:pointer;padding:5px;display:inline-flex;line-height:0;
}
.pdlg-x:hover{border-color:var(--verdigris, var(--a))}
.pdlg-body{padding:14px;overflow:auto;max-height:calc(80vh - 58px)}
/* inside the dialog the rows stack and take the full width */
/* 🚨 [hidden] LOSES TO ANY CLASS RULE THAT SETS display. FOURTH TIME ON THIS
   SITE, and it is written in CLAUDE.md: the UA sheet is one attribute selector
   and .pdlg-body .voicerow is two classes, so display:block won and every
   section showed at once however correctly the JS set hidden. Paul saw it:
   "i open reading speed and its all together."
   ⚠️ VERIFY A HIDE WITH getComputedStyle().display, NEVER el.hidden. Reading
   the property told me it was hidden while it was plainly on screen. */
.pdlg-body > [hidden]{display:none !important}
.pdlg-body .voicerow,.pdlg-body .themerow{display:block;margin:0 0 10px}
/* 🚨 EVERYTHING IN THE SHEET IS CENTRED. Paul: "look at the settings items they
   are not centered look." The controls were already full width and centred, but
   every LABEL sat hard left and the key field sat off to one side, so the box
   read as ragged. One popup holds one setting, so there is no column to align
   to and centre is the right answer on a phone and on a desktop alike. */
.pdlg-body{text-align:center}
/* 🚨 NO BORDER ON A ROW THAT IS ALONE IN A BOX. Paul: "also a double line here
   on the color one." .voicerow, .keyrow and .themerow each carry a border-top
   from when they were stacked separators on the bar. In a popup showing ONE of
   them that line sits directly under the dialog header rule and reads as two. */
/* ⚠️ THE CLASSES ARE NAMED HERE ON PURPOSE. `.pdlg-body > *` reads like it
   would do the job and does not: `*` is worth ZERO specificity, so that
   selector ties with plain `.themerow` and loses on source order.
   ⚠️ The speed row needs no entry of its own: it is a div with class voicerow
   and id speedrow, so .voicerow already covers it. A .speedrow selector would
   match nothing. */
.pdlg-body > .voicerow,.pdlg-body > .keyrow,.pdlg-body > .themerow{border-top:0;padding-top:0;margin-top:0}
/* The key row draws itself as a tinted card, which was right on the bar and is
   a box inside a box here. The dialog IS the box. (.keyrow.show is two classes,
   so this needs three to beat it.) */
.pdlg-body > .keyrow.show{margin:0;padding:0;background:none;border:0;max-width:none}
.pdlg-body .voicerow label,.pdlg-body .themerow .lbl{display:block;margin:0 0 6px;text-align:center}
.pdlg-body .swatches{justify-content:center}
.pdlg-body .keyfield{justify-content:center;flex-wrap:wrap}
.pdlg-body .keyfield input{flex:1 1 200px;min-width:0}
/* the two explanatory paragraphs stay left: centred body text is hard to read */
.pdlg-body .keydisc,.pdlg-body .keynote{text-align:left}
.pdlg-body select.rate{width:100%}
.pdlg-body .vsel{width:100%}
/* 🚨 INSIDE A POPUP, THE POPUP IS THE PICKER. Paul, 2026-09-15: "i cant select
   differnt voices ... even with a regualr popup it will not let me scroll up to
   see more", and on the PC too. .vsel-list is position:absolute with
   bottom:calc(100% + 6px) - it opens UPWARD, which was right when the picker
   sat on a bar docked to the bottom of the screen. In a dialog whose body is
   overflow:auto it opens up and OUT of the box, and the box clips it. There is
   nothing to scroll to: it is drawn outside.
   ⚠️ So do not "fix" this by raising a z-index or adding overflow:visible.
   overflow:visible would break the dialog's own scrolling on a long voice list,
   and z-index cannot rescue a child clipped by its scroll container. The list
   becomes a plain block and the DIALOG scrolls. */
.pdlg-body .vsel-trigger{display:none}
.pdlg-body .vsel-list{
  position:static;bottom:auto;max-height:none;box-shadow:none;margin:0;
  background:transparent;color:inherit;border:0;padding:0;
}
.pdlg-body .vsel-list li{border:1px solid var(--rule, var(--line));margin:0 0 6px}
.pdlg-body .vsel-list li:last-child{margin-bottom:0}
.pdlg-body .keyrow{display:block}
.pdlg-body .keytogglerow{display:none}   /* the row IS the disclosure now */
.pmeta{
  order:2;display:flex;align-items:center;justify-content:center;
  gap:14px;flex-wrap:wrap;text-align:center;
  font-size:12px;color:var(--ink-faint, var(--dim));margin-top:7px;
  font-variant-numeric:tabular-nums;letter-spacing:.02em;
}
.voicerow{
  display:flex;align-items:center;gap:9px;margin-top:9px;
  padding-top:9px;border-top:1px solid var(--rule, var(--line));
}
.voicerow.show{display:flex}
.voicerow label{
  font-size:11px;font-weight:600;letter-spacing:.14em;text-transform:uppercase;
  color:var(--ink-faint, var(--dim));
}
.voicerow select{flex:0 1 auto;min-width:0;max-width:100%;font-size:13px}

/* studio voice key */
.linkbtn{
  margin-left:auto;background:none;border:0;cursor:pointer;padding:4px 2px;
  font-family:inherit;font-size:12px;font-weight:600;letter-spacing:.04em;
  color:var(--verdigris, var(--a));text-decoration:underline;text-underline-offset:3px;
}
.linkbtn:focus-visible{outline:2px solid var(--verdigris, var(--a));outline-offset:2px}
.keyrow{display:none;margin-top:9px;padding-top:9px;border-top:1px solid var(--rule, var(--line))}
.keyrow.show{display:block}
.keyrow label{
  display:block;font-size:11px;font-weight:600;letter-spacing:.14em;
  text-transform:uppercase;color:var(--ink-faint, var(--dim));margin-bottom:7px;
}
.keyfield{display:flex;gap:7px;flex-wrap:wrap}
.keyfield input{
  flex:1 1 180px;min-width:0;font-family:inherit;font-size:13px;
  color:var(--control-ink, var(--fg));background:var(--control-bg, var(--boxfill));
  border:1px solid var(--control-border, var(--boxline));border-radius:3px;padding:9px 10px;
}
.keyfield input:focus-visible{outline:2px solid var(--verdigris, var(--a));outline-offset:2px}
.keynote{font-size:12px;line-height:1.5;color:var(--ink-soft, var(--dim));margin:8px 0 0}
.keynote.bad{color:var(--porphyry)}
.keynote.good{color:var(--verdigris-ink, var(--a))}

/* colour picker */
.themerow{
  display:flex;align-items:center;gap:9px;flex-wrap:wrap;margin-top:9px;
  padding-top:9px;border-top:1px solid var(--rule, var(--line));
}
.themerow .lbl{
  font-size:11px;font-weight:600;letter-spacing:.14em;text-transform:uppercase;
  color:var(--ink-faint, var(--dim));
}
/* Eight swatches since 2026-09-11 (Rose, Gold, Teal joined). They WRAP on a
   narrow phone rather than push the row past the edge. */
.swatches{display:flex;flex-wrap:wrap;gap:7px}
/* `background-clip:padding-box` with a TRANSPARENT border meant the gradient
   stopped short and a 2px ring of the dark page showed through every swatch.
   Paul, 2026-08-26: "can you make the theme colors not have a black spare
   outline ... compare it to the math one". The maths swatches never had it,
   because the gradient runs to the edge and the border is drawn on top. Same
   here now: the ring only appears on the one that is selected. */
.sw{
  width:26px;height:26px;border-radius:50%;cursor:pointer;padding:0;
  border:2px solid var(--rule, var(--line));outline-offset:2px;
  transition:transform .15s,border-color .15s;
}
.sw:hover{transform:scale(1.12)}
.sw[aria-pressed="true"]{border-color:var(--verdigris, var(--a));transform:scale(1.12)}
.sw:focus-visible{outline:2px solid var(--verdigris, var(--a))}
.nospeech{
  font-family:"IBM Plex Sans",system-ui,sans-serif;font-size:13px;
  color:var(--porphyry);margin-top:10px;display:none;line-height:1.5;
}

/* ---------- story ---------- */
.part{margin:0 0 30px}
h2{
  font-family:Cinzel,Georgia,serif;font-weight:500;
  font-size:13px;letter-spacing:.15em;text-transform:uppercase;
  color:var(--ink-faint, var(--dim));
  margin:0 0 6px;padding-bottom:6px;border-bottom:1px solid var(--rule, var(--line));
}
/* 🚨 THE PARAGRAPH NEEDS THE SAME SCROLL ALLOWANCE AS A SENTENCE.
   "Find it in the story" reveals the .para now, not the story top, and without
   this the paragraph's first lines land UNDER the sticky nav - so the student is
   dropped into the middle of the very paragraph he was sent to read. Caught on
   the live page 2026-09-04, testing Question 10: the view opened on "command.
   The exclamation point simply shows urgency", with the three sentences before
   it hidden behind the header. */
.para{margin:0;scroll-margin-top:var(--stickytop,180px)}

/* 🚨 A TAPPABLE WORD IN THE READING. Paul, 2026-09-14: "put an underline with
   certain words in the story that are important to be clickable and it gives
   more information on it."
   ⚠️ A DOTTED UNDERLINE, NOT A LINK COLOUR. A coloured word inside a sentence
   competes with the reading highlight and with the answer-hunt band, and a
   struggling reader ends up looking at the colour rather than the line. The
   underline says "there is more here" without pulling the eye off the sentence.
   ⚠️ `text-underline-offset` keeps it clear of descenders; without it the dots
   collide with every g and y and the word looks misspelled. */
.gloss{
  text-decoration:underline dotted var(--verdigris);
  text-decoration-thickness:1.5px;
  text-underline-offset:4px;
  cursor:pointer;
}
.gloss:hover,.gloss:focus-visible{text-decoration-style:solid;outline:none}
.gloss.opened{text-decoration-color:var(--brass)}

/* The card itself. Sits under the sentence rather than floating over it, so it
   never covers the line the student was reading. */
.glossbox{
  margin:10px 0 2px;padding:12px 16px;
  border-left:3px solid var(--verdigris);
  background:var(--surface-2);border-radius:0 4px 4px 0;
  font-size:.92em;line-height:1.55;
}
.glossbox b{display:block;font-size:.8em;letter-spacing:.06em;
  text-transform:uppercase;color:var(--verdigris);margin-bottom:4px}
.glossbox .gx{float:right;cursor:pointer;color:var(--ink-soft);font-size:1.1em;
  line-height:1;padding:0 2px}

/* 🚨 EXAMPLES AND SCRIPTURE ARE SET APART FROM THE PROSE. Paul, 2026-09-14:
   "the examples in the story need to show them seperate from the text so the
   student can read it directly", and "i also want the bible verses just like the
   examples to be spaced apart from the other text ... it shows its an example."
   A blank line already made each one its own paragraph, but it still rendered in
   the same serif at the same size as the sentence above it, so nothing on screen
   said "this is the worked example" - it read as one more line of story.
   ⚠️ THE PARAGRAPH CARRIES THE CLASS, NOT THE SENTENCE. `.sent` is the highlight
   unit for the voice player and the answer hunt; styling it would fight the
   reading band and move the text under the highlight. */
/* 🚨 ITALIC SERIF, NOT MONOSPACE. Paul, 2026-09-24: he had already said he did
   not like the monospace examples, and it shipped again on ten lessons. Same
   face as english/template.html, which got this right.
   Each example LINE is its own line: consecutive [ex] lines share one box, and
   inline they ran together, so a stacked comparison or a worked step lost its
   shape. */
.para.is-ex{
  font-family:Georgia,"Iowan Old Style","Palatino Linotype",Palatino,"Times New Roman",serif;
  font-style:italic;
  font-size:1.05em;
  margin:6px 0;padding:10px 16px;
  border-left:3px solid var(--verdigris);
  background:var(--surface-2);border-radius:0 4px 4px 0;
}
.para.is-ex .sent{display:block}
/* 🚨 NOT FADED. Paul, 2026-09-24: "you always seem to make it faded". The verse
   is read at full ink like the story; only the box and the italic set it apart. */
.para.is-verse{
  font-family:Georgia,"Iowan Old Style","Palatino Linotype",Palatino,"Times New Roman",serif;
  font-style:italic;
  margin:6px 0;padding:10px 18px;
  border-left:3px solid var(--rule);
  color:var(--ink);
}
/* ⚠️ The highlight band still has to read INSIDE both boxes. `--band` is tuned
   against the page ground, and the example box sits on --surface-2, so the two
   were close enough to lose the band entirely at some themes. Lifting the band's
   own contrast here rather than changing --band keeps every other lesson alone. */
.para.is-ex .sent.on,.para.is-verse .sent.on{box-shadow:0 0 0 2px var(--verdigris)}

/* ---------- the explainer ----------
   Paul, 2026-09-04: "explaining how these sentences worked as it was reading it
   to you ... use a visual way of explaining along with the text so the youth can
   see how these sentences are changing and what they mean. just like how you
   explained with the math problem." And what it is FOR: "basically make this a
   way for attention issue students to still stay engaged."
   That purpose decides the design. One thing on screen at a time, the page holds
   still while it is open, and the meaning is carried by the drawing rather than
   by another paragraph of text to read.
   🚨 --stickytop IS MEASURED, NOT GUESSED. Two things are pinned at the top now,
   the nav and this, so anything scrolled to has to clear both. It used to be a
   hardcoded 180px for the nav alone. */
:root{--stickytop:180px}
/* 🚨 IT DOES NOT PIN. Paul, 2026-09-04: "i like how you snapped it to the top
   but it doesnt need to snap because it gets in the way later when answering
   question even when you hide it the tab is still there."
   A pinned bar is permanent furniture, and this panel is only wanted while the
   reading is happening. Sticky meant the collapsed tab still sat over the
   questions for the whole second half of the lesson, taking height off every
   screen for a control nobody was going to press again. In normal flow it
   scrolls away with the story it belongs to, which is the correct behaviour and
   also less code: no z-index, no measured offset, nothing to keep in step. */
.dbox{
  max-width:760px;margin:0 auto 18px;
  background:var(--surface-2, var(--boxfill));
  border:1px solid var(--rule, var(--line));border-radius:10px;
  padding:14px 16px 10px;
  scroll-margin-top:var(--stickytop,180px);
}
/* 🚨 min-height on the CONTENT, not the box, so collapsing actually collapses.
   While open the frame keeps its height through a blank stretch - a box that
   grew and shrank every few sentences would jog the whole page under the reader,
   which is the exact wobble the word highlight was kept plain to avoid. */
/* The line being read, small and quiet above the drawing. It is a caption on the
   panel, not a second place to read the lesson from - the story below is still
   where the reading happens. */
/* 🚨 A FIXED HEIGHT, BECAUSE THIS BAR MUST NOT RESIZE. Paul, 2026-09-05: "for the
   visual panel it is resizing keep the top bar the same lenth o it doesnt expand
   or contract. if the text is too long you could make it slightly smaller to
   fit."
   The sentence changes on every line of the lesson, and sentences are not the
   same length, so a bar that grows to fit made the whole panel breathe in and
   out several times a paragraph - the exact jitter this panel exists to stop.
   Three lines at the base size, held open whatever is in it, and fitSentence()
   below shrinks the type for the few sentences that need more. */
.dbox-sent{
  height:41px;                       /* 2 lines at 13.5px / 1.5 */
  margin:0 auto 10px;padding-bottom:10px;box-sizing:content-box;
  border-bottom:1px solid var(--rule, var(--line));
  font-size:13.5px;line-height:1.5;text-align:center;
  color:var(--ink-soft, var(--dim));
  max-width:60ch;
  display:flex;align-items:center;justify-content:center;
  overflow:hidden;
}
.dbox.is-shut .dbox-sent{display:none}
.dbox.is-wide .dbox-in{min-height:0}
/* 🚨 16:9 INLINE TOO, NOT ONLY IN FULLSCREEN. Paul, 2026-09-19: "iwant the
   aspect even not in fullscreen to still be 16x9". The frame is then the same
   shape in both places, and it is already the shape a lesson video needs - so
   dropping a video in later does not change the size of the box the student has
   been reading next to.
   ⚠️ min-height is GONE on purpose. Two rules fighting over one box is what made
   the panel jump between frames; aspect-ratio owns the height now, and overflow
   catches the rare frame whose text is taller than the ratio allows. */
.dbox-in{aspect-ratio:16/9;min-height:0;overflow:auto;
  display:flex;flex-direction:column;justify-content:center}
/* Fullscreen hands the ratio to the ART instead, so the frame can use the whole
   screen rather than being capped by the box it sits in. */
.dbox.is-full .dbox-in{aspect-ratio:auto;overflow:visible}
/* The Coming Soon state: the sentence bar stays, the frame area says so. */
.dbox.is-soon .dbox-in{min-height:96px;justify-content:center;text-align:center}
.dbox-soon{
  font-family:"IBM Plex Sans",system-ui,sans-serif;
  font-size:12px;letter-spacing:.14em;text-transform:uppercase;font-weight:700;
  color:var(--verdigris, var(--accent));margin:0 0 8px;
}
.dbox-soonnote{
  font-size:14px;line-height:1.6;color:var(--ink-faint, var(--dim));
  margin:0 auto;max-width:44ch;
}
.dbox-kind:empty{display:none}
.dbox-kind{
  /* flex, not a plain block, so a frame naming TWO parts can put a chip per
     part side by side. A one-word kind centres identically either way. */
  display:flex;gap:10px;flex-wrap:wrap;justify-content:center;align-items:center;
  margin:0 0 10px;text-align:center;
  font-size:11.5px;letter-spacing:.14em;text-transform:uppercase;font-weight:700;
  color:var(--verdigris, var(--accent));
}
/* the sentence, pulled apart into its pieces */
.dbox-line{
  display:flex;flex-wrap:wrap;align-items:baseline;justify-content:center;
  gap:0 8px;margin:0;font-size:21px;line-height:1.4;color:var(--ink);
}
/* 🚨 The understood subject. An imperative sentence "leaves out the subject",
   and that absence IS the lesson - so it is drawn, dimmed and bracketed. There
   is no other way to point at a word that is not there. */
.dbox-ghost{
  color:var(--ink-faint, var(--dim));font-style:italic;
  border:1px dashed var(--ink-faint, var(--dim));border-radius:5px;padding:0 7px;
}
.dbox-mark{
  color:var(--on-accent, #fff);background:var(--verdigris, var(--accent));
  border-radius:5px;padding:0 8px;font-weight:700;display:inline-block;
}
/* 🚨 A VERSE IS NOT A DIAGRAM. Paul, 2026-09-04: "bible verses should be
   included in the panel." It gets the same frame and the same timing, but none
   of the grammar furniture: no punctuation chip, no dashed understood subject.
   Setting a verse in the diagram treatment would put a highlighter on the full
   stop of Proverbs 25:11, which is not what anyone is being asked to look at. */
.dbox-line.is-verse{
  font-style:italic;font-size:19px;line-height:1.5;
  max-width:46ch;margin-inline:auto;
}
/* the drawing, above the line it belongs to. currentColor means it takes the
   lesson palette, so there is no second copy for the other theme. */
.dbox-art{display:flex;justify-content:center;margin:0 0 10px;color:var(--verdigris, var(--accent))}
/* a photo or illustration, as opposed to an inline icon */
.dbox.is-wide{max-width:860px;width:calc(100vw - 44px);margin-left:50%;transform:translateX(-50%)}
.dbox-art img{display:block;width:100%;height:auto;border-radius:8px;cursor:zoom-in}
.dbox-art.pic{margin:0 0 10px}
/* 🚨 THE SLOT IS 16:9 AND IS ALWAYS THERE. Paul, 2026-09-05: "it should just stay
   consistant." On a picture lesson the art area keeps its shape whether this
   frame has an image, a drawing or nothing at all, so the panel is one height
   from the first sentence to the last. `contain` is what makes the reservation
   safe: an image that is not exactly 16:9 letterboxes inside the slot instead of
   being stretched to fill it, which is the distortion Paul caught earlier today. */
.dbox.is-wide .dbox-art{width:100%;aspect-ratio:16/9;align-items:center;margin:0 0 10px}
.dbox.is-wide .dbox-art img{height:100%;object-fit:contain}
/* The two reserved text rows. `kind` is one line whether or not this frame names
   one; the example and its note share a fixed block below it. Together with the
   16:9 slot above, that is every part of the panel pinned to a height, which is
   what makes it the same size from the first sentence to the last. */
.dbox.is-wide .dbox-kind{height:21px;overflow:hidden}
.dbox.is-wide .dbox-words{
  height:74px;overflow:hidden;
  display:flex;flex-direction:column;justify-content:center;
}
.dbox.is-wide .dbox-words .dbox-note{margin:6px 0 0}
@media (max-height:820px){ .dbox.is-wide{max-width:660px} }
@media (max-height:620px){ .dbox.is-wide{max-width:520px} }
/* full screen on tap, because the detail in these does not survive a phone */
/* ── FULLSCREEN VISUAL PANEL ───────────────────────────────────────────────
   Paul, 2026-09-19: "the visual panel also needs to go fullscreen", and it is
   the panel a lesson video will eventually play in.

   🚨 IT STOPS ABOVE THE PLAYER, IT DOES NOT COVER IT. The reading dock is
   position:fixed at z-index 40. A panel on inset:0 would bury the play button,
   the scrub bar and the step arrows - so on a reading lesson the student could
   not advance the very reading the panel is following, and on a video he could
   not pause it. --dbfull-dock is the gap left for the dock.

   🚨 NOT THE FULLSCREEN API. requestFullscreen() needs a fresh user gesture
   every time, is refused outright inside some embedded contexts, and hands the
   screen to the browser so the page can no longer draw anything over the video.
   A fixed element is ours, works on every phone, and Escape still exits.

   ⚠️ .is-wide sets margin-left:50% and a translateX to break out of the column.
   Both have to be unset here or the panel sits half a screen to the right. */
/* 🚨 THE PANEL IS THE WHOLE SCREEN AND THE DOCK MOVES INSIDE IT.
   Three builds tried to leave a gap at the bottom for the docked player, and the
   last one failed INVISIBLY: in native fullscreen the page reported innerHeight
   994 while the window was really 840, so a dock pinned to the bottom of "the
   viewport" sat 150px below the glass. Every measurement said the scrub bar was
   on screen. It was not, and Paul told me so three times.
   Reserving space for an element you do not control is the mistake. The dock is
   RE-PARENTED into the panel on the way in and put back on the way out, so the
   controls live inside the thing that is fullscreen and cannot be lost whatever
   the viewport claims. It is also how a video player does it. */
.dbox.is-full{
  position:fixed;inset:0;
  /* 🚨 ABOVE THE SITE NAV (z-index 60), BELOW the picture lightbox (200).
     In true fullscreen the whole page still renders, so the sticky site nav
     sat across the top of the panel and it did not read as fullscreen at all.
     ⚠️ Raising this does NOT hide the reading dock: the dock stays visible
     because the panel STOPS ABOVE IT by geometry, never by stacking order. */
  z-index:80;max-width:none;width:auto;margin:0;transform:none;
  border:0;border-radius:0;background:var(--ground, var(--bg));
  display:flex;flex-direction:column;justify-content:center;
  padding:20px clamp(16px,5vw,64px);overflow-y:auto;
}
.dbox.is-full .dbox-in{min-height:0}
/* 16:9, which is the shape a YouTube embed wants and what the wide mode already
   used. max-height keeps it inside a short laptop screen. */
/* 🚨 :not([hidden]) IS LOAD-BEARING. .dbox-art carries display:flex, and a class
   rule that sets display BEATS the user-agent [hidden]{display:none} - the trap
   this file already records three times. Without the guard, a text-only frame
   reserved a 440px empty 16:9 well and pushed the words to the floor of the
   screen, which is exactly what the first build did. */
.dbox.is-full .dbox-art[hidden]{display:none}
.dbox.is-full .dbox-art:not([hidden]){
  width:100%;max-width:1100px;margin:0 auto 14px;
  aspect-ratio:16/9;max-height:66vh;align-items:center;
}
.dbox.is-full .dbox-art img{width:100%;height:100%;object-fit:contain}
.dbox.is-full .dbox-art iframe{width:100%;height:100%;border:0;display:block}
.dbox.is-full .dbox-words{max-width:900px;margin-inline:auto;text-align:center}
.dbox.is-full .dbox-sent{max-width:900px;margin-inline:auto;text-align:center}
.dbox.is-full .dbox-kind{height:auto;overflow:visible}
.dbox.is-full .dbox-foot{max-width:1100px;margin-inline:auto;width:100%}
/* A short screen has no room for the dock AND a 16:9 frame. */
@media (max-height:560px){ .dbox.is-full{--dbfull-dock:0px} }
/* The dock, once it is a child of the panel: a normal block at the foot of the
   column instead of a fixed bar. Every control and every handler is unchanged;
   only where it sits changes. */
.dbox.is-full .player{
  position:static;left:auto;right:auto;bottom:auto;z-index:auto;
  width:100%;max-width:1100px;margin:10px auto 0;flex:none;
}
/* 🚨 THE HIDE SWITCH COMES WITH IT. Paul, 2026-09-19: "the slider that hides the
   voice engine is still not there you just put the engine inside the full screen
   but the slider itself is not there." It is a separate fixed element at
   z-index 41, so a panel at 80 simply covered it - the dock arrived in
   fullscreen with no way to collapse it.
   ⚠️ pointer-events is OFF on the label and back ON for the track, because the
   label is stretched to the dock width to line the switch up with it. Without
   that, a click anywhere along that row would toggle the player. */
.dbox.is-full .playerhide{
  position:static;right:auto;bottom:auto;z-index:auto;
  width:100%;max-width:1100px;margin:0 auto;
  display:flex;justify-content:flex-end;pointer-events:none;
}
.dbox.is-full .playerhide .track{pointer-events:auto}
.dbox.is-full .dbox-in{flex:1 1 auto;min-height:0}
html.dbox-full-open, body.dbox-full-open{overflow:hidden}
/* The icon button. 44px so it is a real touch target on a phone. */
.dbox-full{
  appearance:none;background:transparent;border:1px solid transparent;border-radius:8px;
  color:var(--ink-soft, var(--dim));cursor:pointer;
  width:40px;height:40px;display:grid;place-items:center;flex:none;padding:0;
}
.dbox-full:hover{color:var(--ink);border-color:var(--rule)}
.dbox-full:focus-visible{outline:2px solid var(--verdigris);outline-offset:2px}
.dbox-full svg{width:20px;height:20px;fill:none;stroke:currentColor;
  stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
/* One glyph shows at a time: arrows out to enter, arrows in to leave. */
.dbox-full .i-out{display:none}
.dbox.is-full .dbox-full .i-in{display:none}
.dbox.is-full .dbox-full .i-out{display:inline}

.picfull{position:fixed;inset:0;z-index:200;background:rgba(0,0,0,.92);
  display:flex;align-items:center;justify-content:center;padding:16px;cursor:zoom-out}
.picfull img{max-width:100%;max-height:100%;border-radius:6px}
.picfull[hidden]{display:none}
.dbox-art svg{max-width:100%;height:auto}
@media (max-width:560px){ .dbox-art svg{width:104px;height:auto} }

/* the staged reveal - one word at a time, left to right */
.dbox-pop{
  display:inline-block;opacity:0;
  animation:dboxpop .34s ease-out forwards;
}
.dbox-arrow{color:var(--ink-faint, var(--dim));margin:0 2px}
@keyframes dboxpop{
  from{opacity:0;transform:translateY(4px)}
  to{opacity:1;transform:none}
}
@media (prefers-reduced-motion:reduce){
  .dbox-pop{animation:none;opacity:1}
}
/* the one-line shout, for a frame that is a statement rather than a diagram */
.dbox-line.is-shout{font-size:26px;font-weight:600;letter-spacing:.01em}
.dbox-note{
  margin:10px 0 0;text-align:center;font-size:14px;line-height:1.5;
  color:var(--ink-soft, var(--dim));
}
/* 🚨 THE CONTROL NAMES THE PANEL. Paul, 2026-09-05: "when collapsed it says open
   visual panel put a line seperator and it says close visual panel."
   A separate label beside the button was the first attempt and it was redundant
   the moment the button carried the name itself. One item, so it is genuinely
   centred rather than a centred GROUP - two things in a centred row is the trap
   where neither of them lands on the middle. */
/* 🚨 THREE COLUMNS, NOT flex+center. Paul, 2026-09-19: "close visual panel text
   is not centered" - and it was not, because the fullscreen icon is a sibling.
   justify-content:center centres the GROUP, so the label sat pushed right by
   half the width of the icon beside it. 1fr auto 1fr gives the label its own
   column, so it lands on the panel centre line whatever sits to its left. This
   is the same fix .ground summary already uses for its chevron, and the trap is
   written up in CLAUDE.md under the five ways to get centring wrong. */
.dbox-foot{
  display:grid;grid-template-columns:1fr auto 1fr;align-items:center;
  margin:10px 0 0;padding-top:8px;
  border-top:1px solid var(--rule, var(--line));
}
/* The icon sits in column one, hard left; the label owns the middle. */
.dbox-foot .dbox-full{justify-self:start}
.dbox-foot .dbox-x{grid-column:2}
/* 🚨 THE WHOLE ROW IS THE TAP TARGET, NOT JUST THE WORDS. Paul, 2026-09-14:
   "it's also hard to tap on if not pressed directly in the middle text of the
   panel." It was a 12px text button with 4px of padding, so the hit area was the
   glyphs themselves - fine with a mouse, close to unusable with a thumb.
   ⚠️ 44px MINIMUM HEIGHT is the size a finger actually hits. Do not trim it back
   for looks; the label stays small, only the target grew. */
.dbox-x{
  font:inherit;font-size:12px;
  letter-spacing:.08em;text-transform:uppercase;
  background:none;border:0;cursor:pointer;border-radius:5px;
  color:var(--ink-faint, var(--dim));
  display:flex;align-items:center;justify-content:center;
  width:100%;min-height:44px;padding:0 10px;
}
.dbox-x:hover{color:var(--ink)}
.dbox-x:focus-visible{outline:2px solid var(--verdigris, var(--accent));outline-offset:2px}
/* blank keeps the frame and drops the contents; shut drops the frame too */
.dbox.is-blank .dbox-in{visibility:hidden}
/* 🚨 `is-soon` OUTRANKS `is-blank`. The panel ships with `is-blank` on the
   element in the markup, so a Coming Soon panel inherited it and hid the very
   message it exists to show - an empty box that reads as broken, which is the
   one outcome worse than no panel at all. */
.dbox.is-soon .dbox-in{visibility:visible}
.dbox.is-shut{padding:4px 16px}
.dbox.is-shut .dbox-in{display:none}
/* collapsed, the footer IS the panel, so drop the gap it had above the content */
.dbox.is-shut .dbox-foot{margin:0;padding-top:0;border-top:0}


/* ---------- the worked problems ----------
   🚨 THE ESTIMATE COMES FIRST AND THE PAGE ENFORCES IT.
   Paul, 2026-09-05: "another problem we have today is the fact we can ask Google
   how to do these things instead of us solving them on our own which is the
   point of this lesson."
   The book's own answer to that is on page 6: before it divides 810 by 5 it
   rounds and brackets the answer between 100 and 200. Google hands you 162; it
   does not hand you "I decided beforehand this had to land between 100 and 200",
   and that bracket is the only thing that tells a student when an answer is
   wrong. So Solve stays SHUT until an estimate is in, and the order of the four
   steps is the lesson rather than a suggestion.
   ⚠️ Nothing here prints a number the student is supposed to work out. Every
   value he is asked for is an input. A printed answer turns the whole thing back
   into a worked example he watches. */
.work{border:1px solid var(--rule);border-radius:10px;margin:22px 0 0;overflow:hidden}
.work-h{
  margin:0;padding:12px 16px;background:var(--surface-2);
  border-bottom:1px solid var(--rule);
  font-family:"IBM Plex Sans",system-ui,sans-serif;
  font-size:12px;letter-spacing:.14em;text-transform:uppercase;font-weight:700;
  color:var(--verdigris);
}
.work-ask{margin:0;padding:14px 16px 0;font-size:17px;line-height:1.55;color:var(--ink)}
.work-given{margin:10px 16px 0;padding:0;list-style:none;display:grid;gap:4px}
.work-given li{
  font-size:14.5px;color:var(--ink-soft);
  display:flex;gap:8px;flex-wrap:wrap;
}
.work-given b{color:var(--ink);font-weight:600}
/* one row per step, dimmed and inert until the step before it is done */
.wstep{padding:14px 16px;border-top:1px solid var(--rule)}
.wstep.is-locked{opacity:.42}
.wstep-h{
  margin:0 0 6px;font-family:"IBM Plex Sans",system-ui,sans-serif;
  font-size:11.5px;letter-spacing:.12em;text-transform:uppercase;font-weight:700;
  color:var(--ink-faint);
}
.wstep.is-open .wstep-h{color:var(--verdigris)}
.wstep p{margin:0 0 8px;font-size:15px;line-height:1.55;color:var(--ink-soft)}
.wrow{display:flex;flex-wrap:wrap;align-items:center;gap:8px;font-size:17px;color:var(--ink)}
.wbox{
  font:inherit;font-size:17px;width:7.5ch;text-align:center;
  padding:7px 6px;border-radius:6px;
  border:1px solid var(--rule);background:var(--surface);color:var(--ink);
  /* the spinner reserves space on the right and pushes the digits off centre */
  appearance:textfield;-moz-appearance:textfield;
}
.wbox::-webkit-outer-spin-button,.wbox::-webkit-inner-spin-button{appearance:none;margin:0}
.wbox:focus-visible{outline:2px solid var(--verdigris);outline-offset:1px}
.wbox.is-right{border-color:#1c7a4a;background:rgba(28,122,74,.12)}
.wbox.is-wrong{border-color:#a3312b;background:rgba(163,49,43,.12)}
.wbox:disabled{cursor:not-allowed}
.wnote{margin:8px 0 0;font-size:14px;line-height:1.5;color:var(--ink-soft);display:none}
.wnote.show{display:block}
.wnote.good{color:#1c7a4a}
.wunit{color:var(--ink-soft);font-size:15px}
:root:not([data-theme="light"]) .wbox.is-right{border-color:#3f9d6d}
:root:not([data-theme="light"]) .wnote.good{color:#7fc9a1}
:root[data-theme="dark"] .wbox.is-right{border-color:#3f9d6d}
:root[data-theme="dark"] .wnote.good{color:#7fc9a1}
@media (max-width:560px){
  .work-ask{font-size:16px}
  .wrow{font-size:16px}
  .wbox{font-size:16px;width:6.5ch}
}

/* ---------- the compare box ----------
   A silent, visual side-by-side for the place in a lesson where two things look
   identical and are not. Paul asked for it on Kinds of Sentences: "it needs more
   clarification why a certain sentence is imperative over exclamatory ... i
   thought we were going to possibly add a visual box."
   🚨 IT IS NOT PART OF THE STORY. Nothing here enters SENT, so it is never read
   aloud, never highlighted, never counted by findsAt, and never re-baked. It is
   something to LOOK BACK AT while answering, which is the one thing a spoken
   paragraph cannot be. Adding it to the story instead would have renumbered
   every find index in the lesson for no gain. */
.cbox{
  border:1px solid var(--rule, var(--line));border-radius:8px;
  background:var(--surface-2, var(--boxfill));
  padding:16px 18px;margin:18px 0 0;
}
.cbox-h{
  font-family:Cinzel,Georgia,serif;font-weight:500;
  font-size:12.5px;letter-spacing:.14em;text-transform:uppercase;
  color:var(--ink-faint, var(--dim));margin:0;text-align:center;
}
.cbox-lead{
  margin:6px 0 0;text-align:center;font-size:15px;line-height:1.5;
  color:var(--ink-soft, var(--dim));
}
/* auto-fit so two columns become two stacked rows on a phone without a second
   breakpoint to keep in step with the rest of the page */
.cbox-cols{
  display:grid;gap:12px;margin:14px 0 0;
  grid-template-columns:repeat(auto-fit,minmax(220px,1fr));
}
.cbox-col{
  border:1px solid var(--rule, var(--line));border-radius:6px;
  background:var(--surface, var(--bg));padding:12px 14px;
}
.cbox-label{
  font-size:11.5px;letter-spacing:.12em;text-transform:uppercase;font-weight:700;
  color:var(--verdigris, var(--accent));margin:0 0 6px;
}
/* the quoted sentence is the thing being compared, so it carries the weight */
.cbox-s{margin:0;font-size:17px;line-height:1.45;color:var(--ink)}
.cbox-why{margin:6px 0 0;font-size:14.5px;line-height:1.5;color:var(--ink-soft, var(--dim))}
/* the test is the takeaway and sits apart from the two columns it resolves */
.cbox-test{
  margin:14px 0 0;padding-top:12px;
  border-top:1px solid var(--rule, var(--line));
  font-size:15px;line-height:1.55;color:var(--ink);
}
.sent{
  border-radius:3px;padding:1px 0;
  transition:background .2s;
  scroll-margin-top:var(--stickytop,180px);
}
.sent.reading{background:var(--band)}
/* ⚠️ NO BOLD, NO RING, NO SIZE CHANGE. Paul, 2026-08-29: "you are bolding or
   making the words on NexVoice bigger i dont want them to do that." Weight
   changes reflow the line as the highlight moves, which is exactly the wobble
   a struggling reader does not need. Colour only. */
.w.on{background:var(--word);border-radius:3px}
.hunting .sent{
  cursor:pointer;
  box-shadow:inset 0 -1px 0 var(--ink-faint, var(--dim));
}
.hunting .sent:hover{background:var(--verdigris-soft, var(--accent-soft));box-shadow:inset 0 -1px 0 var(--verdigris, var(--a))}
.sent.hit{background:var(--verdigris-soft, var(--accent-soft));box-shadow:inset 0 -2px 0 var(--verdigris, var(--a))}
.sent.miss{background:var(--porphyry-soft);box-shadow:inset 0 -2px 0 var(--porphyry)}
.sent.shown{background:var(--band);box-shadow:inset 0 -2px 0 var(--brass)}

/* ---------- words ---------- */
.dayhead{display:flex;align-items:flex-end;justify-content:space-between;gap:14px;flex-wrap:wrap}
.dayprog{
  font-family:"IBM Plex Sans",system-ui,sans-serif;
  font-size:12px;font-weight:600;letter-spacing:.04em;color:var(--ink-soft);
  white-space:nowrap;
}
.dayprog.done{color:var(--verdigris);font-weight:700}
.daybar{height:4px;border-radius:2px;background:var(--surface-2);overflow:hidden;margin:12px 0 4px}
.daybar i{display:block;height:100%;width:0;background:var(--verdigris);
  border-radius:2px;transition:width .35s var(--ease,ease)}
.daysec{margin-top:54px;padding-top:26px;border-top:2px solid var(--rule)}
.daytag{
  font-family:"IBM Plex Sans",system-ui,sans-serif;
  font-size:11px;font-weight:700;letter-spacing:.18em;text-transform:uppercase;
  color:var(--verdigris);margin:0 0 4px;
}
.sub-head{
  font-family:"IBM Plex Sans",system-ui,sans-serif;
  font-size:12px;font-weight:600;letter-spacing:.14em;text-transform:uppercase;
  color:var(--ink-faint);margin:30px 0 14px;
  padding-bottom:6px;border-bottom:1px solid var(--rule);
}
.daysec .section-head{margin-top:0}
.section-head{
  font-family:Cinzel,Georgia,serif;font-weight:600;
  font-size:22px;letter-spacing:.03em;
  margin:52px 0 6px;
}
.section-note{
  font-family:"IBM Plex Sans",system-ui,sans-serif;
  font-size:14px;line-height:1.6;color:var(--ink-soft);margin:0 0 20px;
}
.cards{display:grid;gap:10px;grid-template-columns:repeat(auto-fit,minmax(148px,1fr))}
.card{
  font-family:"IBM Plex Sans",system-ui,sans-serif;text-align:left;
  background:var(--surface);border:1px solid var(--rule);border-radius:4px;
  padding:14px;cursor:pointer;color:var(--ink);
  transition:border-color .15s,background .15s;
}
.card:hover{border-color:var(--verdigris)}
.card:focus-visible{outline:2px solid var(--verdigris);outline-offset:2px}
.card .term{
  font-family:Cinzel,Georgia,serif;font-size:16px;font-weight:600;
  letter-spacing:.04em;display:block;
}
.card .prompt{font-size:12px;color:var(--verdigris);letter-spacing:.08em;text-transform:uppercase;font-weight:600}
.card .def{font-size:14px;line-height:1.55;color:var(--ink-soft);margin-top:7px;display:none}
.card.open .def{display:block}
.card.open .prompt{display:none}

/* 🚨 THE FIND LINK APPEARS ONLY ON AN OPEN CARD. Showing it on a closed card
   puts a second control on something whose whole job is "tap me to see the
   meaning", and the student taps the wrong one. Read the definition first, then
   go look at where it came from. */
.cardfind{display:none;margin-top:9px;font-size:12.5px;font-weight:600;
  letter-spacing:.04em;text-transform:uppercase;color:var(--verdigris);
  cursor:pointer;text-decoration:underline;text-underline-offset:3px}
.card.open .cardfind{display:inline-block}
.cardfind:hover,.cardfind:focus-visible{color:var(--brass);outline:none}

/* ⚠️ A DIFFERENT HIGHLIGHT FROM THE ANSWER HUNT'S `.shown`. The two mean
   different things - "here is the answer you were hunting" against "here is
   where this word is explained" - and a student who has done both would
   otherwise be looking at one page with two identical bands and no way to tell
   which was which. */
.sent.wordshown{background:var(--band);box-shadow:inset 0 -2px 0 var(--verdigris)}
/* 🚨 GREEN OUTLINE ONCE READ, and it stays after the card is closed again.
   Paul, 2026-09-04: "if you clicked on the vocab section they turn green outline
   too to show you read them." A parent scanning the row can see at a glance
   which words the student actually opened.
   ⚠️ LITERAL green, not a palette token, for the same reason the verdict box is:
   read and unread must not change hue when a lesson theme changes. Same pair as
   the correct verdict, so the page has one green and not two. */
.card.read{border-color:#1c7a4a}
.card.read:hover{border-color:#1c7a4a}
@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]) .card.read{border-color:#3f9d6d}
  :root:not([data-theme="light"]) .card.read:hover{border-color:#5fcf95}
}
:root[data-theme="dark"] .card.read{border-color:#3f9d6d}
:root[data-theme="dark"] .card.read:hover{border-color:#5fcf95}

/* ---------- questions ---------- */
.q{
  background:var(--surface);border:1px solid var(--rule);border-radius:4px;
  padding:18px;margin-bottom:14px;
  font-family:"IBM Plex Sans",system-ui,sans-serif;
}
.qnum{
  font-size:11px;font-weight:600;letter-spacing:.16em;text-transform:uppercase;
  color:var(--verdigris);
}
.qtext{font-family:"Source Serif 4",Georgia,serif;font-size:19px;line-height:1.5;margin:6px 0 14px}
.stage{
  font-size:13px;color:var(--ink-soft);line-height:1.6;
  border-left:2px solid var(--rule);padding-left:12px;margin-bottom:12px;
}
.stage strong{color:var(--ink);font-weight:600}
.route{display:flex;gap:8px;flex-wrap:wrap;margin-top:10px}
.btn.ghost{background:transparent;color:var(--verdigris);box-shadow:inset 0 0 0 1px var(--rule)}
.btn.ghost:hover{box-shadow:inset 0 0 0 1px var(--verdigris);background:transparent}
.choices{display:flex;flex-direction:column;gap:8px}
.choice{
  font-family:inherit;font-size:15px;line-height:1.5;text-align:left;
  background:var(--surface-2);color:var(--ink);
  border:1px solid transparent;border-radius:3px;
  padding:12px 14px;cursor:pointer;
  transition:border-color .15s,background .15s;
}
.choice:hover:not(:disabled){background:var(--surface);border-color:var(--rule)}
.choice:focus-visible{outline:2px solid var(--verdigris);outline-offset:2px}
.choice:disabled{cursor:default}
.choice.right{background:var(--laurel-soft);border-color:var(--laurel);color:var(--laurel);font-weight:600}
.choice.wrong{background:var(--porphyry-soft);border-color:var(--porphyry)}
/* 🚨 A BOX, AND PLAIN GREEN OR RED. Paul, 2026-09-04: "make the box and this
   answer green text ... if wrong say incorrect making everything red."
   It used to be bare text tinted with the lesson's own palette tokens -
   --verdigris-ink and --porphyry - which are a blue-green and a purple-red.
   They read as "the lesson's colours", not as right and wrong.
   ⚠️ These are LITERAL colours on purpose, not palette tokens. Right and wrong
   must not change hue when a student picks a different lesson theme, and both
   pairs are chosen to hold their contrast on the light and dark grounds. */
.verdict{
  font-size:14px;line-height:1.6;margin-top:12px;display:none;
  padding:10px 13px;border-radius:4px;border:1px solid transparent;
}
.verdict.show{display:block}
.verdict.good{color:#1c7a4a;border-color:#1c7a4a;background:rgba(28,122,74,.10)}
.verdict.bad{color:#c0392b;border-color:#c0392b;background:rgba(192,57,43,.10)}
@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]) .verdict.good{color:#5fcf95;border-color:#3f9d6d;background:rgba(95,207,149,.12)}
  :root:not([data-theme="light"]) .verdict.bad{color:#ff7a6b;border-color:#c9564a;background:rgba(255,122,107,.12)}
}
:root[data-theme="dark"] .verdict.good{color:#5fcf95;border-color:#3f9d6d;background:rgba(95,207,149,.12)}
:root[data-theme="dark"] .verdict.bad{color:#ff7a6b;border-color:#c9564a;background:rgba(255,122,107,.12)}

/* ---------- hunt banner ---------- */
.hunt-bar{
  position:fixed;left:0;right:0;bottom:0;z-index:60;
  background:var(--surface);border-top:1px solid var(--verdigris);
  box-shadow:0 -6px 24px rgba(0,0,0,.12);
  padding:13px 20px;
  font-family:"IBM Plex Sans",system-ui,sans-serif;font-size:14px;
  display:none;align-items:center;gap:14px;
}
.hunt-bar.show{display:flex}
.hunt-bar .label{flex:1;line-height:1.45;color:var(--ink)}
.hunt-bar .label b{color:var(--verdigris);font-weight:600}

/* ---------- score ---------- */
.score{
  /* 🚨 NO ACCENT BAR ON THE RESULTS. It was border-top:3px var(--verdigris),
     and --verdigris is the THEME accent, so the results box wore a 3px stripe
     in whatever colour the theme happened to be - grey on Graphite, which is
     what Paul saw: "you have a graphite boader along the top of the box".
     It also implied a verdict the box must not give: this panel shows 29% as
     readily as 100%, so it has no business being coloured. Plain rule, exactly
     like .status right below it. */
  border:1px solid var(--rule);
  border-radius:4px;background:var(--surface);
  padding:22px;margin-top:26px;text-align:center;
  font-family:"IBM Plex Sans",system-ui,sans-serif;display:none;
}
.score.show{display:block}
.score .big{
  font-family:Cinzel,Georgia,serif;font-size:38px;font-weight:600;
  font-variant-numeric:tabular-nums;line-height:1.1;
}
.score .pct{font-size:15px;color:var(--ink-soft);margin-top:4px}
/* 🚨 THE TWO PART BOXES CARRY THE ANSWER A PARENT ACTUALLY WANTS - which HALF
   went wrong - so they were too small for the job. Paul, 2026-09-19. Bigger box,
   bigger number, and the 10px radius every other panel now uses. */
.score .split{
  display:flex;gap:14px;justify-content:center;flex-wrap:wrap;margin-top:18px;
}
.score .split span{
  display:flex;flex-direction:column;gap:5px;align-items:center;justify-content:center;
  border:1px solid var(--rule);border-radius:10px;padding:18px 26px;
  min-width:165px;background:var(--surface-2);
}
/* 🚨 NEUTRAL, NOT THE THEME ACCENT. Same leak as the stripe that used to sit on
   top of this panel: PART ONE / PART TWO are LABELS, and a label has no verdict
   to deliver. Bold, uppercase and letterspaced is already enough to read as a
   heading without borrowing the accent. Paul, 2026-09-19. */
.score .split b{font-size:12px;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--ink-soft)}
.score .split span{font-size:13px;color:var(--ink-soft)}
.score .split i{font-style:normal;font-size:26px;font-weight:700;color:var(--ink);font-variant-numeric:tabular-nums}
.score .line{font-size:14px;color:var(--ink-soft);margin-top:14px;line-height:1.6}

/* completion state */
.status{
  display:none;align-items:center;gap:14px;flex-wrap:wrap;
  margin-top:18px;padding:15px 18px;
  border:1px solid var(--rule);border-radius:4px;background:var(--surface);
  font-family:"IBM Plex Sans",system-ui,sans-serif;
}
.status.show{display:flex}
.statusline{display:flex;align-items:center;gap:10px;flex:1;min-width:200px}
.statusline .mark{
  width:24px;height:24px;border-radius:50%;flex:none;
  display:grid;place-items:center;
  background:var(--verdigris);color:var(--on-accent);
  font-size:14px;font-weight:700;line-height:1;
}
.status.partial .mark{background:var(--brass)}
#statusText{font-size:14px;line-height:1.5;color:var(--ink)}
#statusText b{font-weight:600}
#statusText small{display:block;color:var(--ink-soft);font-size:12.5px;margin-top:2px}

.done-note{
  font-family:"IBM Plex Sans",system-ui,sans-serif;
  font-size:14px;line-height:1.65;color:var(--ink-soft);
  border:1px dashed var(--rule);border-radius:4px;padding:16px;margin-top:22px;
}
.done-note b{color:var(--ink)}

/* ---------- printable answer sheet ---------- */
#sheet{display:none}
@media print{
  /* Only the answer sheet prints. The story, player and quiz UI are screen things. */
  body>*{display:none!important}
  #sheet{display:block!important;color:#000;background:#fff;font-family:"IBM Plex Sans",system-ui,sans-serif}
  @page{margin:16mm}
  #sheet h1{font-family:Cinzel,Georgia,serif;font-size:20pt;margin:0 0 2mm}
  #sheet .sub{font-size:10pt;color:#444;margin:0 0 6mm}
  #sheet .idline{display:flex;gap:10mm;flex-wrap:wrap;font-size:10pt;
    border-top:1px solid #000;border-bottom:1px solid #000;padding:3mm 0;margin-bottom:6mm}
  #sheet .idline b{font-weight:700}
  #sheet .scorebox{float:right;border:2px solid #000;padding:3mm 6mm;text-align:center;margin-left:6mm}
  #sheet .scorebox .n{font-size:20pt;font-weight:800;line-height:1}
  #sheet .scorebox .p{font-size:9pt;color:#333}
  #sheet ol{padding-left:6mm;margin:0}
  #sheet li{margin-bottom:5mm;font-size:10.5pt;line-height:1.45;page-break-inside:avoid}
  #sheet .q{font-weight:700;margin-bottom:1.5mm}
  #sheet .a{padding-left:3mm;border-left:2px solid #666}
  #sheet .a .mk{font-weight:800;margin-right:2mm}
  #sheet .foot{margin-top:8mm;padding-top:3mm;border-top:1px solid #999;font-size:9pt;color:#444}
  #sheet .sign{margin-top:10mm;font-size:10pt}
  #sheet .sign span{display:inline-block;border-bottom:1px solid #000;width:60mm;margin-left:3mm}
}

@media (prefers-reduced-motion:reduce){
  *{transition:none!important;animation:none!important;scroll-behavior:auto!important}
}
@media (max-width:420px){
  body{font-size:19px}
  .btn{padding:9px 10px}
}

/* ── FORM FIELDS ───────────────────────────────────────────────────────────
   🚨 A FIELD IS NOT A CONTROL, AND A FIELD IS PAPER. Paul, 2026-08-29: "the
   text boxes need to be white with black lettering ... i would do outlines in
   the theme color not the boxes themselves. you keep being bad at this across
   all my apps."

   The bug was structural and it was mine repeatedly: fields were painted with
   the palette CONTROL tokens - ctlBg / ctlInk - which EVERY light palette
   defines as a dark fill with near-white text, because those tokens were
   written for solid buttons. So on a light page every select and input
   rendered as a dark block. In graphite light that is ctlBg #333F55 sitting on
   a #F5F5F7 page.

   A field is white in BOTH modes. Making it a "lifted neutral" in dark was the
   same instinct that caused the original bug - it dissolves the field back
   into the panel it sits on. The theme colour lives in the OUTLINE.

   ⚠️ The old dropdown arrow had a hardcoded pale stroke (#DCEADF) which was
   invisible the moment a field went white. It is dark now, because the field
   it sits on is always white. */
select.rate{padding-top:9px;padding-bottom:9px}
select.rate, .keyfield input{
  font-family:inherit;font-size:13px;font-weight:500;
  background-color:#FFFFFF;color:#14161A;
  border:1.5px solid var(--verdigris, var(--a));border-radius:6px;
  padding:10px 12px;
  transition:border-color .15s ease, box-shadow .15s ease;
}
select.rate{
  -webkit-appearance:none;-moz-appearance:none;appearance:none;
  cursor:pointer;max-width:100%;padding-right:30px;
  background-image:url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 12 8%27%3E%3Cpath d=%27M1 1.5 6 6.5 11 1.5%27 fill=%27none%27 stroke=%27%2314161A%27 stroke-width=%272%27 stroke-linecap=%27round%27 stroke-linejoin=%27round%27/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 10px center;background-size:11px 7px;
}
select.rate::-ms-expand{display:none}
select.rate option{background:#FFFFFF;color:#14161A}
select.rate:hover, .keyfield input:hover,
select.rate:focus, .keyfield input:focus,
select.rate:focus-visible, .keyfield input:focus-visible{
  outline:none;border-color:var(--verdigris, var(--a));box-shadow:0 0 0 3px var(--verdigris-soft, var(--accent-soft));
}
.keyfield input{flex:1 1 200px;min-width:0;max-width:320px}
.keyfield input::placeholder{color:#7A7D85}

/* The docked bar covers the foot of the page on EVERY width now, so the
   padding is not a phone special case. Measured by playerDock(). */
body{padding-bottom:var(--player-pad, 150px)}

/* ── THE CONTROL CLUSTER, shared ──────────────────────────────────────────
   These live in the shared block rather than the history-only player CSS,
   because maths imports the fields but not the player chrome - it keeps its
   own stepping engine. Without them the maths cluster stayed left-aligned
   while every other lesson was centred, which is the kind of small
   inconsistency that makes a site feel unfinished. */
.pcontrols{order:0;margin-top:0;position:relative;min-height:44px}
/* pinned to the edges so the transport keeps the centre line to itself */
/* ⚠️ THE SWITCH IS NO LONGER INSIDE .pcontrols (2026-09-15). This rule used to
   pin it there absolutely, which is what dropped it onto the scrub the moment
   that row collapsed. Kept as a note so it is not "restored" by mistake. */
.pcontrols .followchip{position:absolute;left:2px;top:50%;transform:translateY(-50%)}
.scrub{order:1}
.pmeta{order:2}
.nospeech{order:3}
.keyrow{order:4}
.psettings{order:5}   /* last row: the one anchored to the bottom edge */
.phead .playerhide{margin-left:auto}
/* a switch has to LOOK like a switch: a real track, a real knob, at a size you
   can see. It is unlabelled, not invisible. */
@media (max-width: 620px){ .phint{display:none} }

.psettings{order:5}


/* 🚨 THE PLAY BUTTON DOES NOT STRETCH. Paul, 2026-08-29: "you made the button
   for play and pause strech but i want it just a small retwngle shape like the
   same size as on the pc right now. it doesnt need to strech to fit."
   It is an icon in a fixed rectangle. A transport button that changes width
   with the viewport stops being a landmark you can hit without looking. */

/* ── HIDING THE BAR ────────────────────────────────────────────────────────
   Paul, 2026-08-29: "maybe an option to hide it too."

   Even a compact bar is a bar, and a student who just wants to READ a page
   should be able to put it away. Hidden, it collapses to a small tab that
   stays reachable - never gone, because a control you cannot get back is worse
   than one taking up room. The choice is remembered per device, so a reader
   who prefers the page clean is not asked again on every lesson.

   It keeps PLAYING while hidden on purpose: a student may want to listen with
   the whole screen given over to the words. */
.playerhide{
  display:inline-flex;align-items:center;
  border:0;background:none;box-shadow:none;
  padding:6px 2px;margin-right:10px;
  color:var(--ink, var(--fg));cursor:pointer;
}
.playerhide input{position:absolute;opacity:0;width:1px;height:1px}
.playerhide .track{
  width:40px;height:23px;border-radius:999px;flex:0 0 auto;
  background:var(--surface-2, var(--boxfill));
  border:1px solid var(--rule, var(--line));
  position:relative;transition:background .16s ease,border-color .16s ease;
}
.playerhide .knob{
  position:absolute;top:2px;left:2px;width:17px;height:17px;border-radius:50%;
  background:var(--ink, var(--fg));transition:transform .16s ease;
}
.playerhide input:checked + .track{background:var(--verdigris, var(--a));border-color:var(--verdigris, var(--a))}
.playerhide input:checked + .track .knob{transform:translateX(17px);background:var(--on-accent, #0b160f)}
.playerhide input:focus-visible + .track{box-shadow:0 0 0 3px var(--verdigris-soft, var(--accent-soft))}
@media (prefers-reduced-motion: reduce){ .playerhide .track, .playerhide .knob{transition:none} }

/* 🚨 THE SWITCH ROW STAYS WHEN HIDDEN, OR THE PLAYER BURIES ITS OWN OFF-SWITCH
   AND HIDING BECOMES A ONE-WAY DOOR. The comment saying so was already here on
   2026-09-15 and the rule underneath it did the opposite: .pcontrols holds the
   switch, and hiding the whole row took the switch with it. Nobody had noticed
   because nothing ever added .player-hidden - the mode was written and never
   wired up. So the row stays and everything INSIDE it goes except the switch. */
/* 🚨 COLLAPSED IS A THIN SCRUB AND NOTHING ELSE.
   Paul, 2026-09-15: "you had only the thin scub show before i like that when it
   is turned off because it will hide the play button and make only a think srub
   near the bottom show ... the silder is just the tool to show and hide the play
   button." Audio keeps playing while it is collapsed; the switch hides the
   controls, it does not stop the reading. */
body.player-hidden .pcontrols,
body.player-hidden .pmeta,
body.player-hidden .psettings,
body.player-hidden .keyrow,
body.player-hidden .nospeech,
body.player-hidden .followchip{display:none}
body.player-hidden .player{padding:4px 14px calc(6px + env(safe-area-inset-bottom, 0px))}
/* the only thing left to aim at, so it gets a slightly bigger target */
body.player-hidden .scrub{margin:0;height:20px}

/* 🚨 THE SWITCH FLOATS ABOVE THE BAR AND FOLLOWS IT.
   --player-pad is the bar's own height plus a gap, already recomputed on resize,
   on the settings disclosure, and by a ResizeObserver on the player. Hanging the
   switch off it means there is ONE measurement, not two that can drift. */
.playerhide{
  position:fixed;right:14px;z-index:41;
  bottom:calc(var(--player-pad, 60px) - 2px);
  /* 🚨 NO TRANSITION ON THIS. Paul, 2026-09-15: "i can see it move and i rather
     it not visually move but snap upward so it looks like it is part of the
     expansion." A slide here reads as a second, separate thing chasing the bar;
     snapping with it reads as one object changing size. */
}
@media (max-width: 620px){ .player{transition:transform .22s ease} }
@media (prefers-reduced-motion: reduce){ .player, .playerhide{transition:none} }

/* The resume chip. Fixed above the thumb on a phone, out of the way of the
   text, and only present while following is off and something is playing. */
.followchip{
  opacity:0;pointer-events:none;visibility:hidden;
  border:1px solid var(--verdigris, var(--a));border-radius:999px;
  background:transparent;color:var(--verdigris, var(--a));
  font-family:inherit;font-size:11.5px;font-weight:700;padding:4px 11px;
  cursor:pointer;white-space:nowrap;
  transition:opacity .18s ease;
}
.followchip.show{opacity:1;pointer-events:auto;visibility:visible}
.followchip:hover{background:var(--verdigris-soft, var(--accent-soft))}
@media (prefers-reduced-motion: reduce){ .followchip{transition:none} }

/* 🚨 WHY THE COLOURS WERE NOT CENTRED ON A DESKTOP. Paul, 2026-08-29: "you
   also didnt center the theme colors on the pc." Correct, and centring the row
   was never going to fix it: .linkbtn carries margin-left:auto, so the key
   link was pinned to the far right and stretched the row across the whole
   player. The swatches then sat wherever was left. The link has its own
   centred row now, and the auto margin is off inside the player. */
.player .linkbtn{margin-left:0}


/* The disclosure arrow. Paul: "the dropdown arrow to add your own api is
   really small. we need a way that is has an extension option like the text
   that say Use my personal API part needs that arrow next to it." So the arrow
   belongs to the LABEL, is the same size as the label, and turns over when the
   panel opens - the whole thing reads as one control instead of a link with a
   speck beside it. */
/* 🚨 NO BOX. Paul, 2026-08-29: "i said just make the arrow bigger but you drew
   an entire oval box around it and separated api and color themes. that not
   what i wanted. i wanted it compact. i just wanted you to make the arrow
   bigger."

   He asked for one thing and I redesigned the panel around it. The pills and
   the extra row are gone; the API link sits back beside the colours where it
   always was. The ONLY change that stays is the arrow: an 18px SVG instead of
   the ▾ character, because a glyph draws at whatever fraction of its em box
   the font chooses, which is why raising font-size barely moved it. */
.keydisclose{display:inline-flex;align-items:center;gap:6px}

/* ── THE VOICE DROPDOWN ────────────────────────────────────────────────────
   Ours, not the platform's, so a phone and a PC show the same control. The
   trigger matches the other fields - white, theme outline - and the list opens
   UPWARD, because the player is docked to the bottom of the screen and a menu
   opening downward would go off the edge. */
.vsel{position:relative;display:inline-block;min-width:200px;max-width:100%}
.vsel-trigger{
  width:100%;display:flex;align-items:center;justify-content:space-between;gap:10px;
  font:inherit;font-size:13px;font-weight:500;
  background:#FFFFFF;color:#14161A;
  border:1.5px solid var(--verdigris, var(--a));border-radius:6px;
  padding:10px 12px;cursor:pointer;text-align:left;
}
.vsel-trigger:focus-visible{outline:none;box-shadow:0 0 0 3px var(--verdigris-soft, var(--accent-soft))}
.vsel-trigger .chev{flex:0 0 auto;transition:transform .18s ease}
.vsel-trigger[aria-expanded="true"] .chev{transform:rotate(180deg)}
.vsel-list{
  position:absolute;left:0;right:0;bottom:calc(100% + 6px);z-index:70;
  margin:0;padding:5px;list-style:none;
  background:#FFFFFF;color:#14161A;
  border:1.5px solid var(--verdigris, var(--a));border-radius:8px;
  box-shadow:0 -8px 24px rgba(0,0,0,.28);
  max-height:46vh;overflow-y:auto;
}
.vsel-list li{
  padding:11px 12px;border-radius:5px;cursor:pointer;font-size:13.5px;
}
.vsel-list li[aria-selected="true"]{background:var(--verdigris, var(--a));color:var(--on-accent, #0b160f);font-weight:700}
.vsel-list li[aria-disabled="true"]{opacity:.45;cursor:default}
@media (hover:hover){ .vsel-list li:not([aria-disabled="true"]):hover{background:#eceff3} }
.vsel-list li[aria-selected="true"]:hover{background:var(--verdigris, var(--a))}

.keytogglerow{display:flex;justify-content:center;padding:2px 0}
.psettings > summary{
  display:flex;align-items:center;justify-content:center;gap:6px;
  cursor:pointer;list-style:none;padding:6px 0 2px;
  font-size:12px;letter-spacing:.06em;text-transform:uppercase;
  color:var(--ink-faint, var(--dim));
}
.psettings > summary::-webkit-details-marker{display:none}
.psettings > summary::marker{content:""}
/* The SVG points down. Closed is rotated up; open returns it to down. */
.chev{flex:0 0 auto;transform:rotate(180deg);transition:transform .18s ease}
.keydisclose[aria-expanded="true"] .chev,
.psettings[open] > summary .chev{transform:rotate(0deg)}
@media (prefers-reduced-motion: reduce){ .chev{transition:none} }
.psettings{display:flex;flex-direction:column-reverse}
.psettings[hidden]{display:none}

/* ── VOICE AND COLOURS, CENTRED ────────────────────────────────────────────
   Paul, 2026-08-29: "i want that voice selector centered in the middle along
   with the theme colors for both pc and mobile."

   These rows were laid out as label-left / control-right, which is a form
   convention and wrong here: everything else in this player is a centred
   cluster, so two left-aligned rows underneath read as a different component
   bolted on. One axis for the whole player. */
.voicerow, .voicerow.show{justify-content:center;flex-wrap:wrap}
#speedrow, .voicerow, .themerow{
  flex-direction:column;align-items:center;justify-content:center;gap:6px;
}
#speedrow label, .voicerow label, .themerow .lbl{
  text-align:center;margin:0;
}
/* the API link is its own centred line under the swatches */
.themerow .keydisclose{margin-top:2px}
.themerow{justify-content:center;flex-wrap:wrap}

/* ── THE PLAYER ON A PHONE ─────────────────────────────────────────────────
   🚨 It was taking 327px of a 720px screen - 45% of the phone gone before a
   word of the lesson. Measured, not guessed.

   Nothing is removed, because a student on a phone needs the voice picker
   MORE than one on a PC, not less. What changes is that voice and colours stop
   being permanently open: they fold behind the settings summary, so the phone
   gets the controls and the step bar, and the rest is one tap away. */
/* ── THE PHONE PLAYER IS DOCKED TO THE BOTTOM ──────────────────────────────
   Paul, 2026-08-29: "i wonder if we can just lock it to the bottom on mobile
   and make it more compact."

   Right. A player pinned to the TOP of a phone competes with the thing it is
   reading: it takes the best part of the screen and pushes the text down. At
   the bottom it is a transport bar - the shape every music and podcast app
   settled on - it sits under the thumb instead of under the eyes, and the
   whole top of the screen goes back to the lesson.

   ⚠️ Three things have to move with it:
     - the body needs padding, or the bar covers the last paragraph
     - the settings disclosure opens UPWARD, or it would open off-screen
     - nsInView has to know the screen is short from the BOTTOM now, not the
       top, or the reader will happily scroll a sentence to a place the bar is
       covering
   The safe-area inset keeps it clear of the home indicator on an iPhone. */


/* ── BACK AND FORWARD ──────────────────────────────────────────────────────
   Paul, 2026-08-29: "draw a better outline around the back and forward
   button." They carried the generic hairline in --rule, a DIVIDER colour, so
   the arrows read as faint marks beside a solid Play. Back is the most-used
   control here - a student replays a sentence far more often than he starts
   one - so it must not be the faintest thing on the page. */
#back, #fwd{
  border:1.5px solid var(--verdigris, var(--a));border-radius:6px;background:transparent;
  color:var(--ink, var(--fg));min-height:37px;min-width:40px;padding:0 10px;justify-content:center;
}
#back:hover, #fwd:hover{background:var(--verdigris-soft, var(--accent-soft));border-color:var(--verdigris, var(--a));color:var(--ink, var(--fg))}
#back:focus-visible, #fwd:focus-visible{outline:none;box-shadow:0 0 0 3px var(--verdigris-soft, var(--accent-soft))}
#back:disabled, #fwd:disabled{opacity:.4;cursor:default}
#back:disabled:hover, #fwd:disabled:hover{background:transparent}

/* Save sits ON the field row, so it matches the field height exactly. Save is
   filled because it is what you almost always want; Clear stays an outline. */
.keyfield .btn{
  padding:10px 16px;border-radius:6px;border:1.5px solid var(--verdigris, var(--a));
  font-size:13px;font-weight:600;line-height:1.2;min-height:41px;
  background:transparent;color:var(--ink, var(--fg));
}
.keyfield .btn:hover{background:var(--verdigris-soft, var(--accent-soft))}
.keyfield .btn:focus-visible{outline:none;box-shadow:0 0 0 3px var(--verdigris-soft, var(--accent-soft))}
#keySave{background:var(--verdigris, var(--a));border-color:var(--verdigris, var(--a));color:var(--on-accent, #0b160f);font-weight:700}
#keySave:hover{filter:brightness(1.08);background:var(--verdigris, var(--a))}

.keyrow.show{
  display:block;margin:12px auto 0;padding:14px;
  max-width:520px;
  background:var(--verdigris-soft, var(--accent-soft));border:1px solid var(--verdigris, var(--a));border-radius:8px;
}
.keynote a{color:var(--verdigris-ink, var(--a, currentColor));text-decoration:underline;text-underline-offset:2px}
.keydisc{
  display:flex;gap:8px;align-items:flex-start;font-size:12.5px;line-height:1.55;
  color:var(--ink, var(--fg));margin:0 0 11px;font-weight:600;
}

/* ── ADDED, not asked for ──────────────────────────────────────────────────
   The player is sticky at the top. Without this, jumping to a sentence scrolls
   it to y=0 and parks it UNDER the player, so the line being read is the one
   line you cannot see. */
.s{scroll-margin-top:16px;scroll-margin-bottom:150px}
@media (prefers-reduced-motion: reduce){
  *{animation-duration:.01ms !important;transition-duration:.01ms !important;scroll-behavior:auto !important}
}


@media (max-width: 620px){
  .player{padding-left:10px;padding-right:10px}
  /* ⚠️ Measured, not guessed. The bar changes height when the settings panel
     is opened, when the voice list wraps, and on a device with a home
     indicator - a hardcoded number is wrong within a tap, and being wrong here
     means the last lines of a lesson sit behind the bar where nobody can read
     them. Set from the real height by playerDock(). */

  /* settings open upward: the summary is the last thing, the panel above it */
  .psettings{display:flex;flex-direction:column-reverse}
.psettings[hidden]{display:none}
  .s{scroll-margin-top:16px;scroll-margin-bottom:150px}
  .pcontrols{gap:8px}
  /* ⚠️ 12px of dead space used to sit under these buttons and push them hard
     against the top of the bar. Paul, 2026-09-15: "you have a big amount of it
     tied too close to the top if the footer with dead space under it. you cound
     bring it down and get rid of that extra dead space". */
  .pcontrols{min-height:0;padding:2px 0 2px}
  .btn.primary{width:50px;min-width:50px;min-height:40px}
  /* 🚨 ID SELECTORS, SO THEY BEAT ANY CLASS RULE. This is why the back and
     forward buttons stayed 40px while the play button shrank to 32 and Paul saw
     "the back and forther boxes are larger than the play now". Change the size
     HERE; adding .pcontrols .btn rules below will not win against an id. */
  #back, #fwd{min-height:32px;height:32px;min-width:38px;padding:0 8px}
  .scrub{margin-top:2px}

  /* the switch comes down with them, so the row stays balanced */
  /* 🚨 COMPACT ON A PHONE. Paul, 2026-09-15: "on the mobile can you make the
     reader more compact when its turned on. it still takes up alot of space
     like about a good 1/4th." It was 152px of a 696px screen, 22 per cent.

     Where it went: 52px of buttons, 26px of scrub, 24px for the sentence
     counter, 30px for the Settings row, 18px of padding.

     ⚠️ THE BUTTONS ARE NOT THE PLACE TO SAVE. A play button a child taps while
     reading keeps a real touch target; shaving that is how a control becomes
     annoying to hit. The saving comes from two nearly empty rows becoming one. */
  /* 🚨 ORDER 2, OR IT LANDS BETWEEN THE BUTTONS AND THE SCRUB. .scrub carries
     order:1 and everything else defaults to 0, so this row rendered above the
     scrub and read as dead space under the play button. Paul: "you still have
     dead space for the play button ... under it." It was this row. */
  /* 🚨 THREE COLUMNS, SO SETTINGS IS DEAD CENTRE WHATEVER THE COUNTER SAYS.
     Paul: "put the settings dropdown in the middle on mobile it moves when its
     closed and i know its because you have the sentence couter text." He was
     right: side by side in a flex row, "Sentence 9 of 86" and "Sentence 61 of
     86" are different widths, so Settings slid every few sentences. The empty
     third column balances the first and pins the middle one. */
  .pfoot{display:grid;order:2;grid-template-columns:1fr auto 1fr;align-items:center;gap:10px}
  .pfoot .pmeta{grid-column:1;justify-self:start}
  .pfoot .psettings{grid-column:2;justify-self:center}
  .pfoot .psettings[open]{grid-column:1 / -1;justify-self:stretch}
  .pfoot .pmeta{margin:0;flex:0 1 auto;min-width:0}
  .pfoot{flex-wrap:wrap}
  .pfoot .psettings{flex:0 0 auto}
  /* 🚨 OPEN, IT BREAKS OUT TO ITS OWN FULL-WIDTH ROW. Paul, 2026-09-15: the
     settings boxes were "not centered they are too far to the right ... i think
     because you moved the settings arrow". He was right: sharing a flex row with
     the counter meant the OPEN panel was trapped in that narrow flex item and
     its dropdowns sat under the summary instead of spanning the bar. Closed it
     shares the row; open it takes a line of its own. */
  .pfoot .psettings[open]{margin-top:4px}
  /* the hint is instruction, and it is learned after the first lesson */
  .phint{display:none}
  .player{padding:6px 14px calc(8px + env(safe-area-inset-bottom, 0px))}
  .scrub{margin-top:6px}
  /* 12px of bottom padding under the buttons is slack on a phone. The buttons
     themselves stay 40px - that is the touch target and it is not negotiable. */
  /* Paul asked for these smaller on a phone. 40px was my caution, not a rule;
     these stay comfortably tappable and buy back another row of reading. */
  /* 🚨 ALL THREE BUTTONS ARE THE SAME HEIGHT. Paul, 2026-09-15: "the back and
     forther boxes are larger than the play now." They were. .btn carries its own
     min-height:40px and .btn.primary carries a different one, so shrinking the
     padding moved the play button and left the other two exactly where they were.
     ⚠️ Set the height on ALL THREE or they drift apart again. */
  .pcontrols{padding-bottom:4px;justify-content:center;min-height:0}
  .pcontrols .btn{padding:0 9px;min-height:32px;height:32px}
  .pcontrols .btn svg{width:13px;height:13px}
  .pcontrols .btn.primary{width:46px;min-width:46px;min-height:32px;height:32px}
  .pcontrols .btn.primary svg{width:15px;height:15px}
  .psettings > summary{padding:4px 0}

  /* 🚨 ON A PHONE THE DIALOG IS A BOTTOM SHEET. Paul: "i want a mobile popup ui
     on the actual mobile." A centred box is a desktop pattern; a phone expects
     the panel to rise from the bottom edge, full width, with the corners rounded
     at the top only and a grab bar. Same dialog, same controls, different shape. */
  dialog.pdlg{
    top:auto;bottom:0;left:0;transform:none;margin:0;
    width:100vw;max-width:100vw;border-radius:14px 14px 0 0;
    border-left:0;border-right:0;border-bottom:0;
    max-height:min(78vh, 520px);
    padding-bottom:env(safe-area-inset-bottom, 0px);
  }
  /* the grab bar a phone user expects to see on a sheet */
  .pdlg-head::before{
    content:"";position:absolute;top:6px;left:50%;transform:translateX(-50%);
    width:38px;height:4px;border-radius:999px;background:var(--rule, var(--line));
  }
  .pdlg-head{position:relative;padding-top:18px}

  .playerhide .track{width:34px;height:20px}
  .playerhide .knob{top:2px;left:2px;width:14px;height:14px}
  .playerhide input:checked + .track .knob{transform:translateX(14px)}

  /* Jump back stays vertically centred on that row, hard left. It is already
     absolutely positioned, so it does not push the transport off centre. */
  .pcontrols .followchip{font-size:11px;padding:3px 9px}
  .pcontrols .tag{display:none}
  .pmeta{font-size:11px;margin:4px 0 0}
  .pmeta span:last-child{display:none}         /* "tap any sentence" is a desktop hint */
  .scrub{height:24px;margin:2px 0 0}
  .tick{border-radius:2px}

}
