:root{
  color-scheme: dark;
  /* One star, drawn once. `.gold-stars` repeats it as a mask and fills it with
     the gold gradient; the marquee separator uses it as a mask too. Keeping it
     here means the shape is defined in exactly one place. */
  --star-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='m12 2.6 2.9 6.2 6.7.9-4.9 4.6 1.2 6.7L12 17.7l-5.9 3.3 1.2-6.7L2.4 9.7l6.7-.9L12 2.6z'/%3E%3C/svg%3E");
  --bg:#000000;
  --bg-2:#0a0a0a;
  --bg-3:#121110;
  --bg-4:#1c1a17;
  --border:#2a2620;
  --border-soft:#1a1814;
  --text:#f5f3ee;
  --text-muted:#a8a49a;
  --text-subtle:#635e54;
  --accent:#d4af37;
  --accent-2:#d4af37;
  --accent-3:#d4af37;
  --gold:#d4af37;
  --success:#e8e3d6;
  --danger:#e8e3d6;
  --grad-1: linear-gradient(135deg,#e8c86a 0%,#a97e1f 100%);
  --grad-2: linear-gradient(135deg,#d4af37 0%,#8c6d1f 100%);
  --grad-gold: linear-gradient(135deg,#f0d585 0%,#b8892a 100%);
  --r:10px;
  --r-lg:16px;
  --r-xl:22px;
  --r-full:999px;
  --shadow: 0 10px 30px rgb(0 0 0 / 60%);
  --transition: .18s cubic-bezier(.4,0,.2,1);
}
/* ===== REVEAL ON SCROLL =====
   Elements start hidden and animate in when they actually enter the viewport
   (ui.js's observeReveals wires the IntersectionObserver and sets --rv to the
   element's index within its group, which drives the stagger).

   The `html.js` guard is load-bearing: the hidden state only applies once JS
   has confirmed it's running. Scripting off → `.js` never gets set and every
   .reveal stays fully visible.

   Scripting on but broken is the case that actually bit us - the inline
   <head> script sets `.js` before it knows whether app.js will survive, and
   app.js once shipped with an unbalanced brace, which left the whole page at
   opacity 0. That inline script now also runs a 3s watchdog that strips `.js`
   again unless initMotion() has set `.js-motion`. So the guarantee holds in
   both directions: a module that never runs cannot leave the page blank. */
.reveal{opacity:1;transform:none;}
html.js .reveal{
  opacity:0;
  transform:translateY(18px);
  transition:opacity .62s cubic-bezier(.2,.7,.2,1),transform .62s cubic-bezier(.2,.7,.2,1);
  transition-delay:calc(var(--rv,0) * 70ms);
  will-change:opacity,transform;
}
html.js .reveal.is-in{opacity:1;transform:none;}
 /* Once it has played, drop the compositor hint - keeping will-change on every
   revealed node permanently is what makes long pages start to feel heavy. */
html.js .reveal.is-done{will-change:auto;transition-delay:0ms;}

.icon{width:1em;height:1em;stroke:currentColor;fill:none;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round;display:inline-block;vertical-align:-2px;}
/* A price is a small inline run: the coin is drawn, then the number. */
.mond-price{display:inline-flex;align-items:center;gap:6px;}
.coin-ico{width:14px;height:14px;flex:0 0 auto;color:var(--gold);}
.check-icon{width:14px;height:14px;flex-shrink:0;margin-top:2px;}
*{box-sizing:border-box;}
html,body{margin:0;min-height:100%;background:var(--bg);color:var(--text);font-family:'Inter',system-ui,sans-serif;-webkit-font-smoothing:antialiased;overflow-x:hidden;}
h1,h2,h3,h4{font-family:'Space Grotesk',sans-serif;letter-spacing:-.02em;margin:0;}
a{color:inherit;text-decoration:none;}
button{font-family:inherit;}
::-webkit-scrollbar{width:8px;height:8px;}
::-webkit-scrollbar-track{background:var(--bg-2);}
::-webkit-scrollbar-thumb{background:var(--bg-4);border-radius:4px;}

 /* ===== NAV - fully floating, no bar background ===== */
.nav-shell{position:sticky;top:0;z-index:50;padding:18px 20px 0;background:transparent;pointer-events:none;}
.nav-inner{display:flex;align-items:center;justify-content:space-between;gap:16px;max-width:1180px;margin:0 auto;pointer-events:none;}
.brand{display:flex;align-items:center;gap:10px;font-weight:800;font-size:16px;flex-shrink:0;cursor:pointer;pointer-events:auto;}
.brand-mark{width:40px;height:40px;border-radius:10px;background:transparent;display:flex;align-items:center;justify-content:center;color:var(--gold);}
.brand-mark .icon{width:26px;height:26px;}
.brand span{color:var(--gold);}
.nav-bar{position:relative;display:flex;align-items:center;gap:2px;margin:0 auto;padding:4px;border:1px solid var(--border);border-radius:999px;background:rgba(10,10,10,.92);flex:1;max-width:520px;pointer-events:auto;box-shadow:0 8px 24px rgb(0 0 0 / 35%);}
.nav-slot{flex:1;text-align:center;padding:8px 10px;border-radius:999px;color:var(--text-muted);font-size:13px;font-weight:600;cursor:pointer;transition:var(--transition);border:none;background:none;white-space:nowrap;}
.nav-slot:hover{color:var(--text);}
.nav-slot.is-active{background:var(--bg-4);color:var(--gold);box-shadow:inset 0 1px 0 rgb(255 255 255 / 6%),inset 0 0 0 1px rgb(212 175 55 / 25%);}
.nav-right{display:flex;align-items:center;gap:8px;flex-shrink:0;pointer-events:auto;}
.icon-btn{width:36px;height:36px;border-radius:999px;border:1px solid var(--border);background:rgba(10,10,10,.92);display:flex;align-items:center;justify-content:center;cursor:pointer;color:var(--text-muted);transition:var(--transition);}
.icon-btn:hover{color:var(--gold);border-color:var(--gold);}
.avatar-btn{width:36px;height:36px;border-radius:999px;background:var(--bg-3);display:flex;align-items:center;justify-content:center;font-weight:700;font-size:12px;cursor:pointer;border:1px solid var(--gold);color:var(--gold);}
.menu-toggle{display:none;}

@media (max-width:760px){
  .nav-bar{display:none;}
  .menu-toggle{display:flex;}
}

/* mobile drawer */
.drawer-backdrop{position:fixed;inset:0;background:rgb(0 0 0 / 58%);z-index:90;opacity:0;pointer-events:none;transition:opacity .2s;}
.drawer-backdrop.open{opacity:1;pointer-events:auto;}
 /* A short dropdown hanging from the hamburger button - top-right anchored,
    sized to its content - not a full-height slide-in panel. Scales up from
   its top-right corner like the notification panel does. */
.drawer{position:fixed;top:70px;right:20px;left:auto;bottom:auto;width:min(240px,calc(100vw - 32px));max-height:min(70svh,calc(100dvh - 86px));overflow-y:auto;overscroll-behavior:contain;scrollbar-width:thin;background:var(--bg-2);border:1px solid var(--border);border-radius:var(--r-lg);box-shadow:var(--shadow);z-index:91;opacity:0;transform:translateY(-6px) scale(.94);transform-origin:top right;pointer-events:none;transition:opacity .18s ease,transform .22s cubic-bezier(.2,.9,.25,1.1);padding:10px;display:flex;flex-direction:column;gap:3px;}
.drawer.open{opacity:1;transform:none;pointer-events:auto;}
/* A row is a 42px target, not a line of text with some padding on it: the
   taller hit area is what makes a menu read as a menu on a phone, and it gives
   the label room to breathe between the rows above and below it. */
.drawer-link{display:flex;align-items:center;min-height:42px;padding:12px 14px;border-radius:var(--r);color:var(--text-muted);font-weight:600;font-size:14px;cursor:pointer;}
.drawer-link.is-active{background:var(--bg-4);color:#fff;}

/* ===== PAGE / VIEWS ===== */
.page{width:min(100%,1180px);margin:0 auto;padding:0 20px 60px;}
.view{display:none;animation:fadeIn .25s ease;}
.view.active{display:block;}
@keyframes fadeIn{from{opacity:0;transform:translateY(6px);}to{opacity:1;transform:translateY(0);}}

/* ===== TOP LIGHT =====
   A dim sun sitting above every page: a warm bloom that falls in from the top
   edge, brightest just above the centre, and is completely gone before it
   reaches the fold. Before this, every view except home opened onto flat
   black - the only light in the design lived in the two hero photographs.

   Home and season are excluded on purpose. Each already opens with a
   full-bleed hero image, so a second light source would flatten the photo and
   fight the vignette sitting on top of it.

   Notes on how it is built:
   - `fixed`, not absolute: the light stays where the sun would be as the page
     scrolls, instead of sliding away with the first section.
   - Three stacked light layers, in paint order: a thin seam where the light
     enters, the sun's crest, its bloom, then the wide gold haze.
   - The bottom mask is load-bearing. Without it the haze ends on a visible
     horizontal edge, which reads as a banding bug rather than light.
   - `isolation` on .view is what lets `z-index:-1` land above the page
     background but behind every card and heading.

   Keep the alphas low. Each tile in the grid is a solid surface, so this only
   ever shows between and behind cards - past ~10% it stops being light and
   starts looking like a stain on a black screen. */
.view{position:relative;isolation:isolate;}
.view:not(#view-home):not(#view-season)::before{
  content:"";
  position:fixed;
  top:0;left:0;right:0;
  height:min(66vh,600px);
  z-index:-1;
  pointer-events:none;
  background:
    linear-gradient(180deg,rgb(240 213 133 / 7%) 0,transparent 150px),
    radial-gradient(ellipse 22% 26% at 50% -10%,rgb(255 240 208 / 26%),transparent 72%),
    radial-gradient(ellipse 46% 66% at 50% 0%,rgb(255 240 208 / 12%),transparent 74%),
    radial-gradient(ellipse 92% 95% at 50% 4%,rgb(212 175 55 / 9%),transparent 80%);
  -webkit-mask-image:linear-gradient(180deg,#000 0%,#000 42%,transparent 100%);
  mask-image:linear-gradient(180deg,#000 0%,#000 42%,transparent 100%);
}

/* ===== HERO ===== */
.hero{position:relative;width:100vw;margin-left:calc(50% - 50vw);margin-top:-64px;min-height:64svh;overflow:hidden;background-image:
  linear-gradient(180deg, rgb(0 0 0 / 55%) 0%, rgb(0 0 0 / 72%) 55%, rgb(0 0 0 / 92%) 100%),
  url('../img/hero.jpg');
  background-size:cover;background-position:center 30%;background-repeat:no-repeat;
  display:flex;align-items:center;justify-content:center;text-align:center;padding:124px 20px 40px;isolation:isolate;}
.hero::before{content:"";position:absolute;inset:0;background-image:radial-gradient(circle,rgb(255 255 255 / 4%) 1px,transparent 1px);background-size:26px 26px;z-index:-1;}
.hero-inner{max-width:640px;}
.hero h1{font-size:clamp(30px,6vw,52px);font-weight:800;line-height:1.05;margin-bottom:14px;}
.hero h1 .grad{background:var(--grad-1);-webkit-background-clip:text;background-clip:text;color:transparent;}
.hero p{color:var(--text-muted);font-size:15px;line-height:1.6;max-width:480px;margin:0 auto 26px;}
.hero-actions{display:flex;gap:10px;justify-content:center;flex-wrap:wrap;}

 /* Platform switch - a small sliding tab group, not a full nav. Sits between
   the headline and the subtitle so picking App swaps both the gradient word
   in the h1 and the subtitle/CTA beneath it. */
.platform-switch{display:inline-flex;gap:3px;padding:4px;border-radius:999px;background:var(--bg-3);border:1px solid var(--border);margin:0 auto 16px;position:relative;}
.platform-tab{padding:7px 15px;border-radius:999px;border:none;background:none;color:var(--text-muted);font-size:12.5px;font-weight:700;cursor:pointer;transition:var(--transition);position:relative;z-index:1;}
.platform-tab:hover{color:var(--text);}
.platform-tab.is-active{background:var(--grad-1);color:#0a0805;}
.platform-tab.is-soon{opacity:.85;}

/* ===== SEASON HERO (full-bleed art) =====
    The art IS the background - no still image behind it, nothing letterboxed
   in a frame, just the picture bleeding edge to edge with a flat dark scrim
   over it so the heading stays readable.

   Two things this has to beat, both of which broke it before:
   - `section.hero.season-hero` (0,2,1) outranks `.hero` (0,1,0), so the
     hero.jpg background-image is actually cleared instead of showing through.
   - POSITIVE z-index on the art, not negative. `.hero` sets isolation:isolate,
     and a negative-z child inside that stacking context paints in a way that
     let the media end up laid out as a flex item beside the copy. Explicit
     0 / 1 / 2 layering removes the ambiguity.

    The scrim is deliberately a flat black ramp - no colour gradients, no
   radial tint. Tinting fought the artwork's own palette instead of just
   darkening it. If the copy needs more separation, raise the black stops;
   don't reintroduce a hue. */
section.hero.season-hero{background-image:none;background-color:#000;padding:124px 20px 40px;}
section.hero.season-hero::before{content:none;}
.season-hero>.season-hero-art{position:absolute;top:0;left:0;width:100%;height:100%;
  object-fit:cover;object-position:center 28%;z-index:0;pointer-events:none;border:0;
  display:block;user-select:none;}
.season-hero>.season-hero-veil{position:absolute;inset:0;z-index:1;pointer-events:none;
  background:linear-gradient(180deg,rgb(0 0 0 / 30%) 0%,rgb(0 0 0 / 34%) 45%,rgb(0 0 0 / 86%) 100%);}
.season-hero>.hero-inner{position:relative;z-index:2;}
.season-hero .hero-inner h1,.season-hero .hero-inner p{text-shadow:0 2px 22px rgb(0 0 0 / 92%);}

/* ===== TIER TRACK ===== */
.tier-track{display:flex;gap:14px;overflow-x:auto;padding:6px 2px 14px;scroll-snap-type:x proximity;}
.tier-node{flex:0 0 auto;width:112px;scroll-snap-align:start;border:1px solid var(--border);border-radius:var(--r-lg);background:var(--bg-2);padding:14px 10px;text-align:center;transition:var(--transition);}
.tier-node:hover{border-color:var(--gold);transform:translateY(-2px);}
.tier-node .tier-num{font-size:11px;font-weight:800;color:var(--text-subtle);margin-bottom:8px;}
.tier-node .tier-icon{width:36px;height:36px;margin:0 auto 8px;border-radius:999px;background:var(--bg-3);border:1px solid var(--border);display:flex;align-items:center;justify-content:center;color:var(--text-muted);}
.tier-node .tier-label{font-size:11px;font-weight:700;color:var(--text-muted);}
.tier-node.unlocked{border-color:rgb(212 175 55 / 30%);}
.tier-node.unlocked .tier-icon{color:var(--gold);border-color:rgb(212 175 55 / 40%);}
.tier-node.unlocked .tier-num{color:var(--gold);}
.tier-node.current{border-color:var(--gold);box-shadow:0 0 0 1px rgb(212 175 55 / 30%),var(--shadow);}
.tier-node.current .tier-icon{background:var(--grad-1);color:#0a0805;border-color:transparent;}
.tier-node.current .tier-num{color:var(--gold);}
.tier-node.premium{background:linear-gradient(180deg,rgb(212 175 55 / 6%),var(--bg-2));}
.tier-node.premium .tier-icon{color:var(--text-subtle);}
.tier-node.locked-final{width:126px;}

/* ===== BUTTONS ===== */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;padding:11px 20px;border-radius:var(--r);font-size:13.5px;font-weight:700;cursor:pointer;border:1px solid transparent;transition:var(--transition);white-space:nowrap;}
.btn .icon{width:15px;height:15px;}
.btn-primary{background:var(--grad-1);color:#0a0805;}
.btn-primary:hover{filter:brightness(1.08);}
.btn-secondary{background:var(--bg-3);color:var(--text);border-color:var(--border);}
.btn-secondary:hover{background:var(--bg-4);border-color:var(--gold);}
.btn-ghost{background:transparent;color:var(--text-muted);}
.btn-ghost:hover{color:var(--gold);background:var(--bg-3);}
.btn-gold{background:var(--grad-gold);color:#1a1200;}
.btn-gold:hover{filter:brightness(1.06);}
.btn-danger{background:rgb(245 243 238 / 6%);color:var(--text);border-color:var(--border);}
.btn-danger:hover{background:rgb(245 243 238 / 10%);border-color:var(--text-muted);}
.btn-block{width:100%;}
.btn-sm{padding:7px 13px;font-size:12px;}
.btn-icon-only{width:38px;height:38px;padding:0;border-radius:999px;}

/* ===== SECTION HEADERS ===== */
.section{padding:44px 0;}
.section-head{display:flex;align-items:flex-end;justify-content:space-between;margin-bottom:20px;gap:12px;flex-wrap:wrap;}
.section-head h2{font-size:22px;font-weight:800;}
.section-head p{color:var(--text-subtle);font-size:13px;margin-top:4px;}
.section-head .btn{flex-shrink:0;}

/* ===== GRID / CARDS ===== */
.grid-4{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;}
.grid-3{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;}
.grid-2{display:grid;grid-template-columns:repeat(2,1fr);gap:16px;}
@media (max-width:900px){.grid-4{grid-template-columns:repeat(2,1fr);}.grid-3{grid-template-columns:repeat(2,1fr);}}
@media (max-width:560px){.grid-4,.grid-3,.grid-2{grid-template-columns:1fr;}}

.card{border:1px solid var(--border);border-radius:var(--r-lg);background:var(--bg-2);padding:18px;transition:var(--transition);position:relative;overflow:hidden;}
.card:hover{border-color:var(--gold);transform:translateY(-2px);box-shadow:var(--shadow);}
.card-glow::before{content:"";position:absolute;inset:0;background:var(--grad-1);opacity:0;transition:var(--transition);z-index:-1;}

 /* feature card w/ art - monochrome gold-on-black, no rainbow fills */
.feature-card{position:relative;min-height:200px;border-radius:var(--r-xl);overflow:hidden;border:1px solid var(--border);display:flex;align-items:flex-end;cursor:pointer;background:var(--bg-3);transition:var(--transition);}
.feature-card::before{content:"";position:absolute;inset:0;background:radial-gradient(circle at 30% 20%, rgb(212 175 55 / 16%), transparent 60%),var(--bg-3);}
.feature-card::after{content:"";position:absolute;inset:0;background:linear-gradient(180deg,rgb(0 0 0 / 10%) 0%,rgb(0 0 0 / 40%) 45%,rgb(0 0 0 / 94%) 100%);}
.feature-card:hover{border-color:var(--gold);transform:translateY(-3px);box-shadow:var(--shadow);}
/* `.art` swaps the flat gradient panel for the card's own artwork, passed in
   as --art on the element. Lightly desaturated so four different paintings
   still read as one gold-on-black set; the ::after scrim keeps the label
   legible without burying the painting.

   Two things here are deliberate:

   1. The full `background` shorthand, not a lone `background-image` longhand.
      The base rule (.feature-card::before) also uses the shorthand, so it sets
      background-color/size/position/repeat as a unit. Overriding only
      background-image here left the art layered over that rule's leftovers
      instead of replacing them cleanly. One complete layer, no interaction.

   2. `var(--art, none)` - with no fallback, an --art that fails to resolve
      makes the whole declaration invalid at computed-value time, which is
      indistinguishable from the art simply not loading. The fallback degrades
      to the plain panel instead.

   The filter used to be saturate(.35) brightness(.62), which on top of the
   ::after scrim below multiplied out to roughly 3% of source luminance at the
   bottom of the card and ~46% at the top - dark source art crushed to a flat
   black panel. Keep any future adjustments multiplied against that scrim. */
.feature-card.art::before{background:var(--art, none) center/cover no-repeat;filter:saturate(.72) contrast(1.04) brightness(.92);transition:var(--transition);}
.feature-card.art::after{background:linear-gradient(180deg,rgb(0 0 0 / 8%) 0%,rgb(0 0 0 / 32%) 45%,rgb(0 0 0 / 88%) 100%);}
.feature-card.art:hover::before{filter:saturate(.9) contrast(1.06) brightness(1.04);transform:scale(1.04);}
/* Hardcoded backstop. --art above is the mechanism of record, but it routes the
   URL through a custom property, and when the art failed to appear there was no
   way to tell "custom property didn't reach the pseudo-element" apart from
   "image didn't load" - both render an empty ::before. These four rules name the
   files directly, so the art paints even if the var() path breaks. Same URLs, so
   when --art works these are redundant, not additive. Keep both in sync. */
.feature-card.mode-dungeon::before{background:url('/assets/img/modes/dungeon.jpg') center/cover no-repeat;}
.feature-card.mode-characters::before{background:url('/assets/img/modes/characters.jpg') center/cover no-repeat;}
.feature-card.mode-arena::before{background:url('/assets/img/modes/arena.jpg') center/cover no-repeat;}
.feature-card.mode-battlepass::before{background:url('/assets/img/modes/battlepass.jpg') center/cover no-repeat;}
 /* The Characters card is a plain div now that the page is gone - it shouldn't
   pretend to be clickable. */
.feature-card:not(a){cursor:default;}
.feature-card:not(a):hover{border-color:var(--border);transform:none;box-shadow:none;}
.feature-content{position:relative;z-index:1;padding:18px;}
.feature-content h3{font-size:16px;font-weight:800;margin-bottom:5px;color:var(--text);}
.feature-content p{font-size:12.5px;color:rgb(245 243 238 / 72%);line-height:1.4;max-width:200px;}
.feature-icon{position:absolute;top:14px;right:14px;z-index:1;width:32px;height:32px;border-radius:999px;background:rgb(0 0 0 / 45%);border:1px solid rgb(212 175 55 / 30%);display:flex;align-items:center;justify-content:center;color:var(--gold);}
.feature-icon .icon{width:15px;height:15px;}

/* stat pill row */
.stat-row{display:flex;gap:12px;flex-wrap:wrap;}
.stat-pill{flex:1;min-width:130px;border:1px solid var(--border);background:var(--bg-2);border-radius:var(--r-lg);padding:14px 16px;}
.stat-pill .label{font-size:11px;color:var(--text-subtle);font-weight:700;text-transform:uppercase;letter-spacing:.5px;}
.stat-pill .value{font-size:22px;font-weight:800;margin-top:4px;font-family:'Space Grotesk',sans-serif;}
.stat-pill .value.gold{color:var(--gold);}
.stat-pill .value.acc{color:var(--text);}

/* ===== PROFILE ===== */
 /* ===== PROFILE - Discord style banner + pfp ===== */
.profile-banner{position:relative;width:100%;height:200px;margin-top:8px;border-radius:var(--r-lg);background:
  radial-gradient(circle at 20% 30%, rgb(212 175 55 / 22%), transparent 55%),
  radial-gradient(circle at 80% 70%, rgb(212 175 55 / 12%), transparent 50%),
  var(--bg-3);
  border:1px solid var(--border);overflow:hidden;}
.profile-banner::before{content:"";position:absolute;inset:0;background-image:radial-gradient(circle,rgb(255 255 255 / 5%) 1px,transparent 1px);background-size:22px 22px;}
.profile-banner-edit{position:absolute;right:20px;bottom:14px;display:flex;align-items:center;gap:6px;padding:7px 13px;border-radius:999px;background:rgb(0 0 0 / 55%);border:1px solid var(--border);font-size:12px;font-weight:700;color:var(--text-muted);cursor:pointer;transition:var(--transition);}
.profile-banner-edit:hover{color:var(--gold);border-color:var(--gold);}
.profile-banner-edit .icon{width:14px;height:14px;}

/* The row starts a little below the banner and its contents hang from the top.
   With `flex-end` the name block was bottom-aligned to the avatar's overhang,
   so a name + rank + wrapped action buttons (a phone's worth of lines) grew
   upward and printed over the banner - the name ended up crammed against its
   bottom edge, or across it. Anchoring to the top keeps the avatar's overlap
   (that is the look) and puts the text where it belongs: under the banner. */
.profile-head-row{display:flex;align-items:flex-start;gap:18px;flex-wrap:wrap;margin-top:16px;}
.profile-pfp-wrap{position:relative;flex-shrink:0;margin-top:-52px;}
.profile-pfp{width:104px;height:104px;border-radius:999px;background:var(--grad-1);display:flex;align-items:center;justify-content:center;font-size:34px;font-weight:800;color:#0a0805;border:5px solid var(--bg);box-shadow:0 8px 24px rgb(0 0 0 / 50%);}
.profile-status{position:absolute;right:6px;bottom:6px;width:20px;height:20px;border-radius:999px;background:var(--gold);border:4px solid var(--bg);}
.profile-info-row{flex:1;display:flex;align-items:center;justify-content:space-between;gap:14px;flex-wrap:wrap;padding-bottom:6px;}
.profile-info{min-width:180px;}
.profile-info h2{font-size:22px;font-weight:800;display:flex;align-items:center;gap:8px;flex-wrap:wrap;min-width:0;color:var(--text);}
.profile-name-text{min-width:0;overflow-wrap:break-word;}
.profile-info .handle{color:var(--text-subtle);font-size:13px;margin-top:2px;}
.badge{display:inline-flex;align-items:center;gap:4px;padding:2px 9px;border-radius:999px;font-size:10.5px;font-weight:800;flex-shrink:0;white-space:nowrap;}
.badge-gold{background:rgb(212 175 55 / 15%);color:var(--gold);border:1px solid rgb(212 175 55 / 30%);}
.badge-lvl{background:rgb(245 243 238 / 8%);color:var(--text);border:1px solid var(--border);}
.profile-actions{display:flex;gap:8px;flex-wrap:wrap;}

.xp-track{margin-top:10px;height:8px;border-radius:999px;background:var(--bg-4);overflow:hidden;}
.xp-fill{height:100%;background:var(--grad-1);border-radius:999px;}

.tab-row{display:flex;gap:6px;border-bottom:1px solid var(--border);margin-bottom:20px;overflow-x:auto;}
.tab-btn{padding:10px 16px;font-size:13px;font-weight:700;color:var(--text-muted);cursor:pointer;border:none;background:none;border-bottom:2px solid transparent;white-space:nowrap;transition:var(--transition);}
.tab-btn:hover{color:var(--text);}
.tab-btn.active{color:var(--text);border-color:var(--gold);}
.tab-panel{display:none;}
.tab-panel.active{display:block;animation:fadeIn .2s ease;}

/* ===== ITEMS =====
   Inventory and shop share ONE tile: art panel on top, the item's name under
   it, and a one-line meta strip last. The old inventory was a bare 1:1 square
   that showed the plate and nothing else - you couldn't tell two swords apart
   without hovering - and clicking one opened a header shaped like a player
   profile (round avatar overlapping a banner). Items are their own thing now. */
.inv-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(136px,1fr));gap:12px;}
@media (max-width:620px){.inv-grid{grid-template-columns:repeat(auto-fill,minmax(108px,1fr));gap:10px;}}

.item-card{display:flex;flex-direction:column;width:100%;min-width:0;padding:8px;text-align:left;
  border:1px solid var(--border);border-radius:var(--r-lg);background:var(--bg-2);
  font:inherit;color:inherit;-webkit-appearance:none;appearance:none;cursor:pointer;
  transition:transform var(--dur) var(--ease-out),border-color var(--dur) var(--ease-out),box-shadow var(--dur) var(--ease-out);}
.item-card:hover{transform:translateY(-3px);border-color:rgb(212 175 55 / 45%);box-shadow:var(--shadow);}
.item-card:focus-visible{outline:2px solid var(--gold);outline-offset:2px;}
/* A tile with nothing to open (the shop card, whose action is its own Buy
   button) must not promise a click it won't honour. */
.item-card.is-static,div.item-card{cursor:default;}
.item-card.is-static:hover,div.item-card:hover{transform:none;border-color:var(--border);box-shadow:none;}

.item-card-art{position:relative;display:grid;place-items:center;width:100%;aspect-ratio:1;
  padding:0;overflow:hidden;border:1px solid var(--border-soft);border-radius:var(--r);
  background:radial-gradient(circle at 50% 28%,rgb(212 175 55 / 11%),var(--bg-3) 72%);
  font:inherit;color:inherit;-webkit-appearance:none;appearance:none;}
button.item-card-art{cursor:pointer;transition:border-color var(--dur) var(--ease-out);}
button.item-card-art:hover{border-color:rgb(212 175 55 / 45%);}
button.item-card-art:focus-visible{outline:2px solid var(--gold);outline-offset:2px;}
.item-card-img{width:84%;height:84%;object-fit:contain;
  transition:transform var(--dur-slow) var(--ease-out);filter:drop-shadow(0 8px 14px rgb(0 0 0 / 45%));}
.item-card:hover .item-card-img{transform:scale(1.06);}
.item-art-ph{display:grid;place-items:center;width:100%;height:100%;color:var(--text-subtle);}
.item-art-ph svg{width:34%;height:34%;}
.item-card-qty{position:absolute;right:6px;bottom:6px;padding:1px 7px;border-radius:999px;
  font-size:10px;font-weight:800;letter-spacing:.02em;background:rgb(0 0 0 / 68%);
  color:var(--text);border:1px solid var(--border);}
.item-card-art .rarity-pill{position:absolute;top:6px;left:6px;margin:0;}
.item-card-name{display:block;margin:9px 0 0;font-size:12.5px;font-weight:700;line-height:1.35;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.item-card-sub{display:block;margin:2px 0 0;font-size:10.5px;font-weight:600;letter-spacing:.02em;
  color:var(--text-subtle);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.item-card-desc{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;
  margin-top:6px;font-size:11.5px;line-height:1.5;color:var(--text-subtle);}
.item-card.is-rare{border-color:rgb(96 165 250 / 42%);}
.item-card.is-epic{border-color:rgb(168 85 247 / 45%);}
.item-card.is-legendary{border-color:rgb(212 175 55 / 55%);box-shadow:inset 0 0 0 1px rgb(212 175 55 / 12%);}

/* Item details: a big plate over its own name - the collectible-card
   treatment characters already get, not the banner + round avatar header. */
.item-sheet{display:flex;flex-direction:column;align-items:center;padding:20px 18px 0;text-align:center;}
.item-sheet-art{position:relative;display:grid;place-items:center;width:min(214px,64%);aspect-ratio:1;
  border:1px solid rgb(212 175 55 / 30%);border-radius:var(--r-xl);overflow:hidden;
  background:radial-gradient(circle at 50% 26%,rgb(212 175 55 / 16%),var(--bg-3) 74%);}
.item-sheet-img{width:84%;height:84%;object-fit:contain;filter:drop-shadow(0 12px 22px rgb(0 0 0 / 55%));}
.item-sheet.is-rare .item-sheet-art{border-color:rgb(96 165 250 / 48%);}
.item-sheet.is-epic .item-sheet-art{border-color:rgb(168 85 247 / 50%);}
.item-sheet.is-legendary .item-sheet-art{border-color:rgb(212 175 55 / 62%);}
.item-sheet-head{padding:14px 4px 0;max-width:100%;}
.item-sheet-head .rarity-pill{margin-bottom:8px;}
.item-sheet-head h3{font-size:20px;font-weight:800;line-height:1.25;overflow-wrap:anywhere;}
.item-sheet-sub{margin-top:5px;font-size:12.5px;color:var(--text-muted);overflow-wrap:anywhere;}

/* ===== PRICING / PREMIUM ===== */
.pricing-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;align-items:stretch;}
@media (max-width:820px){.pricing-grid{grid-template-columns:1fr;}}
.plan-card{border:1px solid var(--border);border-radius:var(--r-xl);background:var(--bg-2);padding:26px;display:flex;flex-direction:column;position:relative;transition:var(--transition);}
.plan-card:hover{border-color:var(--gold);transform:translateY(-3px);box-shadow:var(--shadow);}
/* The middle card. Black rather than --bg-2, so the gold border and the gold
   price read as lit instead of as a slightly different grey, and it lifts a few
   px above its neighbours without a hover. Owner ask, 2026-09-23: "golden but
   black with a best value tag".

   The lift is cancelled on narrow screens where .pricing-grid collapses to one
   column - a 6px offset in a vertical stack looks like a layout bug, not an
   emphasis. */
.plan-card.featured{
  border:1px solid var(--gold);
  background:#07070a;
  box-shadow:0 0 0 1px rgb(212 175 55 / 22%),0 18px 44px rgb(0 0 0 / 55%),0 0 60px rgb(212 175 55 / 8%);
  transform:translateY(-6px);
}
.plan-card.featured:hover{transform:translateY(-9px);}
.plan-card.featured .plan-name{color:var(--gold);}
.plan-card.featured .plan-price{
  background:var(--grad-1);
  -webkit-background-clip:text;background-clip:text;
  -webkit-text-fill-color:transparent;color:var(--gold);
}
/* The "/ 30d" suffix stays readable text - inheriting the clipped gradient
   above would paint it transparent too. */
.plan-card.featured .plan-price span{
  -webkit-text-fill-color:var(--text-subtle);color:var(--text-subtle);
}
@media (max-width:820px){
  .plan-card.featured,.plan-card.featured:hover{transform:none;}
}
.plan-ribbon{position:absolute;top:-11px;left:50%;transform:translateX(-50%);background:var(--grad-1);color:#0a0805;font-size:10.5px;font-weight:800;padding:4px 14px;border-radius:999px;box-shadow:0 4px 12px rgb(212 175 55 / 40%);}
.plan-name{font-size:13px;font-weight:700;color:var(--text-muted);text-transform:uppercase;letter-spacing:.6px;}
.plan-price{font-size:36px;font-weight:800;margin:10px 0 2px;font-family:'Space Grotesk',sans-serif;}
.plan-price span{font-size:14px;color:var(--text-subtle);font-weight:600;}
.plan-desc{font-size:12.5px;color:var(--text-subtle);margin-bottom:18px;}
.plan-features{list-style:none;padding:0;margin:0 0 22px;display:flex;flex-direction:column;gap:11px;}
.plan-features li{display:flex;align-items:flex-start;gap:9px;font-size:13px;color:var(--text-muted);}
.plan-features li .check{color:var(--success);flex-shrink:0;margin-top:1px;}
.plan-features li.locked{color:var(--text-subtle);}
.plan-features li.locked .check{color:var(--text-subtle);}

/* toggle */
.billing-toggle{display:inline-flex;align-items:center;gap:0;padding:4px;border:1px solid var(--border);border-radius:999px;background:var(--bg-3);margin:0 auto 30px;}
.billing-opt{padding:8px 18px;border-radius:999px;font-size:12.5px;font-weight:700;color:var(--text-muted);cursor:pointer;border:none;background:none;}
.billing-opt.active{background:var(--bg-4);color:#fff;}
.save-tag{background:rgb(212 175 55 / 15%);color:var(--gold);font-size:9.5px;font-weight:800;padding:1px 6px;border-radius:999px;margin-left:6px;}

/* perk grid */
.perk-card{border:1px solid var(--border);border-radius:var(--r-lg);background:var(--bg-2);padding:18px;}
.perk-icon{width:38px;height:38px;border-radius:10px;display:flex;align-items:center;justify-content:center;font-size:18px;margin-bottom:12px;}
.perk-card h4{font-size:14px;font-weight:700;margin-bottom:5px;}
.perk-card p{font-size:12px;color:var(--text-subtle);line-height:1.5;}

/* Roster/shop tiles are real <button>s so they're keyboard-reachable and
   announced as controls. A button brings its own centred text, its own font
   and a shrink-to-fit width, none of which belong on a grid tile. */
button.perk-card{width:100%;text-align:left;font:inherit;color:inherit;-webkit-appearance:none;appearance:none;}

 /* Only a tile that actually does something gets the hand - same reasoning as
   `.feature-card:not(a){cursor:default}` above. A hover lift on a tile that
   ignores the click is a promise the page doesn't keep. */
.perk-card[data-char],.perk-card[data-item],a.perk-card,button.perk-card{cursor:pointer;}
.perk-card:not([data-char]):not([data-item]):not(a):not(button){cursor:default;}
.perk-card:not([data-char]):not([data-item]):not(a):not(button):hover{
  transform:none;border-color:var(--border);box-shadow:none;}
.perk-card:focus-visible,.item-card:focus-visible,.tier-node:focus-visible{
  outline:2px solid var(--gold);outline-offset:2px;}

/* Rarity / display-tag badge. `tag` is a marketing label (legendary, special)
   and `rarity` is the mechanical grade - they share this pill because they
   occupy the same slot on a tile, never both at once. */
.rarity-pill{display:inline-block;padding:2px 8px;margin-bottom:7px;border-radius:999px;
  font-size:9.5px;font-weight:800;letter-spacing:.04em;text-transform:uppercase;
  background:var(--bg-4);color:var(--text-muted);border:1px solid var(--border);}
.rarity-pill.r-legendary,.rarity-pill.r-mythic{background:rgb(212 175 55 / 15%);color:var(--gold);border-color:rgb(212 175 55 / 45%);}
.rarity-pill.r-special{background:rgb(168 85 247 / 15%);color:#c99bff;border-color:rgb(168 85 247 / 45%);}
.rarity-pill.r-epic{background:rgb(168 85 247 / 12%);color:#bd8dfa;border-color:rgb(168 85 247 / 35%);}
.rarity-pill.r-rare{background:rgb(96 165 250 / 12%);color:#8ec2ff;border-color:rgb(96 165 250 / 35%);}
.rarity-pill.r-uncommon{background:rgb(74 222 128 / 12%);color:#86e2a6;border-color:rgb(74 222 128 / 32%);}

/* Season lore */
.lore-card{border-left:2px solid var(--gold);}.lore-lead{font-size:15px;line-height:1.7;color:var(--text);margin:0;}
.lore-card .subtext{margin-top:11px;}
.link-btn{background:none;border:none;padding:0;font:inherit;color:var(--gold);
  font-weight:700;cursor:pointer;text-decoration:underline;text-underline-offset:2px;}
.link-btn:focus-visible{outline:2px solid var(--gold);outline-offset:2px;border-radius:3px;}

/* ── shop ─────────────────────────────────────────────────────────────── */
.shop-wallet{display:flex;align-items:center;gap:12px;flex-wrap:wrap;font-size:13px;}
.shop-wallet strong{color:var(--gold);font-size:15px;}
.shop-group{margin-bottom:30px;}
.shop-group-head{font-size:12px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;
  color:var(--text-subtle);margin-bottom:13px;padding-bottom:7px;border-bottom:1px solid var(--border-soft);}
/* Shop tiles are item cards too: the same art-over-name shape as the profile
   inventory, so an item looks like the same object in both places. Only the
   footer differs (price + Buy instead of rarity · type). */
.shop-card{display:flex;flex-direction:column;gap:0;padding:9px;}
/* Shop names can run long ("Greater Healing Draught"), so they wrap to two
   lines here instead of being clipped to one like an inventory tile. */
.shop-card .item-card-name{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;
  white-space:normal;overflow:hidden;}
.shop-price{display:flex;align-items:baseline;justify-content:space-between;gap:8px;
  margin:10px 0;font-size:13px;font-weight:700;}
.shop-price .gold{color:var(--gold);}
.shop-price s{font-weight:400;margin-left:5px;}
/* Pushes the Buy button to a common baseline so a row of cards with unequal
   description lengths still has its actions aligned. */
.shop-card .btn{margin-top:auto;}

/* ── cards ────────────────────────────────────────────────────────────── */
/* The catalog is display-only, so its tiles don't read as clickable. */
.card-tile{cursor:default;}
.card-img{width:100%;aspect-ratio:3/4;object-fit:cover;border-radius:var(--r);margin-bottom:11px;
  transition:transform var(--dur-slow) var(--ease-out);}
/* Animated cards render as <video>, so the hover rule has to name both. */
.card-tile:hover img,.card-tile:hover video{transform:scale(1.04);}
/* The pull-result banner is a background-image for still cards and a real
   <video> for animated ones; this makes the video fill the banner identically. */
.pd-banner-media{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;}
.rarity-pill.r-ts{background:rgb(212 175 55 / 18%);color:var(--gold);border-color:rgb(212 175 55 / 50%);}
.rarity-pill.r-t6,.rarity-pill.r-t5{background:rgb(168 85 247 / 14%);color:#c99bff;border-color:rgb(168 85 247 / 40%);}
.rarity-pill.r-t4,.rarity-pill.r-t3{background:rgb(96 165 250 / 12%);color:#8ec2ff;border-color:rgb(96 165 250 / 35%);}

/* spin banner
   ─────────────────────────────────────────────────────────────────────────
   The character spin banner on the season page.

   Landscape, unlike .pull-art above: this one sits in the page rather than
   in a modal, so the art is a side panel and the copy runs beside it. Gacha
   art is portrait, so the panel is given a fixed width and the image is
   contained inside it, letterboxed the same way the pull reveal is, instead
   of being cropped by background-size:cover. Stacks on narrow screens. */
.spin-banner{display:flex;gap:0;padding:0;overflow:hidden;}
.spin-art{position:relative;flex:0 0 232px;display:flex;align-items:center;justify-content:center;
  min-height:236px;padding:18px;overflow:hidden;
  background:radial-gradient(circle at 50% 0%, rgb(212 175 55 / 16%), transparent 64%), var(--bg-3);}
.spin-art img{max-width:100%;max-height:280px;width:auto;height:auto;object-fit:contain;
  border-radius:var(--r-lg);box-shadow:0 12px 30px rgb(0 0 0 / 50%);}
.spin-art .perk-icon{opacity:.5;}
.spin-info{flex:1;min-width:0;padding:20px 22px;}
.spin-head{display:flex;align-items:center;gap:10px;flex-wrap:wrap;}
.spin-head h3{margin:0;font-size:19px;}
.spin-head .rarity-pill{margin-bottom:0;}
.spin-stars{margin:6px 0 0;color:var(--gold);font-size:13px;letter-spacing:.14em;}
.spin-desc{margin:10px 0 14px;font-size:13.5px;color:var(--text-muted);line-height:1.6;}
.spin-info .shop-wallet{margin-top:12px;}
/* Every non-spinnable state (yours, claimed, frozen, spent) renders as this
   one line with a badge, so the card never changes height as it changes
   state. */
.spin-note{display:flex;align-items:center;gap:8px;flex-wrap:wrap;
  margin:14px 0 0;font-size:13px;color:var(--text-muted);}
.spin-actions{display:flex;gap:10px;flex-wrap:wrap;margin-top:14px;}
/* The miss reel in the reveal modal: one glyph per spin, monospaced so the
   marks line up in a row rather than drifting with the body font. */
.spin-reel{margin:2px 0 14px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
  font-size:15px;line-height:1.9;color:var(--text-subtle);word-break:break-all;}
@media (max-width:640px){
  .spin-banner{flex-direction:column;}
  .spin-art{flex:0 0 auto;min-height:0;height:min(38svh,272px);}
}

/* faq */
.faq-item{border-bottom:1px solid var(--border-soft);}
.faq-q{display:flex;align-items:center;justify-content:space-between;padding:16px 4px;cursor:pointer;font-weight:700;font-size:13.5px;}
.faq-q .arrow{transition:transform .2s;color:var(--text-subtle);}
.faq-item.open .arrow{transform:rotate(45deg);}
.faq-a{max-height:0;overflow:hidden;transition:max-height .25s ease;font-size:13px;color:var(--text-muted);line-height:1.6;}
.faq-item.open .faq-a{max-height:200px;padding-bottom:16px;}

/* leaderboard */
.lb-row{display:flex;align-items:center;gap:12px;padding:11px 14px;border-radius:var(--r);border:1px solid transparent;}
.lb-row:hover{background:var(--bg-3);border-color:var(--border);}
.lb-rank{width:26px;text-align:center;font-weight:800;font-size:13px;color:var(--text-subtle);}
.lb-rank.top1{color:var(--gold);}
.lb-rank.top2{color:var(--text-muted);}
.lb-rank.top3{color:var(--text-subtle);}
.lb-avatar{width:34px;height:34px;border-radius:999px;background:var(--bg-4);border:1px solid var(--border);color:var(--gold);display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:800;flex-shrink:0;}
.lb-name{flex:1;min-width:0;font-size:13px;font-weight:700;overflow-wrap:anywhere;}
.lb-name .sub{display:block;font-size:11px;color:var(--text-subtle);font-weight:500;}
.lb-score{font-size:13px;font-weight:800;color:var(--gold);}

/* toast */
.toast-wrap{position:fixed;bottom:20px;right:20px;z-index:200;display:flex;flex-direction:column;gap:8px;}
.toast{display:flex;align-items:center;gap:10px;background:var(--bg-3);border:1px solid var(--border);border-radius:var(--r);padding:12px 16px;font-size:13px;font-weight:600;box-shadow:var(--shadow);min-width:220px;animation:slideIn .25s ease;}
@keyframes slideIn{from{transform:translateX(30px);opacity:0;}to{transform:translateX(0);opacity:1;}}
.toast .dot{width:8px;height:8px;border-radius:999px;background:var(--gold);flex-shrink:0;}

/* footer */
.footer{border-top:1px solid var(--border-soft);padding:34px 20px;text-align:center;color:var(--text-subtle);font-size:12px;}
.footer .links{display:flex;gap:16px;justify-content:center;margin-bottom:12px;flex-wrap:wrap;}
.footer .links a{color:var(--text-muted);font-weight:600;font-size:12.5px;}
.footer .links a:hover{color:#fff;}

.site-footer{border-top:1px solid var(--border-soft);background:var(--bg-2);padding:52px 20px 26px;margin-top:40px;}
.site-footer-inner{width:min(100%,1180px);margin:0 auto;}
.site-footer-top{display:grid;grid-template-columns:1.6fr 1fr 1fr 1fr;gap:32px;padding-bottom:32px;}
.site-footer-brand{display:flex;flex-direction:column;align-items:flex-start;gap:14px;}
.site-footer-tag{color:var(--text-subtle);font-size:13px;line-height:1.6;margin:0;max-width:280px;}
.site-footer-col{display:flex;flex-direction:column;gap:11px;}
.site-footer-heading{color:var(--text);font-weight:700;font-size:13px;margin-bottom:2px;}
.site-footer-col a{color:var(--text-muted);font-size:13px;font-weight:500;cursor:pointer;transition:var(--transition);width:fit-content;}
.site-footer-col a:hover{color:var(--gold);}
.site-footer-bottom{display:flex;align-items:center;justify-content:space-between;gap:16px;padding-top:22px;border-top:1px solid var(--border-soft);flex-wrap:wrap;}
.site-footer-social{display:flex;gap:8px;}
 /* Attribution line - quieter than the rest of the footer, but must stay legible: it's a licence requirement, not decoration. */
.site-footer-credit{margin:14px 0 0;font-size:12px;line-height:1.7;color:var(--text-subtle);}
.site-footer-credit a{color:var(--text-muted);text-decoration:underline;text-underline-offset:2px;transition:var(--transition);}
.site-footer-credit a:hover{color:var(--gold);}

@media (max-width:820px){
  .site-footer-top{grid-template-columns:1fr 1fr;row-gap:28px;}
  .site-footer-brand{grid-column:1 / -1;}
}
@media (max-width:480px){
  .site-footer-top{grid-template-columns:1fr;}
  .site-footer-bottom{justify-content:center;text-align:center;}
}

/* switches */
.switch{position:relative;width:40px;height:22px;flex-shrink:0;}
.switch input{opacity:0;width:0;height:0;}
.switch-track{position:absolute;inset:0;background:var(--bg-4);border-radius:999px;cursor:pointer;transition:var(--transition);}
.switch-track::before{content:"";position:absolute;width:16px;height:16px;left:3px;top:3px;background:#fff;border-radius:999px;transition:var(--transition);}
.switch input:checked + .switch-track{background:var(--grad-1);}
.switch input:checked + .switch-track::before{transform:translateX(18px);}
.setting-row{display:flex;align-items:center;justify-content:space-between;padding:14px 0;border-bottom:1px solid var(--border-soft);}
.setting-row:last-child{border-bottom:none;}
.setting-row .label{font-size:13.5px;font-weight:700;}
.setting-row .desc{font-size:11.5px;color:var(--text-subtle);margin-top:2px;}

.copy-box{display:flex;align-items:center;gap:8px;background:var(--bg-3);border:1px solid var(--border);border-radius:var(--r);padding:10px 12px;font-family:monospace;font-size:12.5px;color:var(--accent-3);}
.copy-box button{margin-left:auto;flex-shrink:0;}

.chip-row{display:flex;gap:8px;flex-wrap:wrap;}
.chip{padding:6px 13px;border-radius:999px;background:var(--bg-3);border:1px solid var(--border);font-size:12px;font-weight:600;color:var(--text-muted);cursor:pointer;transition:var(--transition);}
.chip:hover{color:#fff;border-color:var(--bg-4);}
.chip.active{background:var(--grad-1);color:#0a0805;border-color:transparent;}

.subtext{color:var(--text-subtle);font-size:12.5px;}
.muted{color:var(--text-muted);}

/* ══════════════════════════════════════════════════════════════════════
   ADDITIONS - everything above this line is astral.html's stylesheet,
   unchanged. Below are only the components the mockup never had: the
   notification box, the player-detail modal, the OTP inputs, the
   sign-in/sign-up pages, loading skeletons, the offline banner and the
   leaderboard toolbar. Same variables, same idiom, no new colours.
   ══════════════════════════════════════════════════════════════════════ */

/* ===== images inside the round avatars astral defines ===== */
.avatar-btn img,.lb-avatar img,.pd-avatar img,.profile-pfp img{width:100%;height:100%;border-radius:999px;object-fit:cover;display:block;}
.avatar-btn.is-premium{box-shadow:0 0 0 2px rgb(212 175 55 / 25%);}

/* ===== NOTIFICATION BOX ===== */
.bell-wrap{position:relative;}
.bell-dot{position:absolute;top:-3px;right:-3px;width:10px;height:10px;border-radius:999px;background:var(--grad-1);border:2px solid var(--bg);display:none;}
.bell-dot.is-on{display:block;}
 /* A dim scrim behind the panel, same idea as the drawer's - without it the
   page underneath stays fully visible and interactive-looking, so an open
   panel just reads as "floating in the wrong place" instead of a focused
   overlay anchored to the bell. */
.panel-backdrop{position:fixed;inset:0;background:rgb(0 0 0 / 45%);z-index:79;opacity:0;pointer-events:none;transition:opacity .2s;}
.panel-backdrop.open{opacity:1;pointer-events:auto;}
/* The panel is pinned 70px below the top of the viewport, so its ceiling has
   to be measured from there - `min(70svh,520px)` ignored the offset and on a
   short viewport the box ran off the bottom of the screen. calc(100dvh - 86px)
   keeps 16px of breathing room below it at any height, and dvh (not vh) so
   mobile browser chrome collapsing doesn't leave it overhanging.
   It's a short dropdown, not a full-height drawer: it hangs from the bell
   icon (top-right, right:20px matches the header's edge padding) and its
   height is capped by content via max-height, never forced to fill the
   viewport. Opening scales up from that top-right corner - transform-origin
   is set below - rather than just sliding down, so it reads as "popping out
   of the bell" instead of a generic panel appearing from nowhere. */
.panel{position:fixed;top:70px;right:20px;width:min(360px,calc(100vw - 32px));max-height:min(70svh,520px,calc(100dvh - 86px));z-index:80;border:1px solid var(--border);border-radius:var(--r-lg);background:rgba(10,10,10,.99);box-shadow:var(--shadow);display:flex;flex-direction:column;opacity:0;transform:translateY(-6px) scale(.94);pointer-events:none;transition:opacity .18s ease,transform .22s cubic-bezier(.2,.9,.25,1.1);transform-origin:top right;overflow:hidden;}
.panel.open{opacity:1;transform:none;pointer-events:auto;}
.panel-head{display:flex;align-items:center;gap:10px;padding:14px 16px;border-bottom:1px solid var(--border-soft);flex-shrink:0;}
.panel-head strong{font-family:'Space Grotesk',sans-serif;font-size:14px;font-weight:800;flex:1;min-width:0;}
/* min-height:0 is the load-bearing bit. A flex item defaults to
   min-height:auto, which refuses to shrink below its content - so the body
   grew to fit every notification, pushed past the panel's max-height and
   spilled out of the box instead of scrolling inside it. overscroll-behavior
   stops a scroll that hits the end of the list from chaining to the page. */
.panel-body{overflow-y:auto;min-height:0;flex:1;overscroll-behavior:contain;padding:6px;}
.note{display:flex;gap:11px;padding:11px 10px;border-radius:var(--r);cursor:pointer;transition:var(--transition);}
.note:hover{background:var(--bg-3);}
.note-i{width:30px;height:30px;flex-shrink:0;border-radius:999px;background:var(--bg-3);border:1px solid var(--border);display:flex;align-items:center;justify-content:center;font-size:14px;}
.note.is-unread .note-i{border-color:rgb(212 175 55 / 40%);}
.note.sev-urgent .note-i{background:rgb(212 175 55 / 12%);border-color:rgb(212 175 55 / 45%);}
.note-main{min-width:0;flex:1;}
/* A pasted URL or any long unbroken token has no break opportunity, so it
   sets the row's min-content width and forces the whole panel wider than its
   own max-width. anywhere lets it break mid-token rather than overflow. */
.note-t,.note-b{overflow-wrap:anywhere;}
.note-t{font-size:13px;font-weight:700;}
.note.is-unread .note-t{color:var(--gold);}
.note-b{font-size:12px;color:var(--text-muted);line-height:1.45;margin-top:2px;}
.note-time{font-size:10.5px;color:var(--text-subtle);margin-top:4px;font-weight:600;}
.note-x{flex-shrink:0;width:22px;height:22px;border-radius:999px;border:none;background:none;color:var(--text-subtle);font-size:16px;line-height:1;cursor:pointer;opacity:0;transition:var(--transition);}
.note:hover .note-x{opacity:1;}
.note-x:hover{color:var(--text);background:var(--bg-4);}

 /* ===== MODAL - player / character detail ===== */
.modal{position:fixed;inset:0;z-index:120;display:flex;align-items:center;justify-content:center;padding:20px;opacity:0;pointer-events:none;transition:opacity .2s;}
.modal.open{opacity:1;pointer-events:auto;}
.modal-scrim{position:absolute;inset:0;background:rgb(0 0 0 / 80%);}
.modal-card{position:relative;width:min(560px,100%);max-height:86svh;overflow-y:auto;border:1px solid var(--border);border-radius:var(--r-xl);background:var(--bg-2);box-shadow:var(--shadow);transform:translateY(12px) scale(.97);transition:transform .22s cubic-bezier(.2,.7,.2,1);}
.modal.open .modal-card{transform:none;}
.modal-x{position:absolute;top:14px;right:14px;z-index:2;width:32px;height:32px;border-radius:999px;border:1px solid var(--border);background:rgb(0 0 0 / 74%);color:var(--text-muted);cursor:pointer;display:flex;align-items:center;justify-content:center;transition:var(--transition);}
.modal-x:hover{color:var(--gold);border-color:var(--gold);}
.pd-banner{height:118px;background:
  radial-gradient(circle at 20% 30%, rgb(212 175 55 / 22%), transparent 55%),
  radial-gradient(circle at 80% 70%, rgb(212 175 55 / 12%), transparent 50%),
  var(--bg-3);background-size:cover;background-position:center;position:relative;z-index:0;}
/* align-items is flex-start, not flex-end: with flex-end the text column's
   height set the row height, so every extra line (epithet, class/race, bio)
   dragged the avatar down out of the banner and squeezed the name. Starting
   both at the top pins the avatar's -38px overlap regardless of how much
   text sits beside it, and the column below is free to grow. */
.pd-head{position:relative;z-index:1;display:flex;align-items:flex-start;gap:14px;padding:0 22px 6px;}
.pd-avatar{position:relative;z-index:1;width:80px;height:80px;margin-top:-38px;border-radius:999px;background:var(--grad-1);color:#0a0805;display:flex;align-items:center;justify-content:center;font-size:26px;font-weight:800;border:5px solid var(--bg-2);flex-shrink:0;overflow:hidden;}
/* The identity column. min-width:0 lets it shrink below its longest word so a
   long name wraps instead of overflowing - .modal-card sets overflow-y:auto,
   which computes overflow-x to auto too, so an overflowing name was clipped
   rather than visibly spilling. */
.pd-id{min-width:0;flex:1;padding-top:6px;}
/* Block, not flex: as a flex row the name text was an anonymous item stuck at
   min-content, so it could never wrap. Inline-flex badges flow after it. */
.pd-name-row{display:flex;align-items:flex-start;gap:8px;flex-wrap:wrap;min-width:0;}
.pd-head h3{min-width:0;font-size:19px;font-weight:800;line-height:1.3;overflow-wrap:anywhere;word-break:break-word;}
.pd-badges{display:flex;align-items:center;gap:5px;flex-wrap:wrap;padding-top:2px;}
.pd-sub{font-size:12.5px;color:var(--text-muted);margin:4px 0 0;line-height:1.45;overflow-wrap:anywhere;}
.pd-id .subtext{overflow-wrap:anywhere;}
.pd-body{padding:18px 22px 24px;}
.pd-sec-title{font-size:11px;font-weight:800;text-transform:uppercase;letter-spacing:.5px;color:var(--text-subtle);margin:20px 0 10px;}
.pd-stats{display:grid;grid-template-columns:repeat(4,1fr);gap:8px;}
@media (max-width:520px){.pd-stats{grid-template-columns:repeat(2,1fr);}}
.pd-stat{border:1px solid var(--border);border-radius:var(--r);background:var(--bg-3);padding:10px 8px;text-align:center;}
.pd-stat-k{font-size:10px;font-weight:800;text-transform:uppercase;letter-spacing:.4px;color:var(--text-subtle);}
.pd-stat-v{font-size:17px;font-weight:800;font-family:'Space Grotesk',sans-serif;margin-top:3px;}

 /* alert list - inside the modal */
.alert-item{display:flex;gap:11px;padding:11px 12px;border:1px solid var(--border);border-radius:var(--r);background:var(--bg-3);margin-bottom:8px;}
.alert-item.sev-urgent{border-color:rgb(212 175 55 / 45%);}
.alert-t{font-size:12.5px;font-weight:700;}
.alert-b{font-size:12px;color:var(--text-muted);line-height:1.45;margin-top:2px;}

 /* ===== AUTH - sign in / sign up ===== */
.auth-wrap{display:flex;align-items:center;justify-content:center;min-height:70svh;padding:70px 20px 60px;}
.auth-card{width:min(430px,100%);border:1px solid var(--border);border-radius:var(--r-xl);background:var(--bg-2);padding:30px 26px;box-shadow:var(--shadow);}
.auth-card h2{font-size:23px;font-weight:800;margin-bottom:6px;}
.auth-step{display:none;}
.auth-step.active{display:block;animation:fadeIn .2s ease;}
.auth-alt{margin-top:20px;padding-top:18px;border-top:1px solid var(--border-soft);text-align:center;font-size:12.5px;color:var(--text-subtle);display:flex;gap:8px;align-items:center;justify-content:center;flex-wrap:wrap;}
.auth-alt a{color:var(--gold);font-weight:700;cursor:pointer;}
.auth-alt a:hover{text-decoration:underline;}

.field{margin-bottom:14px;}
.field label{display:block;font-size:11.5px;font-weight:800;text-transform:uppercase;letter-spacing:.5px;color:var(--text-subtle);margin-bottom:6px;}
.field input,.field select{width:100%;padding:11px 13px;border-radius:var(--r);border:1px solid var(--border);background:var(--bg-3);color:var(--text);font-family:inherit;font-size:14px;transition:var(--transition);}
.field input:focus,.field select:focus{outline:none;border-color:var(--gold);box-shadow:0 0 0 3px rgb(212 175 55 / 12%);}
.field-hint{font-size:11.5px;color:var(--text-subtle);margin:6px 0 0;line-height:1.5;}
.field-err{font-size:12px;color:var(--gold);margin:6px 0 0;}
.field.has-err input{border-color:var(--gold);}

.otp-row{display:flex;gap:8px;}
.otp-row input{width:100%;aspect-ratio:1/1.15;padding:0;text-align:center;font-size:22px;font-weight:800;font-family:'Space Grotesk',sans-serif;border-radius:var(--r);border:1px solid var(--border);background:var(--bg-3);color:var(--text);transition:var(--transition);}
.otp-row input:focus{outline:none;border-color:var(--gold);box-shadow:0 0 0 3px rgb(212 175 55 / 12%);}
.otp-row input.is-filled{border-color:rgb(212 175 55 / 45%);color:var(--gold);}

 /* busy button - the spinner replaces the label so a double-tap can't fire twice */
.btn.is-busy{pointer-events:none;opacity:.75;position:relative;color:transparent;}
.btn.is-busy::after{content:"";position:absolute;width:15px;height:15px;border-radius:999px;border:2px solid rgb(0 0 0 / 25%);border-top-color:#0a0805;animation:spin .6s linear infinite;}
.btn-secondary.is-busy::after,.btn-ghost.is-busy::after,.btn-danger.is-busy::after{border-color:rgb(245 243 238 / 25%);border-top-color:var(--text);}
@keyframes spin{to{transform:rotate(360deg);}}

/* ===== LEADERBOARD PAGE ===== */
.lb-toolbar{display:flex;align-items:center;gap:14px;margin-bottom:16px;flex-wrap:wrap;}
.lb-toolbar .tab-row{flex:1;margin-bottom:0;border-bottom:none;}
.lb-search{min-width:200px;padding:10px 15px;border-radius:999px;border:1px solid var(--border);background:var(--bg-2);color:var(--text);font-family:inherit;font-size:13px;transition:var(--transition);}
.lb-search:focus{outline:none;border-color:var(--gold);}
.lb-list{border:1px solid var(--border);border-radius:var(--r-lg);background:var(--bg-2);padding:6px;}
.lb-row{width:100%;text-align:left;background:none;cursor:pointer;transition:var(--transition);}
.lb-row.is-you{background:rgb(212 175 55 / 7%);border-color:rgb(212 175 55 / 30%);}

/* ===== SKELETONS / EMPTY ===== */
.skel{background:linear-gradient(90deg,var(--bg-3) 0%,var(--bg-4) 50%,var(--bg-3) 100%);background-size:200% 100%;animation:shimmer 1.3s ease-in-out infinite;border-radius:var(--r);}
@keyframes shimmer{to{background-position:-200% 0;}}
.skel-line{height:12px;}
.skel-row{display:flex;align-items:center;gap:12px;padding:14px;}

.empty{text-align:center;padding:40px 20px;}
.empty-i{width:46px;height:46px;margin:0 auto 12px;border-radius:999px;border:1px solid var(--border);background:var(--bg-3);display:flex;align-items:center;justify-content:center;}
.empty-icon{width:20px;height:20px;stroke:currentColor;fill:none;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round;color:var(--gold);}
.inline-icon{width:14px;height:14px;stroke:currentColor;fill:none;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round;vertical-align:-2px;}
.note-icon,.lb-medal,.tier-icon-svg,.inventory-icon,.character-placeholder{width:18px;height:18px;stroke:currentColor;fill:none;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round;}
.tier-icon-svg{width:21px;height:21px;}
.character-placeholder{width:30px;height:30px;color:var(--gold);}
.inventory-icon{width:22px;height:22px;color:var(--gold);}
.empty h3{font-size:15px;font-weight:800;margin-bottom:5px;}
.empty p{font-size:13px;color:var(--text-subtle);margin:0;line-height:1.55;max-width:320px;margin-inline:auto;}

/* ===== OFFLINE BANNER ===== */
/* ── boot cover ───────────────────────────────────────────────────────────
   The sun, full screen, from the first frame until the first route has
   content. Opaque on purpose: it is covering a page that is still assembling,
   and a see-through cover would show exactly that. app.js lifts it
   (`html.is-ready`); the inline script in <head> lifts it on a timer as a
   fail-safe, because a cover that outlives a broken boot blanks the site. */
.boot-sun{position:fixed;inset:0;z-index:400;display:grid;place-items:center;
  background:radial-gradient(70% 58% at 50% 42%,#14110a 0%,#060605 72%);
  opacity:1;transition:opacity .5s var(--ease-out),visibility .5s var(--ease-out);}
.boot-sun-mark{position:relative;z-index:1;width:46px;height:46px;color:var(--gold);
  stroke:currentColor;fill:none;stroke-width:1.3;stroke-linecap:round;stroke-linejoin:round;
  animation:bootSunSpin 2.6s linear infinite;}
.boot-sun-halo{position:absolute;inset:0;margin:auto;width:116px;height:116px;border-radius:50%;
  border:1px solid rgb(212 175 55 / 16%);
  box-shadow:0 0 46px -8px rgb(212 175 55 / 38%),inset 0 0 34px rgb(212 175 55 / 9%);
  animation:bootHalo 1.9s ease-in-out infinite;}
html.is-ready .boot-sun{opacity:0;visibility:hidden;}
@keyframes bootSunSpin{to{transform:rotate(360deg);}}
@keyframes bootHalo{0%,100%{transform:scale(.9);opacity:.5;}50%{transform:scale(1.05);opacity:1;}}

/* ── route cue ────────────────────────────────────────────────────────────
   The same sun, small, floating under the nav while a page is fetched. It
   never takes a click (`pointer-events:none`) and app.js holds it for a
   minimum beat so an instantly-cached page still reads as a tap that landed. */
.route-sun{position:fixed;top:calc(env(safe-area-inset-top,0px) + 66px);left:50%;z-index:120;
  display:grid;place-items:center;width:34px;height:34px;border-radius:50%;
  background:rgb(10 9 7 / 94%);border:1px solid rgb(212 175 55 / 26%);
  box-shadow:0 12px 28px -14px #000,inset 0 1px 0 rgb(255 255 255 / 6%);
  opacity:0;pointer-events:none;
  transform:translate(-50%,-6px) scale(.86);
  transition:opacity .2s var(--ease-out),transform .26s var(--ease-out);}
.route-sun.is-on{opacity:1;transform:translate(-50%,0) scale(1);}
.route-sun-mark{width:17px;height:17px;color:var(--gold);
  stroke:currentColor;fill:none;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round;
  animation:bootSunSpin 1.5s linear infinite;}

@media (prefers-reduced-motion:reduce){
  .boot-sun-mark,.boot-sun-halo,.route-sun-mark{animation:none;}
}

.offline{position:fixed;top:14px;left:50%;z-index:210;transform:translate(-50%,-160%);padding:9px 16px;border-radius:999px;background:var(--bg-3);border:1px solid var(--gold);color:var(--gold);font-size:12.5px;font-weight:700;box-shadow:var(--shadow);transition:transform .25s cubic-bezier(.2,.7,.2,1);}
.offline.is-on{transform:translate(-50%,0);}

/* ===== TOAST KINDS ===== */
.toast.ok{border-color:rgb(212 175 55 / 45%);}
.toast.ok .dot{background:var(--gold);}
.toast.err{border-color:var(--text-muted);}
.toast.err .dot{background:var(--text-muted);}
.toast.is-out{animation:toastOut .25s ease forwards;}
@keyframes toastOut{to{transform:translateX(30px);opacity:0;}}

/* ===== MISC ===== */
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0;}
.copy-box span{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;}
.section-head .badge{flex-shrink:0;}
code{font-family:monospace;font-size:.92em;color:var(--gold);}

/* =======================================================================
   MOTION
   =======================================================================
   One shared vocabulary so the whole site moves the same way:

     • gold sweeps across a surface on hover (buttons, cards, art)
     • surfaces lift toward the viewer, they never scale up in place
     • entrances rise 12–18px, never slide sideways or bounce

   Everything here animates only `transform`, `opacity` and `filter` - the
   three the compositor can handle without relayout - so it stays smooth on
   the mid-range Android most players are on. Anything that would animate
   width/height/box-shadow directly is done with a pseudo-element instead.

   The whole section is disabled wholesale by the prefers-reduced-motion
   block at the very bottom of this file. Add new motion above that block,
   and add its off-switch to it. */

:root{
  --ease-out: cubic-bezier(.2,.7,.2,1);
  --dur-fast: .22s;
  --dur: .42s;
  --dur-slow: .7s;
}

/* ── entrance: hero ────────────────────────────────────────────────────
   The hero is the one thing above the fold on first paint, so it animates
   on load rather than on scroll. Children rise in sequence; the heading
   gets a touch longer to land so it reads as the anchor of the group. */
.hero-inner > *{animation:riseIn .8s var(--ease-out) both;}
.hero-inner > h1{animation-delay:.13s;animation-duration:.9s;}
.hero-inner > p{animation-delay:.22s;}
.hero-inner > .hero-actions{animation-delay:.3s;}
.hero-inner > .chip-row{animation-delay:.38s;}
@keyframes riseIn{from{opacity:0;transform:translateY(20px);}to{opacity:1;transform:none;}}

/* No bloom, no glow, no frosted panel behind the hero copy. There used to be
   a 22s animated radial gold gradient here, and it read as a big soft blurred
   box sitting behind the headline rather than as lighting - removed on owner
   request (2026-09-23). The hero is now just the photograph, the flat scrim
   that keeps the text readable, and the type. If the copy ever needs more
   separation, darken the scrim ramp in #view-home .hero::before; do not put a
   panel, a border or a backdrop-filter back behind .hero-inner. */

/* The gradient word in the hero heading gets a slow shine travelling
   through it - the one piece of pure decoration on the page. */
.hero h1 .grad{
  background:linear-gradient(100deg,#a97e1f 0%,#e8c86a 42%,#fff3cf 50%,#e8c86a 58%,#a97e1f 100%);
  background-size:220% 100%;
  -webkit-background-clip:text;background-clip:text;color:transparent;
  animation:shine 7s ease-in-out infinite;
}
@keyframes shine{0%,100%{background-position:120% 0;}50%{background-position:-20% 0;}}

/* ── nav: condense on scroll ───────────────────────────────────────────
   ui.js toggles .is-stuck past ~30px. The bar starts invisible over the
   hero and materialises into a solid surface as you leave it, instead of
   sitting there as a permanent box. */
.nav-shell{transition:padding var(--dur) var(--ease-out);}
.nav-shell::before{
  content:"";position:absolute;inset:0;z-index:-1;
  background:rgb(7 7 10 / 95%);
  border-bottom:1px solid var(--border-soft);
  opacity:0;transition:opacity var(--dur) var(--ease-out);
}
.nav-shell.is-stuck{padding-top:10px;}
.nav-shell.is-stuck::before{opacity:1;}

/* ── buttons ───────────────────────────────────────────────────────────
   A single sheen sweep on hover. ::before, not ::after - .btn.is-busy
   already owns ::after for its spinner. */
.btn{position:relative;overflow:hidden;transition:transform var(--dur-fast) var(--ease-out),filter var(--dur-fast) var(--ease-out),background var(--dur-fast) var(--ease-out),border-color var(--dur-fast) var(--ease-out),color var(--dur-fast) var(--ease-out);}
.btn::before{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(100deg,transparent 20%,rgb(255 255 255 / 28%) 50%,transparent 80%);
  transform:translateX(-110%);
  transition:transform .6s var(--ease-out);
}
.btn:hover::before{transform:translateX(110%);}
.btn-secondary::before,.btn-ghost::before,.btn-danger::before{
  background:linear-gradient(100deg,transparent 20%,rgb(212 175 55 / 20%) 50%,transparent 80%);
}
.btn:hover{transform:translateY(-1px);}
/* Press feedback. :active must win over :hover, so it comes after. */
.btn:active{transform:translateY(0) scale(.978);transition-duration:.08s;}
.btn.is-busy::before{display:none;}

/* ── cards ─────────────────────────────────────────────────────────────
   .card-glow's gradient wash was declared with opacity:0 and a transition
   but nothing ever raised it, so the rule never did anything. Wired to
   hover here. */
.card{transition:transform var(--dur) var(--ease-out),border-color var(--dur) var(--ease-out),box-shadow var(--dur) var(--ease-out);}
.card-glow::before{opacity:0;transition:opacity var(--dur) var(--ease-out);}
.card-glow:hover::before{opacity:.09;}

 /* perk-card had no hover state at all - it's a grid tile like the others,
   so it should answer the cursor like the others. */
.perk-card{transition:transform var(--dur) var(--ease-out),border-color var(--dur) var(--ease-out),box-shadow var(--dur) var(--ease-out);}
.perk-card:hover{transform:translateY(-3px);border-color:rgb(212 175 55 / 45%);box-shadow:var(--shadow);}
.perk-card img{transition:transform var(--dur-slow) var(--ease-out);}
.perk-card:hover img{transform:scale(1.05);}

/* ── feature cards (the game-mode art) ────────────────────────────────
   The art zoom was on --transition (.18s), which is button-speed and made
   a full-card image jolt. A photograph wants to drift. */
.feature-card{transition:transform var(--dur) var(--ease-out),border-color var(--dur) var(--ease-out),box-shadow var(--dur) var(--ease-out);}
.feature-card.art::before{transition:transform var(--dur-slow) var(--ease-out),filter var(--dur-slow) var(--ease-out);}
.feature-card.art:hover::before{transform:scale(1.07);}
.feature-content{transition:transform var(--dur) var(--ease-out);}
.feature-card:hover .feature-content{transform:translateY(-3px);}
.feature-icon{transition:transform var(--dur) var(--ease-out),color var(--dur) var(--ease-out),border-color var(--dur) var(--ease-out);}
.feature-card:hover .feature-icon{transform:scale(1.1) rotate(-6deg);border-color:var(--gold);}

/* ── list rows / tiers ─────────────────────────────────────────────────
   Rows nudge toward the reader on hover - enough to show the row is live
   without shifting the column's rhythm. */
.lb-row{transition:transform var(--dur-fast) var(--ease-out),background var(--dur-fast) var(--ease-out);}
.lb-row:hover{transform:translateX(4px);background:rgb(212 175 55 / 6%);}
.tier-node{transition:transform var(--dur) var(--ease-out),border-color var(--dur) var(--ease-out);}
.tier-node:hover{transform:translateY(-4px);}

/* ── progress bars ─────────────────────────────────────────────────────
   Width is set inline by the renderer, so transitioning width here means
   every bar grows into place on first paint for free. */
.xp-fill{transition:width 1.1s var(--ease-out);position:relative;overflow:hidden;}
.xp-fill::after{
  content:"";position:absolute;inset:0;
  background:linear-gradient(90deg,transparent,rgb(255 255 255 / 45%),transparent);
  transform:translateX(-100%);
  animation:xpSheen 2.6s ease-in-out infinite;
}
@keyframes xpSheen{0%{transform:translateX(-100%);}55%,100%{transform:translateX(100%);}}

/* ── modal / drawer ───────────────────────────────────────────────────*/
.modal-scrim{opacity:0;transition:opacity var(--dur) var(--ease-out);}
.modal.open .modal-scrim{opacity:1;}
.modal-card{transition:transform .34s var(--ease-out),opacity .34s var(--ease-out);opacity:0;}
.modal.open .modal-card{opacity:1;}

/* ── route transitions ────────────────────────────────────────────────
   Replaces the old 6px fadeIn with something that matches the reveal
   easing, so arriving on a page and scrolling it feel like one system. */
.view{animation:viewIn .42s var(--ease-out);}
@keyframes viewIn{from{opacity:0;transform:translateY(10px);}to{opacity:1;transform:none;}}

/* ── focus ────────────────────────────────────────────────────────────
   Keyboard users get a real gold ring. :focus-visible only, so clicking a
   button doesn't leave a ring behind. */
a:focus-visible,button:focus-visible,input:focus-visible,select:focus-visible,
textarea:focus-visible,[tabindex]:focus-visible{
  outline:2px solid var(--gold);
  outline-offset:2px;
  border-radius:var(--r);
}

/* ── toasts ───────────────────────────────────────────────────────────*/
.toast{animation:slideIn .3s var(--ease-out);}

@media (prefers-reduced-motion:reduce){
  .skel{animation:none;}
  .reveal,html.js .reveal{opacity:1;transform:none;animation:none;transition:none;}
  /* Kill every looping/decorative animation added in the MOTION section.
      Hover feedback stays - it's a response to the user's own input, not
      unrequested motion - but it stops moving things around. */
  .hero-inner > *,
  .hero h1 .grad,
  .gold-fx,
  .gold-stars,
  .xp-fill::after,
  .view,
  .toast{animation:none;}
  .hero h1 .grad{background:var(--grad-1);-webkit-background-clip:text;background-clip:text;}
  .btn::before{display:none;}
  .btn:hover,.btn:active,
  .card:hover,.perk-card:hover,.feature-card:hover,
  .lb-row:hover,.tier-node:hover,
  .feature-card:hover .feature-content,
  .feature-card:hover .feature-icon,
  .feature-card.art:hover::before,
  .perk-card:hover img,
  .item-card:hover .item-card-img,
  .card-tile:hover img,
  .card-tile:hover video{transform:none;}
  .xp-fill{transition:none;}
  .modal-card,.modal-scrim{transition:opacity .01s;}
}

/* Settings → "Reduce motion". The media query above is the OS-level switch;
   this is the in-app one, set on <html> from localStorage before initMotion()
   runs (see applyLocalPrefs in app.js). Kept as a blanket override rather than
   a copy of the list above so a new animation can't quietly escape it - the
   list above is the tuned version for people whose OS already asked. */
html.no-motion *,
html.no-motion *::before,
html.no-motion *::after{
  animation-duration:.001ms!important;
  animation-iteration-count:1!important;
  transition-duration:.001ms!important;
}
html.no-motion{scroll-behavior:auto!important;}
html.no-motion .reveal,
html.no-motion.js .reveal{opacity:1!important;transform:none!important;}
html.no-motion .btn::before{display:none;}
html.no-motion .hero h1 .grad{background:var(--grad-1);-webkit-background-clip:text;background-clip:text;}
html.no-motion .gold-stars{animation:none;}

/* ── settings page ────────────────────────────────────────────────────── */
.settings-group{margin-top:30px;}
.settings-group:first-of-type{margin-top:0;}
.settings-group > h3{font-size:12px;letter-spacing:.14em;text-transform:uppercase;
  color:var(--text-subtle);font-weight:800;margin-bottom:4px;}
.settings-group > .group-hint{font-size:12px;color:var(--text-subtle);margin-bottom:10px;}

/* ── pfp / banner upload ──────────────────────────────────────────────── */
/* The file input is hidden but must stay focusable and in the a11y tree, so
   it's clipped rather than display:none - the latter drops it from tab order
   and from the label's for= association. */
.visually-hidden{position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0;}

.img-upload{display:flex;align-items:flex-start;gap:14px;padding:14px 0;
  border-bottom:1px solid var(--border-soft);}
.img-upload.busy{opacity:.55;pointer-events:none;}
.img-upload-preview{flex-shrink:0;background:var(--bg-3) center/cover no-repeat;
  border:1px solid var(--border);display:grid;place-items:center;
  font-size:11px;font-weight:700;color:var(--text-subtle);text-align:center;}
.img-upload-preview.banner{width:150px;height:auto;aspect-ratio:2.4 / 1;border-radius:var(--r);background-size:cover;background-position:center;}
.img-upload-preview.avatar{width:68px;height:auto;aspect-ratio:1;border-radius:50%;font-size:18px;
  font-family:'Space Grotesk',sans-serif;color:var(--gold);}
.img-upload-preview.is-empty{border-style:dashed;}
.img-upload-preview.banner.is-empty span{padding:0 8px;line-height:1.3;}

/* min-width:0 so a long error message wraps instead of pushing the preview
   out of the row. */
.img-upload-meta{min-width:0;flex:1;}
.img-upload-meta label{display:block;font-size:11.5px;font-weight:800;
  text-transform:uppercase;letter-spacing:.5px;color:var(--text-subtle);margin-bottom:4px;}
.img-upload-meta .field-hint{margin-top:0;}
.img-upload-actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:9px;}
.img-upload-meta .field-err:empty{display:none;}

@media (max-width:520px){
  .img-upload{flex-wrap:wrap;}
  .img-upload-preview.banner{width:100%;height:auto;aspect-ratio:2.4 / 1;}
  .pd-head{gap:10px;padding-inline:16px;}
  .pd-avatar{width:68px;height:68px;margin-top:-32px;}
  .pd-name-row{display:block;}
  .pd-badges{padding-top:6px;}
  .pd-body{padding-inline:16px;}
  .profile-info{min-width:0;flex:1 1 100%;}
  .profile-info h2{font-size:19px;}
}
.setting-row .setting-label{min-width:0;padding-right:14px;}
.settings-divider{height:1px;background:var(--border-soft);margin:30px 0 0;}
.kind-chips{display:flex;gap:8px;flex-wrap:wrap;padding:12px 0 2px;}
/* A muted kind reads as "off", so the ACTIVE (gold) state is "you get these"
   - inverted from .chip's usual "selected" meaning, hence the explicit class
   rather than reusing .chip.active alone. */
.kind-chip.is-muted{opacity:.5;text-decoration:line-through;}
.rename-locked{display:flex;gap:8px;align-items:flex-start;background:var(--bg-3);
  border:1px solid var(--border);border-radius:var(--r);padding:11px 13px;
  font-size:12px;color:var(--text-muted);margin-top:8px;}

/* ===== HOME POLISH ===== */
body{
  background:
    radial-gradient(circle at 50% -10%,rgb(212 175 55 / 7%),transparent 34rem),
    var(--bg);
}

#view-home .hero{
  min-height:min(760px,78svh);
  padding:132px 20px 72px;
  background-position:center 38%;
}
#view-home .hero::before{
  z-index:0;
  background:linear-gradient(180deg,rgb(0 0 0 / 46%) 0%,rgb(0 0 0 / 28%) 40%,rgb(0 0 0 / 90%) 100%);
}
 /* Unpadded and unboxed. This element used to carry 34px of inset padding,
   which - over the hero photograph and next to the side vignette that used to
   live on ::before - read as a translucent card floating in the middle of the
   hero. It is now a plain centred text column. */
#view-home .hero-inner{
  position:relative;
  z-index:1;
  width:min(100%,740px);
  max-width:740px;
  padding:0;
}
.hero-kicker{
  position:relative;
  z-index:1;
  margin-bottom:14px;
  color:var(--gold);
  font-size:11px;
  font-weight:800;
  letter-spacing:.14em;
  text-transform:uppercase;
}
#view-home .hero h1,
#view-home .hero p,
#view-home .hero-actions,
#view-home .platform-switch,
#view-home .hero-trust,
#view-home .hero-chips{
  position:relative;
  z-index:1;
}
#view-home .hero h1{
  max-width:620px;
  margin-inline:auto;
  font-size:clamp(38px,6vw,64px);
  line-height:1;
  letter-spacing:-.045em;
  text-wrap:balance;
}
#view-home .hero p{
  max-width:510px;
  margin-bottom:22px;
  color:rgb(245 243 238 / 78%);
}
#view-home .platform-switch{
  margin-bottom:14px;
  background:rgb(0 0 0 / 45%);
  box-shadow:inset 0 1px 0 rgb(255 255 255 / 5%);
}
.hero-trust{
  display:flex;
  justify-content:center;
  align-items:center;
  gap:16px;
  flex-wrap:wrap;
  margin-top:18px;
  color:rgb(245 243 238 / 60%);
  font-size:11px;
  font-weight:700;
}
.hero-trust span{
  display:inline-flex;
  align-items:center;
  gap:6px;
}
.trust-dot{
  display:inline-block;
  width:6px;
  height:6px;
  border-radius:50%;
  background:var(--gold);
  box-shadow:0 0 0 3px rgb(212 175 55 / 14%);
}
.section-kicker{
  margin-bottom:8px;
  color:var(--gold);
  font-size:10px;
  font-weight:800;
  letter-spacing:.14em;
  text-transform:uppercase;
}
#view-home .home-stats-section{
  padding-top:32px;
  padding-bottom:24px;
}
#view-home .home-stats-section .stat-row{
  display:grid;
  grid-template-columns:repeat(4,minmax(0,1fr));
  gap:12px;
}
#view-home .stat-pill{
  min-width:0;
  background:linear-gradient(145deg,rgb(18 17 16 / 96%),rgb(10 10 10 / 96%));
  box-shadow:inset 0 1px 0 rgb(255 255 255 / 4%);
}
#view-home .stat-pill .value{
  font-size:24px;
}
#view-home .stat-pill .subtext{
  line-height:1.4;
}
#view-home .feature-card{
  min-height:224px;
}
#view-home .section{
  scroll-margin-top:80px;
}
#view-home #homeSteps .perk-card{
  min-height:168px;
  padding:20px;
  background:linear-gradient(145deg,var(--bg-2),rgb(15 14 12));
}
#view-home #homeSteps .perk-card h4{
  margin-top:14px;
  font-size:15px;
}
#view-home #homeSteps .perk-card p{
  line-height:1.55;
}
#view-home #homeFaq{
  padding:8px 20px;
  background:linear-gradient(145deg,var(--bg-2),rgb(12 11 10));
}
#view-home > .page > .section:last-child .card{
  border-radius:24px;
  box-shadow:0 18px 50px rgb(0 0 0 / 28%),inset 0 1px 0 rgb(255 255 255 / 7%);
}

@media (max-width:760px){
  .nav-shell{padding:10px 12px 0;}
  .nav-inner{gap:8px;}
  .brand-mark{width:36px;height:36px;}
  .brand-mark .icon{width:24px;height:24px;}
  .nav-right{gap:6px;}
  .nav-right > a.icon-btn[href="#/settings"]{display:none;}
  .icon-btn,.avatar-btn{width:35px;height:35px;}
  .drawer{top:58px;right:12px;width:calc(100vw - 24px);max-width:320px;}

  #view-home .hero{
    width:100%;
    margin-left:0;
    margin-top:-52px;
    min-height:min(760px,88svh);
    padding:92px 14px 38px;
    background-position:62% center;
  }
  #view-home .hero::before{
    background:linear-gradient(180deg,rgb(0 0 0 / 60%) 0%,rgb(0 0 0 / 30%) 34%,rgb(0 0 0 / 92%) 100%);
  }
  #view-home .hero-inner{
    padding:0;
  }
  .hero-kicker{font-size:9.5px;letter-spacing:.12em;margin-bottom:12px;}
  #view-home .hero h1{
    font-size:clamp(34px,10.5vw,46px);
    line-height:1.02;
  }
  #view-home .hero p{
    font-size:14px;
    line-height:1.55;
    margin-bottom:18px;
  }
  /* Natural width, centred - not a full-bleed bar. Stretched to 100% this
     reads as a second nav and drags the eye off the headline; two words in a
     pill only need the room the words take. 40px min-height keeps the tap
     target, so the padding is what gives way. */
  #view-home .platform-switch{
    display:inline-flex;
    width:auto;
    max-width:100%;
    gap:2px;
    padding:3px;
  }
  #view-home .platform-tab{
    flex:0 0 auto;
    min-height:40px;
    padding:7px 16px;
    font-size:11.5px;
  }
  /* Keep the two primary hero actions side by side on mobile; the desktop
     buttons are naturally sized, so don't turn them into full-width rows. */
  #view-home .hero-actions{
    flex-direction:row;
    flex-wrap:nowrap;
    gap:8px;
  }
  #view-home .hero-actions .btn{
    width:auto;
    min-height:44px;
    padding-inline:10px;
    gap:6px;
    font-size:12px;
  }
  .hero-trust{
    gap:9px 13px;
    margin-top:16px;
    font-size:10px;
  }
  #view-home #heroChips{margin-top:18px!important;}
  #view-home .page{
    padding-inline:14px;
    padding-bottom:34px;
  }
  #view-home .section{padding:32px 0;}
  #view-home .home-stats-section{padding-top:26px;padding-bottom:16px;}
  #view-home .home-stats-section .stat-row{
    grid-template-columns:repeat(2,minmax(0,1fr));
    gap:10px;
  }
  #view-home .stat-pill{
    padding:13px 12px;
    border-radius:15px;
  }
  #view-home .stat-pill .value{font-size:20px;}
  #view-home .stat-pill .subtext{font-size:11px;}
  #view-home .section-head{
    align-items:flex-start;
    gap:14px;
    margin-bottom:15px;
  }
  #view-home .section-head h2{font-size:21px;}
  #view-home .section-head p{font-size:12px;line-height:1.4;}
  #view-home .section-head > .btn{
    width:100%;
    min-height:40px;
  }
  #view-home .grid-4{
    grid-template-columns:repeat(2,minmax(0,1fr));
    gap:10px;
  }
  /* The global small-screen grid collapses to one column, but the character
     roster should stay a two-up collection on phones. */
  #view-characters #charGrid{
    grid-template-columns:repeat(2,minmax(0,1fr));
    gap:10px;
  }
  #view-home .feature-card{
    min-height:190px;
    border-radius:17px;
  }
  #view-home .feature-content{padding:14px;}
  #view-home .feature-content h3{font-size:14px;}
  #view-home .feature-content p{font-size:11px;line-height:1.42;}
  #view-home .feature-icon{top:10px;right:10px;width:29px;height:29px;}
  #view-home #homeSteps{
    grid-template-columns:1fr;
    gap:10px;
  }
  #view-home #homeSteps .perk-card{
    min-height:0;
    padding:16px;
  }
  #view-home #homeSteps .perk-card h4{margin-top:10px;}
  #view-home #homeFaq{padding:6px 14px;}
  #view-home .faq-q{padding:15px 0;font-size:13px;line-height:1.4;}
  #view-home > .page > .section:last-child .card{padding:24px 16px!important;}
}

