/* style.css
   The look of LOOT RUN (SPEC.md §5): dark, pixel, glowing.
   Sections: 1 basics · 2 background (storm map, edges, flash) · 3 shared pieces
             4 title · 5 HUD + slots · 6 chest · 7 storm timer + guessing
             8 reveal card + other loot · 9 reveal effects · 10 Pirate Token screen
             11 pop-ups on the left edge · 12 end screen + VICTORY ROYALE
             13 sprites, badges, arrows · 14 small phones · 15 less motion */

/* ---------- 1. Basics ---------- */

:root {
  /* Pixel corners: a square with two-step notches cut out of each corner. */
  --notch-8: polygon(8px 0, calc(100% - 8px) 0, calc(100% - 8px) 4px, calc(100% - 4px) 4px, calc(100% - 4px) 8px, 100% 8px, 100% calc(100% - 8px), calc(100% - 4px) calc(100% - 8px), calc(100% - 4px) calc(100% - 4px), calc(100% - 8px) calc(100% - 4px), calc(100% - 8px) 100%, 8px 100%, 8px calc(100% - 4px), 4px calc(100% - 4px), 4px calc(100% - 8px), 0 calc(100% - 8px), 0 8px, 4px 8px, 4px 4px, 8px 4px);
  --notch-6: polygon(6px 0, calc(100% - 6px) 0, calc(100% - 6px) 3px, calc(100% - 3px) 3px, calc(100% - 3px) 6px, 100% 6px, 100% calc(100% - 6px), calc(100% - 3px) calc(100% - 6px), calc(100% - 3px) calc(100% - 3px), calc(100% - 6px) calc(100% - 3px), calc(100% - 6px) 100%, 6px 100%, 6px calc(100% - 3px), 3px calc(100% - 3px), 3px calc(100% - 6px), 0 calc(100% - 6px), 0 6px, 3px 6px, 3px 3px, 6px 3px);
  --bg: #070B16;
  --panel: #0E1526;
  --border: #1E2A44;
  --text: #D7E3F4;
  --dim: #7A8BA8;
  --cyan: #4FE3FF;
  --pink: #FF4F8B;
  --storm: #6B3FD1;
  --gold: #FFD447;
  --tier: #4FE3FF; /* reveal.js swaps this to the tier color during a reveal */
  --storm-rgb: 123, 104, 176; /* storm.js fades this to the color of your loot */
  --chest-w: 98px; /* the pixel chest: 1/3 of its picture size, so pixels stay even */
}

* { box-sizing: border-box; }

/* Every picture and canvas keeps crisp pixels when scaled (SPEC §5b). */
img, canvas {
  image-rendering: pixelated;
  image-rendering: crisp-edges;
}
img { image-rendering: pixelated; }

[hidden] { display: none !important; }

html { height: 100%; }

body {
  margin: 0;
  min-height: 100%;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  background: var(--bg); /* flat (the map covers it anyway) */
  color: var(--text);
  font-family: "VT323", "Courier New", monospace;
  font-size: 20px;
  line-height: 1.2;
  -webkit-text-size-adjust: 100%;
  overflow-x: hidden;
}

/* Layers, bottom to top: the map, sparkles and scanlines (z-index 0), the
   footer (1), then the game itself (2): the prompt, chest, card, chips and
   buttons always sit above the map layers. */
#app {
  position: relative;
  z-index: 2;
  flex: 1;
  width: 100%;
  max-width: 520px;
  margin: 0 auto;
  padding: 12px 16px 8px;
  /* a soft shadow behind every letter, so text reads well over the map */
  text-shadow: 0 2px 0 rgba(0, 0, 0, 0.55);
}

.screen {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 14px;
}

.footer {
  position: relative;
  z-index: 1; /* above the map, below the game (and its fixed HUD + hotbar) */
  padding: 10px 16px 14px;
  text-align: center;
  font-size: 14px;
  color: var(--dim);
  text-shadow: 0 1px 0 #000;
}
.footer a { color: var(--cyan); }

/* ---------- 2. Background: storm map, page edges, flash ---------- */

/* The pixel island + storm, drawn by storm.js on a small canvas and blown up
   with crisp pixels. */
.storm-map {
  position: fixed;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}
.storm-map canvas {
  position: absolute;
  left: 0;
  top: 0;
  image-rendering: pixelated;
  image-rendering: crisp-edges;
}
/* While you're guessing, the map picture is dimmed and greyed so the prompt
   and the answer box are the brightest things on screen. Only the map
   canvas dims: your marker, the circles and the storm (the overlay canvas
   on top of it) stay bright. It comes back to full brightness for the
   reveal and the rotation. */
#storm-canvas, #storm-labels { transition: filter 0.3s steps(3); }
body.guessing #storm-canvas, body.guessing #storm-labels { filter: brightness(0.5) saturate(0.35); }
/* The location names are real text on a canvas that isn't blown up, so they
   stay sharp (no chunky pixels). v6: the same for the HD map and the loot
   path's HD chest icons. */
#storm-canvas, #storm-labels, #storm-icons { image-rendering: auto; }
/* v7 Part 6: the storm is a smooth, anti-aliased circle on its own sharp
   canvas (one pixel per screen pixel), never dimmed. */
#storm-zone { image-rendering: auto; }
/* The 3D battle bus (bus3d.js): v6: drawn smooth at screen resolution (not
   pixelated), moved around by its `transform`, with a soft shadow so it
   reads on the map. Like the storm and your marker, it is never dimmed. */
#bus-3d {
  transform-origin: 0 0;
  will-change: transform;
  image-rendering: auto;
  filter: drop-shadow(0 3px 3px rgba(0, 0, 0, 0.45));
}
#bus-3d[hidden] { display: none; }
/* On the end screen the route map is the hero: the big map isn't shown a
   second time behind it. */
body.on-done .storm-map { display: none; }

/* During the rotation the chest and card step back so you can watch your
   marker travel across the map; they come back for NEXT CHEST. */
#stage { transition: opacity 0.25s steps(3); }
body.rotating #stage { opacity: 0.18; }

/* CRT scanlines: on the background only (above the map, under the game),
   never over the chest, the token, the loot card or the route map. */
.scanlines {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background: repeating-linear-gradient(to bottom, rgba(0, 0, 0, 0.16) 0 1px, transparent 1px 3px);
}

/* Tiny floating loot sparkles (game.js adds the dots) */
.sparkles {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
}
.sparkle {
  position: absolute;
  width: 3px;
  height: 3px;
  background: var(--cyan);
  opacity: 0;
  animation: sparkle-float 7s linear infinite;
}
@keyframes sparkle-float {
  0%   { opacity: 0; transform: translateY(0); }
  20%  { opacity: 0.7; }
  80%  { opacity: 0.25; }
  100% { opacity: 0; transform: translateY(-70px); }
}

/* The storm glow around the page edges, in the color of your loot. storm.js
   turns it up as the storm creeps in; it pulses in the last 5 seconds. */
.storm {
  position: fixed;
  inset: 0;
  z-index: 40;
  pointer-events: none;
  opacity: 0.35;
  box-shadow: inset 0 0 80px 26px rgba(var(--storm-rgb), 0.6);
}
.storm.danger { animation: storm-pulse 1s steps(4) infinite; }
/* While guessing the edge glow stays subtle (the prompt is the star). */
body.guessing .storm { box-shadow: inset 0 0 40px 12px rgba(var(--storm-rgb), 0.45); }
body.guessing .sparkles { display: none; } /* no sparkles while guessing */
@keyframes storm-pulse {
  50% { box-shadow: inset 0 0 120px 46px rgba(var(--storm-rgb), 0.8); }
}

