/* ──────────────────────────────────────────────────────────────────
   QuizX — "Stagecraft" Design System
   ──────────────────────────────────────────────────────────────────
   Design brief: this app is operated in conference rooms, halls and
   classrooms on mediocre projectors, cheap office LCDs and TVs, with
   the audience at the back of the room and the lights left on.
   Legibility at distance beats atmosphere. Every decision below is
   made for that condition first.

   THE THREE RULES THIS FILE ENFORCES
   1. Surfaces are OPAQUE. Translucent panels (rgba white over near
      black) resolved to ~1.15:1 against their background — invisible
      as containers on a projector. Every surface is now a real hex on
      a measured luminance ladder.
   2. Separation is carried by BORDERS, not fill deltas. Projectors
      crush small luminance steps; a 2px light keyline survives.
   3. No gradients behind or inside text-bearing elements. Gradients
      shift contrast across the element (one end passes, the other
      fails) and band badly on 6-bit panels.

   BACKGROUNDS
   Never pure black (#000 blooms and smears on cheap LCDs, and DLP
   projectors crush detail into it) and never pure white (glare). The
   canvas sits at #14161D and carries a low-contrast field built from
   the four answer glyphs — see THE CANVAS below.

   USAGE
     <html data-theme="marketing">  public pages
     <html data-theme="product">    authenticated app
     <html data-theme="projector">  screen / wordcloud-screen
     <body class="qx-canvas">       paints the ambient background

   There are no display modes. Bright Room and High Visibility were
   removed: they asked the operator to make a call mid-event that they
   had no good way to make, and forced every layout to work at four
   combinations, so none was tuned. One canvas, set at the weights the
   bright-room case was reaching for.

   Shared components live here, not per page: .qx-nav, .qx-logo.
   ────────────────────────────────────────────────────────────────── */

/* ── Structural tokens (theme-independent) ────────────────────────── */
:root {
  /* Radii — restrained; large radii read as soft/blurry at distance */
  --qx-r-4: 4px;
  --qx-r-6: 6px;
  --qx-r-8: 8px;
  --qx-r-10: 10px;
  --qx-r-12: 12px;
  --qx-r-14: 14px;
  --qx-r-16: 16px;
  --qx-r-20: 20px;
  --qx-r-24: 24px;
  --qx-r-pill: 999px;

  /* Spacing scale (multiples of 4) */
  --qx-s-1: 4px;
  --qx-s-2: 8px;
  --qx-s-3: 12px;
  --qx-s-4: 16px;
  --qx-s-5: 20px;
  --qx-s-6: 24px;
  --qx-s-8: 32px;
  --qx-s-10: 40px;
  --qx-s-12: 48px;

  /* Elevation. Shadows are structural hints only — they are NEVER the
     sole separator, because they vanish in a bright room. */
  --qx-shadow-sm: 0 1px 2px rgba(0, 0, 0, .25);
  --qx-shadow-md: 0 8px 24px rgba(0, 0, 0, .35);
  --qx-shadow-lg: 0 16px 48px rgba(0, 0, 0, .5);

  /* Keyline widths — the primary separation device.
     Set at the old High-Visibility weights: 1.5px keylines rendered soft
     and inconsistent once a projector resampled them, and borders are the
     one device rule 2 depends on. Whole pixels only. */
  --qx-line: 2px;
  --qx-line-2: 3px;
  --qx-line-3: 4px;

  /* Motion */
  --qx-t-fast: .15s;
  --qx-t-med: .22s;
  --qx-t-slow: .35s;

  /* Z-index scale */
  --qx-z-base: 1;
  --qx-z-sticky: 50;
  --qx-z-overlay: 90;
  --qx-z-modal: 100;
  --qx-z-toast: 150;

  /* ── Distance type scale ──────────────────────────────────────────
     Sized against viewing distance, not screen size. The lower bound
     of each clamp is the "readable on a phone at arm's length" floor;
     the upper bound is "readable from the back row". Minimums are
     deliberately high — 11px labels are a laptop-only luxury. */
     This scale sits midway between the old default and the old High
     Visibility scale. Two switchable scales meant every layout had to
     work at both, so both were compromises; one scale tuned to the real
     viewing distance is what the rooms actually need. */
  --qx-fs-micro:   clamp(13px, .92vw, 15px);   /* dense table meta   */
  --qx-fs-label:   clamp(14px, 1.05vw, 17px);  /* eyebrows, captions */
  --qx-fs-body:    clamp(16px, 1.15vw, 20px);  /* UI body            */
  --qx-fs-lead:    clamp(18px, 1.45vw, 24px);  /* emphasised body    */
  --qx-fs-h3:      clamp(21px, 1.85vw, 30px);
  --qx-fs-h2:      clamp(28px, 2.6vw, 43px);
  --qx-fs-h1:      clamp(34px, 3.45vw, 60px);
  /* Stage sizes — projector/game screens only */
  --qx-fs-stage:   clamp(37px, 4.3vw, 82px);   /* question text      */
  --qx-fs-tile:    clamp(26px, 3vw, 54px);     /* answer tile text   */
  --qx-fs-hero:    clamp(64px, 11vw, 190px);   /* PIN, countdown     */

  --qx-tracking-tight: -.02em;
  --qx-tracking-wide:  .08em;
  --qx-leading-tight:  1.15;
  --qx-leading-body:   1.5;
}

/* ══════════════════════════════════════════════════════════════════
   DARK EVENT MODE — the default surface ladder
   Measured against #14161D:
     text  18.07:1 · muted 11.46:1 · subtle 7.73:1
     border 3.85:1 · keyline 7.12:1
   ══════════════════════════════════════════════════════════════════ */
