/* =========================================================================
   public/plusScroll.css
   Stage + faithful replica styles for the AI portal scroll showcase.

   TWO LAYERS, deliberately separated:

   1. THE STAGE  (.act, .act-pin, .lx-frame, .stage-ground)
      The film around the product. Its ground travels chalk -> ink -> chalk,
      so it carries its OWN inverted token set (--stage-*). Nothing in here
      exists in the real app.

   2. THE REPLICA  (everything scoped under .lx-frame)
      Values copied verbatim from public/aiPortal.css, public/topicProgress.css
      and public/studentInsights.css so the screens are the real screens. The
      frame is ALWAYS light, which is what lets the replica keep the portal's
      own tokens unchanged (--muted #5d6b66 is unreadable on ink).

      One fix vs the real stylesheet, an unscoped-collision bug there:
        .lx-chip    is scoped to .lx-chat-chips       (in the app it also hits
                    the cover's topic chips)

      The .lx-reveal collision this sheet also worked around (the button's pill
      styling landing on every <section class="lx-sec lx-reveal">) is now fixed
      at source in public/aiPortal.css, scoped to .lx-review-actions.
   ========================================================================= */

@import url("https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,400..800&family=Hanken+Grotesk:wght@400;500;600;700&display=swap");

/* ============================ 1. STAGE ================================== */

:root {
  --chalk: #f1f3f2;
  --ink-deep: #101815;

  /* chrome on chalk */
  --stage-ink: #17221f;
  --stage-muted: #5d6b66;
  --stage-accent: #0f6e6a;
  --stage-line: #d4dbd8;

  --font-display: "Bricolage Grotesque", system-ui, sans-serif;
  --font-body: "Hanken Grotesk", system-ui, sans-serif;

  --ease: cubic-bezier(.22, 1, .36, 1);
  --shell: 1240px;
  --pad: clamp(20px, 5vw, 64px);
}

/* On ink the chrome inverts. Contrast against #101815:
   #f4f7f6 ~ 17.4:1 · #a8b8b3 ~ 8.7:1 · #3aa39e ~ 5.9:1 — all pass AA body. */
.on-ink {
  --stage-ink: #f4f7f6;
  --stage-muted: #a8b8b3;
  --stage-accent: #3aa39e;
  --stage-line: #2b3733;
}

* , *::before, *::after { box-sizing: border-box; }

html { scroll-behavior: auto; }

body {
  margin: 0;
  font-family: var(--font-body);
  line-height: 1.6;
  color: var(--stage-ink);
  background: var(--chalk);
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  transition: color .6s var(--ease);
}

/* The travelling ground. One flat colour, tweened by a scrubbed trigger.
   Not a gradient: it should read as a lighting cue, not as decoration. */
.stage-ground {
  position: fixed;
  inset: 0;
  z-index: -1;
  background: var(--chalk);
  will-change: background-color;
}

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 600;
  line-height: 1.06;
  letter-spacing: -.02em;
  margin: 0;
  text-wrap: balance;
}

.shell {
  width: 100%;
  max-width: var(--shell);
  margin: 0 auto;
  padding: 0 var(--pad);
}

/* ---- flat sections (hero, cta), chalk ---- */

.flat { padding: clamp(72px, 14vh, 150px) 0; }

.hero-eyebrow {
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--stage-accent);
  margin: 0 0 22px;
}
.hero h1 {
  font-size: clamp(2.6rem, 7.4vw, 5.2rem);
  font-weight: 700;
  max-width: 15ch;
}
.hero-lead {
  margin: 26px 0 0;
  font-size: clamp(1.05rem, 1.9vw, 1.3rem);
  color: var(--stage-muted);
  max-width: 46ch;
  text-wrap: pretty;
}
.hero-rule {
  width: 64px;
  height: 3px;
  background: var(--stage-accent);
  border-radius: 2px;
  margin: 34px 0 0;
  transform-origin: left center;
}
.hero-cue {
  margin: 54px 0 0;
  font-size: .82rem;
  letter-spacing: .06em;
  color: var(--stage-muted);
  display: flex;
  align-items: center;
  gap: 10px;
}
.hero-cue span { display: inline-block; animation: nudge 2.2s var(--ease) infinite; }
@keyframes nudge { 0%,100% { transform: translateY(0); } 50% { transform: translateY(4px); } }

/* ---- the idle scroll nudge ----
   A pinned page has no natural "there is more" signal: the act holds still
   while the reader scrolls THROUGH it, so a reader who pauses to take a frame
   in cannot tell a slow section from the end of the page.

   So the cue is IDLE-DRIVEN, not permanent: it stays out of the way until the
   reader has genuinely stopped, then shakes, then leaves the moment they move.
   A permanent one would be nagging somebody who is reading exactly as intended.
   Both the hero cue and the floating one share the shake. */
@keyframes idle-shake {
  0%, 100%       { transform: translateY(0); }
  10%, 30%, 50%  { transform: translateY(5px); }
  20%, 40%, 60%  { transform: translateY(0); }
}
.is-nudging { animation: idle-shake 2.4s var(--ease) infinite; }
/* While the whole cue is shaking, the arrow's own gentle loop would beat
   against it, so it stands down for the duration. */
.hero-cue.is-nudging span { animation: none; }

/* ---- back to the homepage ----
   Fixed, so it is reachable from any of the seven acts rather than only from
   the hero. Deliberately quiet: it is an exit, not a call to action, and the
   page's job is to keep you scrolling. It uses the same --stage-* tokens as
   the rest of the chrome, so it inverts with the travelling ground instead of
   disappearing when the page goes to ink. */
.stage-back {
  position: fixed;
  top: clamp(14px, 3vh, 26px);
  left: clamp(14px, 3vw, 30px);
  z-index: 45;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 9px 15px;
  min-height: 44px;
  border: 1px solid var(--stage-line);
  border-radius: 999px;
  background: var(--chalk);
  color: var(--stage-muted);
  font-family: var(--font-body);
  font-size: .78rem;
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  text-decoration: none;
  transition: color .2s var(--ease), border-color .2s var(--ease),
    background-color .6s var(--ease),
    transform .28s var(--ease), opacity .28s var(--ease);
}
.on-ink .stage-back { background: var(--ink-deep); }
.stage-back:hover {
  color: var(--stage-ink);
  border-color: var(--stage-ink);
}
/* The arrow is the affordance, so it travels. 2px, per the motion rule.
   Scoped to the arrow: the label is a sibling span now, and an unscoped
   selector slid the word along with it. */
.stage-back-arrow { transition: transform .2s var(--ease); }
.stage-back:hover .stage-back-arrow { transform: translateX(-2px); }
.stage-back-label { margin-left: 8px; }

.scroll-nudge {
  appearance: none;
  cursor: pointer;
  position: fixed;
  left: 50%;
  bottom: clamp(18px, 4vh, 38px);
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 9px 16px;
  border: 1px solid var(--stage-line);
  border-radius: 999px;
  background: var(--chalk);
  color: var(--stage-muted);
  font-family: var(--font-body);
  font-size: .78rem;
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  /* Centred by the translate, so the shake has to carry it too: a bare
     translateY on .is-nudging would snap it back to the left edge. */
  transform: translateX(-50%);
  opacity: 0;
  visibility: hidden;
  transition: opacity .45s var(--ease), visibility .45s var(--ease);
}
.on-ink .scroll-nudge { background: var(--ink-deep); }
.scroll-nudge.is-visible { opacity: 1; visibility: visible; }
/* The keyframes above move the element itself, so the centring translate is
   folded into every step rather than sitting on the base rule. */
.scroll-nudge.is-nudging { animation-name: idle-shake-centred; }
@keyframes idle-shake-centred {
  0%, 100%       { transform: translateX(-50%) translateY(0); }
  10%, 30%, 50%  { transform: translateX(-50%) translateY(5px); }
  20%, 40%, 60%  { transform: translateX(-50%) translateY(0); }
}

/* ---- the acts ---- */

