/* ============================================================
   FrontierIQ
   Mobile-first gamified PWA styling
   ============================================================ */

:root {
  /* Brand — Frontier palette: Electric Aqua → Frontier Violet on space navy.
     Softer than the previous flat Microsoft blue; issue #2 fix. */
  --fq-primary: #8A6BFF;         /* Frontier Violet */
  --fq-primary-dark: #6B4BE6;    /* deeper violet — CTAs pressed, active */
  --fq-primary-soft: #EEE8FF;    /* soft violet tint — quiet backgrounds */

  --fq-aqua: #45E6FF;            /* Electric Aqua — highlights, gradient starts */
  --fq-aqua-dark: #2ECFEA;
  --fq-aqua-soft: #DFF9FF;

  --fq-space: #0B0E1A;           /* Space navy — dark hero surfaces */
  --fq-space-warm: #131B3A;      /* Slightly-warmer space, adds depth to gradients */

  --fq-accent: #58CC02;
  --fq-accent-dark: #3F8F00;
  --fq-accent-soft: #E5F8D5;

  --fq-orange: #FF9600;
  --fq-orange-dark: #C67300;

  --fq-red: #FF4B4B;
  --fq-red-dark: #C13B3B;
  --fq-red-soft: #FFE1E1;

  --fq-yellow: #FFC800;
  --fq-yellow-dark: #C9A000;

  --fq-purple: #CE82FF;
  --fq-purple-dark: #A24BD9;

  --fq-teal: #1CB0F6;
  --fq-teal-dark: #1082C7;

  --fq-pink: #FF66B2;
  --fq-pink-dark: #C6478A;

  /* ---------------------------------------------------------------------
     Accessible text tokens (WCAG 2.2 AA, #157)
     ---------------------------------------------------------------------
     The brand colours above are tuned for vivid *surfaces* and are correct as
     they are. What failed the audit was putting small text on or in them.

     Two families solve that without touching a single brand colour:

     `--fq-*-text`  A darkened version of a brand hue, for coloured TEXT on a
                    light background. Each passes >=4.6:1 on both #FFFFFF and
                    --fq-bg-alt (#F7F7F7); --fq-primary-text also clears
                    --fq-primary-soft, where the level chip sits.

     `--fq-on-color` Dark ink for text ON a vivid surface. White text scored
                    2.09:1 on --fq-accent and 2.18:1 on --fq-orange — far
                    below AA. Space navy scores 9.21:1 and 8.80:1 on those same
                    backgrounds, so the fix keeps every vivid colour and simply
                    stops using white ink on it. Most reach AAA. */
  --fq-accent-text: #377F01;   /* 5.00:1 on white, 4.67:1 on bg-alt */
  --fq-orange-text: #A36000;   /* 4.96:1 / 4.63:1 */
  --fq-teal-text: #0776AB;     /* 5.01:1 / 4.68:1 */
  --fq-purple-text: #A61CFF;   /* 4.96:1 / 4.63:1 */
  --fq-primary-text: #6840FF;  /* 5.58:1 / 5.21:1 / 4.68:1 on primary-soft */
  --fq-on-color: var(--fq-space);

  /* Neutral (light) */
  --fq-bg: #FFFFFF;
  --fq-bg-alt: #F7F7F7;
  --fq-bg-elev: #FFFFFF;
  --fq-text: #3C3C3C;
  /* WCAG 1.4.3 needs 4.5:1 for text below 18.66px bold / 24px. The previous
     #777777 measured 4.48:1 on white and 4.18:1 on --fq-bg-alt, failing on both
     (34 nodes in the axe audit). #6E6E6E is the smallest darkening that passes
     on BOTH surfaces: 5.10:1 and 4.76:1. */
  --fq-text-muted: #6E6E6E;
  --fq-border: #E5E5E5;
  --fq-border-strong: #CCCCCC;
  --fq-shadow: 0 8px 24px rgba(0, 0, 0, 0.06);

  /* Sizing */
  --fq-radius-sm: 8px;
  --fq-radius-md: 14px;
  --fq-radius-lg: 20px;
  --fq-radius-xl: 28px;
  --fq-tap: 48px;

  /* Motion */
  --fq-ease: cubic-bezier(0.2, 0.7, 0.3, 1);
  --fq-fast: 120ms;
  --fq-med: 220ms;

  /* Safe areas */
  --fq-safe-top: env(safe-area-inset-top, 0px);
  --fq-safe-bottom: env(safe-area-inset-bottom, 0px);
  --fq-safe-left: env(safe-area-inset-left, 0px);
  --fq-safe-right: env(safe-area-inset-right, 0px);
}

@media (prefers-color-scheme: dark) {
  :root {
    --fq-bg: #131F24;
    --fq-bg-alt: #1B2A31;
    --fq-bg-elev: #202F36;
    --fq-text: #F1F7FB;
    --fq-text-muted: #9AB1BD;
    --fq-border: #37464F;
    --fq-border-strong: #4A5C66;
    --fq-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
    --fq-primary-soft: #12314A;
    --fq-accent-soft: #1D3712;
    --fq-red-soft: #3A1E1E;

    /* The --fq-*-text tokens are darkened for light surfaces, which inverts the
       problem here: #377F01 scores 2.95:1 on --fq-bg-alt. Against dark surfaces
       the original brand colours are already well clear of AA (6.4-8.1:1), so
       they simply revert. Only the violet needs lifting. */
    --fq-accent-text: var(--fq-accent);
    --fq-orange-text: var(--fq-orange);
    --fq-teal-text: var(--fq-teal);
    --fq-purple-text: var(--fq-purple);
    --fq-primary-text: #9F86FF;  /* 4.69-5.87:1 across the dark surfaces */
  }
}

/* Manual theme override via body attribute */
[data-theme="dark"] {
  --fq-bg: #131F24;
  --fq-bg-alt: #1B2A31;
  --fq-bg-elev: #202F36;
  --fq-text: #F1F7FB;
  --fq-text-muted: #9AB1BD;
  --fq-border: #37464F;
  --fq-border-strong: #4A5C66;
  --fq-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
  --fq-primary-soft: #12314A;
  --fq-accent-soft: #1D3712;
  --fq-red-soft: #3A1E1E;

  /* See the prefers-color-scheme block above for why these revert. */
  --fq-accent-text: var(--fq-accent);
  --fq-orange-text: var(--fq-orange);
  --fq-teal-text: var(--fq-teal);
  --fq-purple-text: var(--fq-purple);
  --fq-primary-text: #9F86FF;
}
[data-theme="light"] {
  --fq-bg: #FFFFFF;
  --fq-bg-alt: #F7F7F7;
  --fq-bg-elev: #FFFFFF;
  --fq-text: #3C3C3C;
  --fq-text-muted: #6E6E6E;
  --fq-border: #E5E5E5;
  --fq-border-strong: #CCCCCC;
  --fq-shadow: 0 8px 24px rgba(0, 0, 0, 0.06);
  --fq-primary-soft: #E7F1FA;
  --fq-accent-soft: #E5F8D5;
  --fq-red-soft: #FFE1E1;

  /* Restated, not inherited. On a dark-OS device the prefers-color-scheme block
     above has already rewritten these on :root, and an explicit light choice has
     to undo that or coloured text stays tuned for a dark background. */
  --fq-accent-text: #377F01;
  --fq-orange-text: #A36000;
  --fq-teal-text: #0776AB;
  --fq-purple-text: #A61CFF;
  --fq-primary-text: #6840FF;
}

/* ---------- Base ---------- */

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

html {
  height: 100%;
  -webkit-text-size-adjust: 100%;
  color-scheme: light dark;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI Variable", "Segoe UI",
    Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
  /* Prevent the mobile viewport from doing surprising things when the
     software keyboard opens or the address bar collapses (#91). */
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}

body {
  margin: 0;
  min-height: 100%;
  min-height: 100dvh;
  background: var(--fq-bg);
  color: var(--fq-text);
  overscroll-behavior: none;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

/* Interactive controls get 44px minimum hit area on touch — WCAG target size
   AAA (#91). Also opt into fast-tap semantics via `touch-action: manipulation`
   so mobile browsers skip the 300ms click delay. */
button, [role="button"], input, select, textarea, .steppill, .filterchip, .coursechip {
  touch-action: manipulation;
}

/* Portrait-only banner: manifest sets orientation:portrait-primary, but
   browsers can still show the page in landscape when not installed. Hide
   content and show a rotate hint on small landscape screens. */
@media (max-width: 900px) and (orientation: landscape) {
  #app { filter: blur(2px); pointer-events: none; }
  body::after {
    content: "Please rotate to portrait 📱";
    position: fixed;
    inset: 0;
    display: grid;
    place-items: center;
    text-align: center;
    padding: 40px 24px;
    background: var(--fq-bg);
    color: var(--fq-text);
    font-size: 18px;
    font-weight: 800;
    z-index: 999999;
  }
}

button {
  font-family: inherit;
}

button:focus-visible,
[role="button"]:focus-visible {
  outline: 3px solid var(--fq-teal);
  outline-offset: 2px;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ---------- App shell ---------- */

#app {
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  padding-top: var(--fq-safe-top);
  padding-left: var(--fq-safe-left);
  padding-right: var(--fq-safe-right);
}

/* ---------- Splash ---------- */

.splash {
  min-height: 100dvh;
  display: grid;
  place-items: center;
  /* #756: `place-items` sets align-/justify-ITEMS, not -content, so the two
     auto rows stretched to fill the viewport and the 12px gap below never
     applied — the wordmark sat marooned near the bottom of the screen while
     the mark sat near the top. Centre the row block itself so the hero and the
     wordmark read as one lockup. */
  align-content: center;
  background:
    radial-gradient(80% 60% at 20% 15%, rgba(69, 230, 255, 0.18), transparent 60%),
    radial-gradient(70% 55% at 90% 85%, rgba(138, 107, 255, 0.28), transparent 65%),
    var(--fq-space);
  color: white;
  gap: 12px;
  animation: fadeIn var(--fq-med) var(--fq-ease);
}
/* #756: the boot splash is the app's only loading screen, so it carries the
   full-size Rocket hero — the same 132px transparent treatment the onboarding
   surface uses — rather than a 72px mark sunk into a gradient tile. The tile
   read as a favicon on a full-screen surface. Sized against the viewport so a
   narrow phone does not get a hero wider than its own gutters. */
.splash__logo {
  width: min(132px, 45vw);
  aspect-ratio: 1;
  display: grid;
  place-items: center;
}
.splash__mark {
  width: 100%;
  height: auto;
  filter: drop-shadow(0 20px 40px rgba(138, 107, 255, 0.35));
}
.splash__title {
  font-weight: 900;
  font-size: 32px;
  letter-spacing: -0.02em;
}
.splash__subtitle {
  opacity: 0.85;
  font-weight: 600;
}

/* ---------- Header ---------- */

.topbar {
  position: sticky;
  top: 0;
  z-index: 5;
  background: var(--fq-bg);
  border-bottom: 1px solid var(--fq-border);
}
.topbar__inner {
  max-width: 720px;
  margin: 0 auto;
  padding: 12px 16px;
  display: flex;
  align-items: center;
  gap: 12px;
  /* The row is brand + course chip + download chip + stats. At 390px and below
     that overflowed horizontally, which scrolls the whole page sideways. The
     wordmark is the one element that can give up space without losing meaning —
     the logo mark beside it already carries the brand (#501). */
  min-width: 0;
}
.topbar__brandtext {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Below the width where everything fits, drop the wordmark entirely rather
   than shipping a sideways-scrolling page. The logo stays.

   The row is brand + course chip + download chip + stats, and .stats is both
   the widest child (194px) and the least compressible — every value in it is
   meaningful. So the space comes from chrome: the wordmark, the caret, and the
   padding/gaps. Measured at 390px: 395px wide before, 363px after, against a
   362px baseline on main before the download chip existed. */
@media (max-width: 400px) {
  /* 8px rather than the default 16px: at 320 the row measured over even after
     dropping the level pill, and edge padding is the cheapest thing left to
     give — nothing is lost, only inset. */
  .topbar__inner { gap: 5px; padding: 12px 8px; }
  /* Decorative only — the chip is still obviously tappable without it. */
  .coursechip__caret { display: none; }
  .coursechip { gap: 4px; padding-right: 8px; }
  /* Final safety valve. Pixel-counting alone leaves the row one bad font
     substitution away from overflowing again, and there are narrower devices
     than 320. The course chip is the one element that can lose characters
     without losing meaning — the icon beside it identifies the course — so it
     becomes the elastic member and truncates instead of pushing the row wide.
     min-width:0 is required: without it a flex item will not shrink below its
     content width, which is why the row overflowed rather than compressing. */
  .coursechip { min-width: 0; flex-shrink: 1; }
  .coursechip__code {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}
.topbar__brand {
  display: flex;
  align-items: center;
  gap: 6px;
  font-weight: 900;
  font-size: 18px;
  /* The wordmark is 18px, just under the 18.66px bold threshold for WCAG large
     text, so it needs the full 4.5:1. --fq-primary is 3.73:1 on white. */
  color: var(--fq-primary-text);
  letter-spacing: -0.01em;
}
.topbar__mark, .topbar__brandtext {
  background: transparent;
  border: none;
  padding: 4px 6px;
  border-radius: 8px;
  font: inherit;
  color: inherit;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.topbar__mark { padding: 4px; }
.topbar__mark:hover, .topbar__brandtext:hover { background: var(--fq-bg-alt); }
.topbar__mark:active { transform: scale(0.94); }
.topbar__brand svg,
.topbar__brand img {
  width: 26px;
  height: 26px;
}

/* ---------- Rocket easter egg (#77) ---------- */
.rocket-eastr { overflow: hidden; }
.rocket-eastr__rocket {
  position: absolute;
  bottom: 30%;
  left: 50%;
  width: 96px;
  height: 96px;
  transform: translateX(-50%);
  animation: rocket-liftoff 2.4s cubic-bezier(0.5, 0, 0.5, 1) both;
  filter: drop-shadow(0 20px 40px rgba(255, 200, 0, 0.6));
  pointer-events: none;
  z-index: 2;
}
@keyframes rocket-liftoff {
  0%   { transform: translate(-50%, 0) rotate(-4deg) scale(1); opacity: 1; }
  20%  { transform: translate(-50%, -20px) rotate(2deg) scale(1.03); }
  60%  { transform: translate(-50%, -260px) rotate(-8deg) scale(0.9); opacity: 1; }
  100% { transform: translate(-50%, -820px) rotate(-24deg) scale(0.5); opacity: 0; }
}
.rocket-eastr__panel {
  background: var(--fq-bg-elev);
  color: var(--fq-text);
  border-radius: var(--fq-radius-lg, 16px);
  border: 1px solid var(--fq-border);
  max-width: 460px;
  width: 100%;
  padding: 28px;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.35);
  text-align: center;
  z-index: 1;
  animation: appmodal-pop 380ms 1.6s var(--fq-ease) both;
  opacity: 0;
}
.rocket-eastr__title {
  margin: 0 0 6px;
  font-size: 24px;
  font-weight: 900;
  letter-spacing: -0.02em;
  background: linear-gradient(90deg, #45E6FF, #8A6BFF);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.rocket-eastr__subtitle {
  margin: 0 0 18px;
  color: var(--fq-text-muted);
  font-size: 14.5px;
}
.rocket-eastr__stats {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 8px;
  margin: 0 0 16px;
}
.rocket-eastr__stat {
  background: var(--fq-bg-alt);
  border-radius: var(--fq-radius-md);
  padding: 10px 4px;
}
.rocket-eastr__stat-icon { font-size: 20px; }
.rocket-eastr__stat-value {
  font-size: 18px;
  font-weight: 900;
  color: var(--fq-text);
  margin-top: 2px;
}
.rocket-eastr__stat-label {
  font-size: 10.5px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--fq-text-muted);
  font-weight: 800;
}
@media (max-width: 400px) {
  .rocket-eastr__stats { grid-template-columns: repeat(2, 1fr); }
}
.stats {
  margin-left: auto;
  display: flex;
  gap: 6px;
}
.stat {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 6px 10px;
  border-radius: 999px;
  background: var(--fq-bg-alt);
  font-weight: 800;
  font-size: 14px;
  color: var(--fq-text);
  min-height: 34px;
  border: 1px solid var(--fq-border);
}
/* --fq-orange-text, not --fq-orange: the raw hue scored 2.04:1 on --fq-bg-alt in
   light mode. Every sibling below already uses its accessible `-text` token; the
   streak was the one that did not. The token is theme-aware, so dark keeps the
   vivid orange. */
.stat--streak { color: var(--fq-orange-text); }
.stat--battery { color: var(--fq-accent-text); }
.stat--level  { color: var(--fq-primary-text); background: var(--fq-primary-soft); border-color: var(--fq-primary); }
.stat--gem    { color: var(--fq-purple-text); }
.stat--level { border: none; cursor: pointer; }
.stat--action { border: none; cursor: pointer; }
.stat--action:hover { background: var(--fq-bg-elev); }
.stat--action:focus-visible {
  outline: 2px solid var(--fq-primary);
  outline-offset: 2px;
}

/* Mobile header hardening (#105) — hide the "FrontierIQ" text on narrow
   viewports so the compact indicators (course chip + level + streak +
   gems + battery) all fit on one row. Rocket mark stays visible so the
   brand + easter egg surface never disappear. */
@media (max-width: 480px) {
  .topbar__brandtext { display: none; }
  .stats { gap: 4px; }
  .stat { padding: 5px 7px; font-size: 13px; }
}

/* #535 — the row was still 368px wide against a 320px viewport, so the page
   scrolled sideways. #105 above had already tightened everything tightenable;
   the remaining weight is the stats group itself (194px measured), the widest
   child of the row and the least compressible.

   The LEVEL pill goes, and only that one, because it is the only stat that is
   purely a shortcut: its entire click handler is go("profile"), the bottom nav
   already carries a Profile entry one tap away, and that page shows level WITH
   an XP progress bar and the next-level threshold. The other three are live
   counters a learner acts on mid-session — battery decides whether a lesson can
   start at all, gems buy streak freezes, streak is the core loop.

   display:none also removes it from the accessibility tree, which is right
   here: this is not hidden information, it is a duplicate route to information
   presented better elsewhere.

   Must sit AFTER the .stat--level rules above — same specificity, so source
   order decides. An earlier copy of this block silently lost to them.

   Measured: 368 -> 320 at w=320, 368 -> 325 at w=360. */
@media (max-width: 400px) {
  .stat--level { display: none; }
  .stats { gap: 3px; }
}

/* Path chip strip below topbar — retired in favour of the header course chip
   + modal (issue #41). Kept hidden with the class as a no-op safety in case
   any leftover markup still references it. */
.pathstrip { display: none; }
.pathstrip__inner, .pathchip, .pathchip__icon, .pathchip__label, .pathchip__code, .pathchip__soon { display: none; }

/* ---------- Course selector chip + modal (issue #41) ---------- */

.coursechip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px 6px 8px;
  margin-left: 8px;
  border-radius: 999px;
  border: 1px solid var(--fq-border);
  background: var(--fq-bg-alt);
  color: var(--fq-text);
  font-weight: 800;
  font-size: 13px;
  cursor: pointer;
  transition: transform var(--fq-fast) var(--fq-ease), background var(--fq-fast) var(--fq-ease);
}
.coursechip:hover { background: var(--fq-primary-soft); }
.coursechip:active { transform: translateY(1px); }
.coursechip__icon { font-size: 15px; }
.coursechip__code { letter-spacing: 0.02em; }
.coursechip__caret { font-size: 10px; opacity: 0.55; }

/* The dialog is anchored to a stable top offset rather than vertically
   centered. Centering made the modal slide up and down every time its content
   height changed — on each filter toggle, each search keystroke, and each
   filter-group collapse — because the height is content-driven (#258).
   Anchoring the top edge means the search box and filters never move under
   the cursor; the results list grows and scrolls below them instead. */
.modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: 1000;
  background: rgba(11, 14, 26, 0.55);
  backdrop-filter: blur(3px);
  display: grid;
  place-items: end center;
  /* Full-bleed on phones (#452): the padded bottom-sheet left the page
     showing through at the top, which read as unfinished. Desktop keeps the
     centred dialog below. */
  padding: 0;
  animation: fadeIn var(--fq-fast) var(--fq-ease);
}
@media (min-width: 640px) {
  .modal-backdrop {
    place-items: start center;
    padding: 0 24px;
    /* Stable offset: the modal's top edge sits here regardless of content. */
    padding-top: clamp(24px, 8vh, 96px);
  }
}

.modal {
  width: 100%;
  max-width: 560px;
  /* Full height on phones so nothing shows behind it. The desktop override
     below restores the windowed dialog. */
  max-height: 100dvh;
  height: 100dvh;
  padding-top: var(--fq-safe-top);
  padding-bottom: var(--fq-safe-bottom);
  background: var(--fq-bg-elev);
  border-radius: 0;
  box-shadow: 0 24px 60px rgba(0,0,0,0.35);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
@media (min-width: 640px) {
  .modal {
    max-height: min(88vh, 720px);
    height: auto;
    padding-top: 0;
    padding-bottom: 0;
    border-radius: var(--fq-radius-xl);
  }
}

/* Reserve a stable content height so the shell doesn't resize as results
   filter down. Combined with the top anchor above, the dialog is visually
   static: only the inner results list changes. */
@media (min-width: 640px) {
  .modal--course { height: min(72vh, 640px); }
}

.modal__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 18px;
  border-bottom: 1px solid var(--fq-border);
}
.modal__head h2 {
  margin: 0;
  font-size: 18px;
  font-weight: 900;
  color: var(--fq-text);
}
.modal__close {
  width: 36px;
  height: 36px;
  border-radius: 999px;
  border: none;
  background: transparent;
  color: var(--fq-text-muted);
  font-size: 18px;
  cursor: pointer;
}
.modal__close:hover { background: var(--fq-bg-alt); color: var(--fq-text); }

/* Body is the flex child that absorbs the modal's fixed height, so the
   results list scrolls internally instead of resizing the dialog (#258). */
.modal__body {
  padding: 14px 18px 18px;
  overflow: auto;
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

.modal__search {
  width: 100%;
  padding: 12px 14px;
  border-radius: var(--fq-radius-md);
  border: 1px solid var(--fq-border);
  background: var(--fq-bg-alt);
  color: var(--fq-text);
  font-size: 15px;
  font-weight: 600;
  flex: 0 0 auto;
}
.modal__search:focus {
  outline: 2px solid var(--fq-primary);
  outline-offset: 2px;
}

/* ---------- Result sections (#361, #363) ---------- */
.modal__section { display: flex; flex-direction: column; gap: 8px; }
.modal__section + .modal__section { margin-top: 14px; }
.modal__sectionhead {
  margin: 0;
  font-size: 12px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--fq-text-muted);
}
/* Recommended is the one section that is a suggestion rather than a fact, so
   it gets an accent rail to set it apart from the rest of the list (#361). */
.modal__section--recommended .modal__sectionhead { color: var(--fq-primary-dark); }
.modal__section--recommended .courseresult {
  border-left: 3px solid var(--fq-primary);
}

.modal__filterzone { flex: 0 0 auto; }

/* ---------- Collapsible filter groups (#257) ---------- */
.modal__filtertoggle {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1;
  background: transparent;
  border: none;
  padding: 4px 0;
  cursor: pointer;
  color: var(--fq-text);
  font: inherit;
  font-weight: 800;
  text-align: left;
}
.modal__filtertoggle:hover { color: var(--fq-primary); }
.modal__filtertoggle:focus-visible {
  outline: 2px solid var(--fq-primary);
  outline-offset: 2px;
  border-radius: var(--fq-radius-sm);
}
.modal__filtercaret {
  font-size: 9px;
  opacity: 0.6;
  width: 8px;
}
/* Active-selection count — carries meaning, so it stays high-contrast. */
.modal__filtercount {
  background: var(--fq-primary);
  color: #fff;
  border-radius: 999px;
  padding: 1px 7px;
  font-size: 11px;
  font-weight: 800;
}

/* ---------- Generic in-app modal (#22, #30) ---------- */
.appmodal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  backdrop-filter: blur(2px);
  z-index: 10000;
  display: grid;
  place-items: center;
  padding: 24px;
  animation: appmodal-fade 160ms var(--fq-ease);
}
@keyframes appmodal-fade { from { opacity: 0; } to { opacity: 1; } }