[data-theme="marketing"],
[data-theme="product"],
[data-theme="projector"] {
  --qx-bg:          #14161D;
  --qx-bg-2:        #191C24;
  --qx-surface:     #232732;
  --qx-surface-2:   #313644;
  --qx-surface-3:   #3E4454;

  --qx-text:        #FFFFFF;
  --qx-muted:       #C9CEDA;
  --qx-faint:       #A2AAB9;   /* still 7.73:1 — "faint" is visual, not contrast */

  --qx-border:      #6B7488;
  --qx-border-2:    #8791A5;
  --qx-keyline:     #9AA3B4;

  /* Semantic — validated on --qx-surface */
  --qx-success:     #3FD68C;   /* 7.96:1 */
  --qx-danger:      #FF6B85;   /* 5.47:1 */
  --qx-warning:     #F5B301;   /* 8.05:1 */
  --qx-info:        #58BFFF;   /* 7.32:1 */
  --qx-success-solid: #10693A; /* white text 6.77:1 */
  --qx-danger-solid:  #B3122F; /* white text 6.90:1 */

  /* Answer tile fills — solid, never gradients */
  --qx-tile-a:      #B3122F;  --qx-tile-a-ink: #FFFFFF;  /* 6.90:1 */
  --qx-tile-b:      #14509E;  --qx-tile-b-ink: #FFFFFF;  /* 7.86:1 */
  --qx-tile-c:      #F5B301;  --qx-tile-c-ink: #1A1200;  /* 10.02:1 */
  --qx-tile-d:      #10693A;  --qx-tile-d-ink: #FFFFFF;  /* 6.77:1 */
  --qx-tile-dim:    #3A3F4D;  --qx-tile-dim-ink: #C9CEDA;/* 6.67:1 */

  /* Ring used for "this is the answer" emphasis. Must contrast with the
     tile fill AND with the page behind it, so it flips per room mode. */
  --qx-state-ring:  #FFFFFF;
  --qx-state-halo:  rgba(255, 255, 255, .25);

  /* Word-cloud ramp. Words in a cloud are text, not decoration, so each entry
     is held to 4.5:1 against its own field. The cloud assigns these by rank in
     markup (`color: var(--qx-word-N)`), so the ramp re-resolves per theme with
     no second code path. Measured by scripts/contrast-check.js. */
  --qx-word-1: #46D39A;
  --qx-word-2: #A88BFF;
  --qx-word-3: #3EC2FF;
  --qx-word-4: #FFD700;
  --qx-word-5: #FF8FB0;
  --qx-word-6: #FFA94D;
  --qx-word-7: #C4A5FF;
  --qx-word-8: #5AE8F0;
}

/* Brand accents differ per theme — hue is identity, luminance is fixed
   so each one clears 4.5:1 in the role it actually plays. */
[data-theme="marketing"] {
  --qx-accent:      #2E9E6B;   /* green, solid-button fill  */
  --qx-accent-2:    #46D39A;
  --qx-accent-ink:  #FFFFFF;
  --qx-accent-text: #5DE0AB;   /* accent used AS text on dark */
  --qx-font:         'Nunito', system-ui, -apple-system, sans-serif;
  --qx-font-display: 'Nunito', system-ui, -apple-system, sans-serif;
}
[data-theme="product"],
[data-theme="projector"] {
  --qx-accent:      #6D3BF5;   /* white text 5.84:1 */
  --qx-accent-2:    #8B5CF6;
  --qx-accent-ink:  #FFFFFF;
  --qx-accent-text: #C4A5FF;   /* 7.23:1 on surface */
  --qx-purple:      #8B5CF6;
  --qx-green:       var(--qx-success);
  --qx-blue:        var(--qx-info);
  --qx-yellow:      var(--qx-warning);
  --qx-red:         var(--qx-danger);
  /* Display face: Plus Jakarta Sans at heavy weights. Syne is dropped
     from stage roles — its geometric forms and tight apertures close
     up at distance, which is precisely where it was being used. */
  --qx-font:         'Plus Jakarta Sans', system-ui, -apple-system, sans-serif;
  --qx-font-display: 'Plus Jakarta Sans', system-ui, -apple-system, sans-serif;
}
/* Marketing keeps its green semantics too */
[data-theme="marketing"] {
  --qx-purple: #8B5CF6;
  --qx-green:  var(--qx-success);
  --qx-blue:   var(--qx-info);
  --qx-yellow: var(--qx-warning);
  --qx-red:    var(--qx-danger);
}



/* ══════════════════════════════════════════════════════════════════
   THE CANVAS — one tuned surface, no modes

   This replaced two switchable modes (Bright Room and High Visibility).
   Operators were being handed a decision they had no good way to make
   mid-event, and every layout had to survive four combinations of the
   two switches, so none of the four was actually tuned. What is left is
   the dark canvas at the weights the bright-room case was reaching for:
   heavier keylines, a stronger border colour, and a larger type scale.

   The canvas is not flat. It carries a very low-contrast field built
   from the game's own alphabet — the four answer glyphs — plus two
   broad colour blooms. Everything in it is held under a ~2% luminance
   delta so it reads as texture up close and resolves to an even field
   from the back of a room. Text never sits on the pattern alone at
   small sizes; panels are opaque and sit on top of it.
   ══════════════════════════════════════════════════════════════════ */
[data-theme="marketing"],
[data-theme="product"],
[data-theme="projector"] {
  /* Borders step up a rung: structure has to survive a washed-out image */
  --qx-border: #8791A5;

  /* Ambient blooms. Alpha is deliberately low — these are atmosphere,
     never a surface, and nothing relies on them for separation. */
  --qx-glow-1: rgba(109, 59, 245, .10);   /* brand purple, top-left  */
  --qx-glow-2: rgba(46, 158, 107, .08);   /* brand green, bottom-right */
  --qx-figure: rgba(255, 255, 255, .022); /* constellation glyph ink */
}

/* ── The constellation ─────────────────────────────────────────────
   ▲ ◆ ● ■ are the answer markers players already read on every tile, so
   the wallpaper is the product's own alphabet rather than a stock dot
   field. Painted directly on the element with background-attachment:
   fixed — that is what lets the navbar carry the *identical* background
   and have the field run through it without a seam.

   Deliberately static. An earlier pass drifted it; behind a live
   question that motion competes with the content it is supposed to sit
   behind, and it gave the projector image something to chase. */
.qx-canvas,
.qx-canvas-bar {
  background-color: var(--qx-bg);
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='420' height='420' viewBox='0 0 420 420'%3E%3Cg fill='%23ffffff' fill-opacity='0.022'%3E%3Cpath d='M60 34 L86 80 L34 80 Z'/%3E%3Ccircle cx='330' cy='96' r='24'/%3E%3Crect x='196' y='188' width='46' height='46' rx='6'/%3E%3Cpath d='M96 300 L124 328 L96 356 L68 328 Z'/%3E%3Ccircle cx='356' cy='330' r='18'/%3E%3Cpath d='M270 40 L292 78 L248 78 Z'/%3E%3C/g%3E%3C/svg%3E"),
    radial-gradient(ellipse 70% 55% at 12% 0%,   var(--qx-glow-1), transparent 60%),
    radial-gradient(ellipse 60% 50% at 88% 100%, var(--qx-glow-2), transparent 60%);
  background-size: 420px 420px, 140% 115%, 130% 105%;
  background-position: 0 0, 0 0, 100% 100%;
  background-repeat: repeat, no-repeat, no-repeat;
  background-attachment: fixed;
}

/* body.qx-canvas outranks each page's own `body { background: … }` rule,
   which is what the inline per-page stylesheets still declare. */
body.qx-canvas { background-color: var(--qx-bg); }

/* The projector theme runs the field at roughly half strength. A laptop
   at arm's length renders 2.2% cleanly; a projector lifts its own black
   floor and adds ambient light on top, which pushes the same value up to
   where it starts reading as dirt on the lens behind a live question.
   Same field, quieter — the pages still look like one another. */