/* Every height on this page is svh, never dvh.
   dvh tracks the DYNAMIC viewport, so it grows and shrinks as a phone's
   address bar hides and shows. On a pinned page that resizes the pin itself
   mid-scroll: ScrollTrigger's cached start/end go stale, the spacer no longer
   matches, and the next act's heading paints inside the previous act's frame.
   svh is the address-bar-visible height and never changes while scrolling. */
.act { position: relative; }
/* A hand-over must never show two acts at once. Each act paints on its own
   layer in document order, so a later one cannot bleed into an earlier frame. */
.act > .act-pin { position: relative; z-index: 1; }

.act-pin {
  min-height: 100svh;   /* svh, never dvh: see the note above .act */
  display: grid;
  grid-template-columns: minmax(250px, 330px) minmax(0, 1fr);
  align-items: center;
  gap: clamp(28px, 5vw, 72px);
  width: 100%;
  max-width: var(--shell);
  margin: 0 auto;
  padding: clamp(56px, 9vh, 104px) var(--pad);
}

/* The caption column. One line of pitch beside the product, swapped per beat. */
.act-copy { min-width: 0; position: relative; }

.act-num {
  font-family: var(--font-display);
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--stage-accent);
  display: block;
  margin-bottom: 16px;
}
.act-copy h2 {
  font-size: clamp(1.7rem, 3vw, 2.5rem);
  font-weight: 700;
  max-width: 14ch;
}

/* Beats stack in the same grid cell so swapping one for the next never
   reflows the column. The FIRST beat is the layout's height source. */
.beats { display: grid; margin-top: 22px; }
.beat {
  grid-area: 1 / 1;
  margin: 0;
  font-size: clamp(.98rem, 1.5vw, 1.1rem);
  color: var(--stage-muted);
  max-width: 34ch;
  text-wrap: pretty;
  transition: color .6s var(--ease);
}
.beat b { color: var(--stage-ink); font-weight: 600; }

.act-stage { min-width: 0; display: flex; justify-content: center; }

/* ---- the product frame: always light, whatever the ground is doing ---- */

.lx-frame {
  position: relative;
  width: 100%;
  max-width: 880px;
  height: min(78svh, 760px);
  border-radius: 18px;
  background: #f1f3f2;
  border: 1px solid var(--stage-line);
  overflow: hidden;
  isolation: isolate;
  box-shadow: 0 2px 6px rgba(16, 24, 21, .05);
  transition: box-shadow .8s var(--ease), border-color .8s var(--ease);
}
.on-ink .lx-frame {
  border-color: rgba(255, 255, 255, .09);
  box-shadow: 0 44px 90px -28px rgba(0, 0, 0, .72),
              0 0 0 1px rgba(255, 255, 255, .04);
}

/* The frame is a column: a fixed bar, then the scrolling body under it.
   .frame-scroll used to be inset:0, which put it BEHIND the top bar and hid
   the cover's eyebrow under it. flex:1 makes it start where the bar ends. */
.lx-frame { display: flex; flex-direction: column; }
.frame-scroll { position: relative; flex: 1 1 auto; min-height: 0; overflow: hidden; }
/* Bottom padding so a panned document never ends flush with the frame edge,
   which reads as a sentence sliced in half rather than as the end of a page. */
.frame-track { will-change: transform; padding-bottom: 28px; }
.lx-paper-bar, .lx-runner-head { flex: 0 0 auto; }

.frame-pad { padding: 30px clamp(20px, 4vw, 44px) 40px; }

/* A single act can hold several screens stacked on top of each other and
   crossfade between them (cover -> runner -> review). */
.screen { position: absolute; inset: 0; overflow: hidden; }
.screen[data-screen] { background: #f1f3f2; }

/* ========================= 2. THE REPLICA ===============================
   Values below are copied from the real portal. Where a number looks odd,
   it is odd in public/aiPortal.css too, and matching it is the point.
   ======================================================================== */

.lx-frame {
  /* the .lx-paper token block, verbatim */
  --bg: #f1f3f2;
  --surface: #fff;
  --ink: #17221f;
  --muted: #5d6b66;
  --accent: #0f6e6a;
  --accent-dark: #0b5350;
  --accent-soft: #e7efed;
  --accent-ink: #fff;
  --line: #d4dbd8;
  --req: #b8443c;
  --warn: #8a6508;
  --warn-line: #c8a13a;
  --radius: 10px;

  /* aliases so the studentInsights.css block below can be copied unchanged */
  --border: #d4dbd8;
  --surface-2: #e7efed;
  --text-dark: #17221f;
  --text-light: #5d6b66;
  --primary-color: #0f6e6a;

  color: var(--ink);
  font-family: var(--font-body);
  line-height: 1.55;
}

.lx-frame :focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.lx-frame button { font: inherit; }
.lx-frame .sheet { max-width: 900px; margin: 0 auto; padding: 0 24px; }
.lx-frame .sheet > .col { min-width: 0; }

/* ---- top bar ---- */
.lx-paper-bar {
  position: relative; z-index: 40;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 12px clamp(20px, 4vw, 44px);
  background: rgba(241, 243, 242, .92);
  backdrop-filter: blur(6px);
  border-bottom: 1px solid var(--line);
}
.lx-back {
  font-size: .88rem; font-weight: 600; color: var(--ink);
  background: none; border: none; cursor: pointer; padding: 8px 0;
}
.lx-paper-id {
  font-size: .7rem; font-weight: 700; letter-spacing: .14em;
  text-transform: uppercase; color: var(--muted);
}

/* ---- cover ---- */
.lx-paper-cover { padding: 44px 0 40px; }
.cover-eyebrow {
  font-size: .7rem; font-weight: 700; letter-spacing: .2em;
  text-transform: uppercase; color: var(--accent);
}
.cover-title {
  font-family: var(--font-display);
  font-size: clamp(1.7rem, 3.6vw, 2.9rem);
  font-weight: 700; line-height: 1.04; letter-spacing: -.02em; margin: 14px 0 0;
}
.cover-meta {
  margin-top: 16px; font-size: .95rem; color: var(--muted);
  display: flex; flex-wrap: wrap; gap: 6px 18px;
}
.cover-rule {
  width: 64px; height: 2px; background: var(--accent);
  margin: 26px 0 18px; transform-origin: left center;
}
.cover-topics { display: flex; flex-wrap: wrap; gap: 8px; }
/* The cover chip. In the app this is overwritten by the chat-suggestion
   .lx-chip declared 570 lines later; here the chat chip is scoped instead. */
.cover-topics .lx-chip {
  font-size: .78rem; padding: 5px 12px;
  border: 1px solid var(--line); border-radius: 999px;
  background: var(--surface); color: var(--muted);
}

/* ---- launch strip (lives inside the cover, by design) ---- */
.lx-launch { border-top: 1px solid var(--line); margin-top: 30px; padding: 26px 0 0; }
.lx-launch-title { font-family: var(--font-display); font-size: 1.2rem; margin: 0 0 20px; }
.lx-launch-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0; }
.lx-set { padding: 0 28px; border-left: 1px solid var(--line); min-width: 0; }
.lx-set:first-child { border-left: none; padding-left: 0; }
.lx-set h4 { font-family: var(--font-display); font-size: 1.02rem; margin: 0 0 6px; }
.lx-set-count { font-size: .82rem; color: var(--muted); }
.lx-set-status { font-size: .82rem; margin: 12px 0 16px; color: var(--ink); }
.lx-set-btn {
  font-weight: 600; font-size: .92rem; padding: 12px 24px; min-height: 44px;
  border-radius: var(--radius); border: 1px solid var(--accent);
  background: var(--accent); color: var(--accent-ink); cursor: pointer;
}
.lx-set-review { background: none; color: var(--ink); border-color: var(--line); margin-left: 10px; }
.cover-scroll {
  margin-top: 28px; font-size: .8rem; color: var(--muted);
  display: flex; align-items: center; gap: 8px;
}
.cover-scroll span { display: inline-block; animation: nudge 2.2s var(--ease) infinite; }

