:root{
  --bg-deep:#0E0F1A;
  --bg-panel:#171A2B;
  --grid-line:#262B45;
  --text-hi:#F1F0FA;
  --text-mid:#9C9FC2;
  --text-dim:#5A5E82;
  --font-head:'Space Grotesk',sans-serif;
  --font-mono:'IBM Plex Mono',monospace;
}
*{box-sizing:border-box;-webkit-tap-highlight-color:transparent;}

/* ---------- Scrollbars ----------
   Replaces the default OS/browser scrollbar (wherever something on this
   page can scroll) with a slim themed thumb instead of the native one. */
*{scrollbar-width:thin;scrollbar-color:var(--grid-line) transparent;}
*::-webkit-scrollbar{width:8px;height:8px;}
*::-webkit-scrollbar-track{background:transparent;}
*::-webkit-scrollbar-thumb{background:var(--grid-line);border-radius:4px;}
*::-webkit-scrollbar-thumb:hover{background:var(--text-mid);}

html,body{
  margin:0;padding:0;height:100%;
  background:var(--bg-deep);
  font-family:var(--font-head);
  color:var(--text-hi);
  overflow:hidden;              /* the PAGE never scrolls — see #arcade below */
  overscroll-behavior:none;     /* kills iOS rubber-band bounce */
}
/* The shell is exactly one viewport tall and never scrolls itself; whichever
   view is active scrolls INTERNALLY instead (see #arcade-floor /
   #leaderboard-page). This matters most on iOS, where `100vh` is the LARGE
   viewport (it ignores the collapsing browser chrome) — so a `100vh` shell
   is taller than what's actually visible and the whole page drifts up and
   down as you scroll. `100dvh` tracks the real visible height; the plain
   `100vh` line above it is the fallback for anything that doesn't know dvh.
   Keeping the page itself unscrollable is also what lets the game iframe
   below size itself exactly to the space it's given. */
#arcade{
  position:relative;
  height:100vh;
  height:100dvh;
  display:flex;flex-direction:column;
  overflow:hidden;
  isolation:isolate;   /* keeps the ::before backdrop below the content
                          without needing z-index on everything else */
}
/* ---------- Animated arcade backdrop ----------
   Two slow, GPU-cheap layers behind the whole shell: a pair of drifting
   colour washes, and a grid that pans diagonally. Both are pure CSS
   (transform/opacity only — no layout, no JS, no canvas), so they cost
   essentially nothing and keep running across view switches.
   position:fixed + inset:-10% so the drifting layer never exposes an edge
   as it moves. */
#arcade::before,
#arcade::after{
  content:'';position:fixed;inset:-10%;z-index:-1;pointer-events:none;
}
#arcade::before{
  background:
    radial-gradient(38% 30% at 18% 12%, rgba(255,122,69,.16), transparent 70%),
    radial-gradient(34% 28% at 84% 26%, rgba(79,210,232,.14), transparent 70%),
    radial-gradient(40% 34% at 50% 92%, rgba(47,230,164,.12), transparent 70%);
  animation:arcadeWash 26s ease-in-out infinite alternate;
}
#arcade::after{
  background-image:
    linear-gradient(rgba(255,255,255,.028) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255,255,255,.028) 1px, transparent 1px);
  background-size:44px 44px;
  animation:arcadePan 40s linear infinite;
}
@keyframes arcadeWash{
  0%  { transform:translate3d(0,0,0) scale(1); }
  50% { transform:translate3d(2.5%,-2%,0) scale(1.06); }
  100%{ transform:translate3d(-2%,2.5%,0) scale(1.03); }
}
@keyframes arcadePan{
  from{ background-position:0 0, 0 0; }
  to  { background-position:44px 44px, 44px 44px; }
}
/* Anyone who has asked their OS for less motion gets the palette without
   the movement — the backdrop is decoration, never information. */
@media (prefers-reduced-motion: reduce){
  #arcade::before,#arcade::after{animation:none;}
}

/* ---------- Nav ----------
   flex:none so the nav keeps its natural height and every remaining pixel
   of the (unscrollable) shell goes to the active view below it.
   Left to right: the back control (game / shop / standalone pages only),
   the ABigFish Arcade brand (a link home on standalone pages), a "where am
   I" crumb (game name, SHOP, LEADERBOARDS — hidden on the grid), then the
   shard chip and the Leaderboard + Shop buttons (grid only; the Leaderboard
   button also shows, marked current, on leaderboard.php itself).
   Under 640px the buttons drop their labels and keep their icons, and in
   the game/shop views the wordmark hides so the crumb has the room. */
