/* ==========================================================================
   Flashcards — single deck, DESIGN SAMPLE (/flashcards/deck-sample/)

   THE FLIP IS THE PAGE
   A flashcard's pedagogy is the pause between seeing the question and seeing
   the answer. That pause is created by a real 3D rotation, not by revealing
   text below other text. Everything else here exists to keep that one
   interaction fast and unambiguous.

   PERSPECTIVE BELONGS TO THE PARENT
   `perspective` is set on the card, and the rotation on the element inside it.
   Set on the rotating element itself, every card gets its own vanishing point
   and a grid of them fans outward instead of receding together.

   Only tokens that exist in tokens.css. --s-9, --s-13 and --s-14 are NOT in
   the scale, and an undefined custom property invalidates the whole
   declaration at computed-value time — silently.
   ========================================================================== */

.page-fdx {
  --fdx-max:   1240px;
  --fdx-hair:  rgb(255 255 255 / .1);
  --fdx-pane:  rgb(255 255 255 / .04);
  --fdx-pane2: rgb(255 255 255 / .07);
  --fdx-glass: blur(16px) saturate(1.35);
  --fdx-card-h: 15rem;
  background: var(--surface-0);
  overflow-x: clip;
}
.page-fdx [data-depth] { perspective: 1400px; }

/* Body scroll is frozen while an overlay owns the screen. Lenis is paused
   from JS at the same time — it drives scroll from its own rAF loop and would
   otherwise keep moving the page underneath the overlay. */
.fdx-locked { overflow: hidden; }

