/* ══════════════════════════════════════════════════════════════════════
   Daily Dungeon — game client

   The design system is the landing page's, lifted verbatim: same fonts,
   same palette, same 16px radius, same glass surfaces and the same easing
   curve. Anything new here is a game component the landing page does not
   have (grid map, HP bars, combat log, attribute rows).

   Self-hosted fonts, no external requests (DSGVO, same as the landing page).
   ══════════════════════════════════════════════════════════════════════ */

@font-face {
  font-family: 'Orbitron';
  font-style: normal;
  font-weight: 700 900;
  font-display: swap;
  src: url('../../fonts/orbitron-latin.woff2') format('woff2');
}
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 400 500;
  font-display: swap;
  src: url('../../fonts/jetbrains-mono-latin.woff2') format('woff2');
}

:root {
  --bg-deep: #020203;
  --bg-base: #050506;
  --bg-elevated: #0a0a0c;
  --surface: rgba(255, 255, 255, 0.05);
  --surface-strong: rgba(255, 255, 255, 0.08);
  --foreground: #EDEDEF;
  --muted: #8A8F98;
  --gold: #F0B429;
  --gold-glow: rgba(240, 180, 41, 0.25);
  --ember: #E8590C;
  --arcane: #7C6AE8;
  --arcane-glow: rgba(124, 106, 232, 0.22);
  --border: rgba(255, 255, 255, 0.08);
  --radius: 16px;
  --ease: cubic-bezier(0.16, 1, 0.3, 1);
  --font-display: 'Orbitron', sans-serif;
  --font-body: 'JetBrains Mono', monospace;
  --hp: #dc2626;
  --hp-light: #f87171;
  --mana: #38BDF8;
  --xp: #8B5CF6;
  --good: #4ADE80;
  --rarity-common: #9CA3AF;
  --rarity-uncommon: #4ADE80;
  --rarity-rare: #38BDF8;
  --rarity-epic: #A78BFA;
  --rarity-legendary: #F0B429;
  color-scheme: dark;
}

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

body {
  background: linear-gradient(180deg, #0a0a0f 0%, var(--bg-deep) 100%);
  color: var(--foreground);
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.6;
  min-height: 100dvh;
  overflow-x: hidden;
  -webkit-text-size-adjust: 100%;
}
::selection { background: var(--gold); color: #0a0a0c; }
a { color: var(--gold); }

/* ---------- Ambient background (same blobs as the landing page) ------ */
.ambient { position: fixed; inset: 0; z-index: 0; pointer-events: none; overflow: hidden; }
.blob { position: absolute; border-radius: 50%; filter: blur(60px); opacity: 0.12; will-change: transform; }
.blob--gold   { width: 480px; height: 480px; background: var(--gold);   top: -12%; left: -10%;  animation: drift1 26s var(--ease) infinite alternate; }
.blob--arcane { width: 560px; height: 560px; background: var(--arcane); top: 34%;  right: -16%; animation: drift2 32s var(--ease) infinite alternate; }
.blob--ember  { width: 400px; height: 400px; background: var(--ember);  bottom: -14%; left: 20%; opacity: 0.08; animation: drift3 38s var(--ease) infinite alternate; }
@keyframes drift1 { to { transform: translate(70px, 90px) scale(1.15); } }
@keyframes drift2 { to { transform: translate(-90px, -60px) scale(0.9); } }
@keyframes drift3 { to { transform: translate(60px, -80px) scale(1.1); } }

/* ---------- Shell ---------------------------------------------------- */
.topbar {
  position: sticky; top: 0; z-index: 50;
  display: flex; align-items: center; gap: 12px;
  padding: 12px 16px;
  background: rgba(5, 5, 6, 0.62);
  backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);
  border-bottom: 1px solid var(--border);
}
.topbar__logo {
  font-family: var(--font-display); font-weight: 900; font-size: 15px;
  letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--foreground); text-decoration: none;
  display: flex; align-items: center; gap: 8px; flex-shrink: 0;
}
.topbar__logo svg { flex-shrink: 0; }
.topbar__spacer { flex: 1; }
.topbar__stat {
  display: flex; align-items: center; gap: 6px;
  font-size: 13px; color: var(--muted); font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.topbar__stat b { color: var(--gold); font-weight: 500; }
@media (max-width: 560px) { .topbar__stat--hide { display: none; } }

main { position: relative; z-index: 2; max-width: 900px; margin: 0 auto; padding: 20px 16px 96px; }

.view { animation: fadeUp 340ms var(--ease); }
@keyframes fadeUp { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }

h1 {
  font-family: var(--font-display); font-weight: 900;
  font-size: clamp(26px, 6vw, 42px); line-height: 1.1;
  text-transform: uppercase; letter-spacing: 0.02em; margin-bottom: 8px;
}
h2 {
  font-family: var(--font-display); font-weight: 700;
  font-size: clamp(18px, 4vw, 24px); text-transform: uppercase;
  letter-spacing: 0.04em; margin-bottom: 14px;
}
h3 { font-family: var(--font-display); font-size: 14px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; }
.tag { font-size: 12px; letter-spacing: 0.25em; text-transform: uppercase; color: var(--gold); margin-bottom: 10px; }
.muted { color: var(--muted); }
.small { font-size: 13px; }
.center { text-align: center; }
.gold-text { color: var(--gold); }
.num { font-variant-numeric: tabular-nums; }

/* ---------- Cards ---------------------------------------------------- */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px);
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.06);
  padding: 20px;
  margin-bottom: 16px;
}
.card--tight { padding: 14px 16px; }
.card--interactive { transition: transform 250ms var(--ease), border-color 250ms var(--ease), box-shadow 250ms var(--ease); }
.card--interactive:hover {
  transform: translateY(-4px);
  border-color: rgba(240, 180, 41, 0.35);
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.06), 0 16px 40px rgba(0,0,0,0.5), 0 0 32px var(--gold-glow);
}
.card--locked { opacity: 0.5; }
.card__head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 10px; }