#arcade-nav{
  display:flex;align-items:center;gap:10px;
  flex:none;
  padding:10px clamp(12px, 2vw, 22px);
  border-bottom:1px solid var(--grid-line);
  background:
    linear-gradient(180deg, rgba(255,255,255,.035), rgba(255,255,255,0)),
    rgba(14,15,26,.86);
  position:relative;z-index:2;
}
.nav-back{
  font-family:var(--font-mono);font-size:11px;font-weight:600;letter-spacing:.1em;
  background:rgba(255,255,255,.03);color:var(--text-mid);border:1px solid var(--grid-line);
  border-radius:999px;padding:7px 12px 7px 10px;cursor:pointer;text-decoration:none;
  display:none;align-items:center;gap:6px;white-space:nowrap;
  transition:border-color .2s, color .2s, background .2s;
}
.nav-back.visible{display:inline-flex;}
.nav-back:active{transform:translateY(1px);}
.nav-brand{
  display:inline-flex;align-items:center;gap:9px;min-width:0;
  color:inherit;text-decoration:none;
}
.nav-logo{
  flex:none;width:34px;height:34px;border-radius:10px;
  display:grid;place-items:center;color:#FFC857;
  background:
    radial-gradient(circle at 50% 35%, rgba(255,200,87,.26), transparent 72%),
    rgba(255,255,255,.03);
  border:1px solid rgba(255,200,87,.34);
  box-shadow:0 0 0 4px rgba(255,200,87,.06), 0 10px 24px -14px #FFC857;
}
.nav-logo svg{width:20px;height:20px;display:block;filter:drop-shadow(0 0 6px rgba(255,200,87,.55));}
.nav-brand-text{display:flex;flex-direction:column;line-height:1.05;white-space:nowrap;}
.nav-brand-text b{font-family:var(--font-mono);font-size:9px;font-weight:600;letter-spacing:.3em;color:#FFC857;}
.nav-brand-text span{font-size:17px;font-weight:700;letter-spacing:.09em;color:var(--text-hi);transition:color .2s;}
.nav-crumb{
  display:none;margin:0;
  font-family:var(--font-mono);font-size:11px;font-weight:600;letter-spacing:.14em;
  color:var(--text-mid);white-space:nowrap;text-transform:uppercase;
  padding:5px 11px;border:1px solid var(--grid-line);border-radius:999px;
  background:rgba(255,255,255,.03);
}
.nav-crumb.visible{display:inline-flex;align-items:center;gap:7px;}
.nav-crumb::before{content:'';width:5px;height:5px;background:#FFC857;transform:rotate(45deg);}
.nav-spacer{flex:1;}
.nav-shards{
  display:inline-flex;align-items:center;gap:6px;
  padding:6px 12px 6px 10px;border-radius:999px;
  border:1px solid rgba(255,200,87,.3);background:rgba(255,200,87,.07);
  font-family:var(--font-mono);font-size:13px;font-weight:600;color:#FFC857;
  position:relative;user-select:none;-webkit-user-select:none;white-space:nowrap;
}
.nav-shard-icon{font-size:11px;}
.nav-shard-label{font-size:9px;letter-spacing:.14em;font-weight:500;color:rgba(255,200,87,.7);}
/* Dev easter egg feedback (see arcade.js): the chip pops, a "+N" drifts up
   on its free left side (the right side is the Leaderboard button), and six
   diamonds burst off the icon. Transform/opacity only. */
.nav-shards.bonus{animation:shardPop .6s cubic-bezier(.2,1.4,.4,1);}
@keyframes shardPop{
  0%{transform:scale(1);}
  30%{transform:scale(1.35);color:#FFF1C2;text-shadow:0 0 14px rgba(255,200,87,.9);}
  100%{transform:scale(1);}
}
.shard-float{
  position:absolute;right:100%;top:50%;margin-right:10px;
  font-family:var(--font-mono);font-size:14px;font-weight:700;color:#FFC857;
  text-shadow:0 0 10px rgba(255,200,87,.8);white-space:nowrap;pointer-events:none;
  animation:shardFloat 1.2s ease-out forwards;
}
/* On cooldown: same slot, but a dim "wait m:ss" instead of gold. */
.shard-float.cool{color:var(--text-mid);text-shadow:none;font-weight:600;font-size:12px;}
@keyframes shardFloat{
  0%{opacity:0;transform:translateY(-50%) translateX(6px) scale(.8);}
  15%{opacity:1;transform:translateY(-50%) translateX(0) scale(1.1);}
  70%{opacity:1;transform:translateY(calc(-50% - 6px)) scale(1);}
  100%{opacity:0;transform:translateY(calc(-50% - 12px)) scale(1);}
}
.shard-spark{
  position:absolute;left:16px;top:50%;width:6px;height:6px;margin:-3px 0 0 -3px;
  background:#FFC857;pointer-events:none;
  animation:shardSpark .7s ease-out forwards;
}
@keyframes shardSpark{
  0%{opacity:1;transform:translate(0,0) rotate(45deg) scale(1);}
  100%{opacity:0;transform:translate(var(--dx),var(--dy)) rotate(45deg) scale(.3);}
}
@media (prefers-reduced-motion: reduce){
  .nav-shards.bonus{animation:none;}
  .shard-spark{display:none;}
  .shard-float{animation:shardFloatStill 1.2s ease-out forwards;}
  @keyframes shardFloatStill{0%,70%{opacity:1;transform:translateY(-50%);}100%{opacity:0;transform:translateY(-50%);}}
}
/* The two nav buttons share one pill shape; Shop is the filled gold one. */
.nav-btn{
  display:none;align-items:center;gap:7px;
  font-family:var(--font-head);font-weight:600;font-size:13px;letter-spacing:.02em;
  border-radius:999px;padding:7px 14px;cursor:pointer;text-decoration:none;
  border:1px solid var(--grid-line);background:rgba(255,255,255,.03);color:var(--text-mid);
  white-space:nowrap;
  transition:border-color .2s, color .2s, background .2s, transform .2s, box-shadow .2s;
}
.nav-btn.visible{display:inline-flex;}
.nav-btn svg{width:15px;height:15px;flex:none;}
.nav-btn:active{transform:translateY(1px);}
.nav-btn.nav-shop-btn{
  background:linear-gradient(180deg, #FFD97A, #FFC857);color:#2A1206;border-color:transparent;
  box-shadow:0 8px 18px -10px #FFC857;
}
.nav-btn.current{border-color:rgba(255,200,87,.5);color:var(--text-hi);cursor:default;}
@media (hover:hover) and (pointer:fine){
  .nav-back:hover,.nav-btn:not(.current):hover{border-color:var(--text-mid);color:var(--text-hi);background:rgba(255,255,255,.06);}
  .nav-btn.nav-shop-btn:hover{
    color:#2A1206;background:linear-gradient(180deg, #FFE39A, #FFD06A);border-color:transparent;
    box-shadow:0 10px 22px -10px #FFC857;transform:translateY(-1px);
  }
  a.nav-brand:hover .nav-brand-text span{color:#FFC857;}
}

/* ---------- Views ----------
   min-height:0 is required for a flex child to be allowed to shrink below
   its content size — without it `flex:1` + `overflow-y:auto` still grows
   the shell and the page scrolls anyway. */
#arcade-floor{flex:1;position:relative;min-height:0;overflow-y:auto;overflow-x:hidden;}
.view{display:none;}
.view.active{display:block;}

/* ---------- Cabinet grid ----------
   Cabinets grow with the viewport instead of sitting in a fixed 960px
   column: the tile floor is centered, capped generously, and each tile
   sizes between a comfortable minimum and whatever the row allows. The
   min(240px,100%) guard keeps a single tile from overflowing a phone
   narrower than the 240px minimum. */
#cabinet-grid.active{
  display:grid;
  grid-template-columns:repeat(auto-fit, minmax(min(250px, 100%), 1fr));
  gap:clamp(14px, 2.2vw, 24px);
  padding:clamp(14px, 2.6vw, 30px) clamp(16px, 3vw, 32px) clamp(28px, 4vw, 48px);
  width:100%;
  max-width:1180px;
  margin:0 auto;
  align-content:start;
}
/* Click-reactive backdrop canvas (drawn by arcade.js while the grid view is
   showing). Fixed to the viewport, under everything in #arcade's stacking
   context — between the CSS colour wash and the panning grid. */
#arcade-bg{
  position:fixed;inset:0;width:100%;height:100%;
  z-index:-1;pointer-events:none;
}
/* ---------- Hero strip above the cabinets ---------- */
.floor-hero{
  grid-column:1 / -1;
  display:flex;flex-direction:column;align-items:center;text-align:center;gap:6px;
  padding:clamp(6px, 1.4vw, 16px) 0 clamp(2px, .8vw, 8px);
}
.hero-eyebrow{
  font-family:var(--font-mono);font-size:10px;font-weight:600;letter-spacing:.32em;
  color:#FFC857;
  animation:heroBlink 2.2s steps(1, end) infinite;
}
@keyframes heroBlink{0%,60%{opacity:1;}61%,100%{opacity:.25;}}
.hero-title{
  margin:0;
  font-size:clamp(26px, 3.6vw, 42px);font-weight:700;letter-spacing:-.015em;line-height:1.1;
  background:linear-gradient(100deg, var(--text-hi) 0%, var(--text-hi) 35%, #FFC857 50%, var(--text-hi) 65%, var(--text-hi) 100%);
  background-size:220% 100%;
  -webkit-background-clip:text;background-clip:text;color:transparent;
  animation:heroSheen 7s linear infinite;
}
@keyframes heroSheen{from{background-position:120% 0;}to{background-position:-120% 0;}}
@media (max-height:520px){ .floor-hero{gap:2px;} }
@media (prefers-reduced-motion: reduce){
  .hero-eyebrow,.hero-title{animation:none;}
}

/* ---------- Cabinets ---------- */
.cabinet{
  --accent:#FF7A45;
  position:relative;
  display:flex;flex-direction:column;align-items:center;gap:8px;
  text-align:center;
  background:
    linear-gradient(180deg, rgba(255,255,255,.04), rgba(255,255,255,0) 42%),
    var(--bg-panel);
  border:1px solid var(--grid-line);
  border-radius:14px;
  padding:clamp(22px, 2.6vw, 34px) clamp(14px, 1.6vw, 22px) clamp(12px, 1.4vw, 16px);
  font-family:var(--font-head);
  cursor:pointer;
  color:inherit;
  overflow:hidden;
  isolation:isolate;
}
/* Cabinets deal themselves onto the floor, left to right, on every visit to
   the grid view. The animation restarts whenever #cabinet-grid flips back to
   display:grid, so it replays on each return from a game/the shop.
   nth-of-type, not nth-child: the hero strip is the grid's first child. */
@keyframes cabinetIn{
  from{ opacity:0; transform:translateY(16px) scale(.96); }
  to  { opacity:1; transform:none; }
}
#cabinet-grid.active .cabinet{ animation:cabinetIn .44s cubic-bezier(.22,1,.36,1) both; }
#cabinet-grid.active button.cabinet:nth-of-type(1){animation-delay:.04s}
#cabinet-grid.active button.cabinet:nth-of-type(2){animation-delay:.10s}
#cabinet-grid.active button.cabinet:nth-of-type(3){animation-delay:.16s}
#cabinet-grid.active button.cabinet:nth-of-type(4){animation-delay:.22s}
#cabinet-grid.active button.cabinet:nth-of-type(n+5){animation-delay:.26s}

/* accent glow blooming from the top edge, always faintly on, brighter on hover */
.cabinet::before{
  content:'';position:absolute;inset:-1px;pointer-events:none;z-index:-1;
  background:radial-gradient(70% 48% at 50% 0%, var(--accent), transparent 70%);
  opacity:.10;transition:opacity .25s ease;
}
/* the accent rail: a soft gradient bar instead of a hard border-top */
.cabinet::after{
  content:'';position:absolute;left:8%;right:8%;top:0;height:3px;pointer-events:none;
  background:linear-gradient(90deg, transparent, var(--accent) 30%, var(--accent) 70%, transparent);
  border-radius:0 0 3px 3px;
  opacity:.95;
}
button.cabinet{
  transition:border-color .2s, transform .22s cubic-bezier(.34,1.56,.64,1), background .2s, box-shadow .25s;
}
button.cabinet:active{transform:translateY(0) scale(.985);}
@media (hover:hover) and (pointer:fine){
  button.cabinet:hover{
    border-color:color-mix(in srgb, var(--accent) 55%, var(--grid-line));
    transform:translateY(-5px);
    box-shadow:0 22px 44px -22px var(--accent), 0 0 0 1px color-mix(in srgb, var(--accent) 18%, transparent);
  }
  button.cabinet:hover::before{opacity:.22;}
  button.cabinet:hover .cabinet-icon{transform:translateY(-3px) scale(1.06);}
  button.cabinet:hover .cabinet-icon svg{transform:scale(1.08);}
  button.cabinet:hover .cabinet-play{background:var(--accent);color:#0E0F1A;border-color:var(--accent);}
}
@media (prefers-reduced-motion: reduce){
  #cabinet-grid.active .cabinet{animation:none;}
}
.cabinet-soon{opacity:.45;cursor:default;}
/* lift the spans above the ::before glow layer */
.cabinet > *{position:relative;}
/* The icon: an inline SVG (assets/icons/<id>.svg) in the cabinet's accent,
   sitting in a glowing disc. Text-glyph fallbacks size themselves the same. */
.cabinet-icon{
  width:clamp(64px, 6.2vw, 86px);height:clamp(64px, 6.2vw, 86px);
  display:grid;place-items:center;
  border-radius:50%;
  color:var(--accent);
  font-size:clamp(28px, 3vw, 40px);line-height:1;
  background:
    radial-gradient(circle at 50% 38%, color-mix(in srgb, var(--accent) 24%, transparent), transparent 72%),
    rgba(255,255,255,.03);
  border:1px solid color-mix(in srgb, var(--accent) 38%, transparent);
  box-shadow:
    0 0 0 6px color-mix(in srgb, var(--accent) 7%, transparent),
    0 12px 30px -14px var(--accent);
  transition:transform .28s cubic-bezier(.34,1.56,.64,1);
}
.cabinet-icon svg{
  width:56%;height:56%;display:block;
  filter:drop-shadow(0 0 7px color-mix(in srgb, var(--accent) 65%, transparent));
  transition:transform .28s cubic-bezier(.34,1.56,.64,1);
}
.cabinet-name{font-size:clamp(16px, 1.5vw, 21px);font-weight:700;letter-spacing:.06em;color:var(--text-hi);margin-top:2px;}
.cabinet-foot{
  display:flex;align-items:center;justify-content:space-between;gap:8px;
  width:100%;margin-top:12px;padding-top:10px;
  border-top:1px solid rgba(255,255,255,.06);
}
.cabinet-best{
  font-family:var(--font-mono);font-size:10px;letter-spacing:.12em;color:var(--text-dim);
  white-space:nowrap;
}
.cabinet-best b{color:var(--text-hi);font-weight:600;}
.cabinet-play{
  font-family:var(--font-mono);font-size:10px;font-weight:700;letter-spacing:.16em;
  color:var(--accent);
  border:1px solid color-mix(in srgb, var(--accent) 45%, transparent);
  border-radius:999px;padding:5px 10px 5px 12px;
  transition:background .2s, color .2s, border-color .2s;
}

/* ---------- Shop ---------- */
#shop-view.active{display:flex;justify-content:center;padding:clamp(16px, 3vw, 32px);}
.shop-wrap{width:100%;max-width:880px;}
.shop-head{
  display:flex;align-items:baseline;justify-content:space-between;gap:12px;
  margin-bottom:4px;
}
.shop-head h2{margin:0;font-size:20px;font-weight:700;}
.shop-shards-total{font-family:var(--font-mono);font-size:13px;color:#FFC857;font-weight:600;}
.shop-note{font-size:11px;color:var(--text-mid);margin:0 0 16px;line-height:1.5;}
.shop-category-title{
  font-family:var(--font-head);font-size:13px;font-weight:700;letter-spacing:.04em;
  color:var(--text-hi);margin:24px 0 2px;padding-top:14px;border-top:1px solid var(--grid-line);
}
.shop-category-title:first-child{margin-top:0;padding-top:0;border-top:none;}
.shop-section-title{
  font-family:var(--font-mono);font-size:10px;letter-spacing:.1em;color:var(--text-dim);
  margin:18px 0 8px;
}
.shop-section-title:first-child{margin-top:0;}
/* ---------- Shop tabs ----------
   Two levels: a game tab strip, then Upgrades/Cosmetics for that game. Only
   the selected slice is ever in the DOM (see assets/arcade-shop.js), which
   is what replaced a 60+ row single scroll. */
.shop-tabs{
  display:flex;gap:6px;flex-wrap:wrap;
  margin-bottom:2px;
}
.shop-tab{
  flex:1 1 0;min-width:96px;
  font-family:var(--font-head);font-size:13px;font-weight:700;letter-spacing:.03em;
  background:var(--bg-panel);color:var(--text-mid);
  border:1px solid var(--grid-line);border-bottom-width:3px;
  border-radius:6px 6px 0 0;
  padding:11px 12px;cursor:pointer;white-space:nowrap;
  transition:color .15s, background .15s, border-color .15s;
}
.shop-tab.active{
  color:var(--text-hi);background:#1D2038;border-bottom-color:#FFC857;
}
@media (hover:hover) and (pointer:fine){
  .shop-tab:not(.active):hover{color:var(--text-hi);background:#181B2D;}
}
.shop-subtabs{
  display:flex;gap:6px;
  margin:0 0 8px;
}
.shop-subtab{
  flex:1 1 0;
  font-family:var(--font-mono);font-size:11px;font-weight:600;letter-spacing:.08em;
  text-transform:uppercase;
  background:transparent;color:var(--text-dim);
  border:1px solid var(--grid-line);border-radius:4px;
  padding:8px 10px;cursor:pointer;
  transition:color .15s, background .15s, border-color .15s;
}
.shop-subtab.active{
  color:#04221A;background:#2FE6A4;border-color:#2FE6A4;
}
@media (hover:hover) and (pointer:fine){
  .shop-subtab:not(.active):hover{color:var(--text-hi);border-color:var(--text-mid);}
}
.shop-panel{display:flex;flex-direction:column;gap:6px;}
/* each panel fades in, so switching tabs reads as a change rather than a
   silent content swap */
@keyframes shopPanelIn{ from{opacity:0;transform:translateY(6px);} to{opacity:1;transform:none;} }
.shop-panel{animation:shopPanelIn .22s cubic-bezier(.22,1,.36,1) both;}
@media (prefers-reduced-motion: reduce){ .shop-panel{animation:none;} }
/* Where there's width for it, go two-up — a game's cosmetics tab can be a
   dozen rows, and on a desktop that's pointless vertical scrolling. Slot
   titles span both columns so each group still reads as one block. Phones
   stay single-column. */
@media (min-width:760px){
  .shop-panel{display:grid;grid-template-columns:1fr 1fr;align-items:start;}
  .shop-panel .shop-slot-title{grid-column:1 / -1;}
}
.shop-empty{font-size:12px;color:var(--text-dim);margin:10px 2px;}

/* One visual channel of a game's cosmetics (Background / Ball / Dial / ...).
   See the COSMETIC SLOTS note in assets/arcade-shop-catalog.js. */
.shop-slot-title{
  font-family:var(--font-head);font-size:11px;font-weight:700;letter-spacing:.06em;
  text-transform:uppercase;color:var(--text-mid);
  margin:14px 0 4px;padding-left:2px;
  border-left:3px solid #2FE6A4;padding-left:8px;
}
/* The slot strip is a THIRD tab row, shown only inside Cosmetics. Slightly
   quieter than the category row above it so the hierarchy stays readable:
   game > category > slot. */
.shop-slottabs{margin-top:-2px;}
.shop-slottabs .shop-subtab{font-size:10px;padding:6px 8px;}
.shop-slottabs .shop-subtab.active{background:#6FA8FF;border-color:#6FA8FF;color:#061428;}

/* Cosmetic tiers (see the COSMETIC TIERS note in assets/arcade-shop-catalog
   .js). Each slot lists Common / Rare / Prestige / Ultra groups, each under a
   .shop-tier-title heading and each row badged (.upg-tier). The base colour
   is prestige's warm gold; common is a quiet grey, rare a cool cyan, ultra
   violet-to-cyan, so the price ladder reads as deliberate tiers rather than
   as arbitrarily expensive rows. */
.shop-tier-title{
  border-left-color:#FFC857;color:#FFC857;
  margin-top:18px;
}
.shop-tier-title.shop-tier-common{border-left-color:#8A93B0;color:#9AA3C0;}
.shop-tier-title.shop-tier-rare{border-left-color:#4FD2E8;color:#4FD2E8;}
.upg-row.is-common .upg-tier{color:#9AA3C0;border-color:rgba(154,163,192,.45);}
.upg-row.is-rare{
  border-color:rgba(79,210,232,.35);
  background:linear-gradient(180deg, rgba(79,210,232,.06), rgba(79,210,232,.015)), var(--bg-panel);
}
@media (hover:hover) and (pointer:fine){
  .upg-row.is-rare:hover{border-color:rgba(79,210,232,.7);background:linear-gradient(180deg, rgba(79,210,232,.11), rgba(79,210,232,.03)), #1B1F33;}
}
.upg-row.is-rare .upg-tier{color:#4FD2E8;border-color:rgba(79,210,232,.5);}
.upg-row.is-rare .upg-swatch{border-color:rgba(79,210,232,.4);}
.upg-row.is-prestige{
  border-color:rgba(255,200,87,.42);
  background:linear-gradient(180deg, rgba(255,200,87,.07), rgba(255,200,87,.02)), var(--bg-panel);
}
@media (hover:hover) and (pointer:fine){
  .upg-row.is-prestige:hover{border-color:rgba(255,200,87,.75);background:linear-gradient(180deg, rgba(255,200,87,.13), rgba(255,200,87,.04)), #1B1F33;}
}
.upg-tier{
  display:inline-block;vertical-align:1px;margin-left:7px;
  font-family:var(--font-mono);font-size:8px;font-weight:700;letter-spacing:.10em;
  text-transform:uppercase;
  color:#FFC857;border:1px solid rgba(255,200,87,.5);border-radius:3px;
  padding:1px 5px;
}

#shop-list{display:flex;flex-direction:column;gap:6px;}
.upg-row{
  display:flex;align-items:center;justify-content:space-between;gap:10px;
  background:var(--bg-panel);border:1px solid var(--grid-line);border-radius:5px;
  padding:10px 12px;
  transition:border-color .16s ease, background .16s ease, transform .16s ease;
}
@media (hover:hover) and (pointer:fine){
  .upg-row:hover{border-color:#3A4066;background:#1B1F33;}
}
.upg-row-default{background:rgba(255,255,255,.02);}
/* Live preview canvas on every cosmetic row (see arcade-shop.js): a
   miniature of the real background theme, or the entity painted with its
   real ArcadeFX spec. Fixed size so rows stay aligned; the panel's two-up
   layout leaves plenty of room for it. */
.upg-swatch{
  flex:none;width:46px;height:46px;border-radius:9px;
  background:#0B0C15;border:1px solid var(--grid-line);
  display:block;
}
.upg-row.is-prestige .upg-swatch{border-color:rgba(255,200,87,.45);}
/* The ULTRA tier (2-3x prestige): a cool violet-to-cyan treatment so it reads
   as a step above the gold prestige rows, not more of them. */
.shop-tier-title.shop-tier-ultra{border-left-color:#B8A0FF;color:#B8A0FF;}
.upg-row.is-ultra{
  border-color:rgba(184,160,255,.5);
  background:linear-gradient(180deg, rgba(124,92,255,.12), rgba(0,229,255,.04)), var(--bg-panel);
}
@media (hover:hover) and (pointer:fine){
  .upg-row.is-ultra:hover{border-color:rgba(184,160,255,.85);background:linear-gradient(180deg, rgba(124,92,255,.18), rgba(0,229,255,.07)), #1B1F33;}
}
.upg-row.is-ultra .upg-tier{
  color:#0B0620;border-color:transparent;
  background:linear-gradient(90deg, #B8A0FF, #8AF0FF);
}
.upg-row.is-ultra .upg-swatch{border-color:rgba(184,160,255,.6);box-shadow:0 0 14px -4px rgba(138,240,255,.6);}
.upg-info{display:flex;flex-direction:column;gap:2px;flex:1;min-width:0;}
.upg-name{font-size:13px;font-weight:600;color:var(--text-hi);}
.upg-desc{font-size:11px;color:var(--text-mid);}
.upg-lvl{font-family:var(--font-mono);font-size:9px;color:#2FE6A4;}
.upg-buy{
  font-family:var(--font-mono);font-weight:600;font-size:12px;
  background:#FFC857;color:#2A1206;border:none;border-radius:3px;
  padding:9px 14px;cursor:pointer;white-space:nowrap;
}
.upg-buy:disabled{background:var(--grid-line);color:var(--text-dim);cursor:not-allowed;}
.upg-buy:not(:disabled):active{transform:translateY(1px);}
@media (hover:hover) and (pointer:fine){
  .upg-buy:not(:disabled):hover{filter:brightness(1.08);}
}
/* EQUIP / EQUIPPED on an owned slotted cosmetic — deliberately a different
   colour from the gold buy button, so "spend shards" and "switch look"
   never read as the same action. */
.upg-equip{
  background:transparent;color:#2FE6A4;border:1px solid #2FE6A4;
  min-width:88px;text-align:center;
}
.upg-equip.is-equipped,
.upg-equip:disabled{
  background:#2FE6A4;color:#04221A;border-color:#2FE6A4;
  opacity:1;cursor:default;
}
@media (hover:hover) and (pointer:fine){
  .upg-equip:not(:disabled):hover{background:rgba(47,230,164,.14);filter:none;}
}

/* ---------- Game view (iframe) ---------- */
#game-view.active{
  position:absolute;inset:0;top:0;
  display:flex;
}
#game-frame{
  flex:1;width:100%;height:100%;
  border:none;display:block;
  /* Transparent, NOT black: before a game's first paint the iframe shows
     whatever is behind it. It used to be #000, which is exactly the "black
     for a second or two" every cabinet open showed. Now the arcade's own
     ground and the loading label below show through instead. */
  background:transparent;
}
/* Loading label while a game document is still arriving — toggled by
   arcade.js on the iframe's load event (with a timeout backstop). Pure
   decoration on top of the arcade ground; fades itself out. */
#game-view.loading::after{
  content:attr(data-loading);
  position:absolute;inset:0;
  display:flex;align-items:center;justify-content:center;
  font-family:var(--font-mono);font-size:12px;font-weight:600;letter-spacing:.16em;
  color:var(--text-mid);background:var(--bg-deep);
  pointer-events:none;
  animation:loadPulse 1.1s ease-in-out infinite;
}
@keyframes loadPulse{0%,100%{opacity:.55;}50%{opacity:1;}}
@media (prefers-reduced-motion: reduce){
  #game-view.loading::after{animation:none;}
}

/* ---------- Leaderboards page ----------
   This <main> is a direct flex child of #arcade (it is NOT inside
   #arcade-floor, which only exists on index.php), so it needs its own
   flex:1 + min-height:0 + overflow-y:auto to scroll internally instead of
   stretching the unscrollable shell. Inside, it reuses the menu's hero
   strip (.floor-hero) and gives each game a card cut from the same cloth as
   its cabinet: accent rail, glow, icon disc. */
#leaderboard-page{
  flex:1;min-height:0;overflow-y:auto;width:100%;
  padding:clamp(14px, 2.6vw, 30px) clamp(16px, 3vw, 32px) clamp(28px, 4vw, 48px);
}
.lb-wrap{
  display:grid;
  /* 340px minimum + 980px cap = two columns on desktop (a 2x2 for four
     games), one column on phones — never a three-up with an orphan. */
  grid-template-columns:repeat(auto-fit, minmax(min(340px, 100%), 1fr));
  gap:clamp(14px, 2.2vw, 24px);
  width:100%;max-width:980px;margin:0 auto;align-content:start;
}
.lb-game{
  --accent:#FFC857;
  position:relative;isolation:isolate;overflow:hidden;
  background:
    linear-gradient(180deg, rgba(255,255,255,.04), rgba(255,255,255,0) 42%),
    var(--bg-panel);
  border:1px solid var(--grid-line);border-radius:14px;
  padding:18px 16px 12px;
  animation:cabinetIn .44s cubic-bezier(.22,1,.36,1) both;
}
.lb-game:nth-of-type(1){animation-delay:.04s}
.lb-game:nth-of-type(2){animation-delay:.10s}
.lb-game:nth-of-type(3){animation-delay:.16s}
.lb-game:nth-of-type(n+4){animation-delay:.22s}
.lb-game::before{
  content:'';position:absolute;inset:-1px;pointer-events:none;z-index:-1;
  background:radial-gradient(70% 40% at 50% 0%, var(--accent), transparent 70%);
  opacity:.10;
}
.lb-game::after{
  content:'';position:absolute;left:8%;right:8%;top:0;height:3px;pointer-events:none;
  background:linear-gradient(90deg, transparent, var(--accent) 30%, var(--accent) 70%, transparent);
  border-radius:0 0 3px 3px;opacity:.95;
}
.lb-game > *{position:relative;}
.lb-head{display:flex;align-items:center;gap:12px;margin-bottom:12px;}
.lb-icon{
  flex:none;width:46px;height:46px;border-radius:50%;
  display:grid;place-items:center;color:var(--accent);font-size:22px;line-height:1;
  background:
    radial-gradient(circle at 50% 38%, color-mix(in srgb, var(--accent) 24%, transparent), transparent 72%),
    rgba(255,255,255,.03);
  border:1px solid color-mix(in srgb, var(--accent) 38%, transparent);
  box-shadow:0 0 0 4px color-mix(in srgb, var(--accent) 7%, transparent);
}
.lb-icon svg{width:56%;height:56%;display:block;filter:drop-shadow(0 0 6px color-mix(in srgb, var(--accent) 65%, transparent));}
.lb-head-text{display:flex;flex-direction:column;gap:3px;min-width:0;}
.lb-game h2{margin:0;font-size:17px;font-weight:700;letter-spacing:.06em;color:var(--text-hi);}
.lb-head-sub{font-family:var(--font-mono);font-size:10px;letter-spacing:.12em;color:var(--text-dim);white-space:nowrap;}
/* Competitive | Hardcore: a two-half segmented toggle, the active half
   filled in the game's accent so the current board is unmistakable. */
.lb-tabs{
  display:grid;grid-template-columns:1fr 1fr;gap:2px;
  padding:3px;border-radius:999px;margin-bottom:10px;
  background:rgba(0,0,0,.28);border:1px solid var(--grid-line);
}
.lb-tab{
  font-family:var(--font-mono);font-size:11px;font-weight:600;letter-spacing:.1em;
  background:transparent;color:var(--text-mid);border:0;border-radius:999px;
  padding:7px 10px;cursor:pointer;
  transition:background .2s, color .2s;
}
.lb-tab.active{background:var(--accent);color:#0E0F1A;cursor:default;}
.lb-tab:active{transform:translateY(1px);}
@media (hover:hover) and (pointer:fine){
  .lb-tab:not(.active):hover{color:var(--text-hi);background:rgba(255,255,255,.05);}
}
.lb-tab-panel{display:none;}
.lb-tab-panel.active{display:block;}
.lb-empty{
  margin:0 0 6px;padding:18px 12px;text-align:center;
  font-family:var(--font-mono);font-size:11px;letter-spacing:.08em;color:var(--text-dim);
  border:1px dashed var(--grid-line);border-radius:10px;
}
.lb-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:2px;}
.lb-entry{
  display:grid;grid-template-columns:26px minmax(0,1fr) auto;align-items:center;gap:10px;
  padding:6px 8px;border-radius:8px;
  font-family:var(--font-mono);font-size:13px;
}
.lb-entry:nth-child(odd){background:rgba(255,255,255,.025);}
.lb-rank{
  width:24px;height:24px;border-radius:50%;
  display:grid;place-items:center;
  font-size:10px;font-weight:700;color:var(--text-dim);
  border:1px solid var(--grid-line);
}
/* gold / silver / bronze medals for the podium */
.lb-entry.lb-top3 .lb-rank{color:#0E0F1A;border-color:transparent;}
.lb-entry:nth-child(1) .lb-rank{background:#FFC857;box-shadow:0 0 10px rgba(255,200,87,.45);}
.lb-entry:nth-child(2) .lb-rank{background:#C9D1E4;}
.lb-entry:nth-child(3) .lb-rank{background:#D99A6C;}
.lb-name{min-width:0;color:var(--text-mid);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.lb-entry.lb-top3 .lb-name{color:var(--text-hi);}
.lb-entry:first-child .lb-name{font-weight:600;}
.lb-score{font-weight:600;color:var(--text-hi);white-space:nowrap;font-variant-numeric:tabular-nums;}
@media (prefers-reduced-motion: reduce){
  .lb-game{animation:none;}
}

/* ---------- Small screens ----------
   The nav is the only thing between a phone's viewport and the game
   iframe, so it gets tighter here — every pixel saved is a pixel the game
   can scale itself up into (see each game's fitStage()). Buttons keep
   their icons and drop their labels; the brand keeps its mark and shrinks
   its wordmark, and hides the wordmark entirely inside a game or the shop
   so the "where am I" crumb has the room. */
@media (max-width:640px){
  #arcade-nav{gap:7px;padding:7px 10px;}
  .nav-back{padding:6px 9px;}
  .nav-btn-label,.nav-shard-label{display:none;}
  .nav-btn{padding:7px 9px;}
  .nav-logo{width:30px;height:30px;border-radius:9px;}
  .nav-logo svg{width:17px;height:17px;}
  .nav-brand-text b{font-size:8px;letter-spacing:.24em;}
  .nav-brand-text span{font-size:14px;}
  .nav-crumb{font-size:10px;padding:4px 9px;}
  .nav-shards{font-size:12px;padding:5px 10px 5px 9px;}
  #arcade[data-view="game"] .nav-brand-text,
  #arcade[data-view="shop"] .nav-brand-text,
  #arcade-nav.has-crumb .nav-brand-text{display:none;}
}
/* Very short/landscape phones: trim the nav further still. */
@media (max-height:480px){
  #arcade-nav{padding:4px 10px;}
  .nav-logo{width:28px;height:28px;}
  .nav-logo svg{width:16px;height:16px;}
  .nav-brand-text b{display:none;}
  .nav-brand-text span{font-size:14px;}
}