[data-theme="projector"].qx-canvas,
[data-theme="projector"] .qx-canvas,
[data-theme="projector"] .qx-canvas-bar,
body[data-theme="projector"].qx-canvas {
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='420' height='420' viewBox='0 0 420 420'%3E%3Cg fill='%23ffffff' fill-opacity='0.012'%3E%3Cpath d='M60 34 L86 80 L34 80 Z'/%3E%3Ccircle cx='330' cy='96' r='24'/%3E%3Crect x='196' y='188' width='46' height='46' rx='6'/%3E%3Cpath d='M96 300 L124 328 L96 356 L68 328 Z'/%3E%3Ccircle cx='356' cy='330' r='18'/%3E%3Cpath d='M270 40 L292 78 L248 78 Z'/%3E%3C/g%3E%3C/svg%3E"),
    radial-gradient(ellipse 70% 55% at 12% 0%,   rgba(109, 59, 245, .06), transparent 60%),
    radial-gradient(ellipse 60% 50% at 88% 100%, rgba(46, 158, 107, .05), transparent 60%);
}

/* The projector theme drops translucency and blur outright: both
   collapse under ambient light. Laptop-viewed themes keep them. */
[data-theme="projector"] * {
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
  text-shadow: none !important;
}
[data-theme="projector"] .qx-panel,
[data-theme="projector"] .qx-tile { box-shadow: none !important; }

/* ── Shared navbar ─────────────────────────────────────────────────
   Every page hardcoded its own bar fill — rgba(13,13,26,.88),
   rgba(10,10,26,.95), rgba(0,0,0,.55) — none of them derived from the
   canvas, so when the canvas changed the bar stayed where it was and
   read as a foreign strip pasted over the top of the page.

   The bar is not a different surface. It carries the same canvas via
   .qx-canvas-bar (identical background, background-attachment: fixed,
   so the glyph field lines up across the seam) and is separated from
   the page by a keyline alone — rule 2, applied to the one element
   that was ignoring it. It stays opaque so scrolled content passing
   underneath cannot show through and muddy the nav labels. */
.qx-nav {
  position: sticky;
  top: 0;
  z-index: var(--qx-z-sticky);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--qx-s-4);
  padding: 0 clamp(16px, 4vw, 48px);
  height: 64px;
  min-height: 64px;
  box-sizing: border-box;
  border-bottom: var(--qx-line) solid var(--qx-border);
  font-size: 14px;
}
/* Plain text link in the public bar (Contact, Pricing, ...). Buttons keep
   their page classes; this only pins the size and weight of text links so
   every public page's bar reads the same. */
.qx-nav .qx-nav-link,
.qx-nav .nav-link {
  font-size: 14px;
  font-weight: 700;
  color: var(--qx-muted);
  text-decoration: none;
}
.qx-nav .qx-nav-link:hover,
.qx-nav .nav-link:hover { color: var(--qx-text); }

/* ── The logo ──────────────────────────────────────────────────────
   One asset, everywhere: /img/logo-on-dark.svg (all themes are dark;
   /img/logo.svg is the navy-wordmark master for light backgrounds).
   Markup, identical on every page:
     <a href="/" class="qx-logo" aria-label="QuizX">
       <img class="qx-logo__img" src="/img/logo-on-dark.svg" alt="QuizX" width="139" height="42">
     </a>
   Size is set by height only: 32px in the bar, 28px on auth cards and in
   footers, 40px on the player join screen. */
.qx-logo {
  display: inline-flex;
  align-items: center;
  line-height: 0;
  text-decoration: none;
  flex: none;
}
.qx-logo__img { display: block; height: 32px; width: auto; }
/* Mark-only twin, shown where the full lockup does not fit (collapsed
   sidebar, very narrow library bar). Hidden by default. */
.qx-logo__icon { display: none; height: 32px; width: 32px; }
.qx-logo--icon .qx-logo__img { display: none; }
.qx-logo--icon .qx-logo__icon { display: block; }
.qx-logo:hover { opacity: .85; }
.qx-logo:focus-visible {
  outline: var(--qx-line-2) solid var(--qx-accent-text);
  outline-offset: 3px;
  border-radius: var(--qx-r-6);
}
/* Small variant for dense bars (projector test screen) */
.qx-logo--sm .qx-logo__img { height: 24px; }
/* Large, centred variant — auth cards, welcome, player join, /t. */
.qx-logo--lg { justify-content: center; margin-bottom: var(--qx-s-6); }
.qx-logo--lg .qx-logo__img { height: 28px; }
.qx-logo--block { display: flex; }
.qx-logo--footer .qx-logo__img { height: 28px; }
/* Player join screen: the logo is the page heading on a phone. */
#join-screen .qx-logo--lg .qx-logo__img { height: 40px; }

/* Respect the OS reduced-motion setting everywhere. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}

/* ══════════════════════════════════════════════════════════════════
   STAGE COMPONENTS — the live-event primitives
   ══════════════════════════════════════════════════════════════════ */

/* Page canvas. A single flat field: no radial glows behind content,
   because a radial gradient makes contrast a function of *where the
   text happens to sit*, which is untestable and unreliable. */
.qx-stage {
  background: var(--qx-bg);
  color: var(--qx-text);
  font-family: var(--qx-font);
}

/* Panel — an opaque container with a real keyline. */
.qx-panel {
  background: var(--qx-surface);
  border: var(--qx-line) solid var(--qx-border);
  border-radius: var(--qx-r-16);
  color: var(--qx-text);
}
.qx-panel-raised {
  background: var(--qx-surface-2);
  border-color: var(--qx-border-2);
}

/* ── Answer tiles ────────────────────────────────────────────────
   The signature element. Solid fill, heavy type, a letter chip and a
   geometric shape marker. Identity is carried by THREE redundant
   channels — colour, letter and shape — so the tiles still work for
   colour-blind players and on a projector with a failing colour wheel. */
.qx-tile {
  --tile-fill: var(--qx-surface-2);
  --tile-ink:  var(--qx-text);
  display: flex;
  align-items: center;
  gap: clamp(12px, 1.6vw, 26px);
  padding: clamp(16px, 2.2vh, 38px) clamp(20px, 2.6vw, 42px);
  background: var(--tile-fill);
  color: var(--tile-ink);
  border: var(--qx-line-2) solid var(--qx-keyline);
  border-radius: var(--qx-r-14);
  font-family: var(--qx-font-display);
  font-size: var(--qx-fs-tile);
  font-weight: 800;
  line-height: var(--qx-leading-tight);
  letter-spacing: var(--qx-tracking-tight);
  text-align: left;
  overflow: hidden;
  transition: transform var(--qx-t-med), border-color var(--qx-t-med);
}
.qx-tile-a { --tile-fill: var(--qx-tile-a); --tile-ink: var(--qx-tile-a-ink); }
.qx-tile-b { --tile-fill: var(--qx-tile-b); --tile-ink: var(--qx-tile-b-ink); }
.qx-tile-c { --tile-fill: var(--qx-tile-c); --tile-ink: var(--qx-tile-c-ink); }
.qx-tile-d { --tile-fill: var(--qx-tile-d); --tile-ink: var(--qx-tile-d-ink); }

