/* ═══════════════════════════════════════════════════════════════════════════
   hub-theme.css — the mineral / night-sky atmosphere for the games hub.
   ---------------------------------------------------------------------------
   THIS FILE DOES NOT IMPLEMENT DARK MODE.
   index.html already does, and does it well: it remaps the neural.css token
   names under [data-theme="dark"], so every component follows the theme for
   free and there is exactly one place to change a colour. A second theme
   system layered on top would be two sources of truth for the same decision,
   and the one that lost would be whichever happened to load second.

   What this file adds is the part that did NOT exist: a mineral accent set and
   the depth cues that make the dark theme read as a night sky rather than a
   dark grey page. It consumes the existing tokens and the existing
   [data-theme] hook; it never redefines --cream, --ink or --crimson.

   THE MATERIAL LANGUAGE
   Accents are taken from real stones, and each has one job:
     quartz   hairlines and the web — pale, never used for text
     salt     warm rim light on glints
     opal     the single hot accent; the only one warm enough to draw the eye
     amethyst depth and geode interiors; carries the night sky
     geode    rind gold, used on exactly one element (the current rank tier)

   CONTRAST
   None of these are used for body text. quartz and salt sit below 4.5:1 on
   both surfaces and are restricted to hairlines, glows and canvas strokes.
   Text keeps the --ink / --crimson tokens, which index.html already tuned.
   ═══════════════════════════════════════════════════════════════════════════ */

:root {
  --gem-quartz:   #E9C4CC;
  --gem-salt:     #F4A896;
  --gem-opal:     #FF8A3D;
  --gem-amethyst: #7B4B94;
  --gem-geode:    #C9A227;

  /* Atmosphere strengths, read by js/hub-atmos.js. Declared here so the canvas
     and the stylesheet cannot disagree about how loud the effect is. */
  --atmos-web:  0.055;
  --atmos-star: 0;      /* the starfield belongs to the night theme only */
  --atmos-gem:  0.10;
}

:root[data-theme='dark'] {
  --gem-quartz:   #E7B9C6;
  --gem-salt:     #E89A86;
  --gem-opal:     #FFA45C;
  --gem-amethyst: #A97BC7;

  --atmos-web:  0.075;
  --atmos-star: 0.85;
  --atmos-gem:  0.16;
}

/* THE OS-DRIVEN COPY. index.html enforces "keep the two blocks in step" on
   itself — an attribute block, then a duplicate driven by prefers-color-scheme —
   and this file did not mirror it. The consequence was measured, not theorised:
   a dark-preferring visitor who had never touched the header toggle got
   index.html's dark tokens (--cream #1C1917, --ink #F5F1EA) alongside the LIGHT
   mineral set — --gem-quartz still #E9C4CC, --atmos-star still 0, --atmos-web
   still 0.055 — and no night sky at all, because the gradient below hung off
   [data-theme='dark'] only. Dark chrome, light stones, flat sky.

   Same construction as index.html, for the same reasons: :not([data-theme='light'])
   is what lets the header toggle override the OS in BOTH directions, the two
   selectors have equal specificity, so this block must stay AFTER the one above.
   Keep the pair in step. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) {
    --gem-quartz:   #E7B9C6;
    --gem-salt:     #E89A86;
    --gem-opal:     #FFA45C;
    --gem-amethyst: #A97BC7;

    --atmos-web:  0.075;
    --atmos-star: 0.85;
    --atmos-gem:  0.16;
  }
}

/* ---- night sky depth ----------------------------------------------------- */
/* Three wide radials over the existing --cream surface: amethyst at the top
   left, salt warmth at the top right, a rose wash rising from the bottom. The
   result is a graded sky rather than a flat field, without touching a single
   colour token. `fixed` so it behaves like sky and does not scroll with the
   content. */
/* Painted on <html>, NOT <body>, and this matters. The canvas below sits at
   z-index -1 so it never covers page content; a -1 layer paints behind its
   parent stacking context's content but still IN FRONT of the root element's
   background. Put the sky on body and the canvas disappears underneath it.
   Put it on html and the order is exactly right:
       html background  →  #atmos  →  body content
   The earlier version of this file solved the same problem by forcing
   `position: relative; z-index: 1` onto every body child. That worked for
   stacking and broke the layout: #ncg-world was a fixed full-viewport canvas,
   and pulling it into flow gave it 632px of real height and a cream band
   across the top of the page. (#ncg-world is gone from the hub entirely as of
   2026-08-09 — index.html records why — but the rule about where the sky goes
   is unchanged, and so is the reason not to touch body children.)

   THE OTHER HALF OF THE SAME PROBLEM, fixed in index.html rather than here.
   Moving the sky onto <html> stops body's background propagating to the canvas,
   which means body starts painting its OWN background-color box — measured
   rgb(28,25,23), fully opaque, directly over the z-index:-1 layer this comment
   exists to protect. index.html now sets `background-color: transparent` on
   body in both of its dark blocks. Sky here, transparency there; neither works
   without the other. */