@media (max-width:380px){
  #view-home .hero-inner{padding-inline:13px;}
  #view-home .hero h1{font-size:32px;}
  #view-home .feature-card{min-height:176px;}
  #view-home .feature-content p{font-size:10.5px;}
  .hero-trust{font-size:9.5px;gap:8px 10px;}
}

/* ── golden text: the same effect as the hero's "WhatsApp" word ───────── */
.gold-fx{
  background:linear-gradient(100deg,#a97e1f 0%,#e8c86a 42%,#fff3cf 50%,#e8c86a 58%,#a97e1f 100%);
  background-size:220% 100%;
  -webkit-background-clip:text;background-clip:text;color:transparent;
  animation:shine 7s ease-in-out infinite;
}
html.no-motion .gold-fx{animation:none;background:var(--grad-1);-webkit-background-clip:text;background-clip:text;}
.season-hero h1.gold-fx{font-weight:800;}
.hero-badge{display:inline-flex;align-items:center;gap:8px;padding:5px 12px;margin-bottom:16px;border-radius:999px;
  font-size:12px;font-weight:700;color:var(--gold);background:rgb(212 175 55 / 10%);border:1px solid rgb(212 175 55 / 35%);}
.hero-badge .icon{width:14px;height:14px;}

/* Star rows: the base gold gradient, then a stronger variant per star level.
   The stars are a mask, not the ★ character: U+2605 is rendered out of the
   colour-emoji font on some Android builds, which put a row of picture-stars
   into a site where every other mark is line art. A mask keeps the gradient
   fill and the shine animation the text version had, and `--n` is the count. */
.gold-stars{display:inline-block;vertical-align:-.1em;font-size:16px;
  width:calc(var(--n, 1) * 1.06em);height:1.06em;
  background:var(--grad-1);
  -webkit-mask-image:var(--star-mask);mask-image:var(--star-mask);
  -webkit-mask-repeat:repeat-x;mask-repeat:repeat-x;
  -webkit-mask-size:1.06em 1.06em;mask-size:1.06em 1.06em;
  -webkit-mask-position:left center;mask-position:left center;}
.gold-stars.gs-2{filter:drop-shadow(0 0 3px rgb(212 175 55 / 35%));}
.gold-stars.gs-3{background-image:var(--grad-gold);filter:drop-shadow(0 0 5px rgb(232 200 106 / 50%));}
.gold-stars.gs-4,.gold-stars.gs-5,.gold-stars.gs-6{
  background-image:linear-gradient(100deg,#a97e1f 0%,#e8c86a 42%,#fff3cf 50%,#e8c86a 58%,#a97e1f 100%);
  background-size:220% 100%;animation:shine 6s ease-in-out infinite;}
.gold-stars.gs-4{filter:drop-shadow(0 0 6px rgb(232 200 106 / 55%));}
.gold-stars.gs-5{animation-duration:4s;filter:drop-shadow(0 0 8px rgb(240 213 133 / 65%));}
.gold-stars.gs-6{animation:shine 3s ease-in-out infinite,starPulse 2.2s ease-in-out infinite;
  background-image:linear-gradient(100deg,#c9962e 0%,#ffe9a8 40%,#ffffff 50%,#ffe9a8 60%,#c9962e 100%);}
@keyframes starPulse{0%,100%{filter:drop-shadow(0 0 5px rgb(232 200 106 / 45%));}50%{filter:drop-shadow(0 0 13px rgb(255 232 150 / 85%));}}

/* Shared art frame: fixed ratio, so a gif, a still and a video all fit the same box. */
.media-frame{position:relative;width:100%;overflow:hidden;border-radius:var(--r);margin-bottom:11px;
  background:var(--bg-3);display:flex;align-items:center;justify-content:center;}
.media-frame>img,.media-frame>video{width:100%;height:100%;object-fit:cover;display:block;
  transition:transform var(--dur-slow) var(--ease-out);}
.media-frame[data-kind="gif"]>img{image-rendering:auto;}
.media-frame>.icon,.media-frame>svg{width:38%;height:auto;opacity:.6;}
.char-card:hover .media-frame>*{transform:scale(1.04);}

.char-card{display:flex;flex-direction:column;text-align:left;}
.char-stars{margin-bottom:6px;}
.char-lore{display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden;margin-top:2px;}
.char-status{margin-top:auto;padding-top:12px;}

/* ── character details: show the collectible card, not a player-profile header ── */
.cc-wrap{padding:20px 18px 2px;display:flex;flex-direction:column;align-items:center;}
.cc-card{position:relative;width:min(250px,70%);aspect-ratio:2/3;border-radius:20px;overflow:hidden;
  background:linear-gradient(160deg,#241d0e,#0c0a06);border:1px solid rgb(212 175 55 / 35%);}
.cc-card.cc-r-legendary,.cc-card.cc-r-mythic{border-color:rgb(212 175 55 / 75%);}
.cc-card.cc-r-epic,.cc-card.cc-r-special{border-color:rgb(168 85 247 / 55%);}
.cc-card.cc-r-rare{border-color:rgb(96 165 250 / 50%);}
.cc-img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block;}
.cc-ph{position:absolute;inset:0;display:grid;place-items:center;}
.cc-ph svg{width:38%;height:auto;opacity:.5;}
.cc-shade{position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(to top,rgb(0 0 0 / 90%) 0%,rgb(0 0 0 / 45%) 24%,transparent 52%);}
.cc-meta{position:absolute;left:0;right:0;bottom:0;padding:14px;}
.cc-meta h3{margin:0;font:800 20px/1.15 'Space Grotesk',Inter,sans-serif;overflow-wrap:anywhere;}
.cc-meta p{margin:4px 0 0;font-size:12px;font-weight:700;color:var(--gold);}
.cc-flag{position:absolute;top:10px;left:10px;padding:3px 10px;border-radius:999px;font-size:10.5px;font-weight:800;
  background:rgb(0 0 0 / 60%);color:var(--text);border:1px solid var(--border);}
.cc-flag.is-eq{color:var(--gold);border-color:rgb(212 175 55 / 45%);}
.cc-price{margin:12px 0 0;font-size:12.5px;color:var(--text-muted);text-align:center;}
.cc-wrap+.pd-body{padding-top:14px;}

/* ── home: WhatsApp community cards with compact image thumbnails ───────── */
.home-group-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:14px;}
.home-group-card{display:flex;align-items:center;gap:13px;min-width:0;padding:13px;border:1px solid var(--border);
  border-radius:var(--r-lg);background:linear-gradient(145deg,var(--bg-2),#090807);transition:var(--transition);}
.home-group-card:hover{border-color:rgb(212 175 55 / 58%);transform:translateY(-3px);box-shadow:var(--shadow);}
.home-group-thumb{position:relative;display:grid;place-items:center;width:78px;aspect-ratio:1;height:auto;flex:0 0 78px;overflow:hidden;
  border:1px solid rgb(212 175 55 / 24%);border-radius:12px;
  background:linear-gradient(145deg,rgb(212 175 55 / 15%),var(--bg-3));}
.home-group-thumb img{position:absolute;inset:0;display:block;width:100%;height:100%;object-fit:cover;transition:transform var(--transition);}
.home-group-card:hover .home-group-thumb img{transform:scale(1.06);}
.home-group-content{display:flex;flex:1;flex-direction:column;align-items:flex-start;min-width:0;}
.home-group-label{margin-bottom:5px;color:var(--gold);font-size:9px;font-weight:800;letter-spacing:.09em;text-transform:uppercase;}
.home-group-content h3{font-size:15px;font-weight:800;line-height:1.25;overflow-wrap:anywhere;}
.home-group-join{margin-top:11px;}
.home-group-join .icon{width:14px;height:14px;}

@media (max-width:980px){.home-group-grid{grid-template-columns:repeat(2,minmax(0,1fr));}}
@media (max-width:620px){
  .home-group-grid{grid-template-columns:minmax(0,1fr);gap:10px;}
  .home-group-card{gap:12px;padding:11px;}
  .home-group-thumb{width:72px;height:auto;flex-basis:72px;}
}

/* ══════════════════════════════════════════════════════════════════════════
   MOBILE EXPERIENCE (≤ 820px)
   Mirrors assets/js/mobile-pages.js. Above the breakpoint none of this
   exists: the desktop site keeps its curved pill nav, its hero photograph and
   its regular page set, unchanged.

   Three things live down here:
     1. the floating bottom bar that replaces the pill nav on a phone;
     2. the season event carousel that replaces the season hero on a phone;
     3. the phone-only pages (Wallet, Storage, Progress) and the inventory
      manager that opens from the profile.
   ══════════════════════════════════════════════════════════════════════════ */

/* Drawer group heading + the phone-only links inside it. The hairline above
   the label is what separates "the site" from "this phone" - without it the
   heading was just a smaller grey row in the same list. */
.drawer-group{margin-top:4px;padding:14px 14px 6px;font-size:10px;font-weight:800;letter-spacing:.09em;
  text-transform:uppercase;color:var(--text-subtle);
  border-top:1px solid var(--border-soft);}
/* The account action closes the menu, so it gets its own divider and air
   instead of trailing the last route link. */
#drawerAuth{margin-top:4px;padding-top:16px;border-top:1px solid var(--border-soft);border-radius:0 0 var(--r) var(--r);}
@media (min-width:821px){.drawer-link-mobile,.drawer-group{display:none;}}

/* ── floating bottom bar ────────────────────────────────────────────────── */
.mobile-bar{display:none;}

@media (max-width:820px){
  .mobile-bar{
    position:fixed;left:50%;bottom:calc(10px + env(safe-area-inset-bottom,0px));
    transform:translateX(-50%);
    z-index:95;
    display:flex;align-items:stretch;gap:2px;
    width:min(calc(100vw - 24px),460px);
    padding:6px;
    border:1px solid rgb(212 175 55 / 18%);
    border-radius:22px;
    /* Solid, not glass. This bar is on screen for the whole visit while the
       page scrolls underneath it, so it was the most expensive blur on the
       site - re-rasterised on every frame a phone could least afford it. */
    background:rgb(10 10 10 / 97%);
    box-shadow:0 14px 34px rgb(0 0 0 / 62%),0 0 0 1px rgb(255 255 255 / 4%) inset;
    /* Sits above the page but below the modal (120) and toasts (200). */
  }
  /* Every page needs room under it or the last card hides behind the bar. */
  .view.active{padding-bottom:96px;}

  .mbar-slot{
    position:relative;flex:1 1 0;min-width:0;
    display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;
    padding:8px 2px 6px;
    border:0;border-radius:17px;background:none;
    color:var(--text-muted);font:inherit;font-size:10.5px;font-weight:700;
    text-decoration:none;cursor:pointer;
    -webkit-tap-highlight-color:transparent;
    transition:color .18s ease,background .18s ease,transform .12s ease;
  }
  .mbar-slot:active{transform:scale(.94);}
  .mbar-ico{display:flex;align-items:center;justify-content:center;width:22px;height:22px;}
  .mbar-ico .icon{width:21px;height:21px;stroke-width:1.7;}
  .mbar-label{line-height:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%;}
  .mbar-slot.is-active{color:var(--gold);background:rgb(212 175 55 / 12%);}
  /* A hairline gold cap over the active slot: reads as "you are here" while
     still letting the bar sit flat against the page. */
  .mbar-slot.is-active::before{
    content:"";position:absolute;top:0;left:50%;transform:translateX(-50%);
    width:18px;height:2px;border-radius:2px;background:var(--gold);
  }
  .mbar-slot:focus-visible{outline:2px solid var(--gold);outline-offset:-2px;}
}

/* ── season: the event carousel replaces the hero on a phone ───────────── */
.mob-events{display:none;}

@media (max-width:820px){
  .mob-events{display:block;padding:16px 0 4px;}
  /* The photograph used to be the whole above-the-fold on a phone. The
     carousel says three things instead of one, so the photo steps aside. */
  #view-season .season-hero{display:none;}
}

.ev-slider{position:relative;}
.ev-track{
  display:flex;gap:12px;
  overflow-x:auto;overflow-y:hidden;
  scroll-snap-type:x mandatory;
  scrollbar-width:none;-ms-overflow-style:none;
  padding:0 20px 6px;
  /* One slide per "page" of the scroller, with the next one peeking in so it
     is obvious there is more to swipe. */
  scroll-padding-left:20px;
}
.ev-track::-webkit-scrollbar{display:none;}
.ev-track:focus-visible{outline:2px solid var(--gold);outline-offset:4px;border-radius:var(--r-lg);}

.ev-slide{
  flex:0 0 min(86%,360px);
  scroll-snap-align:center;
  display:flex;flex-direction:column;
  border:1px solid var(--border);border-radius:var(--r-xl);overflow:hidden;
  background:linear-gradient(160deg,var(--bg-2),#070707);
}
.ev-media{position:relative;aspect-ratio:16/10;overflow:hidden;background:var(--bg-3);}
.ev-media img{width:100%;height:100%;object-fit:cover;object-position:center 22%;display:block;}
.ev-veil{position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(to top,rgb(0 0 0 / 92%) 0%,rgb(0 0 0 / 30%) 38%,transparent 68%);}
.ev-badge{
  position:absolute;top:10px;left:10px;z-index:1;
  padding:4px 10px;border-radius:999px;
  font-size:9.5px;font-weight:800;letter-spacing:.07em;text-transform:uppercase;
  color:var(--gold);background:rgb(0 0 0 / 62%);border:1px solid rgb(212 175 55 / 45%);
}
.ev-body{padding:4px 15px 16px;margin-top:-26px;position:relative;z-index:1;display:flex;flex-direction:column;gap:8px;}
.ev-title{font-size:20px;font-weight:800;line-height:1.15;}
.ev-desc{font-size:12.5px;line-height:1.55;color:var(--text-muted);}
.ev-rewards{display:flex;flex-wrap:wrap;gap:7px;margin-top:2px;}
.ev-reward{
  display:inline-flex;align-items:center;gap:6px;
  padding:5px 11px;border-radius:999px;
  font-size:12px;font-weight:800;color:var(--gold);
  background:rgb(212 175 55 / 12%);border:1px solid rgb(212 175 55 / 32%);
}
.ev-cta{align-self:flex-start;margin-top:4px;}

.ev-dots{display:flex;justify-content:center;gap:7px;padding:2px 0 10px;}
.ev-dot{width:7px;height:7px;padding:0;border:0;border-radius:999px;background:rgb(255 255 255 / 20%);cursor:pointer;
  transition:width .22s var(--ease-out),background .22s var(--ease-out);}
.ev-dot.is-on{width:20px;background:var(--gold);}
.ev-dot:focus-visible{outline:2px solid var(--gold);outline-offset:3px;}

/* Full-bleed event preview sheet. */
.ev-sheet{display:flex;flex-direction:column;}
.ev-sheet-art{position:relative;aspect-ratio:4/5;max-height:56svh;overflow:hidden;background:var(--bg-3);}
.ev-sheet-art img{width:100%;height:100%;object-fit:cover;object-position:center 20%;display:block;}
.ev-sheet-art .ev-badge{top:12px;left:12px;}

/* Thank-you popup shown after a confirmed Halloween claim. Keep the full image
   visible instead of cropping the artwork, then let the message own the focus. */
.halloween-thanks{display:flex;flex-direction:column;overflow:hidden;}
.halloween-thanks-art{
  display:grid;place-items:center;aspect-ratio:16/10;max-height:44svh;overflow:hidden;
  background:radial-gradient(circle at 50% 35%,rgb(212 175 55 / 12%),transparent 65%),#080706;
}
.halloween-thanks-art img{display:block;width:100%;height:100%;object-fit:contain;}
.halloween-thanks-copy{
  display:flex;flex-direction:column;align-items:center;gap:10px;
  padding:22px 24px 24px;text-align:center;
}
.halloween-thanks-kicker{font-size:10px;font-weight:800;letter-spacing:.13em;text-transform:uppercase;color:var(--gold);}
.halloween-thanks-copy h2{font-size:clamp(21px,4vw,27px);line-height:1.12;}
.halloween-thanks-copy p{max-width:38ch;margin:0;font-size:13px;line-height:1.65;color:var(--text-muted);}
.halloween-thanks-copy .btn{min-width:132px;justify-content:center;margin-top:3px;}
@media (max-width:520px){
  .halloween-thanks-art{aspect-ratio:4/3;max-height:42svh;}
  .halloween-thanks-copy{padding:18px 16px 20px;}
}

/* ── phone-only pages ──────────────────────────────────────────────────── */
.mob-view{/* phone-only route; the router bounces these on a wide viewport */}
.mob-page{padding-top:14px;}
.mob-head{padding:8px 0 16px;}
.mob-head h1{font-size:26px;font-weight:800;line-height:1.15;}
.mob-head p{margin-top:6px;font-size:13px;line-height:1.5;color:var(--text-muted);}
.mob-sec{margin-bottom:24px;}
.sec-head{display:flex;align-items:baseline;justify-content:space-between;gap:12px;margin-bottom:12px;}
.sec-head h2{font-size:16px;font-weight:800;}
.sec-head .subtext{font-size:11.5px;}
.mgr-note{margin:10px 0 0;font-size:11.5px;line-height:1.5;color:var(--text-subtle);}

/* ── wallet ────────────────────────────────────────────────────────────── */
.wallet-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;margin-bottom:18px;}
.wallet-card{display:flex;flex-direction:column;gap:3px;padding:13px 14px;
  border:1px solid var(--border);border-radius:var(--r-lg);background:var(--bg-2);}
.wallet-ico{color:var(--gold);display:flex;margin-bottom:3px;}
.wallet-k{font-size:10.5px;font-weight:800;letter-spacing:.05em;text-transform:uppercase;color:var(--text-subtle);}
.wallet-v{font-size:19px;font-weight:800;font-family:'Space Grotesk',sans-serif;}
.wallet-u{font-size:11px;color:var(--text-subtle);}
.wallet-loan{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:18px;
  border-color:rgb(212 175 55 / 40%);}
.wallet-form{display:flex;flex-direction:column;gap:8px;}
.wallet-form label{font-size:11.5px;font-weight:700;color:var(--text-muted);}
.wallet-form input{width:100%;padding:12px 13px;border-radius:var(--r);border:1px solid var(--border);
  background:var(--bg-3);color:var(--text);font:inherit;font-size:15px;}
.wallet-form input:focus{outline:none;border-color:var(--gold);}
.wallet-btns{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;margin-top:2px;}
.ledger{display:flex;flex-direction:column;border:1px solid var(--border);border-radius:var(--r-lg);
  background:var(--bg-2);overflow:hidden;}
.ledger-row{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:11px 14px;
  border-bottom:1px solid var(--border-soft);}
.ledger-row:last-child{border-bottom:0;}
.ledger-what{display:flex;flex-direction:column;gap:2px;font-size:12.5px;font-weight:700;min-width:0;}
.ledger-what .subtext{font-weight:500;font-size:11px;}
.ledger-amt{font-size:13px;font-weight:800;color:var(--gold);white-space:nowrap;}
.ledger-amt.is-out{color:var(--text-subtle);}

/* ── storage ───────────────────────────────────────────────────────────── */
.storage-cards{display:flex;flex-direction:column;gap:10px;margin-bottom:22px;}
.storage-card{display:flex;align-items:center;gap:12px;padding:13px 14px;
  border:1px solid var(--border);border-radius:var(--r-lg);background:var(--bg-2);}
.storage-card>div{flex:1;min-width:0;}
.storage-card strong{font-size:13.5px;}
.storage-card .subtext{font-size:11.5px;margin-top:2px;}

/* ── storage transfer panel ─────────────────────────────────────────────── */
.storage-move{display:flex;flex-direction:column;gap:10px;padding:14px;}
.mv-row{display:flex;align-items:center;gap:10px;}
.mv-label{
  flex:0 0 42px;font-size:10.5px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;
  color:var(--text-subtle);
}
.mv-row .filter-pills{flex:1;flex-wrap:wrap;margin:0;}
.mv-sum{
  margin:2px 0 0;padding-top:10px;border-top:1px solid var(--border);
  font-size:12px;line-height:1.5;color:var(--text-muted);
}
.mv-sum b{color:var(--gold);font-weight:800;}

/* ── progress ──────────────────────────────────────────────────────────── */
.quest-list{display:flex;flex-direction:column;gap:10px;}
.quest-row{display:flex;align-items:flex-start;gap:11px;padding:12px;
  border:1px solid var(--border);border-radius:var(--r-lg);background:var(--bg-2);}
.quest-ico{flex:0 0 auto;color:var(--gold);display:flex;margin-top:2px;}
.quest-body{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px;}
.quest-body strong{font-size:13px;}
.quest-body .subtext{font-size:11.5px;line-height:1.45;}
.quest-bar{margin-top:6px;}
.ach-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;}
.ach-card{display:flex;flex-direction:column;align-items:flex-start;gap:6px;padding:13px;
  border:1px solid var(--border);border-radius:var(--r-lg);background:var(--bg-2);}
.ach-card strong{font-size:12.5px;line-height:1.3;}
.ach-card .subtext{font-size:11px;line-height:1.45;}
.ach-ico{color:var(--gold);display:flex;}
.ach-card.is-done{border-color:rgb(212 175 55 / 40%);background:rgb(212 175 55 / 6%);}

/* ── inventory manager (profile → Manage) ──────────────────────────────── */
.mgr-head h3{font-size:19px;font-weight:800;}
.mgr-cap{margin:12px 0 14px;padding:12px 13px;border:1px solid var(--border);
  border-radius:var(--r-lg);background:var(--bg-2);}
.mgr-cap-top{display:flex;align-items:baseline;justify-content:space-between;gap:10px;}
.mgr-cap-top strong{font-size:14px;}
.mgr-cap-top .subtext{font-weight:500;font-size:11.5px;}
.mgr-cap-tag{padding:2px 9px;border-radius:999px;font-size:9.5px;font-weight:800;letter-spacing:.05em;
  text-transform:uppercase;color:var(--gold);background:rgb(212 175 55 / 12%);border:1px solid rgb(212 175 55 / 35%);}
.mgr-cap-track{margin-top:9px;height:7px;border-radius:999px;background:var(--bg-4);overflow:hidden;}
.mgr-cap-track i{display:block;height:100%;border-radius:inherit;background:var(--grad-1);
  transition:width .5s var(--ease-out);}
.mgr-tabs{display:flex;gap:5px;padding:4px;margin-bottom:14px;border-radius:999px;
  border:1px solid var(--border);background:var(--bg-2);}
.mgr-tab{flex:1 1 0;padding:8px 6px;border:0;border-radius:999px;background:none;
  font:inherit;font-size:12.5px;font-weight:700;color:var(--text-muted);cursor:pointer;
  transition:background .18s ease,color .18s ease;}
.mgr-tab.is-on{background:var(--bg-4);color:var(--gold);}
.mgr-tab:focus-visible{outline:2px solid var(--gold);outline-offset:-2px;}
.mgr-body{min-height:120px;}
.mgr-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;}
.filter-pills{display:flex;gap:6px;overflow-x:auto;padding-bottom:10px;margin-bottom:4px;
  scrollbar-width:none;-ms-overflow-style:none;}
.filter-pills::-webkit-scrollbar{display:none;}
.filter-pill{flex:0 0 auto;padding:6px 13px;border-radius:999px;border:1px solid var(--border);
  background:var(--bg-2);color:var(--text-muted);font:inherit;font-size:12px;font-weight:700;
  cursor:pointer;transition:var(--transition);white-space:nowrap;}
.filter-pill.is-on{background:rgb(212 175 55 / 14%);border-color:rgb(212 175 55 / 45%);color:var(--gold);}
.filter-pill:focus-visible{outline:2px solid var(--gold);outline-offset:2px;}

.mgr-back{align-self:flex-start;margin-bottom:12px;padding:6px 12px;border-radius:999px;
  border:1px solid var(--border);background:var(--bg-2);color:var(--text-muted);
  font:inherit;font-size:12px;font-weight:700;cursor:pointer;}
.mgr-back:hover{color:var(--text);border-color:var(--gold);}
.mgr-actions{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;margin-top:16px;}

.loadout-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px;margin-top:12px;}
.loadout-slot{display:flex;flex-direction:column;align-items:center;gap:2px;width:100%;padding:8px;
  border:1px dashed var(--border);border-radius:var(--r-lg);background:var(--bg-2);
  font:inherit;color:inherit;cursor:pointer;-webkit-appearance:none;appearance:none;
  transition:border-color var(--dur) var(--ease-out),transform var(--dur) var(--ease-out);}
.loadout-slot.is-filled{border-style:solid;border-color:rgb(212 175 55 / 35%);}
button.loadout-slot:hover{transform:translateY(-2px);border-color:var(--gold);}
.loadout-slot:focus-visible{outline:2px solid var(--gold);outline-offset:2px;}
.loadout-grid.is-static .loadout-slot{cursor:default;}
.loadout-art{display:grid;place-items:center;width:100%;aspect-ratio:1;overflow:hidden;
  border-radius:var(--r);background:radial-gradient(circle at 50% 30%,rgb(212 175 55 / 10%),var(--bg-3) 74%);}
.loadout-art .item-card-img{width:84%;height:84%;}
.loadout-empty{display:grid;place-items:center;color:var(--text-subtle);opacity:.6;}
.loadout-name{margin-top:7px;font-size:11.5px;font-weight:700;text-align:center;line-height:1.3;
  width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.loadout-kind{font-size:10px;color:var(--text-subtle);letter-spacing:.02em;}

.mgr-pvp{display:flex;flex-direction:column;}
.mgr-pvp-head{display:flex;gap:11px;padding:12px 13px;border-radius:var(--r-lg);
  border:1px solid rgb(212 175 55 / 30%);background:rgb(212 175 55 / 7%);}
.mgr-pvp-head .inline-icon{flex:0 0 auto;color:var(--gold);margin-top:2px;}
.mgr-pvp-head strong{font-size:12.5px;}
.mgr-pvp-head .subtext{font-size:11.5px;line-height:1.5;margin-top:3px;}

/* ── phones: two tiles per line, everywhere ─────────────────────────────
   One card per line made every collection four scrolls long. Two-up is the
   phone-native shape: the art still reads, the names are still whole. */
@media (max-width:820px){
  .grid-4,.grid-3,.grid-2{grid-template-columns:repeat(2,minmax(0,1fr));gap:11px;}
  .inv-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;}
  .shop-card .item-card-desc{display:none;}
  .pricing-grid{grid-template-columns:minmax(0,1fr);}
}

/* The bar's "More" opens the drawer; the drawer needs to sit above the bar. */
@media (max-width:820px){
  /* Taller rows need a taller box, or the last two items (Settings, the account
     action) end up below the fold the moment the spacing is fixed. */
  .drawer{top:auto;bottom:calc(84px + env(safe-area-inset-bottom,0px));right:12px;left:auto;
    width:min(264px,calc(100vw - 24px));max-height:min(72svh,calc(100dvh - 116px));
    transform-origin:bottom right;}
  .mgr-body .item-card-name{white-space:normal;display:-webkit-box;-webkit-line-clamp:2;
    -webkit-box-orient:vertical;overflow:hidden;}
}

@media (prefers-reduced-motion:reduce){
  .mobile-bar,.mbar-slot,.ev-dot,.mgr-cap-track i{transition:none;}
}

/* The phone season header that stands in for the hidden hero's title. */
.mob-season-head{display:none;}
@media (max-width:820px){
  .mob-season-head{display:block;padding:14px 20px 2px;}
  .mob-season-title{font-size:24px;font-weight:800;line-height:1.15;}
  .mob-season-desc{margin-top:6px;font-size:12.5px;line-height:1.5;color:var(--text-muted);}
}

/* Page switches on a phone are mostly taps on the bottom bar, so the arrival
   animation is shorter and softer than the desktop one - long enough to read
   as a change of place, short enough that a fast tapper never waits for it. */
@media (max-width:820px){
  .view{animation:viewInMob .26s var(--ease-out);}
  @keyframes viewInMob{from{opacity:0;transform:translateY(6px);}to{opacity:1;transform:none;}}
}
html.no-motion .view,html.no-motion .ev-slide,html.no-motion .mobile-bar{animation:none;}

/* ── wallet ────────────────────────────────────────────────────────────── */
.wallet-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;margin-bottom:18px;}
.wallet-card{display:flex;flex-direction:column;gap:3px;padding:13px 14px;
  border:1px solid var(--border);border-radius:var(--r-lg);background:var(--bg-2);}
.wallet-ico{color:var(--gold);display:flex;margin-bottom:3px;}
.wallet-k{font-size:10.5px;font-weight:800;letter-spacing:.05em;text-transform:uppercase;color:var(--text-subtle);}
.wallet-v{font-size:19px;font-weight:800;font-family:'Space Grotesk',sans-serif;}
.wallet-u{font-size:11px;color:var(--text-subtle);}
.wallet-loan{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:18px;
  border-color:rgb(212 175 55 / 40%);}
.wallet-form{display:flex;flex-direction:column;gap:8px;}
.wallet-form label{font-size:11.5px;font-weight:700;color:var(--text-muted);}
.wallet-form input{width:100%;padding:12px 13px;border-radius:var(--r);border:1px solid var(--border);
  background:var(--bg-3);color:var(--text);font:inherit;font-size:15px;}
.wallet-form input:focus{outline:none;border-color:var(--gold);}
.wallet-btns{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;margin-top:2px;}
.ledger{display:flex;flex-direction:column;border:1px solid var(--border);border-radius:var(--r-lg);
  background:var(--bg-2);overflow:hidden;}
.ledger-row{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:11px 14px;
  border-bottom:1px solid var(--border-soft);}
.ledger-row:last-child{border-bottom:0;}
.ledger-what{display:flex;flex-direction:column;gap:2px;font-size:12.5px;font-weight:700;min-width:0;}
.ledger-what .subtext{font-weight:500;font-size:11px;}
.ledger-amt{font-size:13px;font-weight:800;color:var(--gold);white-space:nowrap;}
.ledger-amt.is-out{color:var(--text-subtle);}

/* ── storage ───────────────────────────────────────────────────────────── */
.storage-cards{display:flex;flex-direction:column;gap:10px;margin-bottom:22px;}
.storage-card{display:flex;align-items:center;gap:12px;padding:13px 14px;
  border:1px solid var(--border);border-radius:var(--r-lg);background:var(--bg-2);}
.storage-card>div{flex:1;min-width:0;}
.storage-card strong{font-size:13.5px;}
.storage-card .subtext{font-size:11.5px;margin-top:2px;}

/* ── progress ──────────────────────────────────────────────────────────── */
.quest-list{display:flex;flex-direction:column;gap:10px;}
.quest-row{display:flex;align-items:flex-start;gap:11px;padding:12px;
  border:1px solid var(--border);border-radius:var(--r-lg);background:var(--bg-2);}
.quest-ico{flex:0 0 auto;color:var(--gold);display:flex;margin-top:2px;}
.quest-body{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px;}
.quest-body strong{font-size:13px;}
.quest-body .subtext{font-size:11.5px;line-height:1.45;}
.quest-bar{margin-top:6px;}
.ach-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;}
.ach-card{display:flex;flex-direction:column;align-items:flex-start;gap:6px;padding:13px;
  border:1px solid var(--border);border-radius:var(--r-lg);background:var(--bg-2);}
.ach-card strong{font-size:12.5px;line-height:1.3;}
.ach-card .subtext{font-size:11px;line-height:1.45;}
.ach-ico{color:var(--gold);display:flex;}
.ach-card.is-done{border-color:rgb(212 175 55 / 40%);background:rgb(212 175 55 / 6%);}

/* ── inventory manager (profile → Manage) ──────────────────────────────── */
.mgr-head h3{font-size:19px;font-weight:800;}
.mgr-cap{margin:12px 0 14px;padding:12px 13px;border:1px solid var(--border);
  border-radius:var(--r-lg);background:var(--bg-2);}
.mgr-cap-top{display:flex;align-items:baseline;justify-content:space-between;gap:10px;}
.mgr-cap-top strong{font-size:14px;}
.mgr-cap-top .subtext{font-weight:500;font-size:11.5px;}
.mgr-cap-tag{padding:2px 9px;border-radius:999px;font-size:9.5px;font-weight:800;letter-spacing:.05em;
  text-transform:uppercase;color:var(--gold);background:rgb(212 175 55 / 12%);border:1px solid rgb(212 175 55 / 35%);}
.mgr-cap-track{margin-top:9px;height:7px;border-radius:999px;background:var(--bg-4);overflow:hidden;}
.mgr-cap-track i{display:block;height:100%;border-radius:inherit;background:var(--grad-1);
  transition:width .5s var(--ease-out);}
.mgr-tabs{display:flex;gap:5px;padding:4px;margin-bottom:14px;border-radius:999px;
  border:1px solid var(--border);background:var(--bg-2);}
.mgr-tab{flex:1 1 0;padding:8px 6px;border:0;border-radius:999px;background:none;
  font:inherit;font-size:12.5px;font-weight:700;color:var(--text-muted);cursor:pointer;
  transition:background .18s ease,color .18s ease;}
.mgr-tab.is-on{background:var(--bg-4);color:var(--gold);}
.mgr-tab:focus-visible{outline:2px solid var(--gold);outline-offset:-2px;}
.mgr-body{min-height:120px;}
.mgr-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;}
.filter-pills{display:flex;gap:6px;overflow-x:auto;padding-bottom:10px;margin-bottom:4px;
  scrollbar-width:none;-ms-overflow-style:none;}
