/* ── Reset ───────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

/* ── Animatable design tokens (CSS @property) ────────────── */
/* Registering --accent as <color> lets the browser interpolate it
   between theme changes — no JS animation needed. */
@property --accent {
  syntax: '<color>';
  inherits: true;
  initial-value: #1ed760;
}
@property --bg {
  syntax: '<color>';
  inherits: true;
  initial-value: #06080A;
}

/* ── Design tokens ───────────────────────────────────────── */
:root {
  --accent: var(--green);
  --accent-rgb: 30, 215, 96;
  --bg: #06080A;
  /* Semantic palette contract. ThemePalette replaces these atomically; all UI
     chrome should consume roles instead of light/dark literal colours. */
  --surface: #111314;
  --surface-alt: #16191a;
  --surface-raised: var(--surface-alt);
  --surface-subtle: #181b1c;
  --surface-strong: #202425;
  --control-bg: #1c2021;
  --input-bg: var(--control-bg);
  --hover-bg: #202425;
  --active-bg: #163521;
  --selection-bg: #17482a;
  --selection-text: #f0f3f1;
  --on-accent: #111111;
  --panel-rgb: 17, 19, 20;
  --panel-soft-rgb: 22, 25, 26;
  --panel-alpha: 0.94;
  --panel-soft-alpha: 0.92;
  --panel-border-alpha: 0.12;
  --panel-shadow-alpha: 0.22;
  --panel-highlight-alpha: 0.05;
  --panel: rgba(var(--panel-rgb), var(--panel-alpha));
  --panel-soft: rgba(var(--panel-soft-rgb), var(--panel-soft-alpha));
  --panel-border: rgba(255, 255, 255, var(--panel-border-alpha));
  --line: #222829;
  --border: var(--line);
  --divider: var(--line);
  --text: #f0f3f1;
  --muted-text: #a6b1ad;
  --dim-text: #7f8a86;
  /* Legacy aliases. Component CSS declares --text-dim/--text-muted with a hard
     literal fallback, but only the Comic skin ever defined them — so on every
     other skin those literals won: ~115 sites frozen to a hardcoded grey the
     palette engine could not touch, and unreadable on a light palette (#8b91a1
     on white is 3.15:1, under the 4.5:1 floor). Pointing them at the semantic
     roles hands them back to the palette. Comic keeps its own override.
     Caveat: an alias is substituted at computed-value time HERE, so it freezes
     to this element's --dim-text and does not track a DESCENDANT override of it
     (the light .media-panel island and .weather-panel both redefine --dim-text).
     No --text-dim user sits inside those today; if one ever does, re-declare the
     alias in that scope too. Same-element overrides (skins on :root) are fine. */
  --text-dim: var(--dim-text);
  --text-muted: var(--muted-text);
  --green: var(--accent);
  --bg-dim: 0.48;
  --bg-safe-dim: 0.48;
  --bg-safe-dim-strong: 0.60;
  --bg-blur: 0px;
  --bg-scale: 1;
  --floating-ui-bg: rgba(4, 8, 10, 0.34);
  --floating-ui-border: rgba(255, 255, 255, 0.18);
  --readability-shadow: 0 1px 3px rgba(0,0,0,0.86), 0 0 14px rgba(0,0,0,0.42);
  --icon-readability-filter: drop-shadow(0 1px 2px rgba(0,0,0,0.86)) drop-shadow(0 0 8px rgba(0,0,0,0.32));
  --color-success: #45d483;
  --color-warn: #f0b84f;
  --color-danger: #ff6268;
  --color-info: #62cbea;
  --success-rgb: 69, 212, 131;
  --warning-rgb: 240, 184, 79;
  --danger-rgb: 255, 98, 104;
  --info-rgb: 98, 203, 234;
  --success-bg: color-mix(in srgb, var(--color-success), transparent 86%);
  --warning-bg: color-mix(in srgb, var(--color-warn), transparent 86%);
  --danger-bg: color-mix(in srgb, var(--color-danger), transparent 86%);
  --info-bg: color-mix(in srgb, var(--color-info), transparent 86%);
  --red: var(--color-danger);
  --cyan: var(--color-info);
  --amber: var(--color-warn);
  --blue: var(--color-info);
  /* legacy radius (kept for existing components) */
  --radius: 8px;

  /* ── Redesign tokens (Phase 1) ───────────────────────────── */
  /* Radii */
  --radius-control: 10px;
  --radius-tile: 16px;
  --radius-modal: 20px;

  /* Spacing scale (4 / 8 base) */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;

  /* Shadows — two layers each: a tight contact shadow (definition) plus a
     soft, negative-spread ambient (depth without mud). */
  --shadow-sm: 0 1px 2px rgba(0,0,0,0.30);
  --shadow-md: 0 1px 2px rgba(0,0,0,0.22), 0 8px 22px -6px rgba(0,0,0,0.38);
  --shadow-lg: 0 2px 4px rgba(0,0,0,0.24), 0 18px 44px -12px rgba(0,0,0,0.50);
  --shadow-xl: 0 3px 6px rgba(0,0,0,0.26), 0 30px 70px -18px rgba(0,0,0,0.60);

  /* Panel finish: hairline top light + drop, split from --shadow-* so the
     50-tile dashboard keeps a lighter elevation than floating chrome.
     (Both overridden for light mode in LIGHT_ONLY_TOKENS.)
     The drop is DERIVED from --panel-shadow-alpha, which until now nothing in
     the codebase read: both the theme editor and the per-tile slider wrote it
     into a token no rule consumed, so neither control did anything. The same
     expression is re-declared per tile by applyTileStyle() — a custom property
     holding var() resolves where it is declared, so a tile that only set the
     alpha would keep inheriting the value :root already computed. */
  --panel-topline: rgba(255,255,255,0.055);
  --panel-drop: 0 1px 1px rgba(0,0,0, calc(var(--panel-shadow-alpha, 0.22) * 0.73)),
                0 10px 30px -14px rgba(0,0,0, calc(var(--panel-shadow-alpha, 0.22) * 2.05));

  /* Liquid Glass material — quieter milk, crisper edge, shorter sheen */
  --glass-bg: linear-gradient(135deg, rgba(255,255,255,0.12), rgba(255,255,255,0.03) 58%, rgba(255,255,255,0.055));
  --glass-border: rgba(255,255,255,0.22);
  --glass-blur: 22px;
  --glass-saturate: 160%;
  --glass-highlight: rgba(255,255,255,0.38);
  --glass-sheen: linear-gradient(180deg, rgba(255,255,255,0.14), transparent 30%);

  /* Deep OLED material */
  --oled-bg-rgb: 8, 10, 12;
  --oled-border: rgba(255,255,255,0.07);

  /* Semantic foregrounds for solid state fills. */
  --on-success: #111111;
  --on-warning: #111111;
  --on-danger: #ffffff;
  --on-info: #111111;

  /* Slider fill / track (theme-aware; overridden in light mode) */
  --slider-fill: #dfe7e3;
  --slider-track: #171c1c;

  /* Motion */
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-in: cubic-bezier(0.55, 0, 1, 0.45);
  --dur-fast: 160ms;
  --dur-mid: 240ms;
}

