:root {
  --bg: #191a20;
  --panel: #222430;
  --line: #343747;
  --text: #f0f1f6;
  --muted: #a0a7bb;
  --accent: #eceef5;

  --tier-common: #7b8296;
  --tier-uncommon: #4ec98a;
  --tier-rare: #4a9eff;
  --tier-epic: #b06cff;
  --tier-legendary: #ffab3d;
  --tier-celestial: #ff4fbd;
  --tier-singularity: #3df0ff;
  --tier-impossible: #ffd93d;

  --logo-r: #c7ccd8;
  --logo-gb: #35c2ff;
  --logo-ng: #ff5ec4;

  /* Background shapes get their own two hues, kept away from the logo's so the dimmed text
     never blends into a same-coloured shape behind it. Set per refresh by setLogoColors(). */
  --bg1: #7a5cff;
  --bg2: #ff9a3d;

  /* UI accents ride the same two logo hues (refreshed each load) so the whole chrome is tinted
     with the roll's own RGB rather than a flat grey. --grad is the signature diagonal blend. */
  --ui: "Chakra Petch", ui-monospace, sans-serif;
  --grad: linear-gradient(135deg, var(--logo-gb), var(--logo-ng));
  --glass: rgba(255, 255, 255, 0.035);
  --glass-line: rgba(255, 255, 255, 0.09);
  /* Opaque button surface so buttons never blend into the colourful background behind them. */
  --btn: #12131a;
  --btn-hi: #1b1d27;
}

* { box-sizing: border-box; }

/* Fixed abstract background: coloured ribbons + glowing lines in the refresh's two hues,
   over the dark base. Behind everything and non-interactive; kept subtle for readability. */
#bg {
  position: fixed;
  inset: 0;
  width: 100vw;
  height: 100vh;
  z-index: -2;
  pointer-events: none;
  /* Fallback only: this live SVG is normally hidden once bakeBackground() rasterises it into
     #bg-baked. The blur here matters only if baking fails. */
  filter: blur(11px);
}

/* The baked, static background bitmap (the blur is already in the pixels). A plain image is never
   re-rendered per frame, so it is the one background that stays cheap while the logo animates. */
#bg-baked {
  position: fixed;
  inset: 0;
  z-index: -2;
  pointer-events: none;
  background-position: center;
  background-size: cover;
  background-repeat: no-repeat;
}
#bg-baked[hidden] { display: none; }
#bg .gb { fill: var(--bg1); stroke: var(--bg1); }
#bg .ng { fill: var(--bg2); stroke: var(--bg2); }
#bg .band { opacity: 0.16; }
#bg .band.dim { opacity: 0.08; }
#bg .ln, #bg .ln2 { fill: none; stroke-linecap: round; }
#bg .ln { stroke-width: 9; opacity: 0.45; }
#bg .ln2 { stroke-width: 2; opacity: 0.85; }

/* Fully static: the background never animates. Each ribbon and line gets a fixed random transform
   at page load (randomizeBackground() in app.js), pivoting on its own centre. With nothing moving,
   the blur on #bg is rasterised once and cached, so the background costs nothing per frame. */
#bg .band, #bg .ln, #bg .ln2 { transform-box: fill-box; transform-origin: center; }

/* Central focus zone: blurs and darkens the background behind the interactive column so the
   shapes never fight the text, while the edges keep their crisp colour. */
#bg-scrim {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: radial-gradient(64% 58% at 50% 46%, rgba(16, 17, 22, 0.74), rgba(16, 17, 22, 0) 78%);
  -webkit-mask: radial-gradient(62% 56% at 50% 46%, #000 48%, transparent 82%);
  mask: radial-gradient(62% 56% at 50% 46%, #000 48%, transparent 82%);
}

body {
  margin: 0;
  padding: 0 16px 64px;
  background: var(--bg);
  color: var(--text);
  font-family: ui-monospace, "SF Mono", "Cascadia Mono", Menlo, Consolas, monospace;
  min-height: 100vh;
}

/* Slim top navigation. Buttons are placeholders for now (Leaderboard / Disclaimer / Profile),
   not yet wired to any page. */
.topbar {
  display: flex;
  justify-content: flex-end;
  flex-wrap: wrap;
  gap: 10px;
  max-width: 980px;
  margin: 0 auto;
  padding: 16px 0 0;
}

.nav-link {
  background: var(--btn);
  border: 1.5px solid var(--logo-gb);
  color: var(--logo-gb);
  font-family: var(--ui);
  font-weight: 700;
  font-size: 0.74rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 9px 18px;
  border-radius: 10px;
  cursor: pointer;
  transition: color 0.16s, background 0.16s, box-shadow 0.16s, transform 0.12s;
  box-shadow: 0 4px 14px -8px #000;
  user-select: none;
  -webkit-user-select: none;
}

.nav-link:hover {
  color: #070810;
  background: var(--logo-gb);
  box-shadow: 0 6px 18px -8px var(--logo-gb);
  transform: translateY(-1px);
}

header {
  text-align: center;
  padding: 52px 0 20px;
}

@media (max-width: 560px) {
  .topbar { justify-content: center; }
}

h1 {
  margin: 0;
  font-size: clamp(2.2rem, 8vw, 3.6rem);
  letter-spacing: 0.35em;
  font-weight: 700;
  text-indent: 0.35em;
}

/* Logo: R is neutral and shared by both hidden words; GB and NG each keep a fixed colour.
   The case crossfades (uppercase glyph over lowercase, opacity-swapped) so no layout shifts,
   and the active word's group lifts and glows while the other dims. */
.logo {
  font-family: "Frijole", ui-monospace, sans-serif;
  font-weight: 400;
  font-variant-caps: small-caps;   /* lowercase renders as small caps: same shape, smaller size */
  letter-spacing: 0.02em;
  text-indent: 0;
  user-select: none;
  -webkit-user-select: none;
}
/* Three transform layers that never fight: .lt bobs (idle wave), .em scales (active emphasis),
   the glyphs slide vertically (case change). */
.logo .lt {
  display: inline-block;
  color: var(--logo-r);
  cursor: default;
  animation: logo-bob 3.2s ease-in-out infinite;
}
.logo .lt-gb { color: var(--logo-gb); cursor: pointer; }
.logo .lt-ng { color: var(--logo-ng); cursor: pointer; }
.logo .lt:nth-child(1) { animation-delay: 0s; }
.logo .lt:nth-child(2) { animation-delay: 0.22s; }
.logo .lt:nth-child(3) { animation-delay: 0.44s; }
.logo .lt:nth-child(4) { animation-delay: 0.66s; }
.logo .lt:nth-child(5) { animation-delay: 0.88s; }

.logo .em {
  position: relative;
  display: block;
  text-align: center;
  transition: transform 0.5s ease, filter 0.5s ease, text-shadow 0.5s ease;
}
.logo .lt b {
  font-style: normal;
  font-weight: inherit;
  display: block;
  /* The hidden glyphs are shifted 115% up/down; without this their invisible boxes stay
     hoverable and trigger the focus swap when the pointer is above or below the logo. */
  pointer-events: none;
  transition: transform 0.5s cubic-bezier(0.2, 0.8, 0.3, 1), opacity 0.45s ease;
}
.logo .lt .l { position: absolute; inset: 0; }
/* hidden states: uppercase waits below, lowercase waits above, so a switch reads as a slide up */
.logo .lt .u { transform: translateY(115%); opacity: 0; }
.logo .lt .l { transform: translateY(-115%); opacity: 0; }
.logo .lt-r .u { transform: none; opacity: 1; }   /* R is shared and never switches */

/* active group shows its uppercase, inactive shows its small-cap lowercase */
.logo.show-rgb .lt-gb .u, .logo.show-rng .lt-ng .u,
.logo.show-rgb .lt-ng .l, .logo.show-rng .lt-gb .l {
  transform: translateY(0);
  opacity: 1;
}

.logo.show-rgb .lt-gb .em, .logo.show-rng .lt-ng .em {
  transform: scale(1.06);
  text-shadow: 0 0 22px currentColor;
}
.logo.show-rgb .lt-ng .em, .logo.show-rng .lt-gb .em {
  transform: scale(0.9);
  filter: brightness(0.88);
}

/* R is shared: on every swap the new group's colour wipes diagonally across the glyph,
   replacing the previous colour. --r-from/--r-to are set in JS (leaving vs incoming group);
   --wipe is the animated boundary position. Two animation names (one per direction) so
   toggling show-rgb <-> show-rng always changes animation-name and re-triggers. */
@property --wipe {
  syntax: '<percentage>';
  initial-value: 0%;
  inherits: false;
}
.logo { --r-from: var(--logo-gb); --r-to: var(--logo-gb); }
.logo .lt-r .em {
  --wipe: 100%;
  background-image: linear-gradient(125deg,
    var(--r-to) 0 calc(var(--wipe) - 7%),
    rgba(255, 255, 255, 0.92) var(--wipe),
    var(--r-from) calc(var(--wipe) + 7%) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
  text-shadow: 0 0 20px var(--r-to);
  transition: text-shadow 0.6s ease;
}
.logo.show-rgb .lt-r .em { animation: r-wipe-a 1.15s cubic-bezier(0.4, 0, 0.2, 1) both; }
.logo.show-rng .lt-r .em { animation: r-wipe-b 1.15s cubic-bezier(0.4, 0, 0.2, 1) both; }
@keyframes r-wipe-a { from { --wipe: 0%; } to { --wipe: 100%; } }
@keyframes r-wipe-b { from { --wipe: 0%; } to { --wipe: 100%; } }

@keyframes logo-bob {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-6px); }
}