/* ── AMBIENCE ──────────────────────────────────────────────────────────── */
.fdxSky { position: fixed; inset: 0; z-index: 0; pointer-events: none; overflow: hidden; }
.fdxSky__grid {
  position: absolute; inset: -20%;
  background-image:
    linear-gradient(to right, rgb(240 198 74 / .045) 1px, transparent 1px),
    linear-gradient(to bottom, rgb(240 198 74 / .045) 1px, transparent 1px);
  background-size: 60px 60px;
  mask-image: radial-gradient(ellipse 70% 55% at 50% 30%, #000 15%, transparent 75%);
  transform: perspective(700px) rotateX(60deg) translateY(14%);
  transform-origin: 50% 100%;
}
.fdxSky__orb {
  position: absolute; border-radius: 50%; filter: blur(90px); opacity: .3;
  animation: fdxDrift 24s var(--ease-inout) infinite alternate;
}
.fdxSky__orb--1 { width: 40vw; height: 40vw; left: -10vw; top: -10vw; background: radial-gradient(circle, rgb(240 198 74 / .5), transparent 70%); }
.fdxSky__orb--2 { width: 32vw; height: 32vw; right: -8vw; top: 40vh; background: radial-gradient(circle, rgb(232 84 74 / .3), transparent 70%); animation-delay: -12s; }
@keyframes fdxDrift { from { translate: 0 0; } to { translate: 3vw 5vh; } }

/* The SECTIONS are lifted above the ambience, never <main> itself.

   z-index on <main> creates a stacking context, and every descendant is then
   confined to it — so the study overlay's z-index: 300 was competing at
   main's level of 1 and lost to the site nav at 100. The overlay rendered
   underneath the header. Lifting the sections instead leaves <main> a plain
   box that the fixed overlays inside it can escape. */
.fdxHero, .fdxBar, .fdxDeck, .fdxFlag { position: relative; z-index: 1; }

/* ── FURNITURE ─────────────────────────────────────────────────────────── */
.fdxEyebrow {
  font-family: var(--font-mono); font-size: var(--fs-xs);
  letter-spacing: .1em; text-transform: uppercase; color: var(--text-lo);
  margin: 0 0 var(--s-3);
}
.fdxEyebrow b { color: var(--accent-ink); font-weight: 600; }

.fdxCrumbs ol {
  display: flex; flex-wrap: wrap; gap: var(--s-2); list-style: none;
  margin: 0 0 var(--s-6); padding: 0; font-size: var(--fs-sm); color: var(--text-lo);
}
.fdxCrumbs li + li::before { content: '/'; margin-right: var(--s-2); color: var(--fdx-hair); }
.fdxCrumbs a { color: var(--text-lo); text-decoration: none; }
.fdxCrumbs a:hover { color: var(--accent-ink); }

.fdxBtn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2);
  padding: .8rem 1.4rem; border-radius: var(--radius-pill); cursor: pointer;
  border: 1px solid transparent; font: inherit; font-size: var(--fs-sm); font-weight: 600;
  text-decoration: none;
  transition: transform var(--dur) var(--ease-spring), box-shadow var(--dur) var(--ease-out),
              background-color var(--dur) var(--ease-out), color var(--dur) var(--ease-out);
}
.fdxBtn kbd {
  padding: .1rem .35rem; border-radius: 4px; font-family: var(--font-mono); font-size: .6rem;
  background: rgb(0 0 0 / .16); border: 1px solid rgb(0 0 0 / .12);
}
.fdxBtn--primary { background: var(--y-yellow); color: var(--y-black); box-shadow: var(--glow-yellow); }
.fdxBtn--primary:hover { transform: translateY(-2px); }
.fdxBtn--ghost { border-color: var(--fdx-hair); color: var(--text-hi); background: var(--fdx-pane); backdrop-filter: var(--fdx-glass); }
.fdxBtn--ghost:hover { border-color: rgb(240 198 74 / .55); color: var(--accent-ink); }
.fdxBtn--ghost kbd { background: rgb(255 255 255 / .1); border-color: var(--fdx-hair); }
.fdxBtn--lg { padding: 1rem 1.8rem; font-size: var(--fs-body); }
.fdxBtn--good { background: rgb(62 158 95 / .18); color: #7fd3a0; border-color: rgb(62 158 95 / .45); }
.fdxBtn--good:hover { background: var(--y-green); color: var(--y-white); }
.fdxBtn--bad  { background: rgb(232 84 74 / .16); color: var(--y-red-300); border-color: rgb(232 84 74 / .42); }
.fdxBtn--bad:hover { background: var(--y-red); color: var(--y-white); }
.fdxBtn--flip { background: var(--fdx-pane2); color: var(--text-hi); border-color: var(--fdx-hair); }
.fdxBtn--flip:hover { border-color: var(--y-yellow); color: var(--accent-ink); }
.fdxBtn--good kbd, .fdxBtn--bad kbd, .fdxBtn--flip kbd { background: rgb(255 255 255 / .12); border-color: var(--fdx-hair); }

.fdxIcon {
  width: 2.2rem; height: 2.2rem; flex: none; display: grid; place-items: center;
  border-radius: 50%; cursor: pointer; font-size: .95rem; line-height: 1;
  border: 1px solid var(--fdx-hair); background: var(--fdx-pane);
  color: var(--text-mid); font-family: inherit;
  transition: border-color var(--dur) var(--ease-out), color var(--dur) var(--ease-out),
              background-color var(--dur) var(--ease-out), transform var(--dur) var(--ease-spring);
}
.fdxIcon:hover { border-color: var(--y-yellow); color: var(--accent-ink); transform: scale(1.08); }
.fdxIcon--close:hover { background: var(--y-red); border-color: transparent; color: var(--y-white); }

.fdxLink {
  border: 0; background: none; padding: 0; cursor: pointer;
  font: inherit; color: var(--accent-ink); text-decoration: underline;
  text-underline-offset: 3px;
}
.fdxLink--mute { color: var(--text-lo); }

/* ══════════════════════════════════════════════════════════════════════════
   01 · HERO
   ══════════════════════════════════════════════════════════════════════════ */
.fdxHero { padding: clamp(var(--s-10), 6vw, var(--s-20)) 0 var(--s-10); }
.fdxHero__inner { max-width: var(--fdx-max); margin-inline: auto; padding-inline: var(--gutter); }
.fdxHero__grid {
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  gap: clamp(var(--s-6), 4vw, var(--s-12)); align-items: end;
}

.fdxHero__h1 {
  font-family: var(--font-display); font-size: var(--fs-h2); font-weight: 500;
  line-height: var(--lh-snug); letter-spacing: var(--tracking-hero);
  color: var(--text-hi); margin: 0 0 var(--s-4); max-width: 20ch;
  animation: fdxRise .8s var(--ease-out) backwards;
}
.fdxHero__sub {
  max-width: 56ch; color: var(--text-mid); line-height: var(--lh-body);
  margin: 0 0 var(--s-6); font-size: var(--fs-body-lg);
  animation: fdxRise .8s var(--ease-out) 90ms backwards;
}
@keyframes fdxRise { from { opacity: 0; translate: 0 .5em; } to { opacity: 1; translate: 0 0; } }

.fdxHero__cta { display: flex; flex-wrap: wrap; gap: var(--s-3); animation: fdxRise .8s var(--ease-out) 180ms backwards; }

.fdxResume {
  margin: var(--s-5) 0 0; font-size: var(--fs-sm); color: var(--text-mid);
  padding: var(--s-3) var(--s-4); border-radius: var(--radius);
  border: 1px dashed rgb(240 198 74 / .4); background: rgb(240 198 74 / .06);
}
.fdxResume[hidden] { display: none; }
.fdxResume b { color: var(--accent-ink); }

.fdxStats {
  display: grid; grid-auto-flow: column; gap: var(--s-3);
  list-style: none; margin: 0; padding: 0;
  translate: 0 var(--py, 0px);
}
.fdxStat {
  min-width: 5.5rem; padding: var(--s-4);
  border-radius: var(--radius); border: 1px solid var(--fdx-hair);
  background: var(--fdx-pane); backdrop-filter: var(--fdx-glass);
  text-align: center;
}
.fdxStat b {
  display: block; font-family: var(--font-display); font-size: var(--fs-h3);
  font-weight: 500; color: var(--text-hi); line-height: 1;
  font-variant-numeric: tabular-nums;
}
.fdxStat span { font-size: var(--fs-xs); color: var(--text-lo); }
.fdxStat--dia b { color: var(--accent-ink); }

/* ══════════════════════════════════════════════════════════════════════════
   02 · CONTROL BAR
   ══════════════════════════════════════════════════════════════════════════ */
.fdxBar {
  position: sticky; top: 0; z-index: 30;
  background: color-mix(in srgb, var(--surface-0) 86%, transparent);
  backdrop-filter: var(--fdx-glass);
  border-bottom: 1px solid var(--fdx-hair);
}
.fdxBar__inner {
  max-width: var(--fdx-max); margin-inline: auto;
  padding: var(--s-3) var(--gutter);
  display: flex; align-items: center; gap: var(--s-4);
}

.fdxSearch { position: relative; flex: 0 0 auto; width: min(15rem, 30vw); }
.fdxSearch input {
  width: 100%; padding: .5rem 2.2rem .5rem .9rem;
  border-radius: var(--radius-pill); border: 1px solid var(--fdx-hair);
  background: var(--fdx-pane); color: var(--text-hi);
  font: inherit; font-size: var(--fs-sm); outline: none;
}
.fdxSearch input:focus { border-color: var(--y-yellow); }
.fdxSearch input::-webkit-search-cancel-button { display: none; }
.fdxSearch kbd {
  position: absolute; right: .5rem; top: 50%; translate: 0 -50%;
  padding: .1rem .35rem; border-radius: 4px; pointer-events: none;
  border: 1px solid var(--fdx-hair); background: var(--surface-2);
  font-family: var(--font-mono); font-size: .6rem; color: var(--text-lo);
}
.fdxSearch input:focus + kbd { opacity: 0; }

.fdxUnits {
  display: flex; align-items: center; gap: var(--s-2);
  flex: 1; min-width: 0; overflow-x: auto; scrollbar-width: none;
  padding-block: var(--s-1);
  mask-image: linear-gradient(to right, transparent, #000 12px, #000 calc(100% - 12px), transparent);
}
.fdxUnits::-webkit-scrollbar { display: none; }

.fdxChip {
  position: relative; flex: none; cursor: pointer; isolation: isolate;
  display: inline-flex; align-items: center; gap: .4rem;
  padding: .4rem .8rem; border-radius: var(--radius-pill);
  border: 1px solid var(--fdx-hair); background: var(--fdx-pane);
  font: inherit; font-size: var(--fs-sm); color: var(--text-mid);
  transition: color var(--dur) var(--ease-out), border-color var(--dur) var(--ease-out);
}
.fdxChip::before {
  content: ''; position: absolute; inset: 0; z-index: -1; border-radius: inherit;
  background: var(--y-yellow); transform-origin: left center; scale: 0 1;
  transition: scale var(--dur) var(--ease-out);
}
.fdxChip:hover { border-color: var(--y-yellow); color: var(--accent-ink); }
.fdxChip.is-on { color: var(--y-black); border-color: transparent; font-weight: 600; }
.fdxChip.is-on::before { scale: 1 1; }
.fdxChip i {
  font-style: normal; font-family: var(--font-mono); font-size: .6rem;
  padding: .05rem .35rem; border-radius: 999px;
  background: var(--surface-2); color: var(--text-lo);
}
.fdxChip.is-on i { background: rgb(28 28 28 / .2); color: var(--y-black); }
.fdxChip--ghost[aria-pressed="true"] {
  border-color: var(--y-red); color: var(--y-red-300); background: rgb(232 84 74 / .12);
}

.fdxBar__right { display: flex; align-items: center; gap: var(--s-2); flex: none; }
.fdxCount { margin: 0; font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--text-lo); white-space: nowrap; }
.fdxCount b { color: var(--text-hi); }

.fdxBar__progress { display: block; height: 2px; background: var(--fdx-hair); }
.fdxBar__progress i {
  display: block; height: 100%; width: var(--w, 0%);
  background: linear-gradient(to right, var(--y-yellow-700), var(--y-yellow));
  transition: width var(--dur-slow) var(--ease-out);
}

/* ══════════════════════════════════════════════════════════════════════════
   03 · THE DECK — unit accordions → topic dropdowns → card rows
   ══════════════════════════════════════════════════════════════════════════ */
.fdxDeck { padding-block: var(--s-10) var(--s-24); }
.fdxDeck__inner {
  max-width: 62rem; margin-inline: auto; padding-inline: var(--gutter);
  display: grid; gap: var(--s-4);
}

.fdxUnit {
  border-radius: var(--radius-lg);
  border: 1px solid var(--fdx-hair);
  background: var(--fdx-pane);
  backdrop-filter: var(--fdx-glass);
  overflow: hidden;
  animation: fdxRise .5s var(--ease-out) backwards;
  animation-delay: calc(var(--i) * 55ms);
  transition: border-color var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease-out);
}
.fdxUnit[hidden] { display: none; }
.fdxUnit:has(.fdxUnit__head[aria-expanded="true"]) {
  border-color: rgb(240 198 74 / .34);
  box-shadow: 0 20px 60px -34px rgb(0 0 0 / .8);
}

