/*
 * Luxthos Tools — the design system, in one place.
 *
 * Served site-wide from /assets/theme.css by the hub Worker, so all four Workers' pages share one
 * copy. Every page links this before its own <style>, which means a page can still override a token
 * locally (the dashboard widens --content-max, for instance) and its own rules always win.
 *
 * Before this existed each of the seven pages redeclared the whole palette, so changing one colour
 * was a seven-file edit and they drifted apart in practice. Add a colour here, not in a page. The one
 * palette kept elsewhere is the casino's, sent with its page and its door (app/src/routes/casino/palette.css).
 *
 * Palette rules, which the tokens are named for rather than described by:
 *
 *   purple  carries the UI      — buttons, active nav, toggles, links, and sub giveaways
 *   gold    is Luxbux, only     — balances, the jackpot, Luxbux giveaways, buy-ticket buttons
 *   teal    marks state, never a button fill — positive amounts, OPEN, Connected, avatar rings
 *   red     is destructive, and the StreamElements claim, which is meant to read as expiring;
 *           live, its own brighter red, says only that the stream is on
 *   slate   is developer mode, which borrows none of the accents
 */

/*
 * Always leave room for the scrollbar.
 *
 * Without this the bar appears on pages tall enough to scroll and not on short ones, so the whole
 * layout jumps sideways by its width as you move between them — the content is centred, so half the
 * shift lands on each side and everything moves. Two pages already did this for themselves; every
 * page needs it or the ones that don't are the ones that jump.
 *
 * `scrollbar-gutter` is the modern way to say it; `overflow-y: scroll` is the fallback for browsers
 * without it, which reserve the space by always drawing the track.
 */
html {
  overflow-y: scroll;
  scrollbar-gutter: stable;
}

