/* desync design system.
   One source of truth for color, type, surfaces, boxes, and the sidebar shell.
   Pages should style their own content only — never the shell, never layout
   margins. Replaces navbar.css, stylesbeta.css, and per-page inline shells.

   ── Theming ──────────────────────────────────────────────────────────────
   The accent is one user-chosen colour, and everything tinted derives from it
   here in CSS rather than being recomputed per page. js/theme.js writes three
   numbers onto <html> — --acc-h, --acc-s, --acc-l (the hue, saturation and
   lightness of the chosen colour) — plus --acc-ink, whichever of black/white
   stays readable on top of it. Every gradient, glow, focus ring and active
   state below is built from those, so a new colour repaints the whole site
   without a stylesheet per theme. */

:root {
  /* Overwritten inline on <html> by js/theme.js before first paint. These are
     the fallback, and also what the site looks like with JS disabled. */
  --acc-h: 0;
  --acc-s: 0%;
  --acc-l: 100%;
  /* Dark, because the default accent is white: --acc-ink is the text colour
     that sits ON the accent (button labels, the active nav item). White here
     would be white-on-white with JS disabled. */
  --acc-ink: #05070d;
  /* The backdrop's own hue. Normally the accent's, but an achromatic accent
     (white is the default) falls back to the house blue so the aurora and the
     CSS streak keep their colour — see js/theme.js. */
  --bg-h: 222;
  --bg-s: 85%;

  --accent-color: hsl(var(--acc-h) var(--acc-s) var(--acc-l));
  --accent-hi: hsl(var(--acc-h) var(--acc-s) min(92%, calc(var(--acc-l) + 14%)));
  --accent-lo: hsl(var(--acc-h) var(--acc-s) max(12%, calc(var(--acc-l) - 18%)));
  --accent-soft: hsl(var(--acc-h) var(--acc-s) var(--acc-l) / 0.16);
  --accent-line: hsl(var(--acc-h) var(--acc-s) var(--acc-l) / 0.38);
  --accent-glow: hsl(var(--acc-h) var(--acc-s) var(--acc-l) / 0.28);
  --accent: linear-gradient(135deg, var(--accent-hi), var(--accent-lo));

  /* Pure black canvas. The only colour on the page is the streak at the top
     and whatever the accent touches. */
  --bg: #000000;
  /* Barely lifted off the canvas — on black, a panel is read from its border
     first and its fill second, so the fill only has to separate it from the
     backdrop, not stand on its own. */
  --surface: rgba(14, 16, 22, 0.66);
  --surface-raised: rgba(255, 255, 255, 0.045);
  --surface-hover: rgba(255, 255, 255, 0.085);
  --line: rgba(255, 255, 255, 0.11);
  --line-strong: rgba(255, 255, 255, 0.24);

  --text: #ffffff;
  --text-dim: rgba(255, 255, 255, 0.62);
  --text-faint: rgba(255, 255, 255, 0.4);

  /* Kept as aliases: pages and older stylesheets still reference these names,
     and they now follow the theme instead of being fixed greys. */
  --sky: var(--accent-color);
  --teal: var(--accent-hi);
  --violet: var(--accent-lo);
  --danger: #ff6b6b;

  /* Square by design. Every surface on the site is a hard-edged box — panels,
     buttons, inputs, boxes, the rail. The tokens are kept (rather than deleting
     every border-radius) so the whole shape language is one edit, and so the
     handful of things that are genuinely round — a toggle knob, a colour
     swatch, an avatar — can opt in explicitly instead of inheriting a curve. */
  --radius: 0px;
  --radius-sm: 0px;
  --radius-lg: 0px;
  --radius-pill: 0px;

  /* The rail floats, so the space the stage reserves is not the rail's own
     width: it is inset + width + gap. Keep these three in agreement.
       collapsed: 12 + 56  + 12 = 80
       open:      12 + 216 + 12 = 240 */
  --rail-inset: 12px;
  --rail-w: 56px;
  --rail-w-open: 216px;
  --rail: 80px;
  --rail-open: 240px;
  --gap: 16px;

  --font: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --shadow: 0 18px 60px rgba(0, 0, 0, 0.6);
}

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