:root[data-theme='dark'] {
  background:
    radial-gradient(1200px 700px at 12% -8%,  rgba(169,123,199,.15), transparent 60%),
    radial-gradient(1000px 620px at 92% 4%,   rgba(232,154,134,.09), transparent 58%),
    radial-gradient(1400px 900px at 50% 108%, rgba(198,112,126,.10), transparent 62%),
    var(--cream);
  background-attachment: fixed;
}
/* The OS-driven copy of the sky. Keep in step with the block above. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) {
    background:
      radial-gradient(1200px 700px at 12% -8%,  rgba(169,123,199,.15), transparent 60%),
      radial-gradient(1000px 620px at 92% 4%,   rgba(232,154,134,.09), transparent 58%),
      radial-gradient(1400px 900px at 50% 108%, rgba(198,112,126,.10), transparent 62%),
      var(--cream);
    background-attachment: fixed;
  }
}

/* ---- the atmosphere canvas ---------------------------------------------- */
/* `pointer-events:none` so it can never intercept a click; aria-hidden in the
   markup because it states nothing the page does not. No sibling rules — see
   the note above. */
#atmos {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: -1;
  pointer-events: none;
}

/* ---- mineral detailing --------------------------------------------------- */

/* The current rank tier becomes a geode: gold rind, crystal interior. This is
   the one place --gem-geode is used, which is what keeps it feeling like a
   marker rather than a decoration. #rank-ladder is written by hub.js, so this
   hangs off the state it sets rather than off markup added here.

   THE FIRST SELECTOR WAS DEAD. It read [aria-current='true'] while hub.js:257
   writes aria-current="step" — which is the correct ARIA value for an item in a
   sequence, so the selector was wrong, not the script. The current rung returned
   matches("[aria-current='true']") === false and matches('.is-current') === true,
   so the treatment only ever landed through the second half. Anyone tidying the
   ".is-current" clause away as redundant would have deleted the whole effect.
   Fixed here rather than by changing what hub.js writes: 'step' is the accurate
   value and screen-reader output should not be bent to suit a stylesheet. */
#rank-ladder > li[aria-current='step'],
#rank-ladder > li.is-current {
  border-color: color-mix(in srgb, var(--gem-geode) 50%, var(--crimson-line));
  background:
    radial-gradient(120% 140% at 50% 0%,
      color-mix(in srgb, var(--gem-amethyst) 15%, transparent), transparent 62%),
    var(--cream-2);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--gem-geode) 20%, transparent);
}

/* WCAG 2.2 SC 1.4.3, and it is this rule that caused the failure. Every other
   rung sits on --hub-surface #FFFDF8, where the ladder's --ink-3 #78716C xp
   label measures 4.72:1 and passes. The geode above drops the CURRENT rung onto
   --cream-2 #F3F1EA, where the identical token measures 4.24:1 — under the
   4.5:1 floor for 10.5px text. So the one rung the page most wants you to read
   was the only one failing. --ink-2 #44403C on the same surface is 9.09:1.
   The rung is still marked by the gold rind, the amethyst wash, the filled
   medal and aria-current; none of that depended on the label being faint. */
#rank-ladder > li[aria-current='step'] .t-xp,
#rank-ladder > li.is-current .t-xp { color: var(--ink-2); }

/* Faceted sheen on the medals — a cut stone rather than a flat disc. */
#rank-ladder .medal {
  background-image:
    linear-gradient(135deg,
      color-mix(in srgb, var(--gem-quartz) 50%, transparent) 0%,
      transparent 44%,
      color-mix(in srgb, var(--gem-opal) 24%, transparent) 100%);
}

@media (prefers-reduced-motion: reduce) {
  :root { --atmos-star: 0; }   /* stars stop twinkling; the web still draws */
}

/* ═══════════════════════════════════════════════════════════════════════════
   A.V PLC coda — the section folded in from the retired av-plc.html.
   ---------------------------------------------------------------------------
   Styled as a CODA, not a fifth game card. It sits after the four experiences
   and before the footer, on a geode-lined panel rather than the cream surface
   the games use, so the eye registers "different product" before the text says
   it. A.V PLC shares none of the games' XP and is not a security game; making
   it look like one would imply both.
   ═══════════════════════════════════════════════════════════════════════════ */

.av-coda {
  margin-top: clamp(48px, 7vw, 96px);
  padding: clamp(26px, 4vw, 48px);
  border-radius: 18px;
  /* The geode: gold rind, amethyst interior, over the page surface. */
  border: 1px solid color-mix(in srgb, var(--gem-geode) 34%, var(--crimson-line));
  background:
    radial-gradient(120% 150% at 12% 0%,
      color-mix(in srgb, var(--gem-amethyst) 11%, transparent), transparent 58%),
    radial-gradient(100% 130% at 92% 100%,
      color-mix(in srgb, var(--gem-salt) 9%, transparent), transparent 56%),
    var(--cream-2);
}

.av-grid {
  display: grid;
  gap: clamp(12px, 2vw, 18px);
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  margin-top: clamp(22px, 3vw, 32px);
}

.av-card {
  padding: clamp(16px, 2.2vw, 22px);
  border-radius: 12px;
  border: 1px solid var(--crimson-line);
  background: var(--cream);
}
.av-card h3 {
  margin: 0 0 .5em;
  font: 500 11.5px/1 var(--mono, ui-monospace, monospace);
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--crimson);
}
.av-card p {
  margin: 0;
  font-size: .93rem;
  line-height: 1.6;
  color: var(--ink-2);
}

.av-note {
  margin-top: clamp(20px, 2.6vw, 28px);
  padding-top: clamp(16px, 2vw, 20px);
  border-top: 1px solid var(--crimson-line);
  font-size: .9rem;
  line-height: 1.6;
}
