/* Mage Arena — carved-arcane dark fantasy UI */

:root {
  --ink: #0a0a14;
  --stone: #1a1a2b;
  --parchment: #e8e0cd;
  --dim: #9a94b8;
  --fire: #ff8a3c;
  --fire-hot: #ffd23c;
  --stonec: #c9a86a;
  --ice: #7de4ff;
  --blood: #ff4d5e;
  --frost: #bfe6ff;          /* icy accent for HUD text/borders */
  --frost-dim: #7fa8c8;
  --frost-gold: #ffdf9e;     /* top-duelist highlight */
  --font-display: 'Cinzel', 'Times New Roman', serif;
  --font-body: 'Alegreya Sans', 'Segoe UI', sans-serif;
  --font-hud: 'Cormorant Garamond', 'Cinzel', serif; /* elegant HUD serif */

  /* Surface tokens. Depth here is built from a translucent slab, one hairline
     border and an inset top highlight — never backdrop-filter, which would
     re-blur the animated starfield behind the menu on every frame. */
  --surface: linear-gradient(168deg, rgba(30,33,58,.78), rgba(15,17,33,.86));
  --surface-soft: linear-gradient(168deg, rgba(38,42,70,.62), rgba(20,22,40,.7));
  --line: rgba(154,176,226,.16);
  --line-strong: rgba(168,198,248,.34);
  --edge-hi: inset 0 1px 0 rgba(210,228,255,.10);
  --lift-sm: 0 8px 22px -12px rgba(0,6,20,.9);
  --lift: 0 22px 50px -26px rgba(0,6,20,.95);
  --radius: 16px;
  --radius-sm: 11px;
  --ease: cubic-bezier(.2,.9,.3,1);
}

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

html, body {
  height: 100%;
  overflow: hidden;
  background: var(--ink);
  font-family: var(--font-body);
  color: var(--parchment);
  user-select: none;
}

#game {
  position: fixed;
  inset: 0;
  display: block;
  cursor: crosshair;
}

.hidden { display: none !important; }

/* ========================= FIRST SCREEN ========================= */

#menu {
  position: fixed;
  inset: 0;
  z-index: 20;
  /* flex + margin:auto on the child centers like place-items:center, but
     stays scrollable when the content is taller than the viewport
     (grid centering would clip the top and bottom with no way to reach them) */
  display: flex;
  overflow-y: auto;
  background:
    radial-gradient(1200px 700px at 50% 120%, rgba(255, 106, 40, 0.13), transparent 60%),
    radial-gradient(900px 500px at 15% -10%, rgba(64, 156, 255, 0.10), transparent 60%),
    radial-gradient(closest-side at 50% 45%, #14142400, #0a0a14 90%),
    #101020;
}

/* fixed so the backdrop stays put if the menu scrolls */
#menu-fx { position: fixed; inset: 0; width: 100%; height: 100%; }

/* Two columns: the pitch on the left, the join panel on the right. The panel
   spans both left rows, and `align-self: end/start` pulls the brand and the
   footer toward their shared boundary so the left stack reads as one centred
   block against it. That is what buys back the vertical space the old centred
   single column was wasting — the controls legend now sits above the fold on
   a laptop instead of below it. */
.menu-inner {
  position: relative;
  margin: auto; /* centers within #menu; top-aligns instead of clipping on overflow */
  text-align: left;
  width: 100%;
  max-width: 1180px;
  padding: clamp(1rem, 3.5vh, 2.4rem) clamp(1.1rem, 4vw, 3rem);
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(336px, 396px);
  grid-template-areas: "brand panel" "foot panel";
  align-content: center;
  column-gap: clamp(1.4rem, 4vw, 3.4rem);
  row-gap: clamp(1rem, 3vh, 2rem);
  animation: menu-rise 0.9s cubic-bezier(.2,.9,.25,1) both;
}
.brand { grid-area: brand; align-self: end; min-width: 0; }
#join-form { grid-area: panel; }
.menu-foot { grid-area: foot; align-self: start; min-width: 0; }

@keyframes menu-rise {
  from { opacity: 0; transform: translateY(26px); }
  to   { opacity: 1; transform: none; }
}

.sigil {
  font-size: 1.5rem;
  line-height: 1;
  color: var(--fire);
  text-shadow: 0 0 18px var(--fire);
  animation: sigil-pulse 3s ease-in-out infinite;
  margin-bottom: .5rem;
}

@keyframes sigil-pulse {
  0%, 100% { opacity: .65; transform: scale(1); }
  50% { opacity: 1; transform: scale(1.18); }
}

.title {
  font-family: var(--font-display);
  font-weight: 900;
  font-size: clamp(2.6rem, 5.6vw, 4.8rem);
  letter-spacing: 0.06em;
  line-height: .98;
  background: linear-gradient(180deg, #fff6e2 0%, #ecd09a 44%, #9a7642 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter: drop-shadow(0 2px 0 rgba(0,0,0,.5)) drop-shadow(0 0 34px rgba(255,150,60,.24));
}

.title-amp {
  background: linear-gradient(180deg, #e2f9ff 0%, #93dcf0 44%, #3f7594 100%);
  -webkit-background-clip: text;
  background-clip: text;
}

.subtitle {
  margin: .9rem 0 0;
  max-width: 46ch;
  font-size: clamp(.96rem, 1.1vw, 1.1rem);
  color: var(--dim);
  line-height: 1.6;
}
.subtitle em { font-style: normal; font-weight: 700; }
.c-fire { color: var(--fire); }
.c-stone { color: var(--stonec); }
.c-ice { color: var(--ice); }
.c-air { color: #7db8ff; }

/* Live status reads as a pill with a breathing dot rather than a bare line of
   orange text — the same information, but it looks like a status indicator. */
#arena-status {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  margin-top: 1.1rem;
  padding: .38rem .85rem .38rem .7rem;
  font-size: .9rem;
  letter-spacing: .02em;
  color: var(--fire-hot);
  background: linear-gradient(180deg, rgba(255,154,76,.14), rgba(255,120,40,.07));
  border: 1px solid rgba(255,168,92,.28);
  border-radius: 999px;
}
#arena-status::before {
  content: '';
  width: 7px; height: 7px; flex: none;
  border-radius: 50%;
  background: currentColor;
  box-shadow: 0 0 10px currentColor;
  animation: status-pulse 2.4s ease-in-out infinite;
}
#arena-status.quiet {
  color: var(--dim);
  background: rgba(154,148,184,.08);
  border-color: rgba(154,148,184,.2);
}
#arena-status.quiet::before { animation: none; box-shadow: none; opacity: .6; }
@keyframes status-pulse {
  0%, 100% { opacity: .4; transform: scale(.82); }
  50% { opacity: 1; transform: scale(1); }
}

/* The join panel: one slab holding every control, in reading order. */
#join-form {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  padding: 1.35rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--lift), var(--edge-hi);
}
.field { display: flex; flex-direction: column; gap: .5rem; min-width: 0; }
.sec-label {
  font-family: var(--font-display);
  font-size: .64rem;
  font-weight: 700;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: #7f7aa0;
}

