/* ==========================================================================
   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); }

/* A labelled icon button. `width` and `place-items` both have to be undone:
   the base rule is a fixed 2.2rem circle, and a grid with one auto column
   stacks the glyph on top of the word instead of setting them side by side. */
.fdxIcon--wide {
  width: auto; border-radius: var(--radius-pill);
  display: inline-flex; align-items: center; gap: .4rem;
  padding-inline: .7rem; font-size: var(--fs-xs); white-space: nowrap;
}

.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;
  /* THE MEASURE CAP AGAIN. site.css sets `p { max-width: var(--measure) }`,
     and this spacer is a <p>. `flex: 1` then grows it only as far as the
     measure, so ~176px of the bar was left unclaimed at the right — and since
     free space in a flex row collects at the END, the close button stopped
     short of the corner and floated in the middle of the bar with nothing
     beside it. Every centred or growing <p> on this site needs this paired
     with it. */
  max-width: none;
  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;
}

/* Pin the close to the corner independently of what else is in the bar.
   The spacer above is what should push it there, but the combo pill and the
   timer appear and disappear mid-session, and a close button that drifts
   inward when a badge shows up is a control that moves under the cursor. */
.fdxPop__top .fdxIcon--close { margin-left: auto; }
.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);
}
/* ── THE DIAGRAM, ON THE CARD ──────────────────────────────────────────────
   225 of the 476 cards carry a diagram and it used to live entirely behind a
   20px glyph in the popup footer. For a question about a Feistel network the
   drawing IS the answer and the prose is the caption, so it belongs in the
   answer's flow, below the text, at a size you can actually read. */
.fdxDia { margin: var(--s-6) 0 0; padding-top: var(--s-5); border-top: 1px solid var(--fdx-hair); }
.fdxDia[hidden] { display: none; }

.fdxDia__label {
  /* <p>, so the global measure cap applies — see .fdxPop__where. */
  margin: 0 0 var(--s-3); max-width: none;
  display: flex; align-items: center; gap: var(--s-2);
  font-family: var(--font-mono); font-size: .58rem;
  letter-spacing: .16em; text-transform: uppercase; color: var(--accent-ink);
}
.fdxDia__mark { font-size: 1rem; line-height: 1; }

.fdxDia__open {
  display: block; position: relative; width: 100%; padding: 0;
  cursor: zoom-in; overflow: hidden;
  border-radius: var(--radius); border: 1px solid rgb(240 198 74 / .32);
  background: #07080c;
  transition: border-color var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease-out);
}
.fdxDia__open:hover, .fdxDia__open:focus-visible {
  border-color: rgb(240 198 74 / .7);
  box-shadow: 0 0 0 4px rgb(240 198 74 / .12);
}
.fdxDia__open img {
  display: block; margin-inline: auto;
  /* Sized by BOTH caps with auto width and height, never `width: 100%` plus
     object-fit. These diagrams are square; stretching one to the card's width
     and then capping its height letterboxes it — a 623x302 box holding a
     302x302 picture, so two fifths of the block is empty and the drawing
     reads as smaller than it is. Letting the image find its own box means the
     picture IS the block, at whichever cap binds first.
     The height cap keeps a square diagram from pushing the answer text off
     the card; the full-size read is one click away, which is what the
     lightbox, the pop-out and full screen are all for. */
  max-width: 100%; max-height: 52vh;
  width: auto; height: auto;
}
.fdxDia__badge {
  position: absolute; right: var(--s-3); bottom: var(--s-3);
  display: inline-flex; align-items: center; gap: .35rem;
  padding: .25rem .6rem; border-radius: var(--radius-pill);
  font-size: var(--fs-xs); color: var(--y-black); background: var(--y-yellow);
  opacity: 0; translate: 0 6px;
  transition: opacity var(--dur) var(--ease-out), translate var(--dur) var(--ease-out);
}
.fdxDia__open:hover .fdxDia__badge, .fdxDia__open:focus-visible .fdxDia__badge { opacity: 1; translate: 0 0; }
.fdxDia__cap { margin: var(--s-2) 0 0; font-size: var(--fs-xs); color: var(--text-lo); }
.fdxDia__cap kbd {
  padding: .05rem .3rem; border-radius: 4px;
  border: 1px solid var(--fdx-hair); background: var(--surface-2); font-family: var(--font-mono);
}
@media (hover: none) { .fdxDia__badge { opacity: 1; translate: 0 0; } }

/* The chip opposite the ANSWER tag. The answer face scrolls, so on a long
   answer the figure above is below the fold — a block you have to scroll to
   find is barely better than the glyph it replaced. This is visible the
   instant the card flips and jumps straight to it. */