.tagline {
  max-width: 500px;
  margin: 18px auto 0;
  color: var(--text);
  font-size: 0.8rem;
  line-height: 1.7;
}

.fullnum {
  font: inherit;
  color: var(--logo-gb);
  background: none;
  border: none;
  border-bottom: 1px dashed currentColor;
  padding: 0;
  cursor: pointer;
  white-space: nowrap;
}
.fullnum sup { font-size: 0.7em; }
.fullnum:hover { color: var(--logo-ng); }

#count-dialog {
  max-width: min(92vw, 560px);
  border: 1px solid var(--glass-line);
  border-radius: 16px;
  background: linear-gradient(180deg, rgba(36, 38, 51, 0.92), rgba(23, 24, 30, 0.94));
  color: var(--text);
  padding: 24px 26px;
  text-align: center;
  box-shadow: 0 30px 70px -24px rgba(0, 0, 0, 0.8);
}
#count-dialog::backdrop { background: rgba(0, 0, 0, 0.6); backdrop-filter: blur(3px); }
.count-label { margin: 0 0 14px; color: var(--muted); font-size: 0.72rem; letter-spacing: 0.06em; }
.count-full {
  margin: 0;
  font-size: 0.95rem;
  font-weight: 700;
  line-height: 1.5;
  word-break: break-all;
  background: var(--grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.count-sci { margin: 12px 0 20px; color: var(--muted); font-size: 0.78rem; }
#count-close {
  font-family: var(--ui);
  font-weight: 600;
  font-size: 0.74rem;
  text-transform: uppercase;
  cursor: pointer;
  color: var(--text);
  background: var(--glass);
  border: 1px solid var(--glass-line);
  border-radius: 10px;
  padding: 10px 22px;
  letter-spacing: 0.12em;
  transition: border-color 0.15s, box-shadow 0.15s;
}
#count-close:hover { border-color: var(--logo-gb); box-shadow: 0 0 0 1px var(--logo-gb); }

.modes {
  display: flex;
  justify-content: center;
  gap: 8px;
  margin-bottom: 36px;
}

/* Opaque chip with a solid logo-hue outline and text at rest, so it reads clearly against the
   colourful background instead of blending into it. The active tab fills solid with the logo hue. */
.mode {
  border: 1.5px solid var(--logo-gb);
  background: var(--btn);
  color: var(--logo-gb);
  padding: 10px 20px;
  font-family: var(--ui);
  font-weight: 700;
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  border-radius: 11px;
  cursor: pointer;
  transition: color 0.18s, box-shadow 0.18s, transform 0.12s, background 0.18s;
  box-shadow: 0 4px 14px -8px #000;
  user-select: none;
  -webkit-user-select: none;
}

.mode:hover {
  background: var(--btn-hi);
  box-shadow: 0 6px 18px -10px var(--logo-gb);
  transform: translateY(-1px);
}

.mode.active {
  color: #070810;
  background: var(--logo-gb);
  border-color: var(--logo-gb);
  box-shadow: 0 8px 22px -8px var(--logo-gb), 0 2px 0 0 rgba(255, 255, 255, 0.28) inset;
}
.mode.active:hover { transform: translateY(-1px); filter: brightness(1.06); }

#sound-btn.muted { opacity: 0.5; text-decoration: line-through; }

/* Endless-mode testing aid. */
#luck {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  margin: 16px 0 0;
  padding: 13px 15px;
  border: 1px dashed var(--glass-line);
  border-radius: 12px;
  background: var(--glass);
  font-size: 0.72rem;
  color: var(--muted);
}

/* The display:flex above would otherwise cancel the hidden attribute set in switchMode. */
#luck[hidden] { display: none; }

#luck label { font-family: var(--ui); font-weight: 600; letter-spacing: 0.14em; text-transform: uppercase; color: var(--text); }