/* Server picker (from master), restyled into the new panel language: the old
   version was a full-width strip of 4px-radius boxes sitting above the form.
   Only the look changes — every class and id the JS writes to
   (.server-country, .server-option, .server-ping, .country-ping and their
   checking/fast/medium/slow/offline states) is untouched. */
#server-picker { min-width: 0; }
/* Bounded and scrollable: the country list comes from servers.config.json, so
   its length is config-driven and the panel must not grow with it — that is
   what keeps the menu on one screen however many regions ship. Only the
   country holding the selected server is open (main.js), so the usual state is
   one expanded card and the rest collapsed to a row. */
#server-countries {
  display: grid;
  gap: .4rem;
  max-height: 116px;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: rgba(154,176,226,.3) transparent;
  padding-right: 2px;
}
#server-countries::-webkit-scrollbar { width: 6px; }
#server-countries::-webkit-scrollbar-thumb { background: rgba(154,176,226,.28); border-radius: 999px; }
.server-country {
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: rgba(8, 9, 20, .45);
  overflow: hidden;
}
.server-country summary {
  display: flex; align-items: center; gap: .5rem;
  padding: .42rem .6rem;
  color: #c8d2ea;
  cursor: pointer;
  font: 700 .72rem var(--font-display);
  letter-spacing: .06em;
  list-style: none;
  transition: background .15s;
}
.server-country summary:hover { background: rgba(160,180,240,.07); }
.server-country summary::-webkit-details-marker { display: none; }
/* our own disclosure arrow, so the summary row can be a flex layout and the
   ping can sit on `margin-left: auto` instead of being floated */
.server-country summary::before {
  content: '\203A';
  font-size: .95rem; line-height: 1; color: #6f6a92;
  transition: transform .18s var(--ease);
}
.server-country[open] summary::before { transform: rotate(90deg); }
.country-ping {
  margin-left: auto;
  font-family: var(--font-body);
  font-weight: 600; font-size: .72rem;
  letter-spacing: 0;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.server-list { padding: 0 .3rem .3rem; display: grid; gap: 2px; }
.server-option {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .5rem;
  width: 100%;
  padding: .34rem .5rem;
  color: #cdc8e4;
  border: 1px solid transparent;
  border-radius: 7px;
  background: transparent;
  cursor: pointer;
  font: 500 .8rem var(--font-body);
  text-align: left;
  transition: background .15s, border-color .15s, color .15s;
}
.server-option:hover { background: rgba(160,180,240,.1); color: #fff; }
.server-option:focus-visible { outline: 2px solid var(--ice); outline-offset: -2px; }
.server-option.selected {
  color: #fff;
  border-color: rgba(255,200,110,.5);
  background: linear-gradient(180deg, rgba(255,154,76,.16), rgba(255,120,40,.08));
}
.server-ping { font-variant-numeric: tabular-nums; font-size: .72rem; flex: none; }
.server-ping.checking, .country-ping.checking { color: #6f6a92; }
.server-ping.fast, .country-ping.fast { color: #6ee7a2; }
.server-ping.medium, .country-ping.medium { color: var(--fire-hot); }
.server-ping.slow, .server-ping.offline,
.country-ping.slow, .country-ping.offline { color: var(--blood); }

/* One fixed footprint no matter how many slots are active: the cards share a
   strip of constant height, so the panel below them never moves when the
   server adds or drops an arena. */
#arena-slots {
  display: grid;
  gap: .5rem;
  height: 128px;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
}
.arena-card {
  min-height: 0;
  padding: 0;
  overflow: hidden;
  position: relative;
  isolation: isolate;
  color: var(--parchment);
  text-align: left;
  cursor: pointer;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: #14162a;
  box-shadow: var(--lift-sm);
  transition: transform .18s var(--ease), box-shadow .18s, border-color .18s;
}
.arena-card:hover {
  transform: translateY(-3px);
  border-color: var(--line-strong);
  box-shadow: 0 16px 30px -14px rgba(0,6,20,.95);
}
.arena-card:focus-visible { outline: 2px solid var(--ice); outline-offset: 2px; }
/* Selection is a ring plus a warm wash over the art, so it survives whatever
   the preview image happens to look like. */
.arena-card.selected {
  border-color: rgba(255,200,110,.75);
  box-shadow: 0 0 0 1px rgba(255,200,110,.45), 0 18px 34px -16px rgba(255,138,60,.5);
}
.arena-card.selected::after {
  content: '';
  position: absolute; inset: 0; z-index: 3;
  pointer-events: none;
  border-radius: inherit;
  background: linear-gradient(0deg, rgba(255,150,60,.16), transparent 55%);
}
.arena-card img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  object-position: center;
}
.arena-card-backdrop {
  z-index: 0;
  object-fit: cover;
  opacity: .45;
  filter: blur(18px) saturate(.8);
  transform: scale(1.08);
}
.arena-card-preview {
  z-index: 1;
  object-fit: contain;
  background: rgba(5, 7, 16, .28);
  opacity: .92;
}
.arena-card-copy {
  display: block;
  position: absolute;
  z-index: 2;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 1.7rem .65rem .55rem;
  background: linear-gradient(transparent, rgba(6, 8, 18, .94) 62%);
}
.arena-card-title {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: .5rem;
  font: 700 .78rem var(--font-display);
  letter-spacing: .04em;
}
/* the online count reads as a live badge, not as part of the arena's name */
.arena-card-online {
  flex: none;
  font-size: .62rem;
  font-weight: 700;
  letter-spacing: .06em;
  color: #bdf0ff;
  white-space: nowrap;
  padding: .1rem .42rem;
  background: rgba(111,216,255,.14);
  border: 1px solid rgba(111,216,255,.3);
  border-radius: 999px;
}
.arena-card-description {
  display: block; margin-top: .2rem;
  color: #a9a4c4; font-size: .72rem; line-height: 1.3;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

@media (max-width: 680px) {
  /* master's `#server-countries { grid-template-columns: 1fr }` is dropped
     here: the picker is a single-column list inside the panel now, so it is
     already one column at every width. */
  #arena-slots { grid-auto-flow: row; grid-auto-rows: minmax(0, 1fr); height: 200px; }
}

#nickname {
  width: 100%;
  padding: .7rem .95rem;
  font: 500 1rem var(--font-body);
  color: var(--parchment);
  background: rgba(8, 9, 20, .6);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  outline: none;
  transition: border-color .2s, box-shadow .2s, background .2s;
}
#nickname:focus {
  border-color: rgba(255,168,92,.55);
  background: rgba(8, 9, 20, .8);
  box-shadow: 0 0 0 3px rgba(255,138,60,.16);
}
#nickname::placeholder { color: #5d5880; }
#nickname.nick-invalid {
  border-color: rgba(255,90,110,.6);
  box-shadow: 0 0 0 3px rgba(255,77,94,.16);
}
.nick-error {
  margin: 0;
  font: 500 .84rem var(--font-body);
  color: #ff9aa6;
}
.nick-error.hidden { display: none; }

/* ---- mage customization panel ---- */
#customize {
  padding: .8rem;
  background: var(--surface-soft);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
}
.cz-body { display: flex; gap: .9rem; align-items: center; }
/* The portrait sits in its own lit alcove — an inset well rather than a framed
   box, so the mage reads as standing in the panel instead of on it. */