/* Screen flash: gold (default), white (Legendary), storm purple (a miss) */
.flash {
  position: fixed;
  inset: 0;
  z-index: 45;
  pointer-events: none;
  background: #FFC63A;
  opacity: 0;
}
.flash.white { background: #FFFFFF; }
.flash.storm { background: var(--storm); }
.flash.go { animation: flash 0.7s ease-out; }
.flash.storm.go { animation-duration: 1s; }
@keyframes flash {
  0%   { opacity: 0; }
  15%  { opacity: 0.6; }
  100% { opacity: 0; }
}

/* ---------- 3. Shared pieces ---------- */

.panel {
  background: rgba(14, 21, 38, 0.92);
  border: 3px solid var(--border);
  padding: 12px 14px;
}

.eyebrow {
  margin: 0;
  color: var(--dim);
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.note {
  margin: 0;
  max-width: 360px;
  font-size: 16px;
  color: var(--dim);
}

.small-title {
  margin: 0;
  font-size: 15px;
  letter-spacing: 0.14em;
  color: var(--dim);
}

/* Chunky pixel button */
.btn {
  min-height: 48px;
  padding: 6px 18px;
  font-family: inherit;
  font-size: 24px;
  letter-spacing: 0.06em;
  color: #04121A;
  text-shadow: none;
  background: var(--cyan);
  border: 3px solid #0A2A33;
  border-radius: 0;
  box-shadow: 0 4px 0 #1B8FA6;
  cursor: pointer;
}
.btn:hover { background: #8EEEFF; }
.btn:active { transform: translateY(4px); box-shadow: 0 0 0 #1B8FA6; }
.btn:focus-visible, .link-btn:focus-visible, .slot:focus-visible {
  outline: 3px solid var(--pink);
  outline-offset: 2px;
}
.btn-big {
  min-height: 56px;
  padding: 8px 28px;
  font-size: 30px;
}

/* A button that looks like plain text (HOW TO PLAY) */
.link-btn {
  min-height: 44px;
  padding: 4px 12px;
  font-family: inherit;
  font-size: 22px;
  letter-spacing: 0.1em;
  color: var(--cyan);
  background: none;
  border: none;
  cursor: pointer;
}
.small-link {
  font-size: 16px;
  color: var(--dim);
  text-decoration: underline;
}

/* ---------- 4. Title screen ---------- */

/* The title sits on a dark panel so it reads clearly over the map. */
#screen-title {
  margin-top: 5vh;
  padding: 18px 14px 20px;
  background: rgba(7, 11, 22, 0.84);
  border: 3px solid var(--border);
  box-shadow: 0 4px 0 #000;
}

.logo {
  margin: 4px 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
  font-family: "Press Start 2P", "VT323", monospace;
  font-size: clamp(40px, 15vw, 68px);
  font-weight: normal;
  line-height: 1;
  color: var(--cyan);
  text-shadow: 4px 4px 0 var(--pink), 6px 6px 0 #000;
}

.subtitle {
  margin: 6px 0 0;
  letter-spacing: 0.14em;
  color: var(--text);
}

.tagline {
  margin: 0;
  color: var(--dim);
}

.howto {
  width: 100%;
  max-width: 400px;
  text-align: left;
}
.howto ol {
  margin: 0 0 10px;
  padding-left: 24px;
}
.howto li { margin-bottom: 4px; }

.tier-list {
  margin: 0;
  padding: 10px 0 0;
  list-style: none;
  border-top: 2px solid var(--border);
  display: grid;
  grid-template-columns: 1fr; /* one tier per line, in point order (worst to best) */
  gap: 4px 12px;
  font-size: 18px;
}
.tier-list li {
  display: flex;
  align-items: center;
  gap: 8px;
}
.tier-chip {
  width: 14px;
  height: 14px;
  flex: none;
  border: 2px solid rgba(255, 255, 255, 0.35);
}
.tier-list .tier-points {
  margin-left: auto;
  color: var(--dim);
}

/* ---------- 5. HUD + inventory squares ---------- */

/* The HUD sits in the screen corners, like Fortnite's: players left and
   score (with our pixel icons) top left, the minimap top right. */
:root {
  --hud-h: 104px; /* room the top HUD takes; the play screen starts below it */
  --hotbar-h: 92px; /* room the hotbar takes at the bottom */
  --slot: 64px; /* hotbar slot size: the item art (64 px) at exactly 1x */
}
.hud {
  position: fixed;
  z-index: 30;
  top: 0;
  left: 0;
  right: 0;
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  padding: 8px 10px;
  pointer-events: none;
}
.hud-stats {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 6px 10px 6px 8px;
  background: rgba(7, 11, 22, 0.78);
  border: 2px solid var(--border);
  box-shadow: 2px 2px 0 #000;
}
.hud-stat {
  display: grid;
  grid-template-columns: auto auto;
  grid-template-rows: auto auto;
  column-gap: 8px;
  align-items: center;
  text-align: left;
}
.hud-icon-slot {
  grid-row: span 2;
  width: 27px;
  height: 27px;
}
.hud-icon {
  display: block;
  width: 27px; /* 9-pixel icon at 3x */
  height: 27px;
}
.hud-label {
  font-size: 14px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--dim);
}
.hud-value {
  font-family: "Press Start 2P", "VT323", monospace;
  font-size: 18px;
  line-height: 1;
  color: var(--text);
  text-shadow: 2px 2px 0 #000;
}

/* The minimap: the whole island, the storm, your route and marker
   (drawn by storm.js). v7 Part 4: bigger (104 px on phones, up to 264 px on
   big screens, section 14) and sharp: its canvas has as many pixels as the
   screen shows there and the HD map is drawn smoothly on it, so it's not
   blown up with chunky pixels any more. (Before v7: 88 px, 176 px on big
   screens, from an 88-pixel canvas.) */
.minimap {
  width: 104px;
  height: 104px;
  image-rendering: auto;
  border: 2px solid var(--border);
  box-shadow: 2px 2px 0 #000;
  background: #0D4965;
}

/* The play screen starts below the HUD and leaves room for the hotbar. */
#screen-play { padding-top: calc(var(--hud-h) - 4px); }
body.on-play { padding-bottom: var(--hotbar-h); }


/* The hotbar: 7 big slots at the bottom of the screen, like Fortnite's. */
.hotbar {
  position: fixed;
  z-index: 30;
  left: 0;
  right: 0;
  bottom: 0;
  height: var(--hotbar-h);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  gap: 4px;
  padding: 0 8px 8px;
  background: rgba(7, 11, 22, 0.82);
  border-top: 2px solid var(--border);
  pointer-events: none;
}
.hotbar-slots { gap: 4px; }
.hotbar-slots .slot {
  width: var(--slot);
  height: var(--slot);
  border-width: 3px;
  background-color: rgba(7, 11, 22, 0.8);
  box-shadow: 2px 2px 0 #000;
}
.hotbar-slots .slot.lit { animation: slot-lit 0.6s steps(3); }
@keyframes slot-lit {
  0%, 66% { box-shadow: 0 0 0 4px var(--tier), 2px 2px 0 #000; }
}
.hotbar .hud-label {
  padding: 1px 6px;
  background: rgba(7, 11, 22, 0.78);
}
.storm-label {
  margin: 0;
  font-family: "Press Start 2P", "VT323", monospace;
  font-size: 9px;
  letter-spacing: 0.1em;
  color: #B9A4FF;
  text-shadow: 2px 2px 0 #000;
  visibility: hidden;
}
body.guessing .stage:not(.spawning) .storm-label {
  visibility: visible;
  animation: label-blink 1.2s steps(2) infinite;
}
@keyframes label-blink { 50% { opacity: 0.4; } }

/* Phones with the keyboard open: the hotbar gets out of the way so the
   prompt, chest, timer and answer box fit above the keyboard. (Pop-ups sit
   below the answer box, see section 11.) */
body.typing .hotbar { display: none; }
body.typing.on-play { padding-bottom: 0; }
/* ...and the chest gets smaller, the timer ring sits on the same row as the
   answer box, and the little labels step aside. */
body.typing { --chest-w: 64px; }
body.typing .storm-label { display: none; }
body.typing .stage { gap: 4px; }
body.typing .prompt-text { margin-top: 0; font-size: 28px; min-height: 0; }
body.typing .prompt-rule { min-height: 0; font-size: 16px; padding: 2px 8px; }
body.typing .guess-area {
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
}
body.typing .clock { flex: none; padding: 4px; gap: 0; } /* just the round key, beside the box */
body.typing .clock-word { display: none; }
body.typing .clock-key { width: 40px; height: 40px; }
body.typing .clock-num { font-size: 17px; }
body.typing .guess-row { flex: 1 1 0; width: auto; min-width: 0; }
body.typing .guess-msg { flex-basis: 100%; min-height: 1.2em; }

/* The item flying from the loot card into its hotbar slot. */
.flying-item {
  position: fixed;
  z-index: 44;
  margin: 0;
  pointer-events: none;
  image-rendering: pixelated;
}

/* The 7 small inventory squares */
.slots {
  display: flex;
  gap: 3px;
  pointer-events: auto;
}
.slot {
  position: relative;
  width: 26px;
  height: 26px;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 2px solid var(--border);
  border-radius: 0;
  background: rgba(7, 11, 22, 0.6);
  font: inherit;
  font-size: 11px;
  line-height: 1;
  color: var(--dim);
}
button.slot { cursor: pointer; }
.slot.current {
  border-color: var(--cyan);
  animation: slot-blink 1s steps(2) infinite;
}
@keyframes slot-blink {
  50% { border-color: var(--border); }
}
/* Filled square: v7 gives it a Fortnite-style rarity background (.rarity-bg,
   section 13b). Only the border color is set here. */
.slot.filled { border-color: var(--tier); }
/* A miss: a dark empty slot with a storm icon (two storm-purple rings). */
.slot.miss {
  border-color: var(--border);
  background: #1A2233;
}
.slot.miss::before,
.slot.miss::after {
  content: "";
  position: absolute;
  inset: 24%;
  border: 3px solid var(--storm);
  border-right-color: transparent;
  border-radius: 50%;
}
.slot.miss::after {
  inset: 38%;
  border-width: 2px;
  border-left-color: transparent;
  border-right-color: var(--storm);
}
/* v7 Part 2: "held position" (a miss while already inside the next circle):
   a grey location pin instead of the storm swirl. No storm damage. */
.slot.miss.held::before {
  inset: 22% 30% 34%;
  border: 3px solid #7A8BA8;
  border-radius: 50% 50% 50% 0;
  transform: rotate(-45deg);
}
.slot.miss.held::after {
  inset: auto 34% 18%;
  height: 3px;
  border: 0;
  border-radius: 0;
  background: #7A8BA8;
}

.slots-big { gap: 6px; }
.slots-big .slot {
  width: 64px; /* the item art at exactly 1x */
  height: 64px;
  border-width: 3px;
}
@media (max-width: 560px) {
  .slots-big .slot { width: 44px; height: 44px; } /* 7 fit on a phone */
}
.slots-big .slot.selected {
  outline: 3px solid var(--cyan);
  outline-offset: 2px;
}

/* ---------- 6. Chest ---------- */

.stage {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}

.prompt-text {
  position: relative;
  z-index: 2;
  margin: 6px 0 0;
  min-height: 2.2em; /* room for two lines so the chest doesn't jump */
  display: flex;
  align-items: center;
  font-size: 34px; /* VT323, large: readable under a timer (SPEC §5b) */
  font-weight: normal;
  line-height: 1.05;
  text-shadow: 2px 2px 0 #000;
  text-wrap: balance; /* even lines: never a lone word ("3") on the last one */
}
.stage.spawning .prompt-text {
  color: var(--dim);
  letter-spacing: 0.1em;
}

/* The prompt's rule ("Anything in the Chapter 2 Season 2 loot pool…").
   v6: near-white, bigger, on a dark see-through backing with a strong
   shadow, so it reads over any part of the map (snow, sand or grass). */
.prompt-rule {
  margin: 0;
  max-width: 460px;
  padding: 3px 10px 4px;
  font-size: 18px;
  line-height: 1.15;
  color: #F2F5FA;
  text-shadow: 1px 1px 0 #000, 0 0 3px #000;
  background: rgba(7, 11, 22, 0.62);
  border-radius: 4px;
  text-wrap: balance;
}
@media (min-width: 700px) {
  .prompt-rule { font-size: 21px; } /* desktop: at least ~20 px */
}
/* Nothing to show yet: no empty dark box either. */
.prompt-rule:empty { visibility: hidden; }
/* The rule stays hidden (but keeps its space) until the clock starts */
.stage.spawning .prompt-rule { visibility: hidden; }
/* During the reveal the rule steps aside to make room for the loot card */
.stage.revealing .prompt-rule { display: none; }

/* The chest picture has empty space above it (room for the lid to pop) and
   a little below it; the area is sized to the chest itself. */
.chest-area {
  position: relative;
  width: 100%;
  height: calc(var(--chest-w) * 0.95);
  display: flex;
  align-items: flex-end;
  justify-content: center;
}

.chest {
  position: relative;
  z-index: 1;
  width: var(--chest-w);
  aspect-ratio: 294 / 348;
  margin-bottom: calc(var(--chest-w) * -0.054);
  filter: drop-shadow(0 0 6px rgba(255, 200, 80, 0.45));
  animation: chest-idle 2s ease-in-out infinite;
}
.chest-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  image-rendering: pixelated;
}
@keyframes chest-idle {
  50% { filter: drop-shadow(0 0 14px rgba(255, 210, 90, 0.75)); }
}

/* Gold shimmer while closed: three tiny twinkles on the chest */
.twinkle {
  position: absolute;
  z-index: 3;
  width: 4px;
  height: 4px;
  background: #FFF6C8;
  box-shadow: 0 0 6px #FFE27A;
  opacity: 0;
  animation: twinkle 2.4s steps(3) infinite;
}
.twinkle:nth-of-type(1) { left: 22%; top: 34%; }
.twinkle:nth-of-type(2) { left: 70%; top: 30%; animation-delay: 0.8s; }
.twinkle:nth-of-type(3) { left: 48%; top: 66%; animation-delay: 1.6s; }
@keyframes twinkle {
  0%, 60%, 100% { opacity: 0; transform: scale(0.5); }
  75% { opacity: 1; transform: scale(1.4) rotate(45deg); }
}
.stage.revealing .twinkle { display: none; }

/* The light between lid and body, only seen once the lid lifts */
.chest-light {
  position: absolute;
  left: 8%;
  right: 12%;
  top: 46%;
  height: 7%;
  background: color-mix(in srgb, var(--tier) 60%, white);
  box-shadow: 0 0 14px 6px var(--tier);
  opacity: 0;
}

/* Countdown: the chest blinks in */
.stage.spawning .chest {
  opacity: 0.45;
  animation: chest-spawn 0.5s steps(2) infinite;
}
@keyframes chest-spawn {
  50% { opacity: 0.8; }
}

/* 1. Shake (0.6 s) */
.stage.shaking .chest { animation: chest-shake 0.6s linear both; }
@keyframes chest-shake {
  0%   { transform: none; }
  10%  { transform: translateX(-2px) rotate(-2deg); }
  20%  { transform: translateX(2px) rotate(2deg); }
  30%  { transform: translateX(-2px) rotate(-2deg); }
  40%  { transform: translateX(3px) rotate(2deg); }
  50%  { transform: translateX(-3px) rotate(-3deg); }
  60%  { transform: translateX(3px) rotate(3deg) translateY(-1px); }
  70%  { transform: translateX(-4px) rotate(-3deg) translateY(-2px); }
  80%  { transform: translateX(4px) rotate(3deg) translateY(-2px); }
  90%  { transform: translateX(-3px) rotate(-2deg) translateY(-1px); }
  100% { transform: none; }
}

/* 2. Light beam: pale while shaking, then the tier color (before the card) */
.beam {
  position: absolute;
  z-index: 0;
  left: 50%;
  bottom: 40%;
  width: 64px;
  height: 180px;
  margin-left: -32px;
  background: linear-gradient(to top, var(--beam), transparent);
  transform-origin: bottom;
  transform: scaleY(0);
  opacity: 0;
  pointer-events: none;
}
.beam-plain { --beam: #FFF4C8; }
.beam-tier { --beam: var(--tier); width: 76px; margin-left: -38px; }
.stage.shaking .beam-plain { animation: beam-rise 0.6s steps(6) forwards; }
@keyframes beam-rise {
  from { transform: scaleY(0); opacity: 0; }
  to   { transform: scaleY(0.55); opacity: 0.4; }
}
.stage.beamed .beam-plain { animation: beam-fade 0.3s forwards; }
@keyframes beam-fade {
  from { transform: scaleY(0.55); opacity: 0.4; }
  to   { transform: scaleY(0.55); opacity: 0; }
}
.stage.beamed .beam-tier { animation: beam-tier 0.5s steps(5) forwards; }
@keyframes beam-tier {
  from { transform: scaleY(0.5); opacity: 0; }
  to   { transform: scaleY(1); opacity: 0.8; }
}

/* 3. The lid pops open and stays up; the chest bounces and glows */
.chest-lid { transform-origin: 90% 48%; }
.stage.popped .chest-lid { animation: lid-pop 0.5s cubic-bezier(0.2, 1.4, 0.4, 1) forwards; }
@keyframes lid-pop {
  0%   { transform: none; }
  45%  { transform: translateY(-17%) rotate(-16deg); }
  100% { transform: translateY(-10%) rotate(-10deg); }
}
.stage.popped .chest-light { opacity: 1; transition: opacity 0.15s; }
.stage.popped .chest {
  filter: drop-shadow(0 0 14px var(--tier));
  animation: chest-pop 0.35s steps(4);
}
@keyframes chest-pop {
  40% { transform: translateY(-6px) scale(1.05); }
}

/* Soft glow behind the chest: Rare and up */
.chest-halo {
  position: absolute;
  z-index: 0;
  left: 50%;
  bottom: -20px;
  width: 220px;
  height: 220px;
  margin-left: -110px;
  border-radius: 50%;
  background: radial-gradient(circle, var(--tier) 0%, transparent 62%);
  opacity: 0;
  pointer-events: none;
}
.stage.popped.tier-rare .chest-halo { opacity: 0.35; transition: opacity 0.4s; }
.stage.popped.tier-epic .chest-halo { opacity: 0.6; transition: opacity 0.4s; }
.stage.popped.tier-legendary .chest-halo,
.stage.popped.tier-token .chest-halo { opacity: 0.75; transition: opacity 0.4s; }

/* Miss: no reveal. The chest goes dark and stays shut. */
.stage.missed .chest {
  filter: grayscale(1) brightness(0.45);
  animation: none;
}
.stage.missed .beam,
.stage.missed .chest-light { display: none; }
.stage.missed .chest-lid { animation: none; }

/* Tapped to skip: jump every reveal animation to its end */
.stage.skip .chest,
.stage.skip .chest-lid,
.stage.skip .beam {
  animation-duration: 0.01s !important;
  animation-delay: 0s !important;
}

/* ---------- 7. Storm timer ring + guessing ---------- */

.guess-area {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}

/* The timer (SPEC §4b, v6): modeled on Fortnite's chest prompt. A dark,
   slightly see-through rounded bar; on the left a round "key" with a thin
   ring that drains smoothly (game.js moves it every frame) and the seconds
   left inside it; on the right the word SEARCH in a bold sans-serif. Smooth,
   not pixel art (v6 style change). */
.clock {
  --ring: #F2F4F8; /* white-ish while there's time */
  display: inline-flex;
  align-items: center;
  gap: 12px;
  padding: 6px 18px 6px 6px;
  background: rgba(14, 18, 30, 0.72);
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 12px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.35);
}
.clock-key {
  position: relative;
  width: 46px;
  height: 46px;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
}
.clock-ring {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  transform: rotate(-90deg); /* starts at the top and drains round */
}
.ring-track {
  fill: rgba(255, 255, 255, 0.07);
  stroke: rgba(255, 255, 255, 0.16);
  stroke-width: 2.5;
}
.ring-fill {
  fill: none;
  stroke: var(--ring);
  stroke-width: 2.5;
  stroke-linecap: round;
  stroke-dasharray: 100;
  stroke-dashoffset: 0;
  transition: stroke 0.6s ease; /* turning red is smooth too */
}
/* The seconds left, where the game shows the key "V". */
.clock-num {
  position: relative;
  font-family: "Arial Black", "Helvetica Neue", Arial, sans-serif;
  font-weight: 900;
  font-size: 19px;
  line-height: 1;
  color: var(--ring);
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
  transition: color 0.6s ease;
}
.clock-word {
  font-family: "Arial Black", "Helvetica Neue", Arial, sans-serif;
  font-weight: 900;
  font-size: 22px;
  line-height: 1;
  letter-spacing: 0.02em;
  color: #FFFFFF;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
}
/* Countdown before the clock: a quiet grey ring, the countdown number in it. */
.clock.countdown { --ring: #8C96A8; }
.clock.countdown .clock-word { color: #B9C2D0; }
/* The last 5 seconds: the ring and number turn red, smoothly. No pulsing. */
.clock.danger { --ring: #FF4D4D; }

.guess-row {
  width: 100%;
  max-width: 420px;
  display: flex;
  gap: 8px;
}
.guess-input {
  flex: 1;
  min-width: 0;
  min-height: 48px;
  padding: 4px 12px;
  font-family: inherit;
  font-size: 26px; /* 16px or more also stops phones zooming in on focus */
  color: var(--text);
  text-shadow: none;
  background: var(--panel);
  border: 3px solid var(--border);
  border-radius: 0;
}
.guess-input:focus {
  outline: none;
  border-color: var(--cyan);
  box-shadow: 3px 3px 0 #000;
}
.guess-input::placeholder { color: #4A5874; }
/* During the countdown the box can't be typed in: it says "wait for it…". */
.guess-input.waiting {
  border-color: var(--border);
  box-shadow: none;
  caret-color: transparent;
  color: var(--dim);
  cursor: default;
}
.guess-input.waiting::placeholder { color: var(--dim); letter-spacing: 0.08em; }
.btn:disabled {
  background: #2C6E80;
  box-shadow: 0 4px 0 #17404B;
  color: #0A2A33;
  cursor: default;
}

.guess-row.shake { animation: shake 0.3s steps(6); }
@keyframes shake {
  0%, 100% { transform: translateX(0); }
  20%, 60% { transform: translateX(-8px); }
  40%, 80% { transform: translateX(8px); }
}

.guess-msg {
  margin: 0;
  min-height: 2.4em;
  max-width: 420px;
  color: var(--pink);
}

/* ---------- 8. Reveal card + other loot ---------- */

.reveal-area {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  cursor: pointer; /* tap anywhere to skip */
  /* The Legendary light sweep is wider than a phone screen. Clip it at the
     sides, or phones zoom the whole page out to fit it. */
  overflow-x: clip;
  /* Room under the chips for the NEXT CHEST button, which is pinned above
     the hotbar (below): everything can always be scrolled clear of it. */
  padding-bottom: 76px;
}

/* The loot card (SPEC §3a v3): a pixel item card. The outer box is the
   frame in the tier color with notched corners; the inner panel holds the
   text. The card waits (invisible, keeping its space) and flips in at "carded". */
.loot-card-wrap {
  position: relative;
  width: 100%;
  max-width: 340px;
  display: flex;
  justify-content: center;
  filter: drop-shadow(3px 3px 0 #000); /* hard pixel shadow (no blur) */
}
.loot-card {
  position: relative;
  z-index: 2;
  width: 100%;
  padding: 4px; /* the frame */
  background: var(--tier);
  clip-path: var(--notch-8);
  opacity: 0;
  transform: perspective(700px) rotateY(90deg);
}
.loot-inner {
  padding: 10px 12px 12px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  background: #0B1222;
  clip-path: var(--notch-6);
}

/* v6: the card reads cause -> result. Top: what you answered (the biggest
   text), its rarity badge and points, the tier blurb. */
.loot-head {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  text-align: center;
}
.loot-label {
  margin: 0;
  font-family: "Press Start 2P", "VT323", monospace;
  font-size: 9px;
  letter-spacing: 0.12em;
  color: var(--dim);
}
.loot-answer {
  max-width: 100%;
  font-size: 40px;
  line-height: 0.95;
  color: #FFFFFF;
  text-shadow: 2px 2px 0 #000;
  overflow-wrap: anywhere;
  text-wrap: balance;
}
.loot-counted {
  font-size: 16px;
  color: var(--dim);
}
/* The rarity badge: "COMMON · +10 PTS" in a box of the tier color. */
.loot-tier {
  margin-top: 3px;
  padding: 4px 8px 3px;
  font-family: "Press Start 2P", "VT323", monospace;
  font-size: 11px;
  line-height: 1.4;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--tier) 60%, #fff);
  text-shadow: 2px 2px 0 #000;
  background: color-mix(in srgb, var(--tier) 22%, #0B1222);
  border: 2px solid var(--tier);
}

/* Then: "Your COMMON answer dropped:", a small arrow, and the item, smaller
   than the answer. It appears half a second after the card: first the line
   and the empty frame, then the item pops out of the chest into the frame. */
.loot-drop {
  width: 100%;
  margin-top: 6px;
  padding-top: 8px;
  border-top: 2px dashed var(--border);
  display: flex;
  flex-direction: column;
  align-items: center;
  opacity: 0;
}
.loot-connector {
  margin: 0;
  font-size: 19px;
  line-height: 1.1;
  color: var(--text);
}
.loot-rarity {
  color: color-mix(in srgb, var(--tier) 65%, #fff);
  text-shadow: 1px 1px 0 #000;
}
.loot-down {
  width: 0;
  height: 0;
  margin: 3px 0 5px;
  border-left: 6px solid transparent;
  border-right: 6px solid transparent;
  border-top: 7px solid var(--dim);
}
.loot-item {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  max-width: 100%;
}
.loot-frame {
  position: relative;
  flex: none;
}
.loot-item-text {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  min-width: 0;
  max-width: 180px;
  text-align: left;
}
.stage.carded .loot-drop { animation: drop-show 0.3s 0.35s steps(3) both; }
.stage.carded .loot-art .sprite,
.stage.carded .loot-name { animation: item-pop 0.35s 0.5s steps(5) both; }
@keyframes drop-show {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes item-pop {
  0%   { opacity: 0; transform: translateY(-48px) scale(0.4); }
  60%  { opacity: 1; transform: translateY(0) scale(1.12); }
  100% { opacity: 1; transform: none; }
}
/* Skipped (tapped) or arriving later: everything is just there. */
.stage.skip .loot-drop,
.stage.skip .loot-art .sprite,
.stage.skip .loot-name {
  animation: none;
  opacity: 1;
  transform: none;
}
.stage.carded .loot-card { animation: card-flip 0.5s steps(6) forwards; }
@keyframes card-flip {
  0%   { opacity: 1; transform: perspective(700px) rotateY(90deg) scale(0.85); }
  100% { opacity: 1; transform: perspective(700px) rotateY(0deg) scale(1); }
}
.stage.skip .loot-card {
  animation: none;
  opacity: 1;
  transform: none;
}

/* The item art: v7 gives it a Fortnite-style rarity background (.rarity-bg,
   section 13b: a rich gradient with light rays from the top left, a lighter
   inner edge and a thin dark border), the sprite at a whole-number size.
   (Before v7: flat diagonal stripes in the tier color, notched corners.) */
.loot-art {
  position: relative;
  z-index: 1;
  min-width: 88px; /* a 64-pixel item at 1x, plus the frame */
  min-height: 88px;
  padding: 12px;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden; /* the shimmer and sparkle stay inside the frame */
}
/* A faint lighter line inside the art's edge. The Epic tier pulses it. */
.loot-art::before {
  content: "";
  position: absolute;
  inset: 4px;
  border: 2px solid var(--r-hi, color-mix(in srgb, var(--tier) 55%, #fff));
  opacity: 0.35;
  pointer-events: none;
}

/* What kind of loot it is ("TRAP"): a small tag over the item's name. It's
   only shown once the item has dropped (SPEC §3a). */
.loot-inner { position: relative; }
.loot-category {
  padding: 0 6px;
  font-size: 15px;
  line-height: 1.2;
  letter-spacing: 0.12em;
  white-space: nowrap;
  color: #FFFFFF;
  background: color-mix(in srgb, var(--tier) 35%, #070B16);
  border: 1px solid var(--tier);
}
/* The item's name: medium text, smaller than your answer. */
.loot-name {
  font-size: 23px;
  line-height: 1;
  color: color-mix(in srgb, var(--tier) 70%, #fff);
  text-shadow: 2px 2px 0 #000;
  overflow-wrap: anywhere;
}

/* ----- Each tier gets its own touch on the card ----- */

/* Uncommon: a small pixel sparkle pops on the art when the card arrives. */
.stage.carded.tier-uncommon .loot-art::after {
  content: "";
  position: absolute;
  top: 14px;
  right: 14px;
  width: 4px;
  height: 4px;
  background: #FFFFFF;
  box-shadow: 0 -4px #FFFFFF, 0 4px #FFFFFF, -4px 0 #FFFFFF, 4px 0 #FFFFFF,
              0 -8px #E6FFD9, 0 8px #E6FFD9, -8px 0 #E6FFD9, 8px 0 #E6FFD9;
  opacity: 0;
  animation: card-sparkle 0.8s 0.85s steps(4) both; /* right after the item pops in */
}
@keyframes card-sparkle {
  0%   { opacity: 0; transform: scale(0.5); }
  40%  { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(0.5); }
}

/* Rare: one shimmer pass, a light band sliding across the art. */
.stage.carded.tier-rare .loot-art::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 26px;
  background: rgba(255, 255, 255, 0.45);
  transform: translateX(-40px) skewX(-20deg);
  animation: card-shimmer 0.7s 0.85s steps(8) both;
}
@keyframes card-shimmer {
  to { transform: translateX(140px) skewX(-20deg); }
}

/* Epic: the item's frame pulses (v6: on the item, not the whole card). */
.stage.carded.tier-epic .loot-art::before {
  animation: epic-pulse 1s 0.85s steps(2) infinite;
}
.stage.skip.tier-epic .loot-art::before { animation: epic-pulse 1s steps(2) infinite; }
@keyframes epic-pulse {
  50% { border-color: #FFFFFF; opacity: 1; }
}

/* Legendary: a rotating light sweep behind the item (plus the white flash,
   reveal.js). The card's panel clips it, so it never leaves the card. */
.card-sweep {
  position: absolute;
  z-index: 0;
  left: 50%;
  top: 50%;
  width: 240px;
  height: 240px;
  margin: -120px 0 0 -120px;
  border-radius: 50%;
  background: conic-gradient(rgba(255, 236, 170, 0.8) 0deg 22deg, transparent 22deg 90deg,
    rgba(255, 236, 170, 0.55) 90deg 104deg, transparent 104deg 180deg,
    rgba(255, 236, 170, 0.8) 180deg 202deg, transparent 202deg 270deg,
    rgba(255, 236, 170, 0.55) 270deg 284deg, transparent 284deg 360deg);
  opacity: 0;
  pointer-events: none;
}
.stage.carded.tier-legendary .card-sweep {
  opacity: 1;
  transition: opacity 0.3s 0.5s steps(3);
  animation: sweep-spin 6s steps(48) infinite;
}
@keyframes sweep-spin { to { transform: rotate(360deg); } }

/* Pirate Token: its own deep teal (the tier color) with a gold frame, and
   the medallion spins like a coin in 8 frames. */
.stage.tier-token .loot-card { background: var(--gold); }
/* (v7: the item itself sits on the Mythic gold rarity background, not teal.) */
.stage.tier-token .loot-art::before { border-color: #FFF6B0; opacity: 0.6; }
.stage.tier-token .loot-name,
.stage.tier-token .loot-tier,
.stage.tier-token .loot-rarity { color: var(--gold); }
.stage.tier-token .loot-tier { border-color: var(--gold); }
.stage.carded.tier-token .loot-art .token-badge { animation: coin-spin 1.2s steps(8) infinite; }
@keyframes coin-spin {
  from { transform: perspective(200px) rotateY(0deg); }
  to   { transform: perspective(200px) rotateY(360deg); }
}
.loot-blurb {
  margin: 0;
  color: var(--dim);
}
/* The Pirate Token card has its fun fact instead (and needs the room). */
.stage.tier-token .loot-blurb { display: none; }
.loot-why {
  margin: 6px 0 0;
  padding-top: 8px;
  border-top: 2px solid var(--border);
  color: #FFC63A;
}

/* A miss uses the same card, just quieter: "NO ANSWER · THE STORM GOT YOU"
   and no item at all. */
.stage.missed .loot-card { background: var(--border); }
.stage.missed .loot-drop,
.stage.missed .loot-label { display: none; }
.stage.missed .loot-answer { font-size: 28px; color: var(--dim); }
/* v7 Part 2: under NO LOOT, one line saying what actually happened (kicked
   off the bus, held your spot, or caught in the storm). */
.stage.missed .loot-blurb { color: var(--text); font-size: 21px; line-height: 1.15; text-wrap: balance; }
.stage.missed:not(.held) .loot-card { background: color-mix(in srgb, var(--storm) 55%, var(--border)); }
.stage.missed .loot-tier {
  color: var(--dim);
  text-shadow: none;
  background: transparent;
  border-color: var(--border);
}

/* NEXT CHEST only appears when the rotation is done. (v6: OTHER LOOT moved
   to the tab on the left edge, section 11b.) */
.next-btn {
  visibility: hidden;
  opacity: 0;
}
/* NEXT CHEST is pinned just above the hotbar, so it's always visible
   without scrolling, however tall the loot card is. The dark edge around
   it keeps it readable if something scrolls behind it. */
.next-btn {
  position: fixed;
  z-index: 32;
  left: 0;
  right: 0;
  bottom: calc(var(--hotbar-h) + 10px);
  width: fit-content;
  margin: 0 auto;
  box-shadow: 0 4px 0 #1B8FA6, 0 0 0 6px rgba(7, 11, 22, 0.88), 0 4px 0 6px rgba(7, 11, 22, 0.88);
}
.next-btn:active { box-shadow: 0 0 0 #1B8FA6, 0 0 0 6px rgba(7, 11, 22, 0.88); }
/* NEXT CHEST shows after the rotation. */
.reveal-area.moved .next-btn {
  visibility: visible;
  opacity: 1;
  transition: opacity 0.3s;
}

/* Answer chips colored by tier */
.chips {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 6px;
}
.loot-chip {
  position: relative;
  padding: 2px 8px;
  font-size: 18px;
  line-height: 1.15;
  color: var(--text);
  background: color-mix(in srgb, var(--tier) 22%, var(--panel));
  border: 2px solid var(--tier);
}
.loot-chip .sweat-badge {
  right: -6px;
  bottom: -6px;
  width: 10px;
  height: 10px;
  border-width: 2px;
  box-shadow: none;
}

/* ---------- 9. Reveal effects (particles, scaled by tier) ---------- */

/* reveal.js drops little <span>s here, at the top of the chest. Each one
   gets its own direction (--dx, --dy …) from reveal.js. */
.fx-layer {
  position: absolute;
  z-index: 3;
  left: 50%;
  top: 30%;
  width: 0;
  height: 0;
  pointer-events: none;
}
.fx {
  position: absolute;
  left: 0;
  top: 0;
  pointer-events: none;
}

/* Common: a small dust puff at the chest's base */
.fx-dust {
  width: 7px;
  height: 7px;
  background: #B9AC92;
  animation: fx-dust 0.7s ease-out var(--delay) both;
}
@keyframes fx-dust {
  from { opacity: 0.9; transform: translate(0, var(--y0)) scale(1); }
  to   { opacity: 0; transform: translate(var(--dx), calc(var(--y0) + var(--dy))) scale(2.4); }
}

/* Sweaty: sweat drops fly off the chest */
.fx-sweat {
  width: 10px;
  height: 10px;
  background: #8FD8FF;
  border: 2px solid #0B2A44;
  border-radius: 0 50% 50% 50%;
  animation: fx-sweat 0.9s ease-in var(--delay) both;
}
@keyframes fx-sweat {
  0%   { opacity: 1; transform: translate(0, 0) rotate(45deg); }
  40%  { opacity: 1; transform: translate(calc(var(--dx) * 0.6), var(--up)) rotate(45deg); }
  100% { opacity: 0; transform: translate(var(--dx), 50px) rotate(45deg); }
}

/* Uncommon, Rare, Epic (and the Pirate Token screen): sparkles in the tier color */
.fx-spark {
  width: var(--s);
  height: var(--s);
  background: var(--c);
  box-shadow: 0 0 8px var(--c);
  animation: fx-spark var(--d) ease-out var(--delay) both;
}
@keyframes fx-spark {
  0%   { opacity: 0; transform: translate(0, 0) scale(0.4) rotate(45deg); }
  20%  { opacity: 1; }
  100% { opacity: 0; transform: translate(var(--dx), var(--dy)) scale(1) rotate(45deg); }
}

/* Legendary: gold confetti bursts up and rains down */
.fx-confetti {
  width: 6px;
  height: 11px;
  background: var(--c);
  animation: fx-confetti 1.7s cubic-bezier(0.2, 0.6, 0.4, 1) var(--delay) both;
}
@keyframes fx-confetti {
  0%   { opacity: 1; transform: translate(0, 0) rotate(0deg); }
  35%  { opacity: 1; transform: translate(calc(var(--dx) * 0.6), var(--up)) rotate(calc(var(--rot) * 0.4)); }
  100% { opacity: 0; transform: translate(var(--dx), 190px) rotate(var(--rot)); }
}

/* ---------- 10. The Pirate Token screen (SPEC §4d) ---------- */

.token-screen,
.victory-screen {
  position: fixed;
  inset: 0;
  z-index: 48;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  cursor: pointer;
  text-align: center;
}

/* 1. Screen dims; parchment with faint map lines fades in */
.token-dim {
  position: absolute;
  inset: 0;
  background: rgba(4, 6, 12, 0.82);
  opacity: 0;
  transition: opacity 0.3s;
}
.token-parchment {
  position: absolute;
  inset: 0;
  background: url("assets/token/parchment.png") center / cover;
  image-rendering: pixelated;
  opacity: 0;
  transition: opacity 0.45s 0.1s;
}
.token-parchment::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse at center, transparent 35%, rgba(60, 30, 0, 0.6) 100%),
    repeating-linear-gradient(0deg, rgba(90, 55, 10, 0.18) 0 2px, transparent 2px 38px),
    repeating-linear-gradient(90deg, rgba(90, 55, 10, 0.18) 0 2px, transparent 2px 38px);
}
.token-screen.on .token-dim,
.victory-screen.on .token-dim { opacity: 1; }
.token-screen.on .token-parchment { opacity: 0.92; }

/* 2. Gold sunburst rays, turning slowly */
.token-rays {
  position: absolute;
  left: 50%;
  top: 45%;
  width: 160vmax;
  height: 160vmax;
  margin: -80vmax 0 0 -80vmax;
  background: repeating-conic-gradient(rgba(255, 236, 150, 0.55) 0deg 7deg, transparent 7deg 18deg);
  -webkit-mask: radial-gradient(circle, #000 4%, transparent 42%);
  mask: radial-gradient(circle, #000 4%, transparent 42%);
  opacity: 0;
  transition: opacity 0.5s;
  animation: rays-spin 26s linear infinite;
}
.token-screen.rays .token-rays,
.victory-screen.on .token-rays { opacity: 1; }
@keyframes rays-spin { to { transform: rotate(360deg); } }

.token-stage,
.victory-stage {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  transition: transform 0.45s ease-in, opacity 0.45s ease-in;
}

/* 3. The medallion drops in from above, bounces, and spins once */
.token-medal {
  width: 192px; /* 2x the medallion's 96-pixel art */
  height: 192px;
  image-rendering: pixelated;
  opacity: 0;
  filter: drop-shadow(0 6px 0 rgba(60, 30, 0, 0.6));
}
.token-screen.drop .token-medal { animation: medal-drop 1.2s linear forwards; }
@keyframes medal-drop {
  0%   { opacity: 1; transform: translateY(-75vh); animation-timing-function: ease-in; }
  32%  { opacity: 1; transform: translateY(0); animation-timing-function: ease-out; }
  44%  { transform: translateY(-28px); animation-timing-function: ease-in; }
  56%  { transform: translateY(0) perspective(400px) rotateY(0deg); animation-timing-function: ease-in-out; }
  100% { opacity: 1; transform: translateY(0) perspective(400px) rotateY(360deg); }
}

/* 4. Big pixel text slams in */
.token-title {
  margin: 22px 16px 0;
  font-family: "Press Start 2P", "VT323", monospace;
  font-size: clamp(17px, 5.2vw, 30px);
  line-height: 1.6;
  color: var(--gold);
  text-shadow: 3px 3px 0 #5A3300, -2px -2px 0 #5A3300, 2px -2px 0 #5A3300, -2px 2px 0 #5A3300;
  opacity: 0;
}
.token-screen.slam .token-title { animation: slam 0.35s cubic-bezier(0.3, 1.5, 0.5, 1) forwards; }
@keyframes slam {
  0%   { opacity: 0; transform: scale(2.8); }
  100% { opacity: 1; transform: scale(1); }
}
.token-screen.slam .token-stage { animation: jolt 0.25s 0.25s steps(3); }
@keyframes jolt {
  33% { transform: translate(-4px, 3px); }
  66% { transform: translate(4px, -2px); }
}
.token-sparkles {
  position: absolute;
  left: 50%;
  top: 72px;
  width: 0;
  height: 0;
}

.token-hint {
  position: absolute;
  bottom: 9%;
  margin: 0;
  font-size: 18px;
  letter-spacing: 0.12em;
  color: #FFE9A6;
  opacity: 0;
}
.token-screen.slam .token-hint,
.victory-screen.on .token-hint { opacity: 0.85; transition: opacity 0.4s 1s; }

/* 5. It shrinks into the item card */
.token-screen.closing .token-stage {
  transform: translate(var(--to-x, 0), var(--to-y, 0)) scale(0.18);
  opacity: 0;
}
.token-screen.closing .token-dim,
.token-screen.closing .token-parchment,
.token-screen.closing .token-rays,
.token-screen.closing .token-hint {
  opacity: 0;
  transition: opacity 0.45s;
}

/* ---------- 11. Pop-ups on the left edge (SPEC §4g) ---------- */

/* The rotation banner, the Pirate Token banner and the feed share one stack.
   Each pop-up slides in from the left edge in chunky steps, and they stack
   top to bottom. game.js clears them all when the next clock starts.

   Phones and narrow windows (the default here): every pop-up is ONE line,
   and game.js places the stack (--popups-top) just below the chest or the
   answer box, so it never covers the prompt, chest, timer or answer box.
   Wide screens (further down): the stack sits in the empty space to the
   left of the game, and pop-ups can use more than one line. */
.popups {
  position: fixed;
  z-index: 46;
  left: 0;
  top: var(--popups-top, 108px);
  max-width: 100vw;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  text-align: left;
  pointer-events: none;
}
.feed {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  max-width: 100%;
}
.popup {
  margin: 0;
  max-width: calc(100vw - 12px);
  padding: 2px 10px 2px 8px;
  font-size: 18px;
  line-height: 1.25;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  color: var(--text);
  background: rgba(7, 11, 22, 0.9);
  border: 2px solid var(--border);
  border-left: 5px solid var(--tier, var(--dim));
  box-shadow: 2px 2px 0 #000;
  animation: popup-in 0.3s steps(4) both;
}
@keyframes popup-in {
  from { transform: translateX(-105%); }
  to   { transform: translateX(0); }
}

/* Feed lines fade out on their own after a few seconds. */
.feed-line { animation: popup-in 0.3s steps(4) both, feed-out 0.3s steps(3) 3.4s forwards; }
.feed-line b { font-weight: normal; color: var(--tier, var(--text)); }
@keyframes feed-out { to { opacity: 0; } }

/* Rotation banner (v7 Part 3): one sentence from the narrator (movement +
   place + item: "Zone pulled your way. Quick rotate up the hill to Lonely
   Lodge, blue Pump Shotgun behind the cabin. Solid."), with the distance as a
   small tag beside it ("240M", "DROP", "HELD"). Unlike the other pop-ups it
   may wrap, up to 3 lines on phones. (Before v7: "ZONE PULLED TO YOU ·
   ROTATED 240m TO" + the place.) */
.popup.loc-banner {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 5px 10px 6px 8px;
  white-space: normal;
}
.loc-title {
  flex: none;
  margin-top: 2px;
  padding: 3px 4px 2px;
  font-family: "Press Start 2P", "VT323", monospace;
  font-size: 9px;
  line-height: 1.2;
  letter-spacing: 0.04em;
  color: var(--tier, var(--dim));
  border: 1px solid var(--tier, var(--dim));
  background: rgba(7, 11, 22, 0.6);
  text-shadow: none;
}
.loc-name {
  min-width: 0;
  font-size: 18px;
  line-height: 1.15;
  color: #FFFFFF;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Pirate Token banner: gold, with the medallion. Tap it to close it. */
.token-banner {
  --tier: var(--gold);
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--gold);
  background: #2F2205;
  border-color: var(--gold);
  pointer-events: auto;
  cursor: pointer;
}
.token-banner.out { animation: popup-out 0.3s steps(4) forwards; }
@keyframes popup-out {
  to { transform: translateX(-105%); }
}
.banner-token {
  flex: none;
  width: 24px; /* the medallion's 96-pixel art at 1/4 */
  height: 24px;
  image-rendering: pixelated;
}
.banner-text { min-width: 0; }
.banner-title {
  margin: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  letter-spacing: 0.04em;
}
/* The fun fact needs room: on one-line pop-ups it's left out (it's also in
   the chest's details on the end screen). */
.banner-why { display: none; }

/* Wide screens: there's empty space left of the game column (520 px wide),
   so the stack lives there, below PLAYERS LEFT and SCORE, and pop-ups can
   wrap onto more lines. */
@media (min-width: 1000px) {
  .popups {
    top: 112px;
    left: 10px;
    width: min(360px, calc((100vw - 520px) / 2 - 24px));
    gap: 6px;
  }
  .popup {
    max-width: 100%;
    padding: 5px 12px 6px 10px;
    white-space: normal;
    overflow: visible;
  }
  .popup.loc-banner { padding: 8px 12px 10px 10px; }
  .loc-name {
    display: block;
    font-size: 20px;
    line-height: 1.2;
    text-shadow: 2px 2px 0 #000;
  }
  .token-banner { align-items: flex-start; gap: 10px; }
  .banner-token { width: 48px; height: 48px; } /* 1/2 of the art */
  .banner-title { font-size: 21px; white-space: normal; }
  .banner-why {
    display: block;
    margin: 2px 0 0;
    font-size: 16px;
    color: #E8D9A8;
  }
}

/* ---------- 11b. The other answers in this chest (v6, SPEC §4e) ---------- */

/* A tab on the left edge that slides in after the reveal and stays until
   the next clock starts (game.js). Inside: the Pirate Token answer and its
   fun fact (unless you found it), then the other answers as tier chips.
   Phones and narrow windows (the default here): only a thin vertical
   "OTHER ANSWERS" handle shows, in the margin left of the loot card; a tap
   slides the panel out (and another tap slides it back). */
.loot-tab {
  position: fixed;
  z-index: 33; /* above NEXT CHEST while it's open; below the token screen */
  left: 0;
  top: 42%;
  display: flex;
  align-items: flex-start;
  animation: popup-in 0.3s steps(4) both;
}
.loot-tab[hidden] { display: none; }
.loot-tab-body {
  display: none;
  width: min(300px, calc(100vw - 40px));
  max-height: 70vh;
  overflow-y: auto;
  padding: 8px 10px 10px;
  background: rgba(7, 11, 22, 0.94);
  border: 2px solid var(--border);
  border-left: 0;
  box-shadow: 2px 2px 0 #000;
}
.loot-tab.open .loot-tab-body { display: block; }
.loot-tab.open { z-index: 47; } /* opened on purpose: on top of the feed lines */
.loot-tab-handle {
  flex: none;
  width: 16px;
  padding: 10px 0;
  font-family: inherit;
  font-size: 13px;
  line-height: 16px;
  letter-spacing: 0.08em;
  color: #FFFFFF;
  writing-mode: vertical-rl;
  transform: rotate(180deg); /* reads bottom to top, like a file tab */
  background: #1B3A5C;
  border: 2px solid var(--cyan);
  border-left: 0;
  box-shadow: -2px 2px 0 #000;
  cursor: pointer;
}
.loot-tab.open .loot-tab-handle { background: var(--cyan); color: #04121A; }
.tab-title {
  margin: 0 0 4px;
  font-family: "Press Start 2P", "VT323", monospace;
  font-size: 10px;
  letter-spacing: 0.08em;
  color: var(--dim);
}
.tab-token {
  margin-bottom: 10px;
  padding-bottom: 8px;
  border-bottom: 2px solid var(--border);
}
.tab-token-name {
  margin: 0;
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 21px;
  line-height: 1.1;
  color: var(--gold);
}
.tab-token-medal {
  flex: none;
  width: 24px; /* the medallion's 96-pixel art at 1/4 */
  height: 24px;
  image-rendering: pixelated;
}
.tab-token-why {
  margin: 4px 0 0;
  font-size: 16px;
  line-height: 1.2;
  color: #E8D9A8;
}
.tab-chips { justify-content: flex-start; }

/* Wide screens: there's room left of the game column, so the panel is
   simply open there, at the bottom, above the hotbar (the pop-up stack is
   at the top of the same space). No handle needed. */
@media (min-width: 1000px) {
  .loot-tab {
    top: auto;
    left: 10px;
    bottom: calc(var(--hotbar-h) + 16px);
    width: min(360px, calc((100vw - 520px) / 2 - 24px));
  }
  .loot-tab-body {
    display: block;
    width: 100%;
    max-height: calc(100vh - var(--hotbar-h) - 400px);
    min-height: 0;
    border-left: 2px solid var(--border);
    border-left-width: 5px;
    border-left-color: var(--gold);
  }
  .loot-tab-handle { display: none; }
}

/* ---------- 12. End screen + VICTORY ROYALE ---------- */

#screen-done { padding-top: 2vh; gap: 12px; }

/* The route map, the hero of the end screen: the whole map at exactly 1x its
   pixel size on phones (320 px) and 2x on bigger screens (640 px, wider
   than the game column), so the pixels stay crisp. */
.route-map {
  position: relative;
  flex: none;
  width: 320px;
  max-width: 100%;
  aspect-ratio: 1;
  box-sizing: content-box;
  border: 3px solid var(--border);
  box-shadow: 0 4px 0 #000;
  background: #0D4965;
}
@media (min-width: 700px) {
  .route-map { width: 640px; max-width: none; }
}
.route-map canvas {
  display: block;
  width: 100%;
  height: 100%;
  image-rendering: auto; /* v6: the map and chest icons are HD; game.js matches the screen's pixels */
}
.route-labels {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.route-label {
  position: absolute;
  transform: translate(8px, -50%);
  padding: 2px 4px;
  font-family: "Press Start 2P", "VT323", monospace;
  font-size: 8px;
  line-height: 1.2;
  white-space: nowrap;
  color: #FFFFFF;
  text-shadow: 1px 1px 0 #000;
  background: rgba(7, 11, 22, 0.85);
  border: 2px solid var(--border);
}
.route-label.to-left { transform: translate(calc(-100% - 8px), -50%); }
/* v5: placed at the bottom edge of the final circle, so it hangs just below it. */
.route-label.final-label { border-color: #FFFFFF; transform: translate(-50%, 4px); }
@media (min-width: 700px) {
  .route-label { font-size: 10px; padding: 3px 6px; }
}
.route-label.pick-label { border-color: var(--cyan); color: var(--cyan); }

/* The tappable chest icons on the end screen's loot path (v5): invisible
   buttons laid exactly over the icons drawn on the map. A little bigger
   than the icon so they're easy to hit on a phone. */
.route-stops { position: absolute; inset: 0; }
.route-stop {
  position: absolute;
  transform: translate(-50%, -50%);
  min-width: 28px;
  min-height: 28px;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
}
.route-stop:focus-visible { outline: 2px solid var(--cyan); outline-offset: 1px; }

/* The story of your run (v5, SPEC §4k): below the route map, above the
   stats. The title in Press Start 2P (with the hard shadow), the sentences
   in VT323. Each sentence lights up its chests on the map when you hover
   or tap it. */
.story {
  width: 640px;
  max-width: 100%;
  box-sizing: border-box;
  padding: 12px 14px;
  background: var(--panel);
  border: 3px solid var(--border);
  box-shadow: 0 4px 0 #000;
  text-align: left;
}
.story[hidden] { display: none; }
.story-title {
  margin: 0 0 8px;
  font-family: "Press Start 2P", monospace;
  font-size: 14px;
  line-height: 1.5;
  color: #FFD447;
  text-shadow: 2px 2px 0 #000;
  text-wrap: balance;
}
.story-text {
  margin: 0;
  font-family: "VT323", monospace;
  font-size: 22px;
  line-height: 1.25;
  color: var(--text);
}
.story-line {
  cursor: pointer;
  border-bottom: 2px solid transparent;
}
.story-line:hover, .story-line:focus-visible, .story-line.on {
  color: #FFFFFF;
  border-bottom-color: #FFD447;
  outline: none;
}
@media (max-width: 480px) {
  .story-title { font-size: 12px; }
  .story-text { font-size: 20px; }
}

.done-final {
  margin: 0;
  font-size: 22px;
}

.placement {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
}
.place-num {
  font-family: "Press Start 2P", "VT323", monospace;
  font-size: clamp(44px, 14vw, 64px);
  line-height: 1.1;
  color: var(--cyan);
  text-shadow: 4px 4px 0 var(--pink), 6px 6px 0 #000;
}
.place-num.winner {
  color: var(--gold);
  text-shadow: 4px 4px 0 #8A4B00, 6px 6px 0 #000;
}
.place-headline {
  margin: 4px 0 0;
  font-family: "Press Start 2P", "VT323", monospace;
  font-size: 13px;
  line-height: 1.5;
  text-shadow: 2px 2px 0 #000;
  letter-spacing: 0.06em;
}

.done-total {
  margin: 0;
  display: flex;
  align-items: baseline;
  gap: 8px;
}
.done-score {
  font-family: "Press Start 2P", "VT323", monospace;
  font-size: 30px;
  line-height: 1;
  color: var(--cyan);
  text-shadow: 2px 2px 0 #000;
}

/* The details of one chest, under the loadout */
.detail {
  width: 100%;
  max-width: 420px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  text-align: left;
  border-color: var(--tier);
}
.detail p { margin: 0; }
.detail .chips { justify-content: flex-start; }
.detail-head { font-size: 15px; letter-spacing: 0.12em; color: var(--dim); }
.detail-prompt { font-size: 22px; }
.detail-loot { font-size: 24px; color: var(--tier); }
.detail-you { color: var(--text); }
.detail-token { color: #E8D9A8; }
.detail-token-name { color: var(--gold); }
.detail-place { color: var(--text); }

.share-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px;
}
.share-msg {
  margin: 0;
  min-height: 1.2em;
  font-size: 18px;
  color: var(--cyan);
}

.next-drop {
  margin: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
}
.next-drop-time {
  font-family: "Press Start 2P", "VT323", monospace;
  font-size: 20px;
  text-shadow: 2px 2px 0 #000;
  line-height: 1;
  letter-spacing: 0.06em;
  color: var(--text);
}

/* VICTORY ROYALE: gold-and-blue rays and big pixel text */
.victory-rays {
  background: repeating-conic-gradient(rgba(79, 227, 255, 0.4) 0deg 7deg, rgba(255, 212, 71, 0.35) 7deg 14deg, transparent 14deg 22deg);
}
.victory-place {
  margin: 0;
  font-family: "Press Start 2P", "VT323", monospace;
  font-size: 34px;
  color: var(--gold);
  text-shadow: 3px 3px 0 #5A3300;
}
.victory-title {
  margin: 14px 12px 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
  font-family: "Press Start 2P", "VT323", monospace;
  font-size: clamp(30px, 10vw, 60px);
  font-weight: normal;
  line-height: 1.1;
  color: var(--gold);
  text-shadow: 4px 4px 0 #8A4B00, 6px 6px 0 #000;
}
/* "#1" pops in alone and holds for a beat (~0.9 s), then VICTORY ROYALE
   slams in, then the screen shakes once. Never repeated. */
.victory-place,
.victory-title { opacity: 0; }
.victory-screen.on .victory-place { animation: place-pop 0.3s 0.15s steps(3) both; }
@keyframes place-pop {
  0%   { opacity: 0; transform: scale(0.4); }
  100% { opacity: 1; transform: scale(1); }
}
.victory-screen.on .victory-title { animation: slam 0.32s 1.05s steps(4) both; }
.victory-screen.on .victory-stage { animation: shake-once 0.36s 1.38s steps(6) both; }
@keyframes shake-once {
  0%, 100% { transform: translate(0, 0); }
  17% { transform: translate(-8px, 4px); }
  33% { transform: translate(7px, -5px); }
  50% { transform: translate(-6px, -3px); }
  67% { transform: translate(5px, 4px); }
  83% { transform: translate(-3px, 2px); }
}
.victory-screen.closing { opacity: 0; transition: opacity 0.4s; }

/* ---------- 13. Item sprites, badges, arrows ---------- */

/* Every pixel image stays crisp when scaled up (SPEC §5). */
.sprite,
.token-badge {
  image-rendering: pixelated;
  display: block;
}

/* The item sprite inside the loot card and the inventory squares. The rarity
   background behind it is the tier color, drawn with CSS (SPEC §5a). */
.loot-art .sprite { width: 64px; height: 64px; } /* game.js sets the exact whole-number size */
/* Badges sit inside the card art's corners (it has notched corners). */
.loot-art .sweat-badge { right: 8px; bottom: 8px; width: 16px; height: 16px; border-width: 2px; }
.loot-art .token-badge { left: 4px; bottom: 4px; width: 32px; height: 32px; }
.slot .sprite { width: 100%; height: 100%; }

/* The tiny label along the top of a filled slot that says what kind of
   loot is in it ("HEAL"). Empty slots have none: the kind stays a secret
   until the item has dropped (SPEC §3a). */
.slot-tag {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  padding: 0 1px;
  font-size: 11px;
  line-height: 1;
  letter-spacing: 0;
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  color: #FFFFFF;
  text-shadow: none;
  background: rgba(7, 11, 22, 0.72);
  pointer-events: none;
}
.hotbar-slots .slot .slot-tag,
.slots-big .slot .slot-tag { font-size: 13px; }
@media (max-width: 560px) {
  .hotbar-slots .slot .slot-tag,
  .slots-big .slot .slot-tag { font-size: 11px; }
}

/* Sweaty: a light-blue sweat drop drawn with CSS (a square with three round
   corners, turned so the point is at the top). */
.sweat-badge {
  position: absolute;
  right: -9px;
  bottom: -9px;
  width: 22px;
  height: 22px;
  background: #8FD8FF;
  border: 3px solid #0B2A44;
  border-radius: 0 50% 50% 50%;
  transform: rotate(45deg);
  box-shadow: inset -3px -3px 0 #4FA8E0;
}
.tier-chip { position: relative; }
.tier-chip .sweat-badge,
.slot .sweat-badge {
  right: -5px;
  bottom: -5px;
  width: 9px;
  height: 9px;
  border-width: 2px;
  box-shadow: none;
}
.slots-big .slot .sweat-badge {
  width: 14px;
  height: 14px;
}

/* The Pirate Token bonus badge, bottom-left corner (the sweat drop uses bottom-right). */
.token-badge {
  position: absolute;
  left: -14px;
  bottom: -14px;
  width: 48px; /* the medallion's 96-pixel art at 1/2 */
  height: 48px;
  filter: drop-shadow(2px 2px 0 #000);
}
.slot .token-badge {
  left: -6px;
  bottom: -6px;
  width: 16px;
  height: 16px;
  filter: none;
}
.slots-big .slot .token-badge { width: 22px; height: 22px; }

/* ---------- 13b. Fortnite-style rarity backgrounds (v7 Part 1) ---------- */
/* Every place an item is shown (hotbar, loot card, end-screen loadout, the
   details panel) sits on one of these, modeled on Fortnite's inventory
   slots (assets/reference/rarity-backgrounds.png): a rich gradient in the
   rarity color, lighter toward the top left, faint light rays fanning out
   from the top left, a slightly lighter inner edge and a thin dark border.
   All CSS, so it's crisp at every size. Which tier gets which look:
   config.js -> ITEM_LOOKS (Common and Sweaty grey, Uncommon green, Rare
   blue, Epic purple, Legendary orange-gold, Pirate Token Mythic gold).
     --r-hi   the light at the top left      --r-mid  the main color
     --r-lo   the dark bottom-right corner   --r-main the color for glows */
.rarity-grey      { --r-hi: #D6DCE1; --r-mid: #949DA6; --r-lo: #4F5760; --r-main: #9AA4AD; }
.rarity-green     { --r-hi: #B4F58A; --r-mid: #4FB23A; --r-lo: #1C5A16; --r-main: #5CC25A; }
.rarity-blue      { --r-hi: #98DCFF; --r-mid: #2B8FE8; --r-lo: #123F8C; --r-main: #3FA5FF; }
.rarity-purple    { --r-hi: #EAA8FF; --r-mid: #A247E6; --r-lo: #4B1882; --r-main: #B25CFF; }
.rarity-legendary { --r-hi: #FFD789; --r-mid: #EE8619; --r-lo: #8E4006; --r-main: #FF9A2E; }
.rarity-mythic    { --r-hi: #FFFBD0; --r-mid: #FFD21F; --r-lo: #A97F00; --r-main: #FFD447; }
.rarity-bg {
  border-color: #0A0E1A;
  background:
    /* the shine in the top-left corner */
    radial-gradient(circle at 18% 12%, rgba(255, 255, 255, 0.42) 0%, rgba(255, 255, 255, 0) 34%),
    /* light rays fanning out from the top left */
    repeating-conic-gradient(from 95deg at 12% 6%,
      rgba(255, 255, 255, 0.15) 0deg 4deg, rgba(255, 255, 255, 0) 6deg 11deg),
    /* the rarity color, light at the top left, dark at the bottom right */
    radial-gradient(circle at 22% 16%, var(--r-hi) 0%, var(--r-mid) 48%, var(--r-lo) 118%);
  background-color: var(--r-mid);
  /* a slightly lighter inner edge, and a little shade along the bottom */
  box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.28), inset 0 -8px 12px -6px rgba(0, 0, 0, 0.35);
}
.slot.rarity-bg { border-color: #0A0E1A; }
.hotbar-slots .slot.rarity-bg {
  background-color: var(--r-mid);
  box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.28), inset 0 -8px 12px -6px rgba(0, 0, 0, 0.35), 2px 2px 0 #000;
}
.loot-art.rarity-bg { border: 2px solid #0A0E1A; }
/* The item sprite keeps a hard shadow so it stands out on every color. */
.rarity-bg .sprite { filter: drop-shadow(1px 2px 0 rgba(0, 0, 0, 0.45)); }

/* An item tile in the end screen's details panel (v7). */
.item-tile {
  position: relative;
  flex: none;
  width: 52px;
  height: 52px;
  border: 2px solid #0A0E1A;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.item-tile .sprite { width: 44px; height: 44px; }
.item-tile .sweat-badge { right: -5px; bottom: -5px; width: 12px; height: 12px; border-width: 2px; box-shadow: none; }
.item-tile .token-badge { left: -6px; bottom: -6px; width: 22px; height: 22px; filter: none; }
.detail-loot { display: flex; align-items: center; gap: 10px; }

/* A miss on the loot card: a storm-purple swirl drawn with CSS rings. */
.loot-art.miss-art::before,
.loot-art.miss-art::after {
  content: "";
  position: absolute;
  inset: 22%;
  border: 4px solid var(--storm);
  border-right-color: transparent;
  border-radius: 50%;
  animation: storm-spin 1.6s linear infinite;
}
.loot-art.miss-art::after {
  inset: 36%;
  animation-direction: reverse;
}
@keyframes storm-spin { to { transform: rotate(360deg); } }

/* Small triangles that replace the old arrow characters (no emoji, SPEC §3). */
.arrow {
  display: inline-block;
  width: 0;
  height: 0;
  border-top: 0.32em solid transparent;
  border-bottom: 0.32em solid transparent;
  border-left: 0.5em solid currentColor; /* points right */
  vertical-align: 0.05em;
  transition: transform 0.15s;
}
.arrow.down,
.link-btn.open .arrow { transform: rotate(90deg); }

/* ---------- 14. Screen sizes ---------- */

/* Big screens: a bigger chest (1/2 of its picture size) */
@media (min-width: 700px) and (min-height: 760px) {
  :root { --chest-w: 147px; }
}

/* Phones: smaller hotbar slots so all 7 fit (7 x 44 px + gaps < 343 px). */
@media (max-width: 560px) {
  /* v7: 16 px more room at the top for the bigger minimap (104 px). */
  :root { --slot: 44px; --hotbar-h: 72px; --hud-h: 116px; }
  /* With the keyboard open the minimap goes back to 88 px, so the prompt,
     chest, timer and answer box still fit above the keyboard. */
  body.typing { --hud-h: 100px; }
  body.typing .minimap { width: 88px; height: 88px; }
}
/* Big screens: the HUD floats in the corners beside the game column, and
   the minimap is big: v7, about 1.5x its old 176 px (264 px), but never wider
   than the empty space beside the 520-px game column, so it never covers the
   prompt. */
@media (min-width: 900px) and (min-height: 700px) {
  :root { --hud-h: 16px; }
  .minimap {
    width: clamp(150px, calc((100vw - 520px) / 2 - 24px), 264px);
    height: clamp(150px, calc((100vw - 520px) / 2 - 24px), 264px);
  }
}

@media (max-width: 380px) {
  .prompt-text { font-size: 26px; }
  .clock-word { font-size: 20px; }
  .hud-label { font-size: 13px; letter-spacing: 0.05em; }
  :root { --slot: 42px; }
  .slots-big .slot { width: 40px; height: 40px; }
  .btn-big { font-size: 26px; padding: 8px 18px; }
}

/* ---------- 15. People who ask their device for less motion ---------- */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01s !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01s !important;
  }
  .sparkles { display: none; }
  /* No screen shake, ever. (Camera zoom, marker travel and the storm
     shrinking are skipped in storm.js/game.js: you just see the end state.) */
  .victory-screen.on .victory-stage,
  .token-screen.slam .token-stage,
  .guess-row.shake,
  .clock.danger { animation: none !important; }
  .card-sweep { animation: none !important; }
}