/* Letter + shape marker sits in a chip cut from the tile ink, so it
   inherits the tile's guaranteed contrast rather than inventing its own. */
.qx-tile-marker {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: .4em;
  font-weight: 900;
  font-variant-numeric: tabular-nums;
  font-size: 1.05em;
  line-height: 1;
}
.qx-tile-shape {
  width: .82em; height: .82em;
  flex-shrink: 0;
  background: currentColor;
}
.qx-tile-a .qx-tile-shape { clip-path: polygon(50% 0%, 100% 100%, 0% 100%); }        /* triangle */
.qx-tile-b .qx-tile-shape { clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%); }/* diamond  */
.qx-tile-c .qx-tile-shape { border-radius: 50%; }                                     /* circle   */
.qx-tile-d .qx-tile-shape { border-radius: 2px; }                                     /* square   */
.qx-tile-text {
  flex: 1;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  min-width: 0;
}

/* Reveal states. Correctness is signalled by keyline weight, a badge
   and scale — never by colour alone, and never by fading a tile into
   illegibility. The old treatment dropped wrong answers to opacity:.35,
   which put their text below 2:1; they now flatten to a slate that
   still holds text at 6.67:1 so the audience can still read what the
   wrong answers actually were. */
.qx-tile.is-correct {
  border-color: var(--qx-state-ring);
  border-width: var(--qx-line-3);
  transform: scale(1.02);
  box-shadow: 0 0 0 6px var(--qx-state-halo);
}
.qx-tile.is-correct::after {
  content: "✓";
  margin-left: auto;
  font-size: 1.1em;
  font-weight: 900;
  flex-shrink: 0;
}
.qx-tile.is-incorrect {
  --tile-fill: var(--qx-tile-dim);
  --tile-ink:  var(--qx-tile-dim-ink);
  border-color: var(--qx-border);
}
.qx-tile.is-incorrect::after {
  content: "✕";
  margin-left: auto;
  font-size: 1em;
  font-weight: 900;
  opacity: .8;
  flex-shrink: 0;
}

/* ── Timer ───────────────────────────────────────────────────────
   Time remaining is encoded three ways at once: a shrinking bar
   (length), a digit readout (number) and an urgency state (colour +
   pulse). Length is the channel that reads from the back of a room. */
.qx-timer {
  display: flex;
  align-items: center;
  gap: var(--qx-s-4);
}
.qx-timer-digits {
  font-family: var(--qx-font-display);
  font-size: var(--qx-fs-h2);
  font-weight: 900;
  font-variant-numeric: tabular-nums;
  color: var(--qx-text);
  min-width: 2.2ch;
  text-align: right;
}
.qx-timer-track {
  flex: 1;
  height: clamp(12px, 1.4vh, 22px);
  background: var(--qx-surface-2);
  border: var(--qx-line) solid var(--qx-border);
  border-radius: var(--qx-r-pill);
  overflow: hidden;
  min-width: 80px;
}
.qx-timer-fill {
  height: 100%;
  background: var(--qx-success);
  transition: width 1s linear, background var(--qx-t-med);
}
.qx-timer.is-warning .qx-timer-fill { background: var(--qx-warning); }
.qx-timer.is-urgent  .qx-timer-fill { background: var(--qx-danger); }
.qx-timer.is-urgent  .qx-timer-digits {
  color: var(--qx-danger);
  animation: qx-pulse 1s steps(2, end) infinite;
}
@keyframes qx-pulse { 50% { opacity: .45; } }

/* ── Scoreboard rows ─────────────────────────────────────────────
   Built as a real table-ish row: rank in a fixed column, name
   flexible, score tabular and right-aligned so digits line up and the
   eye can scan the column without re-reading. */
.qx-score-row {
  display: flex;
  align-items: center;
  gap: clamp(12px, 1.5vw, 24px);
  padding: clamp(10px, 1.3vh, 20px) clamp(14px, 1.6vw, 26px);
  background: var(--qx-surface);
  border: var(--qx-line) solid var(--qx-border);
  border-radius: var(--qx-r-12);
  font-size: var(--qx-fs-lead);
  font-weight: 800;
  color: var(--qx-text);
}
.qx-score-rank {
  flex-shrink: 0;
  min-width: 2.5ch;
  font-family: var(--qx-font-display);
  font-weight: 900;
  font-variant-numeric: tabular-nums;
  color: var(--qx-muted);
  text-align: right;
}
.qx-score-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.qx-score-value {
  flex-shrink: 0;
  font-family: var(--qx-font-display);
  font-weight: 900;
  font-variant-numeric: tabular-nums;
  text-align: right;
}
/* Leader gets a keyline and a rank glyph, not just a colour wash. */
.qx-score-row.is-leader {
  background: var(--qx-surface-2);
  border-color: var(--qx-keyline);
  border-width: var(--qx-line-2);
}

/* ── Status chip ─────────────────────────────────────────────────
   Always pairs a dot AND a word — state is never colour-only. */
.qx-chip {
  display: inline-flex;
  align-items: center;
  gap: .5em;
  padding: .35em .85em;
  border-radius: var(--qx-r-pill);
  border: var(--qx-line) solid var(--qx-border);
  background: var(--qx-surface-2);
  color: var(--qx-text);
  font-size: var(--qx-fs-label);
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.qx-chip-dot {
  width: .6em; height: .6em;
  border-radius: 50%;
  background: currentColor;
  flex-shrink: 0;
}
.qx-chip-live     { color: var(--qx-success); border-color: currentColor; }
.qx-chip-waiting  { color: var(--qx-warning); border-color: currentColor; }
.qx-chip-ended    { color: var(--qx-faint);   border-color: currentColor; }

/* ── Buttons ─────────────────────────────────────────────────────
   Minimum 48px touch target; solid fills only. */
.qx-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .55em;
  min-height: 48px;
  padding: 0 clamp(18px, 1.8vw, 30px);
  border-radius: var(--qx-r-12);
  border: var(--qx-line-2) solid transparent;
  font-family: var(--qx-font);
  font-size: var(--qx-fs-body);
  font-weight: 800;
  cursor: pointer;
  transition: background var(--qx-t-fast), border-color var(--qx-t-fast);
}
.qx-btn-primary {
  background: var(--qx-accent);
  color: var(--qx-accent-ink);
  border-color: var(--qx-accent);
}
.qx-btn-primary:hover { background: var(--qx-accent-2); border-color: var(--qx-accent-2); }
.qx-btn-secondary {
  background: var(--qx-surface-2);
  color: var(--qx-text);
  border-color: var(--qx-border);
}
.qx-btn-secondary:hover { background: var(--qx-surface-3); border-color: var(--qx-border-2); }
.qx-btn-danger {
  background: var(--qx-danger-solid);
  color: #FFFFFF;
  border-color: var(--qx-danger-solid);
}

