/* ═══════════════════════════════════════════════════════════════════════════
   Clean Arcade — neon layer. Loaded by EVERY page (the arcade pages, the six
   canvas games and the Aquarium), so it is deliberately self-sufficient: the
   tube colours and the night ground are declared here as well as in
   styles/arcade.css, with identical values, because the game pages do not load
   the Nocturne token sheet.

   Everything animated below moves transform, opacity or filter only, and all
   of it stops under prefers-reduced-motion (bottom of file).
   ═════════════════════════════════════════════════════════════════════════ */

:root {
  --n-cyan: #00e9ff;
  --n-lime: #b8ff4a;
  --n-magenta: #ff2bd6;
  --n-violet: #a26bff;
  --n-yellow: #ffe14a;
  --n-green: #3dff90;
  --n-pink: #ff4fd8;

  --ground-1: #1b1136;
  --ground-2: #150e28;
  --ground-3: #100c1e;
  --card-face: color-mix(in srgb, var(--color-surface, #232532) 78%, #2b1a52);

  --edge: linear-gradient(135deg,
    color-mix(in srgb, var(--n-magenta) 70%, transparent),
    color-mix(in srgb, var(--n-cyan) 90%, transparent) 34%,
    color-mix(in srgb, var(--n-yellow) 62%, transparent) 62%,
    color-mix(in srgb, var(--n-green) 72%, transparent) 82%,
    color-mix(in srgb, var(--n-magenta) 60%, transparent));
  --edge-lit: linear-gradient(135deg,
    var(--n-magenta), var(--n-cyan) 34%, var(--n-yellow) 62%, var(--n-green) 82%, var(--n-magenta));

  /* the one night ground every page paints */
  --ca-ground:
    radial-gradient(1100px 620px at 8% -10%, color-mix(in srgb, var(--n-violet) 30%, transparent), transparent 68%),
    radial-gradient(900px 520px at 96% 2%, color-mix(in srgb, var(--n-magenta) 17%, transparent), transparent 70%),
    radial-gradient(800px 620px at 50% 108%, color-mix(in srgb, var(--n-cyan) 12%, transparent), transparent 72%),
    linear-gradient(180deg, var(--ground-1) 0%, var(--ground-2) 46%, var(--ground-3) 100%);
}

/* ── shared motion vocabulary ────────────────────────────────────────────── */
@keyframes ca-float { 50% { transform: translateY(-9px); } }
@keyframes ca-bob { 50% { transform: translateY(-4px); } }
@keyframes ca-spin { to { transform: rotate(1turn); } }
@keyframes ca-glow { 0%, 100% { filter: brightness(1); } 50% { filter: brightness(1.22); } }

/* any emoji or glyph that should feel alive */
.ca-float { display: inline-block; animation: ca-float 4.4s ease-in-out infinite; }

/* ── game-page HUD, kept from the first release ──────────────────────────── */
.badge {
  transition: transform 0.15s ease;
}
.badge:hover { transform: scale(1.06); }

button.play, .play {
  transition: transform 0.15s ease, box-shadow 0.15s ease, filter 0.15s ease !important;
  white-space: nowrap;
  line-height: 1.15;
}
button.play:hover, .play:hover {
  transform: scale(1.08) !important;
  filter: brightness(1.15);
}
button.play:active, .play:active {
  transform: scale(0.96) !important;
}

#backLink { transition: transform 0.15s ease, box-shadow 0.15s ease; }
#backLink:hover { box-shadow: 0 0 18px rgba(0, 233, 255, 0.55); }
#backLink:focus-visible { outline: 2px solid var(--n-cyan); outline-offset: 3px; }

/* ── vendored icon set ───────────────────────────────────────────────────
   The interface glyphs used to come from a third-party icon webfont on a CDN.
   When that font did not arrive — a slow mobile network, a blocked host, a
   cold cache — every glyph rendered as a tofu box. Each <i class="ph …"> now
   carries its own inline SVG, so the icons are part of the document and
   cannot fail. The <i> wrapper is kept deliberately: every existing rule that
   sizes, tints or animates `i` still applies, and the SVG fills 1em of it. */
i.ph {
  display: inline-block;
  line-height: 0;
  font-style: normal;
  vertical-align: -0.135em;
  flex: none;
}
i.ph > svg {
  width: 1em; height: 1em;
  display: block;
  overflow: visible;
}
/* the places that carried a neon text-shadow keep their glow — a text-shadow
   does not reach an SVG child, so it is re-declared as a drop-shadow */
.brand-mark > svg,
.sec-ico i > svg,
.feat-item .ico > svg,
.kicker i > svg,
.ticker span i > svg,
.flag-tag i > svg {
  filter: drop-shadow(0 0 0.4em currentColor);
}

/* ── tactile press: one composited ripple, 420 ms, transform+opacity only ── */
.ca-ripple {
  position: absolute;
  inset-block-start: 0; inset-inline-start: 0;
  width: 10px; height: 10px; margin: -5px 0 0 -5px;
  border-radius: 50%;
  background: radial-gradient(circle, var(--n-cyan), transparent 70%);
  opacity: 0.5;
  pointer-events: none;
  animation: ca-ripple-out 0.42s cubic-bezier(.2,.7,.3,1) forwards;
  will-change: transform, opacity;
}
@keyframes ca-ripple-out { to { transform: scale(16); opacity: 0; } }

/* ── cross-document view transitions ─────────────────────────────────────── */
::view-transition-group(root) { animation-duration: 0.32s; }
::view-transition-old(root) { animation: ca-vt-out 0.24s ease-in both; }
::view-transition-new(root) { animation: ca-vt-in 0.32s cubic-bezier(.2,.8,.3,1) both; }
@keyframes ca-vt-out { to { opacity: 0; transform: scale(0.985); } }
@keyframes ca-vt-in { from { opacity: 0; transform: scale(1.008); } }
::view-transition-group(root) { animation-duration: 0.32s; }

/* ═════════════════════════════════════════════════════════════════════════
   The six canvas games — body.ca-game. One ground, one edge treatment and one
   motion vocabulary shared with the arcade, so a game never looks like a
   different site. The canvas art itself is untouched.
   ═══════════════════════════════════════════════════════════════════════ */
body.ca-game { background: var(--ca-ground) no-repeat; background-size: cover; }

/* The chrome bar (back link · developer watermark · break timer) publishes its
   own height as --ca-top; every game reserves it so the bar can never sit on
   top of a score badge, a sound toggle or live gameplay. The fallback keeps
   the layout correct for the one frame before the bar has measured itself. */
.ca-game #wrap {
  background: var(--ca-ground) no-repeat;
  background-size: cover;
  padding-top: var(--ca-top, 46px);
}
/* the HUD no longer has to dodge a fixed break-timer pill in the corner —
   the chrome bar owns that strip, so the games get their full width back */
.ca-game #hud { top: calc(var(--ca-top, 46px) + 8px); padding-right: 0; }
.ca-game canvas {
  border-radius: 12px;
  box-shadow: 0 0 42px -14px var(--n-cyan), 0 0 70px -30px var(--n-magenta);
}
.ca-game #backLink {
  border-radius: 999px;
  border: 1px solid transparent;
  background:
    linear-gradient(color-mix(in srgb, var(--ground-3) 86%, transparent),
                    color-mix(in srgb, var(--ground-3) 86%, transparent)) padding-box,
    var(--edge) border-box;
  box-shadow: 0 0 20px -8px var(--n-cyan), inset 0 0 14px -10px var(--n-cyan);
  /* No backdrop-filter. The back link sits directly over the game canvas, so a
     blur here made the compositor re-read and re-blur a region of a surface
     being rewritten 60 times a second — a per-frame cost on every game page.
     The padding-box gradient already reads as frosted glass. */
}
.ca-game #backLink:hover {
  transform: scale(1.06);
  box-shadow: 0 0 26px -6px var(--n-cyan);
}
.ca-game .panel {
  border: 1.5px solid transparent !important;
  background:
    linear-gradient(160deg, color-mix(in srgb, var(--ground-1) 96%, transparent), var(--ground-3)) padding-box,
    var(--edge-lit) border-box !important;
  box-shadow: 0 0 60px -22px var(--n-cyan), 0 0 90px -40px var(--n-magenta), 0 18px 40px rgba(0,0,0,.55) !important;
}
.ca-game .panel .emoji {
  display: block;
  animation: ca-float 4.4s ease-in-out infinite;
}
.ca-game .badge {
  border: 1px solid transparent !important;
  background:
    linear-gradient(color-mix(in srgb, var(--ground-3) 82%, transparent),
                    color-mix(in srgb, var(--ground-3) 82%, transparent)) padding-box,
    var(--edge) border-box !important;
  box-shadow: 0 0 22px -12px var(--n-cyan);
  /* Static, and its own paint island. These badges used to carry ca-bob AND
     ca-glow (a filter animation) while the game rewrites their text every
     frame — each write dirtied a filtered, animating, promoted layer sitting
     directly on top of the canvas. `contain` means a score write can no longer
     invalidate the canvas or the page around it. */
  contain: layout paint style;
}
.ca-game button.play {
  position: relative;
  overflow: hidden;
  isolation: isolate;
}
.ca-game button.play::before {
  content: "";
  position: absolute; inset: 0; z-index: -1; border-radius: inherit;
  box-shadow: 0 0 30px 4px rgba(255,255,255,0.55);
  opacity: .18;
  animation: ca-play-breathe 2.2s ease-in-out infinite;
}
@keyframes ca-play-breathe { 50% { opacity: .55; } }
.ca-game button.play::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(100deg, transparent 34%, rgba(255,255,255,.45) 50%, transparent 66%);
  transform: translateX(-130%);
  animation: ca-play-shine 3.4s ease-in-out infinite;
}
@keyframes ca-play-shine {
  0%, 55% { transform: translateX(-130%); }
  85%, 100% { transform: translateX(130%); }
}