.appmodal {
  background: var(--fq-bg-elev);
  color: var(--fq-text);
  border-radius: var(--fq-radius-lg, 16px);
  border: 1px solid var(--fq-border);
  max-width: 420px;
  width: 100%;
  padding: 22px;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.35);
  animation: appmodal-pop 220ms var(--fq-ease);
}
@keyframes appmodal-pop {
  from { transform: translateY(12px) scale(0.98); opacity: 0; }
  to   { transform: translateY(0)    scale(1);    opacity: 1; }
}

.appmodal__title {
  margin: 0 0 8px;
  font-size: 20px;
  font-weight: 900;
  letter-spacing: -0.01em;
  color: var(--fq-text);
}
.appmodal__body {
  margin: 0;
  color: var(--fq-text-muted);
  font-size: 14.5px;
  line-height: 1.5;
}
.appmodal__meta {
  margin: 10px 0 0;
  padding: 8px 10px;
  border-radius: var(--fq-radius-md);
  background: var(--fq-bg-alt);
  color: var(--fq-text-muted);
  font-size: 13px;
  line-height: 1.4;
  word-break: break-word;
}
/* Out-of-battery prompt (#619). A modal over the current context rather than a
   full-screen takeover, so a gem refill resumes the lesson instead of ending
   it. Centred because there is a single hero glyph carrying the message. */
.appmodal--battery { text-align: center; }
.appmodal--battery .appmodal__actions { justify-content: center; }
.appmodal__icon {
  font-size: 44px;
  line-height: 1;
  margin-bottom: 10px;
  animation: appmodal-battery-pulse 2.4s var(--fq-ease) infinite;
}
@keyframes appmodal-battery-pulse {
  0%, 100% { opacity: 1;    transform: scale(1); }
  50%      { opacity: 0.62; transform: scale(0.94); }
}
@media (prefers-reduced-motion: reduce) {
  .appmodal__icon { animation: none; }
}

/* Recovery hub (#622). Reached from the empty-battery prompt when a gem refill
   is unaffordable. Nothing on it costs battery, and it is never empty — the
   Rocket tow is the floor. */
.appmodal--recovery { max-width: 460px; max-height: 100%; overflow-y: auto; }
.appmodal-backdrop--recovery { min-height: 0; }
.appmodal__link {
  display: block;
  width: 100%;
  margin-top: 12px;
  padding: 4px;
  background: none;
  border: 0;
  color: var(--fq-accent);
  font: inherit;
  font-size: 13px;
  font-weight: 700;
  text-decoration: underline;
  cursor: pointer;
}
.appmodal__link:hover { opacity: 0.82; }

.recovery__list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 14px;
  text-align: left;
}
.recovery__card {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border: 1px solid var(--fq-border);
  border-radius: var(--fq-radius-md);
  background: var(--fq-bg-alt);
}
.recovery__card--locked { border-style: dashed; }
.recovery__card > .btn { flex: 0 0 auto; }
.recovery__icon { font-size: 24px; line-height: 1; flex: 0 0 auto; }
.recovery__text { flex: 1 1 auto; min-width: 0; }
.recovery__title {
  font-size: 14px;
  font-weight: 800;
  color: var(--fq-text);
}
.recovery__detail {
  font-size: 12.5px;
  line-height: 1.4;
  color: var(--fq-text-muted);
}
.recovery__locked {
  flex: 0 1 100px;
  font-size: 12px;
  font-weight: 700;
  color: var(--fq-text-muted);
  font-variant-numeric: tabular-nums;
}
@media (max-width: 480px) {
  .appmodal-backdrop--recovery { padding: 12px; }
  .appmodal--recovery { padding: 16px; }
  .recovery__card { flex-wrap: wrap; }
  .recovery__text { flex-basis: calc(100% - 40px); }
  .recovery__card > .btn,
  .recovery__locked { margin-left: 34px; }
  .recovery__locked { flex-basis: auto; }
}