#luck select {
  background: rgba(20, 21, 27, 0.9);
  border: 1px solid var(--glass-line);
  color: var(--text);
  font-family: var(--ui);
  font-size: 0.72rem;
  padding: 8px 10px;
  border-radius: 8px;
  cursor: pointer;
}
#luck select:focus { outline: none; border-color: var(--logo-gb); box-shadow: 0 0 0 1px var(--logo-gb); }

#luck-note { flex: 1 1 100%; opacity: 0.75; line-height: 1.6; }

main {
  max-width: 620px;
  margin: 0 auto;
}

/* --- swatches --- */

/* Sticky with a negative offset: the strip keeps its full 220px height at all times and
   simply slides up until only its bottom band is left showing. Nothing ever changes size,
   so the page cannot reflow, the shrink reads as a smooth scroll rather than a jump, and a
   reel mid-spin keeps the exact geometry it started with. */
#stage {
  position: sticky;
  top: -158px;
  z-index: 50;
  height: 220px;
  margin-bottom: 4px;
}

#swatches {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  height: 100%;
  border: 1px solid var(--glass-line);
  border-radius: 14px;
  overflow: hidden;
  box-shadow: 0 16px 34px -12px rgba(0, 0, 0, 0.6);
}

.swatch {
  position: relative;
  overflow: hidden;
  background: var(--c, #262936);
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding-bottom: 12px;
  transition: opacity 0.2s, filter 0.2s, transform 0.2s;
}

/* The end swatches carry the strip's rounding themselves, so the corners stay round even while
   #swatches.inspect drops overflow to visible (which otherwise unclips and squares them). */
.swatch:first-child { border-top-left-radius: 13px; border-bottom-left-radius: 13px; }
.swatch:last-child { border-top-right-radius: 13px; border-bottom-right-radius: 13px; }

/* Sized from the swatch rather than in pixels, so the cells always line up with it exactly
   whatever its height. */
.reel {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: calc(100% * var(--units, 1));
  display: none;
  will-change: transform;
}

.reel i.mystery {
  display: flex;
  align-items: center;
  justify-content: center;
  height: var(--mystery-h, 100%);
  background: #0b0c10;
  color: #9aa1bd;
  font-size: 3.4rem;
  font-weight: 700;
  text-shadow: 0 0 18px rgba(154, 161, 189, 0.45);
  border-bottom: 3px solid #333848;
}

.swatch.spinning .reel { display: block; }

#swatches.idle .swatch {
  background: repeating-linear-gradient(45deg, #23252f, #23252f 6px, #282b36 6px, #282b36 12px);
}

/* Both chips sit at the bottom (RGB just above hex) so they stay inside the thin band that
   remains visible once the sticky strip slides up behind the badge list. pointer-events:none so
   clicks between/around them fall through to the swatch (which opens the enlarged view); the
   chips themselves re-enable pointer events for copy. */
.label {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: center;
  gap: 5px;
  padding: 10px 6px 9px;
  pointer-events: none;
}

.rgb {
  pointer-events: none;        /* enabled only when the swatch is locked (see below) */
  white-space: nowrap;         /* value + copy icon stay on one line, never wrap */
  font-size: 0.65rem;
  letter-spacing: 0.06em;
  padding: 4px 6px;
  user-select: none;           /* clicking copies via clipboard, never selects the text */
  -webkit-user-select: none;
  border-radius: 3px;
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  transition: background 0.15s, box-shadow 0.15s;
}

/* Small dim label in front of each value. content-only, so the scrambler rewriting the value
   text never wipes it, and the copied string (from dataset) stays clean. */
.rgb::before, .hex::before {
  font-family: var(--ui);
  font-weight: 700;
  font-size: 0.82em;
  letter-spacing: 0.1em;
  opacity: 0.6;
  margin-right: 5px;
}
.rgb::before { content: 'RGB'; }
.hex::before { content: 'HEX'; }

.swatch.light .rgb {
  background: rgba(255, 255, 255, 0.72);
  color: #000;
}

/* Copy is available only on a resolved (locked) swatch: idle/greyed chips are not clickable and
   show no copy affordance. A copy icon after each value makes it obvious the code is clickable,
   the hover ring reinforces it, and .copied flashes a green confirmation. */
.swatch.locked .rgb, .swatch.locked .hex { pointer-events: auto; cursor: pointer; }
.swatch.locked .rgb::after, .swatch.locked .hex::after {
  content: '';
  display: inline-block;
  width: 0.82em;
  height: 0.82em;
  margin-left: 6px;
  vertical-align: -0.12em;
  background: currentColor;
  opacity: 0.55;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Crect x='8' y='8' width='13' height='13' rx='2'/%3E%3Cpath d='M4 4h11v3H7v10H4z'/%3E%3C/svg%3E") center/contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Crect x='8' y='8' width='13' height='13' rx='2'/%3E%3Cpath d='M4 4h11v3H7v10H4z'/%3E%3C/svg%3E") center/contain no-repeat;
}
.swatch.locked .rgb:hover, .swatch.locked .hex:hover {
  background: rgba(0, 0, 0, 0.82);
  box-shadow: 0 0 0 1.5px var(--logo-gb);
}
.swatch.light.locked .rgb:hover, .swatch.light.locked .hex:hover {
  background: rgba(255, 255, 255, 0.92);
}
.swatch.locked .rgb:hover::after, .swatch.locked .hex:hover::after { opacity: 1; }
/* Copied state: the JS swaps the value text to "Copied" and we hide the RGB/HEX prefix and copy
   icon. The pill turns a SOLID opaque dark (so no swatch colour bleeds through and washes out the
   text) with bright green text, and a double ring (green + a dark outline) so the confirmation is
   readable on any swatch colour, including green or light ones. */
.rgb.copied, .hex.copied {
  background: #0b1712 !important;
  color: var(--tier-uncommon) !important;
  box-shadow: 0 0 0 1.6px var(--tier-uncommon), 0 0 0 3.4px rgba(0, 0, 0, 0.7) !important;
}
.rgb.copied::before, .hex.copied::before,
.rgb.copied::after, .hex.copied::after { display: none; }

#swatches.idle .label { opacity: 0; }

.swatch .label { opacity: 0; }
.swatch.locked .label, .swatch.spinning .label { opacity: 1; }

/* While spinning the label churns through random values, so it reads as unresolved. */
.swatch.spinning .hex,
.swatch.spinning .rgb {
  background: rgba(0, 0, 0, 0.72);
  color: #6f7591;
}

.swatch.locked .label { animation: hex-snap 0.45s cubic-bezier(0.2, 1.5, 0.4, 1); }