/* ---- rail ---- */
.lx-rail {
  position: sticky; top: 0; z-index: 30;
  background: rgba(241, 243, 242, .94); backdrop-filter: blur(6px);
  border-bottom: 1px solid var(--line);
}
.lx-rail-current {
  display: flex; align-items: center; gap: 12px;
  width: 100%; max-width: 900px; margin: 0 auto;
  padding: 12px 24px; background: none; border: none;
  cursor: pointer; color: var(--ink); text-align: left;
}
.lx-rail-label {
  font-family: var(--font-display); font-weight: 700; font-size: .9rem;
  display: inline-flex; gap: 8px; min-width: 0;
}
.lx-rail-num { color: var(--accent); }
.lx-rail-of {
  margin-left: auto; font-size: .7rem; font-weight: 700; letter-spacing: .13em;
  text-transform: uppercase; color: var(--muted); white-space: nowrap;
}
.lx-railbar { height: 2px; background: var(--line); }
.lx-railbar span { display: block; height: 100%; background: var(--accent); }

/* ---- notes ---- */
.lx-sec { padding: 34px 0; border-top: 1px solid var(--line); }
.lx-sec:first-child { border-top: none; padding-top: 26px; }
.lx-sec-head { display: flex; align-items: baseline; gap: 14px; margin-bottom: 20px; }
.lx-sec-num {
  font-family: var(--font-display); font-size: .85rem; font-weight: 700;
  color: var(--accent); letter-spacing: .06em; padding-top: 2px;
}
.lx-sec-head h2 {
  font-family: var(--font-display); font-size: 1.35rem; font-weight: 700;
  margin: 0; letter-spacing: -.01em;
}
.lx-sec-body { max-width: 68ch; }
.lx-sec-body ul { padding-left: 20px; margin: 0; }
.lx-sec-body li { margin: 7px 0; }

/* 1. key concepts -> definition grid */
.lx-deflist .lx-def {
  display: grid; grid-template-columns: minmax(120px, .42fr) 1fr;
  gap: 8px 26px; padding: 13px 0; border-bottom: 1px solid var(--line);
}
.lx-def-term { font-family: var(--font-display); font-weight: 600; }
.lx-def-text { color: var(--muted); }

/* 3. worked examples -> the one boxed section, auto-numbered */
.lx-example {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 22px 24px; counter-reset: step;
}
.lx-example > ul { list-style: none; padding: 0; }
.lx-example > ul > li {
  counter-increment: step; position: relative; padding-left: 34px; margin: 11px 0;
}
.lx-example > ul > li::before {
  content: counter(step); position: absolute; left: 0; top: 1px;
  width: 22px; height: 22px; border-radius: 50%;
  background: var(--accent-soft); color: var(--accent-dark);
  font-size: .72rem; font-weight: 700; display: grid; place-items: center;
}

/* 4. misconceptions -> slips */
.lx-slip { border-left: 2px solid var(--req); padding: 4px 0 4px 18px; margin: 0 0 20px; }
.lx-slip-wrong { font-weight: 600; }
.lx-slip-fix { margin-top: 7px; color: var(--muted); position: relative; padding-left: 22px; }
.lx-slip-fix::before { content: "\2192"; position: absolute; left: 0; color: var(--accent); font-weight: 700; }

/* 5. next steps -> real checklist */
.lx-steps { padding-left: 0; margin: 0; }
.lx-steps li { list-style: none; }
.lx-check {
  display: flex; align-items: flex-start; gap: 13px; width: 100%;
  min-height: 44px; padding: 10px 0; font-size: 1rem; text-align: left;
  background: none; border: none; color: var(--ink); cursor: pointer; line-height: 1.5;
}
.lx-check .box {
  flex: 0 0 19px; height: 19px; margin-top: 3px;
  border: 1.5px solid var(--line); border-radius: 4px;
  display: grid; place-items: center; font-size: .72rem; color: transparent;
  transition: border-color .15s var(--ease), background .15s var(--ease), color .15s var(--ease);
}
.lx-check[aria-checked="true"] .box { background: var(--accent); border-color: var(--accent); color: #fff; }
.lx-check[aria-checked="true"] .txt { color: var(--muted); text-decoration: line-through; }
.lx-steps-saved { font-size: .78rem; color: var(--muted); margin-top: 10px; }

/* ---- runner ---- */
.lx-runner-head {
  position: relative; z-index: 40;
  background: rgba(241, 243, 242, .94); backdrop-filter: blur(6px);
  border-bottom: 1px solid var(--line); padding: 12px 24px 0;
}
.lx-runner-headrow {
  max-width: 900px; margin: 0 auto;
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
}
.lx-runner-track { display: flex; align-items: center; gap: 10px; min-width: 0; }
.lx-leave {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: .88rem; font-weight: 600; color: var(--ink);
  background: var(--surface); border: 1px solid var(--line); border-radius: 8px;
  cursor: pointer; padding: 8px 14px; min-height: 44px;
}

/* THE SAME BOX AT TWO FONT SIZES. Every dimension in em, so sectionIntro's
   measure-and-scale lands the kicker exactly on the chip. Change a padding or
   a letter-spacing on one and not the other and the landing breaks. */
.lx-runner-sec, .lx-sec-kicker {
  font-family: var(--font-display); font-weight: 700; letter-spacing: .13em;
  text-transform: uppercase; color: var(--accent-dark);
  background: var(--accent-soft); border-radius: 999px;
  padding: .42em .92em; white-space: nowrap; display: inline-block; line-height: 1.25;
}
.lx-runner-sec { font-size: .72rem; }
.lx-sec-kicker { font-size: 1rem; margin-bottom: 18px; }

.lx-runner-pos {
  font-size: .72rem; font-weight: 700; letter-spacing: .13em;
  text-transform: uppercase; color: var(--muted); white-space: nowrap;
}
.lx-saved { font-size: .72rem; font-weight: 600; color: var(--accent); white-space: nowrap; }
.lx-progress { max-width: 900px; margin: 12px auto 0; height: 2px; background: var(--line); }
.lx-progress-fill { height: 100%; background: var(--accent); transform-origin: left center; }

/* The section title card. A direct child of .lx-frame, not of the scroller,
   so it is genuinely full-bleed and covers the runner header too. */
.lx-sec-intro {
  position: absolute; inset: 0; z-index: 60;
  display: grid; place-items: center; background: var(--bg);
}
.lx-sec-card { max-width: 560px; padding: 0 24px; text-align: center; }
.lx-sec-card h2 {
  font-family: var(--font-display); font-size: clamp(1.8rem, 5.4vw, 2.9rem);
  font-weight: 700; line-height: 1.05; margin: 0;
}
.lx-sec-card p { margin: 16px auto 0; max-width: 42ch; color: var(--muted); font-size: 1rem; }

/* question card */
.lx-q { padding: 38px 0 32px; }
.lx-q-marks {
  display: inline-block; font-size: .7rem; font-weight: 700; letter-spacing: .12em;
  text-transform: uppercase; color: var(--muted); margin-bottom: 10px;
}
.lx-q-stem { font-size: 1.14rem; line-height: 1.5; max-width: 60ch; }
.lx-part-stem { margin: 24px 0 10px; font-size: 1.02rem; }
.lx-answer-block { margin-top: 8px; }

.lx-opts { margin-top: 26px; border-top: 1px solid var(--line); max-width: 60ch; }
.lx-opt {
  display: flex; align-items: center; gap: 14px; width: 100%; min-height: 56px;
  padding: 14px 12px; font-size: 1rem; text-align: left; background: none;
  border: none; border-bottom: 1px solid var(--line); cursor: pointer; color: var(--ink);
  transition: background .15s var(--ease), color .15s var(--ease);
}
.lx-opt-key {
  flex: 0 0 26px; height: 26px; border: 1px solid var(--line); border-radius: 50%;
  display: grid; place-items: center; font-size: .8rem; font-weight: 700; color: var(--muted);
  transition: background .15s var(--ease), color .15s var(--ease), border-color .15s var(--ease);
}
/* A CHOICE, not a verdict: it stays changeable to the end, and it is never
   signalled by colour alone. Filled key plus a tick. */
.lx-opt.is-chosen { background: var(--accent-soft); color: var(--accent-dark); font-weight: 600; }
.lx-opt.is-chosen .lx-opt-key { border-color: var(--accent); background: var(--accent); color: #fff; }
.lx-opt.is-chosen::after { content: "\2713"; margin-left: auto; font-weight: 700; color: var(--accent); }

.lx-numeric {
  display: inline-flex; align-items: center; gap: 10px;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface); padding: 0 14px; min-height: 52px;
  transition: border-color .18s var(--ease);
}
.lx-numeric.is-active { border-color: var(--accent); }
.lx-numeric-val {
  font-size: 1.05rem; width: 180px; max-width: 46vw; padding: 13px 0;
  color: var(--ink); font-variant-numeric: tabular-nums;
}
.lx-caret {
  display: inline-block; width: 1px; height: 1.15em; vertical-align: -.2em;
  background: var(--accent); margin-left: 1px; animation: caret 1s steps(2, start) infinite;
}
@keyframes caret { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }
.lx-unit { color: var(--muted); font-size: .92rem; border-left: 1px solid var(--line); padding-left: 10px; }

.lx-written { max-width: 64ch; margin-top: 22px; }
.lx-answer {
  width: 100%; min-height: 168px; padding: 16px; font-size: 1rem; line-height: 1.55;
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface);
  white-space: pre-wrap;
}
.lx-answer.is-active { border-color: var(--accent); }
.lx-written-note { margin-top: 12px; font-size: .85rem; color: var(--muted); max-width: 52ch; }