.appmodal__counter {
  margin-top: 6px;
  text-align: right;
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  color: var(--fq-text-muted);
}
.appmodal__counter--short {
  color: var(--fq-red, #d64545);
}
.shotpreview {
  margin-top: 12px;
}
.shotpreview__status {
  color: var(--fq-text-muted);
  font-size: 12.5px;
}
/* Diagnostic detail for a failed capture (#463). Deliberately small and quiet:
   it is for us, not the learner, but hiding it entirely is what left an
   Android-only failure undiagnosable. */
.shotpreview__reason {
  color: var(--fq-text-muted);
  font-size: 11px;
  opacity: 0.75;
  margin-top: 2px;
  word-break: break-word;
}
.shotpreview__toggle {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: var(--fq-text);
  cursor: pointer;
  margin-bottom: 8px;
}
.shotpreview__img {
  display: block;
  width: 100%;
  max-height: 180px;
  object-fit: contain;
  object-position: top;
  border-radius: var(--fq-radius-md);
  border: 1px solid var(--fq-border);
  background: var(--fq-bg);
}

/* Post-lesson sync prompt (#431). */
.syncprompt {
  min-height: 100dvh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}
.syncprompt__inner {
  width: 100%;
  max-width: 460px;
  text-align: center;
}
.syncprompt__icon {
  font-size: 44px;
  line-height: 1;
  margin-bottom: 14px;
}
.syncprompt__title {
  margin: 0 0 10px;
  font-size: 24px;
  font-weight: 900;
  letter-spacing: -0.01em;
  color: var(--fq-text);
}
.syncprompt__body {
  margin: 0 0 12px;
  color: var(--fq-text);
  font-size: 15px;
  line-height: 1.5;
}
.syncprompt__note {
  margin: 0 0 22px;
  color: var(--fq-text-muted);
  font-size: 13px;
  line-height: 1.5;
}
.syncprompt__actions {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

/* First-run consent gate (#424) and install invite (#425). */
.consent {
  position: fixed;
  inset: 0;
  z-index: 1200;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  background: rgba(0, 0, 0, 0.45);
  padding: 16px;
}
.consent__card {
  width: 100%;
  max-width: 520px;
  background: var(--fq-bg-elev);
  border-radius: var(--fq-radius-lg, 16px);
  padding: 20px;
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.28);
}
.consent__title {
  margin: 0 0 8px;
  font-size: 19px;
  font-weight: 900;
  color: var(--fq-text);
}
.consent__body {
  margin: 0 0 16px;
  color: var(--fq-text-muted);
  font-size: 14px;
  line-height: 1.5;
}
.consent__actions {
  display: flex;
  gap: 10px;
}
/* Both choices get equal width — a reject that is harder to hit than accept
   is not a real choice. The transparent border is load-bearing: .btn--ghost
   carries a 2px border that .btn--accent does not, and with flex-basis 0 the
   border sits OUTSIDE the flex base size regardless of box-sizing, which
   otherwise makes the reject button 4px wider than accept. */
.consent__actions .btn {
  flex: 1 1 0;
  min-width: 0;
  box-sizing: border-box;
  border: 2px solid transparent;
}
.consent__link {
  display: block;
  margin: 14px auto 0;
  background: none;
  border: 0;
  padding: 4px;
  color: var(--fq-primary, #0067C0);
  font-size: 13px;
  font-family: inherit;
  text-decoration: underline;
  cursor: pointer;
}
.install {
  position: fixed;
  inset: auto 0 0 0;
  z-index: 1100;
  display: flex;
  justify-content: center;
  padding: 12px;
  pointer-events: none;
}
.install__card {
  pointer-events: auto;
  width: 100%;
  max-width: 520px;
  background: var(--fq-bg-elev);
  border: 1px solid var(--fq-border);
  border-radius: var(--fq-radius-lg, 16px);
  padding: 14px 16px;
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.22);
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}
.install__text { flex: 1 1 220px; }
.install__title {
  margin: 0 0 3px;
  font-size: 15px;
  font-weight: 800;
  color: var(--fq-text);
}
.install__body {
  margin: 0;
  color: var(--fq-text-muted);
  font-size: 13px;
  line-height: 1.4;
}
.install__actions {
  display: flex;
  gap: 8px;
  flex: 0 0 auto;
}
@media (max-width: 480px) {
  .install__actions { width: 100%; }
  .install__actions .btn { flex: 1 1 0; }
}
.appmodal__textarea {
  width: 100%;
  margin-top: 14px;
  padding: 12px 14px;
  border-radius: var(--fq-radius-md);
  border: 1px solid var(--fq-border);
  background: var(--fq-bg);
  color: var(--fq-text);
  font-family: inherit;
  font-size: 14.5px;
  line-height: 1.45;
  resize: vertical;
  min-height: 96px;
}
.appmodal__textarea:focus {
  outline: 2px solid var(--fq-primary);
  outline-offset: 2px;
}
.appmodal__actions {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
  margin-top: 18px;
}

.streakmodal {
  max-width: 560px;
}
.streakmodal__stats {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
  margin: 8px 0 12px;
}
.streakmodal__chip {
  background: var(--fq-bg);
  border: 1px solid var(--fq-border);
  border-radius: 10px;
  padding: 10px;
  text-align: center;
}
.streakmodal__chip strong {
  display: block;
  font-size: 16px;
}
.streakmodal__chip span {
  color: var(--fq-text-muted);
  font-size: 12px;
}
.streakmodal__weekdays,
.streakmodal__calendar {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 6px;
}
.streakmodal__weekdays {
  margin-bottom: 6px;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--fq-text-muted);
}
.streakmodal__weekdays span {
  text-align: center;
}
.streakmodal__legend {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 14px;
  margin: 10px 0;
  color: var(--fq-text-muted);
  font-size: 11px;
}
.streakmodal__legend-item {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.streakmodal__today-key {
  width: 11px;
  height: 11px;
  border: 2px solid var(--fq-primary);
  border-radius: 3px;
}
.streakmodal__day {
  height: 34px;
  border-radius: 8px;
  border: 1px solid var(--fq-border);
  background: var(--fq-bg);
  color: var(--fq-text-muted);
  display: grid;
  place-items: center;
  font-size: 12px;
  font-weight: 700;
  position: relative;
  padding: 2px 3px;
}
.streakmodal__day-number {
  line-height: 1;
}
.streakmodal__markers {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  height: 12px;
}
.streakmodal__marker {
  display: inline-flex;
  width: 11px;
  height: 12px;
  align-items: center;
  justify-content: center;
  /* Emoji markers (#488). Sized to the old SVG footprint so the 42-day grid
     keeps its layout. line-height:1 stops the taller emoji box from pushing
     the calendar rows apart. */
  font-size: 10px;
  line-height: 1;
}
/* The flame and freeze SVGs were replaced by emoji (#488), so the colour rules
   that drove `currentColor` and the SVG fills no longer apply — emoji carry
   their own colour and cannot be themed. The day-cell tints below still do the
   colour work, which is what actually reads at this size. */
.streakmodal__day--done {
  background: color-mix(in srgb, var(--fq-accent) 20%, var(--fq-bg));
  border-color: color-mix(in srgb, var(--fq-accent) 55%, var(--fq-border));
  color: var(--fq-text);
}
.streakmodal__day--frozen {
  background: color-mix(in srgb, #1976d2 14%, var(--fq-bg));
  border-color: color-mix(in srgb, #1976d2 55%, var(--fq-border));
}
.streakmodal__day--today {
  outline: 2px solid var(--fq-primary);
  outline-offset: 1px;
}
.streakmodal__day--blank {
  border: none;
  background: transparent;
}
@media (max-width: 640px) {
  .streakmodal__stats { grid-template-columns: 1fr; }
}
.modal__filterhead {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: 12px;
  font-size: 12px;
  color: var(--fq-text-muted);
  font-weight: 700;
}
.modal__filterhead-label {
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.modal__clearall, .modal__clearone {
  background: transparent;
  border: 1px solid var(--fq-border);
  color: var(--fq-text);
  font-size: 11px;
  font-weight: 800;
  padding: 3px 8px;
  border-radius: 999px;
  cursor: pointer;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.modal__clearall:hover, .modal__clearone:hover {
  background: var(--fq-bg-alt);
  border-color: var(--fq-primary);
  color: var(--fq-primary-dark);
}
/* ---------- Filter tabs (#359) ---------- */
/* Topic / Role / Level / Product sit in one row and drive a single shared
   panel, so the filter zone keeps a stable height instead of growing a stack
   of independently expanded groups. */
.modal__filtertabs {
  display: flex;
  gap: 6px;
  margin-top: 14px;
  /* One genuine horizontal row at every width (#359). Narrow screens scroll
     the row rather than wrapping it into a second line. */
  flex-wrap: nowrap;
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.modal__filtertabs::-webkit-scrollbar { display: none; }
.modal__filtertab {
  flex: 0 0 auto;
  gap: 5px;
  padding: 7px 10px;
  border: 1px solid var(--fq-border);
  border-radius: 999px;
  background: var(--fq-bg);
  font-size: 13px;
  font-weight: 700;
  white-space: nowrap;
}
.modal__filtertab:hover { border-color: var(--fq-primary); }
/* Open and active are independent: a tab can hold a selection while collapsed,
   which is how an active filter stays visible without being expanded. */
.modal__filtertab--open {
  border-color: var(--fq-primary);
  background: var(--fq-primary-soft);
  color: var(--fq-primary-dark);
}
.modal__filtertab--active { border-color: var(--fq-primary); }
.modal__filterpanel {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 10px;
}
.modal__filterlabel {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--fq-text-muted);
  font-weight: 800;
}
.modal__filterchips { display: flex; flex-wrap: wrap; gap: 6px; }
/* The shared filter panel collapses via the `hidden` attribute; its own
   display:flex would otherwise win and keep collapsed chips on screen (#359,
   ported from the per-group rule added in #257). */
.modal__filterpanel[hidden] { display: none; }

.filterchip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 5px 10px;
  border-radius: 999px;
  border: 1px solid var(--fq-border);
  background: var(--fq-bg);
  color: var(--fq-text);
  font-size: 12px;
  font-weight: 700;
  cursor: pointer;
  text-transform: capitalize;
}
.filterchip:hover { background: var(--fq-bg-alt); }
.filterchip:focus-visible {
  outline: 2px solid var(--fq-primary);
  outline-offset: 2px;
}
.filterchip--active {
  background: var(--fq-primary);
  color: white;
  border-color: var(--fq-primary);
  box-shadow: 0 2px 8px rgba(138, 107, 255, 0.35);
}
/* Per-chip result count (#259) — "what do I get if I click this?" */
.filterchip__count {
  font-size: 10px;
  font-weight: 800;
  opacity: 0.6;
  font-variant-numeric: tabular-nums;
}
.filterchip--active .filterchip__count { opacity: 0.85; }
/* Zero-result chips stay visible (so the taxonomy is legible) but are clearly
   inert. Not colour alone — opacity + cursor + aria-disabled. */
.filterchip--empty {
  opacity: 0.4;
  cursor: not-allowed;
}
.filterchip--empty:hover { background: var(--fq-bg); }

/* Results scroll inside the fixed-height dialog rather than growing it. */
.modal__results {
  margin-top: 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}
.modal__empty {
  color: var(--fq-text-muted);
  text-align: center;
  padding: 20px 0;
  font-weight: 600;
}

.courseresult {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px;
  border-radius: var(--fq-radius-md);
  border: 1px solid var(--fq-border);
  background: var(--fq-bg-alt);
  text-align: left;
  cursor: pointer;
  transition: transform var(--fq-fast) var(--fq-ease);
}
.courseresult:hover { background: var(--fq-primary-soft); }
.courseresult:active { transform: translateY(1px); }
.courseresult--active {
  border-color: var(--fq-primary);
  background: var(--fq-primary-soft);
}
/* --fq-text-muted clears white and --fq-bg-alt but lands at 4.27:1 on the
   tinted active row, so the selected course gets a slightly darker muted. */
.courseresult--active .courseresult__sub { color: #5F5F5F; }
.courseresult__icon { font-size: 26px; flex-shrink: 0; }
.courseresult__body { flex: 1; min-width: 0; }
.courseresult__title {
  display: flex;
  gap: 8px;
  align-items: baseline;
  font-weight: 900;
  color: var(--fq-text);
}
.courseresult__code {
  font-size: 12px;
  padding: 2px 6px;
  border-radius: 6px;
  /* White on --fq-primary is 3.73:1 at 12px (#157). The deeper violet keeps the
     badge on-brand at 5.56:1. */
  background: var(--fq-primary-dark);
  color: white;
  letter-spacing: 0.03em;
}
.courseresult__sub {
  font-size: 13px;
  color: var(--fq-text-muted);
  margin-top: 3px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ---------- Page container ---------- */

.page {
  flex: 1;
  padding: 20px 16px calc(24px + var(--fq-safe-bottom));
  max-width: 720px;
  width: 100%;
  margin: 0 auto;
}
.page--flush { padding: 0; }

/* ---------- Home: hero ---------- */

.hero {
  padding: 20px;
  border-radius: var(--fq-radius-xl);
  background: linear-gradient(160deg, var(--fq-primary), var(--fq-primary-dark));
  color: white;
  margin-bottom: 20px;
  box-shadow: var(--fq-shadow);
  position: relative;
  overflow: hidden;
}
.hero::after {
  content: "";
  position: absolute;
  right: -30px;
  top: -30px;
  width: 160px;
  height: 160px;
  background: radial-gradient(circle at center, rgba(255,255,255,0.18), transparent 70%);
  pointer-events: none;
}
.hero__eyebrow {
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  opacity: 0.85;
  font-weight: 800;
}
.hero__title {
  font-size: 22px;
  font-weight: 900;
  margin: 6px 0 4px;
  letter-spacing: -0.01em;
}
.hero__meta {
  font-size: 14px;
  opacity: 0.9;
}
.hero__duepill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: 10px;
  padding: 6px 12px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.2);
  color: white;
  font-size: 12.5px;
  font-weight: 800;
  letter-spacing: 0.02em;
  backdrop-filter: blur(6px);
  border: none;
  cursor: pointer;
  font-family: inherit;
}
/* Refuel drill gauge (#626). Sits where the retry policy banner sits, so the
   lesson chrome has one slot for "this run is special". */
.drill {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 8px 0 4px;
  padding: 8px 12px;
  border-radius: var(--fq-radius-md);
  background: var(--fq-bg-alt);
  border: 1px solid var(--fq-border);
}
.drill__clock {
  flex: 0 0 auto;
  font-size: 15px;
  font-weight: 900;
  font-variant-numeric: tabular-nums;
  color: var(--fq-text);
}
.drill__fuel {
  flex: 1 1 auto;
  height: 8px;
  border-radius: 999px;
  background: var(--fq-border);
  overflow: hidden;
}
.drill__fuel-fill {
  display: block;
  height: 100%;
  width: 0;
  border-radius: 999px;
  background: var(--fq-accent);
  transition: width 220ms var(--fq-ease);
}
.drill__earned {
  flex: 0 0 auto;
  font-size: 13px;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  color: var(--fq-text-muted);
}
@media (prefers-reduced-motion: reduce) {
  .drill__fuel-fill { transition: none; }
}

/* Salvage pill (#624). Same chrome as the review pill, spaced so the two can
   sit side by side without touching. */
.hero__duepill--salvage { margin-left: 8px; }
/* #678: static, not interactive — unlike salvage there is no single place to
   send the learner, so it must not look like a button or sit in the tab order. */
.hero__duepill--stars {
  margin-left: 8px;
  cursor: default;
  background: rgba(255, 255, 255, 0.14);
}
.hero__duepill:hover, .hero__duepill:focus-visible {
  background: rgba(255, 255, 255, 0.32);
  outline: none;
}
button.hero__duepill:active {
  transform: translateY(1px);
}

/* Review surface (#122) */
.page--review .review__state {
  text-align: center;
  padding: 40px 24px;
  color: var(--fq-text);
}

/* Skill mastery chip on hero (#123 v2) */
.hero__mastery {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: 8px;
  padding: 5px 10px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.15);
  color: white;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.02em;
  backdrop-filter: blur(6px);
}

/* Skill analytics section on profile (#60) */
.skills { margin-top: 12px; }
.skills__hint { color: var(--fq-text-muted); font-size: 13px; margin: 0 0 12px; }
.skills__block { margin-top: 14px; }
.skills__label {
  font-size: 12px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--fq-text-muted);
  margin-bottom: 6px;
}
.skills__row {
  display: grid;
  grid-template-columns: 130px 1fr 46px;
  gap: 8px;
  align-items: center;
  padding: 4px 0;
  font-size: 13px;
}
.skills__tag {
  color: var(--fq-text);
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.skills__theta {
  text-align: right;
  color: var(--fq-text-muted);
  font-variant-numeric: tabular-nums;
  font-size: 12px;
}
.skills__bar {
  height: 10px;
  background: var(--fq-bg-alt);
  border-radius: 999px;
  overflow: hidden;
}
.skills__bar-fill { height: 100%; border-radius: 999px; transition: width 0.4s ease; }
.skills__bar-fill--strong { background: linear-gradient(90deg, #4CD08A, #26a341); }
.skills__bar-fill--weak { background: linear-gradient(90deg, #F5B849, #F27C4A); }
.review__badge {
  font-size: 56px;
  line-height: 1;
  margin-bottom: 12px;
}
.review__spinner {
  font-size: 40px;
  animation: review-spin 1.6s linear infinite;
  display: inline-block;
}
@keyframes review-spin {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}
.review__state h2 { margin: 8px 0 6px; }
.review__state p { color: var(--fq-text-muted); margin-bottom: 20px; }
.hero__progress {
  margin-top: 14px;
  height: 10px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.2);
  overflow: hidden;
}
.hero__progress span {
  display: block;
  height: 100%;
  background: var(--fq-accent);
  border-radius: 999px;
  transition: width var(--fq-med) var(--fq-ease);
}

/* ---------- Unit sections ---------- */

.unit {
  margin: 28px 0;
}
.unit__header {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 16px;
  border-radius: var(--fq-radius-lg);
  /* Dark ink on the vivid unit colour. White scored as low as 2.09:1 here
     (#157); this keeps every unit colour and reaches 4.66-12.38:1. */
  color: var(--fq-on-color);
  box-shadow: 0 4px 0 var(--unit-shadow, rgba(0,0,0,0.1));
  background: var(--unit-color, var(--fq-primary));
  margin-bottom: 20px;
}
.unit__icon {
  width: 44px;
  height: 44px;
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.2);
  display: grid;
  place-items: center;
  font-size: 22px;
  flex-shrink: 0;
}
.unit__titles {
  flex: 1;
  min-width: 0;
}
.unit__eyebrow {
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  /* Was 0.9. Fading dark ink toward a vivid background costs contrast at the
     smallest type on the page, so hierarchy comes from weight and size only. */
  opacity: 1;
  font-weight: 800;
}
.unit__title {
  font-size: 17px;
  font-weight: 900;
  letter-spacing: -0.01em;
  margin: 2px 0 0;
}
.unit__progress-pill {
  padding: 6px 10px;
  /* Was rgba(0,0,0,0.15), which darkened the unit colour. That helped white
     ink and now works against the dark ink the header uses, leaving the pill
     at 3.55-3.89:1. Lightening instead pushes it to a near-white chip. */
  background: rgba(255, 255, 255, 0.92);
  border-radius: 999px;
  font-weight: 800;
  font-size: 13px;
  white-space: nowrap;
}

/* Test-out button on each unit header (#103). Starburst star + small pill
   label — subtle enough not to compete with the primary lesson path, but
   clearly discoverable. */
.unit__testout {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px 6px 8px;
  background: linear-gradient(135deg, var(--fq-yellow), var(--fq-orange));
  color: white;
  border: none;
  border-radius: 999px;
  font-weight: 900;
  font-size: 12px;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  cursor: pointer;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15), 0 0 0 2px rgba(255, 255, 255, 0.2) inset;
  transition: transform var(--fq-fast) var(--fq-ease), box-shadow var(--fq-fast) var(--fq-ease);
  margin-right: 6px;
  touch-action: manipulation;
}
.unit__testout:hover { transform: translateY(-1px); box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2), 0 0 0 2px rgba(255, 255, 255, 0.3) inset; }
.unit__testout:active { transform: translateY(1px); }
.unit__testout-star {
  font-size: 15px;
  line-height: 1;
  filter: drop-shadow(0 0 4px rgba(255, 255, 255, 0.8));
  animation: testout-pulse 2.4s ease-in-out infinite;
}
@keyframes testout-pulse {
  0%, 100% { transform: scale(1) rotate(0deg); }
  50%      { transform: scale(1.2) rotate(15deg); }
}
@media (max-width: 480px) {
  .unit__testout-label { display: none; }
  .unit__testout { padding: 6px 8px; }
}

/* Unit color themes applied via data-color attribute */
.unit[data-color="blue"]   { --unit-color: var(--fq-primary);  --unit-shadow: var(--fq-primary-dark); }
.unit[data-color="purple"] { --unit-color: #8A5DFF;            --unit-shadow: #5A32C4; }
.unit[data-color="teal"]   { --unit-color: var(--fq-teal);     --unit-shadow: var(--fq-teal-dark); }
.unit[data-color="green"]  { --unit-color: var(--fq-accent);   --unit-shadow: var(--fq-accent-dark); }
.unit[data-color="orange"] { --unit-color: var(--fq-orange);   --unit-shadow: var(--fq-orange-dark); }
.unit[data-color="red"]    { --unit-color: var(--fq-red);      --unit-shadow: var(--fq-red-dark); }
.unit[data-color="pink"]   { --unit-color: var(--fq-pink);     --unit-shadow: var(--fq-pink-dark); }
.unit[data-color="yellow"] { --unit-color: var(--fq-yellow);   --unit-shadow: var(--fq-yellow-dark); }

/* Path of lesson nodes (snake layout) */
.path {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 20px;
  padding: 8px 0 4px;
}
.path__node {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  transform: translateX(var(--offset, 0px));
}
/* The current node hangs a START pill 32px above itself, which lands on the
   previous node's step pills at the default 20px path gap. */
.path__node[data-current="true"] { margin-top: 16px; }
.path-fab {
  position: fixed;
  right: max(18px, env(safe-area-inset-right));
  bottom: calc(86px + var(--fq-safe-bottom));
  z-index: 1200;
  width: 56px;
  height: 56px;
  border: 0;
  border-radius: 18px;
  display: grid;
  place-items: center;
  background: var(--fq-primary);
  color: white;
  font-size: 30px;
  font-weight: 1000;
  line-height: 1;
  box-shadow: 0 6px 0 var(--fq-primary-dark), 0 14px 28px rgba(0, 0, 0, 0.22);
  opacity: 0;
  pointer-events: none;
  transform: translateY(14px) scale(0.94);
  transition: opacity var(--fq-med) var(--fq-ease),
              transform var(--fq-med) var(--fq-ease),
              box-shadow var(--fq-fast) var(--fq-ease);
}
.path-fab--visible {
  opacity: 1;
  pointer-events: auto;
  transform: translateY(0) scale(1);
}
.path-fab--settling {
  pointer-events: none;
}
.path-fab:hover { transform: translateY(-2px) scale(1.02); }
.path-fab:active {
  transform: translateY(6px) scale(1);
  box-shadow: 0 0 0 var(--fq-primary-dark), 0 8px 18px rgba(0, 0, 0, 0.2);
}
.path-fab:focus-visible {
  outline: 3px solid var(--fq-primary);
  outline-offset: 4px;
}
/* A control that cannot be used should not be visible (#452). The FAB is
   position: fixed at a higher z-index than the modal backdrop, so it floated
   over the course sheet and invited a tap that did nothing. Hidden rather than
   restacked — same reasoning as suppressing the install invite while another
   surface owns the screen (#431). */
body[data-modal-open="true"] .path-fab {
  display: none;
}
@media (max-width: 420px) {
  .path-fab {
    right: 16px;
    bottom: calc(78px + var(--fq-safe-bottom));
  }
}
.node {
  --node-size: 76px;
  width: var(--node-size);
  height: var(--node-size);
  border-radius: 50%;
  border: none;
  background: var(--node-color, var(--fq-primary));
  color: white;
  display: grid;
  place-items: center;
  cursor: pointer;
  font-size: 32px;
  font-weight: 900;
  box-shadow:
    0 6px 0 var(--node-shadow, var(--fq-primary-dark)),
    0 12px 24px rgba(0, 0, 0, 0.08);
  transition: transform var(--fq-fast) var(--fq-ease),
              box-shadow var(--fq-fast) var(--fq-ease);
  position: relative;
}
.node:hover { transform: translateY(-2px); }
.node:active {
  transform: translateY(6px);
  box-shadow: 0 0 0 var(--node-shadow, var(--fq-primary-dark));
}
.node--locked {
  background: var(--fq-bg-alt);
  color: var(--fq-text-muted);
  box-shadow: 0 4px 0 var(--fq-border-strong);
  cursor: not-allowed;
}
.node--locked:hover { transform: none; }
.node--locked:active { transform: none; box-shadow: 0 4px 0 var(--fq-border-strong); }

/* Completed nodes carry their star score instead of a tick (#377). Three stars
   have to fit inside the 76px circle, so they run smaller than the 32px
   numeral used by unlocked nodes. */
.node--complete {
  background: var(--fq-accent);
  box-shadow: 0 6px 0 var(--fq-accent-dark), 0 12px 24px rgba(0, 0, 0, 0.08);
  font-size: 20px;
  letter-spacing: 1px;
}
.node--current {
  animation: bob 1.6s var(--fq-ease) infinite;
}
.node--current::before {
  content: "START";
  position: absolute;
  top: -32px;
  left: 50%;
  transform: translateX(-50%);
  background: var(--fq-bg);
  border: 2px solid var(--fq-border);
  color: var(--fq-text);
  padding: 4px 10px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 900;
  letter-spacing: 0.1em;
  box-shadow: 0 3px 0 var(--fq-border-strong);
}

.path__node[data-slot="0"] { --offset: 0px; }
.path__node[data-slot="1"] { --offset: 60px; }
.path__node[data-slot="2"] { --offset: 100px; }
.path__node[data-slot="3"] { --offset: 60px; }
.path__node[data-slot="4"] { --offset: 0px; }
.path__node[data-slot="5"] { --offset: -60px; }
.path__node[data-slot="6"] { --offset: -100px; }
.path__node[data-slot="7"] { --offset: -60px; }

@media (max-width: 420px) {
  .path__node[data-slot="1"] { --offset: 40px; }
  .path__node[data-slot="2"] { --offset: 70px; }
  .path__node[data-slot="3"] { --offset: 40px; }
  .path__node[data-slot="5"] { --offset: -40px; }
  .path__node[data-slot="6"] { --offset: -70px; }
  .path__node[data-slot="7"] { --offset: -40px; }
}

.node__label {
  font-size: 12px;
  color: var(--fq-text-muted);
  font-weight: 700;
  max-width: 140px;
  text-align: center;
  line-height: 1.2;
}

/* Concept step pills — mini badges below each concept node showing the
   pedagogy steps (📖 Study / 🎯 Practice / 🏆 Mastery). One pill per group
   the concept spans. See src/app.js:renderPath (#71). */
.steppills {
  display: flex;
  gap: 6px;
  justify-content: center;
  flex-wrap: wrap;
  margin-top: 4px;
}
.steppill {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 8px;
  border-radius: 999px;
  font-size: 10.5px;
  font-weight: 800;
  letter-spacing: 0.02em;
  border: 1.5px solid var(--fq-border);
  background: var(--fq-bg-alt);
  color: var(--fq-text-muted);
  cursor: pointer;
  transition: background var(--fq-fast) var(--fq-ease), transform var(--fq-fast) var(--fq-ease);
}
.steppill:hover:not(:disabled) {
  background: var(--fq-bg-elev);
  transform: translateY(-1px);
}
.steppill--done {
  background: var(--fq-accent-soft);
  border-color: var(--fq-accent);
  color: var(--fq-accent-dark);
}
.steppill--locked {
  opacity: 0.55;
  cursor: not-allowed;
}
.steppill--current {
  background: var(--fq-primary-soft);
  border-color: var(--fq-primary);
  color: var(--fq-primary-text);
}
.steppill__icon { font-size: 12px; }
.steppill__label { text-transform: uppercase; }

/* ---------- Buttons ---------- */

.btn {
  --btn-bg: var(--fq-primary);
  --btn-shadow: var(--fq-primary-dark);
  --btn-text: white;
  min-height: var(--fq-tap);
  padding: 14px 20px;
  border: none;
  border-radius: 16px;
  font-size: 15px;
  font-weight: 900;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  cursor: pointer;
  background: var(--btn-bg);
  color: var(--btn-text);
  box-shadow: 0 4px 0 var(--btn-shadow);
  transition: transform var(--fq-fast) var(--fq-ease),
              box-shadow var(--fq-fast) var(--fq-ease),
              opacity var(--fq-fast) var(--fq-ease);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
}
.btn:hover { transform: translateY(-1px); }
.btn:active {
  transform: translateY(4px);
  box-shadow: 0 0 0 var(--btn-shadow);
}
.btn:disabled {
  --btn-bg: var(--fq-border);
  --btn-shadow: var(--fq-border-strong);
  --btn-text: var(--fq-text-muted);
  cursor: not-allowed;
  opacity: 1;
}
.btn:disabled:hover { transform: none; }
.btn:disabled:active { transform: none; box-shadow: 0 4px 0 var(--btn-shadow); }

/* Accent buttons carry small bold label text. White on --fq-accent is 2.08:1,
   so they use the same dark ink as other vivid surfaces (#157). */
.btn--accent { --btn-bg: var(--fq-accent); --btn-shadow: var(--fq-accent-dark); --btn-text: var(--fq-on-color); }
.btn--red    { --btn-bg: var(--fq-red);    --btn-shadow: var(--fq-red-dark); }
.btn--ghost {
  --btn-bg: transparent;
  --btn-shadow: transparent;
  --btn-text: var(--fq-text);
  box-shadow: none;
  border: 2px solid var(--fq-border-strong);
}
.btn--ghost:active { transform: none; box-shadow: none; }
.btn--sm { min-height: 40px; padding: 10px 14px; font-size: 13px; }
.btn--auto { width: auto; }

/* ---------- Cards ---------- */

.card {
  background: var(--fq-bg-elev);
  border: 2px solid var(--fq-border);
  border-radius: var(--fq-radius-lg);
  padding: 16px;
}
.card__title {
  font-size: 16px;
  font-weight: 900;
  margin: 0 0 4px;
}
.card__subtitle {
  color: var(--fq-text-muted);
  font-size: 14px;
}

/* ---------- Lesson screen ---------- */

.lesson {
  --lesson-body-pad-y: 20px;
  --lesson-body-pad-x: 16px;
  --lesson-body-gap: 20px;
  --lesson-topbar-pad-y: 12px;
  --lesson-topbar-pad-x: 16px;
  --lesson-topbar-extra-top: 28px;
  --lesson-footer-pad-y: 16px;
  --lesson-meter-headroom: 22px;
  --lesson-prompt-size: 22px;
  --lesson-prompt-line: 1.3;
  --lesson-choice-gap: 12px;
  --lesson-choice-pad-y: 16px;
  --lesson-choice-pad-x: 16px;
  --lesson-choice-font: 15px;
  --lesson-choice-line: 1.4;
  --lesson-tf-pad-y: 24px;
  --lesson-tf-pad-x: 12px;
  --lesson-tf-font: 18px;
  --lesson-fill-pad-y: 14px;
  --lesson-fill-pad-x: 16px;
  --lesson-fill-font: 16px;
  --lesson-match-gap: 10px;
  --lesson-match-pad: 12px;
  --lesson-match-font: 14px;
  --lesson-match-min: 56px;
  --lesson-result-pad-y: 14px;
  --lesson-result-pad-x: 16px;
  --lesson-result-line: 1.45;
  --lesson-hintbox-pad-y: 12px;
  --lesson-hintbox-pad-x: 14px;
  --lesson-order-gap: 8px;
  --lesson-order-pad-y: 12px;
  --lesson-order-pad-x: 14px;
  --lesson-order-text: 14.5px;
  --lesson-cat-gap: 14px;
  --lesson-chip-pad-y: 8px;
  --lesson-chip-pad-x: 12px;
  --lesson-chip-font: 13.5px;
  --lesson-scenario-gap: 12px;
  --lesson-scenario-pad-y: 14px;
  --lesson-scenario-pad-x: 16px;
  --lesson-scenario-font: 14.5px;
  --lesson-scenario-line: 1.5;
  --lesson-scenario-footer-gap: 6px;
  min-height: 100dvh;
  height: 100dvh;
  display: flex;
  flex-direction: column;
  background: var(--fq-bg);
  overflow: hidden;
}
.lesson[data-fit-tier="1"] {
  --lesson-body-pad-y: 16px;
  --lesson-body-gap: 16px;
  --lesson-topbar-extra-top: 24px;
  --lesson-footer-pad-y: 14px;
  --lesson-meter-headroom: 21px;
  --lesson-choice-gap: 10px;
  --lesson-choice-pad-y: 14px;
  --lesson-choice-pad-x: 14px;
  --lesson-tf-pad-y: 20px;
  --lesson-result-pad-y: 12px;
  --lesson-hintbox-pad-y: 10px;
  --lesson-order-gap: 7px;
  --lesson-order-pad-y: 10px;
  --lesson-cat-gap: 12px;
  --lesson-scenario-gap: 10px;
  --lesson-scenario-pad-y: 12px;
  --lesson-scenario-line: 1.45;
  --lesson-scenario-footer-gap: 5px;
}
.lesson[data-fit-tier="2"] {
  --lesson-body-pad-y: 14px;
  --lesson-body-gap: 12px;
  --lesson-topbar-extra-top: 20px;
  --lesson-footer-pad-y: 12px;
  --lesson-meter-headroom: 20px;
  --lesson-choice-gap: 8px;
  --lesson-choice-pad-y: 12px;
  --lesson-choice-pad-x: 14px;
  --lesson-tf-pad-y: 16px;
  --lesson-result-pad-y: 12px;
  --lesson-hintbox-pad-y: 9px;
  --lesson-order-gap: 6px;
  --lesson-order-pad-y: 9px;
  --lesson-cat-gap: 10px;
  --lesson-scenario-gap: 9px;
  --lesson-scenario-pad-y: 11px;
  --lesson-scenario-pad-x: 14px;
  --lesson-scenario-font: 14px;
  --lesson-scenario-line: 1.42;
  --lesson-scenario-footer-gap: 4px;
}
.lesson[data-fit-tier="3"] {
  --lesson-body-pad-y: 12px;
  --lesson-body-gap: 10px;
  --lesson-topbar-extra-top: 16px;
  --lesson-footer-pad-y: 10px;
  --lesson-meter-headroom: 19px;
  --lesson-prompt-size: 20px;
  --lesson-prompt-line: 1.25;
  --lesson-choice-gap: 8px;
  --lesson-choice-pad-y: 10px;
  --lesson-choice-pad-x: 12px;
  --lesson-choice-font: 14.5px;
  --lesson-choice-line: 1.32;
  --lesson-tf-pad-y: 12px;
  --lesson-tf-font: 16px;
  --lesson-fill-pad-y: 12px;
  --lesson-fill-font: 15px;
  --lesson-match-gap: 8px;
  --lesson-match-pad: 10px;
  --lesson-match-min: 52px;
  --lesson-result-pad-y: 11px;
  --lesson-result-line: 1.35;
  --lesson-order-pad-y: 8px;
  --lesson-order-text: 14px;
  --lesson-chip-font: 13px;
  --lesson-scenario-gap: 8px;
  --lesson-scenario-pad-y: 10px;
  --lesson-scenario-pad-x: 14px;
  --lesson-scenario-font: 14px;
  --lesson-scenario-line: 1.4;
  --lesson-scenario-footer-gap: 3px;
}
.lesson[data-fit-tier="4"] {
  --lesson-body-pad-y: 10px;
  --lesson-body-gap: 6px;
  --lesson-topbar-extra-top: 12px;
  --lesson-footer-pad-y: 8px;
  --lesson-meter-headroom: 18px;
  --lesson-prompt-size: 17px;
  --lesson-prompt-line: 1.22;
  --lesson-choice-gap: 5px;
  --lesson-choice-pad-y: 6px;
  --lesson-choice-pad-x: 10px;
  --lesson-choice-font: 14px;
  --lesson-choice-line: 1.28;
  --lesson-tf-pad-y: 8px;
  --lesson-tf-font: 15px;
  --lesson-fill-pad-y: 10px;
  --lesson-fill-font: 14px;
  --lesson-match-gap: 7px;
  --lesson-match-pad: 8px;
  --lesson-match-min: var(--fq-tap);
  --lesson-result-pad-y: 10px;
  --lesson-result-pad-x: 14px;
  --lesson-result-line: 1.32;
  --lesson-hintbox-pad-y: 8px;
  --lesson-hintbox-pad-x: 12px;
  --lesson-order-gap: 5px;
  --lesson-order-pad-y: 7px;
  --lesson-order-pad-x: 10px;
  --lesson-order-text: 14px;
  --lesson-cat-gap: 8px;
  --lesson-chip-pad-y: 6px;
  --lesson-chip-pad-x: 10px;
  --lesson-chip-font: 13px;
  --lesson-scenario-gap: 6px;
  --lesson-scenario-pad-y: 8px;
  --lesson-scenario-pad-x: 12px;
  --lesson-scenario-font: 14px;
  --lesson-scenario-line: 1.35;
  --lesson-scenario-footer-gap: 2px;
}
.lesson[data-fitting="true"] .lesson__body {
  visibility: hidden;
}
.lesson__topbar {
  padding: var(--lesson-topbar-pad-y) var(--lesson-topbar-pad-x);
  display: flex;
  align-items: flex-end;
  gap: 12px;
  padding-top: calc(var(--lesson-topbar-extra-top) + var(--fq-safe-top));
  border-bottom: 1px solid var(--fq-border);
}
.lesson__close {
  width: 40px;
  height: 40px;
  border-radius: 12px;
  border: none;
  background: var(--fq-bg-alt);
  font-size: 22px;
  font-weight: 900;
  color: var(--fq-text-muted);
  cursor: pointer;
}
.lesson__close:active { transform: scale(0.96); }

.progress {
  height: 16px;
  border-radius: 999px;
  background: var(--fq-bg-alt);
  overflow: hidden;
  border: 1px solid var(--fq-border);
}
.lesson__meter {
  position: relative;
  flex: 1;
  min-width: 0;
  padding-top: var(--lesson-meter-headroom);
}
.progress__fill {
  height: 100%;
  background: var(--fq-accent);
  transition: width var(--fq-med) var(--fq-ease);
  box-shadow: inset 0 4px 0 rgba(255, 255, 255, 0.3);
}
.combo-indicator {
  position: absolute;
  left: 50%;
  top: 0;
  transform: translateX(-50%);
  padding: 3px 9px;
  border-radius: 999px;
  background: linear-gradient(135deg, var(--fq-yellow), var(--fq-orange));
  color: var(--fq-on-color);
  border: 1px solid rgba(11, 14, 26, 0.16);
  box-shadow: 0 8px 20px rgba(255, 150, 0, 0.24);
  font-size: 11px;
  line-height: 1;
  font-weight: 1000;
  letter-spacing: 0.12em;
  white-space: nowrap;
  pointer-events: none;
  animation: combo-pop var(--fq-med) var(--fq-ease) both;
  will-change: transform, opacity;
}
.hearts {
  color: var(--fq-red);
  font-weight: 900;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 16px;
}

@keyframes combo-pop {
  0% {
    opacity: 0;
    transform: translateX(-50%) translateY(5px) scale(0.82);
  }
  16% {
    opacity: 1;
    transform: translateX(-50%) translateY(0) scale(1.12);
  }
  28% {
    opacity: 1;
    transform: translateX(-50%) translateY(0) scale(1);
  }
  100% {
    opacity: 1;
    transform: translateX(-50%) translateY(0) scale(1);
  }
}

@media (prefers-reduced-motion: reduce) {
  .combo-indicator {
    animation: none;
    opacity: 1;
  }
}

.combo-lightning {
  position: fixed;
  inset: 0;
  z-index: 320;
  pointer-events: none;
  overflow: hidden;
  background: rgba(255, 255, 255, 0);
  animation: combo-flash 820ms steps(1, end) both;
}
.combo-lightning__label {
  position: absolute;
  right: max(18px, env(safe-area-inset-right));
  top: calc(72px + env(safe-area-inset-top));
  padding: 8px 14px;
  border: 3px solid #0b0e1a;
  border-radius: 999px;
  background: #F5E050;
  color: #0b0e1a;
  font-weight: 1000;
  letter-spacing: 0.08em;
  box-shadow: 8px 8px 0 rgba(11, 14, 26, 0.25);
  animation: combo-label-jolt 780ms var(--fq-ease) both;
}
.combo-lightning__bolt {
  position: absolute;
  left: 50%;
  top: -12vh;
  width: clamp(58px, 12vw, 126px);
  height: 118vh;
  transform: translateX(-50%);
  background: #F5E050;
  clip-path: polygon(44% 0, 77% 0, 59% 31%, 84% 31%, 39% 72%, 56% 44%, 29% 44%);
  filter: drop-shadow(8px 0 0 #0b0e1a);
  animation: combo-bolt-strike 760ms cubic-bezier(0.2, 0.9, 0.2, 1) both;
}
.combo-lightning__spark {
  position: absolute;
  left: var(--x);
  bottom: 5vh;
  width: 20px;
  height: 7px;
  background: #F5E050;
  border: 2px solid #0b0e1a;
  transform: rotate(calc(-28deg + var(--i) * 17deg));
  animation: combo-spark-hop 620ms cubic-bezier(0.2, 0.85, 0.2, 1) both;
  animation-delay: var(--delay);
}
.combo-lightning--tier-2 .combo-lightning__bolt,
.combo-lightning--tier-3 .combo-lightning__bolt,
.combo-lightning--tier-4 .combo-lightning__bolt {
  width: clamp(74px, 15vw, 152px);
}
.combo-lightning--reduced {
  animation: none;
}
.combo-lightning--reduced .combo-lightning__bolt {
  top: 16vh;
  height: 52vh;
  opacity: 0.9;
  animation: combo-static-bolt 900ms var(--fq-ease) both;
}
.combo-lightning--reduced .combo-lightning__label {
  animation: none;
}

/* Battery recharge celebration (#339, re-anchored in #390).
   Anchored to the on-screen battery indicator (.stat--battery in the header,
   .hearts in the lesson topbar) rather than the viewport centre, so it no
   longer collides with the Rocket motivational pop-up (.chr--pop, which owns
   the bottom-right / bottom-centre space) and so the reward clearly names what
   was recharged. Falls back to a top-centred position if no anchor is on
   screen. */
.battery-recharge {
  position: fixed;
  left: var(--anchor-x, 50%);
  top: calc(var(--anchor-bottom, 56px) + 14px);
  z-index: 330;
  pointer-events: none;
  display: grid;
  grid-template-columns: auto auto;
  column-gap: 12px;
  row-gap: 2px;
  align-items: center;
  justify-items: start;
  min-width: 172px;
  padding: 14px 20px 14px 16px;
  border: 3px solid rgba(11, 14, 26, 0.22);
  border-radius: 24px;
  background: linear-gradient(135deg, #E8FFE8, #C6F7FF);
  color: #0b0e1a;
  box-shadow: 0 18px 44px rgba(0, 0, 0, 0.28), inset 0 5px 0 rgba(255,255,255,0.68);
  transform: translateX(-50%);
  animation: battery-recharge-pop var(--duration, 900ms) cubic-bezier(0.34, 1.56, 0.64, 1) both;
}
/* Little pointer aimed back at the battery indicator. */
.battery-recharge--anchored::before {
  content: "";
  position: absolute;
  top: -11px;
  left: calc(50% + var(--pointer-shift, 0px));
  width: 16px;
  height: 16px;
  transform: translateX(-50%) rotate(45deg);
  background: #E8FFE8;
  border-left: 3px solid rgba(11, 14, 26, 0.22);
  border-top: 3px solid rgba(11, 14, 26, 0.22);
  border-radius: 4px 0 0 0;
}

/* The battery itself — a real cell that visibly fills, not an emoji. */
.battery-recharge__cell {
  grid-row: 1 / span 2;
  display: flex;
  align-items: center;
  gap: 3px;
}
.battery-recharge__shell {
  position: relative;
  display: block;
  width: 46px;
  height: 66px;
  border: 4px solid #0b0e1a;
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.72);
  overflow: hidden;
  box-shadow: inset 0 2px 0 rgba(255, 255, 255, 0.9);
}
.battery-recharge__cap {
  display: block;
  width: 9px;
  height: 24px;
  border: 4px solid #0b0e1a;
  border-left: none;
  border-radius: 0 5px 5px 0;
  background: #0b0e1a;
}
.battery-recharge__fill {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: var(--fill-to, 100%);
  background: linear-gradient(180deg, #7CF77C, var(--fq-accent, #22C55E));
  animation: battery-fill-rise var(--duration, 900ms) cubic-bezier(0.22, 1, 0.36, 1) both;
}
.battery-recharge__bolt {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-size: 26px;
  line-height: 1;
  filter: drop-shadow(0 2px 0 rgba(11, 14, 26, 0.25));
  animation: battery-bolt-zap var(--duration, 900ms) var(--fq-ease) both;
}
.battery-recharge__amount {
  align-self: end;
  font-size: 34px;
  line-height: 1;
  animation: battery-count-pop var(--duration, 900ms) cubic-bezier(0.34, 1.56, 0.64, 1) both;
}
.battery-recharge__label {
  align-self: start;
  font-size: 12px;
  font-weight: 900;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.battery-recharge__spark {
  position: absolute;
  left: 34px;
  top: 50%;
  width: 9px;
  height: 22px;
  border-radius: 6px;
  background: var(--fq-accent);
  transform-origin: 50% 50%;
  animation: battery-spark-out var(--duration, 900ms) ease-out both;
  animation-delay: calc(var(--i) * 26ms);
}
.battery-recharge--amount-3,
.battery-recharge--amount-4 {
  background: linear-gradient(135deg, #FFF4A8, #BFF7FF 55%, #C4FFD8);
}
.battery-recharge--amount-3 .battery-recharge__fill,
.battery-recharge--amount-4 .battery-recharge__fill {
  background: linear-gradient(180deg, #FFF08A, #7CF77C 60%, var(--fq-accent, #22C55E));
}
.battery-recharge--amount-3::before,
.battery-recharge--amount-4::before { background: #FFF4A8; }
.battery-recharge--reduced {
  animation: battery-recharge-static 900ms var(--fq-ease) both;
}
.battery-recharge--reduced .battery-recharge__spark { display: none; }
.battery-recharge--reduced .battery-recharge__fill,
.battery-recharge--reduced .battery-recharge__bolt,
.battery-recharge--reduced .battery-recharge__amount {
  animation: none;
  height: var(--fill-to, 100%);
}

/* The indicator being rewarded pulses in time with the card. */
.stat--battery.is-recharging,
.hearts.is-recharging {
  animation: battery-anchor-pulse 620ms cubic-bezier(0.34, 1.56, 0.64, 1) 2 both;
}
@media (prefers-reduced-motion: reduce) {
  .stat--battery.is-recharging,
  .hearts.is-recharging { animation: none; }
}

/* Narrow viewports: shrink the cell so the card can never clip off-screen. */
@media (max-width: 480px) {
  .battery-recharge { min-width: 152px; padding: 12px 16px 12px 12px; column-gap: 10px; }
  .battery-recharge__shell { width: 38px; height: 56px; }
  .battery-recharge__cap { height: 20px; }
  .battery-recharge__amount { font-size: 28px; }
  .battery-recharge__bolt { font-size: 22px; }
}

@keyframes combo-flash {
  0% { background: rgba(245, 224, 80, 0); }
  42% { background: rgba(245, 224, 80, 0.16); }
  48% { background: rgba(255, 255, 255, 0.22); }
  56% { background: rgba(245, 224, 80, 0.08); }
  100% { background: rgba(245, 224, 80, 0); }
}
@keyframes combo-bolt-strike {
  0% { opacity: 0; transform: translateX(-50%) translateY(-28vh) scaleX(0.72); }
  38% { opacity: 0; }
  46% { opacity: 1; transform: translateX(-50%) translateY(0) scaleX(1.08); }
  58% { opacity: 1; transform: translateX(-50%) translateY(1vh) scaleX(1); }
  100% { opacity: 0; transform: translateX(-50%) translateY(5vh) scaleX(0.9); }
}
@keyframes combo-spark-hop {
  0% { opacity: 0; transform: translateY(28vh) rotate(-32deg) scale(0.5); }
  18% { opacity: 1; }
  58% { opacity: 1; transform: translateY(-44vh) rotate(26deg) scale(1.16); }
  100% { opacity: 0; transform: translateY(-76vh) rotate(54deg) scale(0.55); }
}
@keyframes combo-label-jolt {
  0% { opacity: 0; transform: translateY(-8px) scale(0.8); }
  42% { opacity: 1; transform: translateY(0) scale(1.12) rotate(-2deg); }
  62% { transform: translateY(0) scale(1) rotate(1deg); }
  100% { opacity: 0; transform: translateY(-8px) scale(0.92); }
}
@keyframes combo-static-bolt {
  0% { opacity: 0; transform: translateX(-50%) scale(0.9); }
  20%, 78% { opacity: 1; transform: translateX(-50%) scale(1); }
  100% { opacity: 0; transform: translateX(-50%) scale(0.95); }
}
@keyframes battery-recharge-pop {
  0% { opacity: 0; transform: translateX(-50%) translateY(-14px) scale(0.82); }
  18% { opacity: 1; transform: translateX(-50%) translateY(0) scale(1.08); }
  74% { opacity: 1; transform: translateX(-50%) translateY(0) scale(1); }
  100% { opacity: 0; transform: translateX(-50%) translateY(-14px) scale(0.96); }
}
@keyframes battery-fill-rise {
  0% { height: var(--fill-from, 0%); }
  16% { height: var(--fill-from, 0%); }
  62% { height: var(--fill-to, 100%); }
  70% { height: calc(var(--fill-to, 100%) + 6%); }
  100% { height: var(--fill-to, 100%); }
}
@keyframes battery-bolt-zap {
  0% { opacity: 0; transform: scale(0.4) rotate(-16deg); }
  22% { opacity: 1; transform: scale(1.28) rotate(6deg); }
  40% { opacity: 0.9; transform: scale(1) rotate(0deg); }
  76% { opacity: 1; transform: scale(1.12) rotate(-4deg); }
  100% { opacity: 0.85; transform: scale(1) rotate(0deg); }
}
@keyframes battery-count-pop {
  0% { opacity: 0; transform: translateY(10px) scale(0.6); }
  26% { opacity: 1; transform: translateY(0) scale(1.24); }
  46% { transform: translateY(0) scale(1); }
  100% { opacity: 1; transform: translateY(0) scale(1); }
}
/* Sparks fan out radially from the battery cell. --n is the spark count, so
   the fan stays evenly distributed however many the reward size produced. */
@keyframes battery-spark-out {
  0% {
    opacity: 0;
    transform: rotate(calc(var(--i) * (360deg / max(1, var(--n, 12))))) translateY(0) scale(0.35);
  }
  20% { opacity: 1; }
  100% {
    opacity: 0;
    transform: rotate(calc(var(--i) * (360deg / max(1, var(--n, 12))))) translateY(-88px) scale(1.05);
  }
}
@keyframes battery-recharge-static {
  0% { opacity: 0; transform: translateX(-50%) scale(0.95); }
  18%, 78% { opacity: 1; transform: translateX(-50%) scale(1); }
  100% { opacity: 0; transform: translateX(-50%) scale(0.97); }
}
@keyframes battery-anchor-pulse {
  0% { transform: scale(1); filter: none; }
  40% { transform: scale(1.22); filter: drop-shadow(0 0 10px var(--fq-accent, #22C55E)); }
  100% { transform: scale(1); filter: none; }
}

.lesson__body {
  flex: 1;
  min-height: 0;
  padding: var(--lesson-body-pad-y) var(--lesson-body-pad-x);
  max-width: 720px;
  width: 100%;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: var(--lesson-body-gap);
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-gutter: stable;
}

.lesson__prompt {
  font-size: var(--lesson-prompt-size);
  font-weight: 900;
  line-height: var(--lesson-prompt-line);
  letter-spacing: -0.01em;
  margin: 0;
}

/* Fill-in-the-blank blank, sized to the canonical answer (#604).
   width comes from --blank-ch, set per-question in renderPromptWithBlank().
   max-width keeps a long multi-word answer from pushing the page sideways on
   narrow viewports — it shrinks instead of overflowing. */
.lesson__prompt .blank {
  display: inline-block;
  width: var(--blank-ch, 8ch);
  max-width: 100%;
  height: 1em;
  vertical-align: -0.15em;
  margin: 0 0.15em;
  border-bottom: 0.12em solid currentColor;
}
.lesson__intro {
  padding: 14px 16px;
  border-radius: var(--fq-radius-md);
  background: var(--fq-primary-soft);
  color: var(--fq-text);
  font-size: 15px;
  line-height: 1.45;
  border-left: 4px solid var(--fq-primary);
}
.lesson__hint {
  font-size: 13px;
  color: var(--fq-text-muted);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.1em;
}

/* Multiple choice */
.choices {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--lesson-choice-gap);
}
.choice {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: var(--lesson-choice-pad-y) var(--lesson-choice-pad-x);
  border: 2px solid var(--fq-border-strong);
  border-bottom-width: 4px;
  border-radius: 16px;
  background: var(--fq-bg-elev);
  color: var(--fq-text);
  cursor: pointer;
  font-size: var(--lesson-choice-font);
  font-weight: 700;
  text-align: left;
  line-height: var(--lesson-choice-line);
  transition: border-color var(--fq-fast), transform var(--fq-fast), background var(--fq-fast);
  min-height: var(--fq-tap);
}
.choice__badge {
  width: 28px;
  height: 28px;
  border-radius: 8px;
  background: var(--fq-bg-alt);
  border: 2px solid var(--fq-border-strong);
  display: grid;
  place-items: center;
  font-size: 13px;
  font-weight: 900;
  flex-shrink: 0;
}
.choice:hover { border-color: var(--fq-teal); }
.choice[aria-pressed="true"] {
  border-color: var(--fq-teal);
  background: var(--fq-primary-soft);
  color: var(--fq-teal-dark);
}
.choice[aria-pressed="true"] .choice__badge {
  border-color: var(--fq-teal);
  color: var(--fq-teal-dark);
  background: white;
}
.choice--correct {
  border-color: var(--fq-accent) !important;
  background: var(--fq-accent-soft) !important;
  color: var(--fq-accent-dark) !important;
}
.choice--wrong {
  border-color: var(--fq-red) !important;
  background: var(--fq-red-soft) !important;
  color: var(--fq-red-dark) !important;
}

/* True/False */
.tf {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--lesson-choice-gap);
}
.tf .choice {
  justify-content: center;
  font-size: var(--lesson-tf-font);
  padding: var(--lesson-tf-pad-y) var(--lesson-tf-pad-x);
  text-align: center;
}

/* Fill in blank */
.fill {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
}
.fill input {
  flex: 1;
  min-width: 0;
  padding: var(--lesson-fill-pad-y) var(--lesson-fill-pad-x);
  border-radius: 14px;
  border: 2px solid var(--fq-border-strong);
  border-bottom-width: 4px;
  background: var(--fq-bg-elev);
  color: var(--fq-text);
  font-size: var(--lesson-fill-font);
  font-weight: 700;
  min-height: var(--fq-tap);
  outline: none;
}
.fill input:focus {
  border-color: var(--fq-teal);
}
.fill input.correct {
  border-color: var(--fq-accent);
  background: var(--fq-accent-soft);
  color: var(--fq-accent-dark);
}
.fill input.wrong {
  border-color: var(--fq-red);
  background: var(--fq-red-soft);
  color: var(--fq-red-dark);
}

/* Match */
.match {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--lesson-match-gap);
}
.match .choice {
  padding: var(--lesson-match-pad);
  font-size: var(--lesson-match-font);
  min-height: var(--lesson-match-min);
}
.match .choice[data-side="left"] { border-color: var(--fq-primary); }
.match .choice[data-side="right"] { border-color: var(--fq-purple); }
.match .choice[data-matched="true"] {
  background: var(--fq-accent-soft);
  border-color: var(--fq-accent);
  color: var(--fq-accent-dark);
  cursor: default;
  opacity: 0.85;
}
.match .choice[data-wrong="true"] {
  background: var(--fq-red-soft);
  border-color: var(--fq-red);
  color: var(--fq-red-dark);
  cursor: default;
  opacity: 0.9;
}
.match .choice[aria-pressed="true"] {
  background: var(--fq-primary-soft);
  border-color: var(--fq-teal);
}

/* ---------- Footer / Check bar ---------- */

.lesson__footer {
  position: sticky;
  bottom: 0;
  padding: var(--lesson-footer-pad-y) 16px calc(var(--lesson-footer-pad-y) + var(--fq-safe-bottom));
  background: var(--fq-bg);
  border-top: 1px solid var(--fq-border);
  z-index: 2;
}
.lesson__footer::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: -18px;
  height: 18px;
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--fq-fast) var(--fq-ease);
  background: linear-gradient(to bottom, rgba(11, 14, 26, 0), rgba(11, 14, 26, 0.16));
}
.lesson--scrollable .lesson__footer::before {
  opacity: 1;
}
.lesson__footer .btn { max-width: 720px; margin: 0 auto; display: flex; }
.local-save-status {
  margin: 8px auto 0;
  max-width: 720px;
  padding-inline: 8px;
  color: var(--fq-text-muted);
  font-size: 13px;
  line-height: 1.4;
  overflow-wrap: anywhere;
}

.result {
  padding: var(--lesson-result-pad-y) var(--lesson-result-pad-x);
  border-radius: var(--fq-radius-md);
  font-weight: 700;
  font-size: 14px;
  line-height: var(--lesson-result-line);
  margin: 0 auto;
  max-width: 720px;
}
.result--correct {
  background: var(--fq-accent-soft);
  color: var(--fq-accent-dark);
  border: 2px solid var(--fq-accent);
}
.result--wrong {
  background: var(--fq-red-soft);
  color: var(--fq-red-dark);
  border: 2px solid var(--fq-red);
}
.result strong {
  display: block;
  font-weight: 900;
  font-size: 15px;
  margin-bottom: 4px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

/* ---------- Complete screen ---------- */

.complete {
  min-height: 100dvh;
  display: grid;
  place-items: center;
  padding: 24px;
  padding-top: calc(24px + var(--fq-safe-top));
  padding-bottom: calc(24px + var(--fq-safe-bottom));
  background: linear-gradient(160deg, var(--fq-accent), #2E8B00);
  color: white;
  text-align: center;
}
.complete__inner {
  max-width: 480px;
  width: 100%;
  animation: pop var(--fq-med) var(--fq-ease);
}
.complete__badge {
  width: 132px;
  height: 132px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.15);
  display: grid;
  place-items: center;
  margin: 0 auto 20px;
  font-size: 56px;
  box-shadow: inset 0 -10px 0 rgba(0,0,0,0.1), 0 20px 60px rgba(0,0,0,0.25);
}
.complete__title {
  font-size: 30px;
  font-weight: 900;
  margin: 0 0 6px;
  letter-spacing: -0.02em;
}
.complete__subtitle {
  opacity: 0.9;
  font-size: 15px;
  margin-bottom: 22px;
}
.complete__stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  margin-bottom: 22px;
}
.complete__stat {
  background: rgba(255, 255, 255, 0.14);
  padding: 14px 8px;
  border-radius: 16px;
  backdrop-filter: blur(8px);
}
.complete__stat-label {
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  opacity: 0.85;
  font-weight: 800;
}
.complete__stat-value {
  font-size: 22px;
  font-weight: 900;
  margin-top: 4px;
}
.complete__actions {
  display: grid;
  gap: 10px;
}
.complete .btn--accent {
  --btn-bg: white;
  --btn-shadow: rgba(0,0,0,0.2);
  --btn-text: var(--fq-accent-dark);
}
.complete .btn--ghost {
  --btn-bg: transparent;
  --btn-text: white;
  border-color: rgba(255,255,255,0.4);
}
.complete .btn--ghost:hover {
  background: rgba(255,255,255,0.1);
}

/* ---------- Profile screen ---------- */

.profile__grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 12px;
  margin: 20px 0;
}
.stat-card {
  padding: 16px;
  border-radius: var(--fq-radius-lg);
  background: var(--fq-bg-elev);
  border: 2px solid var(--fq-border);
  text-align: left;
  /* #646 made the gem and battery cards <button>s. A button takes `buttontext`
     from the UA sheet instead of inheriting, so the 28px value rendered black
     on --fq-bg-elev in dark mode (1.52:1). Declaring the colour here keeps both
     the <div> and <button> forms theme-aware. */
  color: var(--fq-text);
}
.stat-card--action {
  cursor: pointer;
  border: 2px solid var(--fq-border);
}
.stat-card--action:hover {
  border-color: var(--fq-primary);
  transform: translateY(-1px);
}
.stat-card--action:focus-visible {
  outline: 2px solid var(--fq-primary);
  outline-offset: 2px;
}
.stat-card__label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--fq-text-muted);
  font-weight: 800;
}
.stat-card__value {
  font-size: 28px;
  font-weight: 900;
  margin-top: 6px;
}
.stat-card__hint {
  color: var(--fq-text-muted);
  font-size: 13px;
  margin-top: 2px;
}

.daily-reward {
  border: 2px solid var(--fq-border);
  border-radius: var(--fq-radius-lg);
  background: var(--fq-bg-elev);
  padding: 14px 16px;
  margin: 14px 0 18px;
}
.daily-reward__title {
  font-size: 16px;
  font-weight: 900;
}
.daily-reward__summary {
  margin-top: 4px;
  font-weight: 700;
}
.daily-reward__meta {
  margin-top: 5px;
  color: var(--fq-text-muted);
  font-size: 13px;
}
.daily-reward__actions {
  margin-top: 10px;
}

/* ---------- Earned daily reward screen (#311/#328/#329) ---------- */
.reward-screen {
  min-height: 100dvh;
  display: grid;
  place-items: center;
  padding: calc(20px + var(--fq-safe-top)) 20px calc(20px + var(--fq-safe-bottom));
  background:
    radial-gradient(circle at 50% 20%, rgba(255, 200, 0, 0.28), transparent 34%),
    linear-gradient(160deg, #7c3aed, #f97316 58%, #facc15);
  color: white;
  text-align: center;
}
.reward-card {
  width: min(460px, 100%);
  border-radius: 28px;
  padding: 24px;
  background: rgba(20, 20, 35, 0.34);
  border: 1px solid rgba(255, 255, 255, 0.28);
  box-shadow: 0 24px 70px rgba(0, 0, 0, 0.34);
  backdrop-filter: blur(10px);
  animation: pop var(--fq-med) var(--fq-ease);
}
.reward-card__eyebrow {
  font-size: 12px;
  font-weight: 900;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  opacity: 0.82;
}
.reward-card__title {
  margin: 8px 0 6px;
  font-size: clamp(30px, 8vw, 44px);
  line-height: 0.95;
  font-weight: 1000;
  letter-spacing: -0.04em;
}
.reward-card__copy {
  margin: 0 auto 18px;
  max-width: 32ch;
  color: rgba(255, 255, 255, 0.9);
  font-weight: 700;
}
.reward-mailbox {
  position: relative;
  width: 190px;
  height: 172px;
  margin: 0 auto;
  border: 0;
  background: transparent;
  color: inherit;
  cursor: pointer;
  isolation: isolate;
  transform-origin: 50% 85%;
}
.reward-mailbox:focus-visible {
  outline: 4px solid white;
  outline-offset: 8px;
  border-radius: 24px;
}
.reward-mailbox__emoji,
.reward-mailbox__letter {
  position: absolute;
  left: 50%;
  top: 50%;
  display: grid;
  place-items: center;
  width: 150px;
  height: 130px;
  font-size: 112px;
  line-height: 1;
  pointer-events: none;
  user-select: none;
  filter: drop-shadow(0 14px 18px rgba(0, 0, 0, 0.26));
  transform: translate(-50%, -50%);
  transition: opacity 180ms var(--fq-ease), transform 320ms cubic-bezier(0.34, 1.56, 0.64, 1);
  z-index: 2;
}
.reward-mailbox__emoji--open-raised,
.reward-mailbox__emoji--open-lowered,
.reward-mailbox__letter { opacity: 0; }
.reward-card--tap-1 .reward-mailbox__emoji--closed { opacity: 0; transform: translate(-50%, -50%) scale(0.84); }
.reward-card--tap-1 .reward-mailbox__emoji--open-raised { opacity: 1; transform: translate(-50%, -50%) scale(1); }
.reward-card--open .reward-mailbox__emoji--closed,
.reward-card--open .reward-mailbox__emoji--open-raised { opacity: 0; transform: translate(-50%, -50%) scale(0.84); }
.reward-card--open .reward-mailbox__emoji--open-lowered,
.reward-card--open .reward-mailbox__letter { opacity: 1; }
.reward-card--revealed .reward-mailbox__emoji,
.reward-card--revealed .reward-mailbox__letter,
.reward-card--revealed .reward-mailbox__glow {
  opacity: 0;
  transform: translate(-50%, -50%) scale(0.72);
}
/* The opened gift takes the mailbox's place on reveal (#454). Every other
   layer fades to zero, which left an empty hole exactly where the payoff
   should be. */
.reward-mailbox__gift {
  position: absolute;
  left: 50%;
  top: 50%;
  z-index: 5;
  font-size: 72px;
  line-height: 1;
  opacity: 0;
  transform: translate(-50%, -50%) scale(0.4);
  transition: opacity var(--fq-med) var(--fq-ease), transform var(--fq-med) var(--fq-ease);
  pointer-events: none;
}
.reward-card--revealed .reward-mailbox__gift {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
  animation: rewardGiftPop 620ms var(--fq-ease) 1;
}
@keyframes rewardGiftPop {
  0%   { transform: translate(-50%, -50%) scale(0.4) rotate(-8deg); }
  55%  { transform: translate(-50%, -50%) scale(1.12) rotate(4deg); }
  100% { transform: translate(-50%, -50%) scale(1) rotate(0deg); }
}
@media (prefers-reduced-motion: reduce) {
  .reward-mailbox__gift { transition: none; }
  .reward-card--revealed .reward-mailbox__gift { animation: none; }
}
.reward-mailbox__letter {
  left: 61%;
  top: 27%;
  width: 92px;
  height: 82px;
  font-size: 70px;
  z-index: 4;
  transform: translate(-50%, -50%) rotate(8deg) scale(0.82);
}
.reward-card--open .reward-mailbox__letter {
  animation: reward-letter-pop 520ms var(--fq-ease) both;
}
.reward-mailbox__glow {
  position: absolute;
  inset: 38px 30px 28px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.56);
  filter: blur(18px);
  opacity: 0;
  z-index: 1;
}
.reward-card--tap-1 .reward-mailbox,
.reward-card--tap-2 .reward-mailbox { animation: mailbox-hop 420ms var(--fq-ease); }
.reward-card--open .reward-mailbox__glow { opacity: 1; animation: reward-glow 900ms ease-out both; }
.reward-mailbox__kapow {
  position: absolute;
  left: 50%;
  top: 50%;
  z-index: 6;
  min-width: 150px;
  padding: 12px 18px;
  color: #431407;
  background: #facc15;
  border: 5px solid #dc2626;
  clip-path: polygon(50% 0%, 61% 15%, 79% 5%, 78% 24%, 100% 29%, 82% 43%, 97% 61%, 77% 65%, 75% 88%, 58% 77%, 45% 100%, 36% 78%, 14% 91%, 18% 68%, 0% 59%, 18% 44%, 2% 27%, 23% 24%, 24% 3%, 41% 14%);
  font-size: 20px;
  font-weight: 1000;
  letter-spacing: 0.04em;
  opacity: 0;
  pointer-events: none;
  transform: translate(-50%, -50%) scale(0.25) rotate(-8deg);
}
.reward-card--tap-1 .reward-mailbox__kapow,
.reward-card--open .reward-mailbox__kapow,
.reward-card--revealed .reward-mailbox__kapow {
  animation: reward-kapow 520ms var(--fq-ease) both;
}
@keyframes mailbox-hop {
  0%, 100% { transform: translate(0, 0) rotate(0); }
  30% { transform: translate(-7px, -10px) rotate(-4deg); }
  68% { transform: translate(8px, 2px) rotate(3deg); }
}
@keyframes reward-letter-pop {
  0% { transform: translate(-50%, -50%) rotate(8deg) scale(0.72); }
  55% { transform: translate(-50%, -50%) rotate(8deg) scale(1.08); }
  100% { transform: translate(-50%, -50%) rotate(8deg) scale(1); }
}
@keyframes reward-kapow {
  0% { opacity: 0; transform: translate(-50%, -50%) scale(0.25) rotate(-8deg); }
  25% { opacity: 1; transform: translate(-50%, -50%) scale(1.08) rotate(4deg); }
  72% { opacity: 1; transform: translate(-50%, -50%) scale(1) rotate(-2deg); }
  100% { opacity: 0; transform: translate(-50%, -50%) scale(1.2) rotate(0); }
}
@keyframes reward-glow {
  0% { transform: scale(0.4); opacity: 0; }
  40% { opacity: 1; }
  100% { transform: scale(1.35); opacity: 0.54; }
}
.reward-card__tapcount {
  margin-top: 8px;
  font-weight: 900;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.reward-prizes {
  display: grid;
  gap: 8px;
  margin: 14px 0;
}
.reward-prize {
  border-radius: 18px;
  background: rgba(255, 255, 255, 0.92);
  color: #431407;
  padding: 12px 14px;
  font-weight: 1000;
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.18);
  animation: pop var(--fq-med) var(--fq-ease);
}
.reward-prize--perfect { color: #4c1d95; }
.reward-streak {
  display: grid;
  gap: 3px;
  margin-top: 14px;
  color: rgba(255, 255, 255, 0.9);
}
.reward-streak strong {
  font-size: 18px;
  color: white;
}
.reward-streak__count {
  font-size: 13px;
  color: rgba(255, 255, 255, 0.78);
}
.reward-week {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 6px;
  margin: 14px 0 18px;
}
.reward-week__day {
  min-height: 54px;
  border-radius: 14px;
  display: grid;
  place-items: center;
  gap: 2px;
  background: rgba(255, 255, 255, 0.14);
  border: 1px solid rgba(255, 255, 255, 0.22);
}
.reward-week__label {
  font-size: 10px;
  font-weight: 900;
  opacity: 0.78;
  text-transform: uppercase;
}
.reward-week__day strong { font-size: 18px; }
.reward-week__day--done {
  background: rgba(255, 255, 255, 0.94);
  color: #92400e;
}
.reward-week__day--today {
  outline: 3px solid white;
  outline-offset: 2px;
}
.reward-card__actions {
  display: grid;
  grid-template-columns: 1fr;
  gap: 10px;
}
.reward-screen .btn--accent {
  --btn-bg: white;
  --btn-shadow: rgba(0, 0, 0, 0.2);
  --btn-text: #9a3412;
}
.reward-screen .btn--ghost {
  --btn-bg: transparent;
  --btn-text: white;
  border-color: rgba(255, 255, 255, 0.42);
}
@media (prefers-reduced-motion: reduce) {
  .reward-card,
  .reward-mailbox,
  .reward-card--tap-1 .reward-mailbox,
  .reward-card--tap-2 .reward-mailbox,
  .reward-card--open .reward-mailbox__letter,
  .reward-card--open .reward-mailbox__glow,
  .reward-mailbox__kapow {
    animation: none !important;
  }
  .reward-mailbox__kapow { opacity: 0; }
  /* The mailbox swap must still happen — it is the state change, not
     decoration — so only the motion is removed, not the transition itself. */
  .reward-mailbox__emoji,
  .reward-mailbox__letter {
    transition: opacity 120ms linear;
    transform: none !important;
  }
}

.settings {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 8px;
  margin-top: 20px;
}
.settings-row {
  display: flex;
  flex-wrap: wrap;
  min-width: 0;
  align-items: center;
  justify-content: space-between;
  padding: 14px 16px;
  background: var(--fq-bg-elev);
  border: 2px solid var(--fq-border);
  border-radius: var(--fq-radius-md);
  font-weight: 700;
  gap: 12px;
}
.settings-row .btn { width: auto; max-width: 100%; }
.settings-row--haptics { display: grid; grid-template-columns: 1fr auto; }
.haptics__actions { display: flex; flex-wrap: wrap; gap: 10px; }
.haptics__status {
  grid-column: 1 / -1;
  margin: 0;
  color: var(--fq-text-muted);
  font-size: 14px;
  font-weight: 400;
  line-height: 1.5;
}
@media (max-width: 480px) {
  .settings-row--haptics { grid-template-columns: 1fr; }
}

.authrow {
  display: flex;
  align-items: center;
  gap: 12px;
  justify-content: flex-end;
}
.authrow__name {
  font-weight: 700;
  color: var(--fq-text);
  max-width: 220px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 14px;
}

/* ---------- Bottom nav (optional) ---------- */

.bottomnav {
  position: sticky;
  bottom: 0;
  background: var(--fq-bg);
  border-top: 1px solid var(--fq-border);
  padding: 8px 0 calc(8px + var(--fq-safe-bottom));
  z-index: 4;
}
.bottomnav__inner {
  max-width: 720px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 4px;
  padding: 0 8px;
}
.nav-btn {
  background: transparent;
  border: none;
  color: var(--fq-text-muted);
  padding: 10px 4px;
  display: grid;
  place-items: center;
  gap: 2px;
  cursor: pointer;
  font-size: 12px;
  font-weight: 800;
  border-radius: 12px;
}
.nav-btn__icon {
  font-size: 22px;
}
.nav-btn[aria-current="page"] {
  color: var(--fq-primary-text);
  background: var(--fq-primary-soft);
}

/* ---------- Animations ---------- */

/* The wrapper .path__node already applies translateX(var(--offset)) to place
   the node on the serpentine path. --offset inherits, so repeating it here
   would apply the offset a SECOND time and throw the bobbing node off the
   path — up to 200px right at slot 2. That was invisible while the current
   node was always the first one (offset 0); it surfaced the moment the marker
   started advancing (#376). Bob is vertical only. */
@keyframes bob {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-6px); }
}
@keyframes fadeIn {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes pop {
  0%   { transform: scale(0.9); opacity: 0; }
  100% { transform: scale(1); opacity: 1; }
}
@keyframes shake {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-6px); }
  40% { transform: translateX(6px); }
  60% { transform: translateX(-4px); }
  80% { transform: translateX(4px); }
}
.shake { animation: shake 320ms ease-in-out; }

/* ---------- Utilities ---------- */

.mt-lg { margin-top: 24px; }
.mt-md { margin-top: 16px; }
.mt-sm { margin-top: 8px; }
.mb-md { margin-bottom: 16px; }
.center { text-align: center; }
.mono { font-variant-numeric: tabular-nums; }

/* ---------- Onboarding ---------- */

.onboarding {
  min-height: 100dvh;
  display: grid;
  place-items: center;
  padding: 24px;
  padding-top: calc(24px + var(--fq-safe-top));
  padding-bottom: calc(24px + var(--fq-safe-bottom));
  background:
    radial-gradient(70% 50% at 15% 10%, rgba(69, 230, 255, 0.20), transparent 65%),
    radial-gradient(65% 55% at 90% 90%, rgba(138, 107, 255, 0.30), transparent 70%),
    linear-gradient(180deg, var(--fq-space-warm), var(--fq-space));
  color: white;
  text-align: center;
}
.onboarding__inner {
  max-width: 480px;
  width: 100%;
  animation: fadeIn var(--fq-med) var(--fq-ease);
}
.onboarding__badge {
  width: 132px;
  height: 132px;
  border-radius: 32px;
  margin: 0 auto 24px;
  background: transparent;
  display: grid;
  place-items: center;
  overflow: visible;
}
.onboarding__badge svg {
  width: 100%;
  height: 100%;
  filter: drop-shadow(0 20px 40px rgba(138, 107, 255, 0.35));
}
.onboarding__title {
  font-size: 32px;
  font-weight: 900;
  letter-spacing: -0.02em;
  margin: 0 0 8px;
  text-shadow: 0 2px 12px rgba(0, 0, 0, 0.25);
}
.onboarding__subtitle {
  opacity: 0.88;
  margin-bottom: 28px;
  font-size: 16px;
  line-height: 1.55;
}
.onboarding .btn { color: var(--fq-text); }
.onboarding .btn--accent {
  --btn-bg: white;
  --btn-shadow: rgba(0,0,0,0.28);
  --btn-text: var(--fq-primary-dark);
  /* Explicit color to beat the `.onboarding .btn { color: var(--fq-text); }`
     rule above — otherwise the accent CTA label washes out to grey and
     becomes near-invisible against the white pill (issue #23). */
  color: var(--fq-primary-dark);
}
.onboarding .btn--ghost {
  color: white;
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.24);
}
.onboarding .btn--ghost:hover {
  background: rgba(255, 255, 255, 0.14);
}

/* ---------- Toast ---------- */

.toast {
  position: fixed;
  top: calc(20px + var(--fq-safe-top));
  left: 50%;
  transform: translateX(-50%);
  background: var(--fq-text);
  color: var(--fq-bg);
  padding: 10px 16px;
  border-radius: 999px;
  font-weight: 800;
  font-size: 13px;
  box-shadow: 0 8px 20px rgba(0,0,0,0.3);
  z-index: 100;
  opacity: 0;
  transition: opacity var(--fq-med), transform var(--fq-med);
  pointer-events: none;
}
.toast--visible {
  opacity: 1;
  transform: translateX(-50%) translateY(4px);
}

/* ---------- Learner-state recovery ---------- */

.learner-state-incident {
  position: fixed;
  z-index: 110;
  right: max(12px, var(--fq-safe-right));
  bottom: calc(76px + var(--fq-safe-bottom));
  left: max(12px, var(--fq-safe-left));
  max-width: 720px;
  margin: 0 auto;
  padding: 14px 16px;
  border: 2px solid var(--fq-orange-dark);
  border-radius: var(--fq-radius-md);
  background: var(--fq-bg-elev);
  color: var(--fq-text);
  box-shadow: var(--fq-shadow);
  font-size: 14px;
  line-height: 1.45;
}

.learner-state-incident .btn {
  display: block;
  width: auto;
  margin-top: 10px;
}

.learner-sync-status {
  position: fixed;
  z-index: 110;
  inset-inline: max(12px, var(--fq-safe-left)) max(12px, var(--fq-safe-right));
  bottom: calc(76px + var(--fq-safe-bottom));
  max-width: 640px;
  max-height: 48vh;
  overflow: auto;
  margin: 0 auto;
  padding: 12px 16px;
  border: 2px solid var(--fq-orange-dark);
  border-radius: var(--fq-radius-md);
  background: var(--fq-bg-elev);
  color: var(--fq-text);
  box-shadow: var(--fq-shadow);
  font-size: 14px;
  line-height: 1.45;
  overflow-wrap: anywhere;
}
.learner-sync-status p { margin: 4px 0 8px; }
.learner-sync-status summary {
  cursor: pointer;
  padding: 12px 0;
  min-height: 44px;
}
.learner-sync-status .btn { min-height: 44px; margin: 4px 8px 4px 0; }
.learner-sync-status summary:focus-visible {
  outline: 3px solid var(--fq-orange-dark);
  outline-offset: 2px;
}

/* ---------- Reduce motion ---------- */

@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

/* =============================================================
   v2 EXTENSIONS
   ============================================================= */

/* ---------- Path picker ---------- */
.page--picker { padding-top: 24px; }
.pathgrid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 14px;
}
@media (min-width: 720px) {
  .pathgrid { grid-template-columns: 1fr 1fr; }
}
.pathcard {
  position: relative;
  padding: 22px 20px 20px;
  border-radius: var(--fq-radius-xl);
  border: none;
  color: white;
  cursor: pointer;
  text-align: left;
  background: linear-gradient(160deg, var(--pc-color), var(--pc-color2));
  box-shadow: 0 8px 0 rgba(0,0,0,0.15), 0 12px 30px rgba(0,0,0,0.15);
  transition: transform var(--fq-fast) var(--fq-ease);
  overflow: hidden;
}
.pathcard::after {
  content: "";
  position: absolute;
  right: -40px; top: -40px;
  width: 180px; height: 180px;
  background: radial-gradient(circle at center, rgba(255,255,255,0.2), transparent 70%);
  pointer-events: none;
}
.pathcard:hover { transform: translateY(-2px); }
.pathcard:active { transform: translateY(4px); box-shadow: 0 4px 0 rgba(0,0,0,0.15), 0 6px 15px rgba(0,0,0,0.1); }
.pathcard--active { outline: 4px solid var(--fq-accent); outline-offset: 2px; }
.pathcard__badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 56px; height: 56px;
  border-radius: 16px;
  background: rgba(255,255,255,0.18);
  font-size: 28px;
  margin-bottom: 10px;
}
.pathcard__code {
  font-size: 11px;
  letter-spacing: 0.16em;
  opacity: 0.9;
  font-weight: 800;
  text-transform: uppercase;
}
.pathcard__title {
  font-size: 22px;
  font-weight: 900;
  margin: 4px 0 6px;
  letter-spacing: -0.01em;
}
.pathcard__sub { opacity: 0.9; font-weight: 600; font-size: 14px; line-height: 1.4; }
.pathcard__meta {
  margin-top: 12px;
  font-size: 12px;
  letter-spacing: 0.08em;
  opacity: 0.85;
  font-weight: 800;
  text-transform: uppercase;
}
.pathcard__progress {
  margin-top: 10px;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 6px 12px;
  background: rgba(255,255,255,0.18);
  border-radius: 999px;
  font-weight: 900;
  font-size: 13px;
}
.pathcard__progress--new { background: rgba(0,0,0,0.25); }