.filter-pills::-webkit-scrollbar{display:none;}
.filter-pill{flex:0 0 auto;padding:6px 13px;border-radius:999px;border:1px solid var(--border);
  background:var(--bg-2);color:var(--text-muted);font:inherit;font-size:12px;font-weight:700;
  cursor:pointer;transition:var(--transition);white-space:nowrap;}
.filter-pill.is-on{background:rgb(212 175 55 / 14%);border-color:rgb(212 175 55 / 45%);color:var(--gold);}
.filter-pill:focus-visible{outline:2px solid var(--gold);outline-offset:2px;}

.mgr-back{align-self:flex-start;margin-bottom:12px;padding:6px 12px;border-radius:999px;
  border:1px solid var(--border);background:var(--bg-2);color:var(--text-muted);
  font:inherit;font-size:12px;font-weight:700;cursor:pointer;}
.mgr-back:hover{color:var(--text);border-color:var(--gold);}
.mgr-actions{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;margin-top:16px;}

.loadout-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px;margin-top:12px;}
.loadout-slot{display:flex;flex-direction:column;align-items:center;gap:2px;width:100%;padding:8px;
  border:1px dashed var(--border);border-radius:var(--r-lg);background:var(--bg-2);
  font:inherit;color:inherit;cursor:pointer;-webkit-appearance:none;appearance:none;
  transition:border-color var(--dur) var(--ease-out),transform var(--dur) var(--ease-out);}
