/* ==========================================================================
   TILES3D — chain-reaction rotation puzzle
   --------------------------------------------------------------------------
   Letterpress design language: each tile is a small printing block. Paper
   faces carry an engraved ink arrow; the arrow stamps itself in accent red
   the moment its tile is upright (orientation 0), so the board reads at a
   glance like a lights-out grid: red = set, ink = still to turn.

   Colours come straight from the site theme variables, so dark and light
   themes both work by construction — tiles are "paper mixed toward ink" and
   the pedestal mat is solid ink, which inverts tastefully between themes.
   ========================================================================== */

:root {
  --board-size: 3;
  --tile-size: 80px;
  --gap: 10px;
  /* Tiles wear the shared 3D block scheme (the word scramble cube family):
     cyan block bodies with pale glyphs, in BOTH themes — the palette does
     not flip with the theme. Sides are the same hue shadowed toward the
     stage floor tone. The v2 jewel cyan is a step brighter than the old
     muted teal so the arrows carry across the stage. */
  --tile-face: #3aa3b5;
  --tile-side: color-mix(in srgb, #2b7f92 72%, #14121a);
  --tile-rim: color-mix(in srgb, #14121a 35%, transparent);
  --tile-depth: 18px; /* full physical depth of a tile block */
  --turn-ease: cubic-bezier(0.3, 1.28, 0.44, 1);
}

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body.game-3d-wrapper {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  /* The stage is a definite, near-full-height framed block from
     unified-3d-game.css; the page scrolls normally to the About section. */
  background: var(--page-background);
  color: var(--color-text-primary);
  font-family: var(--font-family-base);
}

/* --------------------------------------------------------------------------
   STAGE
   -------------------------------------------------------------------------- */

.scene {
  position: relative;
  flex: 1;
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  perspective: 1300px;
  perspective-origin: 50% 34%;
  /* Room for the HUD strip above the board. */
  padding-top: 56px;
  /* Stage v2 sky: indigo night warming into a violet horizon glow behind
     the board (the old flat two-stop violet read as a dead wall). */
  background: linear-gradient(180deg,
      #07060c 0%,
      #120f1e 34%,
      #241d3e 62%,
      #393063 78%,
      #2a2150 100%);
}

html[data-theme='light'] .scene {
  background: linear-gradient(180deg,
      #f8f2e7 0%,
      #f1e6d2 34%,
      #e0c6a6 58%,
      #4a3f6b 78%,
      #3a3153 100%);
}

/* Contact shadow under the pedestal: the board used to float over the
   stage. Screen-space ellipse, behind the board wrapper. */
.scene::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 66%;
  width: min(380px, 52%);
  height: 88px;
  transform: translateX(-50%);
  background: radial-gradient(50% 50% at 50% 50%,
      rgba(5, 3, 10, 0.55) 0%,
      rgba(5, 3, 10, 0.26) 52%,
      rgba(5, 3, 10, 0) 74%);
  pointer-events: none;
}

html[data-theme='light'] .scene::before {
  background: radial-gradient(50% 50% at 50% 50%,
      rgba(16, 12, 24, 0.45) 0%,
      rgba(16, 12, 24, 0.22) 52%,
      rgba(16, 12, 24, 0) 74%);
}

.board-zoom-wrapper {
  transform-origin: center center;
  transition: transform 0.15s ease-out;
}

/* --------------------------------------------------------------------------
   BOARD + PEDESTAL
   -------------------------------------------------------------------------- */

.board {
  position: relative;
  display: grid;
  grid-template-columns: repeat(var(--board-size), var(--tile-size));
  grid-template-rows: repeat(var(--board-size), var(--tile-size));
  gap: var(--gap);
  transform-style: preserve-3d;
  /* --tilt-x / --tilt-y are driven by pointer parallax (desktop only). */
  transform: rotateX(calc(56deg + var(--tilt-x, 0deg)))
             rotateZ(calc(-33deg + var(--tilt-y, 0deg)));
  user-select: none;
  -webkit-user-select: none;
  /* Custom two-finger pinch zoom on the board itself; the page still scrolls
     from the scene margins, pedestal apron and sidebar. */
  touch-action: none;
}

.pedestal,
.pedestal-mat {
  position: absolute;
  transform-style: preserve-3d;
  pointer-events: none;
}

.pedestal {
  inset: 0;
}

.pedestal-mat {
  /* Paper-ledge mat a little larger than the grid, sitting just under the
     blocks; the grid gaps reveal it, so gaps read as engraved grooves. */
  inset: calc(-1 * var(--gap) * 1.6);
  transform: translateZ(calc(-1 * var(--tile-depth) - 1px));
  background: var(--news-ink);
  border: 2px solid color-mix(in srgb, var(--news-ink) 70%, #000);
  box-shadow:
    inset 0 0 0 2px color-mix(in srgb, var(--news-paper) 24%, transparent),
    0 0 0 2px color-mix(in srgb, var(--news-ink) 30%, transparent);
  animation: mat-in 0.7s var(--turn-ease) backwards;
}

.pedestal-side {
  position: absolute;
  background: color-mix(in srgb, var(--news-ink) 78%, #000);
  backface-visibility: hidden;
}

.pedestal-north {
  left: 0; right: 0; top: 100%;
  height: calc(var(--tile-depth) + 14px);
  transform-origin: top;
  transform: rotateX(-90deg);
}

.pedestal-south {
  left: 0; right: 0; bottom: 100%;
  height: calc(var(--tile-depth) + 14px);
  transform-origin: bottom;
  transform: rotateX(90deg);
}

.pedestal-west {
  top: 0; bottom: 0; right: 100%;
  width: calc(var(--tile-depth) + 14px);
  transform-origin: right;
  transform: rotateY(-90deg);
}

.pedestal-east {
  top: 0; bottom: 0; left: 100%;
  width: calc(var(--tile-depth) + 14px);
  transform-origin: left;
  transform: rotateY(90deg);
}

.pedestal-ground {
  position: absolute;
  inset: -34px;
  transform: translateZ(-16px);
  background: radial-gradient(
    ellipse at center,
    rgba(0, 0, 0, 0.34) 0%,
    rgba(0, 0, 0, 0.18) 45%,
    transparent 72%
  );
  filter: blur(6px);
}

/* Corner wrap: each side strip extends a few pixels past both mat corners so
   perpendicular walls overlap instead of meeting in a bare miter — kills the
   hairline AA seams the original corner miters showed at oblique angles. A
   full-depth wrap reads as a stray fin from the top-down camera, so keep the
   overlap just wide enough to swallow the antialiasing. */
.pedestal-north,
.pedestal-south {
  left: -3px;
  right: -3px;
}

.pedestal-west,
.pedestal-east {
  top: -3px;
  bottom: -3px;
}

/* --------------------------------------------------------------------------
   TILES — printing blocks
   -------------------------------------------------------------------------- */

.tile {
  position: relative;
  width: var(--tile-size);
  height: var(--tile-size);
  transform-style: preserve-3d;
  /* Pivot at the block's volumetric centre: spins stay in place and flips
     tumble the block over into its own footprint instead of floating. */
  transform-origin: 50% 50% calc(var(--tile-depth) / -2);
  transition: transform 0.5s var(--turn-ease), scale 0.16s ease;
  cursor: pointer;
  will-change: transform;
  animation: tile-in 0.55s var(--turn-ease) backwards;
  animation-delay: var(--enter-delay, 0ms);
}

.tile:focus {
  outline: 2px solid var(--news-accent);
  outline-offset: 3px;
}

/* Pointer-driven affordances only for real pointers: on touch the sticky
   :hover state would leave tiles scaled and ghosted after a tap. */
.tile:hover {
  scale: 1.055;
}

@media (hover: none) {
  .tile:hover {
    scale: 1;
  }
}

.tile:active {
  scale: 0.9;
}

/* The cross of tiles a tap will turn: hovered tile lifts with an accent
   ring, its neighbours get an accent-tinted rim and dimmed arrows so the
   chain rule reads instantly before clicking. */
.tile.chain-self .face {
  box-shadow: inset 0 0 0 2px var(--news-accent), 0 4px 12px rgba(0, 0, 0, 0.35);
}

.tile.chain-ghost .face {
  filter: brightness(1.12);
  box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--news-accent) 62%, transparent), 0 4px 10px rgba(0, 0, 0, 0.3);
}

.tile.chain-ghost .glyph {
  opacity: 0.4;
}

/* Hint escalation: the full cross lights up when a hint is repeated. */
.tile.hint-cross .face {
  box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--news-accent) 70%, transparent), 0 4px 12px rgba(0, 0, 0, 0.35);
}

.tile.hint-cross .glyph {
  opacity: 0.45;
}

/* Hint: pulse the tile a solver-selected move should tap. */
.tile.hint-pulse {
  animation: hint-pulse 0.65s ease-in-out 4;
}

@keyframes hint-pulse {
  0%, 100% { scale: 1; }
  50% { scale: 1.12; }
}

.tile.hint-pulse .face {
  box-shadow: inset 0 0 0 3px var(--news-accent), 0 4px 14px rgba(0, 0, 0, 0.4);
}

/* Escalated hint: a floating direction chip pinned over the hinted tile,
   showing the exact move (spin CW/CCW or flip) and how many taps it takes.
   Anchored inside the zoom wrapper with projected coordinates so it tracks
   the 3D-transformed board. */
.hint-direction {
  position: absolute;
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 7px 11px;
  background: var(--news-accent);
  color: #fff;
  border: 2px solid var(--news-ink);
  box-shadow: 3px 3px 0 rgba(0, 0, 0, 0.35);
  font-weight: 800;
  font-size: 13px;
  letter-spacing: 0.06em;
  line-height: 1;
  pointer-events: none;
  white-space: nowrap;
  animation: hint-float 2.4s ease forwards;
}

.hint-direction svg {
  display: block;
}

.hint-direction.mirrored svg {
  transform: scaleX(-1);
}

@keyframes hint-float {
  0% { opacity: 0; transform: translate(-50%, -70%); }
  12% { opacity: 1; transform: translate(-50%, -105%); }
  78% { opacity: 1; transform: translate(-50%, -105%); }
  100% { opacity: 0; transform: translate(-50%, -135%); }
}

.face {
  position: absolute;
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
  border-radius: 4px;
  background: var(--tile-side);
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.38);
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Faint letterpress grain on the paper faces. */
.face::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 4px;
  background:
    repeating-linear-gradient(45deg, rgba(0, 0, 0, 0.028) 0 1px, transparent 1px 4px),
    linear-gradient(160deg, rgba(255, 255, 255, 0.07), transparent 55%);
  pointer-events: none;
}