@keyframes hex-snap {
  0% { transform: scale(1.7); opacity: 0; letter-spacing: 0.4em; }
  100% { transform: scale(1); opacity: 1; letter-spacing: 0.06em; }
}

.swatch.locked { animation: lock-in 0.6s cubic-bezier(0.18, 1.6, 0.35, 1); cursor: zoom-in; }

@keyframes lock-in {
  0% { transform: scale(0.72); filter: brightness(5) saturate(2.5); }
  35% { transform: scale(1.12); filter: brightness(1.8) saturate(1.5); }
  60% { transform: scale(0.98); filter: brightness(1.1); }
  100% { transform: scale(1); filter: none; }
}

/* Reveal burst: a shockwave ring plus shards thrown outwards in the colour that just landed. */
.burst {
  position: absolute;
  inset: 0;
  z-index: 4;
  pointer-events: none;
  overflow: visible;
}

.burst b {
  position: absolute;
  inset: 0;
  background: #fff;
  animation: flash 0.3s ease-out forwards;
}

@keyframes flash {
  0% { opacity: 0.95; }
  100% { opacity: 0; }
}

.burst u {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 8px;
  height: 8px;
  margin: -4px 0 0 -4px;
  border-radius: 50%;
  border: 4px solid #fff;
  animation: shockwave 0.65s cubic-bezier(0.1, 0.8, 0.3, 1) forwards;
}

@keyframes shockwave {
  0% { transform: scale(0.4); opacity: 1; border-width: 5px; }
  100% { transform: scale(17); opacity: 0; border-width: 1px; }
}

.burst i {
  position: absolute;
  left: 50%;
  top: 50%;
  width: var(--size, 5px);
  height: var(--size, 5px);
  border-radius: 50%;
  opacity: 0;
  animation: shard 0.75s cubic-bezier(0.12, 0.7, 0.25, 1) var(--delay, 0ms) forwards;
}

@keyframes shard {
  0% { transform: translate(-50%, -50%) scale(0.3); opacity: 1; }
  70% { opacity: 1; }
  100% {
    transform: translate(calc(-50% + var(--dx)), calc(-50% + var(--dy))) scale(0.2);
    opacity: 0;
  }
}

/* While inspecting a badge, let a lifted swatch spill past the strip instead of being clipped.
   Safe here because the reels only need clipping while spinning, never during result hover. */
#swatches.inspect { overflow: visible; }

/* Colours a hovered badge does not concern go dark but stay opaque (no text showing through);
   the ones it does concern grow and lift out with a bright ring. */
.swatch.dimmed { filter: brightness(0.25) saturate(0.4); }
.swatch.highlight {
  transform-origin: center top;
  transform: scaleY(1.18);
  z-index: 3;
  box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.9);
}

.hex {
  position: relative;
  z-index: 2;
  pointer-events: none;        /* enabled only when the swatch is locked (see below) */
  white-space: nowrap;         /* value + copy icon stay on one line, never wrap */
  font-size: 0.65rem;
  letter-spacing: 0.06em;
  padding: 4px 6px;
  border-radius: 3px;
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  transition: background 0.15s, box-shadow 0.15s, opacity 0.25s;
  user-select: none;           /* clicking copies via clipboard, never selects the text */
  -webkit-user-select: none;
}

.swatch.light .hex {
  background: rgba(255, 255, 255, 0.72);
  color: #000;
}

/* --- launcher --- */

#launcher {
  text-align: center;
  margin: 28px 0;
}

/* Hero button: a solid slab in the refresh's logo hue (opaque, so it never blends into the
   background). The ::before is a glossy top-light for a physical, arcade-button read. */
#roll-btn {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  background: var(--logo-gb);
  border: none;
  color: #070810;
  font-family: var(--ui);
  font-weight: 700;
  font-size: 1.08rem;
  letter-spacing: 0.34em;
  text-indent: 0.34em;
  text-transform: uppercase;
  padding: 20px 64px;
  border-radius: 16px;
  cursor: pointer;
  transition: transform 0.15s, box-shadow 0.22s, filter 0.2s;
  box-shadow:
    0 14px 38px -10px var(--logo-gb),
    0 2px 0 0 rgba(255, 255, 255, 0.28) inset,
    0 -10px 20px -12px rgba(0, 0, 0, 0.5) inset;
  user-select: none;
  -webkit-user-select: none;
}
#roll-btn::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.35), rgba(255, 255, 255, 0) 46%);
  pointer-events: none;
}

#roll-btn:hover:not(:disabled) {
  transform: translateY(-2px);
  filter: brightness(1.08);
  box-shadow:
    0 20px 50px -10px var(--logo-gb),
    0 2px 0 0 rgba(255, 255, 255, 0.4) inset,
    0 -10px 20px -12px rgba(0, 0, 0, 0.5) inset;
}
#roll-btn:active:not(:disabled) { transform: translateY(0) scale(0.985); }

#roll-btn:disabled {
  background: var(--glass);
  color: var(--muted);
  box-shadow: inset 0 0 0 1px var(--glass-line);
  cursor: not-allowed;
}
#roll-btn:disabled::before { display: none; }

#launcher-note {
  color: var(--text);
  font-size: 0.75rem;
  margin-top: 16px;
  line-height: 1.7;
  white-space: pre-line;
}

/* --- result --- */

#result {
  position: relative;
  border: 1px solid var(--glass-line);
  border-radius: 18px;
  background: linear-gradient(180deg, rgba(34, 36, 49, 0.93), rgba(22, 23, 29, 0.95));
  padding: 26px 24px 24px;
  box-shadow: 0 26px 64px -22px rgba(0, 0, 0, 0.75);
  animation: reveal 0.4s ease-out;
}
/* Thin RGB gradient hairline along the top edge, tying the panel to the roll's hues. */
#result::before {
  content: '';
  position: absolute;
  left: 18px;
  right: 18px;
  top: 0;
  height: 2px;
  border-radius: 2px;
  background: var(--grad);
  opacity: 0.85;
}

@keyframes reveal {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: none; }
}

.verdict {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  padding-bottom: 20px;
  border-bottom: 1px solid var(--line);
  animation: reveal 0.4s ease-out;
}

/* Beats the display:flex above, which would otherwise cancel the hidden attribute. */
.verdict[hidden] { display: none; }

.tier {
  position: relative;
  font-family: var(--ui);
  font-size: 1.15rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  padding: 11px 22px;
  border: 2px solid currentColor;
  border-radius: 10px;
  text-shadow: 0 0 12px currentColor;
}

/* The glow lives on a pseudo-element with a STATIC box-shadow whose OPACITY pulses (compositor),
   instead of animating box-shadow itself (a repaint every frame). The scale pulses on the .tier
   element via transform (also compositor). Together they keep the old look with no per-frame paint. */