/* ═════════════════════════════════════════════════════════════════════════
   The Aquarium page's own chrome — body.ca-aquarium. Same ground, same lit
   edges; the game component inside is untouched.
   ═══════════════════════════════════════════════════════════════════════ */
body.ca-aquarium { background: var(--ca-ground) no-repeat; background-size: cover; }
.ca-aquarium header {
  background: color-mix(in srgb, var(--ground-3) 86%, transparent);
  border-bottom: 1px solid transparent;
  box-shadow: 0 1px 0 color-mix(in srgb, var(--n-cyan) 34%, transparent),
              0 8px 30px -18px var(--n-cyan);
}
.ca-aquarium .brand-badge {
  box-shadow: 0 0 14px -4px var(--n-lime), inset 0 0 12px -8px var(--n-lime);
}
.ca-aquarium .info-card,
.ca-aquarium .game-frame-box {
  border: 1.5px solid transparent !important;
  border-radius: 16px;
  background:
    linear-gradient(160deg, color-mix(in srgb, var(--card-face) 92%, var(--n-violet)), var(--ground-3)) padding-box,
    var(--edge) border-box !important;
  box-shadow: 0 0 30px -18px var(--n-cyan), 0 14px 30px -18px #000 !important;
  transition: transform .18s ease, box-shadow .18s ease;
}
.ca-aquarium .info-card:hover {
  transform: translateY(-3px);
  box-shadow: 0 0 34px -10px var(--n-cyan), 0 0 60px -28px var(--n-magenta) !important;
}