/* Block geometry: the tile element IS the top printing surface (z = 0); the
   four side strips hinge off its edges and hang down by --tile-depth, and a
   flat bottom closes the block. Faces keep their semantic names (front = the
   south strip that faces the viewer at orientation 0). */
.face.top,
.face.bottom {
  inset: 0;
  border-radius: 6px;
}

.face.top {
  background: var(--tile-face);
  box-shadow:
    inset 0 0 0 1.5px color-mix(in srgb, var(--news-ink) 55%, transparent),
    0 5px 12px rgba(0, 0, 0, 0.4);
}

.face.bottom {
  /* The cap must be MIRRORED (rotateX 180), not just translated: its front
     normal has to point away from the block, or the underside is backface-
     culled the moment the tile flips over and face-down tiles render as dark
     see-through voids. Mirrored, the slate back + muted arrow read correctly
     when face-down and stay culled when face-up. */
  transform: translateZ(calc(-1 * var(--tile-depth))) rotateX(180deg);
  /* The back print: brushed slate — a quiet vertical sheen (per-tile light)
     over deep ink, ink double keyline, muted arrow. The old loud diagonal
     crosshatch read as a missing texture at any distance; slate stays
     unmistakable against the cyan faces because nothing else in the suite
     wears this value. */
  background:
    linear-gradient(92deg,
      color-mix(in srgb, var(--news-ink) 62%, #6b6478) 0%,
      color-mix(in srgb, var(--news-ink) 44%, #6b6478) 18%,
      color-mix(in srgb, var(--news-ink) 60%, #6b6478) 34%,
      color-mix(in srgb, var(--news-ink) 40%, #6b6478) 55%,
      color-mix(in srgb, var(--news-ink) 58%, #6b6478) 78%,
      color-mix(in srgb, var(--news-ink) 46%, #6b6478) 100%),
    #4a4560;
  box-shadow:
    inset 0 0 0 1.5px color-mix(in srgb, var(--news-ink) 60%, transparent),
    inset 0 0 0 5px color-mix(in srgb, #6b6478 30%, transparent),
    0 5px 12px rgba(0, 0, 0, 0.4);
}

.face.bottom .glyph {
  opacity: 0.75;
}

.face.front {
  left: 0; right: 0; top: 100%;
  height: var(--tile-depth);
  transform-origin: top;
  transform: rotateX(-90deg);
}

.face.back {
  left: 0; right: 0; bottom: 100%;
  height: var(--tile-depth);
  transform-origin: bottom;
  transform: rotateX(90deg);
}

.face.right {
  top: 0; bottom: 0; left: 100%;
  width: var(--tile-depth);
  transform-origin: left;
  transform: rotateY(90deg);
}

.face.left {
  top: 0; bottom: 0; right: 100%;
  width: var(--tile-depth);
  transform-origin: right;
  transform: rotateY(-90deg);
}

/* The engraved arrow: one clean glyph pointing "up" in local face space.
   No drop-shadow filter: it used to bleed past the tile silhouette.
   Fixed pale ink: tiles are the shared teal block colour in both themes,
   so the theme-flipping --news-ink would vanish in light mode. */
.glyph {
  width: 62%;
  height: 62%;
  background: #ece2cc;
  opacity: 0.92;
  clip-path: polygon(50% 6%, 92% 48%, 68% 48%, 68% 94%, 32% 94%, 32% 48%, 8% 48%);
  transition: background 0.25s ease, opacity 0.25s ease;
}

/* Side strips are long and thin: keep the arrow square and centred. */
.face.front .glyph,
.face.back .glyph,
.face.left .glyph,
.face.right .glyph {
  width: auto;
  height: 64%;
  aspect-ratio: 1;
}

.tile.aligned .glyph {
  background: var(--news-accent);
  opacity: 1;
}

/* A freshly aligned tile stamps itself: a one-shot ring flash. */
.tile.aligned::after {
  content: "";
  position: absolute;
  inset: -4px;
  border-radius: 8px;
  border: 2.5px solid var(--news-accent);
  opacity: 0;
  transform: translateZ(1px) scale(0.7);
  pointer-events: none;
}

.tile.just-aligned::after {
  animation: stamp 0.5s ease-out;
}

@keyframes stamp {
  0% { opacity: 0.95; transform: translateZ(1px) scale(0.7); }
  100% { opacity: 0; transform: translateZ(1px) scale(1.28); }
}

/* Solve wave: tiles hop in diagonal order. */
.board.wave .tile {
  animation: wave-hop 0.6s ease-in-out backwards;
  animation-delay: var(--wave-delay, 0ms);
}

@keyframes wave-hop {
  0%, 100% { translate: 0 0 0; }
  45% { translate: 0 0 26px; }
}

.board.solved .pedestal-mat {
  box-shadow:
    inset 0 0 0 2px color-mix(in srgb, var(--news-accent) 75%, transparent),
    0 0 26px color-mix(in srgb, var(--news-accent) 45%, transparent);
}

.board.solved .tile .glyph {
  background: var(--news-accent);
}

@keyframes tile-in {
  0% {
    translate: 0 0 -70px;
    opacity: 0;
  }
  60% {
    opacity: 1;
  }
  100% {
    translate: 0 0 0;
    opacity: 1;
  }
}

/* Faces fade separately: opacity on the 3D-transformed tile itself would
   flatten its preserve-3d children mid-animation. */
.tile .face {
  animation: face-in 0.55s ease-out backwards;
  animation-delay: var(--enter-delay, 0ms);
}

@keyframes face-in {
  0% { opacity: 0; }
  100% { opacity: 1; }
}

@keyframes mat-in {
  0% {
    translate: 0 0 -46px;
    opacity: 0;
  }
  100% {
    translate: 0 0 calc(-1 * var(--tile-depth) - 1px);
    opacity: 1;
  }
}

/* --------------------------------------------------------------------------
   STAGE HUD
   -------------------------------------------------------------------------- */

.stage-hud {
  position: absolute;
  top: 14px;
  left: 0;
  right: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  padding: 0 14px;
  z-index: 20;
  pointer-events: none;
}

.hud-chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 7px 13px;
  background: var(--news-paper);
  border: 1.5px solid var(--news-ink);
  box-shadow: 2.5px 2.5px 0 rgba(0, 0, 0, 0.28);
  color: var(--news-ink);
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  line-height: 1;
  white-space: nowrap;
}

/* display:inline-flex above would defeat the hidden attribute's UA style. */
.hud-chip[hidden] {
  display: none;
}

.hud-goal {
  color: var(--news-accent);
}

.hud-goal svg {
  display: block;
}

.hud-goal-text {
  color: var(--news-ink);
}

.hud-progress {
  gap: 9px;
  padding: 5px 13px 5px 6px;
}

.hud-ring {
  width: 27px;
  height: 27px;
  transform: rotate(-90deg);
}

.hud-ring-track,
.hud-ring-fill {
  fill: none;
  stroke-width: 4.5;
}

.hud-ring-track {
  stroke: color-mix(in srgb, var(--news-ink) 22%, transparent);
}

.hud-ring-fill {
  stroke: var(--news-accent);
  stroke-linecap: round;
  stroke-dasharray: 100;
  stroke-dashoffset: 100;
  transition: stroke-dashoffset 0.45s var(--turn-ease);
}

.hud-combo {
  background: var(--news-accent);
  border-color: var(--news-accent);
  color: #fff;
}

/* Live efficiency feedback: the moves chip warms up once the player goes
   over par, so drifting off the shortest path is visible at a glance. */
#hud-moves.over-par {
  border-color: color-mix(in srgb, var(--news-incorrect) 65%, var(--news-ink));
  box-shadow: 2.5px 2.5px 0 color-mix(in srgb, var(--news-incorrect) 40%, transparent);
}

.hud-combo.pop {
  animation: combo-pop 0.3s var(--turn-ease);
}

@keyframes combo-pop {
  0% { scale: 0.7; }
  60% { scale: 1.14; }
  100% { scale: 1; }
}

/* --------------------------------------------------------------------------
   STAGE TOOLS (undo / sound) — bottom-left of the stage
   -------------------------------------------------------------------------- */

.stage-tools {
  position: absolute;
  left: 16px;
  bottom: 16px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  z-index: 21;
}

.stage-fab {
  width: 48px;
  height: 48px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--news-paper);
  border: 2px solid var(--news-ink);
  box-shadow: 2.5px 2.5px 0 rgba(0, 0, 0, 0.3);
  color: var(--news-ink);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: transform 0.16s ease, background 0.16s ease, color 0.16s ease, opacity 0.16s ease;
}

.stage-fab:hover:not(:disabled) {
  background: var(--news-accent);
  border-color: var(--news-accent);
  color: #fff;
}

.stage-fab:active:not(:disabled) {
  transform: scale(0.9);
}

.stage-fab:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

.stage-fab:focus-visible {
  outline: 2px solid var(--news-accent);
  outline-offset: 3px;
}

/* Sound FAB: the "on" state shows waves, muted shows a crossed speaker. */
.stage-fab .sound-off {
  display: none;
}

.stage-fab.muted .sound-on {
  display: none;
}

.stage-fab.muted .sound-off {
  display: block;
}

.stage-fab.muted {
  color: color-mix(in srgb, var(--news-ink) 55%, transparent);
}

/* --------------------------------------------------------------------------
   MESSAGE TOAST
   -------------------------------------------------------------------------- */

#message {
  position: fixed;
  left: 50%;
  bottom: 18px;
  translate: -50% 0;
  max-width: min(480px, calc(100vw - 36px));
  background: var(--news-ink);
  color: var(--news-paper);
  padding: 13px 17px;
  border-radius: 0;
  border: 1px solid var(--news-ink);
  box-shadow: var(--shadow-hard);
  z-index: var(--z-toast, 10050);
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 0.3s ease, transform 0.3s ease;
  pointer-events: none;
  font-weight: 600;
  font-size: 14px;
  line-height: 1.45;
  text-align: center;
}

#message.show {
  opacity: 1;
  transform: translateY(0);
}

/* Keep clear of the rotation + help FABs on phones. */
@media (max-width: 768px) {
  #message {
    right: 14px;
    left: 14px;
    bottom: 116px;
    /* Undo the desktop centring translate; left/right anchor it here. */
    translate: 0 0;
    text-align: center;
  }
}

/* --------------------------------------------------------------------------
   VICTORY — full letterpress treatment: hard ink rules, offset plate shadow,
   kicker set between rules, and buttons that press into the page.
   -------------------------------------------------------------------------- */

/* The shared overlay transitions `all`, which drags `visibility` through the
   fade — during that window the dialog is unfocusable and an immediate
   focus() after .active is a silent no-op. Page-scoped override: visibility
   flips instantly on show (keyboard focus works at once) and stays visible
   through the fade-out on close. */
#victory-overlay {
  transition: opacity var(--transition-base, 0.2s ease-in-out), visibility 0s linear 0.2s;
}

#victory-overlay.active {
  transition: opacity var(--transition-base, 0.2s ease-in-out), visibility 0s;
}