.tier::after {
  content: '';
  position: absolute;
  inset: -2px;
  border-radius: inherit;
  box-shadow: 0 0 22px 1px currentColor;
  opacity: 0;
  pointer-events: none;
}

.tier[data-tier="common"] { color: var(--tier-common); text-shadow: none; }
.tier[data-tier="uncommon"] { color: var(--tier-uncommon); }
.tier[data-tier="rare"] { color: var(--tier-rare); }
.tier[data-tier="epic"] { color: var(--tier-epic); animation: tier-scale 1.4s ease-in-out infinite alternate; }
.tier[data-tier="legendary"] { color: var(--tier-legendary); animation: tier-scale 1.1s ease-in-out infinite alternate; }
.tier[data-tier="celestial"] { color: var(--tier-celestial); animation: tier-scale 0.95s ease-in-out infinite alternate; }
.tier[data-tier="singularity"] { color: var(--tier-singularity); animation: tier-scale-strong 0.85s ease-in-out infinite alternate; }
.tier[data-tier="impossible"] { color: var(--tier-impossible); animation: tier-scale-strong 0.55s ease-in-out infinite alternate; }

.tier[data-tier="uncommon"]::after { animation: tier-glow 1.9s ease-in-out infinite alternate; }
.tier[data-tier="rare"]::after { animation: tier-glow 1.5s ease-in-out infinite alternate; }
.tier[data-tier="epic"]::after { animation: tier-glow 1.4s ease-in-out infinite alternate; }
.tier[data-tier="legendary"]::after { animation: tier-glow 1.1s ease-in-out infinite alternate; }
.tier[data-tier="celestial"]::after { animation: tier-glow 0.95s ease-in-out infinite alternate; }
.tier[data-tier="singularity"]::after { box-shadow: 0 0 40px 2px currentColor; animation: tier-glow-strong 0.85s ease-in-out infinite alternate; }
.tier[data-tier="impossible"]::after { box-shadow: 0 0 40px 2px currentColor; animation: tier-glow-strong 0.55s ease-in-out infinite alternate; }

@keyframes tier-glow { from { opacity: 0.15; } to { opacity: 0.8; } }
@keyframes tier-glow-strong { from { opacity: 0.35; } to { opacity: 1; } }
@keyframes tier-scale { from { transform: scale(1); } to { transform: scale(1.05); } }
@keyframes tier-scale-strong { from { transform: scale(1); } to { transform: scale(1.09); } }

#score-line { text-align: right; }

#score {
  font-family: var(--ui);
  font-size: 1.9rem;
  font-weight: 700;
}

.score-unit {
  color: var(--muted);
  font-size: 0.68rem;
  letter-spacing: 0.16em;
  margin-left: 8px;
}

#specials {
  margin-top: 18px;
  padding: 12px 14px;
  border: 1px solid color-mix(in srgb, var(--tier-celestial) 55%, transparent);
  border-radius: 10px;
  background: color-mix(in srgb, var(--tier-celestial) 10%, transparent);
  color: var(--tier-celestial);
  font-size: 0.78rem;
  line-height: 1.6;
  text-shadow: 0 0 10px color-mix(in srgb, var(--tier-celestial) 45%, transparent);
}

h2 {
  font-family: var(--ui);
  font-size: 0.74rem;
  font-weight: 600;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  margin: 28px 0 16px;
  width: max-content;
  background: var(--grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

#patterns {
  list-style: none;
  margin: 0;
  padding: 0;
}

#patterns li {
  position: relative;
  overflow: hidden;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 18px;
  padding: 18px 14px;
  margin-bottom: 8px;
  border: 1px solid var(--glass-line);
  border-left: 3px solid rgba(var(--tint), 0.75);
  border-radius: 11px;
  background:
    linear-gradient(90deg, rgba(var(--tint), 0.08), rgba(var(--tint), 0) 40%),
    rgba(255, 255, 255, 0.022);
  transition: background 0.15s, border-color 0.15s, transform 0.12s, box-shadow 0.15s;
}

/* Same trick as the verdict: an ID rule with display:flex outranks the hidden attribute, so
   rows waiting their turn are taken out with a class instead. */
#patterns li.waiting { display: none; }

/* Each badge is prepended at the top, so it grows in from zero height and shoves the rows
   below it downward, while a light sweeps across and the score lands with a bounce. */
#patterns li.appearing {
  animation: row-in 0.4s cubic-bezier(0.2, 0.9, 0.3, 1);
}

/* Transform + opacity only: no layout-triggering properties (max-height/padding/margin), so
   each reveal composites on the GPU instead of reflowing the whole list every frame. The row
   takes its full height at once; the rows below shift down in a single layout per insert. */
@keyframes row-in {
  0% { opacity: 0; transform: translateY(-12px); }
  100% { opacity: 1; transform: none; }
}

/* Lit by the badge's own colour instead of a white sweep across the middle: the glow sits
   behind the palette chips on the left and has faded out well before the text begins. */
#patterns li.appearing::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(90deg,
      rgba(var(--tint), 0.55) 0%,
      rgba(var(--tint), 0.16) 16%,
      rgba(var(--tint), 0.04) 30%,
      transparent 44%);
  animation: tint-wash 0.8s ease-out forwards;
}

@keyframes tint-wash {
  0% { opacity: 0; transform: translateX(-12px); }
  25% { opacity: 1; transform: none; }
  100% { opacity: 0; transform: none; }
}

#patterns li.appearing::after {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 3px;
  pointer-events: none;
  transform-origin: top;
  background: rgb(var(--tint));
  box-shadow: 0 0 10px rgba(var(--tint), 0.9);
  animation: edge-draw 0.85s cubic-bezier(0.2, 0.9, 0.3, 1) forwards;
}

@keyframes edge-draw {
  0% { transform: scaleY(0); opacity: 1; }
  40% { transform: scaleY(1); opacity: 1; }
  100% { transform: scaleY(1); opacity: 0; }
}

/* Keeps the content clear of both effects. */
.p-strip, .p-main, .p-points { position: relative; z-index: 1; }

#patterns li.appearing .p-points {
  animation: points-pop 0.5s cubic-bezier(0.2, 1.7, 0.4, 1);
}

@keyframes points-pop {
  0% { transform: scale(2.4); opacity: 0; }
  55% { transform: scale(0.94); opacity: 1; }
  100% { transform: scale(1); opacity: 1; }
}

#patterns li.appearing .p-strip i {
  animation: chip-in 0.4s ease-out backwards;
}

.p-strip i:nth-child(1) { animation-delay: 0.04s; }
.p-strip i:nth-child(2) { animation-delay: 0.08s; }
.p-strip i:nth-child(3) { animation-delay: 0.12s; }
.p-strip i:nth-child(4) { animation-delay: 0.16s; }
.p-strip i:nth-child(5) { animation-delay: 0.2s; }