:root {
  color-scheme: dark;

  /* ---- surfaces ---- */
  --bg:            #0f1118;
  --surface:       #1c202b;   /* every card */
  --surface-2:     #242936;   /* header pills, buttons, avatars, chips */
  --surface-3:     #2c3241;   /* button hover */
  --line:          #323848;   /* card borders, row dividers */
  --line-bright:   #434b60;   /* hover borders, dashed empty states */

  /* ---- text ---- */
  --ink:           #edeff6;
  --ink-dim:       #b1b7c9;
  --ink-faint:     #a4abc1;

  /* ---- purple: the UI accent, and sub giveaways ---- */
  --purple:        #b478f0;
  --purple-hi:     #cba4ff;
  --purple-tint:   #2e1b50;   /* icon tiles, pills */
  --purple-line:   #4b3179;
  --purple-soft:   rgba(180, 120, 240, .14);

  /* ---- gold: Luxbux ---- */
  --gold:          #ffc93c;
  --gold-tint:     #312513;
  --gold-line:     #a58846;
  --gold-soft:     rgba(255, 201, 60, .12);

  /* ---- teal: state, never a fill ---- */
  --teal:          #5cf0bb;
  --teal-tint:     #153c30;
  --teal-line:     #227a5c;

  /* ---- red: negative values and destructive actions ---- */
  --neg:           #ff8098;
  --neg-badge:     #ff5c7a;   /* the unread badge — the strongest red on the page */
  --neg-surface:   #2a1a21;
  --neg-line:      #502e39;

  /* ---- developer mode: deliberately outside the accent system ---- */
  --dev:           #232a36;
  --dev-line:      #434b60;
  --dev-ink:       #b3c5e2;

  /* ---- Twitch's own brand button, not a sub element ---- */
  --twitch:        #9146ff;

  /* ---- the Luxthos UI mark, the membership's icon: its own tile and its X, whatever the page ---- */
  --lux-mark:        #0e1118;
  --lux-mark-green:  #00f58b;
  --lux-mark-cream:  #f2f1ec;

  /* ---- whether the stream is on: the stream tab and the drawer's pill ---- */
  --live:          #ff4d5e;   /* the dot */
  --live-ink:      #ff6b78;
  --live-soft:     rgba(255, 77, 94, .14);
  --live-glow:     rgba(255, 77, 94, .6);   /* the pulse's ring as it starts */
  --off:           #6b7085;   /* the dot */
  --off-tint:      #26262c;
  --line-dim:      #2a2e3a;   /* a hairline quieter than a card's */
  --ink-mute:      #8b8ea6;   /* a glyph that points rather than says */
  --shade-edge:    rgba(0, 0, 0, .4);   /* what stands off the page's edge */

  /* ---- text on a fill ---- */
  --on-fill:       #fff;      /* on a red, green or purple fill */
  --on-purple:     #1a1030;   /* on the game's purple pill */
  --purple-deep:   #8a52d0;   /* the dark end of a purple bar */

  /*
   * ---- the game ----
   *
   * Luxbound's own colours: rarity, the fight, the bag and the item panel. Named for what they mark,
   * like the rest. Only hues live here; a plain black shadow or a white hairline at some alpha is
   * light and shade rather than a colour, and stays where it is drawn.
   */
  --game-picked:        #241b38;   /* a chosen tab, card or square */
  --game-locked:        #1e1830;
  --game-tile:          #14161a;   /* behind a sprite */
  --game-slot:          #101218;   /* an empty square in the bag or on the doll */
  --game-panel:         #090a0f;   /* the item panel fades from --bg to this */
  --game-say:           rgba(9, 9, 14, .96);
  --game-hatch:         #2f2f39;   /* a run that hands over nothing */
  --game-none:          #3a3a44;

  --game-rarity-base:   #7d8085;   /* a piece with no rarity colour of its own */
  --game-rarity-unset:  #888;

  --game-gold:          #e8b23c;   /* a figure worth shouting about: T1, a power, a hit */
  --game-gold-dim:      #d9a441;   /* what a piece rolled */
  --game-gold-deep:     #c08f2e;
  --game-amber-hi:      #ffe08a;   /* the merchant: a bag slot you can buy, a piece for sale */
  --game-amber:         #e0a92f;
  --game-amber-lo:      #b4801c;
  --game-bonus:         #f0b429;   /* a bonus drop and loot held back */
  --game-bonus-hi:      #ffd77a;
  --game-bonus-ink:     #1c1503;
  --game-crafted:       #35d6f5;   /* a line put there at the bench */
  --game-bench:         #7fd4e8;   /* a piece that is on the bench */

  --game-alert:         #c02b3c;   /* beta, a new class, points waiting */
  --game-alert-hi:      #d8404f;
  --game-alert-line:    #ef6472;
  --game-season:        #1c8a4e;
  --game-win:           #7fe0a0;
  --game-gain:          #5fbba1;
  --game-death:         #ff6b6b;
  --game-death-ink:     #ff9a9a;
  --game-danger-hover:  #3a1d22;
  --game-shut:          #ffb4b4;
  --game-barred:        #a8464f;   /* a piece this class cannot wear */
  --game-barred-mark:   #c0454f;
  --game-plus:          #9aa1b0;
  --game-power:         #e0a26a;
  --game-defence:       #7fb6e0;

  --game-hp-you:        #3f9c60;
  --game-hp-you-hi:     #62cd86;
  --game-hp-foe:        #9d4441;
  --game-hp-foe-hi:     #cc6360;

  /* A dungeon's level against yours, World of Warcraft's five (`levelColor` in shared/game/v2/colors.ts).
     Named for the colour, because here the colour is the message; lifted enough to read on a rung. */
  --game-level-grey:    #9d9d9d;   /* outgrown */
  --game-level-green:   #4fd05a;
  --game-level-yellow:  #f2df4b;   /* about your level */
  --game-level-orange:  #ff9346;
  --game-level-red:     #ff5b4f;   /* far above you */

  --game-welcome:       #2a1c46;   /* the first-visit screen, top to bottom */
  --game-welcome-mid:   #14161d;
  --game-welcome-end:   #0d0f14;

  /* The item panel's text, brightest first. */
  --tt-ink-hi:          #eef1f8;
  --tt-ink:             #ccd0da;
  --tt-plain:           #b9bdc9;
  --tt-row:             #9298a6;
  --tt-muted:           #868b99;
  --tt-caption:         #7e8391;
  --tt-quiet:           #7a7e8b;
  --tt-hint:            #757a88;
  --tt-foot:            #6b6f7c;

  /* ---- type ---- */
  --font-display: 'Space Grotesk', system-ui, sans-serif;
  --font-body:    'IBM Plex Sans', system-ui, sans-serif;
  --font-mono:    'IBM Plex Mono', ui-monospace, monospace;

  /* ---- geometry ---- */
  --radius:       14px;
  --radius-sm:    9px;
  --topbar-h:     74px;
  --sidebar-w:    236px;
  /* One width for every page, the dashboard included. Its sidebar sits inside this rather than
     widening the page: a different width there moved the header and everything under it by 80px
     whenever you navigated to or from it. */
  --content-max:  1228px;   /* with the 24px sides, the 1180px of content Luxbound was drawn at */

  /*
   * Older names, kept pointing at the new palette.
   *
   * The pages were written against --ok and --danger before teal and red became state colours with
   * their own tints. Aliasing rather than renaming means every existing rule adopts the new palette
   * on this change alone, and each page can drop its use of these as it gets converted.
   */
  --ok:            var(--teal);
  --ok-soft:       var(--teal-tint);
  --danger:        var(--neg);
  --danger-soft:   rgba(255, 92, 122, .1);
}