/* Focus is never removed — it is made unmistakable. A double ring
   keeps it visible whether it lands on a light or a dark surface. */
:where(a, button, input, select, textarea, [tabindex]):focus-visible,
.qx-focus-ring:focus-visible {
  outline: 3px solid #FFFFFF;
  outline-offset: 2px;
  box-shadow: 0 0 0 6px var(--qx-accent);
}

/* ── Form fields ─────────────────────────────────────────────────
   The old fields were translucent white over dark, which put their
   boundary near 1.2:1. Opaque fill plus a real keyline. */
.qx-field {
  width: 100%;
  min-height: 48px;
  padding: 0 var(--qx-s-4);
  background: var(--qx-surface-2);
  color: var(--qx-text);
  border: var(--qx-line-2) solid var(--qx-border);
  border-radius: var(--qx-r-10);
  font-family: var(--qx-font);
  font-size: var(--qx-fs-body);
  font-weight: 700;
}
.qx-field::placeholder { color: var(--qx-faint); }
.qx-field:focus { border-color: var(--qx-accent-2); outline: none; }

/* ── Utilities ───────────────────────────────────────────────────── */
.qx-font          { font-family: var(--qx-font); }
.qx-font-display  { font-family: var(--qx-font-display); }
.qx-tabular       { font-variant-numeric: tabular-nums; }

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

/* ──────────────────────────────────────────────────────────────────
   App Shell — persistent left-side nav + topbar
   ────────────────────────────────────────────────────────────────── */

/* Root container — position-based layout. Works correctly even before
   shell.js runs: content offsets via margin-left, aside is fixed.    */
.qx-shell {
  --qx-shell-w: 232px;
  min-height: 100vh;
  background: var(--qx-bg);
  color: var(--qx-text);
  font-family: var(--qx-font);
}

/* Left aside is fixed to the viewport */
.qx-shell-aside {
  position: fixed;
  top: 0;
  left: 0;
  width: var(--qx-shell-w);
  height: 100vh;
  background: var(--qx-bg-2);
  border-right: var(--qx-line) solid var(--qx-border);
  display: flex;
  flex-direction: column;
  padding: 18px 14px;
  overflow-y: auto;
  z-index: var(--qx-z-sticky);
}
.qx-shell-aside::-webkit-scrollbar { width: 4px; }
.qx-shell-aside::-webkit-scrollbar-thumb { background: var(--qx-border-2); border-radius: 4px; }

/* Logo */
.qx-shell-logo {
  display: flex;
  align-items: center;
  padding: 4px 10px 18px;
  line-height: 0;
  text-decoration: none;
  flex-shrink: 0;
}
.qx-shell-logo img { display: block; height: 32px; width: auto; }

/* Nav list */
.qx-shell-nav {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1;
  min-height: 0;
}
.qx-shell-nav-divider {
  height: 1px;
  background: var(--qx-border);
  margin: 10px 6px;
  flex-shrink: 0;
}
.qx-shell-nav-section {
  font-size: var(--qx-fs-micro);
  font-weight: 900;
  text-transform: uppercase;
  letter-spacing: var(--qx-tracking-wide);
  color: var(--qx-faint);
  padding: 14px 12px 6px;
  flex-shrink: 0;
}
.qx-shell-nav-item {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 10px 12px;
  border-radius: var(--qx-r-10);
  border: none;
  background: transparent;
  color: var(--qx-muted);
  cursor: pointer;
  font-size: var(--qx-fs-body);
  font-weight: 700;
  font-family: inherit;
  text-decoration: none;
  text-align: left;
  transition: color var(--qx-t-fast), background var(--qx-t-fast);
  white-space: nowrap;
  width: 100%;
  min-height: 44px;
}
.qx-shell-nav-item:hover {
  color: var(--qx-text);
  background: var(--qx-surface);
}
/* Active state carries a left bar as well as a fill, so it survives a
   washed-out projection where the fill difference disappears. */