@keyframes chip-in {
  from { opacity: 0; transform: scaleY(0.2); }
}

#patterns li:last-child { margin-bottom: 0; }

#patterns li.targetable { cursor: help; }
#patterns li.targetable:hover {
  background:
    linear-gradient(90deg, rgba(var(--tint), 0.16), rgba(var(--tint), 0) 46%),
    rgba(255, 255, 255, 0.05);
  border-color: rgba(var(--tint), 0.6);
  box-shadow: 0 8px 26px -14px rgb(var(--tint));
  transform: scale(1.015);
}

.p-main {
  display: flex;
  flex-direction: column;
  gap: 7px;
  min-width: 0;
  flex: 1;
}

.p-label {
  font-family: var(--ui);
  font-weight: 600;
  font-size: 0.92rem;
  letter-spacing: 0.02em;
}

/* Category tag: says whether the badge is about the hex code, the RGB channels, or the look. */
.p-cat {
  display: inline-block;
  font-family: var(--ui);
  font-size: 0.56rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  vertical-align: middle;
  padding: 2px 6px;
  margin-right: 8px;
  border-radius: 5px;
  border: 1px solid color-mix(in srgb, currentColor 55%, transparent);
  background: color-mix(in srgb, currentColor 16%, transparent);
  text-shadow: 0 0 8px color-mix(in srgb, currentColor 60%, transparent);
}

.cat-hex { color: #6fb6ff; }
.cat-rgb { color: #6ad39b; }
.cat-visual { color: #c79bff; }

/* The palette repeated per row: colours that earned the badge stay lit, the others fade out. */
.p-strip {
  display: flex;
  flex: 0 0 auto;
  gap: 2px;
  cursor: help;
}

.p-strip i {
  width: 13px;
  height: 34px;
  border-radius: 2px;
  transition: opacity 0.15s, transform 0.15s;
}

.p-strip i.off {
  opacity: 0.13;
  transform: scaleY(0.62);
}

.p-strip i.on {
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.35);
}

.p-note {
  color: var(--muted);
  font-size: 0.76rem;
  line-height: 1.65;
}

/* The numbers that actually triggered the badge, for the ones where the eye cannot tell. */
.p-evidence {
  color: var(--text);
  font-size: 0.73rem;
  line-height: 1.5;
  opacity: 0.85;
  padding-left: 8px;
  border-left: 2px solid rgba(var(--tint), 0.75);
}

.p-rarity {
  color: var(--muted);
  font-size: 0.7rem;
  letter-spacing: 0.06em;
  opacity: 0.7;
}

.p-points {
  display: inline-block;
  font-family: var(--ui);
  font-weight: 600;
  font-size: 0.95rem;
  white-space: nowrap;
  color: var(--text);
  opacity: 0.85;
}

#no-pattern {
  color: var(--muted);
  font-size: 0.78rem;
  line-height: 1.6;
  margin: 0;
}


.actions {
  display: flex;
  gap: 10px;
  margin-top: 24px;
}

.actions button {
  flex: 1;
  border: 1.5px solid var(--logo-gb);
  background: var(--btn);
  border-radius: 11px;
  color: var(--logo-gb);
  font-family: var(--ui);
  font-weight: 700;
  font-size: 0.75rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 14px;
  cursor: pointer;
  box-shadow: 0 4px 14px -8px #000;
  transition: box-shadow 0.15s, transform 0.12s, color 0.15s, background 0.18s;
}

.actions button:hover:not(:disabled) {
  color: #070810;
  background: var(--logo-gb);
  box-shadow: 0 8px 22px -10px var(--logo-gb);
  transform: translateY(-1px);
}
.actions button[hidden] { display: none; }

footer {
  max-width: 620px;
  margin: 48px auto 0;
  text-align: center;
  color: var(--text);
  font-size: 0.7rem;
  line-height: 1.8;
}

#session-stats { margin-bottom: 12px; }

.fineprint { opacity: 0.78; }

@media (max-width: 480px) {
  #stage { height: 180px; top: -126px; }
  .hex { font-size: 0.58rem; }
  .verdict { justify-content: center; }
  #score-line { text-align: center; }
}

/* --- enlarged colour view --------------------------------------------------- */

#color-view {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(8, 9, 12, 0.62);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  animation: cv-fade 0.2s ease-out;
}
#color-view[hidden] { display: none; }

@keyframes cv-fade { from { opacity: 0; } to { opacity: 1; } }

/* Sized to the colour block; the arc is positioned relative to it. */
.cv-stage {
  position: relative;
  animation: cv-pop 0.28s cubic-bezier(0.2, 1.3, 0.35, 1);
}

@keyframes cv-pop {
  from { transform: scale(0.82); opacity: 0; }
  to { transform: scale(1); opacity: 1; }
}

/* Exit: reverse of the entry, applied while the .closing class is on before the element hides. */
#color-view.closing { animation: cv-fade-out 0.2s ease-in forwards; }
#color-view.closing .cv-stage { animation: cv-pop-out 0.2s ease-in forwards; }
@keyframes cv-fade-out { to { opacity: 0; } }
@keyframes cv-pop-out { to { transform: scale(0.86); opacity: 0; } }

.cv-color {
  width: min(64vmin, 440px);
  height: min(64vmin, 440px);
  border-radius: 26px;
  box-shadow: 0 40px 90px -30px rgba(0, 0, 0, 0.8), inset 0 0 0 1px rgba(255, 255, 255, 0.14);
}

/* Small arc of buttons sitting entirely OUTSIDE the colour's right edge; the middle one bulges
   further out so the three read as an arc. */
.cv-arc {
  position: absolute;
  top: 50%;
  left: 100%;
  transform: translateY(-50%);
  display: flex;
  flex-direction: column;
  gap: 16px;
  margin-left: 22px;
}
.cv-arc .cv-btn:nth-child(2) { transform: translateX(16px); }

.cv-btn {
  position: relative;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  border: 1px solid var(--glass-line);
  background: rgba(20, 21, 27, 0.82);
  color: var(--text);
  font-family: var(--ui);
  font-weight: 700;
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  box-shadow: 0 10px 24px -12px rgba(0, 0, 0, 0.9);
  transition: color 0.15s, border-color 0.15s, box-shadow 0.15s;
}
.cv-btn:hover {
  border-color: var(--logo-gb);
  color: #fff;
  box-shadow: 0 0 0 1px var(--logo-gb), 0 10px 24px -12px var(--logo-gb);
  z-index: 5;
}