/* ============================================================================
 * Components
 *
 * The pieces that appear on more than one page. A giveaway card shows up on the
 * hub, the Luxbux page and the giveaway index; the level widget on the hub and
 * the profile. Defining them once is the whole reason this file exists.
 *
 * Anything used by exactly one page stays in that page's own <style>.
 * ========================================================================= */

/* ---- the glow behind the page ----
 * Purple at the top-left, a fainter gold at the top-right, both fading out well before the fold.
 * It lifts the top of a page without becoming something you look at.
 *
 * Background layers on the body rather than a positioned pseudo-element: a ::before pinned with
 * z-index:-1 renders behind the body's own background colour, which is opaque, so the gradients
 * compute correctly and draw nothing.
 *
 * `fixed` attachment anchors the glow to the viewport while the page scrolls, and also means the
 * percentage positions are read against the viewport — so the centres sit just inside the top edge
 * rather than above it, where only their faintest edge would reach the page.
 *
 * Every page carries it except the dashboard, which opts out below: it is a working surface with a
 * sidebar and a dense grid of panels, and a wash across the top only makes it harder to read.
 */
/* Each glow fades to its own hue at zero alpha, never to bare `transparent`. `transparent` is
   rgba(0,0,0,0), so a colour ramping to it passes through darkened, desaturated versions of itself
   and the falloff shows as a dirty band across the wash. The extra midpoints give the ramp more
   steps to work with, which is the other half of why it banded. */
/* The 8px every browser puts on <body> by default. Most pages here reset it in their own <style>,
   which meant a page that forgot — the game's, for one — sat eight pixels in from every edge with
   its top bar inset with it. It belongs in the one stylesheet they all load, where forgetting is
   not possible. */
body {
  margin: 0;
  background-image:
    radial-gradient(900px 560px at 10% 2%,
      rgba(180,120,240,.22) 0%,
      rgba(180,120,240,.12) 32%,
      rgba(180,120,240,.04) 52%,
      rgba(180,120,240,0) 66%),
    radial-gradient(640px 420px at 92% 6%,
      rgba(255,201,60,.07) 0%,
      rgba(255,201,60,.035) 38%,
      rgba(255,201,60,0) 70%);
  background-attachment: fixed;
  background-repeat: no-repeat;
}

/* ---- arriving content ----
   Two rules for anything that is not in the first paint: hold its space, then fade it in.

   Holding the space is the important half. The bar is mounted by script into an empty <header>, so
   until the bar was drawn the whole page sat 75px too high and then dropped — the single largest layout
   shift on the site, and on the pack tracker most of a CLS of 0.25. Reserving the height it is
   going to take costs nothing and the page never moves.

   Fading is the cosmetic half, and deliberately short: long enough to read as arriving rather than
   appearing, short enough that nobody waits for it. Anyone who has asked for less motion gets the
   content with no animation at all — they still get the reserved space, which is the part that
   stops things jumping. */
#topbar { display: block; min-height: calc(var(--topbar-h) + 1px); }