.fdxPop__diaJump {
  position: absolute; top: clamp(var(--s-4), 3vw, var(--s-6)); right: clamp(var(--s-6), 4vw, var(--s-10));
  z-index: 2;
  display: inline-flex; align-items: center; gap: .35rem;
  padding: .25rem .7rem; border-radius: var(--radius-pill); cursor: pointer;
  border: 1px solid rgb(240 198 74 / .45); background: rgb(240 198 74 / .12);
  color: var(--accent-ink); font-size: var(--fs-xs); line-height: 1.6;
  transition: background var(--dur) var(--ease-out), border-color var(--dur) var(--ease-out);
}
.fdxPop__diaJump[hidden] { display: none; }
.fdxPop__diaJump:hover { background: var(--y-yellow); color: var(--y-black); border-color: transparent; }

.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; }
/* The docked-only controls are wrapped in a span so one rule can hide them all
   in pop-out mode. That wrapper has to be a flex row itself — left as an
   inline box its buttons stack down the bar and the title bar grows to four
   rows tall. */
.fdxLb__dockOnly { display: flex; align-items: center; gap: var(--s-2); }
.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 {
  position: relative;            /* containing block for the image — see below */
  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; }

/* The image is absolutely positioned inside a relative pane, and that is a
   correctness fix, not a layout preference.
Sizing it as an ordinary grid item with `max-height: 100%` did NOT work: the
   pane is a `1fr` row, and a percentage max-height on an item inside an
   `fr` track is a cyclic dependency the engine resolves as "no constraint".
   A 900px diagram in a 611px pane therefore rendered at 900px and was
   cropped top and bottom — at a reported zoom of "100%" you could not see
   the whole diagram, which is the one thing a lightbox exists to do.
   Absolute positioning resolves the percentage against the pane's padding
   box, which is definite, and `inset: 0; margin: auto` centres it. */