.cz-stage {
  position: relative;
  flex: none;
  width: 104px; height: 104px;
  border-radius: 12px;
  overflow: hidden;
  background:
    radial-gradient(120% 90% at 50% 12%, rgba(126,150,220,.16), transparent 62%),
    linear-gradient(180deg, #171a2e, #0d0f1c);
  box-shadow: inset 0 0 0 1px rgba(150,175,230,.12), inset 0 12px 24px -14px rgba(0,0,0,.9);
}
.cz-stage::after {
  content: '';
  position: absolute; left: 12%; right: 12%; bottom: 9px; height: 1px;
  background: linear-gradient(90deg, transparent, rgba(180,205,255,.22), transparent);
}
#cz-preview { display: block; width: 100%; height: 100%; }
.cz-controls { flex: 1; min-width: 0; text-align: left; }
.cz-label {
  font-size: .6rem; font-weight: 700; letter-spacing: .18em; text-transform: uppercase;
  color: #6f6a92; margin: 0 0 .4rem;
}
.cz-label + * + .cz-label { margin-top: .7rem; }
#cz-colors { display: flex; gap: 8px; flex-wrap: wrap; }
/* The swatch is the robe, so it is painted with the robe's own gradient (set
   inline by main.js) and the selection ring sits outside it — putting the ring
   on the border would eat into the colour it is meant to be showing. */
.cz-swatch {
  position: relative;
  width: 24px; height: 24px;
  border-radius: 50%;
  border: 0;
  padding: 0;
  cursor: pointer;
  box-shadow: inset 0 1px 0 rgba(255,255,255,.4), inset 0 -3px 6px -2px rgba(0,0,0,.55),
              0 0 0 1px rgba(8,9,20,.85);
  transition: transform .14s var(--ease), box-shadow .14s;
}
.cz-swatch:hover { transform: translateY(-2px) scale(1.08); }
.cz-swatch:focus-visible { outline: 2px solid var(--ice); outline-offset: 3px; }
.cz-swatch.sel {
  transform: scale(1.06);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.45), inset 0 -3px 6px -2px rgba(0,0,0,.5),
              0 0 0 2px #0c0e1c, 0 0 0 4px rgba(255,208,120,.9), 0 0 14px rgba(255,170,70,.5);
}
/* the hat stepper: one pill, arrows inside it */
.cz-hat {
  display: flex; align-items: center; justify-content: space-between; gap: .3rem;
  padding: 3px;
  background: rgba(8, 9, 20, .55);
  border: 1px solid var(--line);
  border-radius: 999px;
}
.cz-hat button {
  width: 26px; height: 26px; flex: none;
  font: 700 1.05rem/1 var(--font-body);
  color: #b9b4d6;
  background: transparent;
  border: 0;
  border-radius: 50%;
  cursor: pointer;
  transition: background .15s, color .15s;
}
.cz-hat button:hover { background: rgba(160,180,240,.14); color: #fff; }
.cz-hat button:focus-visible { outline: 2px solid var(--ice); outline-offset: 1px; }
#cz-hat-name {
  flex: 1; min-width: 0; text-align: center;
  font-size: .84rem; font-weight: 600; letter-spacing: .02em; color: #ddd8f2;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

#play-btn {
  position: relative;
  overflow: hidden;
  width: 100%;
  margin-top: .1rem;
  padding: .92rem 1rem;
  font: 700 1.02rem/1 var(--font-display);
  letter-spacing: .17em;
  color: #2a1606;
  cursor: pointer;
  border: none;
  border-radius: 12px;
  background: linear-gradient(135deg, #ffd884 0%, #ff9f45 44%, #f26f1e 100%);
  box-shadow: 0 14px 30px -14px rgba(255,124,40,.95), inset 0 1px 0 rgba(255,255,255,.5);
  transition: transform .16s var(--ease), box-shadow .2s, filter .2s;
}
#play-btn > span { position: relative; z-index: 1; }
/* one transform-only sweep on hover, no looping animation */
#play-btn::after {
  content: '';
  position: absolute; inset: -50% -60%;
  background: linear-gradient(102deg, transparent 44%, rgba(255,255,255,.32) 50%, transparent 56%);
  transform: translateX(-115%);
  transition: transform .65s ease-out;
}
#play-btn:hover {
  transform: translateY(-2px);
  filter: saturate(1.06);
  box-shadow: 0 20px 38px -16px rgba(255,124,40,1), inset 0 1px 0 rgba(255,255,255,.6);
}
#play-btn:hover::after { transform: translateX(115%); }
#play-btn:active { transform: translateY(0); box-shadow: 0 8px 18px -12px rgba(255,124,40,.9), inset 0 1px 0 rgba(255,255,255,.4); }
#play-btn:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }

.controls-preview {
  display: flex;
  flex-wrap: wrap;
  gap: .45rem .5rem;
  font-size: .82rem;
  color: var(--dim);
}
.ctl {
  display: flex; align-items: center; gap: .3rem;
  padding: .3rem .6rem .3rem .34rem;
  background: rgba(20,22,40,.5);
  border: 1px solid rgba(154,176,226,.1);
  border-radius: 999px;
}
kbd {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 1.6em; height: 1.55em;
  padding: 0 .38em;
  font: 700 .76rem/1 var(--font-body);
  color: #ded9f4;
  background: linear-gradient(180deg, #2b2f4c, #1e2138);
  border: 1px solid rgba(160,180,235,.2);
  border-bottom-color: rgba(8,9,20,.9);
  border-radius: 6px;
  box-shadow: 0 1px 0 rgba(255,255,255,.06) inset, 0 2px 3px -1px rgba(0,0,0,.5);
}
kbd.mouse { font-size: .9rem; }

.one-arena { margin-top: .9rem; font-size: .8rem; color: #6a6488; letter-spacing: .02em; }

/* Compact tiers. The panel is the tall column — it is what decides whether the
   page scrolls — so each tier trims the panel's own rhythm first and only
   sacrifices copy at the very bottom. At its natural size the panel is ~543px,
   which already clears a 640px laptop viewport on its own; these exist for the
   shorter ones and for browser chrome eating into the height.
   These live at the END of the menu block on purpose: they target the same
   ids and classes as the base rules at identical specificity, so source order
   is the only thing that decides the winner. Written higher up, every
   `#arena-slots`/`.cz-stage` override in them lost silently. */
@media (max-height: 780px) {
  .menu-inner { row-gap: 1rem; padding-top: 1.1rem; padding-bottom: 1.1rem; }
  .sigil { font-size: 1.25rem; margin-bottom: .3rem; }
  .title { font-size: clamp(2.3rem, 4.6vw, 3.6rem); }
  .subtitle { margin-top: .7rem; font-size: .98rem; line-height: 1.5; }
  #arena-status { margin-top: .8rem; }
  /* the server picker is the tallest thing the merge added to the panel; one
     open country still fits whole, the rest are a scroll away */
  #join-form { gap: .9rem; }
  #server-countries { max-height: 92px; }
}
@media (max-height: 660px) {
  .menu-inner { row-gap: .75rem; padding-top: .75rem; padding-bottom: .75rem; }
  .sigil { display: none; }
  #join-form { gap: .7rem; padding: 1rem; }
  #arena-slots { height: 96px; }
  #server-countries { max-height: 76px; }
  .cz-stage { width: 80px; height: 80px; }
  #customize { padding: .6rem; }
  #nickname { padding: .58rem .9rem; }
  #play-btn { padding: .8rem 1rem; }
  .one-arena { display: none; }
}
@media (max-height: 580px) {
  .subtitle { display: none; }
  #join-form { gap: .6rem; }
  #arena-slots { height: 90px; }
  #server-countries { max-height: 78px; }
  .cz-stage { width: 78px; height: 78px; }
  #play-btn { padding: .78rem 1rem; }
}

/* Single column once the two-column split would squeeze either side. Stacking
   costs height, so the panel gets the compact treatment here regardless of
   viewport height — otherwise the brand, the panel and the legend add up past
   even a tall narrow window. */
@media (max-width: 940px) {
  .menu-inner {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "brand" "panel" "foot";
    max-width: 470px;
    row-gap: .8rem;
    padding-top: .9rem;
    padding-bottom: .9rem;
    text-align: center;
  }
  .brand, .menu-foot { align-self: auto; }
  .sigil { display: none; }
  .title { font-size: clamp(2.2rem, 9vw, 3.2rem); }
  .subtitle { margin: .6rem auto 0; font-size: .94rem; line-height: 1.45; }
  #join-form { gap: .8rem; padding: 1.1rem; }
  #arena-slots { height: 96px; }
  #server-countries { max-height: 96px; }
  .cz-stage { width: 84px; height: 84px; }
  #customize { padding: .65rem; }
  .controls-preview { justify-content: center; gap: .35rem .4rem; font-size: .78rem; }
  /* flavour text is the first thing to go when the columns stack — the arena
     picker it describes is right there on screen anyway */
  .one-arena { display: none; }
}

/* ========================= HUD ========================= */

#hud { position: fixed; inset: 0; z-index: 10; pointer-events: none; font-size: 14px; --mute-shift: 0px; }
/* Muting adds a "🔇 muted (M)" line to the top-right panel, which grows it by
   23px and drove it straight through the Duelists board pinned at top:112px.
   Everything stacked under that panel reads one variable, so the whole right
   rail slides down together instead of each piece being nudged by hand. */
#hud.muted { --mute-shift: 24px; }

/* The HUD card. Depth is three flat layers — a translucent slab, one hairline
   border, one inset top highlight — and nothing else.
   `backdrop-filter: blur(4px)` used to live here. Over a 60fps canvas that is
   the single most expensive declaration in the file: the compositor has to
   re-sample and blur the frame behind every panel, every frame, forever. The
   look it bought is reproduced with gradients, which are painted once. */
.hud-panel {
  position: absolute;
  padding: .62rem .85rem;
  background:
    linear-gradient(160deg, rgba(32,42,66,.82), rgba(11,16,30,.88));
  border: 1px solid rgba(150,190,240,.18);
  border-radius: 13px;
  box-shadow:
    inset 0 1px 0 rgba(200,226,255,.13),
    0 14px 34px -16px rgba(0,4,14,.95);
  font-family: var(--font-hud);
  letter-spacing: .01em;
}

.hud-topleft { top: 14px; left: 14px; min-width: 232px; }
#hud-name {
  font-family: var(--font-display); font-weight: 700; letter-spacing: .05em;
  font-size: .98rem; color: #eef4ff; margin-bottom: .5rem;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* HP: a flat capsule. The old version was a glossy 3D tube with a specular
   streak — the single most dated element on screen. */
.hpbar {
  position: relative;
  height: 12px;
  background: rgba(6,10,20,.75);
  border-radius: 999px;
  overflow: hidden;
  box-shadow: inset 0 0 0 1px rgba(150,190,240,.14);
}
#hpfill {
  height: 100%;
  width: 100%;
  border-radius: 999px;
  background: linear-gradient(90deg, #3fd07a, #7ef0a6);
  box-shadow: 0 0 12px rgba(80,230,130,.4);
  transition: width .18s ease-out, background .3s;
}
#hpfill.low { background: linear-gradient(90deg, #e08c2a, #ffc880); box-shadow: 0 0 12px rgba(255,170,60,.4); }
#hpfill.crit { background: linear-gradient(90deg, #d8283c, #ff8a95); box-shadow: 0 0 14px rgba(255,70,90,.5); animation: hp-flash .6s infinite; }
@keyframes hp-flash { 50% { filter: brightness(1.45); } }
/* the readout sits beside the bar, not stamped on top of it */
#hptext {
  position: absolute; right: 7px; top: 50%;
  transform: translateY(-50%);
  font: 700 9px var(--font-body); line-height: 1;
  color: rgba(255,255,255,.92); text-shadow: 0 1px 2px rgba(0,0,0,.8);
  letter-spacing: .06em;
}
/* K/D as two labelled stats. Two crossed-swords and skull emoji rendered at
   13px were the most dated glyphs in the HUD and read differently on every
   platform; words do not. */
.hud-stats {
  margin-top: .5rem;
  display: flex; gap: .9rem;
  font-size: .74rem; letter-spacing: .07em; text-transform: uppercase;
  color: #6f88a8;
}
.hud-stats .stat { display: inline-flex; align-items: baseline; gap: .3rem; }
.hud-stats b {
  font-size: .96rem; font-weight: 700; letter-spacing: 0;
  color: #dceaf8; font-variant-numeric: tabular-nums;
}
.conn-row { display: flex; align-items: baseline; gap: .35rem; }

.hud-topright {
  top: 14px; right: 14px;
  display: flex; flex-direction: column; align-items: flex-end; gap: .18rem;
  text-align: right; line-height: 1.4; font-size: .9rem;
}
/* connection reads as a dot + label pair rather than a bullet glyph in prose */
#hud-conn { display: inline-flex; align-items: center; gap: .32rem; font-size: .82rem; }
#hud-conn::before {
  content: ''; width: 6px; height: 6px; border-radius: 50%;
  background: currentColor; box-shadow: 0 0 8px currentColor;
}
.conn-ok { color: #7fe6a0; }
.conn-bad { color: var(--blood); animation: hp-flash .8s infinite; }
#hud-online { color: #cfe4f8; }
#hud-ping { color: var(--frost-dim); font-size: .78rem; font-variant-numeric: tabular-nums; }
#hud-muted { color: var(--frost-dim); font-size: .8rem; }
#hud-round { color: var(--frost-gold); font-variant-numeric: tabular-nums; }

/* Leaderboard: a refined table with a thin frost grid */
/* No transition on `top`. A property whose value comes from a custom property
   is not re-evaluated as a transition trigger, so declaring one here left the
   panel stuck at its old offset when --mute-shift changed. Snapping is the
   right behaviour anyway — and it costs no animated layout. */
.hud-leaderboard {
  top: calc(112px + var(--mute-shift, 0px));
  right: 14px; min-width: 182px; font-size: .92rem;
}
.lb-title {
  font-family: var(--font-display); font-size: .64rem; font-weight: 700;
  letter-spacing: .22em; text-transform: uppercase;
  color: #7f96b8; margin-bottom: .45rem; padding-bottom: .38rem;
  border-bottom: 1px solid rgba(150,190,240,.14); text-align: left;
}

/* The board holds the whole field but shows a fixed five rows — the panel must
   never grow into the choice cards or off the bottom of a short window, so the
   overflow is scrolled rather than laid out. `max-height` is written by
   Hud.leaderboard from a real row measurement (fonts load late; five rows in
   hard px would be five-and-a-bit once Cormorant swaps in).

   The scroller stays `pointer-events: none` like the rest of the HUD. Aim and
   casting are bound to the canvas, so an interactive panel in the top-right
   corner would carve a dead zone out of the play area; the wheel is delivered
   by a window-level listener that hit-tests the panel instead (Hud._lbWheel).
   That also means the native scrollbar could never be dragged, so it is hidden
   and replaced by cues that read at a glance: edge fades and a footer. */
.lb-scroll { position: relative; }
#leaderboard {
  overflow-y: auto; overflow-x: hidden;
  scrollbar-width: none;                       /* see the note above */
  overscroll-behavior: contain;
}
#leaderboard::-webkit-scrollbar { width: 0; height: 0; }
/* "there is more, in both directions" — pure paint, no layout, and each fade
   is switched by one class the scroll handler toggles */