.loadout-slot.is-filled{border-style:solid;border-color:rgb(212 175 55 / 35%);}
button.loadout-slot:hover{transform:translateY(-2px);border-color:var(--gold);}
.loadout-slot:focus-visible{outline:2px solid var(--gold);outline-offset:2px;}
.loadout-grid.is-static .loadout-slot{cursor:default;}
.loadout-art{display:grid;place-items:center;width:100%;aspect-ratio:1;overflow:hidden;
  border-radius:var(--r);background:radial-gradient(circle at 50% 30%,rgb(212 175 55 / 10%),var(--bg-3) 74%);}
.loadout-art .item-card-img{width:84%;height:84%;}
.loadout-empty{display:grid;place-items:center;color:var(--text-subtle);opacity:.6;}
.loadout-name{margin-top:7px;font-size:11.5px;font-weight:700;text-align:center;line-height:1.3;
  width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.loadout-kind{font-size:10px;color:var(--text-subtle);letter-spacing:.02em;}

.mgr-pvp{display:flex;flex-direction:column;}
.mgr-pvp-head{display:flex;gap:11px;padding:12px 13px;border-radius:var(--r-lg);
  border:1px solid rgb(212 175 55 / 30%);background:rgb(212 175 55 / 7%);}
.mgr-pvp-head .inline-icon{flex:0 0 auto;color:var(--gold);margin-top:2px;}
.mgr-pvp-head strong{font-size:12.5px;}
.mgr-pvp-head .subtext{font-size:11.5px;line-height:1.5;margin-top:3px;}

/* The phone season header that stands in for the hidden hero's title. */
.mob-season-head{display:none;}
@media (max-width:820px){
  .mob-season-head{display:block;padding:14px 20px 2px;}
  .mob-season-title{font-size:24px;font-weight:800;line-height:1.15;}
  .mob-season-desc{margin-top:6px;font-size:12.5px;line-height:1.5;color:var(--text-muted);}
}

/* Page switches on a phone are mostly taps on the bottom bar, so the arrival
   animation is shorter and softer than the desktop one - long enough to read
   as a change of place, short enough that a fast tapper never waits for it. */
@media (max-width:820px){
  .view{animation:viewInMob .26s var(--ease-out);}
  @keyframes viewInMob{from{opacity:0;transform:translateY(6px);}to{opacity:1;transform:none;}}
}
html.no-motion .view,html.no-motion .ev-slide,html.no-motion .mobile-bar{animation:none;}

/* ── storage: the ender chest now reads as owned ───────────────────────── */
.storage-card.is-owned{border-color:rgb(212 175 55 / 45%);background:rgb(212 175 55 / 6%);}
.storage-card .mgr-cap-track{margin-top:8px;height:6px;}
.storage-card .chip-sm{margin-left:6px;font-size:9.5px;padding:2px 8px;color:var(--gold);
  border-color:rgb(212 175 55 / 40%);background:rgb(212 175 55 / 12%);}

/* ── season carousel, second pass ─────────────────────────────────────── */
@media (max-width:820px){
  .mob-events{padding-top:12px;}
  .ev-track{gap:14px;padding:2px 20px 10px;}
  .ev-slide{
    flex-basis:min(88%,380px);
    border-color:rgb(212 175 55 / 22%);
    background:linear-gradient(165deg,#121009,#060606 62%);
    box-shadow:0 16px 34px rgb(0 0 0 / 55%);
  }
  .ev-media{aspect-ratio:4/3;}
  .ev-media img{transform:scale(1.02);}
  .ev-slide::after{
    content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;
    box-shadow:inset 0 0 0 1px rgb(255 255 255 / 4%);
  }
  .ev-body{padding:2px 16px 16px;margin-top:-34px;gap:7px;}
  .ev-title{font-size:22px;}
  .ev-reward{background:rgb(212 175 55 / 16%);box-shadow:0 4px 14px rgb(212 175 55 / 12%);}
  .ev-dot{width:6px;height:6px;background:rgb(255 255 255 / 16%);}
  .ev-dot.is-on{width:26px;background:var(--gold);box-shadow:0 0 10px rgb(212 175 55 / 60%);}
  .mob-season-head{padding-top:16px;}
  .mob-season-title{font-size:26px;}
  /* a hairline that reads as "there is more to the right" */
  .ev-slider::after{
    content:"";position:absolute;top:0;right:0;width:34px;height:calc(100% - 26px);
    background:linear-gradient(90deg,transparent,rgb(0 0 0 / 55%));
    pointer-events:none;border-radius:0 var(--r-xl) var(--r-xl) 0;
  }
}

/* ── inventory manager, second pass ───────────────────────────────────── */
.mgr-head{display:flex;flex-direction:column;}
.mgr-head h3{display:flex;align-items:center;justify-content:space-between;gap:10px;}
.mgr-cap{background:linear-gradient(160deg,rgb(212 175 55 / 8%),var(--bg-2) 60%);}
.mgr-cap-num{font-family:'Space Grotesk',sans-serif;font-size:15px;font-weight:800;}
.mgr-cap.is-tight .mgr-cap-track i{background:linear-gradient(135deg,#e07a5f,#b4462f);}
.mgr-cap.is-tight .mgr-cap-tag{color:#ffb4a2;background:rgb(224 122 95 / 14%);border-color:rgb(224 122 95 / 40%);}
.mgr-cap-track i{background:var(--grad-1);}
.mgr-actions .btn{justify-content:center;}
.mgr-actions .btn-primary{grid-column:1 / -1;}
.mgr-empty{padding:26px 8px;text-align:center;}

/* manager item sheet: give the plate a little more presence */
.mgr-body .item-sheet{padding:14px 0 0;}
.mgr-body .item-sheet-art{width:min(180px,54%);}

/* ══════════════════════════════════════════════════════════════════════════
   PROGRESS → ART ON THE QUEST / ACHIEVEMENT CARDS
   Each quest row and achievement card carries the painting of the track it
   belongs to, behind a scrim, so the lists read as illustrated cards. The
   painting is the backdrop only: the words, the progress bar and the Claim
   button all sit above it and stay legible on any of the six images.

   `--art` is set inline by trackArtAttrs() in mobile-pages.js. A record whose
   track can't be worked out gets no `has-art`, and falls back to the flat card
   it had before rather than guessing a picture.
   ══════════════════════════════════════════════════════════════════════════ */

.quest-row.has-art,.ach-card.has-art{
  position:relative;overflow:hidden;
  border-color:rgb(212 175 55 / 22%);
}
.quest-row.has-art::before,.ach-card.has-art::before{
  content:"";position:absolute;inset:0;z-index:0;
  background-image:var(--art,none);
  background-size:cover;background-position:center 32%;
  /* Dimmed hard: these are backdrops, and the text on top has to win. */
  filter:saturate(.72) brightness(.5) contrast(1.05);
  transform:scale(1.02);
  transition:transform var(--dur-slow) var(--ease-out),filter var(--dur) var(--ease-out);
}
.quest-row.has-art::after,.ach-card.has-art::after{
  content:"";position:absolute;inset:0;z-index:0;pointer-events:none;
  background:linear-gradient(100deg,rgb(0 0 0 / 88%) 0%,rgb(0 0 0 / 68%) 46%,rgb(0 0 0 / 40%) 100%);
}
.quest-row.has-art:hover::before,.ach-card.has-art:hover::before{
  transform:scale(1.05);filter:saturate(.9) brightness(.58) contrast(1.04);
}
/* Everything the card actually says sits above the two layers. */
.quest-row.has-art>*,.ach-card.has-art>*{position:relative;z-index:1;}

/* Track name, small, above the title. */
.card-track{
  display:inline-flex;align-items:center;
  margin-bottom:3px;padding:1px 7px;border-radius:999px;
  font-size:9px;font-weight:800;letter-spacing:.07em;text-transform:uppercase;
  color:var(--gold);background:rgb(0 0 0 / 45%);
  border:1px solid rgb(212 175 55 / 34%);
}
.ach-card .card-track{align-self:flex-start;margin-top:auto;}
.ach-card.has-art .chip-sm,.ach-card.has-art .btn{position:relative;z-index:1;}
/* The progress bar needs to stay visible over dark photography. */
.quest-row.has-art .quest-bar .bar-track,.quest-row.has-art .quest-bar{position:relative;z-index:1;}

@media (prefers-reduced-motion:reduce){
  .quest-row.has-art::before,.ach-card.has-art::before{transition:none;}
}

/* ══════════════════════════════════════════════════════════════════════════
   ITEM POPUP
   The old popup was a bare sheet: a plate, a name, and nothing you could do
   with it. It is now a card - rarity ribbon, quantity, description, stats,
   value, an amount stepper and the actions - so tapping an item in the
   inventory (or in the manager) reads as opening the item, not a dead end.
   ══════════════════════════════════════════════════════════════════════════ */

.item-pop{display:flex;flex-direction:column;padding:16px 16px 4px;}
.item-pop-hero{position:relative;display:flex;justify-content:center;}
.item-pop-art{
  position:relative;display:grid;place-items:center;
  width:min(190px,58%);aspect-ratio:1;overflow:hidden;
  border:1px solid rgb(212 175 55 / 28%);border-radius:var(--r-xl);
  background:
    radial-gradient(circle at 50% 26%,rgb(212 175 55 / 18%),transparent 66%),
    var(--bg-3);
  box-shadow:inset 0 1px 0 rgb(255 255 255 / 6%),0 16px 34px rgb(0 0 0 / 45%);
}
.item-pop-img{width:86%;height:86%;object-fit:contain;
  filter:drop-shadow(0 12px 20px rgb(0 0 0 / 55%));}
.item-pop-art>.rarity-pill{position:absolute;top:8px;right:8px;margin:0;font-size:9.5px;letter-spacing:.08em;z-index:1;}
@media (max-width:520px){
  .item-pop{padding:14px 12px 2px;}
  .item-pop-art{width:min(168px,62%);}
}
.item-pop-qty{
  position:absolute;right:7px;bottom:7px;
  padding:2px 9px;border-radius:999px;font-size:11px;font-weight:800;
  background:rgb(0 0 0 / 70%);border:1px solid var(--border);color:var(--text);
}
.item-pop.is-rare .item-pop-art{border-color:rgb(96 165 250 / 48%);}
.item-pop.is-epic .item-pop-art{border-color:rgb(168 85 247 / 50%);}
.item-pop.is-legendary .item-pop-art{border-color:rgb(212 175 55 / 62%);
  box-shadow:inset 0 1px 0 rgb(255 255 255 / 8%),0 16px 34px rgb(0 0 0 / 45%),0 0 40px rgb(212 175 55 / 16%);}

.item-pop-head{padding:14px 2px 0;text-align:center;}
.item-pop-head h3{font-size:20px;font-weight:800;line-height:1.2;overflow-wrap:anywhere;}
.item-pop-sub{margin-top:5px;font-size:12px;font-weight:700;color:var(--gold);}
.item-pop-body{padding:12px 2px 14px;}
.item-pop-desc{font-size:12.5px;line-height:1.55;color:var(--text-muted);}
.item-pop-value{
  display:flex;align-items:center;gap:7px;margin-top:14px;padding:9px 11px;
  border:1px solid var(--border);border-radius:var(--r);background:var(--bg-2);
  font-size:12px;color:var(--text-muted);
}
.item-pop-value strong{color:var(--gold);font-weight:800;}
.item-pop-note{margin-top:10px;font-size:11.5px;color:var(--text-subtle);}
.item-pop-actions{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;margin-top:16px;}
.item-pop-actions .btn{justify-content:center;}
.item-pop-actions .btn-primary,.item-pop-actions .btn[data-mgr-action="equip"]{grid-column:1 / -1;}
.item-pop-stepper{
  display:flex;align-items:center;gap:10px;margin-top:14px;padding:9px 11px;
  border:1px solid var(--border);border-radius:var(--r);background:var(--bg-2);
  font-size:12px;color:var(--text-muted);
}
.item-pop-stepper>span:first-child{font-weight:700;}
.item-pop-stepper .subtext{margin-left:auto;font-size:11px;}
.stepper{display:flex;align-items:center;gap:4px;}
.stepper-btn{
  width:30px;height:30px;display:grid;place-items:center;
  border:1px solid var(--border);border-radius:9px;background:var(--bg-3);
  color:var(--text);font:inherit;font-size:16px;font-weight:800;line-height:1;cursor:pointer;
  transition:var(--transition);
}
.stepper-btn:hover{border-color:var(--gold);color:var(--gold);}
.stepper input{
  width:52px;height:30px;text-align:center;
  border:1px solid var(--border);border-radius:9px;background:var(--bg-3);color:var(--text);
  font:inherit;font-size:13px;font-weight:800;
  -moz-appearance:textfield;appearance:textfield;
}
.stepper input::-webkit-outer-spin-button,.stepper input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0;}
.stepper input:focus{outline:none;border-color:var(--gold);}

/* ══════════════════════════════════════════════════════════════════════════
   PROGRESS → HALLOWEEN EVENT CARD
   The event slider on the season page is a poster; this is the same event as a
   card you can act on. Its state - live, claimed, or not live yet - comes from
   /api/events, so the button never claims something the bot won't pay out.
   ══════════════════════════════════════════════════════════════════════════ */

.ev-card{
  overflow:hidden;padding:0;
  border-color:rgb(212 175 55 / 26%);
  background:linear-gradient(170deg,var(--bg-3),#080606);
}
.ev-card .ev-media{aspect-ratio:16/9;border-bottom:1px solid var(--border);}
.ev-card .ev-media img{object-position:center 34%;}
/* The slider pulls its caption up over the photograph; this card must not.
   The title sat on top of the art and collided with the badge, so the body
   starts below the image and keeps its own background. */
.ev-card .ev-body{margin-top:0;padding:13px 14px 14px;background:var(--bg-2);}
.ev-card .ev-title{font-size:19px;letter-spacing:-.01em;}
.ev-card .ev-rewards{margin-top:4px;}
/* A single chip is a label, not a button: it must not stretch the full width. */
.ev-card .ev-body>.chip-sm{align-self:flex-start;}
.ev-card.is-quiet{border-color:var(--border);background:var(--bg-2);}
.ev-card.is-quiet .ev-body{background:transparent;}