/* ── the tank is playing: stand the page's own effects down ───────────────
   While the game runs, every pixel the browser can spend belongs to the
   canvas, so the two aurora blobs and the header's backdrop blur stand down.
   The class is written once per phase change by the game shell. It was briefly
   a :has() selector on <body>, which tied the whole document's style to the
   game's subtree and recalculated the page on every HUD write — the cause of
   the phone stutter. The neon edges, glows and colours are untouched. */
body.ca-aquarium.ca-playing .aurora { display: none; }
body.ca-aquarium.ca-playing .site-head { backdrop-filter: none; }
/* the frame's hover lift promotes a layer around a live canvas; pointerless
   devices can never trigger it anyway */
body.ca-aquarium.ca-playing .game-frame-box { transition: none; }

/* Phones: the header's backdrop blur is a per-frame compositor read on every
   scroll, and the two aurora blobs are 46vmax textures under a 70px blur — the
   single most expensive thing on the page, and it sits directly above the
   game. Both are replaced by a static wash that reads the same at a glance. */
@media (max-width: 760px) {
  .ca-aquarium .site-head { backdrop-filter: none; background: var(--ground-3); }
  .ca-aquarium .aurora { display: none; }
  .ca-aquarium .hero {
    background:
      radial-gradient(60% 70% at 8% 0%, color-mix(in srgb, var(--n-cyan) 26%, transparent), transparent 70%),
      radial-gradient(52% 60% at 100% 6%, color-mix(in srgb, var(--n-magenta) 20%, transparent), transparent 70%);
  }
  /* paint containment keeps the game frame's own compositing work from
     invalidating the sections above and below it while the tank animates */
  .ca-aquarium .game-frame-box { contain: paint; }
  .ca-aquarium .info-card:hover { transform: none; }
}

/* ── light-scheme devices: the same night, a few steps lighter ──────────── */
@media (prefers-color-scheme: light) {
  :root {
    --ground-1: #2c2456;
    --ground-2: #241d45;
    --ground-3: #1e1838;
  }
}

/* ── honour the OS motion preference across every page, games included ──── */
@media (prefers-reduced-motion: reduce) {
  .badge, .ca-float,
  .ca-game .badge, .ca-game .panel .emoji,
  .ca-game button.play::before, .ca-game button.play::after { animation: none !important; }
  .ca-game button.play::after { display: none; }
  .badge:hover, button.play:hover, .play:hover, #backLink:hover { transform: none !important; }
  .ca-ripple { display: none; }
  ::view-transition-old(root), ::view-transition-new(root) { animation: none; }
  .ca-game #backLink:hover { transform: none !important; }
}