.lb-scroll::before, .lb-scroll::after {
  content: ''; position: absolute; left: 0; right: 0; height: 18px;
  pointer-events: none; opacity: 0; transition: opacity .18s ease-out;
}
.lb-scroll::before {
  top: 0; background: linear-gradient(180deg, rgba(12,19,32,.96), transparent);
}
.lb-scroll::after {
  bottom: 0; background: linear-gradient(0deg, rgba(12,19,32,.96), transparent);
}
.lb-scroll.has-above::before, .lb-scroll.has-below::after { opacity: 1; }

/* Footer: the explicit half of the affordance. The fades say "more"; this says
   how much more and how to reach it. */
#lb-more {
  margin-top: .3rem; padding-top: .28rem;
  border-top: 1px solid rgba(150,200,235,.18);
  font-size: .72rem; letter-spacing: .06em; text-align: center;
  color: var(--frost-dim);
  transition: color .2s ease-out;
}
#lb-more .lb-caret { display: inline-block; animation: lb-nudge 1.9s ease-in-out infinite; }
/* transform-only, on one glyph — the compositor handles it without a repaint,
   and it stops for good the first time the player actually scrolls */
@keyframes lb-nudge { 0%, 100% { transform: translateY(-1px); } 50% { transform: translateY(2px); } }
#lb-more.used .lb-caret { animation: none; }
#lb-more.at-end { color: rgba(150,200,235,.32); }
@media (prefers-reduced-motion: reduce) { #lb-more .lb-caret { animation: none; } }
/* Rows carry no dividers any more — five hairlines stacked in a 130px box read
   as a spreadsheet. Rhythm comes from the line height, and the only row that
   gets a surface is yours. */
.lb-row {
  position: relative;
  display: flex; justify-content: space-between; gap: .7rem; align-items: baseline;
  line-height: 1.5; padding: .1rem .42rem;
  border-radius: 6px;
}
.lb-name { color: #c4d5e8; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lb-score { color: #6f88a8; font-variant-numeric: tabular-nums; letter-spacing: .04em; flex: none; }
.lb-row.me { background: rgba(120,170,235,.12); box-shadow: inset 0 0 0 1px rgba(140,190,245,.14); }
.lb-row.me .lb-name { color: #eaf4ff; font-weight: 600; }
.lb-row.me .lb-score { color: var(--frost); }
.lb-row.top .lb-name { color: var(--frost-gold); font-weight: 600; }
.lb-row.top .lb-score { color: var(--frost-gold); }
.lb-row .lb-bot { opacity: .52; }

#killfeed {
  position: absolute;
  top: 14px; left: 50%;
  transform: translateX(-50%);
  display: flex; flex-direction: column; gap: 4px;
  align-items: center;
  font-size: .85rem;
}
.kf-row {
  padding: .2rem .8rem;
  background: rgba(10,10,20,.72);
  border: 1px solid rgba(232,224,205,.1);
  border-radius: 3px;
  animation: kf-in .25s ease-out both;
}
.kf-row .k { color: var(--fire); font-weight: 700; }
.kf-row .v { color: var(--dim); }
@keyframes kf-in { from { opacity: 0; transform: translateY(-8px); } }

#minimap {
  position: absolute;
  bottom: 16px; right: 16px;
  border: 1px solid rgba(150,190,240,.18);
  border-radius: 13px;
  background: rgba(9,14,26,.86);
  box-shadow:
    inset 0 1px 0 rgba(200,226,255,.12),
    0 14px 34px -16px rgba(0,4,14,.95);
}

/* spell bar: a plinth holding the slots */
#spellbar {
  position: absolute;
  bottom: 16px; left: 50%;
  transform: translateX(-50%);
  display: flex; gap: 8px;
  padding: 8px;
  background: linear-gradient(160deg, rgba(32,42,66,.8), rgba(11,16,30,.86));
  border: 1px solid rgba(150,190,240,.16);
  border-radius: 17px;
  box-shadow:
    inset 0 1px 0 rgba(200,226,255,.12),
    0 18px 40px -18px rgba(0,4,14,.95);
}
.slot {
  position: relative;
  width: 56px; height: 56px;
  background: linear-gradient(180deg, rgba(46,58,86,.7), rgba(16,22,38,.8));
  border: 1px solid rgba(150,190,240,.16);
  border-radius: 12px;
  display: grid; place-items: center;
  box-shadow: inset 0 1px 0 rgba(200,226,255,.12);
  transition: border-color .2s, box-shadow .2s;
}
/* only the cooldown-ring svg is absolutely-positioned & rotated, not the icon */
.slot > svg { position: absolute; inset: 0; width: 100%; height: 100%; transform: rotate(-90deg); }
.spell-ico { width: 28px; height: 28px; display: block; filter: drop-shadow(0 1px 2px rgba(0,0,0,.5)); }
.cd-ring {
  fill: none;
  stroke: var(--fire);
  stroke-width: 2.5;
  stroke-dasharray: 106.8;
  stroke-dashoffset: 0;
  opacity: 0;
  transition: stroke-dashoffset .1s linear;
}
#slot-s1 .cd-ring { stroke: var(--stonec); }
#slot-s2 .cd-ring { stroke: var(--ice); }
.slot-icon { font-size: 24px; filter: grayscale(0); transition: filter .2s, opacity .2s; }
/* The key cap tucks INSIDE the slot now. Hung below the tile it forced the
   whole bar to reserve dead space for a 9px label, and it read as a sticker
   stuck to the bottom edge. */