.grid2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 14px; }

/* ---------- Buttons -------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 46px; padding: 12px 22px;
  border-radius: 12px;
  font-family: var(--font-body); font-size: 15px; font-weight: 500;
  text-decoration: none; cursor: pointer;
  border: 1px solid transparent; color: var(--foreground);
  background: var(--surface-strong);
  transition: transform 180ms var(--ease), box-shadow 220ms var(--ease), background 180ms var(--ease);
}
.btn:active { transform: scale(0.97); }
.btn:disabled { opacity: 0.42; cursor: not-allowed; }
.btn:disabled:active { transform: none; }
.btn--primary {
  background: linear-gradient(120deg, var(--gold), #d99a1c);
  color: #0a0a0c; font-weight: 500;
  box-shadow: 0 0 26px var(--gold-glow), 0 4px 18px rgba(0,0,0,0.45);
}
.btn--primary:hover:not(:disabled) { transform: translateY(-2px); box-shadow: 0 0 46px var(--gold-glow), 0 8px 24px rgba(0,0,0,0.5); }
.btn--ghost { background: var(--surface); border-color: var(--border); }
.btn--ghost:hover:not(:disabled) { background: var(--surface-strong); }
.btn--danger { background: rgba(220, 38, 38, 0.18); border-color: rgba(220,38,38,0.35); color: #fca5a5; }
.btn--wide { width: 100%; }
.btn--sm { min-height: 36px; padding: 8px 14px; font-size: 13px; border-radius: 10px; }
.btn-row { display: flex; gap: 10px; flex-wrap: wrap; }
.btn-col { display: flex; flex-direction: column; gap: 10px; }
a:focus-visible, button:focus-visible, input:focus-visible {
  outline: 2px solid var(--gold); outline-offset: 3px;
}

/* ---------- Forms ---------------------------------------------------- */
label { display: block; font-size: 13px; color: var(--muted); margin-bottom: 6px; }
input[type=text], input[type=password] {
  width: 100%; min-height: 46px; padding: 12px 14px;
  background: rgba(255,255,255,0.04);
  border: 1px solid var(--border); border-radius: 12px;
  color: var(--foreground); font-family: var(--font-body); font-size: 15px;
}
input::placeholder { color: rgba(138,143,152,0.7); }
.field { margin-bottom: 14px; }