/* ---------- Study sections ---------- */
.sec-text { line-height: 1.6; font-size: 15.5px; }
.sec-text p { margin: 0 0 12px; }
.sec-text p:last-child { margin: 0; }
.sec-text strong { color: var(--fq-primary); }
.sec-text code {
  background: var(--fq-bg-alt);
  padding: 2px 6px;
  border-radius: 6px;
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  font-size: 0.9em;
}

.callout {
  padding: 14px 16px;
  border-radius: var(--fq-radius-md);
  border-left: 4px solid var(--fq-primary);
  background: var(--fq-primary-soft);
}
.callout__title {
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  font-weight: 900;
  margin-bottom: 4px;
  color: var(--fq-primary);
}
.callout__body { font-size: 14.5px; line-height: 1.5; }
.callout--tip     { border-left-color: var(--fq-accent);    background: var(--fq-accent-soft); }
.callout--tip .callout__title { color: var(--fq-accent-dark); }
.callout--warn    { border-left-color: var(--fq-orange);    background: rgba(255, 150, 0, 0.1); }
.callout--warn .callout__title { color: var(--fq-orange-dark); }
.callout--success { border-left-color: var(--fq-accent);    background: var(--fq-accent-soft); }
.callout--success .callout__title { color: var(--fq-accent-dark); }