.lx-runner-foot {
  border-top: 1px solid var(--line); padding: 18px 0 40px;
  display: flex; justify-content: space-between; gap: 12px;
}
.lx-prev, .lx-next {
  font-weight: 600; font-size: .92rem; padding: 12px 24px; min-height: 44px;
  border-radius: var(--radius); border: 1px solid var(--line);
  background: none; color: var(--ink); cursor: pointer;
}
.lx-next { border-color: var(--accent); background: var(--accent); color: var(--accent-ink); }
.lx-prev[disabled] { opacity: .4; cursor: not-allowed; }

.lx-marking {
  display: flex; flex-direction: column; align-items: center; gap: 18px;
  padding: 80px 24px; text-align: center; color: var(--muted);
}
.lx-marking p { margin: 0; font-size: .95rem; }
.lx-marking-spin {
  width: 34px; height: 34px; border: 3px solid var(--line); border-top-color: var(--accent);
  border-radius: 50%; animation: lx-spin .8s linear infinite;
}
@keyframes lx-spin { to { transform: rotate(360deg); } }

/* ---- review sheet ---- */
.lx-review-head {
  display: flex; align-items: center; gap: 24px;
  padding: 30px 0 26px; border-bottom: 1px solid var(--line); flex-wrap: wrap;
}
.lx-review-meta h3 { font-family: var(--font-display); font-size: 1.3rem; margin: 0 0 4px; }
.lx-review-meta div { color: var(--muted); font-size: .9rem; }
.lx-retake {
  font-weight: 600; font-size: .9rem; padding: 12px 22px; min-height: 44px;
  border: 1px solid var(--accent); border-radius: var(--radius);
  background: none; color: var(--accent); cursor: pointer; margin-left: auto;
}

.lx-insight { margin: 22px 0 8px; padding: 18px 20px; background: var(--accent-soft); border-radius: 14px; }
.lx-insight-head {
  display: flex; align-items: center; gap: 8px; font-family: var(--font-display);
  font-size: .72rem; font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
  color: var(--accent-dark); margin-bottom: 10px;
}
.lx-insight-head .fa-wand-magic-sparkles { color: var(--accent); }
.lx-insight p { margin: 0; font-size: 1rem; line-height: 1.55; max-width: 70ch; }
.lx-insight-cols { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px 36px; margin-top: 16px; }
.lx-insight h4 { font-family: var(--font-display); font-size: .8rem; font-weight: 700; margin: 0 0 6px; color: var(--accent-dark); }
.lx-insight ul { margin: 0; padding-left: 18px; font-size: .92rem; line-height: 1.5; }
.lx-insight li { margin-bottom: 4px; }
.lx-insight li::marker { color: var(--accent); }

.lx-review-q { padding: 22px 0; border-bottom: 1px solid var(--line); }
.lx-review-top { display: flex; align-items: center; gap: 12px; margin-bottom: 6px; }
.lx-review-qid {
  font-family: var(--font-display); font-size: .72rem; font-weight: 700;
  letter-spacing: .12em; color: var(--muted); text-transform: uppercase;
}
/* Right and wrong are never colour alone: the pill carries its own text. */
.lx-verdict {
  margin-left: auto; font-size: .75rem; font-weight: 700; letter-spacing: .04em;
  border-radius: 999px; padding: 4px 10px; white-space: nowrap; font-variant-numeric: tabular-nums;
}
.lx-verdict.hit { color: var(--accent-dark); background: var(--accent-soft); }
.lx-verdict.miss { color: #8c332c; background: #fbeceb; }
.lx-verdict.pending { color: var(--muted); background: var(--bg); border: 1px solid var(--line); }
/* A near miss sits BESIDE the mark, never instead of it: the answer is still
   wrong and still scored nothing, and a pill that softened that would be
   telling the student something untrue. It is amber because it is neither of
   the two verdicts, and it carries its own words for the same reason every
   other pill here does. margin-left:auto lives on .lx-verdict, so the pair
   would split across the row; the second one resets it and takes a gap. */
.lx-verdict.near {
  margin-left: 8px; color: #8a6508; background: #fdf3dc;
  font-weight: 600; letter-spacing: .02em;
}

/* A criterion whose mark is still to be earned. The wording of the mark scheme
   line is withheld (on an "explain why" question it IS the answer) and a hint
   stands in its place, so the row still shows WHERE the mark went without
   handing over what would have won it. */
.lx-crit.locked span { color: var(--muted); }
.lx-crit-locked {
  display: inline-block; font-size: .68rem; font-weight: 700; letter-spacing: .1em;
  text-transform: uppercase; color: #8a6508; background: #fdf3dc;
  border-radius: 999px; padding: 2px 8px; margin-right: 6px; vertical-align: 1px;
}
.lx-review-stem { margin: 0 0 12px; max-width: 62ch; }
.lx-review-yours, .lx-review-right { font-size: .92rem; margin-top: 10px; }
.lx-review-yours b, .lx-review-right b {
  display: block; font-weight: 600; color: var(--muted); font-size: .72rem;
  letter-spacing: .1em; text-transform: uppercase; margin-bottom: 3px;
}
.lx-review-why { margin-top: 8px; font-size: .9rem; color: var(--muted); max-width: 62ch; }

.lx-criteria { margin-top: 18px; max-width: 62ch; }
.lx-crit {
  display: grid; grid-template-columns: 26px 46px 1fr; gap: 12px; align-items: baseline;
  padding: 11px 0; border-bottom: 1px solid var(--line);
}
.lx-crit .mark { font-weight: 700; }
.lx-crit.hit .mark { color: var(--accent); }
.lx-crit.miss .mark { color: var(--req); }
.lx-crit code { font-family: var(--font-display); font-weight: 700; font-size: .8rem; color: var(--muted); }
.lx-crit i { color: var(--muted); display: block; margin-top: 3px; font-size: .88rem; }

/* Amber, matching the portal's own hint banner: a hint only ever appears on a
   question they got WRONG, and teal is the "this is fine" colour everywhere
   else here, so a nudge about a mistake in teal reads as a confirmation.
   #a16207 on #fdf6ec is 6.2:1, AA at small bold. */
.lx-hint {
  margin-top: 14px; padding: 14px 16px; background: #fdf6ec;
  border-left: 3px solid #d99a1c; border-radius: 0 10px 10px 0; max-width: 62ch;
}
.lx-hint b {
  display: block; font-size: .72rem; font-weight: 700; letter-spacing: .1em;
  text-transform: uppercase; color: #a16207; margin-bottom: 6px;
}
.lx-hint p { margin: 0; font-size: .94rem; line-height: 1.55; }

.lx-review-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 14px; }
.lx-review-actions > * { flex: 0 0 auto; }
/* Scoped, unlike the app, where this rule also lands on every notes section. */
.lx-review-actions .lx-reveal,
.lx-review-actions .lx-flag {
  font-size: .82rem; font-weight: 600; color: var(--muted); background: none;
  border: 1px solid var(--line); border-radius: 999px; cursor: pointer;
  padding: 9px 15px; min-height: 44px;
}