.fdxUnit__h { margin: 0; }
.fdxUnit__head {
  all: unset; box-sizing: border-box; cursor: pointer;
  width: 100%; display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto auto auto;
  align-items: center; gap: var(--s-4);
  padding: var(--s-5) var(--s-6);
  transition: background-color var(--dur) var(--ease-out);
}
.fdxUnit__head:hover { background: var(--fdx-pane2); }
.fdxUnit__head:focus-visible { outline: 2px solid var(--y-yellow); outline-offset: -3px; }

.fdxUnit__n {
  font-family: var(--font-mono); font-size: var(--fs-xs);
  padding: .25rem .65rem; border-radius: var(--radius-pill);
  background: rgb(240 198 74 / .16); color: var(--accent-ink); white-space: nowrap;
}
.fdxUnit__t {
  font-family: var(--font-display); font-size: var(--fs-h3); font-weight: 500;
  color: var(--text-hi); line-height: var(--lh-snug);
}
.fdxUnit__meta { font-size: var(--fs-xs); color: var(--text-lo); white-space: nowrap; }
.fdxUnit__meta b { color: var(--text-mid); }
.fdxUnit__bar { width: 4.5rem; height: 3px; border-radius: 3px; background: var(--surface-2); overflow: hidden; }
.fdxUnit__bar i {
  display: block; height: 100%; width: var(--w, 0%);
  background: var(--y-green); transition: width var(--dur) var(--ease-out);
}

/* One chevron drawn in CSS rather than an icon font or an inline SVG per row —
   it rotates, and there are eighty of them on this page. */
.fdxChev {
  width: .5rem; height: .5rem; flex: none;
  border-right: 2px solid var(--text-lo); border-bottom: 2px solid var(--text-lo);
  rotate: 45deg; translate: 0 -2px;
  transition: rotate var(--dur) var(--ease-out), border-color var(--dur) var(--ease-out);
}
.fdxChev--sm { width: .4rem; height: .4rem; border-width: 1.5px; }
[aria-expanded="true"] .fdxChev { rotate: -135deg; translate: 0 2px; border-color: var(--y-yellow); }