/* ---------- Bars ----------------------------------------------------- */
.bar { height: 10px; border-radius: 999px; background: rgba(255,255,255,0.07); overflow: hidden; }
.bar__fill { transition: width 260ms var(--ease); }
@media (prefers-reduced-motion: reduce) { .bar__fill { transition: none; } }
.bar__fill { height: 100%; border-radius: 999px; transition: width 520ms var(--ease); }
.bar--hp   .bar__fill { background: linear-gradient(90deg, var(--hp), var(--hp-light)); }
.bar--good .bar__fill { background: linear-gradient(90deg, #16a34a, var(--good)); }
.bar--mana .bar__fill { background: linear-gradient(90deg, #0284c7, var(--mana)); }
.bar--xp   .bar__fill { background: linear-gradient(90deg, #6d28d9, var(--xp)); }
/* The creature's health had a fill with a width and no colour, so the bar
   looked empty however much was left in it. */
.bar--boss .bar__fill { background: linear-gradient(90deg, #b91c1c, var(--ember)); }
.bar-label { display: flex; justify-content: space-between; font-size: 12px; color: var(--muted); margin-bottom: 5px; }
.bar-label b { color: var(--foreground); font-weight: 500; }

/* ---------- Sprites (placeholder until real art lands) --------------- */
.sprite {
  display: grid; place-items: center;
  aspect-ratio: 1; width: 100%;
  border-radius: 12px;
  border: 1px dashed rgba(255,255,255,0.16);
  background:
    repeating-linear-gradient(45deg, rgba(255,255,255,0.022) 0 8px, transparent 8px 16px),
    rgba(255,255,255,0.03);
  color: var(--muted);
  font-size: 10px; line-height: 1.25; text-align: center;
  padding: 6px; word-break: break-word; overflow: hidden;
  background-size: cover; background-position: center;
}
.sprite--img { border-style: solid; border-color: var(--border); color: transparent; }
.sprite--pixel { image-rendering: pixelated; }
.sprite--sm { width: 40px; }
.sprite--md { width: 72px; }
.sprite--lg { width: 128px; }
.sprite--enemy { width: 132px; margin: 0 auto 12px; }
.sprite--hit { animation: spriteHit 320ms var(--ease); }
@keyframes spriteHit {
  0% { transform: none; filter: none; }
  20% { transform: translateX(-6px) scale(1.04); filter: brightness(2.2); }
  60% { transform: translateX(4px); }
  100% { transform: none; }
}

/* ---------- Dungeon grid --------------------------------------------- */
.map { display: grid; grid-template-columns: repeat(5, 1fr); gap: 8px; max-width: 380px; margin: 0 auto; }
.tile {
  aspect-ratio: 1; border-radius: 12px;
  border: 1px solid var(--border);
  background: rgba(255,255,255,0.028);
  display: grid; place-items: center; gap: 2px;
  font-size: 10px; color: var(--muted);
  cursor: default; padding: 4px; text-align: center;
  transition: transform 180ms var(--ease), border-color 180ms var(--ease), background 180ms var(--ease);
}
.tile__icon { font-size: 17px; line-height: 1; }
.tile--unknown { color: rgba(138,143,152,0.5); }
.tile--reachable { cursor: pointer; border-color: rgba(240,180,41,0.4); background: rgba(240,180,41,0.06); }
.tile--reachable:hover { transform: translateY(-3px); border-color: var(--gold); box-shadow: 0 0 20px var(--gold-glow); }
.tile--here { border-color: var(--gold); background: rgba(240,180,41,0.14); box-shadow: 0 0 24px var(--gold-glow); }
.tile--cleared { opacity: 0.55; }
.tile--boss { border-color: rgba(220,38,38,0.5); background: rgba(220,38,38,0.09); }
.tile--ladder { border-color: rgba(124,106,232,0.5); background: rgba(124,106,232,0.1); }

/* ---------- Combat log ----------------------------------------------- */
.log {
  background: rgba(255,255,255,0.035);
  border: 1px solid var(--border); border-radius: 12px;
  padding: 12px 14px; font-size: 12.5px; line-height: 1.75;
  max-height: 240px; overflow-y: auto; overscroll-behavior: contain;
  color: var(--muted); margin-top: 14px;
}
.log__line { display: block; }
.log__line--hit      { color: var(--foreground); }
.log__line--crit     { color: var(--gold); text-shadow: 0 0 8px var(--gold-glow); }
.log__line--damage   { color: var(--hp-light); }
.log__line--heal     { color: var(--good); }
.log__line--loot     { color: var(--rarity-epic); }
.log__line--gold     { color: var(--gold); }
.log__line--victory  { color: var(--gold); font-weight: 500; }
.log__line--death    { color: var(--hp-light); font-weight: 500; }
.log__line--boss     { color: var(--hp-light); font-weight: 500; }
.log__line--level    { color: var(--xp); }
.log__line--upgrade  { color: var(--arcane); }
.log__line--buff     { color: var(--arcane); }
.log__line--status   { color: var(--mana); }
.log__line--floor    { color: var(--gold); }
.log__line--miss     { color: var(--muted); font-style: italic; }
.log__line--night    { color: var(--arcane); }
.log__line--extract  { color: var(--good); }

/* ---------- Moves ---------------------------------------------------- */
.moves { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; margin-top: 14px; }
.move {
  text-align: left; padding: 12px 14px; border-radius: 12px;
  background: var(--surface-strong); border: 1px solid var(--border);
  color: var(--foreground); cursor: pointer; min-height: 62px;
  transition: transform 180ms var(--ease), border-color 180ms var(--ease), box-shadow 180ms var(--ease);
  font-family: var(--font-body);
}
.move:hover:not(:disabled) { transform: translateY(-2px); border-color: rgba(240,180,41,0.45); box-shadow: 0 0 22px var(--gold-glow); }
.move:disabled { opacity: 0.4; cursor: not-allowed; }
.move__name { font-size: 13.5px; font-weight: 500; display: block; }
.move__meta { font-size: 11px; color: var(--muted); }
.move--magic { border-left: 3px solid var(--mana); }
.move--heal  { border-left: 3px solid var(--good); }
.move--buff  { border-left: 3px solid var(--arcane); }
.move--physical { border-left: 3px solid var(--gold); }

/* ---------- Items ---------------------------------------------------- */
.item-row {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 12px; border-radius: 12px;
  border: 1px solid var(--border); background: rgba(255,255,255,0.028);
  margin-bottom: 8px;
}
.item-row--gone { opacity: 0.45; }
.item-row--active { border-color: var(--gold); background: rgba(240,180,41,0.06); }
.swatch {
  display: inline-block; width: 9px; height: 9px; margin-right: 7px;
  border-radius: 2px; vertical-align: baseline;
  box-shadow: 0 0 0 1px rgba(0,0,0,0.4);
}
.item-row__body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.item-row__name { font-size: 13.5px; font-weight: 500; overflow-wrap: anywhere; }
.item-row__stats { font-size: 11.5px; color: var(--muted); }
/* The count that sits beside a progress bar. Fixed, or the bar gets shorter as
   the number gets longer -- a list of "done", "6/8" and "25/300" drew three
   different bars for three identical rows. */
.item-row__tally { flex: 0 0 66px; text-align: right; }
.rarity-common    { color: var(--rarity-common); }
.rarity-uncommon  { color: var(--rarity-uncommon); }
.rarity-rare      { color: var(--rarity-rare); }
.rarity-epic      { color: var(--rarity-epic); }
.rarity-legendary { color: var(--rarity-legendary); text-shadow: 0 0 8px var(--gold-glow); }

/* ---------- The floor grid ---------- */
.dgrid {
  display: grid; grid-template-columns: repeat(5, 1fr); gap: 6px; margin-top: 12px;
}
.dtile {
  aspect-ratio: 1; display: grid; place-items: center;
  border: 1px solid var(--border); border-radius: 10px;
  background: rgba(255,255,255,.03); color: var(--foreground);
  font: inherit; font-size: 18px; cursor: pointer;
}
.dtile:disabled { cursor: default; opacity: .55; }
.dtile--dark { color: var(--muted); background: rgba(255,255,255,.012); }
.dtile--done { opacity: .45; }
/* A room walked out of with something still in it. Loud on purpose: the whole
   point of being able to leave is being able to find your way back. */
.dtile--pending {
  border-color: var(--gold);
  color: var(--gold);
  background: rgba(240,180,41,0.14);
  box-shadow: 0 0 0 1px var(--gold) inset, 0 0 12px rgba(240,180,41,0.35);
}
.dtile--here { border-color: var(--gold); color: var(--gold); background: rgba(240,180,41,.10); }

/* ---------- Paperdoll -------------------------------------------------
   Three columns, Tibia's arrangement: jewellery and weapon down the left,
   armour top to bottom in the middle, everything that carries things down
   the right. A list would say the same words; only the grid says where a
   piece sits on the body, which is what makes a gap obvious. ---------- */
.doll {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  grid-template-rows: repeat(4, auto);
  gap: 12px 10px;
  justify-items: center;
}
.doll__slot {
  position: relative;
  display: grid; gap: 5px; justify-items: center;
  width: 100%; max-width: 88px;
}
.doll__slot .sprite { width: 100%; font-size: 9px; }
.doll__label {
  font-size: 10px; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--muted); text-align: center;
}
.doll__slot--filled .sprite { border-style: solid; }
.doll__slot--common    .sprite { border-color: var(--rarity-common); }
.doll__slot--uncommon  .sprite { border-color: var(--rarity-uncommon); }
.doll__slot--rare      .sprite { border-color: var(--rarity-rare); }
.doll__slot--epic      .sprite { border-color: var(--rarity-epic); }
.doll__slot--legendary .sprite { border-color: var(--rarity-legendary); box-shadow: 0 0 12px var(--gold-glow); }

.doll__remove {
  position: absolute; top: -6px; right: -6px;
  width: 24px; height: 24px; padding: 0;
  display: grid; place-items: center;
  border-radius: 999px; border: 1px solid var(--border);
  background: var(--bg-elevated); color: var(--muted);
  font: inherit; font-size: 15px; line-height: 1; cursor: pointer;
  transition: color 160ms var(--ease), border-color 160ms var(--ease);
}
.doll__remove:hover:not(:disabled) { color: var(--ember); border-color: var(--ember); }
.doll__remove:disabled { opacity: 0.35; cursor: default; }

.doll__badge {
  position: absolute; top: -6px; right: -6px;
  padding: 1px 7px; border-radius: 999px;
  border: 1px solid var(--border); background: var(--bg-elevated);
  font-size: 10.5px; font-variant-numeric: tabular-nums; color: var(--muted);
}
.doll__badge--over { color: var(--ember); border-color: var(--ember); }

/* Placement is the whole point of this component, so it is spelled out
   rather than derived from source order. */
.doll__slot--amulet   { grid-area: 1 / 1; }
.doll__slot--weapon   { grid-area: 2 / 1; }
.doll__slot--ring     { grid-area: 3 / 1; }
.doll__slot--helmet   { grid-area: 1 / 2; }
.doll__slot--armor    { grid-area: 2 / 2; }
.doll__slot--legs     { grid-area: 3 / 2; }
.doll__slot--boots    { grid-area: 4 / 2; }
.doll__slot--backpack { grid-area: 1 / 3; }
.doll__slot--shield   { grid-area: 2 / 3; }
.doll__slot--pocket   { grid-area: 3 / 3; }

/* ---------- Attributes ----------------------------------------------- */
.attr-row { display: flex; align-items: center; gap: 12px; padding: 9px 0; border-bottom: 1px solid rgba(255,255,255,0.05); }
.attr-row:last-child { border-bottom: 0; }
.attr-row__name { flex: 1; font-size: 13.5px; }
.attr-row__hint { display: block; font-size: 11px; color: var(--muted); }
.attr-row__value { display: flex; align-items: center; justify-content: flex-end; gap: 7px; font-family: var(--font-display); font-weight: 700; font-size: 17px; color: var(--gold); min-width: 34px; text-align: right; font-variant-numeric: tabular-nums; }

/* ---------- Daily reward strip --------------------------------------- */
.days { display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px; }
.day {
  border-radius: 10px; border: 1px solid var(--border);
  background: rgba(255,255,255,0.028);
  padding: 8px 4px; text-align: center; font-size: 10px; color: var(--muted);
}
.day__n { font-family: var(--font-display); font-weight: 700; font-size: 11px; color: var(--muted); display: block; }
.day--done { opacity: 0.45; }
.day--done .day__n { color: var(--good); }
.day--today { border-color: var(--gold); background: rgba(240,180,41,0.12); box-shadow: 0 0 18px var(--gold-glow); }
.day--today .day__n { color: var(--gold); }
.day--big { border-color: rgba(167,139,250,0.4); }

/* ---------- Slider (fountain) ---------------------------------------- */
input[type=range] { width: 100%; accent-color: var(--gold); height: 34px; }
.risk { display: flex; justify-content: space-between; font-size: 13px; margin-bottom: 4px; }
.risk__heal { color: var(--good); }
.risk__death { color: var(--hp-light); }

/* ---------- Toast ---------------------------------------------------- */
.beast { padding: 0; }
.beast > summary {
  list-style: none; cursor: pointer; display: flex; align-items: center; gap: 12px;
  padding: 12px 14px;
}
.beast > summary::-webkit-details-marker { display: none; }
.beast__body { padding: 0 14px 14px; }
.beast__who { flex: 1; min-width: 0; }
.beast__locked { color: var(--muted); font-style: italic; }
.beast__resists { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 2px 14px; }
.beast--unknown { display: flex; align-items: center; gap: 12px; padding: 10px 14px; color: var(--muted); }
.beast--unknown .sprite { opacity: 0.35; }
.build-badge {
  position: fixed; left: 10px; bottom: calc(8px + env(safe-area-inset-bottom, 0px));
  z-index: 80; pointer-events: none; user-select: none;
  font-size: 11px; letter-spacing: 0.04em; color: var(--muted); opacity: 0.75;
  padding: 3px 8px; border-radius: 999px;
  background: rgba(10,10,12,0.6); border: 1px solid var(--border);
}
.build-badge b { color: var(--ember); font-weight: 700; }
.toasts { position: fixed; left: 0; right: 0; bottom: 18px; z-index: 90; display: flex; flex-direction: column; align-items: center; gap: 8px; pointer-events: none; padding: 0 16px; }
.toast {
  max-width: 460px; padding: 11px 18px; border-radius: 12px;
  background: rgba(10,10,12,0.94); border: 1px solid var(--border);
  backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
  font-size: 13.5px; box-shadow: 0 10px 30px rgba(0,0,0,0.55);
  animation: toastIn 260ms var(--ease);
}
.toast--bad  { border-color: rgba(220,38,38,0.45); color: #fca5a5; }
.toast--good { border-color: rgba(74,222,128,0.4); color: var(--good); }
@keyframes toastIn { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }

/* ---------- Misc ------------------------------------------------------ */
.pill {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 12px; border-radius: 999px;
  background: var(--surface); border: 1px solid var(--border);
  font-size: 12px; color: var(--muted);
}
.pill--night { border-color: rgba(124,106,232,0.45); color: #c4b5fd; }
.pill--gold  { border-color: rgba(240,180,41,0.4); color: var(--gold); }
.dot { width: 8px; height: 8px; border-radius: 50%; background: var(--good); box-shadow: 0 0 8px var(--good); animation: pulse 2s ease-in-out infinite; flex-shrink: 0; }
.dot--off { background: var(--muted); box-shadow: none; animation: none; }
@keyframes pulse { 50% { opacity: 0.4; } }
.divider { height: 1px; background: var(--border); margin: 16px 0; }
.stack > * + * { margin-top: 10px; }
.spinner { width: 22px; height: 22px; border: 2px solid rgba(255,255,255,0.15); border-top-color: var(--gold); border-radius: 50%; animation: spin 700ms linear infinite; margin: 40px auto; }
@keyframes spin { to { transform: rotate(360deg); } }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; }
}

/* ---------- Combat effects -------------------------------------------
   The turn arrives from the server all at once. These are what make it
   readable as a sequence: a number leaves whatever was hit, the thing that
   took it reacts, and the bar walks to its new value.                    */
.fx-host { position: relative; }

/* A column beside the portrait. Numbers over a creature cover the creature,
   and two numbers in the same place cover each other. */
.fx-column {
  display: flex; flex-direction: column; align-items: flex-start; gap: 4px;
  min-width: 108px; padding-top: 8px;
}
.fx-column .floater {
  position: static; animation: floatBeside 2400ms var(--ease) forwards;
}
@keyframes floatBeside {
  0%   { opacity: 0; transform: translateX(-8px); }
  12%  { opacity: 1; transform: none; }
  75%  { opacity: 1; transform: none; }
  100% { opacity: 0; transform: translateY(-8px); }
}
.fx-row { display: flex; align-items: flex-start; gap: 14px; }
.fx-row .sprite--enemy { margin: 0; }

.floater {
  position: absolute; top: 6px;
  font-family: var(--font-display); font-size: 19px; font-weight: 700;
  color: var(--hp-light); text-shadow: 0 2px 12px rgba(0,0,0,0.65);
  pointer-events: none; z-index: 4; white-space: nowrap;
  animation: floatUp 1000ms var(--ease) forwards;
}
.floater--heal { color: var(--good); }
.floater--miss { font-size: 14px; font-style: italic; font-weight: 500; color: var(--muted); }
.floater--crit { font-size: 26px; color: var(--gold); text-shadow: 0 0 18px var(--gold-glow), 0 2px 12px rgba(0,0,0,0.7); }
.floater--fire   { color: var(--ember); text-shadow: 0 0 16px rgba(232,89,12,0.55); }
.floater--ice    { color: var(--mana);  text-shadow: 0 0 16px rgba(56,189,248,0.55); }
.floater--poison { color: var(--good);  text-shadow: 0 0 16px rgba(74,222,128,0.5); }

@keyframes floatUp {
  0%   { opacity: 0; transform: translateY(6px) scale(0.85); }
  18%  { opacity: 1; transform: translateY(-2px) scale(1.08); }
  100% { opacity: 0; transform: translateY(-42px) scale(1); }
}

/* Whoever took the hit reacts. The header card stands in for the player,
   the enemy sprite for the enemy. */
.fx-damage { animation: fxDamage 420ms var(--ease); }
.fx-crit   { animation: fxCrit 460ms var(--ease); }
.fx-heal   { animation: fxHeal 460ms var(--ease); }

@keyframes fxDamage {
  0%   { box-shadow: none; }
  25%  { box-shadow: inset 0 0 0 2px rgba(248,113,113,0.85), 0 0 26px rgba(220,38,38,0.35); transform: translateX(-3px); }
  55%  { transform: translateX(3px); }
  100% { box-shadow: none; transform: none; }
}
@keyframes fxCrit {
  0%   { box-shadow: none; }
  25%  { box-shadow: inset 0 0 0 2px rgba(240,180,41,0.9), 0 0 34px var(--gold-glow); transform: translateX(-5px) scale(1.01); }
  60%  { transform: translateX(4px); }
  100% { box-shadow: none; transform: none; }
}
@keyframes fxHeal {
  0%   { box-shadow: none; }
  30%  { box-shadow: inset 0 0 0 2px rgba(74,222,128,0.7), 0 0 26px rgba(74,222,128,0.28); }
  100% { box-shadow: none; }
}

/* Elemental hits tint the sprite for a beat rather than adding a new shape. */
.fx-elem { animation: fxElem 520ms var(--ease); }
.fx-elem--fire   { --fx-tint: rgba(232,89,12,0.75); }
.fx-elem--ice    { --fx-tint: rgba(56,189,248,0.75); }
.fx-elem--poison { --fx-tint: rgba(74,222,128,0.7); }
@keyframes fxElem {
  0%   { box-shadow: none; filter: none; }
  30%  { box-shadow: 0 0 0 3px var(--fx-tint), 0 0 30px var(--fx-tint); filter: saturate(1.5); }
  100% { box-shadow: none; filter: none; }
}

@media (prefers-reduced-motion: reduce) {
  .floater { animation-duration: 1ms; }
  .fx-damage, .fx-crit, .fx-heal, .fx-elem, .sprite--hit { animation: none; }
}

/* ---------- Chips ----------------------------------------------------- */
.chips { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.chip {
  display: inline-flex; align-items: center;
  padding: 3px 9px; border-radius: 999px;
  border: 1px solid var(--border); background: rgba(124,106,232,0.12);
  font-size: 11.5px; color: var(--foreground); cursor: help;
}
.chip--gain { background: rgba(74,222,128,0.14); color: var(--good); border-color: rgba(74,222,128,0.3); }

/* ---------- Popovers -------------------------------------------------
   A :hover tooltip does not exist on a phone. These open on click as well,
   which is also why the trigger carries a visible mark rather than only a
   help cursor.                                                          */
.explains { cursor: pointer; }
.explains:focus-visible { outline: 2px solid var(--gold); outline-offset: 3px; border-radius: 8px; }
.explains__mark {
  display: inline-grid; place-items: center;
  width: 15px; height: 15px; margin-left: 2px;
  border-radius: 999px; border: 1px solid var(--border);
  background: rgba(255,255,255,0.05);
  font-family: var(--font-display); font-size: 9px; font-weight: 700; font-style: italic;
  color: var(--muted); line-height: 1;
}
.explains:hover .explains__mark { color: var(--gold); border-color: rgba(240,180,41,0.5); }

.pop {
  position: fixed; z-index: 90; min-width: 190px; max-width: min(320px, calc(100vw - 16px));
  padding: 11px 13px; border-radius: 13px;
  border: 1px solid var(--border);
  background: rgba(12,12,17,0.97);
  backdrop-filter: blur(10px);
  box-shadow: 0 18px 50px rgba(0,0,0,0.55);
  opacity: 0; transform: translateY(4px);
  transition: opacity 140ms var(--ease), transform 140ms var(--ease);
}
.pop--in { opacity: 1; transform: none; }
.pop__title {
  font-family: var(--font-display); font-size: 12.5px; font-weight: 700;
  margin-bottom: 8px; padding-bottom: 7px; border-bottom: 1px solid var(--border);
  text-transform: capitalize;
}
.pop__row { display: flex; justify-content: space-between; gap: 14px; font-size: 12px; padding: 2.5px 0; }
.pop__label { color: var(--muted); overflow-wrap: anywhere; }
.pop__value { color: var(--foreground); white-space: nowrap; }
.pop__foot {
  margin-top: 8px; padding-top: 7px; border-top: 1px solid var(--border);
  font-size: 11px; color: var(--muted); text-transform: capitalize;
}
.pop--rarity-common .pop__title    { color: var(--rarity-common); }
.pop--rarity-uncommon .pop__title  { color: var(--rarity-uncommon); }
.pop--rarity-rare .pop__title      { color: var(--rarity-rare); }
.pop--rarity-epic .pop__title      { color: var(--rarity-epic); }
.pop--rarity-legendary .pop__title { color: var(--rarity-legendary); text-shadow: 0 0 8px var(--gold-glow); }

/* Loot lines are the first place a drop is ever seen, so that is where you
   should be able to ask what it was. */
/* Rarity beats the generic loot colour, so the line itself says whether it is
   worth opening. Stated after .log__line--loot for exactly that reason. */
.log__line--item { cursor: pointer; text-decoration: underline dotted rgba(255,255,255,0.28); text-underline-offset: 3px; }
.log__line--item:hover { text-decoration-color: currentColor; }
/* What this turn wrote. Marked down the edge rather than by colour, so it
   stacks with the colour a line already carries for what it says. */
.log__line--fresh {
  border-left: 2px solid var(--gold);
  padding-left: 8px;
  margin-left: -10px;
  background: linear-gradient(90deg, rgba(240,180,41,0.10), transparent 60%);
}

.log__line--item.rarity-common    { color: var(--rarity-common); }
.log__line--item.rarity-uncommon  { color: var(--rarity-uncommon); }
.log__line--item.rarity-rare      { color: var(--rarity-rare); }
.log__line--item.rarity-epic      { color: var(--rarity-epic); }
.log__line--item.rarity-legendary { color: var(--rarity-legendary); text-shadow: 0 0 8px var(--gold-glow); font-weight: 500; }

/* ---------- Daily strip ----------------------------------------------- */
.day__unit { font-size: 9px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.04em; }
.day--rare      { border-color: rgba(56,189,248,0.45); }
.day--epic      { border-color: rgba(167,139,250,0.5); }
.day--legendary {
  border-color: rgba(240,180,41,0.65);
  background: rgba(240,180,41,0.09);
  box-shadow: inset 0 0 20px rgba(240,180,41,0.10);
}
.day--legendary .day__n { color: var(--rarity-legendary); }

/* Today outranks rarity: which cell is claimable matters more than what is
   in it. Stated last, and doubled up, so source order cannot decide it. */
.day.day--today { border-color: var(--gold); background: rgba(240,180,41,0.14); box-shadow: 0 0 18px var(--gold-glow); }
.day.day--today .day__n { color: var(--gold); }
.day.day--done { opacity: 0.45; }

/* ---------- Time of day ------------------------------------------------ */
.pill--time { cursor: pointer; }
.pill--night   { background: rgba(124,106,232,0.16); border-color: rgba(124,106,232,0.4); color: var(--arcane); }
.pill--evening { background: rgba(232,89,12,0.13);   border-color: rgba(232,89,12,0.35);  color: var(--ember); }
.pill--morning { background: rgba(56,189,248,0.12);  border-color: rgba(56,189,248,0.32); color: var(--mana); }
.pill--day     { background: rgba(240,180,41,0.12);  border-color: rgba(240,180,41,0.32); color: var(--gold); }

/* ---------- Rarity shimmer -------------------------------------------- */
/*
 * Colour says which tier. Intensity says how far up. Both come from two
 * custom properties per tier, so retuning a tier is one line here instead of
 * an edit in the row, the paperdoll and the popup.
 *
 * The row reads its tier off the name it already contains, through :has().
 * That is why no view changed: every list that renders a .rarity-* name --
 * bag, shop, codex, drops, sell -- lights up on its own, including the ones
 * written after this.
 *
 * Common is deliberately flat, and gets there for free: every length below is
 * multiplied by --rar-i, so an intensity of 0 renders nothing at all. If
 * everything shimmers, the shimmer stops being a signal -- a legendary has to
 * be recognisable from across the screen without reading a word.
 */
.item-row:has(.rarity-common)    { --rar: var(--rarity-common);    --rar-i: 0;    --rar-sweep: 0s; }
.item-row:has(.rarity-uncommon)  { --rar: var(--rarity-uncommon);  --rar-i: 0.30; --rar-sweep: 0s; }
.item-row:has(.rarity-rare)      { --rar: var(--rarity-rare);      --rar-i: 0.52; --rar-sweep: 7.5s; }
.item-row:has(.rarity-epic)      { --rar: var(--rarity-epic);      --rar-i: 0.76; --rar-sweep: 5.5s; }
.item-row:has(.rarity-legendary) { --rar: var(--rarity-legendary); --rar-i: 1;    --rar-sweep: 3.6s; }

/* --item-row--active keeps its gold treatment: which row you are acting on
   outranks what tier it is. */
.item-row:not(.item-row--active):has(.rarity-uncommon),
.item-row:not(.item-row--active):has(.rarity-rare),
.item-row:not(.item-row--active):has(.rarity-epic),
.item-row:not(.item-row--active):has(.rarity-legendary) {
  position: relative;
  overflow: hidden;
  border-color: color-mix(in srgb, var(--rar) calc(var(--rar-i) * 55%), var(--border));
  background:
    radial-gradient(140% 190% at 0% 50%,
      color-mix(in srgb, var(--rar) calc(var(--rar-i) * 22%), transparent) 0%,
      transparent 70%),
    rgba(255,255,255,0.028);
  box-shadow: 0 0 calc(var(--rar-i) * 16px)
              color-mix(in srgb, var(--rar) calc(var(--rar-i) * 30%), transparent);
}

/* The travelling sheen starts at rare. Below that the static wash carries the
   tier on its own, and two moving highlights in a full bag is noise. */
.item-row:not(.item-row--active):has(.rarity-rare)::after,
.item-row:not(.item-row--active):has(.rarity-epic)::after,
.item-row:not(.item-row--active):has(.rarity-legendary)::after {
  content: ''; position: absolute; inset: 0;
  pointer-events: none; border-radius: inherit;
  background: linear-gradient(105deg,
    transparent 40%,
    color-mix(in srgb, var(--rar) calc(var(--rar-i) * 40%), transparent) 50%,
    transparent 60%);
  background-size: 260% 100%;
  animation: rarSweep var(--rar-sweep) linear infinite;
}

@keyframes rarSweep {
  from { background-position: 160% 0; }
  to   { background-position: -60% 0; }
}

/* The paperdoll gets the same two variables, so a worn legendary and a
   legendary in the bag glow at the same strength. */
.doll__slot--common    { --rar: var(--rarity-common);    --rar-i: 0; }
.doll__slot--uncommon  { --rar: var(--rarity-uncommon);  --rar-i: 0.30; }
.doll__slot--rare      { --rar: var(--rarity-rare);      --rar-i: 0.52; }
.doll__slot--epic      { --rar: var(--rarity-epic);      --rar-i: 0.76; }
.doll__slot--legendary { --rar: var(--rarity-legendary); --rar-i: 1; }

.doll__slot--uncommon .sprite,
.doll__slot--rare .sprite,
.doll__slot--epic .sprite,
.doll__slot--legendary .sprite {
  box-shadow:
    0 0 calc(var(--rar-i) * 18px) color-mix(in srgb, var(--rar) calc(var(--rar-i) * 38%), transparent),
    inset 0 0 calc(var(--rar-i) * 22px) color-mix(in srgb, var(--rar) calc(var(--rar-i) * 20%), transparent);
}

/* Motion is the decoration here, not the information -- the colour and the
   wash still say the tier with the sweep switched off. */
@media (prefers-reduced-motion: reduce) {
  .item-row::after { animation: none; }
}

/* ---------- The delve header ------------------------------------------
   Leaving a run is the rarest thing anybody does down here and had the
   widest button on the screen. It sits in the corner instead, big enough
   to hit on a phone and quiet enough to ignore. */
.delve-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
}
.btn--x {
  flex: 0 0 auto;
  width: 38px;
  height: 38px;
  padding: 0;
  font-size: 22px;
  line-height: 1;
  border-radius: 10px;
  color: var(--muted);
  background: transparent;
}
.btn--x:hover { color: var(--hp-light); border-color: var(--hp-light); }

/* The way down and the way out, once they are found. Not the gold of an
   unfinished room -- these are places, not errands. */
.dtile--down {
  border-color: var(--good);
  color: var(--good);
  box-shadow: 0 0 0 1px var(--good) inset, 0 0 10px rgba(90,190,120,0.30);
}
.dtile--out {
  border-color: var(--mana);
  color: var(--mana);
  box-shadow: 0 0 0 1px var(--mana) inset, 0 0 10px rgba(56,189,248,0.30);
}

/* ---------- Elements, one colour each ---------------------------------
   A damage number's colour is the only thing that says which element it
   was, so the four have to be told apart at a glance. Physical takes the
   same grey as the word "armour" beside it: it is the plain case, and
   colouring it would imply it was something. */
.floater--earth  { color: #6FCF7C; text-shadow: 0 0 16px rgba(111,207,124,0.5); }
.floater--energy { color: #F5D142; text-shadow: 0 0 16px rgba(245,209,66,0.5); }
.floater--death  { color: #A78BFA; text-shadow: 0 0 16px rgba(167,139,250,0.5); }
.floater--holy   { color: #FDE68A; text-shadow: 0 0 16px rgba(253,230,138,0.5); }
.floater--physical,
.floater--life_drain,
.floater--mana_drain { color: var(--muted); }

/* The same four, as words on the creature panel. */
.elem--fire   { color: var(--ember); }
.elem--ice    { color: var(--mana); }
.elem--earth  { color: #6FCF7C; }
.elem--energy { color: #F5D142; }
.elem--death  { color: #A78BFA; }
.elem--holy   { color: #FDE68A; }
.elem--physical { color: var(--muted); }

/* ---------- What a summoner called -----------------------------------
   Beside the thing that called it, and pickable: two rats hitting for
   seventeen a round with nothing on screen to explain it is not a design
   decision, it is a missing sprite. */
.summons { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.summon {
  flex: 1 1 130px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 8px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: rgba(255,255,255,0.02);
  cursor: pointer;
}
.summon:hover:not(:disabled) { border-color: var(--gold); }
.summon--marked { border-color: var(--gold); box-shadow: 0 0 0 1px var(--gold) inset; }
.summon--gone { opacity: 0.35; cursor: default; }
.summon__name { font-size: 12px; letter-spacing: 0.04em; }

/* The skill and material bars. They asked for a `sk` fill that the stylesheet
   never defined, so both drew an empty track -- the same way the boss health
   bar was invisible until it was given a gradient. */
.bar--skill .bar__fill { background: linear-gradient(90deg, #6d28d9, var(--xp)); }
/* A skill bar with the run's own contribution on the end of it: what you had
   in purple, what this descent put on in green. Two fills in a row need the
   bar to BE a row -- stacked, the second one was invisible under the first. */
.bar--split { display: flex; }
.bar--split .bar__fill { border-radius: 0; }
.bar--split .bar__fill:first-child { border-radius: 999px 0 0 999px; }
.bar--split .bar__fill:last-child { border-radius: 0 999px 999px 0; }
/* Two classes deep, because `.bar--skill .bar__fill` is two classes deep and
   specificity does not care what order they are written in. The gain segment
   was being painted the exact colour it exists to be told apart from. */
.bar--split .bar__fill--gain {
  background: linear-gradient(90deg, #16a34a, var(--good));
}

/* ---------- Materials ------------------------------------------------
   Five qualities of one thing on one line. The question anybody has is
   "how close am I to a stack of the good one", and two rows with two bars
   made you read both to answer it. Zeros are shown on purpose: a zero is
   the answer to "have I got any". */
.mat-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 0;
  border-top: 1px solid var(--border);
}
.mat-row:first-of-type { border-top: 0; }
.mat-row__name { flex: 1 1 auto; font-size: 14px; }
.mat-row__of { flex: 0 0 auto; font-size: 12px; color: var(--muted); }
.mat-tally { display: flex; gap: 4px; flex: 0 0 auto; }
.mat-box {
  min-width: 26px;
  padding: 3px 5px;
  text-align: center;
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 600;
  border: 1px solid currentColor;
  border-radius: 6px;
  background: rgba(255,255,255,0.03);
}
.mat-box--none { opacity: 0.30; }
.mat-box--full { box-shadow: 0 0 0 1px currentColor inset, 0 0 8px -2px currentColor; }

/* A room a torch showed you, painted for what it is. Marking the way out and
   leaving a trap looking like a plain square wastes the only thing a torch
   is for. */
.dtile--danger {
  border-color: var(--hp-light);
  color: var(--hp-light);
  box-shadow: 0 0 0 1px var(--hp-light) inset, 0 0 10px rgba(248,113,113,0.28);
}
.dtile--prize {
  border-color: var(--gold);
  color: var(--gold);
  box-shadow: 0 0 0 1px var(--gold) inset, 0 0 10px rgba(240,180,41,0.28);
}

/* How many of a stack. */
.count-row { display: flex; align-items: center; gap: 10px; }
.count-slider { flex: 1 1 auto; accent-color: var(--gold); }

/* The two drains, as words in the log. They have no colour of their own --
   grey is the honest answer for damage that is not an element you resist. */
.elem--life_drain, .elem--mana_drain { color: var(--muted); }

/* ---------- A bag row ------------------------------------------------
   Icon, what it is, what you can do with it. Three buttons sitting beside
   the text left roughly one character per line for the name, so every row
   grew into a column of letters -- worst on a phone held upright, which is
   how the game is actually played.

   The buttons are their own group and wrap to a line of their own the
   moment the row is narrow. The icon never shrinks; the text takes what is
   left and the buttons take what they need. */
.item-row--bag { flex-wrap: wrap; row-gap: 8px; }
.item-row--bag .sprite { flex: 0 0 auto; }
.item-row--bag .item-row__body { flex: 1 1 9rem; }
.item-row__acts {
  display: flex;
  gap: 6px;
  flex: 0 0 auto;
  margin-left: auto;
}

@media (max-width: 560px) {
  /* Own line, full width, and share it evenly: at this size there is no
     honest way to fit three buttons beside a sentence. */
  .item-row__acts { flex: 1 0 100%; margin-left: 0; }
  .item-row__acts .btn { flex: 1 1 0; }
  .item-row--bag .sprite--sm { width: 32px; }
}

/* ---------- The delve panels -----------------------------------------
   On a phone held upright the map, the bag, the materials and thirty lines
   of log are one very long column, and the thing you came to look at is
   somewhere in the middle of it. One strip, one panel at a time. The room
   you are standing in stays above the strip: it is the situation, not a
   place to browse to. */
.delve-tabs {
  display: flex;
  gap: 6px;
  margin: 4px 0 12px;
}
.delve-tab {
  flex: 1 1 0;
  padding: 9px 6px;
  font-family: var(--font-body);
  font-size: 12.5px;
  letter-spacing: 0.04em;
  color: var(--muted);
  background: rgba(255,255,255,0.03);
  border: 1px solid var(--border);
  border-radius: 10px;
  cursor: pointer;
}
.delve-tab--on {
  color: var(--gold);
  border-color: var(--gold);
  background: rgba(240,180,41,0.08);
}
.delve-panel.is-hidden { display: none; }

/* Three more bars that asked for a fill nobody wrote: daily quests, guild
   tasks and achievements all drew an empty track. Same bug the boss health
   bar and the skill bars had, found by listing every kind bar() is called
   with against every kind the stylesheet answers. */
.bar--quest .bar__fill { background: linear-gradient(90deg, #b45309, var(--gold)); }
.bar--task  .bar__fill { background: linear-gradient(90deg, #0f766e, #2dd4bf); }
.bar--ach   .bar__fill { background: linear-gradient(90deg, #6d28d9, var(--xp)); }

/* A material's own art, beside its name. */
.mat-row .sprite--sm { width: 26px; flex: 0 0 auto; }
