/* ─────────────────────────────────────────────────────────────────────────
   THE SITE NAV, for pages that are NOT built from the ns.css shell.

   Lesson pages carry their own design systems: the history template has its
   own palette (--ground / --ink / --rule), the maths template runs on ns.css
   variables. Both needed the same nav. Paul, 2026-08-26: *"we need access to
   the home side menu on all our pages and resources ... this is what makes
   the website feel like a website."*

   So this file styles the nav against FIVE variables of its own, and each
   template maps its palette onto them. One nav, one stylesheet, no duplicated
   rules, and a theme change in either lesson type carries the nav with it.

     --nv-bg      the bar and drawer background
     --nv-fg      text at full strength
     --nv-dim     secondary text
     --nv-line    borders
     --nv-accent  the button fill
     --nv-on      text ON the button
   ───────────────────────────────────────────────────────────────────────── */

.ns-nav{position:sticky;top:0;z-index:60;background:var(--nv-bg);
  border-bottom:1px solid transparent;transition:border-color .3s}
.ns-nav.stuck{border-bottom-color:var(--nv-line)}
.ns-nav a{text-decoration:none}
/* 🚨 THESE THREE NUMBERS MUST EQUAL ns.css. Paul, 2026-09-04: "the entire header
   icons look shifted too the left too far including the home button."
   They were 20px/22px here and 28px/26px there, so the home button on a lesson
   started 8px left of the home button on every other page and the row was 16px
   wider. Nothing looked broken on either page alone; it only showed up moving
   between them. */
.nv{max-width:1180px;margin:0 auto;padding:14px 28px;display:flex;align-items:center;gap:26px}
/* 🚨 THE WHOLE WORDMARK IS ONE COLOUR. Paul, 2026-09-04: "the NexStudents name
   is the wrong text color. you have the NEX part Greay and the Students part
   White."
   Exactly right, and it was this rule: --nv-dim on .word with --nv-fg only on
   the <b>, so "Nex" came out grey and "Students" white. ns.css never dimmed it -
   there .word inherits --fg and .word b repeats --fg, so both halves match. The
   <b> stays in the markup because it is the brand's own capitalisation, not a
   styling hook. */
/* 🅰️ THE LESSON-PAGE TWIN OF ns.css's .word. Anton, caps, positive tracking,
   same as the site bar and the picker - Paul, 2026-09-12: "add that font style
   to the site top banner", and the bar is on lesson pages too.
   🚨 check-nav-css PASSED WITH THIS WRONG. It checks that a class is styled in
   BOTH sheets, not that the two agree, and .word was styled in both with
   different values - so lesson pages would have kept Archivo while every other
   page moved to Anton, with no warning. Change these two rules together.
   🔴 RED, not --nv-fg. Paul, 2026-09-12: "make the text not white but red on
   the site." A lesson palette must not recolour the brand, so this is literal. */
/* 🟠 ORANGE SINCE 2026-09-21, tracking ns.css's .word.
   🚨 LITERALS WITH A THEME OVERRIDE, NOT var(--a-text), and this is the whole
   reason this file exists separately: a LESSON PAGE HAS ITS OWN PALETTE. There
   is no --a-text there, so the token would resolve to nothing and the wordmark
   would render unstyled. Same pattern as .mg-grade.live and .drawer .navbtn.
   ⚠️ Values must stay identical to ns.css's --a-text: #FF9440 / #A8490A.
   check-nav-css catches a class styled in only one file; it CANNOT catch these
   two disagreeing. Change one, change both. */