/* ---- the Ask dock ----
   position:absolute inside the frame rather than fixed to the viewport, but
   every other value is the dock's own. ONE launcher, not a button per
   question, and the sheet gives up the space rather than being covered. */
.lx-ask {
  position: absolute; z-index: 55; right: 20px; top: 20px; bottom: 20px;
  width: 380px; display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--line); border-radius: 16px;
  box-shadow: 0 18px 48px rgba(16, 24, 21, .18); overflow: hidden;
}
/* Only the log may flex. Without this a growing conversation shrinks every
   row proportionally and the question at the top clips mid line. */
.lx-ask > * { flex: 0 0 auto; }
/* Opaque, and above the log. The header, the picker and the promise line sit
   over a scrolling conversation, so a transparent background let a message
   show through the "I will not give you the answer" note as it scrolled. */
.lx-ask-top { padding: 14px 16px 12px; background: var(--surface); position: relative; z-index: 2; }
.lx-ask-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.lx-ask-head b { display: flex; align-items: center; gap: 8px; font-size: .9rem; }
.lx-ask-head .fa-wand-magic-sparkles { color: var(--accent); }
.lx-ask-close { font-size: .82rem; color: var(--muted); background: none; border: none; cursor: pointer; }
.lx-ask-pick { display: flex; align-items: center; gap: 8px; margin-top: 12px; }
.lx-ask-pick label { font-size: .72rem; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); }
.lx-ask-select {
  flex: 1 1 auto; min-width: 0; font-size: .85rem; padding: 9px 11px;
  border: 1px solid var(--line); border-radius: 8px; background: var(--bg); color: var(--ink);
}
.lx-ask-stem { margin: 12px 0 0; font-size: .9rem; color: var(--ink); }
.lx-chat-note {
  margin: 12px 0 0; padding: 9px 12px; font-size: .8rem; font-weight: 600;
  color: var(--accent-dark); background: var(--accent-soft); border-radius: 8px;
}
.lx-chat-log {
  /* Only the log may flex. Without flex:0 0 auto on its siblings a growing
     conversation shrinks every other row; without min-height:0 it pushes the
     dock taller instead of scrolling inside it. */
  flex: 1 1 auto; min-height: 0; overflow-y: auto; scrollbar-width: none;
  display: flex; flex-direction: column; gap: 10px; padding: 14px 16px;
  /* Functional, not decoration: a message scrolled half out of view was cut
     with a hard edge flush under the promise note, which read as two panels
     colliding. The fade dissolves the partial line and says "more above". */
  mask-image: linear-gradient(to bottom, transparent 0, #000 16px);
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 16px);
}
.lx-chat-log::-webkit-scrollbar { width: 0; height: 0; }
.lx-msg { font-size: .92rem; line-height: 1.55; padding: 10px 13px; border-radius: 12px; max-width: 90%; }
.lx-msg.me { align-self: flex-end; background: var(--accent); color: #fff; border-bottom-right-radius: 4px; }
.lx-msg.bot { align-self: flex-start; background: var(--bg); border: 1px solid var(--line); border-bottom-left-radius: 4px; }
.lx-chat-chips { display: flex; flex-wrap: wrap; gap: 8px; padding: 0 16px 12px; }
/* Scoped, unlike the app, where this also overrides the cover's topic chips. */
.lx-chat-chips .lx-chip {
  font-size: .79rem; color: var(--muted); background: var(--bg);
  border: 1px solid var(--line); border-radius: 999px; cursor: pointer;
  padding: 8px 13px; min-height: 36px; text-align: left;
}
.lx-chat-ask {
  display: flex; gap: 8px; align-items: flex-end; padding: 12px 16px;
  border-top: 1px solid var(--line); background: var(--surface);
  position: relative; z-index: 2;
}
.lx-chat-input {
  flex: 1 1 auto; min-width: 0; font-size: .9rem; padding: 10px 12px;
  border: 1px solid var(--line); border-radius: 10px; background: var(--bg); color: var(--muted);
}
.lx-chat-send {
  font-weight: 600; font-size: .88rem; padding: 10px 16px; border-radius: 10px;
  border: 1px solid var(--accent); background: var(--accent); color: #fff; cursor: pointer;
}
/* The sheet gives up the space rather than being covered. */
.lx-review.ask-open .sheet { max-width: 520px; margin-right: 420px; }
.lx-review-q.is-asking {
  background: var(--accent-soft); border-radius: 4px;
  box-shadow: -14px 0 0 var(--accent-soft), 14px 0 0 var(--accent-soft);
}

/* ---- progress rings (public/lessonCalendar.js progressRing) ---- */
.tp-ring { position: relative; flex: none; line-height: 0; display: inline-block; }
.tp-ring svg { display: block; transform: rotate(-90deg); }
.tp-ring-track { stroke: #eef2f5; }
.tp-ring-arc { stroke-linecap: round; }
.tp-ring-pct {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  font-size: .72rem; font-weight: 700; color: #374151; line-height: 1;
  font-variant-numeric: tabular-nums;
}
.tp-ring-pct-sign { font-size: .55rem; font-weight: 600; color: #9aa5b1; margin-left: 1px; }
.tp-ring-done { font-size: 1.05rem; color: #059669; }

/* ---- coverage accordion (public/topicProgress.css) ----
   Slate hexes, not teal. The real portal does not use brand colour here and
   drifting to teal would make the replica wrong. */
.tp-card { background: #fff; border: 1px solid #e5e9ee; border-radius: 14px; padding: 20px 22px; }
.tp-card-title { font-weight: 700; font-size: 1.05rem; color: #1f2937; margin-bottom: 16px; }
.tp-head { display: flex; align-items: center; gap: 14px; padding: 10px 0; }
.tp-headtext { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.tp-name { font-weight: 600; color: #1f2937; }
.tp-count { font-size: .78rem; color: #6b7280; }
.tp-units { margin-top: 4px; margin-left: 14px; }
.tp-unit { border-top: 1px solid #eef1f4; }
.tp-unit-head { display: flex; align-items: center; gap: 10px; padding: 9px 0; }
.tp-unit-name { font-size: .9rem; font-weight: 600; color: #374151; flex: 1 1 auto; min-width: 0; }
.tp-unit-count { margin-left: auto; }
.tp-topics { padding: 4px 0 12px; }
/* The four bands. TWO columns here, not the app's four: the app's breakpoints
   measure the VIEWPORT, and this card is a ~400px column inside an 880px
   frame on a desktop page, so the real media query would put four columns in
   a space the app itself would only give one. Two is the same layout the
   component renders at that width when it IS the page. */
.tp-cols { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px 10px; }
.tp-col { min-width: 0; border-top: 2px solid #e2e8f0; padding-top: 6px; }
.tp-col-head { display: flex; align-items: baseline; gap: 6px; margin-bottom: 6px; }
.tp-col-name {
  font-size: .64rem; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  color: #94a3b8;
}
.tp-col-count { font-size: .64rem; font-weight: 700; color: #94a3b8; }
/* Red, yellow, teal, same as public/topicProgress.css: the colour is in the
   rule and the chip border, the headings stay slate. A real yellow cannot
   make 4.5:1 as small bold text. */
.tp-col[data-col="needs"] { border-top-color: #dc2626; }
.tp-col[data-col="nearly"] { border-top-color: #eab308; }
.tp-col[data-col="mastered"] { border-top-color: #0f6e6a; }
.tp-col[data-col="needs"] .tp-col-name, .tp-col[data-col="needs"] .tp-col-count,
.tp-col[data-col="nearly"] .tp-col-name, .tp-col[data-col="nearly"] .tp-col-count,
.tp-col[data-col="mastered"] .tp-col-name, .tp-col[data-col="mastered"] .tp-col-count { color: #475569; }
.tp-chiprow { display: flex; flex-direction: column; align-items: stretch; gap: 5px; }
.tp-chiprow-empty { font-size: .7rem; color: #cbd5e1; font-style: italic; }
.tp-topic {
  display: block; text-align: left;
  padding: 5px 9px; border-radius: 8px; font-size: .72rem; line-height: 1.3;
  background: #f8fafc; color: #64748b; border: 1px solid #e2e8f0;
}
/* Clamped to two lines HERE only. The frame's columns are a fixed slice of an
   880px stage and A Level topic titles run to a full sentence, so an unclamped
   chip grew to five lines and the unit stopped reading as four columns. In the
   app the card is the width of the page and the title wraps in full. */
.tp-topic-name {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; overflow-wrap: anywhere;
}
.tp-topic-meta { display: flex; justify-content: flex-end; gap: 6px; margin-top: 2px; }
.tp-topic-done { background: #fff; color: #334155; border-color: #cbd5e1; }
/* the date must never break across lines: "18 / Jan" reads as a bug */
.tp-topic-date, .tp-topic-tag { font-size: .64rem; opacity: .75; white-space: nowrap; }
/* Coverage and mastery are separate axes: the ring stays coverage, the chip's
   COLUMN carries the score. Folding scores into the ring makes progress go
   DOWN after a quiz, which is demoralising and also wrong. */
.tp-topic-done[data-band="needs"] { border-color: #dc2626; }
.tp-topic-done[data-band="nearly"] { border-color: #d4a017; }
.tp-topic-done[data-band="mastered"] { border-color: #0f6e6a; background: #eaf2f1; color: #0b5350; }
.tp-topic-done[data-band="untested"] { border-style: dashed; border-color: #cbd5e1; color: #64748b; }
.tp-topic-done[data-band="untested"] .tp-topic-tag { font-style: italic; }
.tp-more { background: #fff; border-style: dashed; border-color: #cbd5e1; text-align: center; }

/* ---- insights panel (public/studentInsights.css) ---- */
.insights-panel { background: var(--surface); border: 1px solid var(--border); border-radius: 14px; padding: 22px; }
.insights-panel .card-title {
  font-family: var(--font-display); font-size: 1.2rem; font-weight: 700;
  margin: 0 0 18px; padding-bottom: 10px; border-bottom: 3px solid var(--primary-color);
}
.si-hero { padding-bottom: 18px; border-bottom: 1px solid var(--border); }
.si-hero-value {
  font-family: var(--font-display); font-weight: 700; font-size: 3rem;
  line-height: 1; color: var(--text-dark);
}
.si-hero-unit { font-size: 1rem; font-weight: 600; color: var(--text-light); margin-left: 8px; }
.si-hero-label { font-size: .9rem; margin-top: 6px; color: var(--text-light); }
.si-hero-spark { margin-top: 10px; }
.si-streak { margin: 14px 0 0; font-size: .9rem; font-weight: 600; color: var(--primary-color); }
.si-effort { font-size: .78rem; color: var(--text-light); font-variant-numeric: tabular-nums; margin: 6px 0 0; }
/* The 1px gap over a border-coloured background IS the divider. */
.si-tiles {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1px;
  background: var(--border); border: 1px solid var(--border);
  border-radius: 10px; overflow: hidden; margin-top: 18px;
}
.si-tile { background: var(--surface); padding: 12px 10px; }
.si-tile-label { font-size: .72rem; text-transform: uppercase; letter-spacing: .04em; color: var(--text-light); }
.si-tile-value { font-family: var(--font-display); font-weight: 700; font-size: 1.45rem; }
.si-tile-sub { font-size: .75rem; color: var(--text-light); }
.si-section { margin-top: 22px; }
.si-h {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
  font-family: var(--font-display); font-size: .95rem; font-weight: 600; margin: 0 0 8px;
}
.si-chart { display: block; overflow: visible; width: 100%; }
.si-axis { font-family: var(--font-body); font-size: 9px; fill: #5d6b66; font-variant-numeric: tabular-nums; }
.si-rowlabel { font-size: 9.5px; fill: #5d6b66; font-weight: 600; }
.si-dumb-key { display: flex; gap: 16px; margin: 2px 0 8px; font-size: .74rem; color: #5d6b66; }
.si-dumb-key span { display: flex; align-items: center; gap: 6px; }
.si-dot-first, .si-dot-best { width: 9px; height: 9px; border-radius: 50%; display: inline-block; }
.si-dot-first { background: #7d8a85; }
.si-dot-best { background: #0f6e6a; }
.si-subject + .si-subject { border-top: 1px solid var(--border); }
.si-subject-head { display: flex; align-items: center; gap: 10px; min-height: 44px; padding: 8px 0; }
.si-subject-dot { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.si-subject-name { flex: 1 1 auto; font-weight: 600; min-width: 0; }
.si-subject-pct { font-variant-numeric: tabular-nums; font-size: .85rem; color: var(--text-light); }
.si-topic { display: grid; grid-template-columns: minmax(0, 1fr) 84px 36px; gap: 8px; padding: 5px 0; align-items: center; }
.si-topic-title { font-size: .83rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.si-topic-bar, .si-weak-bar { height: 8px; border-radius: 4px; background: #eef1f0; overflow: hidden; }
.si-topic-fill, .si-weak-fill { display: block; height: 100%; border-radius: 4px; transform-origin: left center; }
.si-topic-pct, .si-weak-pct { font-size: .78rem; font-variant-numeric: tabular-nums; text-align: right; }
.si-weak-item {
  display: grid; grid-template-columns: 56px minmax(0, 1fr) 36px;
  grid-template-areas: "bar title pct" "bar sub pct";
  column-gap: 10px; min-height: 44px; padding: 6px 0; border-top: 1px solid var(--border);
  align-items: center;
}
.si-weak-item:first-of-type { border-top: none; }
.si-weak-bar { grid-area: bar; }
.si-weak-title { grid-area: title; font-size: .85rem; font-weight: 600; }
.si-weak-sub { grid-area: sub; font-size: .75rem; color: var(--text-light); }
.si-weak-pct { grid-area: pct; }
/* Amber is a STATUS, never a series colour, and never travels alone: the
   percentage is printed beside every bar. */
.si-weak-fill { background: #a16207; }

/* ---- cookie consent ----
   Ported from index.html, which carries the same banner and the same
   `learnex_cookie_consent` key. It cannot borrow that page's CSS: this page
   loads plusScroll.css alone, so the rules are restated here in the stage's
   own tokens rather than styles.css's.

   `.on-ink` is toggled on <body>, so the --stage-* tokens invert underneath
   this and the text follows the ground for free. Only the surface has to be
   named on both sides, since the banner is opaque and the ground behind it
   changes from chalk to ink as the reader scrolls. */
.cookie-banner {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 200;
  background: var(--chalk); border-top: 1px solid var(--stage-line);
  transform: translateY(100%); visibility: hidden;
  transition: transform .35s var(--ease), visibility .35s var(--ease),
              background .6s linear, border-color .6s linear;
}
body.on-ink .cookie-banner { background: var(--ink-deep); }
.cookie-banner.visible { transform: translateY(0); visibility: visible; }
.cookie-banner-inner {
  width: 100%; max-width: var(--shell); margin: 0 auto;
  padding: 14px var(--pad);
  display: flex; align-items: center; justify-content: space-between; gap: 20px;
  flex-wrap: wrap;
}
.cookie-banner-text {
  margin: 0; min-width: 0; font-size: .88rem; line-height: 1.5;
  color: var(--stage-muted);
}
.cookie-banner-text a { color: var(--stage-accent); text-decoration: underline; text-underline-offset: 3px; }
.cookie-banner-actions { display: flex; gap: 10px; flex-shrink: 0; }
.cookie-btn {
  font: inherit; font-size: .88rem; font-weight: 600; cursor: pointer;
  min-height: 44px; padding: 10px 20px; border-radius: 8px;
  background: none; color: var(--stage-ink); border: 1px solid var(--stage-line);
  transition: border-color .2s var(--ease), background .2s var(--ease), color .2s var(--ease);
}
.cookie-btn:hover { border-color: var(--stage-accent); color: var(--stage-accent); }
.cookie-btn.is-primary {
  background: var(--stage-accent); border-color: var(--stage-accent); color: #fff;
}
.cookie-btn.is-primary:hover { filter: brightness(1.08); color: #fff; }

/* ---- CTA ---- */
.cta-title { font-size: clamp(2.2rem, 5.4vw, 3.8rem); font-weight: 700; max-width: 18ch; }

/* The recap. Three columns divided by vertical hairlines, collapsing to one
   column with horizontal rules on a phone: the page's grid rule, not a bento
   of cards. It restates what was demonstrated above rather than introducing
   anything, which is the only reason it earns the space this late. */
.cta-recap {
  list-style: none; margin: 46px 0 0; padding: 0;
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  border-top: 1px solid var(--stage-line);
}
.cta-recap li {
  min-width: 0; padding: 22px 26px 0 0;
  border-left: 1px solid var(--stage-line); padding-left: 26px;
}
.cta-recap li:first-child { border-left: none; padding-left: 0; }
.cta-recap b {
  display: block; font-family: var(--font-display); font-size: 1.02rem;
  font-weight: 700; margin-bottom: 7px;
}
.cta-recap span { display: block; color: var(--stage-muted); font-size: .93rem; }

.cta-fine {
  margin: 22px 0 0; color: var(--stage-muted); font-size: .88rem; max-width: 52ch;
}
.cta-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 18px; margin: 38px 0 0; }
.cta-btn {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 1rem 1.9rem; min-height: 44px; border-radius: 8px;
  background: var(--stage-accent); color: #fff; text-decoration: none;
  font-weight: 600; font-size: 1.05rem;
  transition: background .2s var(--ease), transform .2s var(--ease);
}
.cta-btn:hover { background: #0b5350; transform: translateY(-1px); }
.cta-link { color: var(--stage-muted); font-size: .95rem; text-decoration: underline; text-underline-offset: 3px; }
.cta-link:hover { color: var(--stage-ink); }

/* ---- act 1: the transcript ---- */
.tx { padding: 26px clamp(18px, 3.5vw, 34px); }
.tx-head {
  display: flex; align-items: center; gap: 10px; padding-bottom: 14px;
  margin-bottom: 8px; border-bottom: 1px solid var(--line);
  font-size: .72rem; font-weight: 700; letter-spacing: .13em;
  text-transform: uppercase; color: var(--muted);
}
.tx-live { width: 7px; height: 7px; border-radius: 50%; background: var(--req); flex: none; }
.tx-line { display: grid; grid-template-columns: 46px 1fr; gap: 12px; padding: 7px 0; align-items: baseline; }
.tx-t { font-size: .72rem; color: var(--muted); font-variant-numeric: tabular-nums; }
.tx-text { font-size: .93rem; line-height: 1.5; }
.tx-line[data-who="you"] .tx-text { color: var(--ink); font-weight: 500; }
.tx-line[data-who="tutor"] .tx-text { color: var(--muted); }
/* The one "AI happens here" beat: the sheet wipes up over the transcript. */
.tx-sheet {
  position: absolute; inset: 0; background: var(--bg);
  border-top: 1px solid var(--line); display: grid; place-items: center; padding: 0 30px;
}
.tx-sheet-inner { text-align: center; max-width: 34ch; }
.tx-sheet-icon { font-size: 1.5rem; color: var(--accent); margin-bottom: 14px; }
.tx-sheet h3 { font-family: var(--font-display); font-size: 1.3rem; margin: 0 0 8px; }
.tx-sheet p { margin: 0; font-size: .92rem; color: var(--muted); }

/* ---- act 7: the record, two panels side by side ---- */
.record-grid {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 18px; padding: 22px; align-content: start;
}
.record-grid > * { min-width: 0; }

/* ---- responsive ---- */
@media (max-width: 1000px) {
  .act-pin {
    grid-template-columns: minmax(0, 1fr);
    align-content: center;
    gap: 20px;
    padding-top: clamp(32px, 5vh, 60px);
  }
  .act-copy h2 { max-width: 20ch; }
  .beat { max-width: 52ch; }
  .lx-frame { height: min(62svh, 620px); }
  .record-grid { grid-template-columns: minmax(0, 1fr); }
  .lx-ask { left: 20px; width: auto; }
  .lx-review.ask-open .sheet { max-width: 100%; margin-right: 0; }
}

/* ---------------------------------------------------------------------------
   Phones.

   The stage is a caption stacked ON TOP of the product frame, so every pixel
   the caption takes is a pixel the frame loses. At 390x844 the caption was
   eating 300px and the frame was left at 490px holding content up to 1015px
   tall, so the cover and the transcript were simply cut off at the bottom.

   Three things happen here, in order of how much they buy:
     1. the caption becomes compact, which hands ~90px back to the frame
     2. the frame takes what is left rather than a fixed fraction of the
        viewport, so it is as tall as the act allows
     3. the replica's own type and spacing step down, so a screen designed
        around a 900px sheet actually fits a 390px one
   --------------------------------------------------------------------------- */
@media (max-width: 700px) {
  /* The back pill sat on top of every act title. The copy column is
     left-aligned and its first line lands at y 45, inside the pill's 25-69
     band, so a top-LEFT pill covers the act number and the heading on every
     act, at every width this narrow.

     Moved to the right AND reduced to the arrow alone. Moving it was not
     enough by itself: the heading's BOX is 18ch wide, which measures 286px at
     390px and reaches x306, while the full pill starts at x265. Dropping the
     word takes the pill to 44px, starting at x332, which clears it. 44px is
     also the tap-target floor, so it cannot shrink further.

     Nudging the copy down instead would have cost about 56px of a frame that
     is already fighting for height on a phone. Top-right also stays clear of
     the two other fixed things, the scroll nudge and the cookie banner, which
     both live at the bottom. */
  .stage-back {
    left: auto; right: clamp(14px, 3vw, 30px);
    width: 44px; padding: 0; gap: 0; justify-content: center; font-size: 1rem;
  }
  /* Clipped, not display:none: the accessible name stays "Learnex", so the
     link still announces where it goes. */
  .stage-back-label {
    position: absolute; width: 1px; height: 1px; margin: 0;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap;
  }

  /* And it gets out of the way while the reader is going forwards. Moving it
     is what makes the overlap impossible; this is what stops a pill floating
     over the frame for 28,000px of scrolling. It comes back the moment they
     scroll up, so the way out is always one small gesture away, which is the
     whole reason it is fixed rather than sitting in the hero. */
  .stage-back.is-tucked {
    transform: translateY(calc(-100% - 28px));
    opacity: 0;
    pointer-events: none;
  }

  .act-pin { gap: 14px; padding: 20px var(--pad) 24px; align-content: start; }

  /* a compact caption: the frame is the thing worth the room */
  .act-num { margin-bottom: 8px; font-size: .66rem; }
  .act-copy h2 { font-size: clamp(1.35rem, 6.2vw, 1.7rem); max-width: 18ch; }
  .beats { margin-top: 12px; }
  .beat { font-size: .92rem; line-height: 1.45; max-width: 40ch; }

  /* the frame takes the rest of the viewport, not a fixed slice of it */
  .lx-frame { height: calc(100svh - 250px); min-height: 380px; max-height: 620px; border-radius: 14px; }

  .lx-frame .sheet { padding: 0 16px; }
  .frame-pad { padding: 18px 16px 24px; }

  /* --- the replica, stepped down --- */
  /* The bar is two labels on one line; at 390px both wrapped to two lines and
     doubled its height. The board line keeps its board and drops the rest. */
  .lx-paper-bar { padding: 9px 14px; gap: 8px; }
  .lx-paper-id {
    font-size: .58rem; letter-spacing: .08em; text-align: right;
    max-width: 52%; line-height: 1.25;
  }
  .lx-back { font-size: .78rem; white-space: nowrap; min-height: 36px; padding: 4px 0; }

  .lx-paper-cover { padding: 22px 0 24px; }
  .cover-title { font-size: clamp(1.35rem, 6.4vw, 1.8rem); margin-top: 10px; }
  .cover-eyebrow { font-size: .62rem; letter-spacing: .16em; }
  .cover-meta { font-size: .82rem; margin-top: 10px; gap: 2px 12px; }
  .cover-rule { margin: 16px 0 12px; }
  .cover-topics .lx-chip { font-size: .7rem; padding: 4px 9px; }
  .lx-launch { margin-top: 20px; padding-top: 18px; }
  .lx-launch-title { font-size: 1.02rem; margin-bottom: 14px; }
  .lx-launch-grid { grid-template-columns: 1fr; }
  .lx-set { padding: 0; border-left: none; }
  .lx-set + .lx-set { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--line); }
  .lx-set h4 { font-size: .95rem; }
  .lx-set-count, .lx-set-status { font-size: .76rem; }
  .lx-set-status { margin: 8px 0 10px; }
  .lx-set-btn { padding: 10px 18px; font-size: .86rem; min-height: 40px; }
  .lx-set-review { margin-left: 6px; }
  .cover-scroll { margin-top: 16px; font-size: .74rem; }

  /* the transcript: fewer, tighter turns, because each one wraps to three
     lines at this width and twelve of them cannot fit any frame */
  .tx { padding: 16px 16px; }
  .tx-head { font-size: .62rem; letter-spacing: .1em; padding-bottom: 10px; }
  .tx-line { grid-template-columns: 38px 1fr; gap: 8px; padding: 5px 0; }
  .tx-t { font-size: .64rem; }
  .tx-text { font-size: .84rem; line-height: 1.4; }
  .tx-line:nth-child(n + 9) { display: none; }
  .tx-sheet h3 { font-size: 1.1rem; }
  .tx-sheet p { font-size: .85rem; }

  .lx-rail-current { padding: 10px 16px; }
  .lx-rail-label { font-size: .82rem; }
  .lx-rail-of { font-size: .62rem; }
  .lx-sec { padding: 24px 0; }
  .lx-sec-head { margin-bottom: 14px; gap: 10px; }
  .lx-sec-head h2 { font-size: 1.12rem; }
  .lx-sec-body { font-size: .9rem; }
  .lx-deflist .lx-def { grid-template-columns: minmax(0, 1fr); gap: 2px; padding: 10px 0; }
  .lx-example { padding: 16px 16px; }
  .lx-check { font-size: .9rem; }

  .lx-runner-head { padding: 9px 14px 0; }
  .lx-runner-subject { display: none; }
  .lx-runner-pos, .lx-runner-sec { font-size: .6rem; letter-spacing: .08em; }
  .lx-leave {
    padding: 5px 9px; font-size: .74rem; min-height: 34px;
    white-space: nowrap; gap: 5px;   /* wrapped to two lines and doubled the header */
  }
  /* A section B question is a stem plus three parts plus the foot, and that
     is the tallest thing the runner ever shows. Everything here is trimmed so
     the last part is not left hanging off the bottom of the frame. */
  .lx-q { padding: 16px 0 12px; }
  .lx-q-marks { margin-bottom: 6px; font-size: .64rem; }
  .lx-q-stem { font-size: .95rem; line-height: 1.42; }
  .lx-part-stem { font-size: .88rem; margin: 12px 0 6px; }
  .lx-answer-block { margin-top: 4px; }
  .lx-opt { min-height: 46px; padding: 10px 8px; font-size: .9rem; }
  .lx-opts { margin-top: 16px; }
  .lx-numeric { min-height: 40px; padding: 0 11px; }
  .lx-numeric-val { font-size: .92rem; width: 96px; padding: 7px 0; }
  .lx-unit { font-size: .82rem; padding-left: 8px; }
  .lx-answer { min-height: 120px; font-size: .9rem; padding: 12px; }
  .lx-written-note { font-size: .78rem; }
  .lx-runner-foot { padding: 10px 0 14px; }
  .lx-prev, .lx-next { padding: 9px 14px; font-size: .82rem; min-height: 40px; }
  .lx-sec-card h2 { font-size: clamp(1.5rem, 8vw, 2.1rem); }
  .lx-sec-card p { font-size: .88rem; margin-top: 12px; }

  .lx-review-head { padding: 20px 0 16px; gap: 14px; }
  .lx-review-meta h3 { font-size: 1.05rem; }
  .lx-review-meta div { font-size: .8rem; }
  .lx-retake { padding: 9px 14px; font-size: .8rem; min-height: 40px; }
  .lx-insight { padding: 14px 15px; }
  .lx-insight p { font-size: .9rem; }
  .lx-insight-cols { grid-template-columns: minmax(0, 1fr); gap: 10px; margin-top: 12px; }
  .lx-insight ul { font-size: .85rem; }
  .lx-review-q { padding: 16px 0; }
  .lx-review-stem { font-size: .92rem; }
  .lx-crit { grid-template-columns: 20px 36px 1fr; gap: 8px; font-size: .88rem; }
  .lx-hint p { font-size: .88rem; }

  /* the dock IS this act on a phone, so it takes the frame */
  .lx-ask { left: 12px; right: 12px; top: 12px; bottom: 12px; }
  .lx-ask-stem { font-size: .84rem; }
  .lx-chat-note { font-size: .74rem; padding: 8px 10px; }
  .lx-msg { font-size: .85rem; padding: 9px 11px; }
  .lx-ask-pick label { font-size: .62rem; }

  .record-grid { grid-template-columns: minmax(0, 1fr); gap: 12px; padding: 14px; }
  /* One band per row on a phone, same as the component itself does there. */
  .tp-cols { grid-template-columns: minmax(0, 1fr); gap: 10px; }
  .tp-card, .insights-panel { padding: 16px; }
  .tp-card-title { font-size: .96rem; margin-bottom: 12px; }
  .si-tiles { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .si-tile-value { font-size: 1.15rem; }
  .si-tile-label { font-size: .62rem; }
  .si-tile-sub { font-size: .66rem; }
  .si-hero-value { font-size: 2.2rem; }

  .cta-actions { gap: 14px; }
  .cookie-banner-inner { flex-direction: column; align-items: stretch; gap: 12px; padding: 14px var(--pad) 16px; }
  .cookie-banner-actions { justify-content: flex-end; }
  /* One column, and the vertical dividers become horizontal rules. */
  .cta-recap { grid-template-columns: minmax(0, 1fr); border-top: none; }
  .cta-recap li,
  .cta-recap li:first-child {
    border-left: none; border-top: 1px solid var(--stage-line);
    padding: 18px 0 0; margin-top: 18px;
  }
  .cta-recap li:first-child { margin-top: 0; }
}

/* Reduced motion: no pinning, no scrubbing. The page becomes a long static
   sequence with every act already in its finished state and the same copy.
   ScrollTrigger is never created at all, so there is nothing to fall back. */
@media (prefers-reduced-motion: reduce) {
  .act-pin { min-height: auto; }
  .cover-scroll span, .hero-cue span, .lx-caret, .lx-marking-spin { animation: none; }
  /* The nudge still shows, it just holds still: it says "there is more",
     which is information the reader needs either way. */
  .is-nudging, .scroll-nudge.is-nudging { animation: none; }
  .beat { position: relative; }
  * { transition-duration: .01ms !important; }
}

/* three subjects stacked in the coverage card */
.tp-subject + .tp-subject { border-top: 1px solid #eef1f4; margin-top: 6px; padding-top: 6px; }
.lx-runner-subject { margin-left: auto; }