/* Hover label: slides out to the right, emerging from behind the button. */
.cv-btn::after {
  content: attr(data-label);
  position: absolute;
  left: 100%;
  top: 50%;
  white-space: nowrap;
  padding: 6px 11px;
  border-radius: 9px;
  border: 1px solid var(--glass-line);
  background: rgba(16, 17, 22, 0.96);
  color: #fff;
  font-family: var(--ui);
  font-weight: 600;
  font-size: 0.62rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  pointer-events: none;
  opacity: 0;
  transform: translate(-45%, -50%) scale(0.85);
  transition: transform 0.22s cubic-bezier(0.2, 0.9, 0.3, 1.2), opacity 0.16s ease;
}
.cv-btn:hover::after {
  opacity: 1;
  transform: translate(14px, -50%) scale(1);
}

.cv-close { font-size: 1.1rem; font-weight: 400; }

.cv-btn.copied {
  border-color: var(--tier-uncommon);
  color: var(--tier-uncommon);
  box-shadow: 0 0 0 1px var(--tier-uncommon), 0 10px 24px -12px var(--tier-uncommon);
}

/* On narrow screens the arc drops below the colour, and its labels slide up instead of right. */
@media (max-width: 560px) {
  .cv-arc {
    top: 100%;
    left: 50%;
    transform: translateX(-50%);
    flex-direction: row;
    margin-left: 0;
    margin-top: 22px;
  }
  .cv-arc .cv-btn:nth-child(2) { transform: translateY(16px); }
  .cv-btn::after {
    left: 50%;
    top: auto;
    bottom: 100%;
    transform: translate(-50%, 8px) scale(0.85);
  }
  .cv-btn:hover::after { transform: translate(-50%, -10px) scale(1); }
}

/* --- auth dialog --- */
#auth-dialog {
  border: 1px solid var(--glass-line);
  border-radius: 18px;
  background: linear-gradient(180deg, #191b24, #111219);
  color: var(--text);
  width: min(94vw, 420px);
  padding: 26px 28px 22px;
  position: relative;
  overflow: hidden;
  box-shadow: 0 30px 80px -20px rgba(0, 0, 0, 0.7);
}
#auth-dialog::backdrop { background: rgba(8, 9, 12, 0.62); backdrop-filter: blur(5px); }

.auth-close {
  position: absolute;
  top: 12px;
  right: 12px;
  width: 30px;
  height: 30px;
  border: none;
  border-radius: 8px;
  background: transparent;
  color: var(--muted);
  font-size: 1.3rem;
  line-height: 1;
  cursor: pointer;
  transition: background 0.15s, color 0.15s;
}
.auth-close:hover { background: rgba(255, 255, 255, 0.06); color: var(--text); }