.fdxLb__pane img {
  position: absolute; inset: 0; margin: auto;
  max-width: 100%; max-height: 100%;
  width: auto; height: auto;      /* never upscale past the natural size */
  /* --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; }

/* ── FULL SCREEN ───────────────────────────────────────────────────────────
   :fullscreen is applied to .fdxLb__box itself, so the box must drop the
   sizing it carries as a modal or it stays a 46rem window floating in the
   middle of a black screen. UA stylesheets also paint a white backdrop on
   the fullscreen element — on a dark page that is a full-screen white flash
   at the moment of entry, so the background is set explicitly. */
.fdxLb__box:fullscreen {
  width: 100vw; height: 100vh; max-width: none;
  border: 0; border-radius: 0;
  background: #07080c;
}
.fdxLb__box:fullscreen .fdxLb__pane { max-height: none; }
.fdxLb__box::backdrop { background: #07080c; }

/* ── 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;
  /* --fw is written by the resize grip. It is an ALLOWANCE, not a width: the
     box shrinks to the image inside it and only uses --fw as the ceiling.
     Setting it as a hard width instead leaves a square diagram floating in a
     wide checkerboard whenever the grip is dragged sideways — resizing the
     window would visibly grow the window without growing the picture. This
     way the corner drag raises both allowances, the image takes what it can,
     and the window is always exactly the size of what it is showing. */
  width: fit-content;
  min-width: 16rem;                       /* keep the drag bar grabbable */
  max-width: var(--fw, min(30rem, 90vw));
  /* Docked, the box is a fixed 46rem tall so the image always sits in the same
     place as you page through cards. Floating, that same fixed height is just
     a column of empty black under a small picture — and it pushes the bottom
     of the window off the screen. The window shrinks to its content instead. */
  height: auto; max-height: 92dvh;
  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; }

/* ── WHAT THE POP-OUT IS NOT ───────────────────────────────────────────────
   In pop-out the window is JUST the diagram: a bar to drag it by, full screen
   and close. The zoom stepper, the percentage readout, the reset arrow, the
   title and the keyboard hint are all docked-mode furniture, and in a small
   window parked beside a card they take more of it than the picture does.
   Scroll-to-zoom and drag-to-pan still work — the controls go, the
   capabilities stay. */
.fdxLb.is-float .fdxLb__dockOnly,
.fdxLb.is-float .fdxLb__label,
.fdxLb.is-float .fdxLb__hint { display: none; }

/* With the label gone the bar would collapse onto the two buttons and there
   would be almost nothing left to grab. The handle has to keep a width. */
.fdxLb.is-float .fdxLb__bar { padding: var(--s-2) var(--s-4); justify-content: flex-end; }
.fdxLb.is-float .fdxLb__bar::before {
  content: ''; flex: 1; align-self: center; height: 3px; margin-right: var(--s-3);
  border-radius: 2px; background: var(--fdx-hair);
}

/* Smaller window, and the image goes back INTO the flow.
   Docked, it has to be absolute: the pane is a `1fr` row of a fixed-height
   box, and a percentage max-height inside an `fr` track does not resolve.
   Floating, the box is auto-height, so that row sizes to its content — and an
   absolute child contributes nothing, which would collapse the pane to zero
   and leave a title bar with no picture under it. In flow the pane hugs the
   image exactly, which is also what makes the window have no dead space. */
.fdxLb.is-float .fdxLb__pane { max-height: none; }
.fdxLb.is-float .fdxLb__pane img {
  position: static; margin: 0;
  max-width: 100%; max-height: var(--fh, 46vh); width: auto; height: auto;
}

/* ── RESIZE GRIPS (pop-out only) ───────────────────────────────────────────
   The window has no height of its own — it is the title bar plus whatever the
   image comes to — so a grip cannot set a height on the box. It sets the
   box's max-width and the image's max-height, and the window follows. That is
   also why it is correct for both shapes: on a wide diagram the width binds,
   on a tall one the height does, and the picture never distorts either way.

   One grip per corner. A window dragged into the bottom-right of the screen
   has nowhere to grow down-and-right, and its top-left corner is the only one
   with room; with a single SE grip that window simply cannot be made bigger. */
.fdxLb__grip { display: none; }
.fdxLb.is-float .fdxLb__grip {
  display: block; position: absolute; z-index: 4;
  padding: 0; border: 0; background: none;
  /* Without touch-action the browser claims the gesture for panning and the
     grip never receives a pointermove on a touch device. */
  touch-action: none;
}
.fdxLb.is-float .fdxLb__grip::before {
  content: ''; position: absolute; width: .55rem; height: .55rem;
  border: 0 solid var(--text-lo);
  transition: border-color var(--dur) var(--ease-out);
}
.fdxLb.is-float .fdxLb__grip:hover::before,
.fdxLb.is-float .fdxLb__grip:focus-visible::before { border-color: var(--y-yellow); }

/* The BOTTOM corners sit on open pane, so they can be full size.
   The TOP corners share the title bar with the full-screen and close buttons,
   so they are smaller and the bar is padded to keep those buttons clear of
   them — a resize grip that swallows the edge of the close button would be a
   bad trade for a control the user reaches for far more often. */
.fdxLb.is-float .fdxLb__grip--se,
.fdxLb.is-float .fdxLb__grip--sw { width: 1.4rem; height: 1.4rem; bottom: 0; }
.fdxLb.is-float .fdxLb__grip--ne,
.fdxLb.is-float .fdxLb__grip--nw { width: .95rem; height: .95rem; top: 0; }

.fdxLb.is-float .fdxLb__grip--se { right: 0; cursor: nwse-resize; }
.fdxLb.is-float .fdxLb__grip--sw { left: 0;  cursor: nesw-resize; }
.fdxLb.is-float .fdxLb__grip--ne { right: 0; cursor: nesw-resize; }
.fdxLb.is-float .fdxLb__grip--nw { left: 0;  cursor: nwse-resize; }

/* Each bracket opens toward its own corner, so the grip reads as that corner
   of the frame rather than as a decoration that happens to be nearby. */
.fdxLb.is-float .fdxLb__grip--se::before {
  right: .28rem; bottom: .28rem; border-right-width: 2px; border-bottom-width: 2px;
  border-bottom-right-radius: 3px;
}
.fdxLb.is-float .fdxLb__grip--sw::before {
  left: .28rem; bottom: .28rem; border-left-width: 2px; border-bottom-width: 2px;
  border-bottom-left-radius: 3px;
}
.fdxLb.is-float .fdxLb__grip--ne::before {
  right: .2rem; top: .2rem; border-right-width: 2px; border-top-width: 2px;
  border-top-right-radius: 3px;
}
.fdxLb.is-float .fdxLb__grip--nw::before {
  left: .2rem; top: .2rem; border-left-width: 2px; border-top-width: 2px;
  border-top-left-radius: 3px;
}

/* Nothing to resize when the window IS the screen. */
.fdxLb__box:fullscreen .fdxLb__grip { display: none; }

/* Full screen from INSIDE the pop-out: the box is still .is-float, so without
   this it would keep the 30rem width and the dragged translate and sit as a
   small window on a black screen. */
.fdxLb.is-float .fdxLb__box:fullscreen {
  width: 100vw; margin-top: 0; transform: none !important;
}
.fdxLb.is-float .fdxLb__box:fullscreen .fdxLb__pane { max-height: none; }

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

.fdxLb__hint {
  /* Centred <p> + the global measure cap = text centred inside a narrow
     left-aligned box, i.e. visibly off-centre under a wide image. Centring on
     a <p> always has to be paired with releasing the cap. */
  margin: 0; max-width: none;
  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; }
}