.fdxUnit__body { padding: 0 var(--s-4) var(--s-4); display: grid; gap: var(--s-2); }
.fdxUnit__body[hidden] { display: none; }

/* --- topic dropdown ------------------------------------------------------ */
.fdxTopic {
  border-radius: var(--radius); border: 1px solid var(--fdx-hair);
  background: var(--surface-1); overflow: hidden;
}
.fdxTopic[hidden] { display: none; }
.fdxTopic__h { margin: 0; }
.fdxTopic__head {
  all: unset; box-sizing: border-box; cursor: pointer;
  width: 100%; display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-4) var(--s-5);
  transition: background-color var(--dur) var(--ease-out);
}
.fdxTopic__head:hover { background: var(--fdx-pane); }
.fdxTopic__head:focus-visible { outline: 2px solid var(--y-yellow); outline-offset: -3px; }
.fdxTopic__dot {
  width: .45rem; height: .45rem; flex: none; border-radius: 50%;
  background: var(--fdx-hair);
  transition: background-color var(--dur) var(--ease-out), scale var(--dur) var(--ease-spring);
}
[aria-expanded="true"] .fdxTopic__dot { background: var(--y-yellow); scale: 1.4; }
.fdxTopic__t { flex: 1; min-width: 0; font-size: var(--fs-body); color: var(--text-hi); }
.fdxTopic__n {
  flex: none; font-family: var(--font-mono); font-size: .62rem;
  padding: .1rem .45rem; border-radius: 999px;
  background: var(--surface-2); color: var(--text-lo);
}

/* --- card rows ----------------------------------------------------------- */
.fdxRows {
  list-style: none; margin: 0; padding: 0 var(--s-3) var(--s-3);
  display: grid; gap: 2px;
}
.fdxRows[hidden] { display: none; }

.fdxRow { position: relative; }
.fdxRow[hidden] { display: none; }

.fdxRow__btn {
  all: unset; box-sizing: border-box; cursor: pointer;
  width: 100%; display: flex; align-items: center; gap: var(--s-3);
  padding: .6rem var(--s-3); border-radius: var(--radius-sm);
  transition: background-color var(--dur) var(--ease-out), translate var(--dur) var(--ease-out);
}
.fdxRow__btn:hover { background: var(--fdx-pane2); translate: 3px 0; }
.fdxRow__btn:focus-visible { outline: 2px solid var(--y-yellow); outline-offset: -2px; }

.fdxRow__n {
  flex: none; width: 2.6rem;
  font-family: var(--font-mono); font-size: .64rem; color: var(--text-lo);
}
.fdxRow__q {
  flex: 1; min-width: 0; font-size: var(--fs-sm); color: var(--text-mid);
  line-height: 1.45;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.fdxRow__btn:hover .fdxRow__q { color: var(--text-hi); }
.fdxRow__end { flex: none; display: flex; align-items: center; gap: var(--s-2); }
.fdxRow__dia { font-size: .7rem; color: var(--accent-ink); }
.fdxRow__state {
  width: .45rem; height: .45rem; border-radius: 50%;
  border: 1px solid var(--fdx-hair); background: transparent;
}
.fdxRow.is-known   .fdxRow__state { background: var(--y-green); border-color: transparent; }
.fdxRow.is-unknown .fdxRow__state { background: var(--y-red);   border-color: transparent; }
.fdxRow.is-known   .fdxRow__n { color: var(--y-green); }
.fdxRow.is-unknown .fdxRow__n { color: var(--y-red-300); }

.fdxEmpty { text-align: center; color: var(--text-lo); padding: var(--s-16) 0; }
.fdxEmpty[hidden] { display: none; }

/* ══════════════════════════════════════════════════════════════════════════
   04 · THE CARD POPUP
   One surface, opened either by a row or by Start studying.
   ══════════════════════════════════════════════════════════════════════════ */
.fdxPop {
  position: fixed; inset: 0; z-index: 320;
  display: grid; place-items: center; padding: clamp(.5rem, 2vw, 1.5rem);
  animation: fdxFade .28s var(--ease-out);
}
.fdxPop[hidden] { display: none; }
@keyframes fdxFade { from { opacity: 0; } to { opacity: 1; } }

.fdxPop__veil {
  position: absolute; inset: 0; border: 0; cursor: zoom-out;
  background: rgb(4 4 6 / .82); backdrop-filter: blur(14px);
}

.fdxPop__shell {
  position: relative; z-index: 1;
  width: min(58rem, 100%); height: min(40rem, 92dvh);
  display: grid;
  /* The COLUMN has to be declared, not just the rows. With rows alone the
     implicit column is `auto`, which resolves to max-content — so the shell
     stretched to the footer's unwrapped width (measured 640px inside a 356px
     shell at 390px wide) and flex-wrap never had a reason to fire.
     minmax(0, 1fr) bounds it, and the footer wraps as intended. */
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto 1fr auto;
  border-radius: var(--radius-lg); overflow: hidden;
  border: 1px solid var(--fdx-hair);
  background: linear-gradient(180deg, var(--fdx-pane2), transparent 40%), var(--surface-0);
  box-shadow: 0 50px 130px -40px rgb(0 0 0 / .95);
  animation: fdxPop .38s var(--ease-out);
}
@keyframes fdxPop { from { opacity: 0; scale: .94; translate: 0 14px; } to { opacity: 1; scale: 1; translate: 0 0; } }

.fdxPop__top {
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-4) var(--s-5);
  border-bottom: 1px solid var(--fdx-hair);
}
.fdxPop__where {
  margin: 0; flex: 1; min-width: 0;
  font-family: var(--font-mono); font-size: var(--fs-xs);
  color: var(--text-lo); letter-spacing: .08em; text-transform: uppercase;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.fdxPop__pos {
  margin: 0; flex: none; display: flex; align-items: baseline; gap: .2rem;
  font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--text-lo);
}
.fdxPop__pos b { color: var(--text-hi); font-size: var(--fs-body); }
.fdxPop__track { flex: 0 0 5rem; height: 3px; border-radius: 3px; background: var(--fdx-hair); overflow: hidden; }
.fdxPop__track i {
  display: block; height: 100%; width: var(--w, 0%);
  background: linear-gradient(to right, var(--y-yellow-700), var(--y-yellow));
  transition: width var(--dur) var(--ease-out);
}
.fdxPop__timer { margin: 0; flex: none; font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--text-lo); font-variant-numeric: tabular-nums; }
.fdxPop__timer[hidden] { display: none; }