/* The `hidden` attribute and `.hidden` class must always win. An element that
   sets its own display (flex/grid) otherwise overrides the UA `[hidden]` rule
   and never actually hides — which is why e.g. the music player and lyrics
   panel, both `display:*` + `hidden`, were stuck visible. !important settles it. */
[hidden] { display: none !important; }
.hidden { display: none !important; }

body {
  min-height: 100vh;
  font-family: var(--font);
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
}

/* ── Gradient backdrop ────────────────────────────────────── */
/* One faded smear across the top of the page, and black everywhere else.
   Deliberately not a full-viewport mesh: colour behind the whole page fought
   the content sitting on top of it, and the point of the gradient is to be a
   quiet accent, not a wash.
   Three overlapping ellipses read as a single streak rather than three blobs —
   an accent-coloured core, a warm tail opposite it on the wheel, and a low
   spread underneath tying them together.
   It lives on a fixed pseudo-element rather than on `body` so the blur costs
   nothing on scroll, and so `html.no-bg-grad` (the Settings toggle) can drop
   it outright. */
body::before {
  content: "";
  position: fixed;
  /* Anchored to the top and only a fraction of the viewport tall — the streak
     never reaches the middle of the page, let alone the bottom. */
  top: -12vh;
  left: -10vw;
  right: -10vw;
  height: 68vh;
  z-index: -2;
  pointer-events: none;
  /* A slight tilt is what makes it read as a sweep instead of a horizontal
     band. Rotating about the top-right keeps the lifted edge off screen. */
  transform: rotate(-7deg);
  transform-origin: 75% 0;
  background:
    radial-gradient(46vw 17vh at 60% 46%, hsl(var(--acc-h) var(--acc-s) 58% / 0.46), transparent 72%),
    radial-gradient(26vw 12vh at 85% 33%, hsl(calc(var(--acc-h) + 160) 72% 58% / 0.36), transparent 74%),
    radial-gradient(30vw 12vh at 38% 56%, hsl(var(--acc-h) var(--acc-s) 52% / 0.24), transparent 76%);
  filter: blur(52px) saturate(125%);
  /* Fades the streak out downwards. Without this its lower edge lands as a
     visible seam partway down the page. */
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 34%, transparent 96%);
  mask-image: linear-gradient(to bottom, #000 0%, #000 34%, transparent 96%);
}

html.no-bg-grad body::before { display: none; }

/* ── Aurora ───────────────────────────────────────────────── */
/* The animated backdrop (js/aurora.js, a port of React Bits' <Aurora />).
   Same job and same footprint as the CSS streak above: a band across the top
   that fades out downward. When the canvas mounts it sets .has-aurora on
   <html> and the CSS streak steps aside, so exactly one of the two is ever
   visible — the CSS one is the fallback for no-WebGL.

   The shader draws its brightest output at the top of its own container, so
   the element is anchored to the top of the viewport and given a fraction of
   its height rather than being full-screen. */
#aurora {
  position: fixed;
  /* Pulled up so the container's top edge sits off-screen. The shader's output
     is `intensity * rampColor` with no ceiling, and intensity peaks at the top
     of its own container — left at top:0 that hot edge clips toward white and
     grey-washes the top of the page. Hoisting it out of view keeps only the
     part of the ramp that still has colour in it. */
  top: -22vh;
  left: 0;
  right: 0;
  height: 82vh;
  z-index: -2;
  pointer-events: none;
  /* Faded on purpose. At full strength the shader is a light show; this keeps
     it a wash that content can sit on top of. */
  opacity: 0.34;
  /* Feathers the bottom edge, which would otherwise cut off as a hard line
     partway down the page. */
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 30%, transparent 100%);
  mask-image: linear-gradient(to bottom, #000 0%, #000 30%, transparent 100%);
}
#aurora canvas { display: block; width: 100%; height: 100%; }

html.has-aurora body::before { display: none; }
html.no-bg-grad #aurora { display: none; }

/* The animation is decoration. Anyone asking for reduced motion gets the
   static CSS streak instead. */
@media (prefers-reduced-motion: reduce) {
  #aurora { display: none; }
  html.has-aurora body::before { display: block; }
}