.sec-table {
  overflow-x: auto;
  border-radius: var(--fq-radius-md);
  border: 2px solid var(--fq-border);
  background: var(--fq-bg-elev);
}
.sec-table table {
  border-collapse: collapse;
  width: 100%;
  font-size: 14px;
}
.sec-table th, .sec-table td {
  padding: 10px 12px;
  text-align: left;
  border-bottom: 1px solid var(--fq-border);
}
.sec-table th {
  background: var(--fq-bg-alt);
  font-weight: 900;
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--fq-text-muted);
}
.sec-table tr:last-child td { border-bottom: none; }

.compare {
  display: grid;
  grid-template-columns: 1fr;
  gap: 12px;
}
@media (min-width: 560px) {
  .compare { grid-template-columns: 1fr 1fr; }
}
.compare__card {
  padding: 14px 16px;
  border-radius: var(--fq-radius-md);
  border: 2px solid var(--fq-border);
  background: var(--fq-bg-elev);
}
.compare__title {
  margin: 0 0 8px;
  font-size: 15px;
  font-weight: 900;
  color: var(--fq-primary);
}
.compare__card ul { margin: 0; padding-left: 20px; font-size: 14.5px; line-height: 1.55; }
.compare__card li { margin-bottom: 4px; }

.keyterms {
  padding: 14px 16px;
  border-radius: var(--fq-radius-md);
  background: var(--fq-bg-alt);
  margin: 0;
}
.keyterms dt {
  font-weight: 900;
  color: var(--fq-primary);
  margin-top: 8px;
  font-size: 15px;
}
.keyterms dt:first-child { margin-top: 0; }
.keyterms dd { margin: 2px 0 6px; font-size: 14.5px; line-height: 1.5; }