/* ── Base ────────────────────────────────────────────────── */
html,
body {
  width: 100%;
  height: 100%;
}

html {
  overflow: hidden;
  background: var(--bg);
}

body {
  min-height: 100dvh;
  overflow: hidden;
  position: relative;
  background: var(--bg);
  color: var(--text);
  /* --user-font-family is set by applyUiFont() when a custom typeface is uploaded;
     when unset it falls back to Inter, so the default look is unchanged. */
  font-family: var(--user-font-family, 'Inter'), 'Inter', 'Segoe UI', system-ui, sans-serif;
  user-select: none;
  -webkit-font-smoothing: antialiased;
}

button, input { font: inherit; }
button { -webkit-tap-highlight-color: transparent; }
/* Prevent horizontal range sliders from triggering the pager's CSS scroll-snap
   when swiped: touch-action:none hands all touch handling to the slider itself
   so the browser never interprets horizontal movement as a container scroll. */
input[type="range"] { touch-action: none; }

/* ── Page-level layout ───────────────────────────────────── */
.shell {
  width: 100%;
  height: 100%;
  padding: 18px;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);
  gap: 14px;
}
/* When in layout edit mode, add a dock row between topbar and pager. */
.layout-editing .shell {
  grid-template-rows: auto auto minmax(0, 1fr);
}