.fdxPop__stage {
  display: flex; align-items: center; gap: var(--s-3);
  padding: clamp(var(--s-4), 3vw, var(--s-8)); min-height: 0;
  perspective: 1800px;
}
.fdxPop__arrow {
  flex: none; width: 2.6rem; height: 2.6rem; cursor: pointer;
  display: grid; place-items: center; border-radius: 50%;
  border: 1px solid var(--fdx-hair); background: var(--fdx-pane);
  color: var(--text-hi); font-size: 1rem;
  transition: background-color var(--dur) var(--ease-out), border-color var(--dur) var(--ease-out),
              color var(--dur) var(--ease-out), transform var(--dur) var(--ease-spring);
}
.fdxPop__arrow:hover { background: var(--y-yellow); border-color: transparent; color: var(--y-black); transform: scale(1.08); }
.fdxPop__arrow[disabled] { opacity: .28; cursor: default; transform: none; }

.fdxPop__card { flex: 1; min-width: 0; height: 100%; cursor: pointer; }
.fdxPop__inner {
  position: relative; width: 100%; height: 100%;
  transform-style: preserve-3d;
  transition: transform .6s var(--ease-out);
}
.fdxPop.is-flipped .fdxPop__inner { transform: rotateY(180deg); }
/* A short slide as the card changes, so advancing reads as movement through a
   deck rather than as text being swapped in place. */
.fdxPop.is-moving .fdxPop__card { animation: fdxSlide .32s var(--ease-out); }
@keyframes fdxSlide {
  from { opacity: 0; translate: var(--dir, 36px) 0; }
  to   { opacity: 1; translate: 0 0; }
}

.fdxPop__face {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; justify-content: center; gap: var(--s-4);
  padding: clamp(var(--s-6), 4vw, var(--s-10));
  border-radius: var(--radius-lg); border: 1px solid var(--fdx-hair);
  background: linear-gradient(165deg, var(--fdx-pane2), transparent 55%), var(--surface-1);
  /* Hides the reversed face visually and NOT from a crawler — the answer is
     still in the DOM, so this indexes exactly as the live deck does. */
  backface-visibility: hidden; -webkit-backface-visibility: hidden;
  overflow-y: auto;
}
.fdxPop__face--back {
  transform: rotateY(180deg);
  background: linear-gradient(165deg, rgb(240 198 74 / .1), transparent 50%), var(--surface-1);
  border-color: rgb(240 198 74 / .3);
  justify-content: flex-start;
}
.fdxPop__tag {
  position: absolute; top: clamp(var(--s-4), 3vw, var(--s-6)); left: clamp(var(--s-6), 4vw, var(--s-10));
  margin: 0; font-family: var(--font-mono); font-size: .58rem;
  letter-spacing: .16em; text-transform: uppercase; color: var(--text-lo);
}
.fdxPop__tag--a { color: var(--accent-ink); }
.fdxPop__hint {
  position: absolute; bottom: clamp(var(--s-4), 3vw, var(--s-6)); left: clamp(var(--s-6), 4vw, var(--s-10));
  margin: 0; font-size: var(--fs-xs); color: var(--text-lo); opacity: .7;
}
.fdxPop__hint kbd {
  padding: .05rem .3rem; border-radius: 4px;
  border: 1px solid var(--fdx-hair); background: var(--surface-2); font-family: var(--font-mono);
}

.fdxPop__q {
  font-family: var(--font-display); font-weight: 500;
  font-size: clamp(1.25rem, .9rem + 1.4vw, 1.9rem);
  line-height: var(--lh-snug); color: var(--text-hi);
}
.fdxPop__a { font-size: var(--fs-body); line-height: var(--lh-body); color: var(--text-mid); padding-top: var(--s-6); }
.fdxPop__a :where(p, ul, ol, table) { margin: 0 0 var(--s-3); }
.fdxPop__a :where(ul, ol) { padding-left: 1.2rem; }
.fdxPop__a li { margin-bottom: .3rem; }
.fdxPop__a table { width: 100%; border-collapse: collapse; font-size: .9em; }
.fdxPop__a :where(th, td) { border: 1px solid var(--fdx-hair); padding: .35rem .5rem; text-align: left; }
.fdxPop__a th { background: var(--fdx-pane); color: var(--text-hi); }
.fdxPop__a code { font-family: var(--font-mono); font-size: .86em; background: var(--surface-2); padding: .05rem .3rem; border-radius: 4px; }
.fdxPop__a pre { background: var(--surface-2); padding: var(--s-3); border-radius: var(--radius-sm); overflow-x: auto; }