.statblock {
  text-align: center;
  padding: 18px 12px;
  border-radius: var(--fq-radius-md);
  background: linear-gradient(160deg, var(--fq-primary), var(--fq-primary-dark));
  color: white;
}
.statblock__value {
  font-size: 44px;
  font-weight: 900;
  letter-spacing: -0.02em;
  line-height: 1;
}
.statblock__label { font-weight: 800; margin-top: 4px; font-size: 14px; }
.statblock__desc { opacity: 0.85; margin-top: 6px; font-size: 13px; }

.sec-diagram {
  margin: 0;
  padding: 14px;
  border-radius: var(--fq-radius-md);
  border: 2px solid var(--fq-border);
  background: var(--fq-bg-elev);
}
.sec-diagram__svg { display: block; width: 100%; }
.sec-diagram__svg svg { display: block; max-width: 100%; height: auto; }
.sec-diagram figcaption {
  margin-top: 8px;
  font-size: 13px;
  color: var(--fq-text-muted);
  text-align: center;
  font-weight: 700;
}

/* ---------- Study progress bar variant ---------- */
.progress__fill--study { background: var(--fq-teal); }

.lesson__meta {
  font-size: 11px;
  font-weight: 900;
  letter-spacing: 0.14em;
  padding: 6px 10px;
  background: var(--fq-bg-alt);
  color: var(--fq-text-muted);
  border-radius: 999px;
  flex: 0 0 auto;
}
/* #395: the study topbar puts the progress bar directly in the flex row (the
   question topbar wraps it in .lesson__meter, which already has flex: 1). Now
   that a battery chip sits alongside the STUDY pill, the bar needs to claim
   the remaining space explicitly or it collapses to its content width. */