.slot-key {
  position: absolute;
  bottom: 3px; left: 50%;
  transform: translateX(-50%);
  font: 700 8px var(--font-body); letter-spacing: .08em;
  padding: 1px 5px;
  color: #a8c2e0;
  background: rgba(6,10,20,.72);
  border-radius: 5px;
  white-space: nowrap;
  pointer-events: none;
}
.slot-cd {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  font-weight: 700; font-size: 17px;
  color: #fff; text-shadow: 0 1px 3px #000;
}
.slot.cooling { background: linear-gradient(180deg, rgba(28,34,54,.7), rgba(12,16,28,.8)); }
.slot.cooling .slot-icon { filter: grayscale(1); opacity: .3; }
.slot.cooling .spell-ico { filter: grayscale(1) drop-shadow(0 1px 2px rgba(0,0,0,.5)); opacity: .3; }
.slot.cooling .cd-ring { opacity: 1; }
.slot.cooling .slot-key { opacity: .45; }
.slot.ready { animation: slot-ready .3s ease-out; box-shadow: inset 0 1px 0 rgba(200,226,255,.12), 0 0 16px rgba(255,210,60,.35); }
@keyframes slot-ready { 0% { transform: scale(1.12); } }
.slot.deny { animation: slot-deny .25s; }
@keyframes slot-deny {
  25% { transform: translateX(-3px); border-color: var(--blood); }
  75% { transform: translateX(3px); border-color: var(--blood); }
}

/* bat form: casting is locked while scattered */
#spellbar.batform .slot { opacity: .35; filter: grayscale(.6); }

/* next-unlock prompt: the escalation carrot above the spell bar */
#next-unlock {
  position: absolute;
  /* clears the spell bar (bottom:16px, 78px tall) by 12px — at 86px the
     baseline sat straight on top of the slot icons */
  bottom: 106px; left: 50%;
  transform: translateX(-50%);
  font-family: var(--font-display);
  font-weight: 700; font-size: .8rem; letter-spacing: .12em;
  color: #ffd88a;
  text-shadow: 0 1px 3px #000;
  white-space: nowrap;
  animation: next-unlock-pulse 2.2s ease-in-out infinite;
  pointer-events: none;
}
@keyframes next-unlock-pulse {
  0%, 100% { opacity: .55; }
  50% { opacity: 1; text-shadow: 0 0 14px rgba(255,190,90,.6), 0 1px 3px #000; }
}

/* class-slot unlocks: s3/s4/s5 reveal with a pop as kills come in */
#slot-s3 .cd-ring { stroke: #5ab0ff; }
#slot-s4 .cd-ring { stroke: #ff5a2c; }
#slot-s5 .cd-ring { stroke: #ffd23c; }
.slot.unlocking { animation: slot-unlock .7s cubic-bezier(.2, 1.4, .4, 1) both; }
@keyframes slot-unlock {
  0% { transform: scale(0) rotate(-30deg); }
  60% { transform: scale(1.25); box-shadow: 0 0 30px rgba(255, 138, 60, .9); }
  100% { transform: scale(1); box-shadow: 0 0 14px rgba(255, 138, 60, .45); }
}
/* spell-bar morph: every slot pulses when the class transforms */
.slot.morph { animation: slot-morph .8s cubic-bezier(.2, 1.3, .4, 1) both; }
@keyframes slot-morph {
  0% { transform: scale(1); filter: brightness(1); }
  35% { transform: scale(1.28) rotate(4deg); filter: brightness(2); box-shadow: 0 0 34px var(--class-glow, rgba(255,138,60,.9)); }
  100% { transform: scale(1); filter: brightness(1); box-shadow: 0 0 12px var(--class-glow, rgba(255,138,60,.4)); }
}
/* awakened spell bar carries the class color */
#spellbar.awakened .slot { border-color: var(--class-color, rgba(255,138,60,.55)); }

/* ===== Awakening: class choice cards (non-blocking) =====
   Performance contract for this whole block, because these cards sit on top
   of a live 60fps canvas:
     - NO backdrop-filter. The obvious way to get the glass look would sample
       and blur the game canvas every single frame; the depth here is faked
       with layered gradients, which the compositor draws once.
     - No looping animation touches anything but transform/opacity, so nothing
       here can force a repaint while the fight is running.
     - box-shadow / border-color move only on :hover — one short transition
       under the pointer, never a loop.
     - `contain` walls the cards off so their paint can never invalidate the
       rest of the HUD.                                                     */
/* The cards live in the bottom-left corner, stacked up from just above the
   controls hint. That corner is the quietest part of the HUD — the board and
   the minimap own the right side — so the cards keep their full size instead
   of being squeezed into a corridor. `--rail-bottom` is measured (Hud._fitOffer)
   rather than hard-coded because the floor is not always the hint line: on a
   narrow window the spell-bar plinth reaches across into this column and
   becomes the floor instead. `--acs` still scales the stack down, but only in
   the corner case of a genuinely short window. */
#awaken-choice {
  position: absolute;
  left: 14px;
  bottom: var(--rail-bottom, 46px);
  --acs: 1.15;                                /* set by Hud._fitOffer (AC_BASE) */
  display: flex; flex-direction: column;
  gap: calc(9px * var(--acs));
  width: calc(336px * var(--acs));
  /* Deliberately NOT a scroll container. `overflow-y: auto` forces overflow-x
     to `auto` as well, and the hover state (translateX + scale) then grew
     straight into the clip edge — the card body and its glow were cut off.
     Nothing here clips now; the fit is guaranteed by size. */
  overflow: visible;
  animation: ac-in .4s cubic-bezier(.2, 1.2, .4, 1) both;
}
/* the stack enters from its own edge and leans toward open screen on hover —
   leaning the other way would push the hovered card off the left edge */
@keyframes ac-in { from { opacity: 0; transform: translateX(-44px); } }
#ac-cards { display: flex; flex-direction: column; gap: calc(9px * var(--acs)); }

.ac-title {
  font-family: var(--font-display);
  font-weight: 700; font-size: calc(.96rem * var(--acs)); letter-spacing: .15em;
  color: #ffe8c0;
  /* static shadow + an opacity breath: the old version animated text-shadow,
     which repaints the glyphs every frame mid-fight */
  text-shadow: 0 0 18px rgba(255,160,60,.75);
  text-align: center;
  animation: ac-breathe 2s ease-in-out infinite;
}
@keyframes ac-breathe { 0%, 100% { opacity: .74; } 50% { opacity: 1; } }