.nv .word{font-family:'Anton',Archivo,system-ui,sans-serif;font-weight:400;font-size:21px;
  letter-spacing:.035em;text-transform:uppercase;color:#FF9440}
.nv .word b{color:#FF9440;font-weight:inherit}
:root[data-theme="light"] .nv .word,
:root[data-theme="light"] .nv .word b{color:#A8490A}
.nv .tabs{display:flex;gap:20px}
.nv .tabs a{font-size:14px;font-weight:500;color:var(--nv-dim);transition:color .18s}
.nv .tabs a.on{color:var(--nv-fg)}
@media(hover:hover){ .nv .tabs a:hover{color:var(--nv-fg)} }
@media(max-width:940px){ .nv .tabs{display:none} }

.nv .navbtn{margin-left:auto;display:inline-flex;align-items:center;justify-content:center;
  white-space:nowrap;text-align:center;line-height:1.1;
  font-family:inherit;font-weight:700;font-size:14px;padding:10px 20px;
  border-radius:999px;border:0;cursor:pointer;
  background:var(--nv-accent);color:var(--nv-on)}
@media(hover:hover){ .nv .navbtn:hover{filter:brightness(1.08)} }

.burger{display:none;flex-direction:column;justify-content:center;gap:5px;width:42px;height:38px;
  padding:0 9px;margin-left:-9px;background:none;border:0;cursor:pointer}
.burger i{display:block;height:2px;background:var(--nv-fg);border-radius:2px;
  transition:transform .3s ease,opacity .2s}
.burger i:nth-child(2){width:72%;align-self:flex-end}
body.nav-open .burger i:nth-child(1){transform:translateY(7px) rotate(45deg)}
body.nav-open .burger i:nth-child(2){opacity:0}
body.nav-open .burger i:nth-child(3){transform:translateY(-7px) rotate(-45deg)}
@media(max-width:940px){ .burger{display:inline-flex} }

.scrim{position:fixed;inset:0;background:rgba(0,0,0,.6);z-index:70;opacity:0;visibility:hidden;
  transition:opacity .3s ease,visibility .3s}
body.nav-open .scrim{opacity:1;visibility:visible}

.drawer{position:absolute;inset:0;background:var(--nv-bg);
  transform:none;transition:transform .34s cubic-bezier(.22,.61,.36,1);
  display:flex;flex-direction:column;padding:22px;overflow-y:auto}

.drawer .x{align-self:flex-end;background:none;border:0;color:var(--nv-dim);
  font-size:26px;line-height:1;cursor:pointer;padding:4px 8px}
.drawer a{display:block;padding:14px 0;font-size:1.05rem;font-weight:700;letter-spacing:-.02em;
  color:var(--nv-fg);text-decoration:none;border-bottom:1px solid var(--nv-line)}
.drawer a.on{color:var(--nv-accent)}
.drawer a small{display:block;font-size:.62rem;font-weight:700;letter-spacing:.13em;
  text-transform:uppercase;color:var(--nv-dim);margin-top:4px}
/* Centred, because `.drawer a` is display:block and would otherwise leave the
   label hard left in a full-width button. That bug shipped once already. */

/* ══════════════════════════════════════════════════════════════════════
   MEGA MENU — ONE panel, contents cross-fade
   Rewritten 2026-08-26 after watching lttstore with Paul.

   The first version gave every tab its own <details> with its own panel, so
   moving toward the menu left the element controlling it and the menu died
   mid-reach, and moving between tabs closed one and opened another with a
   jump. One panel fixes both: it stays open along the whole bar, only the
   inner swaps, and the height eases to the new content.

   The panel is a child of the NAV, not of a tab, so it spans the full bar.
   Nothing here closes it — that is a JS timer, because the pointer crosses
   dead space on the way down.
   ══════════════════════════════════════════════════════════════════════ */
.mg-top{position:relative;font-size:14px;font-weight:500;color:var(--nv-dim);
  padding-block:6px;text-decoration:none;transition:color .18s}
.mg-top::after{content:"";position:absolute;left:0;right:0;bottom:0;height:2px;
  background:currentColor;transform:scaleX(0);transform-origin:left;
  transition:transform .22s var(--ease,ease)}
.mg-top:hover,.mg-top.mg-live,.mg-top.on{color:var(--nv-fg)}
/* ONE rule draws the line. With JS the nav is .js-nav and exactly one tab
   carries .mg-live; without JS the current page draws its own. */
.js-nav .mg-top.mg-live::after{transform:none}
nav:not(.js-nav) .mg-top.on::after,
.ns-nav:not(.js-nav) .mg-top.on::after{transform:none}

.mg-panel{position:absolute;left:0;right:0;top:100%;z-index:59;overflow:hidden;
  background:var(--nv-bg);border-bottom:1px solid var(--nv-line);
  box-shadow:0 18px 40px -24px rgba(0,0,0,.55);
  height:0;opacity:0;visibility:hidden;pointer-events:none;
  transition:height .26s var(--ease,ease),opacity .18s ease,visibility .18s}
.mg-panel.open{opacity:1;visibility:visible;pointer-events:auto;
  border-top:1px solid var(--nv-line)}
/* Inners are stacked in the same box so one can fade out while the next fades
   in. Only the shown one takes pointer events. */
.mg-inner{position:absolute;left:0;right:0;top:0;
  max-width:1180px;margin:0 auto;padding:24px 28px 22px;
  display:flex;gap:34px;align-items:flex-start;
  opacity:0;pointer-events:none;transition:opacity .2s ease}
.mg-inner.on{opacity:1;pointer-events:auto}
.mg-body{flex:1 1 auto;min-width:0}
.mg-all{margin:16px 0 0}
.mg-all a{font-size:13px;font-weight:700;color:var(--nv-accent);text-decoration:none}

/* Grades: the nine tiles, in miniature. */
.mg-grades{display:grid;grid-template-columns:repeat(9,minmax(0,1fr));gap:8px}
.mg-grade{aspect-ratio:1/1;border:1px solid var(--nv-line);border-radius:var(--r,4px);
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;
  text-decoration:none;transition:transform .2s,border-color .2s}
.mg-grade b{font-size:1.15rem;font-weight:800;letter-spacing:-.03em;line-height:1}
.mg-grade span{font-size:.5rem;font-weight:700;letter-spacing:.11em;text-transform:uppercase}
/* 🚨 THE GRADE TILES ARE SITE CHROME, SO THEY ARE THE SITE'S COLOUR. Paul,
   2026-09-09: "above this particular lesson if i go to grades its a different
   color. like the graphite blue color for the grade selector boxes instead of
   being white."
   They were taking `--nv-accent`, which every lesson maps to ITS OWN palette,
   so the grade picker changed colour depending on which lesson you happened to
   be reading. ns.css uses `--a` / `--a-ink` here, which is white on dark and
   graphite on light. LITERAL, exactly like `.drawer .navbtn` below and for the
   same reason: the mega menu belongs to the site, not to the lesson.
   ⚠️ `check-nav-css.js` CANNOT catch this. It fails when a class is styled in
   one stylesheet and not the other, and this class is styled in both - just in
   the wrong colour. Compare the VALUES when touching shared chrome. */
/* 🟠 ORANGE SINCE 2026-09-21, tracking ns.css's --a / --a-ink. These stay
   LITERAL for the reason above, so they do NOT follow ns.css on their own -
   when the site accent moves, move them by hand in the same change. Dark ink
   on both, because this orange cannot carry white text. */
.mg-grade.live{background:#FF9440;color:#2A1206;border-color:#FF9440}
:root[data-theme="light"] .mg-grade.live{background:#E8822F;color:#2A1206;border-color:#E8822F}
.mg-grade.soon{color:var(--nv-dim);opacity:.6}
/* 🚨 THE NAV IS ONE MARKUP AND TWO STYLESHEETS, so the three grade states
   have to exist HERE as well as in ns.css or the dropdown on a lesson page
   disagrees with the dropdown everywhere else. Added 2026-09-21 with Pending
   and Queued; check-nav-css.js caught them missing, which is what it is for.
   Words come from gradeStatus() in nav.js. Only grade 7 gets .live, so only
   grade 7 is filled. */
.mg-grade.queued span{color:var(--nv-fg);opacity:.72}
.mg-grade.pending span{color:var(--nv-dim)}
@media(hover:hover){ .mg-grade.live:hover{transform:translateY(-3px)} }
.mg-note{color:var(--nv-dim);font-size:.82rem;margin:14px 0 0;max-width:62ch}

/* Columns reflow to whatever the section needs, as theirs do. */
.mg-cols{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:22px}
.mg-col h4{margin:0 0 9px;font-size:.68rem;font-weight:800;letter-spacing:.13em;
  text-transform:uppercase;color:var(--nv-dim)}
.mg-col ul{list-style:none;margin:0;padding:0}
.mg-col li{margin-bottom:6px}
.mg-col a{font-size:.92rem;font-weight:600;color:var(--nv-fg);text-decoration:none}
@media(hover:hover){ .mg-col a:hover{text-decoration:underline;text-underline-offset:3px} }
.mg-soon{font-size:.92rem;color:var(--nv-dim)}

/* The promo: caption OVER the picture, so the panel does not grow a text row
   under every image. Square, because the artwork is square. */
.mg-promo{position:relative;display:block;width:230px;flex:none;
  border-radius:var(--r,4px);overflow:hidden;text-decoration:none}
.mg-promo img{display:block;width:100%;aspect-ratio:var(--promo-ratio,3/2);object-fit:cover;object-position:center top}
.mg-promo b{position:absolute;left:0;right:0;bottom:0;padding:22px 12px 10px;
  font-size:.74rem;font-weight:800;letter-spacing:.03em;color:#fff;
  background:linear-gradient(transparent,rgba(0,0,0,.78))}
@media(hover:hover){ .mg-promo:hover img{transform:scale(1.04);transition:transform .4s} }
@media(max-width:1040px){ .mg-promo{display:none} }
@media(max-width:940px){ .mg-panel{display:none} }

/* ── PHONE: a stack of sheets ─────────────────────────────────────────────
   The ROW opens the sheet. The section's own page is a "View …" row inside,
   which is how the destination stays reachable without the label stealing the
   tap. Paul, 2026-08-26.
   -------------------------------------------------------------------------- */
.dl-open{width:100%;text-align:left;background:none;border:0;font:inherit;cursor:pointer;
  display:block;position:relative;padding:14px 26px 14px 0;
  font-size:1.05rem;font-weight:700;letter-spacing:-.02em;color:var(--nv-fg);
  border-bottom:1px solid var(--nv-line)}
.dl-open small{display:block;font-size:.62rem;font-weight:700;letter-spacing:.13em;
  text-transform:uppercase;color:var(--nv-dim);margin-top:4px}
.dl-open i{position:absolute;right:2px;top:50%;transform:translateY(-50%);
  font-style:normal;font-size:22px;line-height:1;color:var(--nv-dim)}
.dl-open.on{color:var(--nv-accent)}

.dsub{position:absolute;inset:0;
  background:var(--nv-bg);border-right:1px solid var(--nv-line);padding:22px;
  transform:translateX(100%);opacity:0;visibility:hidden;pointer-events:none;
  transition:transform .34s cubic-bezier(.22,.61,.36,1),opacity .2s ease,visibility .34s;
  display:flex;flex-direction:column;gap:12px;overflow-y:auto}
/* 🚨 The closed sheet sits at translateX(100%), which is x=320..640 - ON
   SCREEN, not off it, because the sheet is anchored left:0. Sliding in from
   the right therefore REQUIRES hiding it when closed, or it just sits there
   beside the drawer in plain view. Paul caught it immediately. */
.dsub.open{transform:none;opacity:1;visibility:visible;pointer-events:auto}
.dsub-back{align-self:flex-start;background:none;border:0;color:var(--nv-dim);
  font:inherit;font-size:.9rem;cursor:pointer;padding:4px 0}
.dsub-back:hover{color:var(--nv-fg)}
.dsub h3{margin:0;font-size:1.3rem}
.dsub-body{display:flex;flex-direction:column}
.dsub .mg-promo{display:block;width:100%;margin-top:auto}

.dsr{display:block;position:relative;width:100%;text-align:left;
  background:none;border:0;font:inherit;cursor:pointer;text-decoration:none;
  padding:13px 26px 13px 0;font-size:1rem;font-weight:700;letter-spacing:-.02em;
  color:var(--nv-fg);border-bottom:1px solid var(--nv-line)}
.dsr small{display:block;font-size:.6rem;font-weight:700;letter-spacing:.13em;
  text-transform:uppercase;color:var(--nv-dim);margin-top:3px}
.dsr i{position:absolute;right:2px;top:50%;transform:translateY(-50%);
  font-style:normal;font-size:21px;line-height:1;color:var(--nv-dim)}
.dsr.soon{opacity:.45;font-weight:600;cursor:default}
.dsr.view{color:var(--nv-accent)}

/* Measuring pass: all inners briefly laid out so the tallest can be found.
   Invisible to the reader - it never paints, it is only read. */
.mg-panel.measuring{height:auto!important;transition:none!important}
.mg-panel.measuring .mg-inner{position:static!important;opacity:0!important}


/* ══════════════════════════════════════════════════════════════════════
   THE PHONE MENU IS ONE COLUMN
   Paul, 2026-08-27: "it feels like the previous menu is moving away to the
   right and the new menu moves in ... you seem to be layering one over the
   other", and "their side menu covers the entire screen while ours only
   covers 75 or 80%".

   Both were right. Sliding a sheet OVER a stationary drawer reads as a stack.
   The levels have to travel together, and every one of them has to be masked
   by the same column or the motion spills across the page.

   So: .menucol is the menu. It slides in as a whole, clips everything, and is
   FULL WIDTH on a phone. Inside it each level is absolutely positioned:
     level 0  the drawer      at 0, leaves to -100% when a sheet opens
     level n  a .dsub sheet   waits at +100%, sits at 0, leaves to -100%
   Nothing overlaps; each level hands off to the next.
   ══════════════════════════════════════════════════════════════════════ */
.menucol{position:fixed;top:0;left:0;height:100dvh;z-index:80;
  width:min(380px,86vw);overflow:hidden;
  transform:translateX(-100%);visibility:hidden;
  transition:transform .38s cubic-bezier(.22,.61,.36,1),visibility .38s}
body.nav-open .menucol{transform:none;visibility:visible}
/* On a phone it takes the whole screen, as theirs does. */
@media(max-width:560px){ .menucol{width:100vw} }

/* level 0 */
body.sub-open .drawer{transform:translateX(-100%)}

/* levels 1..n */
.menucol .dsub{position:absolute;inset:0;width:100%;
  background:var(--nv-bg);border-right:0;
  transform:translateX(100%);visibility:hidden;pointer-events:none;
  transition:transform .34s cubic-bezier(.22,.61,.36,1),visibility .34s}
.menucol .dsub.open{transform:none;visibility:visible;pointer-events:auto;opacity:1}
.menucol .dsub.exit{transform:translateX(-100%);visibility:visible;pointer-events:none}
.menucol .dsub{border-top:0}
.menucol .dsub h3{border-bottom:1px solid var(--nv-line);padding-bottom:12px}

/* ── EVERY LEVEL FITS ONE SCREEN ──────────────────────────────────────────
   Paul, 2026-08-27: "when you tap the arrow the scroll line swipes left also
   so it doesnt feel seemless ... make all the menu items fit on one screen and
   make it have a size for all kinds of mobile devices."

   A scrollbar inside a sliding panel travels with it and gives the trick away.
   So the content is sized to fit instead: Grades became a tile grid, the promo
   steps aside on short screens, and the spacing scales with the viewport.
   The bar itself is hidden too, so a long list on a very small phone still
   scrolls but never draws a line mid-slide.
   -------------------------------------------------------------------------- */
.drawer,.dsub,.dsub-body{scrollbar-width:none;-ms-overflow-style:none}
.drawer::-webkit-scrollbar,.dsub::-webkit-scrollbar,.dsub-body::-webkit-scrollbar{width:0;height:0;display:none}

/* spacing that shrinks with the screen rather than a fixed 22px */
.menucol .drawer,.menucol .dsub{padding:clamp(12px,3.2vh,22px) clamp(14px,4vw,22px)}
.menucol .dl,.menucol .dl-open{padding-block:clamp(8px,1.5vh,14px)}
.menucol .dsr{padding-block:clamp(8px,1.4vh,13px)}
.menucol .dsub h3{font-size:clamp(1.05rem,4.4vw,1.3rem)}

/* Grades as tiles: three across, so nine years cost three rows not nine. */
.dst-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px;margin-top:4px}
/* 🚨 THE GRADE TILES ARE WHITE ON EVERY PAGE AND IN EVERY THEME, and this file
   had `background:none`, so on a lesson page they showed the dark drawer through
   themselves and read as BLACK. Paul found it on his phone, 2026-09-15: go into a
   lesson, open the side menu, pick a grade, and the K-8 blocks are black.

   ⚠️ THIS IS THE ns.css / lesson-nav.css DRIFT AGAIN, and it is the fifth time.
   Same markup from nav.js, two stylesheets, and only one of them had the fill.
   check-nav-css.js did not catch it because it fails when a class is styled in
   ONE sheet and says nothing when BOTH style it and disagree - which is exactly
   this. The guard is extended in the same change.

   ⚠️ These are NOT palette colours. ns.css sets --tile-bg:#ffffff in BOTH its
   themes on purpose: a grade tile is white on a dark page and on a light one.
   So they are their own three tokens rather than the six --nv-* ones, and a
   template may override them without touching this file. */
.dst{aspect-ratio:1/1;display:flex;align-items:center;justify-content:center;
  background:var(--nv-tile-bg,#ffffff);border:1px solid var(--nv-line);border-radius:var(--r,4px);
  color:var(--nv-tile-fg,#0a0b0d);font:inherit;cursor:pointer;padding:0}
/* 🚨 A GRADE TILE IS AN <a>, SO WITHOUT THIS THE BROWSER UNDERLINES THE NUMBER.
   ns.css has had a.dst{text-decoration:none} all along and this file never did,
   so every grade number in a lesson drawer has been underlined since the tiles
   were built. Nobody saw it because the tile was transparent and the underline
   sat on a dark ground; giving the tile its white fill on 2026-09-15 made it
   obvious, and Paul spotted it within a minute. The bug is older than the fix
   that revealed it. */
a.dst{text-decoration:none}
.dst b{font-size:clamp(1.1rem,5vw,1.5rem);font-weight:800;letter-spacing:-.03em}
.dst:not(.soon):hover{border-color:var(--nv-accent)}
/* ⚠️ --nv-dim is tuned for text on the DARK drawer and disappears on a white
   tile. A "coming soon" grade has to stay readable, so it takes its own. */
.dst.soon{color:var(--nv-tile-dim,#6b7280);opacity:.45;cursor:default}

/* The promo is the first thing to go when the screen is short. */
@media(max-height:760px){ .menucol .mg-promo{display:none} }

/* The logo sits BESIDE the wordmark rather than replacing it: the words are
   what a search engine reads and a screen reader announces, so the mark is
   decorative and carries an empty alt. Paul's Canva export had no alpha - the
   black was baked in - so it was keyed out before use, or it would have been a
   black square in day mode. */
.word{display:inline-flex;align-items:center;gap:9px}
.word img{height:clamp(22px,4.4vw,28px);width:auto;display:block}
@media(max-width:380px){ .word img{height:22px} }

/* ── THE BURGER MUST NOT SHRINK ───────────────────────────────────────────
   Adding the logo widened .word, and the burger had no flex:none, so flex
   squeezed it from 42px to 18px and its three bars collapsed to 1px wide -
   present, visible, and invisible. Paul, 2026-08-27: "i dont see the hamburger
   side menu icon anymore."
   -------------------------------------------------------------------------- */
.burger{flex:none}
.burger i{width:100%;min-width:22px}
.burger i:nth-child(2){width:72%;min-width:16px}
.nv .word{flex:0 1 auto;min-width:0;overflow:hidden;white-space:nowrap}

/* ── ONE ROW HEIGHT ON EVERY LEVEL ────────────────────────────────────────
   Paul, 2026-08-27: "we have lines between our menu options on the side menu
   they are different sizes so you can visually see its not seemless."

   Measured: drawer rows were 70px, sheet rows 47px, and rows with a note were
   66px. Three rhythms. When one level slides out and the next slides in, the
   dividers land in different places and the eye reads it as a different thing
   arriving rather than the same menu continuing.

   So every row - drawer link, drawer button, sheet row - is the same height,
   with the label centred whether or not it carries a note.
   -------------------------------------------------------------------------- */
.menucol{--row-h:clamp(54px,8vh,70px)}
.menucol .dl,
.menucol .dl-open,
.menucol .dsr{
  min-height:var(--row-h);
  display:flex;flex-direction:column;justify-content:center;
  padding-block:0;
  border-bottom:1px solid var(--nv-line)}
.menucol .dl-open,.menucol .dsr{position:relative}
.menucol .dl-open i,.menucol .dsr i{position:absolute;right:2px;top:50%;transform:translateY(-50%)}
.menucol .dsr.view{min-height:var(--row-h)}
/* the tile grid is not a row list, so it keeps its own rhythm */
.menucol .dst-grid{margin-block:10px}

/* ── NO DIVIDER LINES IN THE MENU ─────────────────────────────────────────
   lttstore has none: checked their stylesheet and there is not a single
   border declaration on a menu row, only gap spacing. Lines were also what
   made the hand-off look wrong - a row of horizontal rules sliding sideways
   draws the eye to the movement, and any mismatch in rhythm becomes obvious.
   Whitespace does the same job and survives the slide.
   -------------------------------------------------------------------------- */
.menucol .dl,
.menucol .dl-open,
.menucol .dsr,
.menucol .drawer a,
.menucol .dsub h3{border-bottom:0}
.menucol .drawer,
.menucol .dsub-body{gap:2px}
/* the label block keeps its own breathing room now that no rule separates them */
.menucol .dl,
.menucol .dl-open,
.menucol .dsr{padding-inline:10px;border-radius:var(--r,4px)}
.menucol .dl-open i,.menucol .dsr i{right:10px}
@media(hover:hover){
  .menucol .dl:hover,
  .menucol .dl-open:hover,
  .menucol .dsr:not(.soon):hover{background:var(--nv-line)}
}

/* ── NO TAP FLASH ─────────────────────────────────────────────────────────
   Paul, 2026-08-27: "when i click i see a blue box that moves where i pressed
   ... i dont see that when i tap on their nav."

   That box is the browser's own tap highlight, drawn on any tappable element
   unless a page turns it off. Killing it does NOT remove the focus ring: the
   :focus-visible rule below keeps a real outline for keyboard users, which is
   the part that actually matters for getting around without a mouse.
   -------------------------------------------------------------------------- */
/* On the root, not on .menucol - see the note in ns.css. Lesson pages load this
   file INSTEAD of ns.css, so the declaration has to exist in both. */
html{-webkit-tap-highlight-color:transparent}
.menucol a:focus,.menucol button:focus{outline:none}
.menucol a:focus-visible,.menucol button:focus-visible{
  outline:2px solid var(--nv-accent);outline-offset:-2px;border-radius:var(--r,4px)}

/* ── THE SHEET SETTLES IN ─────────────────────────────────────────────────
   Paul: "their sub menu nav page section have a animation where they fade in
   from the top. the titles themselves i believe."

   The panel slides, then its contents drop the last few pixels into place, a
   beat behind and staggered. That second, smaller movement is what stops the
   panel reading as one flat rectangle sliding by.
   -------------------------------------------------------------------------- */
@keyframes ns-row-in{
  from{opacity:0;transform:translateY(-9px)}
  to{opacity:1;transform:none}
}
.menucol .dsub.open .dsub-back,
.menucol .dsub.open h3,
.menucol .dsub.open .dsub-body > *,
.menucol .dsub.open .dst-grid,
.menucol .dsub.open .mg-promo{
  animation:ns-row-in .3s both cubic-bezier(.22,.61,.36,1)}
.menucol .dsub.open .dsub-back{animation-delay:.06s}
.menucol .dsub.open h3{animation-delay:.10s}
.menucol .dsub.open .dsub-body > *:nth-child(1){animation-delay:.14s}
.menucol .dsub.open .dsub-body > *:nth-child(2){animation-delay:.18s}
.menucol .dsub.open .dsub-body > *:nth-child(3){animation-delay:.21s}
.menucol .dsub.open .dsub-body > *:nth-child(4){animation-delay:.24s}
.menucol .dsub.open .dsub-body > *:nth-child(5){animation-delay:.27s}
.menucol .dsub.open .dsub-body > *:nth-child(6){animation-delay:.29s}
.menucol .dsub.open .dsub-body > *:nth-child(n+7){animation-delay:.31s}
.menucol .dsub.open .mg-promo{animation-delay:.34s}
/* A level that has been pushed aside must not re-run its entrance. */
.menucol .dsub.exit .dsub-back,
.menucol .dsub.exit h3,
.menucol .dsub.exit .dsub-body > *,
.menucol .dsub.exit .dst-grid,
.menucol .dsub.exit .mg-promo{animation:none}

@media(prefers-reduced-motion:reduce){
  .menucol .dsub.open .dsub-back,
  .menucol .dsub.open h3,
  .menucol .dsub.open .dsub-body > *,
  .menucol .dsub.open .dst-grid,
  .menucol .dsub.open .mg-promo{animation:none}
}

/* ══════════════════════════════════════════════════════════════════════
   THE TOP BANNER
   Paul, 2026-08-27: "the nex Student title at the top is cut off. i think we
   are crowding the top banner espically since we need a login and cart icon
   there later ... keep the logo itself centered and the same size as ltt."

   The clipping was mine: fixing the squashed burger I put overflow:hidden on
   .word, which chopped the name. Gone.

   Measured on lttstore: logo 40x40, header icons 22-24px. Matched.

   On a PHONE the bar is three zones and nothing else - burger, logo, icons.
   Pick a Grade and the day/night toggle both live in the drawer already, so
   repeating them up here was the crowding.
   ══════════════════════════════════════════════════════════════════════ */
.nv .word{overflow:visible;white-space:nowrap}
.nv .word img{height:40px;width:40px;flex:none}

/* 🚨 THE ICONS SIT AT THE FAR RIGHT. Paul, 2026-09-04: "the login and cart icon
   are not on the far right".
   .nv is a flex row and every item packed left, so account and cart ended 277px
   short of the nav's own right edge while the page content below them ran the
   full width - which reads as the whole header being shoved left.
   ⚠️ margin-left:auto had only ever been on .nv .btn, and Support Us is not
   rendered in the top nav, so nothing was pushing anything. On the phone the
   grid takes over and justify-self:end does this job instead. */
.navicons{display:flex;align-items:center;gap:2px;flex:none;margin-left:auto}
.navicon{background:none;border:0;padding:8px;cursor:pointer;line-height:0;
  color:var(--nv-dim);border-radius:var(--pill,999px);
  display:inline-grid;place-items:center;transition:color .18s,background .18s}
.navicon:hover{color:var(--nv-fg)}
/* Reserved, not working: accounts and the cart are still to be built, so they
   read as present-but-inactive rather than pretending to work. */
.navicon[aria-disabled="true"]{opacity:.45;cursor:default}
.navicon[aria-disabled="true"]:hover{color:var(--nv-dim);background:none}

@media(max-width:940px){
  /* three zones, logo dead centre */
  .nv{display:grid;grid-template-columns:1fr auto 1fr;gap:8px;padding:10px 14px}
  .nv .burger{justify-self:start;margin-left:0}
  .nv .word{justify-self:center;gap:0}
  .nv .wordtext{display:none}
  .navicons{justify-self:end}
  /* both of these are in the drawer, so they were pure crowding up here */
  /* .modetog was dropped 2026-09-02; only .btn is left up here. */
  .nv .btn{display:none}
}
@media(max-width:380px){
  .nv .word img{height:34px;width:34px}
  .navicon{padding:6px}
}

.ns-nav{border-bottom:1px solid var(--nv-line)}

/* ── THE DAY / NIGHT SWITCH ────────────────────────────────────────────────
   🚨 THIS WAS MISSING ENTIRELY AND IT IS WHY THE CONTROL LOOKED "RETIRED".
   Paul, 2026-09-03, from his phone: "not only do we have the login, the cart,
   the day mode /night mode icon (retired version) and the logo out of order".
   It was never a retired control. nav.js emits the SAME `.mswitch` markup on
   every page, but the rules for it live in ns.css - which a lesson page does
   not load. So on a lesson the track and the knob had no size, no colour and
   no position, and the button fell back to the browser default: a white slab
   with the words "Night Mode" in it. Identical HTML, no CSS, and it read as an
   older design that had been left behind.
   ⚠️ This is the trap already written down in CLAUDE.md - ns.css class names
   resolve to NOTHING inside a lesson page - and the switch walked straight
   into it the day it was added. Anything nav.js grows from here needs a rule
   in BOTH stylesheets or it ships unstyled on every lesson.
   ⚠️ Mapped onto --nv-* only. There is no --panel, --line or --a here; those
   are ns.css names and would resolve to nothing, which is the same bug again. */
.mswitch{margin-right:auto;background:none;border:0;padding:0;cursor:pointer;
  display:inline-flex;align-items:center;gap:9px;
  font:inherit;font-weight:700;color:var(--nv-fg);-webkit-tap-highlight-color:transparent}
.mswitch-track{position:relative;width:40px;height:13px;border-radius:999px;
  background:var(--nv-line);border:1px solid var(--nv-line);flex:none}
/* 🚨 ONLY THE KNOB'S POSITION ANIMATES, never its colour. Both ends invert
   between themes, so a cross-fade passes through a mid-grey where the knob and
   its track match and the knob vanishes. Same reasoning as in ns.css - if that
   rule is ever changed, change it here too. */
.mswitch-knob{position:absolute;top:50%;left:2px;width:9px;height:9px;border-radius:999px;
  background:var(--nv-accent);transform:translateY(-50%);
  transition:transform .34s cubic-bezier(.4,.05,.2,1)}
@media(prefers-reduced-motion:reduce){ .mswitch-knob{transition:none} }
:root[data-theme="light"] .mswitch-knob{transform:translateY(-50%) translateX(26px)}
@media(hover:hover){ .mswitch:hover .mswitch-track{background:var(--nv-dim)} }
.mswitch:focus-visible{outline:2px solid var(--nv-accent);outline-offset:3px;border-radius:4px}
.mswitch-drawer{width:100%;margin:16px 0 0;padding:14px 2px 0;
  border-top:1px solid var(--nv-line);border-radius:0;
  justify-content:space-between;flex-direction:row-reverse}

/* ── THE NAV AND DRAWER USE THE SITE UI FONT, NOT THE PAGE'S ───────────────
   🚨 Paul, 2026-09-03: "this is not the same side menu page as the one on the
   home page ... and the text on the links". The markup IS the same - nav.js
   emits it once for every page. The FONT was not.
   `.drawer a` inherits, and a lesson page sets body to "Source Serif 4", the
   READING face, while an ns.css page sets Archivo. So the identical drawer came
   out serif on a lesson and sans everywhere else, which is exactly what he saw.
   It is declared here rather than inherited, so the nav is the same object on
   every page regardless of what the page around it is set in.
   ⚠️ Same @font-face as ns.css, same file, so it is already cached for anyone
   who has opened any other page. Keep the two declarations identical. */
@font-face{font-family:'Archivo';src:url('/assets/fonts/archivo-var.woff2') format('woff2-variations');
  font-weight:400 900;font-display:swap}
.ns-nav, .ns-nav *, .drawer, .drawer *{
  font-family:'Archivo',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif}

/* ── THE HEADER LAYOUT FIX, WHICH ONLY EVER EXISTED IN ns.css ──────────────
   🚨 THIS IS WHY THE HEADER WAS STILL WRONG AFTER IT WAS "FIXED". The .navleft
   wrapper went into nav.js on 2026-09-02 to keep the burger and the home icon in
   ONE grid cell - without it .nv is a three column grid handed four items, so the
   logo, account and cart drop onto a second row. Paul at the time: "well you moved
   all the logo cart and login", and again on 2026-09-03 from his phone.
   The MARKUP reached lesson pages on 2026-09-03. The RULE never did, because it
   lives in ns.css and a lesson page does not load ns.css. Identical HTML, no CSS,
   header still broken - and it looked like the fix had been reverted.
   ⚠️ Any future nav element needs a rule in BOTH stylesheets. check-nav-css.js
   fails the build when one of them is missing. */
.navleft{display:flex;align-items:center;gap:6px;flex:0 0 auto}
.homeb{display:inline-flex;align-items:center;justify-content:center;
  width:36px;height:36px;border-radius:4px;color:var(--nv-dim);
  text-decoration:none;flex:0 0 auto}
.homeb:hover{color:var(--nv-fg)}
@media(max-width:520px){ .homeb{width:32px;height:32px} }

/* ── SUPPORT US, MATCHING THE ONE ON EVERY OTHER PAGE ──────────────────────
   🚨 Paul: "the support looks off". It was a PILL here and a 4px rounded
   rectangle everywhere else, with different padding and a smaller label - because
   ns.css styles `.btn` and this file styles `.navbtn`, and the two were written
   months apart. navMarkup() takes the class precisely so the two shells can theme
   it, but the SHAPE should not have been part of that.
   Matched to ns.css `.btn`: 16px/32px padding, 15px label, 4px radius. Colours
   still come from --nv-* so a lesson keeps its own palette. */
.drawer .navbtn{margin-top:auto;margin-left:0;display:flex;align-items:center;
  justify-content:center;text-align:center;border-bottom:0;line-height:1.1;
  background:var(--nv-accent);color:var(--nv-on);
  border-radius:4px;padding:16px 32px;font-size:15px;font-weight:700}
@media(hover:hover){ .drawer .navbtn:hover{filter:brightness(1.08)} }

/* ── SUPPORT US IS SITE CHROME, SO IT IS THE SITE'S COLOUR ─────────────────
   🚨 Paul, 2026-09-03: "even the support button is a different color". It was
   painted with --nv-accent, which every lesson template maps from its OWN
   palette - so the button came out verdigris on one lesson, periwinkle on
   another, and white on the home page. Three colours for one button.
   The drawer is site chrome, not lesson content. It should look the same
   wherever it is opened, which is the whole of what he asked for with "you cant
   use the same side menu or what?".
   ⚠️ So these two are LITERAL, matching ns.css --a / --a-ink exactly rather
   than pointing at --nv-accent. A lesson's palette must not reach this button.
   Keep them in step with ns.css if that ever changes: white on dark, #27313f on
   light, and the ink inverts with it.
   ⚠️ Everything ELSE in the drawer still uses --nv-*. The knob and the active
   link stay per lesson on purpose; it is only the solid button that has to
   match, because a filled block of colour is what the eye compares. */
/* 🟠 The drawer's own button, orange since 2026-09-21. Same literal-tracking
   rule as .mg-grade.live above: ns.css moved --a to orange, so this moves too
   or the side menu is a different colour on a lesson page. */
.drawer .navbtn{background:#FF9440;color:#2A1206}
:root[data-theme="light"] .drawer .navbtn{background:#E8822F;color:#2A1206}

/* ── THE CART COUNT BADGE ──────────────────────────────────────────────────
   Lives in the nav BAR, so it takes the lesson's palette like the rest of the
   bar does. That is the opposite of the drawer rules below, and deliberate:
   the bar is part of the lesson, the drawer is site chrome. */
.navicon{position:relative}
.cartn{position:absolute;top:2px;right:2px;min-width:17px;height:17px;padding:0 4px;
  border-radius:999px;background:var(--nv-accent);color:var(--nv-on);
  font-size:.66rem;font-weight:800;line-height:17px;text-align:center;
  pointer-events:none}

/* ── THE CART DRAWER ───────────────────────────────────────────────────────
   🚨 THE TWIN OF THE BLOCK IN ns.css. Lesson pages do not load ns.css, so a
   rule added there reaches the home page and silently does nothing here --
   the drift machine documented in CLAUDE.md. check-nav-css.js fails the build
   if these two ever fall out of step on a class the nav emits.
   ⚠️ Change one, change the other, same session. */
.cscrim{position:fixed;inset:0;background:rgba(0,0,0,.6);z-index:80;opacity:0;
  visibility:hidden;transition:opacity .3s ease,visibility .3s}
body.cart-open .cscrim{opacity:1;visibility:visible}
/* 🚨 680px, matching ns.css. The buttons were never oversized - the panel was
   too narrow for them. See the ns.css twin for the measurement. */
.cdrawer{position:fixed;top:0;right:0;bottom:0;width:min(680px,100%);z-index:81;
  background:var(--nv-bg);border-left:1px solid var(--nv-line);
  display:flex;flex-direction:column;
  transform:translateX(100%);transition:transform .34s cubic-bezier(.22,.61,.36,1)}
body.cart-open .cdrawer{transform:none}
.cd-top{display:flex;align-items:center;justify-content:space-between;gap:12px;
  padding:18px 20px;border-bottom:1px solid var(--nv-line);flex:none}
.cd-h{margin:0;font-size:1.15rem;letter-spacing:-.02em;color:var(--nv-fg);
  display:flex;align-items:center;gap:9px}
/* 🚨 A LESSON TEMPLATE STYLES EVERY BARE h2 (Cinzel, 13px, uppercase), and
   both drawer titles are h2s, so on a lesson page they came out "ACCOUNT" and
   "CART" in serif capitals. Found 2026-09-11. Same rule in ns.css. */
.cd-h,.ad-h{font-family:"Archivo",system-ui,sans-serif;text-transform:none;padding:0;border:0}
.ad-h{color:var(--ad-fg)}
/* The header count. --nv-* like the rest of the drawer: CLAUDE.md records that
   only the solid BUTTON is literal, because a filled block of colour is what
   the eye compares against the page. A 21px pill is not that. */
.cd-n{min-width:21px;height:21px;padding:0 6px;border-radius:999px;background:var(--nv-accent);
  color:var(--nv-on);font-size:.72rem;font-weight:800;line-height:21px;text-align:center}
.cd-x{background:none;border:0;color:var(--nv-dim);font-size:26px;line-height:1;
  cursor:pointer;padding:2px 6px}
.cd-x:hover{color:var(--nv-fg)}
.cd-body{flex:1;overflow-y:auto;padding:6px 20px}
.cd-empty{color:var(--nv-dim);font-size:.92rem;padding:22px 0}

/* 👤 THE ACCOUNT PANEL, 2026-09-10. 🚨 TWIN OF ns.css: everything below the
   token line is IDENTICAL there; only the four --ad-* tokens differ. */
.adrawer{--ad-bg:var(--nv-bg);--ad-fg:var(--nv-fg);--ad-dim:var(--nv-dim);--ad-line:var(--nv-line);
  font-family:"Archivo",system-ui,sans-serif}
.ascrim{position:fixed;inset:0;background:rgba(0,0,0,.6);z-index:80;opacity:0;
  visibility:hidden;transition:opacity .3s ease,visibility .3s}
body.acct-open .ascrim{opacity:1;visibility:visible}
.adrawer{position:fixed;top:0;right:0;bottom:0;width:min(420px,100%);z-index:81;
  background:var(--ad-bg);color:var(--ad-fg);border-left:1px solid var(--ad-line);
  display:flex;flex-direction:column;
  transform:translateX(100%);transition:transform .34s cubic-bezier(.22,.61,.36,1)}
body.acct-open .adrawer{transform:none}
.adrawer .hidden,.adrawer [hidden]{display:none!important}
.ad-top{display:grid;grid-template-columns:1fr auto 1fr;align-items:center;gap:8px;
  padding:16px 18px;border-bottom:1px solid var(--ad-line);flex:none}
.ad-h{grid-column:2;margin:0;font-size:1rem;font-weight:600;letter-spacing:0;text-align:center;max-width:none}
.ad-back,.ad-x{background:none;border:0;padding:4px 0;color:var(--ad-fg);cursor:pointer;
  font:inherit;font-size:.7rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}
.ad-back{grid-column:1;justify-self:start;display:flex;align-items:center;gap:4px}
.ad-x{grid-column:3;justify-self:end}
.ad-back:hover,.ad-x:hover{text-decoration:underline}
.ad-body{flex:1;overflow-y:auto;padding:4px 18px 24px}
.ad-empty,.ad-msg{color:var(--ad-dim);font-size:.9rem;line-height:1.5;padding:18px 0 0;margin:0}
.ad-hi{text-align:center;padding:26px 0 22px}
.ad-hi h3{margin:0;font-size:1.45rem;font-weight:600;letter-spacing:-.01em}
.ad-out{background:none;border:0;color:var(--ad-dim);font:inherit;font-size:.78rem;cursor:pointer;
  padding:4px 0;margin-top:2px}
.ad-out:hover{color:var(--ad-fg);text-decoration:underline}
.ad-row{display:flex;flex-direction:column;align-items:center;gap:4px;width:100%;padding:20px 0;
  background:none;border:0;border-bottom:1px solid var(--ad-line);color:var(--ad-fg);
  font:inherit;cursor:pointer;text-align:center}
.ad-row b{font-size:1.02rem;font-weight:600}
.ad-row span{font-size:.8rem;color:var(--ad-dim)}
.ad-row:hover b{text-decoration:underline}
.ad-pro{display:flex;justify-content:center;flex-wrap:wrap;gap:18px;padding:0 0 22px;
  border-bottom:1px solid var(--ad-line)}
.ad-pro-i{display:flex;flex-direction:column;align-items:center;gap:7px;width:72px;background:none;
  border:0;padding:0;color:var(--ad-fg);font:inherit}
.ad-pro-i i{width:56px;height:56px;border-radius:8px;display:grid;place-items:center;font-style:normal;
  font-size:1.5rem;font-weight:700;background:#c62828;color:#fff}
.ad-pro-i b{font-size:.78rem;font-weight:600;max-width:72px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ad-pro-i.is-add{cursor:pointer}
/* ➕ THE PLUS IS AN SVG (AD_PLUS in nav.js, 2026-09-11). Twin of ns.css; see there. */
.ad-pro-i.is-add i,.ad-kidrow.is-add>i{background:none;color:var(--ad-dim);border:1.5px dashed var(--ad-line)}
.ad-pro-i.is-add svg,.ad-kidrow.is-add>i svg{width:42%;height:42%;display:block;fill:none;stroke:currentColor;
  stroke-width:1.6;stroke-linecap:round}
.ad-pro-i.is-add b{color:var(--ad-dim)}
.ad-pro-i.is-add:hover i{border-color:var(--ad-fg);color:var(--ad-fg)}
/* The strip sits a little BELOW "Hi, name". Paul, 2026-09-11: "the profile
   icons needs to come down a bit from the Hi, Kolten or Hi Paul." It was
   pulled 8px UP; now 6px down, a 28px gap. */
.ad-hi+.ad-pro{margin-top:6px}
.ad-signout{display:block;width:100%;margin-top:26px;padding:12px 0;background:none;border:0;
  color:var(--ad-dim);font:inherit;font-size:.86rem;cursor:pointer;text-align:center}
.ad-signout:hover{color:var(--ad-fg);text-decoration:underline}
.ad-kv .mswitch{margin:0}
.ad-go{width:100%;background:none;border:0;border-bottom:1px solid var(--ad-line);color:var(--ad-fg);
  font:inherit;font-size:.88rem;cursor:pointer;text-align:left}
.ad-go>span:last-child{display:flex;align-items:center;gap:8px;min-width:0}
.ad-go i{font-style:normal;font-size:1.2rem;line-height:1;color:var(--ad-dim);transition:transform .2s}
.ad-go.is-open i{transform:rotate(90deg)}
.ad-go:hover>span:first-child{text-decoration:underline}
.ad-ok{font-style:normal;font-size:.64rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;
  color:#2e7d4f;border:1px solid currentColor;border-radius:999px;padding:2px 7px}
.ad-note{margin:8px 0 0;font-size:.8rem;line-height:1.45;color:var(--ad-dim)}
.ad-ord{display:flex;align-items:center;gap:12px;width:100%;padding:18px 0;background:none;border:0;
  border-bottom:1px solid var(--ad-line);color:var(--ad-fg);font:inherit;cursor:pointer;text-align:left}
.ad-ord-t{flex:1;display:flex;flex-direction:column;gap:3px;min-width:0}
.ad-ord-t b{font-size:.92rem;font-weight:600}
.ad-ord-t>span:not(.ad-ths){font-size:.78rem;color:var(--ad-dim)}
.ad-ths{display:flex;flex-wrap:wrap;gap:8px;margin-top:8px}
.ad-ths img,.ad-ths i{width:42px;height:42px;border-radius:4px;object-fit:cover;display:block;
  background:var(--ad-line)}
.ad-chev{color:var(--ad-dim);font-size:1.4rem;line-height:1;margin-right:10px}
.ad-ord:hover b{text-decoration:underline}
/* Section titles in the panel match the Terms page headings: bright, 800,
   capitals, .06em. Paul, 2026-09-11: "the title for these text need to be
   more brighter similar to the copyright page." They were dim 10.5px. 13px,
   not the page's 14px, to sit in a 420px panel. "Optional" stays dim. */
.ad-cap{margin:24px 0 6px;font-size:.8rem;font-weight:800;letter-spacing:.06em;text-transform:uppercase;
  color:var(--ad-fg)}
.ad-cap .ad-opt{color:var(--ad-dim)}
/* ❓ The "?" info (adCap in nav.js), after MyWika's .info-icon/.info-tooltip:
   18px round ?, faint at rest, the box fades in over .35s, right-aligned
   under the ?, 220px. ⚠️ Font set LONGHAND: "inherit" is not a legal family
   inside the font shorthand and would drop the whole rule (CLAUDE.md). */
.ad-caprow{display:flex;align-items:center;gap:10px;margin:24px 0 6px}
.ad-caprow .ad-cap{margin:0 auto 0 0}
/* 10px in from the panel edge: at the edge it was cut off on a phone.
   Paul, 2026-09-11: "move it slightly to the left so it doesnt get cut off
   on mobile devices." The box is anchored to it, so it moves in too. */
.ad-infow{position:relative;flex:none;display:flex;margin-right:10px}
.ad-info{width:18px;height:18px;border-radius:50%;border:0;padding:0;cursor:pointer;display:grid;place-items:center;
  background:rgba(127,127,127,.22);color:var(--ad-fg);font-family:inherit;font-size:.72rem;font-weight:700;
  line-height:1;opacity:.55;transition:opacity .2s}
.ad-info:hover,.ad-info[aria-expanded="true"]{opacity:.95}
.ad-tip{position:absolute;right:0;top:calc(100% + 8px);width:220px;z-index:5;visibility:hidden;opacity:0;
  transition:opacity .35s,visibility .35s;padding:10px 12px;border:1px solid var(--ad-line);border-radius:8px;
  background:var(--ad-bg);color:var(--ad-fg);font-size:.8rem;line-height:1.4;box-shadow:0 4px 14px rgba(0,0,0,.35)}
.ad-tip.is-on{visibility:visible;opacity:1}
@media (prefers-reduced-motion:reduce){.ad-tip{transition:none}}
/* ⚠️ 10px in on the right, like the "?" buttons (2026-09-11). Twin of ns.css. */
.ad-kv{display:flex;justify-content:space-between;align-items:center;gap:14px;padding:12px 10px 12px 0;
  border-bottom:1px solid var(--ad-line);font-size:.88rem}
.ad-kv>span:last-child{color:var(--ad-dim);text-align:right}
.ad-tot>span{color:var(--ad-fg)!important;font-weight:700}
.ad-dim{color:var(--ad-dim)}
.ad-item{padding:14px 0 0;border-bottom:1px solid var(--ad-line)}
.ad-item-t{display:flex;justify-content:space-between;align-items:baseline;gap:12px}
.ad-item-t b{font-size:.95rem;font-weight:600}
.ad-item-t span{font-size:.82rem;color:var(--ad-dim);flex:none}
.ad-q{display:block;font-size:.78rem;color:var(--ad-dim);margin-top:3px}
.ad-dl{display:flex;justify-content:space-between;align-items:center;margin:10px 0 0 24px;
  padding:12px 0;border-top:1px solid var(--ad-line);color:var(--ad-fg);text-decoration:none;font-size:.86rem}
.ad-dl span{color:var(--ad-dim);font-size:1.2rem;line-height:1}
.ad-dl:hover{text-decoration:underline}
.ad-in{flex:1;min-width:0;background:none;border:0;color:var(--ad-fg);font:inherit;font-size:.88rem;
  text-align:left;padding:2px 0;margin-left:24px}
.ad-in:focus{outline:none;border-bottom:1px solid var(--ad-fg)}
.ad-box{display:block;width:100%;margin:10px 0 0;padding:10px 0;border-bottom:1px solid var(--ad-line)}
/* 👁️ SHOW / HIDE PASSWORD (2026-09-11). The wrap takes the box's outer margin
   so the eye can sit centred inside the box, over its right end. Twin of ns.css. */
.pw-wrap{position:relative;display:block}
.pw-wrap>input{margin:0;padding-right:40px}
.pw-wrap.is-auth{margin:0 0 6px}
.pw-wrap.is-box{margin:10px 0 0}
.pw-wrap.is-kv{flex:1;min-width:0;margin-left:24px}
.pw-wrap.is-kv>input{display:block;width:100%}
.pw-eye{position:absolute;top:0;right:0;bottom:0;width:38px;display:flex;align-items:center;justify-content:center;
  background:none;border:0;padding:0;cursor:pointer;color:inherit;opacity:.6}
.pw-eye:hover,.pw-eye.is-on{opacity:1}
/* In an .ad-kv row the wrap is the row's last span, which is dimmed. */
.ad-kv .pw-eye{color:var(--ad-fg)}
.pw-eye svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
/* 🚨 CLOSED EYE = HIDDEN, OPEN EYE = SHOWING. Paul, 2026-09-12: "hidden is the
   eye closed not open and open shows the password." This was inverted since the
   eye shipped: the default state (password masked) drew the OPEN eye, so the
   icon described the state you would get by clicking rather than the state you
   were in. The icon names the CURRENT state, like every other toggle. */
.pw-eye .eye-on,.pw-eye.is-on .eye-off{display:none}
.pw-eye.is-on .eye-on{display:block}
.ad-link{display:block;margin:20px auto 0;background:none;border:0;color:var(--ad-fg);cursor:pointer;
  font:inherit;font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}
.ad-link:hover{text-decoration:underline}
.ad-msg{text-align:center}
/* 🧒 STUDENT PROFILES + PIN, 2026-09-11 (ROADMAP 50). Still the twin rule:
   this block is IDENTICAL in ns.css and lesson-nav.css. The six av-* colours all carry
   white text at 6:1 or better; the account holder keeps the red above, and no
   theme is red. .whop (the picker) and .nv-av (the nav icon) sit OUTSIDE the
   panel, so they use literal colours, not --ad-* tokens.
   ⚠️ A profile box's colour is an INLINE style from AD_THEMES (nav.js), which
   reads the eight lesson palettes. There are no per-colour classes here. */
.ad-pro-i{cursor:pointer}
.ad-opt{font-style:normal;font-weight:500;letter-spacing:.04em;text-transform:none;margin-left:6px}
.ad-bday>span:last-child{display:flex;align-items:center;gap:4px;color:var(--ad-dim)}
.ad-bday .ad-in{flex:none;width:2.6em;margin:0;text-align:center;border-bottom:1px solid var(--ad-line)}
.ad-bday .ad-yyyy{width:3.6em}
.ad-chips.is-center{justify-content:center}
/* The account holder's DEFAULT box is red wherever a box is drawn. Picking
   Default once left the big box and the Manage Profiles row see-through,
   because only the strip and the picker had a fallback. An inline theme
   colour still wins over this. */
.ad-edit-av i,.ad-kidrow>i,.nv-av{background:#c62828}
/* 📈 Progress views (2026-09-11): three stat boxes, a course row that opens its
   lesson list, the student's "next up" links, the Access Type pill. */
.ad-stats{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px;margin:16px 0 4px}
.ad-stats>div{border:1px solid var(--ad-line);border-radius:8px;padding:10px 6px;text-align:center;display:flex;flex-direction:column;gap:3px}
.ad-stats b{font-size:1.3rem;font-weight:800}
.ad-stats span{font-size:.7rem;color:var(--ad-dim)}
.ad-crs.is-open .ad-chev{transform:rotate(90deg)}
.ad-crs .ad-chev{transition:transform .2s}
.ad-crsbody{padding:2px 0 8px 10px;border-bottom:1px solid var(--ad-line)}
/* A unit inside a course: name, "2 of 5 lessons · 40%", a thin bar. */
.ad-unit{display:flex;align-items:center;gap:10px;width:100%;padding:12px 0;background:none;border:0;
  border-bottom:1px solid var(--ad-line);color:var(--ad-fg);font:inherit;cursor:pointer;text-align:left}
.ad-unit-t{flex:1;display:flex;flex-direction:column;gap:4px;min-width:0}
.ad-unit-t b{font-size:.86rem;font-weight:600}
.ad-unit-t>span{font-size:.74rem;color:var(--ad-dim)}
.ad-unit .ad-chev{transition:transform .2s}
.ad-unit.is-open .ad-chev{transform:rotate(90deg)}
.ad-bar{display:block;height:4px;border-radius:2px;background:var(--ad-line);overflow:hidden}
.ad-bar>i{display:block;height:100%;background:var(--ad-fg)}
.ad-unitbody{padding:0 0 6px 10px;border-bottom:1px solid var(--ad-line)}
.ad-lsn{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:2px 10px;align-items:center;padding:9px 0;
  border-bottom:1px solid var(--ad-line);font-size:.84rem}
.ad-lsn:last-child{border-bottom:0}
.ad-lsn>span:first-child{min-width:0;overflow-wrap:anywhere}
.ad-lsn .ad-reset1{grid-column:1/-1;justify-self:start;margin:2px 0 0;font-size:.68rem}
.ad-reset1.is-armed{color:var(--ad-red)}
.ad-nextup{display:flex;flex-direction:column;gap:2px;padding:11px 0;border-bottom:1px solid var(--ad-line);
  color:var(--ad-fg);text-decoration:none;font-size:.86rem}
.ad-nextup span{font-size:.78rem;color:var(--ad-dim)}
a.ad-nextup:hover b{text-decoration:underline}
.ad-move{margin-top:20px}
.ad-soon{font-style:normal;font-size:.64rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;
  border:1px solid var(--ad-line);border-radius:999px;padding:3px 8px;color:var(--ad-dim)}
/* My Profile (2026-09-11): header, About Me list, named color tiles. */
.ad-me{display:flex;align-items:center;gap:16px;padding:22px 0 18px;border-bottom:1px solid var(--ad-line)}
.ad-me>i{width:64px;height:64px;border-radius:10px;display:grid;place-items:center;flex:none;
  font-style:normal;font-size:1.8rem;font-weight:700;color:#fff;background:#c62828}
.ad-me>div{display:flex;flex-direction:column;gap:3px;min-width:0}
.ad-me b{font-size:1.15rem;font-weight:700}
.ad-me span{font-size:.82rem;color:var(--ad-dim)}
.ad-about{margin:6px 0 0;display:grid;grid-template-columns:minmax(0,1fr);gap:0}
.ad-about dt{font-size:.74rem;color:var(--ad-dim);padding-top:10px}
.ad-about dd{margin:2px 0 0;padding-bottom:10px;border-bottom:1px solid var(--ad-line);font-size:.9rem;
  overflow-wrap:anywhere;white-space:pre-line}
.ad-ta{display:block;width:100%;margin:8px 0 0;padding:10px;border:1px solid var(--ad-line);border-radius:6px;
  resize:vertical;font:inherit;font-size:.9rem;line-height:1.45}
.ad-ta:focus{outline:none;border-color:var(--ad-fg)}
.ad-tiles{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px;padding:10px 0 4px}
.ad-tiles button{display:flex;flex-direction:column;align-items:stretch;gap:0;padding:0;overflow:hidden;cursor:pointer;
  border:1px solid var(--ad-line);border-radius:8px;background:none;color:var(--ad-fg);font:inherit;position:relative}
.ad-tiles i{display:block;height:34px}
.ad-tiles b{font-size:.74rem;font-weight:600;padding:6px 4px;text-align:center}
.ad-tiles button[aria-pressed="true"]{border-color:var(--ad-fg);box-shadow:0 0 0 1px var(--ad-fg)}
.ad-tiles button[aria-pressed="true"]::after{content:"\2713";position:absolute;top:4px;right:6px;
  color:#fff;font-size:.8rem;font-weight:700}
.ad-row[disabled]{opacity:.45;cursor:default}
.ad-row[disabled]:hover b{text-decoration:none}
.ad-pro-i i{transition:box-shadow .15s}
.ad-pro-i.is-me i{box-shadow:0 0 0 2px var(--ad-bg),0 0 0 4px var(--ad-fg)}
.ad-pro-i:not(.is-me):not(.is-add):hover i{box-shadow:0 0 0 2px var(--ad-bg),0 0 0 4px var(--ad-dim)}
.ad-lock{display:inline-block;vertical-align:-1px;margin-right:3px}
/* The strip's padlock: a small badge on the box's top-right corner, so the
   name under the box stays centred on it. */
.ad-pro-i{position:relative}
.ad-lockb{position:absolute;top:-6px;left:50%;margin-left:19px;width:20px;height:20px;border-radius:50%;
  display:grid;place-items:center;background:var(--ad-bg);border:1px solid var(--ad-line);color:var(--ad-fg)}
.ad-lockb .ad-lock{margin:0;vertical-align:0}
.ad-mid{text-align:center}
.ad-kidrow>i{width:42px;height:42px;border-radius:6px;display:grid;place-items:center;flex:none;
  font-style:normal;font-weight:700;font-size:1.1rem;color:#fff}
/* .ad-kidrow.is-add>i: drawn with the tile's plus, above. */
.ad-edit-av{padding:22px 0 6px;display:flex;justify-content:center}
.ad-edit-av i{width:84px;height:84px;border-radius:10px;display:grid;place-items:center;font-style:normal;
  font-size:2.3rem;font-weight:700;color:#fff}
.ad-chips{display:flex;flex-wrap:wrap;gap:6px;padding:10px 0 4px}
.ad-chips button{min-width:34px;height:36px;padding:0 6px;border-radius:6px;background:none;cursor:pointer;
  border:1px solid var(--ad-line);color:var(--ad-fg);font:inherit;font-size:.86rem;font-weight:600}
.ad-chips button[aria-pressed="true"]{background:var(--ad-fg);color:var(--ad-bg);border-color:var(--ad-fg)}
.ad-sw{display:flex;flex-wrap:wrap;gap:12px;padding:10px 0 4px}
.ad-sw button{width:36px;height:36px;border-radius:8px;border:0;cursor:pointer;padding:0}
.ad-sw button[aria-pressed="true"]{box-shadow:0 0 0 2px var(--ad-bg),0 0 0 4px var(--ad-fg)}
/* --ad-red: 7.0:1 on the dark panel, 6.5:1 on the light one. */
.adrawer{--ad-red:#ff7b72}
:root[data-theme="light"] .adrawer{--ad-red:#b3261e}
.ad-del{color:var(--ad-red)}
.ad-warn{margin:18px 0 0;padding:14px 16px;border:1px solid var(--ad-red);border-radius:8px;font-size:.88rem;line-height:1.5}
.ad-warn b{display:block;color:var(--ad-red);margin-bottom:6px}
.ad-warn p{margin:0 0 6px}.ad-warn p:last-child{margin:0;color:var(--ad-dim)}
.ad-danger{display:block;width:100%;margin-top:18px;padding:13px 0;border:0;border-radius:4px;cursor:pointer;
  background:#b3261e;color:#fff;font:inherit;font-size:.9rem;font-weight:700}
.ad-danger:hover{background:#961f18}.ad-danger[disabled]{opacity:.6;cursor:default}
/* 🎨 THE PROFILE'S THEME COLOR IS AN ACCENT, NOT A REPAINT. Paul, 2026-09-11,
   seeing a whole lesson turn teal: "the theme needs to be a variable of that
   theme color ... certain elements instead of the entire color of the site."
   He picked BUTTONS and NAV BAR ACCENTS. modeBoot() puts html.has-me and
   --me (that theme's light accent, white text at 6:1 or better) on the page
   before it paints; nothing changes for the account holder or a visitor.
   ⚠️ Ghost buttons keep their own look: they are the second choice. The
   lesson colour picker is a separate, per-device choice and is untouched. */
/* ⚠️ NOT THE LESSON PLAYER. Paul, 2026-09-11, of the play button: "doesnt
   need to be a different color it looks strange." The player belongs to the
   lesson's own palette, so anything inside .player is left alone. */
html.has-me .btn:not(.ghost):not(.player *),html.has-me .navbtn{background:var(--me);color:#fff}
html.has-me .drawer .navbtn,html.has-me .cdrawer .navbtn{background:var(--me);color:#fff}
html.has-me nav{box-shadow:inset 0 3px 0 var(--me)}
html.has-me .mg-top::after{background:var(--me)}
.ad-pinbox{position:relative;display:flex;justify-content:center;gap:12px;margin:22px auto 0;width:max-content}
.ad-pinbox span{width:48px;height:56px;border:1.5px solid var(--ad-line);border-radius:8px;display:grid;
  place-items:center;transition:border-color .15s}
.ad-pinbox span.is-on::after{content:"";width:12px;height:12px;border-radius:50%;background:var(--ad-fg)}
.ad-pinbox input:focus~span.is-cur,.ad-pinbox:focus-within span.is-cur{border-color:var(--ad-fg)}
.ad-pinbox input{position:absolute;inset:0;width:100%;height:100%;opacity:0;border:0;padding:0;
  font-size:16px;cursor:pointer}
.ad-pinbox.is-shake{animation:adShake .32s}
@keyframes adShake{20%,60%{transform:translateX(-6px)}40%,80%{transform:translateX(6px)}}
@media (prefers-reduced-motion:reduce){.ad-pinbox.is-shake{animation:none}}
/* 22px like the icons beside it, and lifted 2px. Its box centre already
   matched the cart's, but the cart's ink sits high (basket over wheels, the
   count badge on top), so a centred box read as low. Paul, 2026-09-11: "it
   seems it need to go up a bit to be centered." */
.nv-av{display:grid;place-items:center;width:22px;height:22px;border-radius:5px;color:#fff;
  font:700 .76rem/1 "Archivo",system-ui,sans-serif;transform:translateY(-2px)}
/* 🚨 THE PRE-PAINT COVER. modeBoot puts html.whop-pre on in <head>, before the
   browser has drawn anything, so the sign-in cover is the FIRST thing painted
   rather than something that lands on top of a page already on screen.
   Paul: "its better but its still kind of there" - mounting it from the nav
   script was still after first paint.
   ⚠️ ::before on <html>, because <body> may not exist yet when this applies.
   ⚠️ The real .whop sits at z-index 120 and covers this, so there is no flash
   BETWEEN the two - the ground never changes colour.
   ⚠️ #0a0b0d is .whop background, literally. Change one, change the other. */
html.whop-pre::before{content:"";position:fixed;inset:0;z-index:119;background:#0a0b0d}
/* Mounted straight into a cover, so it must not fade the page back in. */
html.whop-pre .whop{opacity:1;transition:none}
.whop{position:fixed;inset:0;z-index:120;background:#0a0b0d;color:#f1f3f5;display:grid;place-items:center;
  padding:24px 16px;overflow-y:auto;opacity:1;font-family:"Archivo",system-ui,sans-serif}
/* 🚨 NO FADE. It used to mount at opacity:0 and ease in over 250ms, which
   is 250ms of the site showing THROUGH the cover - Paul, 2026-09-16:
   "there is still a flicker". A sign-in screen is not a modal over the
   page, it is the first thing you see, so it is opaque from the start.
   .is-in is kept as a no-op: the script still adds it. */
.whop.is-in{opacity:1}
/* 🚪 SIGN OUT on the picker, bottom right, phone and desktop alike.
   ⚠️ Inside .whop, which is display:grid/place-items:center, so it is taken
   OUT of the grid with position:absolute - a normal child would be centred
   with the tiles. Same reason .whop-brand is absolute.
   ⚠️ The safe-area inset matters: on a phone this sits exactly where the home
   indicator lives, and without it the button is half under the gesture bar. */
.whop-out{
  position:fixed;z-index:121;
  right:calc(16px + env(safe-area-inset-right, 0px));
  bottom:calc(16px + env(safe-area-inset-bottom, 0px));
  display:inline-flex;align-items:center;gap:8px;
  font:inherit;font-size:13px;font-weight:600;letter-spacing:.01em;cursor:pointer;
  color:#c9ced6;background:transparent;
  border:1px solid rgba(255,255,255,.22);border-radius:999px;padding:9px 15px 9px 13px;
}
.whop-out svg{width:17px;height:17px;flex:0 0 auto}
.whop-out:hover{color:#fff;border-color:rgba(255,255,255,.5)}
.whop-out:focus-visible{outline:none;box-shadow:0 0 0 3px rgba(255,255,255,.35)}
.whop-out[disabled]{opacity:.6;cursor:default}
@media (max-width:520px){ .whop-out{font-size:12.5px;padding:8px 13px 8px 11px} }
/* 🚨 THE GROUND NEVER ANIMATES. THE CONTENTS DO.
   The first version faded the whole .whop in from transparent, which is the
   flicker Paul kept seeing - 250ms of the site showing through the cover.
   Then it was opaque and hard-cut, and he asked for it "a bit more smoth".
   Both are satisfied by animating what is ON the cover rather than the cover:
   the dark ground is there from the first frame, and the wordmark, question,
   tiles and Sign Out ease up into it.
   ⚠️ Never put a transition back on .whop itself. That is the flicker. */
/* 🚨 IT DROPS IN, AND IT REUSES ns-row-in. Paul, 2026-09-16: "if the elements
   can drop in similar to the side window text does and the grades do ... I
   mentioned drop down because I think that would be the best effect."
   That motion already exists on this site - the drawer rows and the grade
   tiles use ns-row-in, which he asked for on 2026-09-02. Reusing it means the
   sign-in screen moves like the rest of the site instead of inventing a second
   feel. The black ground never animates, so this reads as a fade up out of it.
   ⚠️ ANIMATIONS, NOT transitions keyed off .is-in. A transition needs the
   script to add a class; if that ever failed the contents would sit at
   opacity 0 and the screen would be blank and dark. fill:both starts itself,
   so the worst case is no motion rather than no screen.
   🚨 DO NOT ALSO ANIMATE .whop-in. It is the PARENT of the heading, the tiles
   and Manage Profiles - nesting two ns-row-in transforms compounds them, the
   children start 18px up instead of 9 and drift as the parent settles. Same
   trap as .dst-grid, written up above. Animate the pieces, never the box. */
/* 🚨 THE CASCADE WAITS FOR THE CARDS, and it is ordered by POSITION.
   Paul, 2026-09-16: "all the elements need to drop in from top to bottom ...
   First the Nexstudents text, then Who is learning?, then the profile cards,
   then the manage profiles, then the admin ... and the sign out together. It
   will look like it is cascading down."
   The cards come from the network, so a fixed stagger cannot put them third -
   they would land whenever the fetch did, after Manage Profiles had already
   dropped. So nothing moves until the tiles are in, and nsWhopCascade() then
   assigns the delays in order of each element s actual top edge. Position, not
   a hardcoded list, so a phone that wraps the row still cascades correctly.
   ⚠️ Everything starts at opacity 0 and .is-ready releases it. That IS a
   dependency on the script, which is normally the thing to avoid - the cover
   here is already script-driven end to end and it times out after 8s and
   uncovers the site, so the failure mode is bounded. */
.whop-brand,.whop-in > h2,.whop-manage,.whop-out,.whop-row .whop-i{opacity:0}
.whop.is-ready .whop-brand,
.whop.is-ready .whop-in > h2,
.whop.is-ready .whop-manage,
.whop.is-ready .whop-out,
.whop.is-ready .whop-row .whop-i{animation:ns-row-in .3s both cubic-bezier(.22,.61,.36,1)}
/* The tiles arrive after the profile fetch, so they animate themselves rather
   than sitting inside a parent whose animation already finished. Same cascade
   the grade tiles have. */

@media (prefers-reduced-motion:reduce){
  .whop-brand,.whop-in > h2,.whop-manage,.whop-out,.whop-row .whop-i{animation:none;opacity:1}
}
.whop-in{text-align:center;max-width:760px}
/* 🎬 The wordmark, top left, brand red - the Netflix placement Paul sent.
   🟠 Orange since 2026-09-21, stroke moved to #2A1206. TWINNED with ns.css
   byte for byte - check-nav-css fails the build if they drift.
   ⚠️ .whop is display:grid/place-items:center, so a normal child would be
   centred with everything else. Absolute takes it out of that flow.
   ⚠️ TWINNED: byte-identical in ns.css and lesson-nav.css, or check-nav-css
   fails the build. */
/* 🅰️ ANTON, loaded ONLY for the picker wordmark. It is a heavy condensed
   display face - the same category as the Monalyon Paul found - and it is
   deliberately not used anywhere else on the site. 18.6 KB, OFL, self-hosted
   beside Archivo with its licence in OFL-Anton.txt.
   ⚠️ font-display:swap so the wordmark draws in Archivo and reflows to Anton
   rather than leaving a hole. It is decoration, never a blocking load. */
@font-face{font-family:'Anton';src:url('/assets/fonts/anton-latin.woff2') format('woff2');
  font-weight:400;font-style:normal;font-display:swap}
/* 🚨 THE OUTLINE IS DARK RED (#7a0f0f), NOT BLACK AND NOT WHITE. Paul picked
   it off a five-way comparison on 2026-09-12. A black outline is invisible on
   this page - the picker background IS #0a0b0d, so the first attempt drew an
   outline nobody could see. White read as a poster. Dark red gives the letters
   an edge and a little depth while staying one colour family.
   🚨 paint-order:stroke fill IS THE WHOLE TRICK. A plain -webkit-text-stroke
   paints the stroke ON TOP of the fill, eating into the letter from the inside
   and making a heavy face look heavier and muddier. paint-order puts the stroke
   BEHIND the fill, so the outline grows outward and the red stays full width.
   ⚠️ Anton is a single weight (400 is its only cut) - do NOT ask for 900 here.
   A browser that cannot find 900 synthesises a fake bold, which is exactly the
   smeared look Paul was reacting to. */
.whop-brand{position:absolute;top:clamp(22px,3.4vw,46px);left:clamp(16px,2.8vw,46px);
  font-family:'Anton',Archivo,system-ui,sans-serif;font-weight:400;
  font-size:clamp(34px,4.9vw,60px);letter-spacing:.045em;
  text-transform:uppercase;color:#FF9440;line-height:1;
  -webkit-text-stroke:5px #2A1206;paint-order:stroke fill;
  pointer-events:none;user-select:none}
/* One weight across the whole word. The nav's wordmark splits Nex from a bolder
   Students, but the reference logotype has no such split - it is one solid
   run of caps - so the b carries no extra weight here. The markup keeps the
   tag so the picker and the nav can share one string. */
.whop-brand b{font-weight:inherit}
/* 📱 PHONE: CENTRED AT THE TOP, not tucked in the corner - the Netflix mobile
   layout Paul sent. Sized a little larger than theirs on purpose (his call),
   which NEXSTUDENTS can carry because Anton is condensed: eleven letters in the
   space their seven take.
   ⚠️ left:0;right:0 with text-align:center is what centres an absolutely
   positioned element. Setting left:50% and translating would work too but
   breaks the moment the word changes length. */
@media(max-width:640px){
  .whop-brand{left:0;right:0;top:clamp(28px,7vw,40px);text-align:center;
    font-size:clamp(30px,8.4vw,42px);-webkit-text-stroke-width:4px}
}
.whop h2{margin:0 0 34px;font-size:clamp(1.8rem,5vw,2.8rem);font-weight:600;letter-spacing:-.01em}
.whop-row{display:flex;flex-wrap:wrap;justify-content:center;gap:clamp(16px,3vw,30px)}
.whop-i{display:flex;flex-direction:column;align-items:center;gap:12px;width:clamp(96px,16vw,140px);
  background:none;border:0;padding:0;color:#98a1ab;font:inherit;cursor:pointer}
/* 🎬 THE PICKER TILE, to the Netflix/HBO shape Paul named. At rest it is a
   real card - a soft edge, a top sheen and a drop shadow - because a flat
   colour square with a letter on it reads as a placeholder. The selected feel
   comes on hover and focus: the edge goes white and the tile lifts.
   ⚠️ overflow:hidden and the img rule are here for the CUSTOM AVATARS Paul is
   making. An image drops in and is clipped to the same radius with no restyle;
   the letter stays the fallback for a profile that has no picture yet.
   ⚠️ TWINNED. This block is byte-identical in ns.css and lesson-nav.css, and
   check-nav-css fails the build if a .whop class is styled in one only. */
.whop-i i{width:100%;aspect-ratio:1;max-width:100%;border-radius:12px;display:grid;place-items:center;
  overflow:hidden;font-style:normal;font-weight:700;font-size:clamp(2.4rem,6vw,3.6rem);
  color:#fff;background:#c62828;border:2px solid rgba(241,243,245,.24);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.18), 0 6px 18px rgba(0,0,0,.45);
  transition:transform .18s cubic-bezier(.2,.7,.3,1), border-color .18s, box-shadow .18s}
.whop-i i img{width:100%;height:100%;object-fit:cover;display:block}
.whop-i b{font-weight:500;font-size:1rem;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.whop-i:hover b,.whop-i:focus-visible b{color:#f1f3f5}
.whop-i:hover i,.whop-i:focus-visible i{border-color:#f1f3f5;transform:scale(1.06) translateY(-3px);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.26), 0 14px 30px rgba(0,0,0,.55)}
/* ⚠️ A lift is decoration. Someone who asked their machine for less motion
   still needs to see which tile is selected, so the edge still goes white. */
@media(prefers-reduced-motion: reduce){
  .whop-i i{transition:border-color .18s}
  .whop-i:hover i,.whop-i:focus-visible i{transform:none}
}
.whop-i:focus-visible{outline:none}
.whop-manage{margin-top:44px;background:none;border:1px solid #98a1ab;color:#98a1ab;font:inherit;
  font-size:.8rem;font-weight:700;letter-spacing:.14em;text-transform:uppercase;padding:10px 22px;cursor:pointer}
.whop-manage:hover,.whop-manage:focus-visible{color:#f1f3f5;border-color:#f1f3f5}
.cd-row{display:flex;align-items:center;gap:13px;padding:15px 0;
  border-bottom:1px solid var(--nv-line)}
/* Quantity box stacked over Remove, the twin of the ns.css rule. */
.cd-qty{flex:none;display:flex;flex-direction:column;align-items:center;gap:6px}
/* 🚨 SQUARE - the twin of the ns.css rule. min-width plus padding let the text
   set the height and gave a lozenge; explicit equal width and height instead. */
.cd-qbox{width:40px;height:40px;display:grid;place-items:center;
  border:1px solid var(--nv-line);border-radius:7px;
  background:var(--nv-bg);color:var(--nv-fg);font-size:.85rem;font-weight:700}
.cd-th{width:56px;height:56px;flex:none;border-radius:9px;object-fit:contain;
  background:#fff;border:1px solid var(--nv-line)}
.cd-noth{background:var(--nv-line)}
.cd-info{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px;color:var(--nv-fg)}
.cd-info b{font-size:.9rem;line-height:1.3;letter-spacing:-.01em}
.cd-price{font-size:.85rem;color:var(--nv-dim);font-weight:700}
.cd-rm{flex:none;background:none;border:0;color:var(--nv-dim);cursor:pointer;
  font:inherit;font-size:.78rem;text-decoration:underline;padding:4px}
.cd-rm:hover{color:var(--nv-fg)}
/* 🚨 56px A SIDE is what makes the buttons 280 wide in a 680 panel. Twin of the
   ns.css rule - the button rule was right, the container was too narrow. */
.cd-foot{flex:none;padding:16px 56px 20px;border-top:1px solid var(--nv-line)}
.cd-tot{display:flex;justify-content:space-between;align-items:baseline;color:var(--nv-fg);
  font-weight:800;font-size:1.05rem;margin-bottom:14px}
.cd-acts{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:8px}
/* 🚨 THE TWIN OF THE .cd-acts .btn RULE IN ns.css. Keep the numbers identical
   or the same drawer is two different sizes depending which page opened it.
   ⚠️ 1fr auto 1fr so the padlock cannot push "Check Out" off centre. */
/* ⚠️ min-height, not a padding sum - one value to turn. Twin of the ns.css
   rule; keep the number identical or the drawer is two heights depending on
   which page opened it. */
.cd-acts .navbtn{display:grid;grid-template-columns:1fr auto 1fr;align-items:center;
  text-align:center;min-height:56px;padding:8px 14px;font-size:1.05rem;font-weight:700;
  letter-spacing:-.01em}
/* 🚨 display:block ON THE ICON - the twin of the ns.css rule. An inline SVG
   sits on the text baseline and hangs low against a centred label whatever
   align-items says. As a block it has no baseline and the grid centres it. */
/* 2px up, 6px closer - twin of ns.css, see the note there (2026-09-10). */
.cd-acts .navbtn svg{display:block;width:17px;height:17px;justify-self:end;
  align-self:center;margin-right:2px;transform:translateY(-2px)}
/* Both labels are wrapped in a span, so both land in the centre column. A bare
   text node becomes an anonymous item in column 1 and sits left of centre. */
.cd-acts .navbtn span{grid-column:2}
/* 🚨 LITERAL, like .drawer .navbtn above and for the same reason: a filled
   block of colour is what the eye compares, and site chrome must not take a
   lesson's palette. Keep these two pairs identical. */
/* 🚨 BOTH SOLID, SAME COLOUR. A .cd-alt ghost rule for View Cart lived here and
   was deleted, not disabled: the reference had two identical buttons and the
   split was invented. Dead CSS that once ran is how a reverted design creeps
   back. Keep this in step with the ns.css twin. */
.cdrawer .navbtn{background:#ffffff;color:#0a0b0d}
:root[data-theme="light"] .cdrawer .navbtn{background:#27313f;color:#ffffff}

/* 🚨 SIDE BY SIDE ON EVERY PHONE SIZE - twin of the ns.css rule. They stacked
   under 420px, which gave a phone a different layout from a desktop for no
   reason a reader can see. The GRID never collapses; the PADDING gives way. */
@media(max-width:560px){
  .cdrawer{border-left:0}
  .cd-body{padding-left:14px;padding-right:14px}
  .cd-foot{padding-left:14px;padding-right:14px}
  .cd-acts{gap:7px}
  .cd-acts .navbtn{padding-inline:6px;font-size:.9rem}
  .cd-acts .navbtn svg{margin-right:1px}
}