.tiles3d-victory {
  position: relative;
  min-width: min(430px, 88vw);
  background: var(--news-paper);
  border: 2.5px solid var(--news-ink);
  box-shadow: 8px 8px 0 rgba(0, 0, 0, 0.35);
  padding-top: 30px;
  padding-bottom: 30px;
  overflow: hidden;
}

/* Inner hairline rule, the classic newspaper box. */
.tiles3d-victory::before {
  content: "";
  position: absolute;
  inset: 7px;
  border: 1.5px solid color-mix(in srgb, var(--news-ink) 35%, transparent);
  pointer-events: none;
}

.victory-kicker {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--news-accent);
  margin-bottom: 10px;
}

.victory-kicker::before,
.victory-kicker::after {
  content: "";
  width: 34px;
  height: 2px;
  background: var(--news-accent);
}

.tiles3d-victory-title {
  font-family: var(--news-font-display, inherit);
  font-size: var(--font-size-2xl, 28px);
  font-weight: 900;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--color-text-primary);
  margin-bottom: var(--spacing-md, 16px);
}

.victory-stars {
  font-size: 44px;
  line-height: 1;
  margin-bottom: var(--spacing-md, 16px);
  letter-spacing: 0.1em;
}

.victory-stars .star {
  color: color-mix(in srgb, var(--news-ink) 25%, transparent);
  transition: color 0.2s ease;
}