.ac-card {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  contain: layout paint style;
  display: grid;
  grid-template-columns: calc(45px * var(--acs)) 1fr;
  grid-template-areas:
    "sigil name"
    "sigil fantasy"
    "kit   kit";
  align-items: center;
  column-gap: calc(.7rem * var(--acs));
  padding: calc(.56rem * var(--acs)) calc(.76rem * var(--acs)) calc(.58rem * var(--acs)) calc(.78rem * var(--acs));
  text-align: left;
  color: var(--ink);
  cursor: pointer;
  font-family: inherit;
  /* #hud is pointer-events:none so the canvas underneath stays clickable —
     the cards are the one part of it that MUST take the mouse, and only the
     cards, so the title and the gaps between them still pass clicks through */
  pointer-events: auto;
  /* --ac-edge is the class colour lifted toward white. A raw accent at 1px
     reads fine for the Pyromancer's orange and nearly vanishes for the
     Necromancer's violet (relative luminance .354 vs .182), so the frame is
     drawn in the lifted tone and the identity colour is carried by the name,
     the sigil ring and the side bar instead. */
  border: 1px solid var(--ac-edge, rgba(255,190,140,.85));
  border-radius: calc(13px * var(--acs));
  /* faked glass: a class-tinted bloom from the top-left over a dark slab */
  background:
    radial-gradient(115% 90% at 6% 0%, var(--ac-wash, rgba(255,138,60,.16)), transparent 62%),
    linear-gradient(180deg, rgba(28,26,52,.95), rgba(12,11,26,.96));
  /* the inset ring doubles the edge so the card reads as a closed shape on
     every side — the bottom edge in particular */
  box-shadow: 0 6px 20px rgba(0,0,0,.55),
              inset 0 0 0 1px var(--ac-wash, rgba(255,138,60,.16)),
              inset 0 1px 0 rgba(255,255,255,.07);
  transition: transform .16s cubic-bezier(.2,.9,.3,1.25),
              box-shadow .16s ease-out,
              border-color .16s ease-out;
  animation: ac-card-in .34s cubic-bezier(.2,1.2,.4,1) both;
  animation-delay: calc(var(--i, 0) * .07s);
}
@keyframes ac-card-in { from { opacity: 0; transform: translateX(-26px) scale(.96); } }

.ac-card:hover,
.ac-card:focus-visible {
  transform: translateX(8px) scale(1.035);
  border-color: #fff;
  box-shadow: 0 14px 34px rgba(0,0,0,.62),
              0 0 34px var(--ac-glow, rgba(255,138,60,.45)),
              inset 0 0 0 1px var(--ac-glow, rgba(255,138,60,.45)),
              inset 0 1px 0 rgba(255,255,255,.14);
  outline: none;
}
.ac-card:active { transform: translateX(8px) scale(.995); }