.lesson__topbar > .progress {
  flex: 1;
  min-width: 0;
}
.lesson__topbar > .hearts { flex: 0 0 auto; }


/* ---------- Glossary terms (#137) ---------- */
/* A <button> rather than a styled <span> so the affordance is keyboard
   reachable and tappable without extra JS — :focus-within drives the popup on
   touch, so mobile is never hover-dependent. */
.gloss {
  position: relative;
  display: inline;
  padding: 0;
  margin: 0;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  text-decoration: underline dashed currentColor 1px;
  text-underline-offset: 3px;
  cursor: help;
}
.gloss:hover,
.gloss:focus-visible {
  text-decoration-style: solid;
  color: var(--fq-primary);
}
.gloss:focus-visible {
  outline: 2px solid var(--fq-primary);
  outline-offset: 2px;
  border-radius: 3px;
}

.gloss__pop {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 8px);
  transform: translateX(calc(-50% + var(--gloss-shift, 0px))) translateY(4px);
  z-index: 40;
  width: max-content;
  max-width: min(280px, 78vw);
  padding: 10px 12px;
  border-radius: 10px;
  background: #1e2437;
  color: #fff;
  font-size: 13px;
  font-weight: 500;
  line-height: 1.45;
  text-align: left;
  text-decoration: none;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.22);
  opacity: 0;
  visibility: hidden;
  transition: opacity 140ms ease, transform 140ms ease;
  pointer-events: none;
}
/* The arrow counter-shifts so it keeps pointing at the term even when the
   bubble has been nudged away from a viewport edge. */
.gloss__pop::after {
  content: "";
  position: absolute;
  top: 100%;
  left: calc(50% - var(--gloss-shift, 0px));
  transform: translateX(-50%);
  border: 6px solid transparent;
  border-top-color: #1e2437;
}
.gloss:hover .gloss__pop,
.gloss:focus .gloss__pop,
.gloss:focus-visible .gloss__pop {
  opacity: 1;
  visibility: visible;
  transform: translateX(calc(-50% + var(--gloss-shift, 0px))) translateY(0);
}

/* Near the viewport edge the centered popup would clip; nudge it inward. */
@media (max-width: 420px) {
  .gloss__pop { max-width: 72vw; }
}

@media (prefers-reduced-motion: reduce) {
  .gloss__pop { transition: none; }
}

/* ---------- Hint ---------- */
.hintbtn {
  align-self: flex-start;
  background: var(--fq-purple);
  color: white;
  border: none;
  padding: 10px 14px;
  border-radius: 12px;
  font-weight: 800;
  font-size: 13px;
  cursor: pointer;
  box-shadow: 0 3px 0 var(--fq-purple-dark);
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.hintbtn:active { transform: translateY(3px); box-shadow: 0 0 0 var(--fq-purple-dark); }
.hintbox {
  padding: var(--lesson-hintbox-pad-y) var(--lesson-hintbox-pad-x);
  border-radius: var(--fq-radius-md);
  background: rgba(206, 130, 255, 0.15);
  border-left: 4px solid var(--fq-purple);
  font-size: 14.5px;
  line-height: 1.4;
}

/* ---------- Multi-select ---------- */
.choices--multi .choice {
  gap: 12px;
}
.check-badge {
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border-radius: 8px;
  border: 2px solid var(--fq-border-strong);
  background: var(--fq-bg-alt);
  color: var(--fq-accent-dark);
  font-weight: 900;
  flex-shrink: 0;
}
.choice--picked { border-color: var(--fq-teal); background: var(--fq-primary-soft); }
.choice--picked .check-badge {
  border-color: var(--fq-teal);
  background: var(--fq-teal);
  color: white;
}

/* ---------- Order (reorder) ---------- */
.orderlist {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--lesson-order-gap);
}
.orderrow {
  display: grid;
  grid-template-columns: 32px 1fr auto;
  align-items: center;
  gap: 10px;
  padding: var(--lesson-order-pad-y) var(--lesson-order-pad-x);
  border: 2px solid var(--fq-border-strong);
  border-bottom-width: 4px;
  border-radius: 14px;
  background: var(--fq-bg-elev);
}
.orderrow__num {
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border-radius: 8px;
  background: var(--fq-primary);
  color: white;
  font-weight: 900;
}
.orderrow__text { font-weight: 700; font-size: var(--lesson-order-text); line-height: 1.4; }
.orderrow__ctrl { display: flex; gap: 4px; }
.orderctl {
  width: 32px;
  height: 32px;
  border: 2px solid var(--fq-border-strong);
  border-radius: 8px;
  background: var(--fq-bg-alt);
  cursor: pointer;
  font-weight: 900;
}
.orderctl:disabled { opacity: 0.35; cursor: not-allowed; }
.orderrow--correct { border-color: var(--fq-accent); background: var(--fq-accent-soft); }
.orderrow--wrong   { border-color: var(--fq-red);    background: var(--fq-red-soft); }

/* ---------- Categorize ---------- */
.catwrap { display: flex; flex-direction: column; gap: var(--lesson-cat-gap); }
.catbank {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding: 12px;
  border: 2px dashed var(--fq-border-strong);
  border-radius: var(--fq-radius-md);
  background: var(--fq-bg-alt);
  min-height: 60px;
}
.chip {
  padding: var(--lesson-chip-pad-y) var(--lesson-chip-pad-x);
  border-radius: 12px;
  border: 2px solid var(--fq-primary);
  background: var(--fq-bg-elev);
  color: var(--fq-text);
  font-weight: 700;
  font-size: var(--lesson-chip-font);
  cursor: pointer;
}
.chip[aria-pressed="true"] {
  background: var(--fq-primary);
  color: white;
  box-shadow: 0 3px 0 var(--fq-primary-dark);
}
.chip--placed { border-color: var(--fq-teal); background: var(--fq-primary-soft); }
.chip--correct { border-color: var(--fq-accent); background: var(--fq-accent-soft); color: var(--fq-accent-dark); }
.chip--wrong   { border-color: var(--fq-red);    background: var(--fq-red-soft);    color: var(--fq-red-dark); }

.catbuckets {
  display: grid;
  grid-template-columns: 1fr;
  gap: 10px;
}
@media (min-width: 560px) {
  .catbuckets { grid-template-columns: 1fr 1fr; }
}
.bucket {
  padding: 12px;
  border-radius: var(--fq-radius-md);
  border: 2px solid var(--fq-primary);
  background: var(--fq-bg-elev);
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.bucket__label {
  font-weight: 900;
  font-size: 13px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fq-primary);
}
.bucket__body {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  min-height: 32px;
}
.bucket__hint {
  padding: 6px 10px;
  border-radius: 8px;
  border: 2px dashed var(--fq-border-strong);
  color: var(--fq-text-muted);
  font-weight: 800;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  opacity: 0.7;
}

/* #299: keyboard placement control. Rendered only while a chip is selected,
   so it is present exactly when it is actionable and the tab order stays
   short the rest of the time. This is the only path a keyboard user has to
   place an item — the drop targets are pointer-only <div>s. */
.bucket__place {
  align-self: flex-start;
  padding: 6px 12px;
  border-radius: 999px;
  border: 2px solid var(--fq-primary);
  background: var(--fq-primary-soft);
  color: var(--fq-primary-text);
  font-weight: 800;
  font-size: 12px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  cursor: pointer;
}
.bucket__place:hover { background: var(--fq-primary); color: white; }
.bucket__place:focus-visible {
  outline: 3px solid var(--fq-primary);
  outline-offset: 2px;
}
.catwrap__help {
  font-size: 13px;
  margin-bottom: 2px;
}

/* Chip states */
.chip {
  touch-action: none;   /* prevent scroll during drag on touch */
  cursor: grab;
  transition: transform 120ms var(--fq-ease), box-shadow 120ms var(--fq-ease);
}
.chip:active { cursor: grabbing; }
.chip:disabled { cursor: default; }
.chip--selected {
  background: var(--fq-primary);
  color: white;
  box-shadow: 0 4px 0 var(--fq-primary-dark);
  transform: translateY(-1px);
}

/* Bank / bucket become obvious drop targets while dragging */
body.dragging .catbank,
body.dragging .bucket {
  transition: border-color 120ms, background 120ms, transform 120ms;
}
body.dragging .catbank { border-style: solid; border-color: var(--fq-teal); }
body.dragging .bucket  { border-color: var(--fq-teal); background: var(--fq-primary-soft); }

.drop-target--hover {
  border-color: var(--fq-accent) !important;
  background: var(--fq-accent-soft) !important;
  transform: scale(1.02);
  box-shadow: 0 6px 20px rgba(88, 204, 2, 0.25);
}

/* Ghost element (clone floating with pointer) */
.drag-ghost {
  cursor: grabbing !important;
  will-change: transform, left, top;
  transform: rotate(-3deg) scale(1.06);
  box-shadow: 0 14px 40px rgba(0, 0, 0, 0.35);
  opacity: 0.94;
  filter: brightness(1.05);
}
.drag-source {
  opacity: 0.35;
  transform: scale(0.96);
  pointer-events: none;
}

/* Global drag state */
body.dragging {
  cursor: grabbing;
  -webkit-user-select: none;
  user-select: none;
  touch-action: none;
}
body.dragging * { cursor: grabbing !important; }

/* Order-row drag handle */
.orderrow {
  grid-template-columns: 24px 32px 1fr auto !important;
  touch-action: none;
  cursor: grab;
  user-select: none;
}
.orderrow:active { cursor: grabbing; }
.orderrow.drag-source { opacity: 0.35; }
.orderrow__grab {
  display: grid;
  place-items: center;
  height: 32px;
  color: var(--fq-text-muted);
  font-weight: 900;
  font-size: 16px;
  letter-spacing: -2px;
  border-radius: 6px;
  background: var(--fq-bg-alt);
  user-select: none;
  pointer-events: none;
}
.orderrow[data-orderidx] { transition: background 120ms, transform 120ms; }
.orderrow.drop-target--hover {
  background: var(--fq-accent-soft) !important;
  box-shadow: inset 0 3px 0 var(--fq-accent), inset 0 -3px 0 var(--fq-accent);
  transform: scale(1.02);
}
.orderctl {
  touch-action: manipulation;
}
body.dragging .orderrow { transition: transform 180ms cubic-bezier(0.2, 0.8, 0.2, 1); }
.orderwrap { position: relative; }
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ---------- Hotspot ---------- */
.hotspot {
  margin: 0;
  padding: 12px;
  border-radius: var(--fq-radius-md);
  background: var(--fq-bg-elev);
  border: 2px solid var(--fq-border);
}
.hotspot__svg svg { width: 100%; height: auto; display: block; }
.hotspot__spot {
  fill: var(--fq-primary);
  fill-opacity: 0.5;
  stroke: white;
  stroke-width: 2;
  cursor: pointer;
  transition: fill-opacity 120ms;
}
.hotspot__spot:hover { fill-opacity: 0.75; }
.hotspot__spot--picked  { fill: var(--fq-teal); fill-opacity: 0.85; }
.hotspot__spot--correct { fill: var(--fq-accent); fill-opacity: 0.85; }
.hotspot__spot--wrong   { fill: var(--fq-red);    fill-opacity: 0.85; }
.hotspot__caption {
  margin-top: 8px;
  text-align: center;
  font-weight: 800;
  color: var(--fq-text-muted);
  font-size: 13px;
}