a { color: inherit; }

::selection { background: var(--accent-soft); color: var(--text); }

:focus-visible {
  outline: 2px solid var(--accent-color);
  outline-offset: 2px;
  border-radius: 0;
}

/* Ambient particle field, owned by js/particles.js. Sits behind everything;
   body's background propagates to the viewport canvas, so it still shows. */
#particles {
  position: fixed;
  inset: 0;
  z-index: -1;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

/* ── Shell ────────────────────────────────────────────────── */
/* The sidebar is fixed; the stage owns its own margin. Nothing else may
   set margins on the stage — that was the old ads.js !important bug. */

.ol-stage {
  margin-left: var(--rail);
  padding: 28px clamp(16px, 3vw, 32px) 96px;
  transition: margin-left 0.18s ease;
}
body.rail-open .ol-stage { margin-left: var(--rail-open); }

/* The rail floats clear of the edges and carries no surface of its own —
   each .ol-group is a separate pill. That is what makes it read as a rail
   rather than a wall, and it lets Home sit apart from the rest of the nav. */
.ol-sidebar {
  position: fixed;
  inset: var(--rail-inset) auto var(--rail-inset) var(--rail-inset);
  z-index: 100;
  width: var(--rail-w);
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 10px;
  transition: width 0.18s ease;
}
body.rail-open .ol-sidebar { width: var(--rail-w-open); }

.ol-group {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 6px;
  background: rgba(8, 10, 15, 0.5);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  overflow: hidden;
}

/* The rail is one pill that fills the sidebar's height; the .ol-spacer inside
   pushes account/settings and panic to the bottom. */
.ol-rail { flex: 1; min-height: 0; }

/* Brand header at the top of the rail: logo mark over the wordmark. Links home. */
.ol-brand {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 6px 4px 10px;
  margin-bottom: 2px;
  border-bottom: 1px solid var(--line);
  text-decoration: none;
  color: var(--text);
  overflow: hidden;
}
.ol-brand-logo {
  width: 100%;
  max-width: 150px;
  height: auto;
  object-fit: contain;
  display: block;
}
.ol-brand-name {
  display: none;
  font-size: 17px;
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1;
}
body.rail-open .ol-brand-name { display: block; }

.ol-link,
.ol-toggle {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 44px;
  /* Collapsed, the padding must match the icon's own inset so a 20px icon
     lands dead centre of the 44px square. */
  padding: 0 12px;
  border: 0;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--text-dim);
  font: inherit;
  font-size: 14px;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background 0.14s ease, color 0.14s ease;
}
.ol-link svg,
.ol-toggle svg { width: 20px; height: 20px; flex-shrink: 0; }
.ol-link:hover,
.ol-toggle:hover { background: var(--surface-hover); color: var(--text); }

.ol-link.active {
  background: var(--accent);
  color: var(--acc-ink);
  font-weight: 600;
  box-shadow: 0 6px 20px var(--accent-glow);
}
.ol-link.active:hover { background: var(--accent); filter: brightness(1.06); }

.ol-panic { color: var(--text-faint); }
.ol-panic:hover { background: rgba(255, 255, 255, 0.1); color: var(--text); }

/* Labels collapse to zero width on the rail, so icons stay centred. */
.ol-label {
  opacity: 0;
  max-width: 0;
  overflow: hidden;
  transition: opacity 0.16s ease, max-width 0.18s ease;
}
body.rail-open .ol-label { opacity: 1; max-width: 150px; }

.ol-spacer { flex: 1; min-height: 12px; }

/* Chevron points the way the click goes: right to expand, left to collapse. */
.ol-toggle svg {
  transform: rotate(180deg);
  transition: transform 0.18s ease;
}
body.rail-open .ol-toggle svg { transform: rotate(0deg); }

/* ── Surfaces ─────────────────────────────────────────────── */

.ol-panel {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  box-shadow: var(--shadow);
}
.ol-h1 { font-size: clamp(28px, 4.4vw, 44px); line-height: 1.04; letter-spacing: -0.03em; }
.ol-h2 { font-size: 18px; letter-spacing: -0.01em; }
.ol-sub { color: var(--text-dim); font-size: 14px; line-height: 1.6; }