/* left accent bar — brightens with the card */
.ac-card .ac-bar {
  position: absolute; left: 0; top: 10%; bottom: 10%;
  width: 3px; border-radius: 0 3px 3px 0;
  background: linear-gradient(180deg, transparent, var(--ac-color, #ff9a4c), transparent);
  opacity: .75;
  transition: opacity .16s ease-out;
}
.ac-card:hover .ac-bar { opacity: 1; }

/* the school's sigil: its basic attack, ringed in the class colour */
.ac-card .ac-sigil {
  grid-area: sigil;
  display: grid; place-items: center;
  width: calc(45px * var(--acs)); height: calc(45px * var(--acs));
  font-size: calc(1.46rem * var(--acs)); line-height: 1;
  border-radius: 50%;
  border: 1px solid var(--ac-color, rgba(255,138,60,.5));
  background: radial-gradient(circle at 50% 45%, var(--ac-wash, rgba(255,138,60,.18)), rgba(0,0,0,.35) 72%);
  transition: transform .16s cubic-bezier(.2,.9,.3,1.4);
}
.ac-card:hover .ac-sigil { transform: scale(1.1) rotate(-6deg); }

.ac-card .ac-key {
  position: absolute;
  top: 7px; right: 7px;
  font-size: calc(11px * var(--acs)); font-weight: 800; line-height: 1.35;
  min-width: calc(18px * var(--acs)); text-align: center;
  padding: 1px calc(6px * var(--acs));
  color: var(--dim);
  background: rgba(10,10,22,.85);
  border: 1px solid rgba(232,224,205,.22);
  border-radius: 4px;
  transition: color .16s ease-out, border-color .16s ease-out;
}
.ac-card:hover .ac-key { color: var(--ink); border-color: var(--ac-color, #ff9a4c); }

.ac-card .ac-name {
  grid-area: name;
  min-width: 0;
  font-family: var(--font-display);
  font-weight: 700; font-size: calc(1.19rem * var(--acs)); letter-spacing: .06em; line-height: 1.25;
  color: var(--ac-color, #ff9a4c);
  padding-right: calc(27px * var(--acs));   /* clear of the key badge */
}
/* One line, always. Left to wrap, the Necromancer's two-liner alone made every
   card 13px taller, so the stack's height would depend on which schools the
   draw happened to deal. The full line still shows on hover (title). */
.ac-card .ac-fantasy {
  grid-area: fantasy;
  min-width: 0;
  /* Held near its old absolute size while everything around it grew: at .76rem
     the Necromancer's line overran by 5px and picked up an ellipsis it never
     had. The genuinely long lines (Aeromancer, Cryomancer) still clip — they
     always did — and the tooltip carries the full text. */
  font-size: calc(.77rem * var(--acs)); font-style: italic; line-height: 1.3;
  color: var(--dim);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* The starting kit: only the three spells this school hands you on the spot.
   F / R / 5 arrive later on the kill ladder and are summed up in one line
   instead of shown as dead squares. */
.ac-card .ac-kit {
  grid-area: kit;
  display: flex; align-items: center; gap: calc(6px * var(--acs));
  margin-top: calc(.4rem * var(--acs)); padding-top: calc(.38rem * var(--acs));
  border-top: 1px solid rgba(232,224,205,.1);
}
/* icon and key side by side rather than stacked — the same information in
   roughly two thirds of the height */
.ac-card .ac-chip {
  display: flex; align-items: center; gap: calc(5px * var(--acs));
  padding: calc(3px * var(--acs)) calc(7px * var(--acs)) calc(3px * var(--acs)) calc(6px * var(--acs));
  border-radius: calc(8px * var(--acs));
  background: linear-gradient(180deg, rgba(255,255,255,.07), rgba(255,255,255,.025));
  border: 1px solid rgba(232,224,205,.14);
  transition: border-color .16s ease-out;
}
.ac-card:hover .ac-chip { border-color: var(--ac-wash, rgba(255,138,60,.16)); }
.ac-card .ac-chip .ci { font-size: calc(1.01rem * var(--acs)); line-height: 1.15; }
.ac-card .ac-chip .ck {
  font-size: calc(10px * var(--acs)); font-weight: 800; letter-spacing: .04em;
  color: var(--dim); line-height: 1;
}
.ac-card .ac-later {
  margin-left: auto;
  font-size: calc(.75rem * var(--acs)); font-style: italic; letter-spacing: .02em;
  color: rgba(232,224,205,.42);
  white-space: nowrap;
}

/* hover sheen: a single skewed highlight swept with transform only */
.ac-card .ac-sheen {
  position: absolute; inset: -50% -60%;
  z-index: 2; pointer-events: none;
  background: linear-gradient(102deg, transparent 44%, rgba(255,255,255,.13) 50%, transparent 56%);
  transform: translateX(-115%);
  transition: transform .6s ease-out;
}
.ac-card:hover .ac-sheen { transform: translateX(115%); }

@media (prefers-reduced-motion: reduce) {
  #awaken-choice, .ac-card, .ac-title { animation: none; }
  .ac-card .ac-sheen { display: none; }
}

/* ===== Awakening ceremony ===== */
#awakenbanner {
  position: absolute; inset: 0;
  display: grid; place-content: center; justify-items: center;
  gap: .4rem;
  overflow: hidden;
  pointer-events: none;
}
#awakenbanner .ab-rays {
  position: absolute; top: 50%; left: 50%;
  width: 140vmax; height: 140vmax;
  translate: -50% -50%;
  background: repeating-conic-gradient(var(--class-glow, rgba(255,138,60,.5)) 0deg 5deg, transparent 5deg 22deg);
  -webkit-mask: radial-gradient(closest-side, rgba(0,0,0,.8), transparent 68%);
  mask: radial-gradient(closest-side, rgba(0,0,0,.8), transparent 68%);
  animation: ab-rays-fade 2.6s ease-out both, ab-rays-spin 9s linear infinite;
}
@keyframes ab-rays-fade {
  0% { opacity: 0; } 8% { opacity: .5; } 70% { opacity: .38; } 100% { opacity: 0; }
}
@keyframes ab-rays-spin { to { rotate: 360deg; } }
#awakenbanner .ab-ring {
  position: absolute; top: 50%; left: 50%;
  width: 40px; height: 40px;
  translate: -50% -50%;
  border: 4px solid var(--class-color, #ff9a4c);
  border-radius: 50%;
  animation: ab-ring 1.1s cubic-bezier(.2,.7,.3,1) .12s both;
}
@keyframes ab-ring {
  0% { width: 40px; height: 40px; opacity: .95; border-width: 5px; }
  100% { width: 1300px; height: 1300px; opacity: 0; border-width: 1px; }
}
#awakenbanner .ab-title {
  font-family: var(--font-display);
  font-weight: 900; font-size: 3.6rem; letter-spacing: .18em;
  color: #fff;
  text-shadow:
    0 0 14px var(--class-glow, rgba(255,138,60,.8)),
    0 0 50px var(--class-glow, rgba(255,138,60,.8)),
    3px 3px 0 var(--class-color, #ff9a4c),
    0 2px 6px #000;
  white-space: nowrap;
  animation: ab-slam 2.6s cubic-bezier(.16,1.3,.3,1) both;
}
@keyframes ab-slam {
  0% { opacity: 0; transform: scale(2.6); filter: blur(10px); }
  11% { opacity: 1; transform: scale(1); filter: blur(0); }
  84% { opacity: 1; }
  100% { opacity: 0; transform: scale(.97); }
}
#awakenbanner .ab-sub {
  font-family: var(--font-display);
  font-weight: 700; font-size: 1.02rem; letter-spacing: .3em;
  color: var(--class-color, #ff9a4c);
  text-shadow: 0 1px 4px #000;
  text-transform: uppercase;
  animation: ab-sub 2.6s ease-out both;
}
@keyframes ab-sub {
  0%, 14% { opacity: 0; transform: translateY(14px); }
  28% { opacity: 1; transform: translateY(0); }
  84% { opacity: 1; }
  100% { opacity: 0; }
}
/* full-screen aura wash behind the banner */
#hud.awakening::before {
  content: '';
  position: absolute; inset: 0;
  pointer-events: none;
  background: radial-gradient(closest-side, var(--class-glow, rgba(255,138,60,.28)), transparent 70%);
  animation: awaken-wash 1.6s ease-out both;
}
@keyframes awaken-wash {
  0% { opacity: 0; }
  20% { opacity: 1; }
  100% { opacity: 0; }
}

#unlock-toast {
  position: absolute;
  top: 31%; left: 50%;
  transform: translateX(-50%);
  display: flex; align-items: center; gap: .6rem;
  padding: .55rem 1.5rem;
  font-family: var(--font-display);
  font-weight: 700; font-size: 1.12rem; letter-spacing: .12em;
  color: #ffeed4;
  background: linear-gradient(180deg, rgba(34,26,16,.92), rgba(20,14,8,.92));
  border: 1px solid var(--class-color, rgba(255,160,70,.6));
  border-radius: 6px;
  box-shadow: 0 0 34px var(--class-glow, rgba(255,130,40,.4));
  white-space: nowrap;
  animation: toast-punch 2.6s cubic-bezier(.2,1.4,.4,1) both;
}
#unlock-toast .toast-icon { font-size: 1.7rem; }
#unlock-toast kbd {
  font-size: .78rem; font-weight: 800;
  padding: .1rem .5rem;
  color: #1a1206;
  background: linear-gradient(180deg, #ffd88a, var(--class-color, #ff9a4c));
  border-radius: 4px;
}
@keyframes toast-punch {
  0% { opacity: 0; transform: translateX(-50%) scale(.4); }
  9% { opacity: 1; transform: translateX(-50%) scale(1.12); }
  14% { transform: translateX(-50%) scale(1); }
  86% { opacity: 1; }
  100% { opacity: 0; transform: translateX(-50%) translateY(-8px); }
}

#hud-hint {
  position: absolute;
  bottom: 16px; left: 14px;
  font-size: .74rem;
  color: #565278;
  letter-spacing: .03em;
}

/* kill banner */
#killbanner {
  position: absolute;
  top: 24%; left: 50%;
  transform: translateX(-50%);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.7rem;
  letter-spacing: .1em;
  color: var(--fire-hot);
  text-shadow: 0 0 24px rgba(255,138,60,.8), 0 2px 4px #000;
  animation: banner 1.6s ease-out both;
  white-space: nowrap;
}
@keyframes banner {
  0% { opacity: 0; transform: translateX(-50%) scale(1.6); }
  12% { opacity: 1; transform: translateX(-50%) scale(1); }
  80% { opacity: 1; }
  100% { opacity: 0; }
}

/* death screen */
#deathscreen {
  position: absolute; inset: 0;
  display: grid; place-content: center; text-align: center;
  background: radial-gradient(closest-side, rgba(60,8,16,.35), rgba(10,10,20,.75));
  animation: death-in .4s ease-out both;
}
@keyframes death-in { from { opacity: 0; } }
.death-title {
  font-family: var(--font-display);
  font-weight: 900; font-size: 3rem; letter-spacing: .18em;
  color: var(--blood);
  text-shadow: 0 0 30px rgba(255,77,94,.6);
}
.death-by { color: var(--dim); margin-top: .4rem; }
.death-count { margin-top: 1.2rem; font-size: 1.1rem; }
#respawn-count { font-weight: 700; color: var(--fire-hot); font-size: 1.4rem; }

/* damage vignette on local hit */
#hud::after {
  content: '';
  position: absolute; inset: 0;
  pointer-events: none;
  box-shadow: inset 0 0 120px 40px rgba(255, 40, 60, 0);
  transition: box-shadow .5s ease-out;
}
#hud.hurt::after {
  box-shadow: inset 0 0 120px 40px rgba(255, 40, 60, .45);
  transition: box-shadow .05s;
}

/* persistent danger vignette while below 25 HP */
#hud.lowhp::after {
  animation: lowhp-pulse 1.6s ease-in-out infinite;
}
@keyframes lowhp-pulse {
  0%, 100% { box-shadow: inset 0 0 110px 30px rgba(255, 40, 60, .16); }
  50% { box-shadow: inset 0 0 130px 44px rgba(255, 40, 60, .3); }
}