.fdxPop__foot {
  display: flex; align-items: center; justify-content: center; gap: var(--s-2);
  padding: var(--s-4) var(--s-5); flex-wrap: wrap;
  border-top: 1px solid var(--fdx-hair);
}
.fdxPop__sep { width: 1px; height: 1.6rem; background: var(--fdx-hair); }
.fdxPop__foot [hidden] { display: none; }

/* --- the finish card ----------------------------------------------------- */
.fdxDone {
  position: absolute; inset: 0; z-index: 2;
  display: grid; place-content: center; justify-items: center; gap: var(--s-3);
  text-align: center; padding: var(--gutter);
  background: color-mix(in srgb, var(--surface-0) 95%, transparent);
  backdrop-filter: blur(10px);
  animation: fdxFade .3s var(--ease-out);
}
.fdxDone[hidden] { display: none; }
.fdxDone__mark { font-size: 3rem; margin: 0; color: var(--y-yellow); line-height: 1; }
.fdxDone h2 { font-family: var(--font-display); font-size: var(--fs-h2); font-weight: 500; margin: 0; color: var(--text-hi); }
.fdxDone__line { margin: 0; color: var(--text-mid); font-family: var(--font-mono); font-size: var(--fs-sm); }
.fdxDone__line b { color: var(--text-hi); }
.fdxDone__cta { display: flex; flex-wrap: wrap; gap: var(--s-3); justify-content: center; margin-top: var(--s-4); }

/* ══════════════════════════════════════════════════════════════════════════
   05 · DIAGRAM LIGHTBOX
   ══════════════════════════════════════════════════════════════════════════ */
.fdxLb {
  position: fixed; inset: 0; z-index: 400;
  display: grid; place-items: center; padding: clamp(.5rem, 2vw, 2rem);
  animation: fdxFade .25s var(--ease-out);
}
.fdxLb[hidden] { display: none; }

/* The veil is a real <button>, so click-away and Esc are one control and it
   is reachable rather than being a div that only a mouse can use. */
.fdxLb__veil {
  position: absolute; inset: 0; border: 0; cursor: zoom-out;
  background: rgb(4 4 6 / .88); backdrop-filter: blur(8px);
}

.fdxLb__box {
  position: relative; z-index: 1; margin: 0;
  display: grid; grid-template-rows: auto 1fr auto;
  width: min(72rem, 100%); height: min(46rem, 92dvh);
  border-radius: var(--radius-lg); overflow: hidden;
  border: 1px solid var(--fdx-hair); background: var(--surface-1);
  box-shadow: 0 50px 120px -40px rgb(0 0 0 / .95);
  animation: fdxPop .34s var(--ease-out);
}
@keyframes fdxPop { from { opacity: 0; scale: .95; } to { opacity: 1; scale: 1; } }

.fdxLb__bar {
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  border-bottom: 1px solid var(--fdx-hair);
  background: var(--fdx-pane);
}
.fdxLb__label { flex: 1; min-width: 0; font-size: var(--fs-sm); color: var(--text-hi); }
.fdxLb__tools { display: flex; align-items: center; gap: var(--s-2); flex: none; }
.fdxLb__pct { font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--text-lo); min-width: 3.2rem; text-align: center; }

.fdxLb__pane {
  overflow: hidden; min-height: 0;
  display: grid; place-items: center;
  background:
    repeating-conic-gradient(rgb(255 255 255 / .02) 0% 25%, transparent 0% 50%) 50% / 22px 22px,
    #07080c;
  cursor: grab;
}
.fdxLb__pane.is-panning { cursor: grabbing; }
.fdxLb__pane img {
  max-width: 100%; max-height: 100%;
  /* --z and --x/--y are written by JS. Composing here rather than letting JS
     set `transform` keeps the two systems from overwriting each other. */
  translate: var(--x, 0px) var(--y, 0px);
  scale: var(--z, 1);
  transition: scale var(--dur) var(--ease-out);
  user-select: none; -webkit-user-drag: none;
}
.fdxLb__pane.is-panning img { transition: none; }

/* ── FLOATING MODE ─────────────────────────────────────────────────────────
   The pop-out. The lightbox stops being a modal and becomes a window you can
   drag beside the card it belongs to.

   `pointer-events` is the whole trick: the fixed full-screen .fdxLb layer
   still spans the viewport, so it has to stop catching clicks or the page
   underneath is unusable while the window is open. The layer goes
   transparent to the pointer and the box alone takes events back. */
.fdxLb.is-float {
  /* Stop centring — the box carries its own translate from the drag. */
  place-items: start center;
  pointer-events: none;
  animation: none;
}

.fdxLb.is-float .fdxLb__veil { display: none; }

.fdxLb.is-float .fdxLb__box {
  pointer-events: auto;
  width: min(30rem, 90vw);
  margin-top: 12vh;
  box-shadow: 0 24px 70px -18px rgb(0 0 0 / .75), 0 0 0 1px var(--surface-line);
  /* The drag writes translate() inline; nothing here may animate transform or
     the window would lag a frame behind the cursor on every move. */
  transition: none;
}

.fdxLb.is-float .fdxLb__bar {
  cursor: grab;
  /* The bar is the handle, so its label must not start a text selection
     mid-drag. Buttons inside keep their own cursor. */
  user-select: none;
}
.fdxLb.is-float .fdxLb__bar.is-moving { cursor: grabbing; }
.fdxLb.is-float .fdxLb__bar .fdxIcon { cursor: pointer; }

/* Smaller window, smaller image pane. */
.fdxLb.is-float .fdxLb__pane { max-height: 46vh; }

@media (prefers-reduced-motion: reduce) {
  .fdxLb { animation: none; }
}