.auth-title {
  margin: 0 0 10px;
  padding-top: 3px;
  font-family: "Frijole", var(--ui);
  font-weight: 400;
  font-size: 1.05rem;
  line-height: 1.4;
  background: var(--grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.auth-lead { margin: 0 0 16px; color: var(--muted); font-size: 0.82rem; line-height: 1.55; }
.auth-lead span, .auth-lead strong { color: var(--text); }

.auth-input {
  width: 100%;
  box-sizing: border-box;
  background: #0e0f16;
  border: 1.5px solid var(--glass-line);
  border-radius: 10px;
  color: var(--text);
  font-family: var(--ui);
  font-size: 0.95rem;
  padding: 12px 14px;
  margin-bottom: 14px;
  transition: border-color 0.15s, box-shadow 0.15s;
}
.auth-input:focus { outline: none; border-color: var(--logo-gb); box-shadow: 0 0 0 1px var(--logo-gb); }

.auth-primary {
  width: 100%;
  border: none;
  border-radius: 11px;
  background: var(--logo-gb);
  color: #070810;
  font-family: var(--ui);
  font-weight: 700;
  font-size: 0.86rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 13px;
  cursor: pointer;
  box-shadow: 0 10px 26px -12px var(--logo-gb), 0 2px 0 0 rgba(255, 255, 255, 0.25) inset;
  transition: filter 0.15s, transform 0.12s;
}
.auth-primary:hover { filter: brightness(1.08); transform: translateY(-1px); }
.auth-primary:active { transform: translateY(0); }

.auth-link {
  display: block;
  width: 100%;
  margin-top: 10px;
  background: none;
  border: none;
  color: var(--muted);
  font-family: var(--ui);
  font-size: 0.76rem;
  text-decoration: underline;
  cursor: pointer;
}
.auth-link:hover { color: var(--text); }

.rgb-hint {
  margin: 0 0 12px;
  padding: 9px 12px;
  border: 1px solid var(--logo-gb);
  border-radius: 10px;
  background: color-mix(in srgb, var(--logo-gb) 12%, transparent);
  color: var(--text);
  font-size: 0.78rem;
  font-weight: 600;
  text-align: center;
  white-space: nowrap;
}
.rgb-hint kbd {
  display: inline-block;
  min-width: 1.5em;
  padding: 1px 6px;
  margin: 0 2px;
  border: 1px solid var(--logo-gb);
  border-radius: 6px;
  background: rgba(0, 0, 0, 0.35);
  color: var(--logo-gb);
  font-family: var(--ui);
  font-size: 0.8rem;
  font-weight: 700;
  line-height: 1.4;
}
.rgb-inputs { display: flex; gap: 10px; margin-bottom: 14px; }
.rgb-inputs label {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  font-family: var(--ui);
  font-weight: 700;
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  color: var(--muted);
}
.rgb-box {
  width: 100%;
  box-sizing: border-box;
  text-align: center;
  background: #0e0f16;
  border: 1.5px solid var(--glass-line);
  border-radius: 10px;
  color: var(--text);
  font-family: ui-monospace, monospace;
  font-size: 1.25rem;
  font-weight: 700;
  padding: 12px 4px;
  transition: border-color 0.15s, box-shadow 0.15s;
}
.rgb-box:focus { outline: none; border-color: var(--logo-gb); box-shadow: 0 0 0 1px var(--logo-gb); }

.auth-swatch {
  height: 8px;
  border-radius: 5px;
  margin-bottom: 16px;
  background: transparent;
  border: 1px solid var(--glass-line);
  transition: background 0.15s;
}

.auth-msg { margin: 12px 0 0; min-height: 1em; font-size: 0.8rem; text-align: center; }
.auth-msg[data-kind="err"] { color: #ff6b8a; }
.auth-msg[data-kind="ok"] { color: var(--tier-uncommon); }

/* ============================================================================
   Sub-pages (profile / leaderboard / disclaimer)
   ============================================================================ */

/* Compact header: the logo doubles as a home link. */
header.sub { padding: 24px 0 8px; }
.logo-home { display: inline-block; text-decoration: none; }
header.sub h1 { font-size: clamp(1.7rem, 6vw, 2.6rem); }

.page {
  max-width: 820px;
  margin: 0 auto;
  padding: 8px 0 40px;
}

.page-title {
  font-family: "Frijole", var(--ui);
  font-weight: 400;
  font-size: 1.5rem;
  margin: 8px 0 18px;
  background: var(--grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  line-height: 1.4;
  padding-top: 4px;
}

.panel {
  background: var(--panel);
  border: 1px solid var(--glass-line);
  border-radius: 16px;
  padding: 22px 24px;
  margin-bottom: 18px;
  box-shadow: 0 18px 50px -30px #000;
}

.muted { color: var(--muted); font-size: 0.9rem; }
.lead { margin: 0 0 16px; color: var(--text); font-size: 0.96rem; line-height: 1.6; }
.section-h {
  font-family: var(--ui);
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 0 14px;
}

/* Category chips, shared by the disclaimer and the badge pills. */
.cat {
  display: inline-block;
  font-family: var(--ui);
  font-weight: 700;
  font-size: 0.68rem;
  letter-spacing: 0.1em;
  padding: 2px 8px;
  border-radius: 6px;
  vertical-align: middle;
}
.cat-rgb { color: #070810; background: var(--tier-rare); }
.cat-hex { color: #070810; background: var(--tier-legendary); }
.cat-visual { color: #070810; background: var(--tier-epic); }

/* ---- Disclaimer ---- */
.doc p { color: var(--text); font-size: 0.92rem; line-height: 1.65; margin: 0 0 14px; }
.doc .section-h { margin-top: 22px; }
.doc code {
  font-family: ui-monospace, monospace;
  background: var(--glass);
  border: 1px solid var(--glass-line);
  border-radius: 5px;
  padding: 1px 6px;
  font-size: 0.86em;
}
.callout {
  border: 1px solid var(--logo-gb);
  background: color-mix(in srgb, var(--logo-gb) 9%, transparent);
  border-radius: 12px;
  padding: 16px 18px;
  margin: 20px 0;
}
.callout h4 {
  margin: 0 0 10px;
  font-family: var(--ui);
  font-size: 0.95rem;
  color: var(--logo-gb);
}
.callout p { margin: 0 0 10px; }
.callout p:last-child { margin-bottom: 0; }
.fineprint { color: var(--muted); font-size: 0.78rem; line-height: 1.5; margin: 18px 0 0; }

/* ---- shared mini palette strip ---- */
.mini-strip { display: inline-flex; gap: 3px; }
.mini-sq {
  width: 20px;
  height: 20px;
  border-radius: 4px;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.12);
}

/* ---- Profile ---- */
.signedout { text-align: center; }
.signedout .auth-primary { max-width: 220px; margin: 6px auto 0; }

.profile-head { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.profile-name {
  font-family: "Frijole", var(--ui);
  font-size: 1.4rem;
  background: var(--grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  line-height: 1.4;
  padding-top: 3px;
}
.profile-points { font-family: var(--ui); font-size: 1.2rem; font-weight: 700; color: var(--text); }
.profile-points .unit { color: var(--muted); font-size: 0.78rem; font-weight: 500; }

.badge-grid { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 8px; }
.badge-pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: var(--glass);
  border: 1px solid var(--glass-line);
  border-left: 3px solid var(--muted);
  border-radius: 8px;
  padding: 6px 12px;
  font-family: var(--ui);
  font-size: 0.82rem;
  color: var(--text);
}
.badge-pill.cat-rgb { border-left-color: var(--tier-rare); }
.badge-pill.cat-hex { border-left-color: var(--tier-legendary); }
.badge-pill.cat-visual { border-left-color: var(--tier-epic); }
.bp-count {
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--muted);
  background: rgba(0, 0, 0, 0.3);
  border-radius: 999px;
  padding: 1px 7px;
}

.roll-list { list-style: none; margin: 0; padding: 0; }
.roll-row {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 10px 0;
  border-bottom: 1px solid var(--glass-line);
}
.roll-row:last-child { border-bottom: none; }
.roll-meta { display: flex; flex-direction: column; gap: 2px; margin-left: auto; text-align: right; }
.roll-tier { font-family: var(--ui); font-size: 0.8rem; font-weight: 700; text-transform: capitalize; color: var(--text); }
.roll-score { font-size: 0.75rem; color: var(--muted); }
.roll-time { font-size: 0.75rem; color: var(--muted); white-space: nowrap; cursor: help; }

/* ---- Leaderboard ---- */
.lb-tabs { display: flex; gap: 10px; margin: 0 0 14px; flex-wrap: wrap; }
.lb-tab {
  background: var(--btn);
  border: 1.5px solid var(--logo-gb);
  color: var(--logo-gb);
  font-family: var(--ui);
  font-weight: 700;
  font-size: 0.76rem;
  letter-spacing: 0.06em;
  padding: 8px 16px;
  border-radius: 10px;
  cursor: pointer;
  transition: color 0.16s, background 0.16s;
}
.lb-tab.active { background: var(--logo-gb); color: #070810; }
.lb-caption { color: var(--muted); font-size: 0.84rem; margin: 0 0 14px; }

.lb-table { display: flex; flex-direction: column; }
.lb-row {
  display: grid;
  grid-template-columns: 34px 1fr auto auto;
  align-items: center;
  gap: 12px;
  padding: 9px 4px;
  border-bottom: 1px solid var(--glass-line);
}
.lb-table.has-palette .lb-row {
  grid-template-columns: 34px minmax(80px, 1fr) auto auto auto;
}
.lb-row:last-child { border-bottom: none; }
.lb-head {
  font-family: var(--ui);
  font-size: 0.68rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}
.lb-rank { font-family: var(--ui); font-weight: 700; color: var(--muted); text-align: center; }
.lb-name { font-weight: 600; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lb-points { font-family: var(--ui); font-weight: 700; color: var(--text); text-align: right; }
.lb-time { font-size: 0.74rem; color: var(--muted); white-space: nowrap; cursor: help; text-align: right; }
.lb-pal { display: flex; }
.lb-row.top .lb-rank { color: var(--text); }
.lb-row.top-1 .lb-rank { color: var(--tier-legendary); }
.lb-row.top-2 .lb-rank { color: #cbd2e0; }
.lb-row.top-3 .lb-rank { color: #d89a5b; }

@media (max-width: 560px) {
  .lb-time, .lb-head span:last-child { display: none; }
  .lb-row { grid-template-columns: 30px 1fr auto; }
  .lb-table.has-palette .lb-row { grid-template-columns: 30px minmax(64px, 1fr) auto auto; }
  .mini-sq { width: 16px; height: 16px; }
}