.qx-shell-nav-item.is-active {
  color: var(--qx-text);
  background: var(--qx-surface-2);
  box-shadow: inset 3px 0 0 var(--qx-accent-text);
}
.qx-shell-nav-icon {
  width: 18px;
  height: 18px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.qx-shell-nav-icon svg {
  width: 17px;
  height: 17px;
  stroke: currentColor;
  fill: none;
}
.qx-shell-nav-badge {
  margin-left: auto;
  background: var(--qx-accent);
  color: var(--qx-accent-ink);
  font-size: var(--qx-fs-micro);
  font-weight: 900;
  padding: 2px 8px;
  border-radius: var(--qx-r-pill);
}

/* User chip at bottom of aside */
.qx-shell-user {
  flex-shrink: 0;
  padding-top: 12px;
  border-top: var(--qx-line) solid var(--qx-border);
  margin-top: 8px;
  position: relative;
}
.qx-shell-user-chip {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 7px 10px 7px 7px;
  border-radius: var(--qx-r-10);
  background: var(--qx-surface);
  border: var(--qx-line) solid var(--qx-border);
  cursor: pointer;
  transition: background var(--qx-t-fast);
  width: 100%;
  font-family: inherit;
  text-align: left;
  min-height: 44px;
}
.qx-shell-user-chip:hover { background: var(--qx-surface-2); }
.qx-shell-user-avatar {
  width: 30px;
  height: 30px;
  border-radius: var(--qx-r-8);
  background: var(--qx-accent);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
  font-weight: 900;
  color: var(--qx-accent-ink);
  flex-shrink: 0;
  overflow: hidden;
}
.qx-shell-user-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
.qx-shell-user-text { flex: 1; min-width: 0; line-height: 1.3; }
.qx-shell-user-name {
  font-size: var(--qx-fs-label); font-weight: 800; color: var(--qx-text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.qx-shell-user-plan {
  font-size: var(--qx-fs-micro); font-weight: 800; letter-spacing: .04em;
  color: var(--qx-muted); text-transform: uppercase;
}
.qx-shell-user-caret { opacity: .7; flex-shrink: 0; transition: transform var(--qx-t-fast); }
.qx-shell-user[data-open="1"] .qx-shell-user-caret { transform: rotate(180deg); }

/* Dropdown menu that opens upward from the user chip. */
.qx-shell-user-menu {
  position: absolute;
  bottom: calc(100% + 6px);
  left: 0;
  right: 0;
  background: var(--qx-surface-2);
  border: var(--qx-line-2) solid var(--qx-border-2);
  border-radius: var(--qx-r-12);
  padding: 6px;
  box-shadow: var(--qx-shadow-lg);
  display: none;
  z-index: var(--qx-z-modal);
}
.qx-shell-user[data-open="1"] .qx-shell-user-menu { display: block; }
.qx-shell-user-menu a,
.qx-shell-user-menu button {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 10px 12px;
  border: none;
  background: transparent;
  color: var(--qx-text);
  font: inherit;
  font-size: var(--qx-fs-label);
  font-weight: 700;
  cursor: pointer;
  border-radius: var(--qx-r-8);
  text-align: left;
  text-decoration: none;
  min-height: 44px;
}
.qx-shell-user-menu a:hover,
.qx-shell-user-menu button:hover { background: var(--qx-surface-3); }
.qx-shell-user-menu .qx-shell-user-menu-icon {
  width: 20px; text-align: center; opacity: .9; font-size: 14px; flex-shrink: 0;
}
.qx-shell-user-menu hr {
  border: none;
  border-top: var(--qx-line) solid var(--qx-border);
  margin: 4px 6px;
}
.qx-shell-user-menu .qx-shell-user-menu-logout { color: var(--qx-danger); }

/* Main pane — offset from the fixed aside. Same offset applies
   to .qx-shell-content when it is still a direct child (i.e. before
   shell.js wraps it in .qx-shell-main).                              */
.qx-shell-main,
.qx-shell > .qx-shell-content {
  margin-left: var(--qx-shell-w);
  min-height: 100vh;
  background: var(--qx-bg);
}
/* If shell.js fails to mount, fall back to no-offset layout. */
.qx-shell.qx-shell-failed > .qx-shell-content,
.qx-shell.qx-shell-failed .qx-shell-main { margin-left: 0; }
.qx-shell-main {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

/* Topbar inside main */
/* The app shell's bar carries the canvas for the same reason the marketing
   navbar does — so the field is unbroken and the bar is separated by its
   keyline rather than by being a different colour. */
.qx-shell-topbar {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 14px 28px;
  border-bottom: var(--qx-line) solid var(--qx-border);
  background-color: var(--qx-bg);
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='420' height='420' viewBox='0 0 420 420'%3E%3Cg fill='%23ffffff' fill-opacity='0.022'%3E%3Cpath d='M60 34 L86 80 L34 80 Z'/%3E%3Ccircle cx='330' cy='96' r='24'/%3E%3Crect x='196' y='188' width='46' height='46' rx='6'/%3E%3Cpath d='M96 300 L124 328 L96 356 L68 328 Z'/%3E%3Ccircle cx='356' cy='330' r='18'/%3E%3Cpath d='M270 40 L292 78 L248 78 Z'/%3E%3C/g%3E%3C/svg%3E");
  background-size: 420px 420px;
  background-attachment: fixed;
  flex-shrink: 0;
  min-height: 60px;
}
.qx-shell-topbar-title {
  flex: 1;
  font-family: var(--qx-font-display);
  font-size: var(--qx-fs-h3);
  font-weight: 800;
  color: var(--qx-text);
  letter-spacing: -.01em;
  min-width: 0;
}
.qx-shell-topbar-search {
  display: flex;
  align-items: center;
  gap: 8px;
  background: var(--qx-surface);
  border: var(--qx-line) solid var(--qx-border);
  border-radius: var(--qx-r-10);
  padding: 8px 14px;
  width: 280px;
  max-width: 32vw;
  min-height: 44px;
  transition: border-color var(--qx-t-fast), background var(--qx-t-fast);
}
.qx-shell-topbar-search:focus-within {
  border-color: var(--qx-accent-2);
  background: var(--qx-surface-2);
}
.qx-shell-topbar-search input {
  background: none; border: none; outline: none;
  color: var(--qx-text); font: inherit;
  font-size: var(--qx-fs-body); font-weight: 600; flex: 1; min-width: 0;
}
.qx-shell-topbar-search input::placeholder { color: var(--qx-faint); }
.qx-shell-topbar-search svg { opacity: .8; flex-shrink: 0; stroke: currentColor; fill: none; }

/* Main scroll area */
.qx-shell-content {
  flex: 1;
  overflow-y: auto;
  padding: 24px 28px 48px;
  min-height: 0;
}
.qx-shell-content::-webkit-scrollbar { width: 6px; }
.qx-shell-content::-webkit-scrollbar-thumb { background: var(--qx-border-2); border-radius: 4px; }

/* Mobile burger (visible <768px) */
.qx-shell-burger {
  display: none;
  background: none;
  border: none;
  color: var(--qx-text);
  padding: 6px;
  cursor: pointer;
  flex-shrink: 0;
}
.qx-shell-burger svg { width: 22px; height: 22px; stroke: currentColor; fill: none; }

/* Overlay behind open mobile drawer */
.qx-shell-overlay {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, .65);
  z-index: calc(var(--qx-z-sticky) - 1);
}

/* ── Responsive: tablet (769–960px) — slide-out drawer ── */
@media (max-width: 960px) {
  .qx-shell-aside {
    width: 80vw;
    max-width: 280px;
    transform: translateX(-100%);
    transition: transform var(--qx-t-med);
    box-shadow: var(--qx-shadow-lg);
  }
  .qx-shell-aside.is-open { transform: translateX(0); }
  .qx-shell-main,
  .qx-shell > .qx-shell-content { margin-left: 0; }
  .qx-shell-burger { display: inline-flex; align-items: center; }
  .qx-shell-aside.is-open ~ .qx-shell-overlay { display: block; }
  .qx-shell-topbar-search { width: 100%; max-width: none; }
}

/* ── Responsive: phone (≤768px) — bottom tab bar ────────────────
   Replaces the slide-out drawer with a fixed bottom bar so the
   nav matches the /host dashboard on every authenticated page.    */
@media (max-width: 768px) {
  /* Cancel the tablet drawer transform */
  .qx-shell-aside,
  .qx-shell-aside.is-open {
    transform: none !important;
  }

  /* Convert sidebar to fixed bottom tab bar. Opaque, not blurred —
     backdrop-filter is expensive on old phones and the translucency
     put nav labels over unpredictable content. */
  .qx-shell-aside {
    position: fixed !important;
    bottom: 0; left: 0; right: 0; top: auto !important;
    width: 100% !important; max-width: none !important;
    height: calc(62px + env(safe-area-inset-bottom, 0px)) !important;
    min-height: calc(62px + env(safe-area-inset-bottom, 0px)) !important;
    padding: 0 0 env(safe-area-inset-bottom, 0px) 0 !important;
    flex-direction: row !important;
    border-right: none !important;
    border-top: var(--qx-line-2) solid var(--qx-border);
    background: var(--qx-bg-2) !important;
    backdrop-filter: none;
    z-index: 500 !important;
    overflow: visible !important;
    box-shadow: 0 -4px 24px rgba(0,0,0,.45) !important;
    transition: none !important;
  }

  /* Logo and user chip hidden — no room in bottom bar */
  .qx-shell-logo { display: none !important; }
  .qx-shell-user { display: none !important; }
  .qx-shell-nav-divider { display: none !important; }

  /* Nav row horizontal */
  .qx-shell-nav {
    flex-direction: row !important;
    padding: 0 !important;
    width: 100% !important;
    overflow-x: auto; overflow-y: hidden;
    scrollbar-width: none; -ms-overflow-style: none;
  }
  .qx-shell-nav::-webkit-scrollbar { display: none; }

  /* Nav items: icon stacked above label, touch-friendly */
  .qx-shell-nav-item {
    flex-direction: column !important;
    gap: 3px !important;
    padding: 7px 4px 8px !important;
    min-width: 52px !important;
    flex: 1 !important;
    align-items: center !important;
    border-radius: 0 !important;
    justify-content: center !important;
    min-height: 62px !important;
    text-decoration: none;
    border-left: none !important;
  }
  .qx-shell-nav-item.is-active {
    background: var(--qx-surface-2) !important;
    box-shadow: inset 0 3px 0 var(--qx-accent-text);
  }
  .qx-shell-nav-icon { opacity: .8; transition: opacity .14s; width: 20px; height: 20px; }
  .qx-shell-nav-item.is-active .qx-shell-nav-icon { opacity: 1; }
  /* Labels were 9px — below the readable floor on a phone held at
     arm's length. Raised to 11px and full-opacity. */
  .qx-shell-nav-label {
    display: block !important;
    font-size: 11px !important;
    font-weight: 800 !important;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 60px;
    opacity: 1;
  }
  .qx-shell-nav-item.is-active .qx-shell-nav-label { color: var(--qx-accent-text); }

  /* Main content: push bottom so the tab bar doesn't overlap */
  .qx-shell-main {
    margin-left: 0 !important;
    padding-bottom: calc(62px + env(safe-area-inset-bottom, 0px)) !important;
  }

  /* Burger hidden — not needed with bottom bar */
  .qx-shell-burger { display: none !important; }

  /* Topbar: compact */
  .qx-shell-topbar { padding: 10px 14px !important; gap: 8px; }
  .qx-shell-topbar-search { display: none; }

  /* Overlay: not needed (drawer is gone) */
  .qx-shell-overlay { display: none !important; }
}

/* ══════════════════════════════════════════════════════════════════
   PROJECTOR — "ink on paper" light field
   ══════════════════════════════════════════════════════════════════
   A projector adds light; it cannot subtract it. On a screen, "black" is only
   the ambient light the surface reflects back, so a dark design contributes
   almost no light across most of the field and the audience sees ambient wash
   with a few white letters on it. The contrast RATIO is roughly symmetric
   either way — the gain here is that eyes in a lit room are adapted to high
   luminance, and that the black floor is dirty: lens flare, screen texture and
   light spill all sit at the same magnitude as a dark design's content. On a
   light field the lamp swamps that noise.

   Measured against #EDF0F3 by scripts/contrast-check.js:
     ink 15.80:1 · muted 6.93:1 · rule 1.59:1 · every semantic >= 4.70:1
   The four answer hues are LOCKED and identical to the dark ladder: a player
   maps the red triangle on the wall to the red triangle in their hand.
   See docs/superpowers/specs/2026-09-22-projector-legibility-design.md
   ══════════════════════════════════════════════════════════════════ */
[data-theme="projector"] {
  --qx-bg:          #EDF0F3;   /* never pure white — white glares under a lamp */
  --qx-bg-2:        #E3E8ED;
  --qx-surface:     #FFFFFF;
  --qx-surface-2:   #F4F6F8;
  --qx-surface-3:   #E1E6EC;

  --qx-text:        #14161D;   /* the dark ladder's background, reused as ink */
  --qx-muted:       #4A5163;
  --qx-faint:       #5B6577;

  --qx-border:      #B8C1CC;
  --qx-border-2:    #97A3B2;
  --qx-keyline:     #14161D;   /* tile outlines are ink, not grey */

  --qx-success:     #0F6A39;
  --qx-danger:      #B3122F;
  --qx-warning:     #8A6400;
  --qx-info:        #15539E;
  --qx-success-solid: #0F6A39;
  --qx-danger-solid:  #B3122F;

  /* LOCKED — identical to the dark ladder. Do not diverge. */
  --qx-tile-a:      #B3122F;  --qx-tile-a-ink: #FFFFFF;
  --qx-tile-b:      #14509E;  --qx-tile-b-ink: #FFFFFF;
  --qx-tile-c:      #F5B301;  --qx-tile-c-ink: #1A1200;
  --qx-tile-d:      #10693A;  --qx-tile-d-ink: #FFFFFF;
  /* the dark ladder's dim grey resolves to 1.23:1 here — invisible */
  --qx-tile-dim:    #9AA5B3;  --qx-tile-dim-ink: #14161D;

  --qx-state-ring:  #14161D;
  --qx-state-halo:  rgba(20, 22, 29, .18);

  --qx-accent:      #5B2BD9;
  --qx-accent-2:    #6D3BF5;
  --qx-accent-ink:  #FFFFFF;
  --qx-accent-text: #5B2BD9;
  --qx-purple:      #5B2BD9;
  --qx-green:       var(--qx-success);
  --qx-blue:        var(--qx-info);
  --qx-yellow:      var(--qx-warning);
  --qx-red:         var(--qx-danger);

  /* Word cloud: the dark ramp's neons resolve to 1.23-2.05:1 here. */
  --qx-word-1: #B3122F;
  --qx-word-2: #14509E;
  --qx-word-3: #10693A;
  --qx-word-4: #5B2BD9;
  --qx-word-5: #8A6400;
  --qx-word-6: #0F6A6A;
  --qx-word-7: #9A2B86;
  --qx-word-8: #8A3A12;

  /* Ambient glows carry no information and band on 6-bit panels; on a light
     field they only muddy it. The glyph field stays, in ink rather than white. */
  --qx-glow-1: transparent;
  --qx-glow-2: transparent;
  --qx-figure: rgba(20, 22, 29, .035);   /* glyph field in ink, not white */
  --qx-border: #B8C1CC;                  /* re-declared: a later shared block
                                            resets this for all three themes */

  /* Archivo: chosen by rendering ș and ț at 76px against Plus Jakarta Sans,
     Source Sans 3, IBM Plex Sans and Figtree. All set the comma below
     correctly; Archivo keeps it separated from the letter at distance and holds
     the widest strokes at weight 900. */
  --qx-font:         'Archivo', system-ui, -apple-system, sans-serif;
  --qx-font-display: 'Archivo', system-ui, -apple-system, sans-serif;
}

/* The shared canvas paints its glyph field in white at 2.2%, which is invisible
   on a light ground. Same six glyphs, in ink. */
[data-theme="projector"] .qx-canvas,
[data-theme="projector"] .qx-canvas-bar,
[data-theme="projector"] body.qx-canvas {
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='420' height='420' viewBox='0 0 420 420'%3E%3Cg fill='%2314161D' fill-opacity='0.035'%3E%3Cpath d='M60 34 L86 80 L34 80 Z'/%3E%3Ccircle cx='330' cy='96' r='24'/%3E%3Crect x='196' y='188' width='46' height='46' rx='6'/%3E%3Cpath d='M96 300 L124 328 L96 356 L68 328 Z'/%3E%3Ccircle cx='356' cy='330' r='18'/%3E%3Cpath d='M270 40 L292 78 L248 78 Z'/%3E%3C/g%3E%3C/svg%3E");
  background-size: 420px 420px;
  background-repeat: repeat;
}

/* ── Language switch (RO | EN) — rendered by /js/i18n.js into .qx-lang-switch ── */
.qx-lang-switch{display:inline-flex;align-items:center;gap:2px;padding:2px;border:1px solid var(--qx-border,rgba(127,127,127,.4));border-radius:999px;font:inherit;line-height:1;vertical-align:middle}
.qx-lang-switch:empty{display:none}
.qx-lang-btn{appearance:none;border:0;background:transparent;color:inherit;opacity:.7;font:inherit;font-size:12px;font-weight:800;letter-spacing:.06em;padding:5px 9px;border-radius:999px;cursor:pointer;min-width:34px}
.qx-lang-btn:hover{opacity:1}
.qx-lang-btn.is-active{opacity:1;background:var(--qx-text,currentColor);color:var(--qx-bg,#14161D)}
.qx-lang-btn:focus-visible{outline:2px solid var(--qx-accent-text,#5DE0AB);outline-offset:2px}

/* ── Public footer (index, pricing, quiz-uri, ghiduri, contact, privacy, terms, retragere,
   reinnoire, 404, 500) — one markup, one design. Desktop: brand left, link groups in
   columns right, bottom row (company line · ANPC SAL · RO | EN). ── */
.qx-footer{border-top:1px solid rgba(255,255,255,.08);padding:clamp(28px,4vw,48px) clamp(16px,4vw,48px) 24px;margin-top:clamp(40px,6vw,80px);color:var(--qx-muted,#C9CEDA);font-size:14px;line-height:1.5}
.qx-footer__main,.qx-footer__bottom{max-width:1160px;margin:0 auto}
.qx-footer__main{display:grid;grid-template-columns:minmax(220px,1fr) minmax(0,2.4fr);gap:32px 48px;align-items:start}
.qx-footer__brand{display:flex;flex-direction:column;gap:12px;align-items:flex-start}
.qx-footer__tagline{margin:0;font-size:13px;font-weight:600;color:var(--qx-muted,#C9CEDA);max-width:300px}
.qx-footer__groups{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:24px 32px}
.qx-footer__title{margin:0 0 12px;font-size:12px;font-weight:900;text-transform:uppercase;letter-spacing:.1em;color:var(--qx-faint,#A2AAB9)}
.qx-footer__list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px}
.qx-footer__list a{font-size:13px;font-weight:700;color:var(--qx-muted,#C9CEDA);text-decoration:none;transition:color .15s}
.qx-footer__list a:hover{color:var(--qx-text,#fff);text-decoration:underline}
.qx-footer a:focus-visible{outline:2px solid var(--qx-accent-text,#5DE0AB);outline-offset:2px;border-radius:4px}
.qx-footer__bottom{display:flex;flex-wrap:wrap;align-items:center;gap:12px 24px;margin-top:32px;padding-top:20px;border-top:1px solid rgba(255,255,255,.08)}
.qx-footer__copy{margin:0;flex:1 1 320px;font-size:12px;font-weight:700;color:var(--qx-faint,#A2AAB9)}
.qx-footer__sal{display:inline-block;line-height:0;flex:none}
.qx-footer__sal img{display:block;width:160px;max-width:100%;height:auto}
.qx-footer .qx-lang-switch,.qx-footer__switch{flex:none;color:var(--qx-muted,#C9CEDA)}
@media (max-width:900px){
  .qx-footer__main{grid-template-columns:1fr}
  .qx-footer__groups{grid-template-columns:repeat(3,minmax(0,1fr))}
  /* 3 columns: Produs | Cont + Companie (stacked) | Legal — keeps the footer short */
  .qx-footer__groups>.qx-footer__group:nth-child(1){grid-column:1;grid-row:1 / span 2}
  .qx-footer__groups>.qx-footer__group:nth-child(2){grid-column:2;grid-row:1}
  .qx-footer__groups>.qx-footer__group:nth-child(3){grid-column:2;grid-row:2}
  .qx-footer__groups>.qx-footer__group:nth-child(4){grid-column:3;grid-row:1 / span 2}
}
@media (max-width:600px){
  .qx-footer__groups{grid-template-columns:repeat(2,minmax(0,1fr))}
  /* 2 columns: Produs + Legal | Cont + Companie */
  .qx-footer__groups>.qx-footer__group:nth-child(1){grid-column:1;grid-row:1}
  .qx-footer__groups>.qx-footer__group:nth-child(4){grid-column:1;grid-row:2}
}
@media (max-width:480px){
  .qx-footer__brand{align-items:center;text-align:center}
  .qx-footer__tagline{max-width:none}
  .qx-footer__groups{gap:20px 16px}
  .qx-footer__bottom{flex-direction:column;text-align:center}
  .qx-footer__copy{flex:none}
}

/* ── RO | EN switch on public pages: cross-document view transition + sliding thumb ──
   html.qx-vt is set by /js/i18n.js on public pages only (QX_I18N.alt), which also adds
   @view-transition { navigation: auto } there. Nav and footer stay put; content cross-fades. */
html.qx-vt .qx-nav{view-transition-name:qx-nav}
html.qx-vt .qx-footer{view-transition-name:qx-footer}
::view-transition-old(root),::view-transition-new(root){animation-duration:200ms;animation-timing-function:ease}
::view-transition-group(qx-nav),::view-transition-group(qx-footer){animation:none}
::view-transition-old(qx-nav),::view-transition-old(qx-footer){animation:none;opacity:0}
::view-transition-new(qx-nav),::view-transition-new(qx-footer){animation:none}
.qx-lang-switch,.lib-lang-switch{position:relative;isolation:isolate}
.qx-lang-thumb{position:absolute;z-index:-1;top:2px;bottom:2px;border-radius:999px;background:var(--qx-text,#fff);transition:transform 150ms ease;pointer-events:none}
.qx-lang-switch.is-sliding .qx-lang-btn.is-active,.lib-lang-switch.is-sliding .qx-lang-btn.is-active{background:transparent;color:inherit}
.qx-lang-switch.is-sliding .qx-lang-btn.is-target,.lib-lang-switch.is-sliding .qx-lang-btn.is-target{color:var(--qx-bg,#14161D);opacity:1;transition:color 150ms ease}
@media (prefers-reduced-motion:reduce){
  ::view-transition-group(*),::view-transition-old(*),::view-transition-new(*){animation:none!important}
  .qx-lang-thumb{transition:none}
}