@keyframes u-enter-in {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: none; }
}
.u-enter { animation: u-enter-in .26s cubic-bezier(.2, .6, .3, 1) both; }

/* A stand-in of roughly the right size, so a list that is about to hold rows is not zero-tall while
   it waits. The shimmer says "coming", where an empty box says "broken". */
@keyframes u-skel-sheen { from { background-position: 200% 0; } to { background-position: -200% 0; } }
.skel {
  border-radius: 9px;
  background-image: linear-gradient(90deg, var(--surface-2) 0%, var(--surface-3) 48%, var(--surface-2) 100%);
  background-size: 200% 100%;
  animation: u-skel-sheen 1.4s linear infinite;
}
.skel--row { height: 46px; margin-bottom: 10px; }
.skel--row:last-child { margin-bottom: 0; }
.skel--line { height: 13px; }

@media (prefers-reduced-motion: reduce) {
  .u-enter { animation: none; }
  .skel { animation: none; }
}

/* ---- layout helpers ---- */
.u-wrap { box-sizing: border-box; width: 100%; max-width: var(--content-max); margin: 0 auto; padding: 0 24px; }
.u-stack { display: flex; flex-direction: column; gap: 14px; }
.u-row { display: flex; flex-wrap: wrap; gap: 14px; align-items: stretch; }
.u-col { flex: 1 1 300px; min-width: 280px; }
.u-spacer { flex: 1; }

/* The name of the page you are on. One size everywhere, so the pages read as one product. */
.page-title { margin: 0; font-family: var(--font-display); font-size: 28.3px; font-weight: 700; letter-spacing: -.02em; }
.page-blurb { margin: 5px 0 0; font-size: 15.3px; color: var(--ink-faint); }
/* The page's own header sits further from the content than two cards sit from each other — it is a
   level above them, not another box in the stack. 4px had it clinging to the first card. */
.page-head { margin-bottom: 24px; }

/* Small-caps section label, the design's standard heading inside a card. */
.eyebrow {
  margin: 0; font-size: 13.1px; font-weight: 700; letter-spacing: .12em;
  text-transform: uppercase; color: var(--ink-faint);
}
.eyebrow--gold { color: var(--gold); }

/*
 * A heading that sits *between* boxes rather than inside one.
 *
 * It needs to lift off the box above it or it reads as that box's footer rather than the label for
 * what follows. The container's 14px gap alone is not enough separation for a heading, even though
 * it is the right amount between two boxes — so this adds to it, giving 32px above and the plain 14
 * below, which ties the heading to the section it introduces.
 */
.section-head { margin: 18px 2px 0; }
/* A heading inside a card is followed by its content at this distance, with no divider —
   the card border already separates one thing from the next. */
.card > .eyebrow + *, .card > .lb-head + * { margin-top: 14px; }
/* Except a big figure, which needs less air under its label than a table or a list does. */
.card > .eyebrow + .figure, .card > .eyebrow + .figure-gold { margin-top: 8px; }

.card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: 16px; padding: 20px 22px;
}
/* Whole cards are clickable where they lead somewhere. Hover lifts the border to the card's own
   accent and adds a matching glow, so what is clickable is visible before you click it. */