.victory-stars .star.lit {
  color: #f3b229;
  text-shadow: 0 2px 0 rgba(0, 0, 0, 0.18);
  animation: star-pop 0.45s var(--turn-ease) backwards;
  animation-delay: var(--star-delay, 0ms);
}

@keyframes star-pop {
  0% { scale: 0.2; opacity: 0; }
  70% { scale: 1.35; opacity: 1; }
  100% { scale: 1; opacity: 1; }
}

.tiles3d-victory-copy {
  color: var(--color-text-secondary);
  margin-bottom: var(--spacing-lg, 24px);
  font-size: 15px;
  line-height: 1.7;
}

.tiles3d-victory-copy .victory-par {
  color: var(--news-accent);
  font-weight: 700;
}

.tiles3d-victory-copy .victory-sub {
  font-size: 13px;
  letter-spacing: 0.04em;
  color: var(--color-text-muted);
}

.victory-actions {
  display: flex;
  gap: 12px;
  justify-content: center;
  flex-wrap: wrap;
}

/* Letterpress buttons scoped to this dialog: hard ink borders and offset
   shadows that collapse when pressed, matching the stage FABs. */
.tiles3d-victory .btn {
  border: 2px solid var(--news-ink);
  border-radius: 0;
  font-weight: 800;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  font-size: 13px;
  padding: 11px 22px;
  box-shadow: 3px 3px 0 rgba(0, 0, 0, 0.3);
  transition: transform 0.12s ease, box-shadow 0.12s ease, background 0.16s ease, color 0.16s ease;
}