/* ── Boxes ────────────────────────────────────────────────── */
/* The boxed layout the whole site now uses: a titled container whose contents
   are rows divided by hairlines. Anything that is "a group of related
   settings, facts or controls" should be one of these rather than inventing a
   card per page. */

.ol-box {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  overflow: hidden;
}

/* Small-caps eyebrow at the top of a box, as in "TAB COVER". Sits inside the
   box's own padding so it reads as a heading for the rows beneath it. */
.ol-box-head {
  padding: 16px 18px 12px;
  border-bottom: 1px solid var(--line);
}
.ol-box-eyebrow {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--text-faint);
}
.ol-box-title {
  margin-top: 6px;
  font-size: 16px;
  font-weight: 600;
  letter-spacing: -0.01em;
}
.ol-box-desc {
  margin-top: 4px;
  font-size: 13px;
  line-height: 1.55;
  color: var(--text-dim);
}
.ol-box-body { padding: 6px 18px 14px; }

/* One setting. Label and description on the left, the control on the right;
   they stack on narrow screens rather than crushing the control. */
.ol-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  padding: 14px 0;
  border-bottom: 1px solid var(--line);
}
.ol-row:last-child { border-bottom: 0; }
.ol-row-main { min-width: 0; flex: 1; }
.ol-row-label { font-size: 14px; font-weight: 500; }
.ol-row-desc {
  margin-top: 3px;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--text-faint);
}
.ol-row-control { flex-shrink: 0; display: flex; align-items: center; gap: 8px; }

/* A row whose control needs the full width beneath the label (a text field, a
   swatch grid) rather than sitting beside it. */
.ol-row-stack {
  display: block;
  padding: 14px 0;
  border-bottom: 1px solid var(--line);
}
.ol-row-stack:last-child { border-bottom: 0; }
.ol-row-stack .ol-row-control { margin-top: 10px; display: block; }

@media (max-width: 560px) {
  .ol-row { flex-direction: column; align-items: stretch; }
  .ol-row-control { width: 100%; }
  .ol-row-control > select,
  .ol-row-control > .ol-input { width: 100%; }
}

/* ── Controls ─────────────────────────────────────────────── */