/* ---------- Scenario ---------- */
/* #675: these were hardcoded, so the ONE question type that needs the fit
   ladder most was the one type that could not use it. A case study stacks a
   setup card, a step meta row, a prompt, the answer and a footer, and the
   setup card alone measured a fixed 188px on every phone. The ladder burned
   all the way to tier 3/4 compressing everything else and still could not
   touch it. These tokens put the scenario on the same ladder. The text floor
   stays at 14px, matching --lesson-choice-font. */
.scenario {
  display: flex;
  flex-direction: column;
  gap: var(--lesson-scenario-gap);
}
.scenario__setup {
  padding: var(--lesson-scenario-pad-y) var(--lesson-scenario-pad-x);
  border-radius: var(--fq-radius-md);
  background: var(--fq-bg-alt);
  border-left: 4px solid var(--fq-purple);
  font-size: var(--lesson-scenario-font);
  line-height: var(--lesson-scenario-line);
}
/* The reported whitespace (#675), and the largest single win here. The setup
   text is wrapped in a <p>, which carries the browser default margin: 1em 0 —
   about 28px of dead space INSIDE the card, above and below the text, on top
   of the card's own padding. Nothing in the stylesheet reset it. */
.scenario__setup p { margin: 0; }
.scenario__setup p + p { margin-top: 0.6em; }
/* One row rather than two stacked ones. "Step 2 of 3" and "Choose the best
   answer" are both short, and neither needs a line of its own on a phone. */
.scenario__stepmeta {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 8px;
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--fq-purple-dark);
  font-weight: 800;
}
.scenario__stepmeta::before { content: none; }
.scenario__stepkind {
  color: var(--fq-text-muted);
  font-weight: 700;
}
.scenario__stepkind::before {
  content: "·";
  margin-right: 8px;
  color: var(--fq-purple-dark);
}
.scenario__footer { margin-top: var(--lesson-scenario-footer-gap); }
.scenario__footer .btn { max-width: none; }

/* ---------- Complete + confetti + levelup ---------- */
.confetti {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 5;
}
.complete__inner { position: relative; z-index: 6; }
.levelup {
  margin: 10px auto 18px;
  display: inline-block;
  padding: 8px 14px;
  border-radius: 999px;
  background: rgba(255,255,255,0.2);
  color: white;
  font-weight: 900;
  letter-spacing: 0.08em;
  font-size: 13px;
  animation: pop 420ms var(--fq-ease);
}
.newbadges {
  margin: 12px 0 22px;
  padding: 12px;
  background: rgba(255,255,255,0.14);
  border-radius: 16px;
  backdrop-filter: blur(8px);
}
.newbadges__label {
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-weight: 900;
  margin-bottom: 8px;
}
.newbadges__row {
  display: flex;
  gap: 10px;
  justify-content: center;
  flex-wrap: wrap;
}
/* With the award reason now shown (#456) a badge is much taller than an
   icon-and-title chip, so cap the width and stack them on narrow screens
   rather than letting two badges squeeze into one row. */
.newbadges__row .badge { max-width: 280px; }
@media (max-width: 480px) {
  .newbadges__row { flex-direction: column; align-items: center; }
  .newbadges__row .badge { width: 100%; max-width: 340px; }
}

/* ---------- Badges ---------- */
.badgesec { margin: 22px 0; }
.badgesec__title {
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--fq-text-muted);
  font-weight: 900;
  margin: 0 0 10px;
}
.badgegrid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 10px;
}
.badge {
  padding: 14px 10px;
  border-radius: 16px;
  border: 2px solid var(--fq-border);
  background: var(--fq-bg-elev);
  text-align: center;
  position: relative;
}
.badge__icon {
  font-size: 32px;
  margin-bottom: 4px;
  line-height: 1;
}
.badge__title { font-weight: 900; font-size: 13.5px; }
.badge__desc { font-size: 12px; color: var(--fq-text-muted); margin-top: 4px; line-height: 1.35; }
.badge__xp {
  margin-top: 6px;
  display: inline-block;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--fq-yellow);
  color: var(--fq-yellow-dark);
  font-size: 11px;
  font-weight: 900;
}
.badge__earned {
  margin-top: 6px;
  color: var(--fq-text-muted);
  font-size: 11px;
  font-weight: 700;
  line-height: 1.25;
}
.badge--bronze  { border-color: #C08052; }
.badge--silver  { border-color: #A8B4BE; }
.badge--gold    { border-color: #F1B900; }
.badge--platinum{ border-color: var(--fq-purple); box-shadow: 0 0 0 3px rgba(206,130,255,0.15); }
.badge--locked { filter: grayscale(1) opacity(0.5); }

/* ---------- Level bar ---------- */
.levelbar {
  position: relative;
  height: 24px;
  background: var(--fq-bg-alt);
  border-radius: 999px;
  overflow: hidden;
  border: 1px solid var(--fq-border);
  margin: 4px 0 16px;
}
.levelbar__fill {
  position: absolute;
  inset: 0 auto 0 0;
  background: linear-gradient(90deg, var(--fq-primary), var(--fq-purple));
  transition: width var(--fq-med) var(--fq-ease);
}
.levelbar__label {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-weight: 900;
  font-size: 12px;
  color: var(--fq-text);
  mix-blend-mode: difference;
  color: white;
}

/* ---------- Improve intro on lesson (was inline) ---------- */
.lesson__intro {
  padding: 12px 14px;
  border-radius: var(--fq-radius-md);
  background: var(--fq-primary-soft);
  color: var(--fq-text);
  font-size: 14.5px;
  border-left: 4px solid var(--fq-primary);
  margin: 0;
  line-height: 1.5;
}


/* =============================================================
   CHARACTER CELEBRATIONS
   ============================================================= */

/* ---------- Pop-up (during a lesson) ---------- */
.chr {
  position: fixed;
  z-index: 200;
  pointer-events: none;
  will-change: transform, opacity;
  --chr-size: 96px;
}
.chr--pop {
  right: max(12px, env(safe-area-inset-right));
  bottom: calc(120px + env(safe-area-inset-bottom));
  display: flex;
  align-items: flex-end;
  gap: 10px;
  opacity: 0;
  transform: translateY(20px) scale(0.8);
  transition: opacity 220ms var(--fq-ease), transform 320ms cubic-bezier(0.34, 1.56, 0.64, 1);
}
.chr--pop.chr--visible {
  opacity: 1;
  transform: translateY(0) scale(1);
}
@media (max-width: 520px) {
  .chr--pop {
    left: 12px;
    right: 12px;
    bottom: calc(96px + env(safe-area-inset-bottom));
    justify-content: center;
    --chr-size: 78px;
  }
}

.chr__portrait {
  width: var(--chr-size);
  height: var(--chr-size);
  border-radius: 50%;
  overflow: hidden;
  box-shadow: 0 12px 30px rgba(0, 0, 0, 0.35),
              inset 0 -6px 0 rgba(0, 0, 0, 0.15);
  border: 3px solid #fff;
  flex-shrink: 0;
  position: relative;
  animation: chrBob 1.4s ease-in-out infinite;
}
.chr__portrait svg { display: block; width: 100%; height: 100%; }

.chr__bubble {
  background: #fff;
  color: #3C3C3C;
  border-radius: 18px 18px 18px 4px;
  padding: 10px 14px;
  max-width: 220px;
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.25);
  border: 2px solid var(--fq-border);
  position: relative;
  font-weight: 700;
  font-size: 13.5px;
  line-height: 1.35;
  animation: chrBubblePop 320ms cubic-bezier(0.34, 1.56, 0.64, 1) 100ms both;
}
.chr__bubble::after {
  content: "";
  position: absolute;
  left: -8px;
  bottom: 6px;
  border: 8px solid transparent;
  border-right-color: #fff;
  border-left: 0;
  filter: drop-shadow(-2px 0 0 var(--fq-border));
}
.chr__name {
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--fq-primary);
  font-weight: 900;
  margin-bottom: 2px;
}

/* Sparkle particles orbiting the character */
.chr__particles {
  position: absolute;
  inset: 0;
  pointer-events: none;
  width: var(--chr-size);
  height: var(--chr-size);
}
.chr__particle {
  position: absolute;
  left: 50%;
  top: 50%;
  color: var(--fq-yellow);
  font-size: 18px;
  animation: chrParticle 1.6s ease-out infinite;
  animation-delay: calc(var(--i, 0) * 0.15s);
  transform-origin: 0 0;
  filter: drop-shadow(0 2px 4px rgba(0,0,0,0.3));
}
.chr__particle:nth-child(1) { color: var(--fq-yellow); }
.chr__particle:nth-child(2) { color: var(--fq-orange); font-size: 16px; }
.chr__particle:nth-child(3) { color: #fff; }
.chr__particle:nth-child(4) { color: var(--fq-teal); font-size: 14px; }
.chr__particle:nth-child(5) { color: var(--fq-pink); }
.chr__particle:nth-child(6) { color: var(--fq-accent); font-size: 15px; }

@keyframes chrParticle {
  0%   { transform: rotate(calc(var(--i, 0) * 60deg)) translateX(0) scale(0); opacity: 0; }
  25%  { opacity: 1; }
  100% { transform: rotate(calc(var(--i, 0) * 60deg)) translateX(60px) scale(1); opacity: 0; }
}
@keyframes chrBob {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-4px); }
}
@keyframes chrBubblePop {
  0%   { opacity: 0; transform: scale(0.6) translateY(8px); }
  60%  { opacity: 1; transform: scale(1.05) translateY(0); }
  100% { transform: scale(1); }
}

/* ---------- Hero character on lesson-complete screen ---------- */
.complete__character {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  margin-bottom: 16px;
  animation: chrHeroIn 600ms cubic-bezier(0.34, 1.56, 0.64, 1);
}
.complete__portrait {
  width: 152px;
  height: 152px;
  border-radius: 50%;
  overflow: hidden;
  border: 4px solid rgba(255, 255, 255, 0.9);
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.35),
              inset 0 -12px 0 rgba(0, 0, 0, 0.15);
  animation: chrHeroBob 1.8s ease-in-out infinite;
}
.complete__portrait svg { display: block; width: 100%; height: 100%; }
.complete__speech {
  background: #fff;
  color: #3C3C3C;
  padding: 12px 18px;
  border-radius: 18px;
  max-width: 320px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.28);
  position: relative;
}
.complete__speech::before {
  content: "";
  position: absolute;
  top: -10px;
  left: 50%;
  transform: translateX(-50%);
  width: 0;
  height: 0;
  border-left: 10px solid transparent;
  border-right: 10px solid transparent;
  border-bottom: 12px solid #fff;
}
.complete__speech-name {
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--fq-primary);
  font-weight: 900;
  margin-bottom: 2px;
}
.complete__speech-msg { font-weight: 800; font-size: 15px; line-height: 1.35; }

@keyframes chrHeroIn {
  0%   { opacity: 0; transform: scale(0.5) rotate(-8deg); }
  60%  { opacity: 1; transform: scale(1.08) rotate(2deg); }
  100% { opacity: 1; transform: scale(1) rotate(0); }
}
@keyframes chrHeroBob {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-6px); }
}

/* Reduced motion: kill the bouncing but keep the appearance */
@media (prefers-reduced-motion: reduce) {
  .chr__portrait, .complete__portrait { animation: none !important; }
  .chr__particle { animation-duration: 0.01ms !important; }
}


/* ---------- Feedback surfaces (Strategy §5.1) ---------- */
.feedback {
  display: flex;
  align-items: center;
  gap: 4px;
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px dashed var(--fq-border-strong);
  flex-wrap: wrap;
}
.feedback__label {
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--fq-text-muted);
  margin-right: 0;
  white-space: nowrap;
}
.feedback__btn {
  padding: 4px 8px;
  min-height: 32px;
  border: 2px solid var(--fq-border-strong);
  border-radius: 10px;
  background: var(--fq-bg-elev);
  color: var(--fq-text);
  font-weight: 800;
  font-size: 14px;
  cursor: pointer;
  transition: transform 100ms var(--fq-ease), border-color 100ms;
}
.feedback__btn:hover:not(:disabled) { border-color: var(--fq-teal); transform: translateY(-1px); }
.feedback__btn:active:not(:disabled) { transform: translateY(1px); }
.feedback__btn:disabled { opacity: 0.55; cursor: default; }
.feedback__btn--on {
  background: var(--fq-accent-soft);
  border-color: var(--fq-accent);
}
.feedback__btn--report {
  font-size: 11px;
  padding: 4px 9px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.feedback__thanks {
  margin-left: 4px;
  font-size: 13px;
  font-weight: 800;
  color: var(--fq-accent-dark);
}
.lesson[data-fit-tier="4"] .feedback {
  flex-wrap: nowrap;
}
.lesson[data-fit-tier="4"] .feedback__label,
.lesson[data-fit-tier="4"] .feedback__thanks {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --- post-lesson badge + retry flow (#486, #487) ------------------------- */
.postlesson {
  min-height: 100dvh;
  display: grid;
  place-items: center;
  padding: 28px 18px;
  background:
    radial-gradient(circle at 50% 10%, color-mix(in srgb, var(--fq-primary) 22%, transparent), transparent 42%),
    var(--fq-bg);
  color: var(--fq-text);
}
.postlesson__inner {
  width: min(620px, 100%);
  text-align: center;
}
.postlesson__eyebrow {
  color: var(--fq-primary-text);
  font-size: 12px;
  font-weight: 900;
  letter-spacing: 0.09em;
  text-transform: uppercase;
}
.postlesson__title {
  margin: 8px 0 10px;
  font-size: clamp(30px, 8vw, 48px);
  line-height: 1.05;
}
.postlesson__copy {
  max-width: 520px;
  margin: 0 auto 14px;
  color: var(--fq-text-muted);
  font-size: 17px;
  line-height: 1.5;
}
.postlesson__count,
.postlesson__reassurance {
  margin: 8px 0;
  font-weight: 700;
}
.postlesson__reassurance {
  color: var(--fq-text-muted);
  font-size: 13px;
}
.postlesson__actions {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 22px;
}
.postlesson__retry-icon {
  width: 88px;
  height: 88px;
  margin: 0 auto 12px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--fq-primary-soft);
  color: var(--fq-primary-text);
  font-size: 50px;
  font-weight: 900;
}
.retry-policy {
  margin: 0 16px;
  padding: 7px 10px;
  border: 1px solid color-mix(in srgb, var(--fq-primary) 45%, var(--fq-border));
  border-radius: 10px;
  background: color-mix(in srgb, var(--fq-primary) 10%, var(--fq-bg-alt));
  color: var(--fq-text);
  text-align: center;
  font-size: 12px;
  font-weight: 800;
}
.retry-policy--offer {
  width: fit-content;
  max-width: 100%;
  margin: 14px auto;
  padding: 10px 14px;
}

.badge-award__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 12px;
  margin-top: 24px;
}
.badge-award {
  padding: 20px 14px;
  border: 2px solid var(--fq-border);
  border-radius: 18px;
  background: var(--fq-bg-elev);
  box-shadow: 0 14px 34px color-mix(in srgb, var(--fq-primary) 14%, transparent);
}
.badge-award--platinum { border-color: #8a6bff; }
.badge-award--gold { border-color: var(--fq-yellow-dark); }
.badge-award--silver { border-color: #9aa7b8; }
.badge-award--bronze { border-color: #b9793f; }
.badge-award__icon { font-size: 58px; line-height: 1; }
.badge-award__title { margin: 10px 0 4px; font-size: 22px; }
.badge-award__reason {
  margin: 0;
  min-height: 2.7em;
  color: var(--fq-text-muted);
  font-size: 14px;
  line-height: 1.35;
}
.badge-award__xp {
  display: inline-block;
  margin-top: 12px;
  padding: 3px 9px;
  border-radius: 999px;
  background: var(--fq-yellow);
  color: var(--fq-yellow-dark);
  font-size: 12px;
  font-weight: 900;
}

@media (max-width: 480px) {
  .postlesson { padding: 22px 14px; }
  .postlesson__actions { flex-direction: column; }
  .postlesson__actions .btn { width: 100%; }
}
@media (prefers-reduced-motion: reduce) {
  .badge-award { box-shadow: none; }
}
/* Account bootstrap contains no learner projection. */
.account-status {
  max-width: 36rem;
  margin: 12vh auto;
  padding: 1.5rem;
  overflow-wrap: anywhere;
}
.account-status .btn { margin: .5rem .5rem .5rem 0; }
.account-notice {
  margin: 1rem auto;
  padding: 1rem;
  max-width: 40rem;
  overflow-wrap: anywhere;
}

/* #792: a run closed because the learner kept learning elsewhere. */
.stale-run-notice {
  display: block;
  margin: 12px auto;
  padding: 12px 16px;
  max-width: 40rem;
  border: 2px solid var(--fq-orange-dark);
  border-radius: var(--fq-radius-md);
  background: var(--fq-bg-elev);
  color: var(--fq-text);
  font-size: 14px;
  line-height: 1.45;
  overflow-wrap: anywhere;
}
.stale-run-notice .btn {
  display: block;
  width: auto;
  margin-top: 10px;
}