.dashboard {
  min-height: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 14px;
}

.panel {
  min-width: 0;
  min-height: 0;
  border: 1px solid var(--panel-border);
  border-radius: var(--radius-tile);
  /* Top-lit surface wash over the tinted panel colour: cards read as gently
     lit objects instead of flat translucent rectangles. The wash is white at
     ~3% so it disappears on light-mode white panels by itself.
     It also scales with --panel-alpha, so a panel asked to be fully transparent
     really does vanish: a fixed wash left a visible white sheet behind every
     "transparent" card, which is what made panelAlpha look broken at its floor. */
  background:
    linear-gradient(180deg, rgba(255,255,255, calc(0.028 * var(--panel-alpha, 1))), rgba(255,255,255,0) 42%),
    var(--panel);
  box-shadow: inset 0 1px 0 var(--panel-topline), var(--panel-drop);
}



/* ── Theme transition ────────────────────────────────────── */
/* Smooth animated color change when the user switches theme.
   Only --accent and --bg are registered as <color> via @property
   so only those two interpolate; other tokens update instantly. */
:root { transition: --accent 0.38s ease, --bg 0.38s ease; }

/* ── Tabular figures for data ────────────────────────────────
   Numbers in stats, volume, timers and the clock must not jitter. */
.clock-h, .clock-m,
.lock-time-h, .lock-time-m,
.stat-value, .bw-value, .stat-unit,
.vol-value, .mic-vol-value,
.weather-temp { font-variant-numeric: tabular-nums; }

/* ── Shared "live" ripple ────────────────────────────────────
   The expanding halo behind every live/on-air status dot (Discord, OBS,
   Streamer.bot, Twitch, YouTube). Each component paints its own ::after
   (selector + colour) and references this single keyframe. transform/opacity
   only, so it runs entirely on the compositor (GitHub #99). */
@keyframes live-ripple {
  0%, 100% { transform: scale(1); opacity: 1; }
  60% { transform: scale(2.4); opacity: 0; }
}

/* ── "Paste SVG code" dialog (shared by tile decor, background, Deck) ──
   Must sit above every editor it can open from — the Deck style/key modals and
   some Settings overlays reach z-index 4000, so this clears them. */
.svg-paste-overlay {
  position: fixed; inset: 0; z-index: 5000;
  display: flex; align-items: center; justify-content: center; padding: 20px;
  background: rgba(2, 5, 6, 0.42); backdrop-filter: blur(3px);
}
.svg-paste-box {
  width: min(560px, calc(100vw - 32px)); max-height: calc(100vh - 40px); overflow: auto;
  display: flex; flex-direction: column; gap: 10px; padding: 18px;
  border: 1px solid var(--panel-border); border-radius: 16px;
  background: rgba(10, 13, 14, 0.97); color: var(--text);
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.5);
}
.svg-paste-title { font-size: 15px; font-weight: 800; letter-spacing: 0.2px; }
.svg-paste-hint { font-size: 12.5px; line-height: 1.45; opacity: 0.7; }
.svg-paste-input {
  width: 100%; min-height: 180px; resize: vertical; padding: 10px 12px;
  border: 1px solid var(--panel-border); border-radius: 10px;
  background: rgba(255, 255, 255, 0.05); color: var(--text);
  font: 12.5px/1.5 ui-monospace, "SFMono-Regular", "Cascadia Code", Menlo, Consolas, monospace;
}
.svg-paste-input:focus { outline: none; border-color: var(--accent); }
.svg-paste-error { font-size: 12px; color: var(--color-danger); line-height: 1.4; }
.svg-paste-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 2px; }
.svg-paste-btn {
  padding: 8px 18px; cursor: pointer; border: 1px solid var(--panel-border);
  border-radius: 9px; background: rgba(255, 255, 255, 0.05); color: var(--text);
  font: inherit; font-weight: 700; font-size: 12.5px;
  transition: background 0.15s ease, transform 0.1s ease;
}
.svg-paste-btn:active { transform: scale(0.96); }
.svg-paste-btn.primary { border-color: transparent; background: var(--accent); color: var(--on-accent); }