.tiles3d-victory .btn-primary {
  background: var(--news-accent);
  border-color: var(--news-ink);
  color: #fff;
}

.tiles3d-victory .btn-primary:hover,
.tiles3d-victory .btn-primary:focus-visible {
  background: color-mix(in srgb, var(--news-accent) 86%, #000);
  color: #fff;
}

.tiles3d-victory .btn-secondary {
  background: var(--news-paper);
  color: var(--news-ink);
}

.tiles3d-victory .btn-secondary:hover,
.tiles3d-victory .btn-secondary:focus-visible {
  background: var(--news-ink);
  color: var(--news-paper);
}

.tiles3d-victory .btn:active {
  transform: translate(2px, 2px);
  box-shadow: 1px 1px 0 rgba(0, 0, 0, 0.3);
}

.victory-flawless {
  display: inline-block;
  margin-top: 10px;
  padding: 5px 12px;
  border: 2px solid var(--news-accent);
  color: var(--news-accent);
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  transform: rotate(-3deg);
}

.tiles3d-announcer {
  position: absolute;
  left: -10000px;
}

/* --------------------------------------------------------------------------
   SMALL SCREENS
   -------------------------------------------------------------------------- */

@media (max-width: 768px) {
  .scene {
    padding-top: 48px;
  }

  .stage-hud {
    top: 10px;
    gap: 6px;
  }

  .hud-chip {
    font-size: 10.5px;
    padding: 6px 10px;
  }

  .hud-goal-text {
    /* Keep the goal glyph chip compact on phones. */
    max-width: 130px;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .hud-ring {
    width: 23px;
    height: 23px;
  }

  .stage-tools {
    left: 12px;
    bottom: 12px;
    gap: 8px;
  }

  .stage-fab {
    width: 44px;
    height: 44px;
  }

  .victory-stars {
    font-size: 38px;
  }
}

/* --------------------------------------------------------------------------
   REDUCED MOTION
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .tile,
  .tile .face,
  .pedestal-mat {
    animation: none;
  }

  .board.wave .tile {
    animation: none;
  }

  .tile {
    transition: transform 0.18s ease, scale 0.12s ease;
  }

  .hud-ring-fill {
    transition: none;
  }

  .hint-direction {
    animation: none;
    transform: translate(-50%, -105%);
  }

  .victory-stars .star.lit {
    animation: none;
  }
}