.card--link {
  cursor: pointer; display: block; color: inherit; text-decoration: none;
  transition: background .16s ease, border-color .16s ease, box-shadow .16s ease;
}
.card--link:hover { background: var(--surface-2); border-color: #4e5769; color: inherit; }

/* ---- gold: anything that is Luxbux ---- */
.card--gold {
  border-color: var(--gold-line);
  background: radial-gradient(140% 150% at 50% 125%, rgba(255,183,64,.16) 0%, rgba(255,178,58,.07) 45%, rgba(28,32,43,0) 80%), var(--surface);
}
.card--gold.card--link:hover {
  background: radial-gradient(140% 150% at 50% 125%, rgba(255,183,64,.16) 0%, rgba(255,178,58,.07) 45%, rgba(28,32,43,0) 80%), var(--surface);
  border-color: var(--gold);
  box-shadow: 0 0 0 1px rgba(255,201,60,.2), 0 10px 30px rgba(255,201,60,.16);
}
/* A large numeral: the display face, tight tracking, and figures that line up in a column. */
.figure { font-family: var(--font-display); font-weight: 700; line-height: 1.1; letter-spacing: -.03em; font-variant-numeric: tabular-nums; }
.figure-gold {
  font-family: var(--font-display); font-weight: 700; line-height: 1.1; letter-spacing: -.03em;
  color: #ffe6a6; text-shadow: 0 0 30px rgba(255,190,70,.5); font-variant-numeric: tabular-nums;
}

/* ---- level widget (hub header, profile) ---- */
.lvl { display: flex; align-items: center; gap: 16px; }
.lvl__tile {
  flex: none; width: 62px; height: 62px; border-radius: 14px;
  background: radial-gradient(140% 150% at 50% 125%, rgba(255,183,64,.34) 0%, rgba(255,178,58,.16) 45%, rgba(34,34,45,0) 82%), #2a2519;
  border: 1px solid var(--gold-line); box-shadow: inset 0 -14px 22px -12px rgba(255,190,80,.4);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1px;
}
.lvl__num { font-family: var(--font-display); font-size: 25px; font-weight: 700; line-height: 1; color: #ffe6a6; text-shadow: 0 0 16px rgba(255,190,70,.5); }
.lvl__word { font-size: 10px; font-weight: 700; letter-spacing: .14em; color: #b5a68a; }
.lvl__main { flex: 1; min-width: 0; }
.lvl__top { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 10px; }
.lvl__exp { font-family: var(--font-display); font-size: 17.4px; font-weight: 700; }
.lvl__total { font-size: 13.6px; color: var(--ink-faint); }
.lvl__note { font-size: 13.6px; color: var(--ink-faint); margin-top: 8px; }

/* ---- progress bars ---- */
.bar { height: 8px; margin-top: 9px; border-radius: 999px; background: var(--line); overflow: hidden; }
.bar--thin { height: 7px; margin-top: 10px; }
.bar__fill { height: 100%; border-radius: 999px; background: linear-gradient(90deg, #e0a52a, #ffd977); transition: width .3s ease; }
.bar__fill--purple { background: linear-gradient(90deg, var(--purple-deep), var(--purple-hi)); }

/* ---- pills ---- */
.pill {
  display: inline-flex; align-items: center; gap: 6px; padding: 3px 9px; border-radius: 999px;
  font-size: 12px; font-weight: 700; letter-spacing: .06em; white-space: nowrap;
}
.pill--open { background: var(--teal-tint); border: 1px solid var(--teal-line); color: var(--teal); }
.pill--done { background: var(--surface-2); border: 1px solid var(--line-bright); color: var(--ink-faint); }
.pill__dot { width: 7px; height: 7px; border-radius: 999px; background: currentColor; }

.type-pill {
  padding: 4px 9px; border-radius: 6px; font-size: 12px; font-weight: 700;
  letter-spacing: .06em; white-space: nowrap;
}
.type-pill--sub { color: var(--purple-hi); background: rgba(203,164,255,.1); border: 1px solid rgba(203,164,255,.2); }
.type-pill--luxbux { color: var(--gold); background: rgba(255,201,60,.1); border: 1px solid rgba(255,201,60,.2); }
.type-pill--done { color: var(--ink-faint); background: var(--surface-2); border: 1px solid var(--line-bright); }

/* ---- giveaway card (hub, Luxbux page, giveaway index) ----
   The hover lift is on `a.gcard--*` rather than `.gcard--*`: handoff rule 8 gives it to cards
   that lead somewhere, and the dashboard reuses these colours on a row that is not a link.
   Purple is a sub giveaway, gold is a Luxbux one, and a finished one carries no colour at all.
   The whole border says which, which is why there is no left accent bar anywhere. */
.gcard {
  display: block; padding: 24px 22px; border-radius: 16px;
  background: var(--surface); border: 1px solid var(--line);
  color: inherit; text-decoration: none; cursor: pointer;
  transition: background .16s ease, border-color .16s ease, box-shadow .16s ease;
}
a.gcard:hover { color: inherit; }
.gcard__head { display: flex; align-items: center; gap: 10px; }
.gcard__icon { flex: none; width: 38px; height: 38px; border-radius: 10px; background-size: 22px 22px; background-repeat: no-repeat; background-position: center; }
.gcard__title { font-family: var(--font-display); font-size: 19.6px; font-weight: 700; margin: 10px 0 0; }
.gcard__meta { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px; margin-top: 8px; font-size: 14.7px; color: var(--ink-dim); }
.gcard__figure { font-family: var(--font-display); font-size: 18.5px; font-weight: 700; }

.gcard--sub { border-color: #8a61c9; }
a.gcard--sub:hover { background: #251c3a; border-color: var(--purple); box-shadow: 0 0 0 1px rgba(180,120,240,.2), 0 10px 30px rgba(168,85,247,.18); }
.gcard--sub .gcard__icon { background-color: var(--purple-tint); background-image: url("/assets/icon-gift-purple.svg"); border: 1px solid var(--purple-line); }
.gcard--sub .gcard__figure { color: var(--purple-hi); }

.gcard--luxbux { border-color: var(--gold-line); }
a.gcard--luxbux:hover { background: #262117; border-color: var(--gold); box-shadow: 0 0 0 1px rgba(255,201,60,.2), 0 10px 30px rgba(255,201,60,.16); }
.gcard--luxbux .gcard__icon { background-color: var(--gold-tint); background-image: url("/assets/icon-gift-gold.svg"); border: 1px solid #886e33; }
.gcard--luxbux .gcard__figure { color: var(--gold); }

.gcard--done { border-color: var(--line); }
a.gcard--done:hover { background: var(--surface-2); border-color: var(--line-bright); box-shadow: none; }
.gcard--done .gcard__icon { background-color: var(--surface); background-image: url("/assets/icon-gift-gold.svg"); border: 1px solid var(--line-bright); filter: grayscale(1); opacity: .55; }
.gcard--done .gcard__figure { color: var(--ink-dim); }

/* ---- buttons ---- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  padding: 8px 14px; border-radius: 9px; border: 1px solid transparent; cursor: pointer;
  font-family: inherit; font-size: 14.7px; font-weight: 700; text-decoration: none;
  background: var(--purple); color: #fff; transition: background .16s ease, border-color .16s ease;
}
.btn:hover { background: var(--purple-hi); color: #fff; }
.btn:disabled { opacity: .5; cursor: default; }
.btn--gold { background: var(--gold); color: #221800; }
.btn--gold:hover { background: #ffd977; color: #221800; }
.btn--twitch { background: var(--twitch); color: #fff; padding: 10px 16px; border-radius: 10px; }
.btn--twitch:hover { background: #a970ff; color: #fff; }
.btn--secondary { background: var(--surface-2); border-color: var(--line-bright); color: var(--ink); }
.btn--secondary:hover { background: var(--surface-3); color: var(--ink); }
.btn--ghost { background: transparent; border-color: transparent; color: var(--ink-dim); }
.btn--ghost:hover { background: var(--surface-2); color: var(--ink); }
.btn--sm { padding: 6px 11px; font-size: 13.5px; }
.btn--danger { background: var(--neg-badge); color: #fff; }
.btn--danger:hover { background: var(--neg); color: #fff; }

/* A text link that reads as an action rather than a button. */
.link-more { padding: 0; border: none; background: none; color: var(--ink-faint); font-family: inherit; font-size: 14.7px; font-weight: 600; cursor: pointer; text-decoration: none; }
.link-more:hover { color: var(--ink); }

/* ---- transaction rows (hub, Luxbux page, profile) ---- */
.txn { display: flex; align-items: center; justify-content: space-between; gap: 14px; padding: 12px 0; border-bottom: 1px solid var(--line); }
.txn:last-child { border-bottom: none; }
.txn__label { font-size: 15.8px; font-weight: 600; }
.txn__time { font-size: 13.6px; color: var(--ink-faint); margin-top: 3px; }
.txn__amount { font-family: var(--font-display); font-size: 16.4px; font-weight: 700; font-variant-numeric: tabular-nums; }
.txn__amount--pos { color: var(--teal); }
.txn__amount--neg { color: var(--neg); }

.empty { margin: 0; color: var(--ink-faint); font-size: 14.7px; padding: 14px 0; }