.ol-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 40px;
  padding: 0 16px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface-raised);
  color: var(--text);
  font: inherit;
  font-size: 14px;
  cursor: pointer;
  text-decoration: none;
  transition: background 0.14s ease, border-color 0.14s ease, transform 0.14s ease;
}
.ol-btn:hover { background: var(--surface-hover); border-color: var(--line-strong); }
.ol-btn:active { transform: translateY(1px); }
.ol-btn-primary {
  background: var(--accent);
  border-color: transparent;
  color: var(--acc-ink);
  font-weight: 600;
  box-shadow: 0 8px 24px var(--accent-glow);
}
.ol-btn-primary:hover { filter: brightness(1.08); background: var(--accent); }
.ol-btn-danger { color: #ffb4b4; border-color: rgba(255, 107, 107, 0.4); background: rgba(255, 107, 107, 0.08); }
.ol-btn-danger:hover { background: rgba(255, 107, 107, 0.16); border-color: rgba(255, 107, 107, 0.62); }
.ol-btn-sm { min-height: 30px; padding: 0 10px; font-size: 12px; }

.ol-input {
  width: 100%;
  min-height: 42px;
  padding: 0 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: rgba(0, 0, 0, 0.32);
  color: var(--text);
  font: inherit;
  font-size: 14px;
  outline: none;
  transition: border-color 0.14s ease, background 0.14s ease, box-shadow 0.14s ease;
}
.ol-input::placeholder { color: var(--text-faint); }
.ol-input:focus {
  border-color: var(--accent-line);
  background: rgba(0, 0, 0, 0.5);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

/* Native select, restyled to match .ol-input. The arrow is an inline SVG data
   URI so it inherits nothing from the OS widget. */
.ol-select {
  min-width: 190px;
  min-height: 40px;
  padding: 0 34px 0 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background-color: rgba(0, 0, 0, 0.42);
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23ffffff' stroke-opacity='.65' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 10px center;
  background-size: 16px;
  color: var(--text);
  font: inherit;
  font-size: 14px;
  cursor: pointer;
  outline: none;
  appearance: none;
  -webkit-appearance: none;
  transition: border-color 0.14s ease, box-shadow 0.14s ease;
}
.ol-select:focus { border-color: var(--accent-line); box-shadow: 0 0 0 3px var(--accent-soft); }
.ol-select option { background: #0b0d14; color: var(--text); }

/* Toggle switch. Off is a grey track; on fills with the accent. */
.ol-switch {
  position: relative;
  display: inline-block;
  flex-shrink: 0;
  width: 46px;
  height: 26px;
}
.ol-switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.ol-switch-track {
  position: absolute;
  inset: 0;
  border: 1px solid var(--line);
  /* Deliberately still a capsule — see the note on the radius tokens. */
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.08);
  cursor: pointer;
  transition: background 0.16s ease, border-color 0.16s ease;
}
.ol-switch-track::after {
  content: "";
  position: absolute;
  top: 3px;
  left: 3px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: #fff;
  transition: transform 0.16s ease;
}
.ol-switch input:checked + .ol-switch-track {
  background: var(--accent);
  border-color: transparent;
}
.ol-switch input:checked + .ol-switch-track::after { transform: translateX(20px); }
.ol-switch input:focus-visible + .ol-switch-track { box-shadow: 0 0 0 3px var(--accent-soft); }

/* ── Legal links, home only ───────────────────────────────── */

.ol-legal {
  position: fixed;
  right: 16px;
  bottom: 16px;
  z-index: 60;
  display: flex;
  gap: 8px;
}
.ol-legal a {
  padding: 7px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  background: rgba(8, 10, 15, 0.78);
  backdrop-filter: blur(14px);
  color: var(--text-dim);
  font-size: 12px;
  text-decoration: none;
  transition: color 0.14s ease, border-color 0.14s ease;
}
.ol-legal a:hover { color: var(--text); border-color: var(--accent-line); }

/* ── Page headers ─────────────────────────────────────────── */

.ol-page-head { margin-bottom: 22px; }
.ol-page-title {
  font-size: clamp(24px, 3.4vw, 32px);
  font-weight: 700;
  letter-spacing: -0.03em;
}
.ol-page-sub { margin-top: 6px; color: var(--text-dim); font-size: 14px; }

/* ── Toast ────────────────────────────────────────────────── */

.toast {
  position: fixed;
  left: 50%;
  bottom: 28px;
  z-index: 3000;
  transform: translate(-50%, 20px);
  padding: 11px 18px;
  border: 1px solid var(--accent-line);
  border-radius: var(--radius-pill);
  background: rgba(8, 10, 15, 0.94);
  backdrop-filter: blur(14px);
  color: var(--text);
  font-size: 13.5px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease, transform 0.2s ease;
}
.toast.show { opacity: 1; transform: translate(-50%, 0); }

/* ── Responsive: rail becomes a bottom bar ────────────────── */

/* One floating bar along the bottom. The groups stop being pills and hand
   their children straight to the bar, which becomes the single surface. */
@media (max-width: 820px) {
  .ol-sidebar {
    inset: auto var(--rail-inset) var(--rail-inset) var(--rail-inset);
    width: auto !important;
    flex-direction: row;
    align-items: center;
    gap: 4px;
    padding: 6px;
    /* Over scrolling content on mobile, so a near-black fill (not transparent)
       keeps it readable, with the same outline as the desktop rail. */
    background: rgba(5, 6, 10, 0.86);
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    backdrop-filter: blur(20px);
    -webkit-backdrop-filter: blur(20px);
    box-shadow: 0 10px 34px rgba(0, 0, 0, 0.5);
  }
  .ol-group { display: contents; }
  .ol-spacer, .ol-toggle, .ol-brand { display: none; }
  .ol-link { flex: 1; justify-content: center; padding: 0; min-height: 46px; }
  .ol-label { display: none; }
  .ol-stage, body.rail-open .ol-stage { margin-left: 0; padding-bottom: 92px; }
  .ol-legal { bottom: 84px; right: 12px; }
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
}