.fdxLb__hint {
  margin: 0; padding: var(--s-3) var(--s-4);
  border-top: 1px solid var(--fdx-hair);
  font-size: var(--fs-xs); color: var(--text-lo); text-align: center;
}
.fdxLb__hint kbd {
  padding: .05rem .3rem; border-radius: 4px;
  border: 1px solid var(--fdx-hair); background: var(--surface-2);
  font-family: var(--font-mono); font-size: .9em;
}

/* ══════════════════════════════════════════════════════════════════════════
   06 · SHORTCUTS
   ══════════════════════════════════════════════════════════════════════════ */
.fdxHelp {
  position: fixed; inset: 0; z-index: 420;
  display: grid; place-items: center; padding: var(--gutter);
  background: rgb(4 4 6 / .8); backdrop-filter: blur(8px);
  animation: fdxFade .25s var(--ease-out);
}
.fdxHelp[hidden] { display: none; }
.fdxHelp__box {
  width: min(28rem, 100%); padding: var(--s-8);
  border-radius: var(--radius-lg); border: 1px solid var(--fdx-hair);
  background: var(--surface-1); box-shadow: var(--shadow-card);
  animation: fdxPop .3s var(--ease-out);
}
.fdxHelp h2 { font-family: var(--font-display); font-size: var(--fs-h3); font-weight: 500; margin: 0 0 var(--s-5); color: var(--text-hi); }
.fdxHelp dl { display: grid; gap: var(--s-3); margin: 0 0 var(--s-6); }
.fdxHelp dl > div { display: flex; align-items: center; gap: var(--s-4); }
.fdxHelp dt { flex: 0 0 7rem; display: flex; gap: .3rem; }
.fdxHelp dd { margin: 0; font-size: var(--fs-sm); color: var(--text-mid); }
.fdxHelp kbd {
  padding: .15rem .45rem; border-radius: 5px;
  border: 1px solid var(--fdx-hair); background: var(--surface-2);
  font-family: var(--font-mono); font-size: .68rem; color: var(--text-hi);
}

.fdxFlag {
  position: relative; z-index: 1;
  text-align: center; margin: 0; padding: var(--s-4);
  font-family: var(--font-mono); font-size: var(--fs-xs);
  color: var(--text-lo); background: var(--surface-2);
}
.fdxFlag a { color: var(--accent-ink); }
.fdxFlag__note { color: var(--text-mid); }

/* ══════════════════════════════════════════════════════════════════════════
   RESPONSIVE
   Each breakpoint restates the grid it changes. An element left holding a
   template that no longer exists is auto-placed into an implicit row — a
   random gap that reports no error anywhere.
   ══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 64rem) {
  .fdxHero__grid { grid-template-columns: minmax(0, 1fr); align-items: start; }
  .fdxStats { grid-auto-flow: column; justify-content: start; }
}

@media (max-width: 48rem) {
  .fdxBar__inner { flex-wrap: wrap; gap: var(--s-2); }
  .fdxSearch { width: 100%; order: 1; }
  .fdxUnits { order: 3; flex-basis: 100%; }
  .fdxBar__right { order: 2; margin-left: auto; }
}

@media (max-width: 46rem) {
  .fdxSearch input { font-size: 1rem; }  /* 16px — below it iOS zooms on focus */
  .fdxSearch kbd { display: none; }

  /* The unit header's five columns cannot survive a phone. Two rows instead,
     with the progress bar spanning under the title. */
  .fdxUnit__head {
    grid-template-columns: auto minmax(0, 1fr) auto;
    gap: var(--s-2) var(--s-3); padding: var(--s-4);
  }
  .fdxUnit__t { grid-column: 1 / -1; font-size: var(--fs-body-lg); }
  .fdxUnit__meta { grid-column: 1 / 3; }
  .fdxUnit__bar { display: none; }
  .fdxUnit__body { padding: 0 var(--s-2) var(--s-2); }

  /* On a phone the arrows would eat a third of the card's width. Swipe and the
     footer buttons do the same job with the whole screen given to the card. */
  .fdxPop__arrow { display: none; }
  .fdxPop__shell { height: 94dvh; }
  .fdxPop__foot { gap: var(--s-2); }
  .fdxBtn kbd { display: none; }
  .fdxStats { grid-auto-flow: row; grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .fdxLb__hint { display: none; }
}

@media (max-width: 24rem) {
  .fdxBtn { flex: 1 1 100%; }
  .fdxPop__foot { flex-direction: column; }
  .fdxPop__sep { display: none; }
}

/* ══════════════════════════════════════════════════════════════════════════
   REDUCED MOTION
   tokens.css zeroes durations globally, but it cannot know about this page's
   infinite loops — and it must not kill the FLIP, which is the one animation
   here that carries meaning rather than decoration. The flip is kept and
   shortened; everything decorative goes.
   ══════════════════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: reduce) {
  .fdxSky__orb, .fdxHero__h1, .fdxHero__sub, .fdxHero__cta, .fdxUnit { animation: none; }
  .fdxPop.is-moving .fdxPop__card { animation: none; }
  .fdxStats { translate: none; }
  /* The flip is kept and shortened. It is the one animation on this page that
     carries meaning rather than decoration — without it the answer simply
     appears, which is the behaviour this page exists to replace. */
  .fdxPop__inner { transition-duration: .01ms; }
  .fdxRow__btn:hover { translate: none; }
}

/* ══════════════════════════════════════════════════════════════════════════
   07 · THE GAME LAYER — combo, XP, burst, ripple, rank
   Retention furniture. All of it is decoration over state that already
   exists: the combo counts consecutive "Nailed it", XP counts cards nailed,
   the rank is a band on the same percentage the progress bar draws. Nothing
   here invents a number, which is what keeps it honest when somebody checks
   it against the deck.
   ══════════════════════════════════════════════════════════════════════════ */

/* --- combo pill ---------------------------------------------------------- */
.fdxCombo {
  margin: 0; flex: none;
  display: inline-flex; align-items: center; gap: .35rem;
  padding: .25rem .6rem; border-radius: var(--radius-pill);
  border: 1px solid rgb(240 198 74 / .45);
  background: rgb(240 198 74 / .14);
  font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--accent-ink);
  white-space: nowrap;
}
.fdxCombo[hidden] { display: none; }
.fdxCombo b { color: var(--y-yellow); font-size: var(--fs-body); line-height: 1; }
.fdxCombo__word { font-size: .58rem; letter-spacing: .1em; text-transform: uppercase; opacity: .8; }
/* Re-triggered from JS on every increment, so the pill pulses each time the
   streak grows rather than only when it first appears. */
.fdxCombo.is-bump { animation: fdxBump .42s var(--ease-spring); }
@keyframes fdxBump {
  0%   { scale: 1; }
  35%  { scale: 1.22; }
  100% { scale: 1; }
}
.fdxCombo__flame { display: inline-block; animation: fdxFlick 1.1s var(--ease-inout) infinite alternate; }
@keyframes fdxFlick { from { scale: 1; opacity: .85; } to { scale: 1.15; opacity: 1; } }

/* Hot streaks tint the whole card frame. Colour is the cheapest possible
   signal and it does not move anything, so it cannot fight the flip. */
.fdxPop.is-hot .fdxPop__face { border-color: rgb(240 198 74 / .55); }
.fdxPop.is-hot .fdxPop__shell { box-shadow: 0 50px 130px -40px rgb(0 0 0 / .95), var(--glow-yellow); }

/* --- XP rail ------------------------------------------------------------- */
.fdxXp {
  display: flex; align-items: center; gap: var(--s-3);
  padding: 0 var(--s-5); height: 1.6rem;
  font-family: var(--font-mono); font-size: .6rem; color: var(--text-lo);
}
.fdxXp__lvl b { color: var(--accent-ink); font-size: .72rem; }
.fdxXp__track { flex: 1; height: 3px; border-radius: 3px; background: var(--fdx-hair); overflow: hidden; }
.fdxXp__track i {
  display: block; height: 100%; width: var(--w, 0%);
  background: linear-gradient(to right, var(--y-yellow-700), var(--y-yellow));
  transition: width .5s var(--ease-out);
}
.fdxXp__n b { color: var(--text-mid); }

/* --- burst --------------------------------------------------------------- */
.fdxBurst { position: fixed; inset: 0; z-index: 340; pointer-events: none; }
.fdxBurst i {
  position: absolute; width: 7px; height: 7px; border-radius: 2px;
  background: var(--y-yellow);
  /* --dx/--dy are written per particle by JS. Composing the flight in CSS
     keeps the JS to two custom properties and no transform of its own. */
  animation: fdxFly var(--life, 620ms) var(--ease-out) forwards;
}
@keyframes fdxFly {
  from { opacity: 1; translate: 0 0; scale: 1; rotate: 0deg; }
  to   { opacity: 0; translate: var(--dx, 0px) var(--dy, 0px); scale: .3; rotate: var(--rot, 180deg); }
}

/* --- tap ripple ---------------------------------------------------------- */
/* A touch affordance, not a mouse one: on a phone there is no hover, so a tap
   with no visual response reads as a tap that did not register. */
.fdxRipple {
  position: absolute; border-radius: 50%; pointer-events: none;
  background: radial-gradient(circle, rgb(240 198 74 / .5), transparent 70%);
  animation: fdxRip .55s var(--ease-out) forwards;
}
@keyframes fdxRip {
  from { opacity: .8; scale: 0; }
  to   { opacity: 0; scale: 1; }
}
.fdxRow__btn, .fdxUnit__head, .fdxTopic__head, .fdxBtn, .fdxPop__card { position: relative; overflow: hidden; }
/* The card must NOT clip — the flip needs to overflow, and a ripple is not
   worth losing it for. It gets the ripple without the clip. */
.fdxPop__card { overflow: visible; }

/* A miss shakes rather than flashes. Two frames of horizontal movement read
   as "no" without a colour change that would fight the theme. */
.fdxPop.is-miss .fdxPop__shell { animation: fdxShake .38s var(--ease-out); }
@keyframes fdxShake {
  0%, 100% { translate: 0 0; }
  20% { translate: -9px 0; }
  45% { translate: 7px 0; }
  70% { translate: -4px 0; }
}

/* --- rank ---------------------------------------------------------------- */
.fdxRank {
  margin: 0 0 var(--s-2); display: grid; justify-items: center; gap: .2rem;
  animation: fdxPop .5s var(--ease-spring) backwards; animation-delay: .12s;
}
.fdxRank[hidden] { display: none; }
.fdxRank b {
  font-family: var(--font-display); font-size: 3.4rem; line-height: 1; font-weight: 500;
  color: var(--y-yellow); text-shadow: 0 0 40px rgb(240 198 74 / .5);
}
.fdxRank span {
  font-family: var(--font-mono); font-size: var(--fs-xs);
  letter-spacing: .16em; text-transform: uppercase; color: var(--text-lo);
}

@media (prefers-reduced-motion: reduce) {
  .fdxCombo.is-bump, .fdxCombo__flame, .fdxBurst i,
  .fdxRipple, .fdxPop.is-miss .fdxPop__shell, .fdxRank { animation: none; }
  .fdxBurst { display: none; }
}
