/* Pixelroom Studio - Broadcast Control shell.
   Dark studio surfaces, coral accent, Geist (brand/headlines + body) —
   matches the Svelte app shell's font system (app/src/lib/ui/base.css).
   Widget previews stay on dark OBS stages (--stage*), which is why those
   tokens stay dark. */

@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('/public/fonts/geist/GeistVF.woff2') format('woff2');
}

@property --ring {
  syntax: '<angle>';
  inherits: false;
  initial-value: 0deg;
}

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('/public/fonts/inter-latin-var.woff2') format('woff2');
}

:root {
  color-scheme: dark;

  /* Broadcast Control tokens - source of truth, mirrors public/editor/editor.css */
  --st-bg: #101418;
  --st-panel: #14181d;
  --st-chrome: #171c22;
  --st-line: #232a33;
  --st-stage: #0a0d10;
  --st-stage-2: #10161c;
  --st-text: #e8edf2;
  --st-text-dim: #8b96a3;
  /* Round 2 audit fix (polish): #6c7885 measured ~3.7-4.3:1 against this
     app's dark surfaces - below WCAG AA's 4.5:1 floor for normal-size text
     (stage-ruler/feature-stat/fd-arrow all use this token at 10-12px, well
     under the "large text" 3:1 exception). Reuses --st-text-dim's own
     value rather than inventing a third gray - comfortably AA-compliant
     (5.5-6:1) while staying in the same muted-label family. */
  --st-label: #8b96a3;
  --st-accent: #ff5c39;
  --st-accent-hover: #ff8266;
  --st-accent-deep: #b23417;
  --st-on-accent: #160905;
  --st-live: #ff3b30;
  --st-preview: #34c759;
  --st-select: #3d84f5;

  /* legacy aliases so every existing rule keeps resolving - now dark */
  --paper: var(--st-bg);
  --cream: var(--st-chrome);
  --card: var(--st-panel);
  --ink: var(--st-text);
  --ink-soft: #cdd6df;
  --muted: var(--st-text-dim);
  --line: var(--st-line);
  --shadow: rgba(0, 0, 0, 0.5);

  /* brand accent is coral now (was candy pink) */
  --pink: var(--st-accent);
  --pink-deep: var(--st-accent-hover);

  /* semantic accents, dark-tuned: draft/success/info/premium/error */
  --gold: #ffbf3d;
  --gold-soft: #ffd06a;
  --gold-ink: #ffd889;
  --teal: #2fd4c6;
  --teal-ink: #56d7e0;
  --mint: #4fae72;
  --mint-ink: #8fe3ab;
  --violet: #8a6ae4;
  --danger: #ff7b8a;
  --ok: #7be0a8;

  /* dark OBS stages (unchanged intent, retuned to the studio slate) */
  --stage: var(--st-stage);
  --stage-2: var(--st-stage-2);
  --stage-text: #e8edf2;

  /* legacy aliases so every component reads from one palette */
  --bg: var(--paper);
  --surface: var(--card);
  --surface-2: var(--cream);
  --text: var(--ink);
  --acc-1: var(--pink);
  --acc-2: var(--violet);
  --acc-3: var(--gold-ink);
  --grad: linear-gradient(120deg, var(--st-accent), var(--gold) 55%, var(--violet));
  --radius: 18px;
  --font-display: 'Geist', ui-sans-serif, system-ui, sans-serif;
  --font-body: 'Inter', ui-sans-serif, system-ui, -apple-system, sans-serif;
  --font-mono: 'SFMono-Regular', Consolas, 'Liberation Mono', ui-monospace, monospace;
}

* { box-sizing: border-box; }

html { height: 100%; }
body {
  margin: 0;
  min-height: 100%;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.55;
  display: flex;
  flex-direction: column;
}

a { color: var(--pink-deep); text-decoration: none; font-weight: 700; }
a:hover { text-decoration: underline; }
code { font-family: var(--font-mono); font-size: 0.86em; color: var(--ink-soft); background: var(--cream); border-radius: 5px; padding: 1px 5px; }

:focus-visible {
  outline: 2.5px solid var(--teal);
  outline-offset: 2px;
  border-radius: 6px;
}

/* Screen-reader-only: visually hidden but still announced, for aria-live
   regions and similar status text that has no on-screen home of its own. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* ---------- Top bar / footer ---------- */

.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 18px clamp(16px, 4vw, 48px);
  border-bottom: 2px solid var(--line);
  background: var(--paper);
}

.wordmark {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 20px;
  letter-spacing: 0.01em;
  color: var(--ink);
}
.wordmark:hover { text-decoration: none; }
.wordmark em { font-style: normal; color: var(--pink); }
.wordmark .pxlogo { display: block; }

.topnav { display: flex; align-items: center; gap: 20px; }
.topnav a { color: var(--muted); font-family: var(--font-display); font-weight: 700; font-size: 14.5px; }
/* nav items never break mid-word ("Log in" / long account emails); long
   account names truncate instead of widening the page */
.topnav a { white-space: nowrap; max-width: 38vw; overflow: hidden; text-overflow: ellipsis; }
.topnav a.active, .topnav a:hover { color: var(--ink); text-decoration: none; }
.inline-form { display: inline; }

/* Phone topbar: the Phase G nav (Editor · Library · Live · Account) is wider
   than a 390px viewport - wrap the nav under the wordmark instead of forcing
   the whole page into horizontal scroll. */
@media (max-width: 700px) {
  .topbar { flex-wrap: wrap; padding: 14px 16px; gap: 8px 16px; }
  .topnav { flex-wrap: wrap; gap: 6px 14px; row-gap: 4px; }
}

.main { flex: 1; width: 100%; max-width: 1240px; margin: 0 auto; padding: 0 clamp(16px, 4vw, 48px) 64px; }
/* the three-zone customizer needs the room */
.main:has(.customizer) { max-width: 1720px; }

.footer {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 24px;
  justify-content: space-between;
  padding: 20px clamp(16px, 4vw, 48px);
  border-top: 1px solid var(--line);
  color: var(--muted);
  font-size: 13px;
}

/* ---------- Type ---------- */

h1 {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(27px, 4.5vw, 46px);
  line-height: 1.08;
  letter-spacing: -0.01em;
  margin: 0 0 12px;
  color: var(--ink);
}

h2 { font-family: var(--font-display); font-weight: 700; font-size: 18px; margin: 0; }

.eyebrow {
  font-family: var(--font-display);
  font-size: 12px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--pink);
  margin: 0 0 10px;
}

/* ---------- Gallery ---------- */

.hero { padding: clamp(36px, 7vw, 72px) 0 36px; max-width: 640px; }
.hero-sub { color: var(--muted); font-size: 16px; margin: 14px 0 0; }

.library-search { max-width: 360px; margin: 0 0 20px; }

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 22px;
  padding-bottom: 24px;
}

.card {
  display: flex;
  flex-direction: column;
  background: var(--card);
  border: 2px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  color: var(--ink);
  box-shadow: 0 6px 18px -10px var(--shadow);
  transition: transform 0.18s ease, border-color 0.18s ease, box-shadow 0.18s ease;
}
/* Same class-vs-attribute specificity tie the .field[hidden] override
   further down this file exists for: .card's own `display: flex` otherwise
   beats the UA stylesheet's `[hidden]` rule, so public/gallery.js's search
   filter (card.hidden = true) would leave a filtered-out card fully laid out. */
.card[hidden] { display: none; }
.card:hover {
  text-decoration: none;
  transform: translateY(-4px);
  border-color: rgba(232, 237, 242, 0.4);
  box-shadow: 0 18px 34px -14px var(--shadow);
}

.card-stage {
  position: relative;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  background: repeating-conic-gradient(var(--stage) 0% 25%, var(--stage-2) 0% 50%);
  background-size: 22px 22px;
  border-bottom: 2px solid var(--line);
}
.card-stage iframe {
  position: absolute;
  top: 0; left: 0;
  width: 1920px; height: 1080px;
  border: 0;
  transform-origin: top left;
  /* gallery.js fits the canvas to the card, then zooms in on the widget */
  transform: scale(0.208);
  pointer-events: none;
  transition: transform 0.55s cubic-bezier(0.3, 0.9, 0.3, 1);
}
@media (prefers-reduced-motion: reduce) {
  .card-stage iframe { transition: none; }
}

.card-body { padding: 16px 18px 18px; display: flex; flex-direction: column; gap: 6px; }
.card-body p { margin: 0; color: var(--muted); font-size: 13.5px; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.card-cta {
  margin-top: 8px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 13.5px;
  color: var(--pink-deep);
}

.empty { color: var(--muted); }

/* Consolidated "nothing here yet" placeholder text: one shared look, one
   font size, instead of the three near-identical variants this replaces
   (.empty, the empty-state use of .url-help, .status-empty) had drifted
   into. Those three stay defined for pages that haven't switched over yet -
   this class is additive, not a replacement for them site-wide. */
.empty-state { margin: 0; color: var(--muted); font-size: 13px; }

.elements-section { margin-top: 8px; padding-bottom: 24px; }
.section-heading { margin: 0 0 16px; font-size: 22px; }
.grid-elements { grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); }

.chip {
  position: absolute;
  top: 10px; right: 10px;
  font-family: var(--font-display);
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  padding: 3px 10px;
  border-radius: 999px;
}
.chip-draft { background: var(--cream); color: var(--gold-ink); border: 1.5px solid var(--gold); position: static; }
.card-stage .chip-draft { position: absolute; }
.chip-live { background: rgba(121, 207, 145, 0.2); color: var(--mint-ink); border: 1.5px solid var(--mint); position: static; }
.chip-expired { background: rgba(107, 97, 120, 0.12); color: var(--muted); border: 1.5px solid var(--muted); position: static; }

/* ---------- Customizer ---------- */

.customizer {
  display: grid;
  /* three zones, zero page scroll: controls | preview + OBS URL | tabs */
  grid-template-columns: 330px minmax(0, 1fr) minmax(300px, 370px);
  grid-template-areas: 'panel preview side';
  gap: 24px;
  align-items: start;
  padding-top: 28px;
}
.panel { grid-area: panel; }
.preview-col { grid-area: preview; }
.side-col {
  grid-area: side;
  display: flex;
  flex-direction: column;
  gap: 12px;
  position: sticky;
  top: 16px;
  min-width: 0;
  max-height: calc(100vh - 130px);
}
/* commands are always visible and yield height to the connection tabs by
   scrolling internally when the screen is short */
.side-col > .commands-card {
  flex: 0 1 auto;
  min-height: 200px;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.side-col .side-tabs { flex-shrink: 0; }
.customizer.no-side {
  grid-template-columns: 330px minmax(0, 1fr);
  grid-template-areas: 'panel preview';
}


.panel {
  display: flex;
  flex-direction: column;
  gap: 14px;
  position: sticky;
  top: 16px;
  /* the panel scrolls internally instead of pushing the page taller;
     130px = topbar + page padding, so Save stays visible without scrolling */
  max-height: calc(100vh - 130px);
  overflow-y: auto;
  overscroll-behavior: contain;
  padding-right: 4px;
}
/* never SQUASH panel children to meet max-height (flex would shrink the
   collapsed groups to sub-text height before scrolling) - scroll instead */
.panel > * { flex-shrink: 0; }

/* Save is always within reach, no matter how far the panel is scrolled;
   the gradient fades clipped groups out instead of hard-cutting them */
.panel .save-actions {
  position: sticky;
  bottom: 0;
  z-index: 2;
  margin-top: -6px;
  padding: 26px 0 2px;
  background: linear-gradient(180deg, transparent, var(--bg) 55%);
}

.panel-head h1 { font-size: 25px; }
.panel-desc {
  color: var(--muted);
  font-size: 13.5px;
  margin: 4px 0 6px;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.group {
  background: var(--card);
  border: 2px solid var(--line);
  border-radius: 14px;
  overflow: hidden;
}

.group summary {
  list-style: none;
  cursor: pointer;
  padding: 12px 14px;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 13.5px;
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.group summary::-webkit-details-marker { display: none; }
.group summary::after {
  content: '';
  width: 7px; height: 7px;
  border-right: 2px solid var(--muted);
  border-bottom: 2px solid var(--muted);
  transform: rotate(45deg);
  transition: transform 0.15s ease;
}
.group[open] summary::after { transform: rotate(225deg); }
.group-fields { padding: 4px 14px 14px; display: flex; flex-direction: column; gap: 12px; }

.field { display: flex; flex-direction: column; gap: 6px; }
/* Design review Phase L: .field's own `display: flex` above otherwise beats
   the [hidden] attribute's UA-stylesheet `display: none` (same class-vs-
   attribute specificity tie resolved elsewhere in this file/editor.css/
   director.css for every other toggled panel/card/popover). Without this,
   automations.js's "+ New rule" and "+ New webhook" forms - whose conditional
   .field rows are toggled via `hidden` in automations/rules.js's
   updateTriggerFields()/updateActionFields() - rendered ALL fields at once
   regardless of the selected trigger/action type. */
.field[hidden] { display: none; }
.field label { font-size: 12.5px; font-weight: 700; color: var(--muted); }
/* Round 2 audit fix (blocker): dependsOn field-gating, ported from the
   editor Inspector's .ins-field-gated-off/.ins-field-gate-hint. */
.field.field-gated-off { opacity: 0.45; pointer-events: none; }
.field-gate-hint { margin: 0; color: var(--muted); font-size: 11px; font-style: italic; }

input[type="text"], input[type="number"], input[type="password"], input[type="email"], input[type="url"], select, textarea {
  width: 100%;
  border: 2px solid var(--line);
  border-radius: 10px;
  background: var(--card);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 13.5px;
  padding: 9px 11px;
}
input:focus, select:focus, textarea:focus { border-color: var(--teal); outline: none; }
select { appearance: none; background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%), linear-gradient(135deg, var(--muted) 50%, transparent 50%); background-position: calc(100% - 16px) 55%, calc(100% - 11px) 55%; background-size: 5px 5px; background-repeat: no-repeat; padding-right: 30px; }

.slider-row { display: flex; align-items: center; gap: 10px; }
.slider-row output {
  min-width: 42px;
  text-align: right;
  font-family: var(--font-mono);
  font-size: 12.5px;
  color: var(--ink-soft);
}

input[type="range"] {
  flex: 1;
  appearance: none;
  height: 5px;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--pink), var(--gold));
  outline-offset: 4px;
}
input[type="range"]::-webkit-slider-thumb {
  appearance: none;
  width: 17px; height: 17px;
  border-radius: 50%;
  background: var(--card);
  border: 2.5px solid var(--ink);
  box-shadow: 0 2px 4px var(--shadow);
  cursor: grab;
}
input[type="range"]::-moz-range-thumb {
  width: 13px; height: 13px;
  border-radius: 50%;
  background: var(--card);
  border: 2.5px solid var(--ink);
  cursor: grab;
}
input[type="range"]::-moz-range-track { height: 5px; border-radius: 999px; background: linear-gradient(90deg, var(--pink), var(--gold)); }

.color-row { display: flex; align-items: center; gap: 10px; }
input[type="color"] {
  width: 44px; height: 34px;
  padding: 0;
  border: 2px solid var(--line);
  border-radius: 9px;
  background: var(--card);
  cursor: pointer;
}
input[type="color"]::-webkit-color-swatch-wrapper { padding: 3px; }
input[type="color"]::-webkit-color-swatch { border: none; border-radius: 5px; }
input[type="color"]::-moz-color-swatch { border: none; border-radius: 5px; }
.color-code { font-size: 12px; color: var(--muted); font-family: var(--font-mono); }

.field-check { flex-direction: row; align-items: center; gap: 8px; }
.field-check input { width: auto; accent-color: var(--pink); }
.field-check label { color: var(--ink); font-size: 13.5px; font-weight: 600; }

/* buttons/url-bars carry their own display value, which would beat the
   hidden attribute (same trap as the draft banner) */
.twitch-connect-card [hidden], .kofi-card [hidden] { display: none !important; }

.twitch-connect-card .connect-status, .kofi-card .connect-status {
  font-size: 13px;
  font-weight: 700;
  color: var(--muted);
  margin: 10px 0 12px;
}
.twitch-connect-card .connect-status.connected, .kofi-card .connect-status.connected { color: var(--teal); }
.twitch-connect-card .connect-status.connected::before, .kofi-card .connect-status.connected::before { content: '● '; }
.kofi-card .url-bar { margin-bottom: 12px; }

/* setup switcher (multi-instance) */
.setup-bar {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  padding: 10px 12px;
  border: 2px solid var(--line);
  border-radius: 12px;
  background: var(--card);
}
.setup-bar label {
  font-size: 11.5px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 1px;
  color: var(--muted);
}
.setup-bar select { flex: 1; min-width: 120px; width: auto; padding: 6px 26px 6px 10px; font-size: 13px; }
.setup-bar .linklike { font-size: 12.5px; }
.setup-bar .setup-delete { color: var(--pink-deep); }

.role-chips { display: flex; flex-wrap: wrap; gap: 7px; }
.role-chip {
  border: 2px solid var(--line);
  border-radius: 999px;
  background: var(--card);
  color: var(--muted);
  font-family: var(--font-body);
  font-size: 12.5px;
  font-weight: 700;
  padding: 6px 13px;
  cursor: pointer;
  transition: transform 0.12s ease, border-color 0.12s ease, background 0.12s ease, color 0.12s ease;
}
.role-chip:hover { transform: translateY(-1px); border-color: var(--teal); color: var(--ink); }
.role-chip.active {
  background: linear-gradient(135deg, var(--pink), var(--pink-deep));
  border-color: transparent;
  color: #fff;
  box-shadow: 0 2px 8px rgba(255, 92, 57, 0.35);
}

.inline-action-row { display: flex; gap: 8px; }
.inline-action-row input { flex: 1; min-width: 0; }
.btn-apply {
  flex-shrink: 0;
  border: 2px solid var(--teal);
  border-radius: 999px;
  background: var(--card);
  color: var(--teal-ink);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 12.5px;
  padding: 0 14px;
  cursor: pointer;
  transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.btn-apply:hover { background: rgba(13, 184, 197, 0.1); }
.btn-apply.applied { border-color: var(--mint); color: var(--mint-ink); background: rgba(121, 207, 145, 0.15); }

.field-hint { margin: 6px 0 0; color: var(--muted); font-size: 11.5px; }
.field-hint.pending { color: var(--gold-ink); }
.field-hint.error { color: var(--danger); }

.save-actions { display: flex; flex-direction: column; gap: 6px; }
.save-actions[hidden] { display: none; }
.save-actions .field-hint { margin: 0; }

button:disabled { opacity: 0.55; cursor: default; }

.degraded-banner {
  margin: 0;
  padding: 10px 14px;
  border-radius: 12px;
  background: rgba(217, 58, 85, 0.07);
  border: 1.5px solid rgba(217, 58, 85, 0.35);
  color: var(--danger);
  font-size: 12.5px;
  line-height: 1.5;
}
.degraded-banner[hidden] { display: none; }

/* ---------- Preview stage: a dark OBS screen in a bright room ---------- */

.preview-col { display: flex; flex-direction: column; gap: 14px; min-width: 0; }

.stage-wrap { display: flex; flex-direction: column; gap: 8px; }

.stage {
  position: relative;
  /* fit the whole page on a laptop: below the stage sit the slim OBS URL
     row and the Live status card, the rest lives in the side column */
  width: min(100%, calc((100vh - 430px) * 1.7778));
  min-width: min(100%, 560px);
  margin: 0 auto;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius);
  border: 2px solid rgba(232, 237, 242, 0.28);
  overflow: hidden;
  background: repeating-conic-gradient(var(--stage) 0% 25%, var(--stage-2) 0% 50%);
  background-size: 26px 26px;
  box-shadow: 0 12px 28px -12px var(--shadow);
}
.stage iframe {
  position: absolute;
  top: 0; left: 0;
  width: 1920px;
  height: 1080px;
  border: 0;
  transform-origin: top left;
  /* actual transform set by JS: fit-widget zoom or full-canvas scale */
  transition: transform 0.4s cubic-bezier(0.3, 0.9, 0.3, 1);
}
/* move & resize needs the frame EXACTLY where the overlay math says it is -
   no lagging animation between the pink box and the widget */
.stage.layout-active iframe { transition: none; }
@media (prefers-reduced-motion: reduce) {
  .stage iframe { transition: none; }
}

.stage-meta {
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 6px 16px;
  font-family: var(--font-mono);
  font-size: 11.5px;
  color: var(--muted);
}
.stage-hint { color: var(--violet); }
/* mid-size screens: not enough width for three columns - the tab area moves
   below the preview (still no page scroll thanks to its internal scrolling) */
@media (max-width: 1180px) {
  .customizer {
    grid-template-columns: 330px minmax(0, 1fr);
    grid-template-areas: 'panel preview' 'panel side';
  }
  .side-col { position: static; }
  .side-tab-panel > .url-card { max-height: max(220px, calc(100vh - 640px)); }
  .stage { width: min(100%, calc((100vh - 545px) * 1.7778)); }
}


/* ---------- The signature: OBS URL bar ---------- */

.url-card {
  background: var(--card);
  border: 2px solid var(--line);
  border-radius: var(--radius);
  padding: 18px 20px 16px;
  box-shadow: 0 6px 18px -10px var(--shadow);
}
.url-label {
  margin: 0 0 10px;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 14.5px;
}

/* slim variant: label + one-line help side by side, bar below - saves the
   vertical space the old stacked card burned */
.url-card-slim { padding: 12px 16px 12px; }
.url-card-slim .url-row {
  display: flex;
  align-items: baseline;
  gap: 14px;
  flex-wrap: wrap;
  margin-bottom: 8px;
}
.url-card-slim .url-label { margin: 0; white-space: nowrap; }
.url-card-slim .url-help { margin: 0; font-size: 12px; }

/* tabbed side area below the preview: the active card scrolls internally,
   so the page itself never grows past the viewport */
.side-tabs { display: flex; flex-direction: column; gap: 10px; }
.side-tabs .tabs { flex-wrap: wrap; }
.side-tab-panel[hidden] { display: none; }
.side-tab-panel > .url-card {
  box-shadow: none;
  max-height: max(240px, calc(100vh - 620px));
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* live status sits in the center column under the OBS URL - Try-it clicks in
   the side column show their effect right here */
.preview-col .live-status { min-height: 120px; }
.preview-col .live-status .status-list { max-height: 26vh; overflow-y: auto; }

.url-bar {
  position: relative;
  display: flex;
  gap: 0;
  border-radius: 12px;
  padding: 2.5px;
  background: conic-gradient(from var(--ring), var(--pink), var(--gold), var(--violet), var(--pink));
  animation: ring-spin 6s linear infinite;
}
@keyframes ring-spin { to { --ring: 360deg; } }

.url-bar input {
  flex: 1;
  border: 0;
  border-radius: 9.5px 0 0 9.5px;
  background: var(--stage);
  font-family: var(--font-mono);
  font-size: 12.5px;
  color: var(--stage-text);
  padding: 11px 12px;
  min-width: 0;
}
.url-bar button {
  border: 0;
  border-radius: 0 9.5px 9.5px 0;
  background: var(--stage);
  color: var(--pink);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 13.5px;
  padding: 0 18px;
  cursor: pointer;
  border-left: 1px solid rgba(255, 250, 240, 0.18);
}
.url-bar button:hover { color: #fff; }
.url-bar button.copied { color: var(--mint); }

.url-help { margin: 10px 0 0; color: var(--muted); font-size: 12.5px; }
.url-help strong { color: var(--ink); font-weight: 800; }

.status-list {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.status-list dt { font-family: var(--font-mono); font-size: 11px; color: var(--muted); }
.status-list dd { margin: 2px 0 0; font-family: var(--font-mono); font-size: 13px; color: var(--ink-soft); }
.status-empty { margin: 0; color: var(--muted); font-size: 12.5px; }

.commands-list {
  list-style: none;
  margin: 0 0 12px;
  padding: 0;
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  column-gap: 16px;
  row-gap: 14px;
}
/* Each <li> contributes its 3 children as direct grid items, so code / desc /
   button line up in tidy columns across every row — a real list semantically,
   a table visually, no markup duplication needed. */
.command-row { display: contents; }
.command-row code {
  justify-self: start;
  background: var(--cream);
  border: 1.5px solid var(--line);
  color: var(--ink-soft);
  padding: 5px 10px;
  border-radius: 8px;
  font-size: 12.5px;
  white-space: nowrap;
}
.command-desc { color: var(--muted); font-size: 13px; }
.btn-try {
  justify-self: end;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  background: var(--card);
  border: 2px solid var(--teal);
  border-radius: 999px;
  color: var(--teal-ink);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 12px;
  padding: 5px 14px 5px 11px;
  cursor: pointer;
  white-space: nowrap;
  transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.btn-try::before {
  content: '';
  flex-shrink: 0;
  width: 0;
  height: 0;
  border-style: solid;
  border-width: 4px 0 4px 6px;
  border-color: transparent transparent transparent currentColor;
}
.btn-try:hover { background: rgba(13, 184, 197, 0.1); }
.btn-try.tried { border-color: var(--mint); color: var(--mint-ink); background: rgba(121, 207, 145, 0.15); }

@media (max-width: 640px) {
  .commands-list { grid-template-columns: 1fr; row-gap: 0; }
  .command-row {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 8px;
    padding: 12px 0;
    border-bottom: 1px solid var(--line);
  }
  .command-row:last-child { border-bottom: none; }
}

/* ---------- Buttons: candy pills like the main site ---------- */

/* Same class-vs-[hidden] trap as .field above: the `display: inline-flex`
   below otherwise beats the UA stylesheet's [hidden]{display:none}. Without
   this, e.g. the widget editor's Delete button (hidden on /widgets/new)
   rendered - and fired DELETE /api/widgets/null - on a brand-new widget. */
.btn-primary[hidden], .btn-ghost[hidden], .btn-danger[hidden], .btn-twitch[hidden] { display: none; }

.btn-primary, .btn-ghost, .btn-danger, .btn-twitch {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  border-radius: 999px;
  padding: 10px 20px;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 14px;
  cursor: pointer;
  border: 2px solid transparent;
  transition: transform 0.15s ease, box-shadow 0.15s ease, filter 0.15s ease;
}
.btn-primary {
  background: linear-gradient(180deg, var(--st-accent-hover), var(--pink));
  color: #fff;
  box-shadow: 0 3px 0 var(--st-accent-deep), 0 10px 20px -10px var(--shadow);
}
.btn-primary:hover { transform: translateY(-2px); text-decoration: none; box-shadow: 0 5px 0 var(--st-accent-deep), 0 14px 24px -10px var(--shadow); }
.btn-primary:active { transform: translateY(0); box-shadow: 0 2px 0 var(--st-accent-deep); }
.btn-ghost { background: var(--card); color: var(--ink); border-color: var(--line); }
.btn-ghost:hover { border-color: var(--ink); text-decoration: none; transform: translateY(-1px); }
.btn-danger { background: var(--card); color: var(--danger); border-color: rgba(217, 58, 85, 0.45); }
.btn-danger:hover { background: rgba(217, 58, 85, 0.07); }

.linklike {
  background: none;
  border: 0;
  padding: 0;
  color: var(--muted);
  font-family: inherit;
  font-size: inherit;
  cursor: pointer;
}
.linklike:hover { color: var(--ink); text-decoration: underline; }

/* ---------- Login ---------- */

.login-wrap { display: flex; justify-content: center; padding: 10vh 0 48px; }
.login-card {
  width: min(400px, 100%);
  background: var(--card);
  border: 2px solid var(--line);
  border-radius: var(--radius);
  padding: 30px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  box-shadow: 0 12px 28px -14px var(--shadow);
}
.login-card h1 { font-size: 27px; margin: 0; }
.login-card label { font-size: 12.5px; font-weight: 700; color: var(--muted); }

.form-error {
  margin: 0;
  color: var(--danger);
  font-size: 13px;
  background: rgba(217, 58, 85, 0.07);
  border: 1.5px solid rgba(217, 58, 85, 0.3);
  border-radius: 10px;
  padding: 8px 12px;
}

/* ---------- Admin list ---------- */

.admin-head {
  display: flex;
  align-items: end;
  justify-content: space-between;
  gap: 16px;
  padding: clamp(28px, 5vw, 48px) 0 20px;
}

.admin-table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  background: var(--card);
  border: 2px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  box-shadow: 0 6px 18px -10px var(--shadow);
}
.admin-table th, .admin-table td {
  text-align: left;
  padding: 12px 16px;
  border-bottom: 1px solid var(--line);
  font-size: 13.5px;
}
.admin-table th {
  font-family: var(--font-display);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--muted);
  background: var(--cream);
  font-weight: 800;
}
.admin-table tr:last-child td { border-bottom: 0; }
.row-name { color: var(--ink); font-weight: 800; }
.cell-date { color: var(--muted); font-family: var(--font-mono); font-size: 12px; }
.cell-actions { text-align: right; white-space: nowrap; }
.cell-actions a { margin-left: 14px; font-weight: 700; }

/* ---------- Admin editor ---------- */

.editor { padding-top: 28px; display: flex; flex-direction: column; gap: 22px; }

.editor-meta { display: flex; flex-direction: column; gap: 16px; }
.editor-meta h1 { font-size: 27px; }
.editor-meta-fields {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
  background: var(--card);
  border: 2px solid var(--line);
  border-radius: var(--radius);
  padding: 18px;
  box-shadow: 0 6px 18px -10px var(--shadow);
}
.field-wide { grid-column: 1 / -1; }

.editor-split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 22px;
  align-items: start;
}

.editor-code {
  background: var(--card);
  border: 2px solid var(--line);
  border-radius: var(--radius);
  padding: 14px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  box-shadow: 0 6px 18px -10px var(--shadow);
}

.tabs { display: flex; gap: 6px; }
.tab {
  background: transparent;
  border: 2px solid transparent;
  border-radius: 999px;
  color: var(--muted);
  font-family: var(--font-display);
  font-size: 12.5px;
  font-weight: 800;
  padding: 6px 15px;
  cursor: pointer;
}
.tab:hover { color: var(--ink); }
.tab.active { background: var(--cream); color: var(--ink); border-color: var(--violet); }

/* Code panes stay dark — a little editor screen, same as the OBS stages. */
textarea.code {
  display: none;
  width: 100%;
  height: 460px;
  resize: vertical;
  font-family: var(--font-mono);
  font-size: 12.5px;
  line-height: 1.6;
  background: var(--stage);
  color: var(--stage-text);
  border: 2px solid rgba(232, 237, 242, 0.28);
  border-radius: 12px;
  tab-size: 2;
  white-space: pre;
}
textarea.code:focus { border-color: var(--teal); }
textarea.code.active { display: block; }

.editor-hint { margin: 0; color: var(--muted); font-size: 12px; }
.editor-hint code { background: var(--cream); }

.editor-preview { display: flex; flex-direction: column; gap: 14px; position: sticky; top: 16px; }
.editor-actions { display: flex; gap: 10px; flex-wrap: wrap; }

.stage-editor { aspect-ratio: 16 / 9; }

/* ---------- Admin: list actions, draft banner, version history ---------- */

.admin-head-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.cell-actions .linklike { margin-left: 14px; font-weight: 700; font-size: inherit; }

.draft-banner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  flex-wrap: wrap;
  background: rgba(255, 191, 61, 0.12);
  border: 2px solid var(--gold);
  border-radius: var(--radius);
  padding: 12px 16px;
  font-size: 13.5px;
  color: var(--ink);
  font-weight: 600;
}
.draft-banner-actions { display: flex; align-items: center; gap: 14px; }

.history-panel {
  background: var(--card);
  border: 2px solid var(--line);
  border-radius: var(--radius);
  padding: 14px 16px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.history-head { display: flex; flex-direction: column; gap: 3px; }
.history-head strong { font-family: var(--font-display); font-size: 15px; }
.history-hint { color: var(--muted); font-size: 12px; }
.history-list {
  margin: 0;
  padding: 0;
  list-style: none;
  max-height: 260px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
}
.history-list li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 8px 2px;
  border-bottom: 1px solid var(--line);
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--muted);
}
.history-list li:last-child { border-bottom: 0; }
.history-empty { justify-content: flex-start; }
.btn-small { padding: 5px 14px; font-size: 12.5px; }

.draft-banner[hidden], .history-panel[hidden] { display: none; }

/* ---------- Accounts: login, dashboard, premium ---------- */

.btn-twitch {
  background: #9146ff;
  color: #fff;
  box-shadow: 0 3px 0 #5c2bb8, 0 10px 20px -10px var(--shadow);
}
.btn-twitch:hover { transform: translateY(-2px); text-decoration: none; box-shadow: 0 5px 0 #5c2bb8, 0 14px 24px -10px var(--shadow); }
.btn-twitch:active { transform: translateY(0); box-shadow: 0 2px 0 #5c2bb8; }

/* "Log in with Kick" - Kick green (#53fc18) with dark ink text for contrast,
   mirroring the pill shape/shadow language of .btn-twitch. */
.btn-kick {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  border-radius: 999px;
  padding: 10px 20px;
  font-family: var(--font-display);
  font-weight: 800;
  background: #53fc18;
  color: #0a1207;
  box-shadow: 0 3px 0 #2fa30a, 0 10px 20px -10px var(--shadow);
  transition: transform .12s ease, box-shadow .12s ease;
}
.btn-kick:hover { transform: translateY(-2px); text-decoration: none; box-shadow: 0 5px 0 #2fa30a, 0 14px 24px -10px var(--shadow); }
.btn-kick:active { transform: translateY(0); box-shadow: 0 2px 0 #2fa30a; }
.btn-kick[hidden] { display: none; }

.login-divider {
  display: flex;
  align-items: center;
  gap: 12px;
  color: var(--muted);
  font-size: 12px;
  font-weight: 700;
}
.login-divider::before, .login-divider::after {
  content: '';
  flex: 1;
  border-top: 2px solid var(--line);
}

.login-email-form { display: flex; flex-direction: column; gap: 8px; }

.form-notice {
  margin: 0;
  color: var(--mint-ink);
  font-size: 13px;
  background: rgba(121, 207, 145, 0.14);
  border: 1.5px solid rgba(121, 207, 145, 0.5);
  border-radius: 10px;
  padding: 8px 12px;
}

.me-grid {
  display: grid;
  grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
  gap: 26px;
  align-items: start;
  padding-bottom: 40px;
}
.me-col { display: flex; flex-direction: column; gap: 14px; }
.me-section-title { font-size: 19px; margin: 8px 0 0; }
.me-widget-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.me-unlocks { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.me-unlocks li {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 7px 0;
  border-bottom: 1px solid var(--line);
}
.me-unlocks li:last-child { border-bottom: 0; }
.me-date { color: var(--muted); font-family: var(--font-mono); font-size: 12px; }
.redeem-form { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; }
.redeem-msg.error { color: var(--danger); }
.redeem-msg.success { color: var(--mint-ink); }
.me-links { display: flex; flex-direction: column; gap: 10px; }
.me-link-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.me-link-row code { font-size: 12.5px; }

/* Task K4: soft "reconnect to enable alerts" hint (account.js) - gold like
   the pending chip, not the mint success tone .form-notice uses elsewhere,
   since this is a nudge rather than a confirmation. */
.reconnect-hint {
  margin: 0;
  color: var(--gold-ink);
  font-size: 12.5px;
  background: var(--cream);
  border: 1.5px solid var(--gold);
  border-radius: 10px;
  padding: 8px 12px;
}
.reconnect-hint a { color: inherit; font-weight: 800; text-decoration: underline; }

.chip-premium {
  background: var(--violet);
  color: #fff;
  border: 1.5px solid rgba(23, 17, 31, 0.35);
  position: absolute;
  top: 10px;
  left: 10px;
  right: auto;
  transform: rotate(-2.5deg);
}
/* Round 2 audit fix (blocker): a premium widget the viewer already owns -
   distinct from .chip-premium (still locked) and .chip-open (this widget
   is free for everyone), so all three states read differently at a glance. */
.chip-owned {
  background: var(--mint);
  color: #10231a;
  border: 1.5px solid rgba(23, 17, 31, 0.35);
  position: absolute;
  top: 10px;
  left: 10px;
  right: auto;
  transform: rotate(-2.5deg);
}
.chip-open { background: rgba(13, 184, 197, 0.12); color: var(--teal-ink); border: 1.5px solid var(--teal); position: static; }

.unlock-card { border-color: var(--violet); }
.unlock-actions { display: flex; align-items: flex-start; gap: 12px; flex-wrap: wrap; margin-top: 10px; }
.unlock-actions .redeem-form { margin-top: 0; flex: 1; min-width: 240px; }

.codes-generate { margin-bottom: 20px; }
.codes-form {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 2fr) 90px minmax(0, 2fr) auto;
  gap: 10px;
}
.codes-output {
  width: 100%;
  margin-top: 12px;
  font-family: var(--font-mono);
  font-size: 13px;
  line-height: 1.7;
  background: var(--cream);
  color: var(--ink);
  border: 2px solid var(--line);
  border-radius: 10px;
  padding: 10px 12px;
  resize: vertical;
}
.code-cell { font-size: 12px; letter-spacing: 0.4px; }
.code-redeemer { display: block; color: var(--muted); font-size: 11.5px; margin-top: 3px; }

/* ---------- Responsive ---------- */

@media (max-width: 960px) {
  .customizer, .customizer.no-side { grid-template-columns: 1fr; grid-template-areas: 'panel' 'preview' 'side'; }
  .overlay-editor { grid-template-columns: 1fr; grid-template-areas: 'panel' 'preview'; }
  .panel { position: static; max-height: none; overflow: visible; padding-right: 0; }
  .side-col { position: static; }
  .stage { width: 100%; min-width: 0; }
  .editor-split { grid-template-columns: 1fr; }
  .editor-preview { position: static; }
  .editor-meta-fields { grid-template-columns: 1fr; }
  .me-grid { grid-template-columns: 1fr; }
  .codes-form { grid-template-columns: 1fr 1fr; }
}

/* ---------- Motion ---------- */

@media (prefers-reduced-motion: reduce) {
  .url-bar { animation: none; }
  .card, .btn-primary, .btn-ghost, .btn-twitch, .btn-kick { transition: none; }
}

/* ---------- Broadcast Control details ---------- */

/* A thin coral "on air" strip along the very top - one deliberate accent,
   not the old rainbow candy awning (S6 design review: the drifting rainbow
   broke the dark studio look on every page). */
.topbar { position: relative; }
.topbar::before {
  content: '';
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 2px;
  background: var(--st-accent);
}

/* marker highlight behind a headline word, slightly tilted */
.marker { position: relative; display: inline-block; z-index: 0; }
.marker::after {
  content: '';
  position: absolute;
  z-index: -1;
  right: -0.08em;
  bottom: 0.03em;
  left: -0.08em;
  height: 0.42em;
  border-radius: 0.3em;
  background: linear-gradient(100deg, rgba(255, 92, 57, 0.45), rgba(127, 93, 226, 0.4));
  transform: rotate(-1.2deg);
}

/* floating shapes around the hero */
.hero { position: relative; max-width: none; }
.hero > .eyebrow, .hero > h1 { position: relative; z-index: 1; }
.hero-sub { max-width: 640px; position: relative; z-index: 1; }
/* Round 3 audit fix (minor): .hero-deco/.deco/.deco-star(2)/.deco-plus/
   .deco-dot/.deco-square (the floating hero decorations) and the float-a/
   float-b keyframes they alone used had zero references anywhere in any
   lib/views or public JS file - the current hero markup has no
   equivalent elements. Removed. */

/* sticker wall: cards sit slightly tilted, straighten on hover */
.grid .card:nth-child(odd) { transform: rotate(-0.7deg); }
.grid .card:nth-child(even) { transform: rotate(0.6deg); }
.grid .card:hover { transform: rotate(0deg) translateY(-5px); }
.card-stage .chip { transform: rotate(-2.5deg); }

/* shine stripe that sweeps across primary buttons on hover */
.btn-primary { position: relative; overflow: hidden; }
.btn-primary::before {
  content: '';
  position: absolute;
  top: 0; bottom: 0;
  left: -70%;
  width: 45%;
  background: linear-gradient(105deg, transparent, rgba(255, 255, 255, 0.4), transparent);
  transform: skewX(-16deg);
  pointer-events: none;
  transition: left 450ms ease;
}
.btn-primary:hover::before, .btn-primary:focus-visible::before { left: 125%; }

/* colored sticker dots in front of card titles and setting groups */
.url-label::before, .group summary::before {
  content: '';
  display: inline-block;
  width: 9px; height: 9px;
  border-radius: 3px;
  margin-right: 8px;
  transform: rotate(8deg);
  background: var(--gold);
}
.group summary { justify-content: flex-start; gap: 8px; }
.group summary::after { margin-left: auto; }
.group:nth-of-type(4n+1) summary::before { background: var(--pink); }
.group:nth-of-type(4n+2) summary::before { background: var(--gold); }
.group:nth-of-type(4n+3) summary::before { background: var(--teal); }
.group:nth-of-type(4n+0) summary::before { background: var(--violet); }
.preview-col .url-card:nth-of-type(4n+1) .url-label::before { background: var(--pink); }
.preview-col .url-card:nth-of-type(4n+2) .url-label::before { background: var(--teal); }
.preview-col .url-card:nth-of-type(4n+3) .url-label::before { background: var(--violet); }

@media (prefers-reduced-motion: reduce) {
  .btn-primary::before { transition: none; }
}

/* ---------- More fun: juice pack ---------- */

/* coral text selection */
::selection { background: rgba(255, 92, 57, 0.32); }

/* hero pops in, the marker draws itself */
.hero .eyebrow { animation: rise-in 0.45s cubic-bezier(0.34, 1.4, 0.64, 1) both; }
.hero h1 { animation: rise-in 0.5s cubic-bezier(0.34, 1.4, 0.64, 1) 0.08s both; }
.hero .hero-sub { animation: rise-in 0.5s cubic-bezier(0.34, 1.4, 0.64, 1) 0.16s both; }
@keyframes rise-in {
  from { opacity: 0; transform: translateY(14px); }
  to { opacity: 1; transform: translateY(0); }
}
.marker::after {
  transform-origin: left center;
  animation: marker-draw 0.5s ease-out 0.55s both;
}
@keyframes marker-draw {
  from { transform: scaleX(0) rotate(-1.2deg); }
  to { transform: scaleX(1) rotate(-1.2deg); }
}

/* squishy buttons */
.btn-primary:active, .btn-ghost:active, .btn-danger:active, .btn-twitch:active,
.btn-apply:active, .btn-try:active, .url-bar button:active { scale: 0.94; }
.btn-primary, .btn-ghost, .btn-danger, .btn-twitch, .btn-apply, .btn-try, .url-bar button {
  transition: transform 0.15s ease, box-shadow 0.15s ease, scale 0.12s ease,
    background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease, filter 0.15s ease;
}

/* playful wavy underline on link hover */
a:hover, .linklike:hover { text-decoration: underline wavy; text-decoration-thickness: 1.5px; text-underline-offset: 3px; }

/* gallery card: arrow scoots, chip wiggles, preview brightens */
.card-cta svg { transition: transform 0.18s cubic-bezier(0.34, 1.56, 0.64, 1); }
.card:hover .card-cta svg { transform: translateX(5px); }
.card:hover .chip { animation: wiggle 0.45s ease; }
.card-stage iframe { transition: filter 0.2s ease; }
.card:hover .card-stage iframe { filter: brightness(1.15); }
@keyframes wiggle {
  0%, 100% { rotate: 0deg; }
  25% { rotate: -5deg; }
  60% { rotate: 4deg; }
}
.card-stage .chip { animation: none; }
.card:hover .card-stage .chip { animation: wiggle 0.45s ease; }

/* settings group: sticker dot wiggles when the group opens */
.group[open] summary::before { animation: wiggle 0.5s ease; }
.group summary:hover::before { animation: wiggle 0.5s ease; }

/* candy scrollbars (webkit) */
::-webkit-scrollbar { width: 12px; height: 12px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: linear-gradient(180deg, var(--pink), var(--violet));
  border-radius: 999px;
  border: 3px solid var(--paper);
}
::-webkit-scrollbar-thumb:hover { background: linear-gradient(180deg, var(--pink-deep), var(--violet)); }

/* wordmark mark does a happy flip on hover */
.wordmark .pxlogo { transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); }
.wordmark:hover .pxlogo { transform: rotate(180deg); }

/* empty states get a little bounce dot trio */
.empty::after,
.empty-state::after {
  content: ' ● ● ●';
  letter-spacing: 3px;
  font-size: 8px;
  color: var(--pink);
  vertical-align: 2px;
}

@media (prefers-reduced-motion: reduce) {
  .topbar::before { animation: none; }
  .hero .eyebrow, .hero h1, .hero .hero-sub, .marker::after { animation: none; }
  .card:hover .chip, .group[open] summary::before, .group summary:hover::before { animation: none; }
  .wordmark .pxlogo { transition: none; }
}

/* Widget iframes must keep a light color-scheme: when the embedding page is
   dark and the embedded document isn't, browsers replace the iframe's
   transparency with an opaque white canvas - the checkerboard would vanish. */
.stage iframe, .card-stage iframe { color-scheme: light; }

/* wavy underline: don't skip descenders, keep the wave in one piece */
a:hover, .linklike:hover {
  text-decoration-skip-ink: none;
  text-decoration-thickness: 1.5px;
}

/* ---------- Move & resize overlay ---------- */

.stage.layout-active { overflow: visible; }
.layout-overlay {
  position: absolute;
  inset: 0;
  z-index: 6;
}
.layout-overlay[hidden] { display: none; }
.layout-box {
  position: absolute;
  border: 2px dashed var(--pink);
  border-radius: 6px;
  cursor: grab;
  touch-action: none;
  background: rgba(255, 92, 57, 0.06);
}
.layout-box:active { cursor: grabbing; }
.layout-handle {
  position: absolute;
  width: 15px;
  height: 15px;
  border-radius: 50%;
  background: var(--card);
  border: 2.5px solid var(--pink);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
  touch-action: none;
}
.layout-handle[data-handle="nw"] { top: -8px; left: -8px; cursor: nwse-resize; }
.layout-handle[data-handle="ne"] { top: -8px; right: -8px; cursor: nesw-resize; }
.layout-handle[data-handle="sw"] { bottom: -8px; left: -8px; cursor: nesw-resize; }
.layout-handle[data-handle="se"] { bottom: -8px; right: -8px; cursor: nwse-resize; }
.layout-scale {
  position: absolute;
  top: -30px;
  left: 50%;
  transform: translateX(-50%);
  background: var(--pink);
  color: #fff;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 11px;
  padding: 2px 10px;
  border-radius: 999px;
  white-space: nowrap;
  pointer-events: none;
}
.layout-actions { display: inline-flex; gap: 14px; }
.layout-reset { color: var(--pink-deep); }

/* double-buffered customizer preview: only the active frame is visible */
.stage .preview-frame { visibility: hidden; }
.stage .preview-frame.active-frame { visibility: visible; }

/* ---------- Overlays: list + canvas editor ---------- */

.overlay-card .url-help { margin: 6px 0 10px; }

/* Reuses the customizer's grid areas ('panel'/'preview' on `.panel`/
   `.preview-col`) so the sticky-scrolling panel and the dark stage need no
   new layout rules of their own - just a container that defines the areas. */
.overlay-editor {
  display: grid;
  grid-template-columns: 340px minmax(0, 1fr);
  grid-template-areas: 'panel preview';
  gap: 24px;
  align-items: start;
  padding: 28px 0 40px;
}

.overlay-member-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-height: 320px;
  overflow-y: auto;
}
.overlay-member-widget {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 13px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.overlay-member-setup { font-size: 11.5px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.inline-action-row select { flex: 1; min-width: 0; }

/* Inline field-edit panel (Stufe 5) for the selected member: reuses the
   customizer's own .field/.slider-row/.color-row/.field-check controls
   (public/base.css above), just stacked in a scrollable column instead of
   grouped sections. */
#field-panel-empty { margin: 0; }
.save-hint { margin: 0; font-size: 12px; font-weight: 700; color: var(--mint-ink); }
.save-hint[hidden] { display: none; }
.save-hint.error { color: var(--danger); }

/* ---------- Live dashboard (mobile-first control page) ---------- */

.live-intro { max-width: 720px; margin: 0 0 26px; }
.live-dashboard { display: flex; flex-direction: column; gap: 30px; max-width: 720px; }
.live-cards { display: flex; flex-direction: column; gap: 14px; }

/* Setup card: a <details> built on .url-card, but with its own summary/body
   padding (like .group) instead of the flat url-card padding, since the
   summary itself is the always-visible touch target. */
.setup-card { padding: 0; }
.setup-card summary {
  list-style: none;
  cursor: pointer;
  min-height: 44px;
  padding: 14px 20px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 14px;
}
.setup-card summary::-webkit-details-marker { display: none; }
.setup-card summary::after {
  content: '';
  flex-shrink: 0;
  width: 7px; height: 7px;
  border-right: 2px solid var(--muted);
  border-bottom: 2px solid var(--muted);
  transform: rotate(45deg);
  transition: transform 0.15s ease;
}
.setup-card[open] summary::after { transform: rotate(225deg); }
.setup-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.setup-overlay-badge {
  flex-shrink: 0;
  font-size: 11px;
  font-weight: 700;
  color: var(--muted);
  background: var(--cream);
  border: 1.5px solid var(--line);
  border-radius: 999px;
  padding: 3px 10px;
}
.setup-body { padding: 4px 20px 20px; display: flex; flex-direction: column; gap: 20px; }
.setup-section { display: flex; flex-direction: column; gap: 10px; }

.cmd-list { display: flex; flex-direction: column; gap: 12px; }
.cmd-row { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
.btn-cmd { min-height: 44px; min-width: 44px; }
.btn-cmd.cmd-sent { background: linear-gradient(180deg, var(--mint), #4fae72); }
.cmd-arg { flex: 1 1 90px; min-width: 70px; max-width: 160px; min-height: 44px; }
.cmd-desc { color: var(--muted); font-size: 12.5px; flex-basis: 100%; }
.cmd-note { margin: 0; color: var(--gold-ink); font-size: 12.5px; }
.cmd-note[hidden] { display: none; }

/* Task K4: alertbox test-alert buttons on a setup card (owner-only, only
   rendered for the seeded 'alertbox' widget). Same checkmark-flash feedback
   as .btn-cmd.cmd-sent above, kept as its own class since these buttons stay
   .btn-ghost/.btn-small rather than .btn-cmd (no argument input to align with). */
.test-event-row { display: flex; flex-wrap: wrap; gap: 8px; }
.test-event-btn.test-event-sent { background: linear-gradient(180deg, var(--mint), #4fae72); color: #fff; border-color: transparent; }
.test-event-error { margin-top: 4px; }

.field-row { display: flex; flex-direction: column; gap: 6px; }
.field-row label { font-size: 12.5px; font-weight: 700; color: var(--muted); }
.field-row .inline-action-row input { min-height: 44px; }
.save-field-btn { min-height: 44px; }

.live-member-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.live-member-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 8px 10px;
  border: 1.5px solid var(--line);
  border-radius: 10px;
}
.live-member-row.member-hidden { opacity: 0.55; }
.live-member-row.member-hidden .overlay-member-widget { color: var(--muted); }
.live-member-info { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.live-member-actions { display: flex; align-items: center; gap: 16px; flex-shrink: 0; }
.live-member-actions .linklike { display: inline-flex; align-items: center; min-height: 44px; font-size: 12.5px; }

/* Scene chips on the /live overlay card (Stufe 5 Task 5): a single self-
   contained touch target (44px, no separate rename/delete cluster) since
   applying is the only action this card offers. */
.live-scene-row { display: flex; flex-direction: column; gap: 8px; }
.live-scene-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.live-scene-chip {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 16px;
  border: 1.5px solid var(--line);
  border-radius: 999px;
  background: var(--card);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 12.5px;
  color: var(--ink);
  cursor: pointer;
}
.live-scene-chip:hover { border-color: var(--violet); }
.live-scene-chip.scene-chip-sent { background: rgba(140, 106, 255, 0.1); border-color: var(--violet); }
.live-scene-chip:disabled { opacity: 0.6; cursor: default; }
/* Szenen 2.0: the ACTIVE scene (what OBS shows) renders as a filled chip.
   The doubled .scene-chip-sent selector wins over the transient ✓ tint above
   so an active chip stays clearly filled while its checkmark shows. */
.live-scene-chip.live-scene-active,
.live-scene-chip.live-scene-active.scene-chip-sent {
  background: var(--violet);
  border-color: var(--violet);
  color: #fff;
}

@media (max-width: 640px) {
  .setup-card summary { padding: 14px 16px; }
  .setup-body { padding: 4px 16px 18px; }
}

/* ---------- Mod access / Activity / Shared with you (Stufe 4) ---------- */

.grant-card { display: flex; flex-direction: column; gap: 10px; }
.grant-who { margin: -4px 0 0; }
.grant-scope-list { margin: 0; padding-left: 20px; font-size: 12.5px; color: var(--muted); display: flex; flex-direction: column; gap: 3px; }
.grant-card .btn-danger { align-self: flex-start; min-height: 44px; }

/* "Invite a mod" form: setup checkboxes at 44px min touch height, with an
   indented nested list of per-command checkboxes that only shows once its
   setup is checked (see public/live.js .im-setup 'change' listener). */
.im-setups { display: flex; flex-direction: column; gap: 14px; }
.im-setup-row { display: flex; flex-direction: column; gap: 8px; }
.im-setup-label,
.im-cmd-row {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  font-size: 13.5px;
  cursor: pointer;
}
.im-setup-label { font-weight: 700; }
.im-setup-label input, .im-cmd-row input { width: 18px; height: 18px; flex-shrink: 0; }
.im-cmd-list { padding-left: 28px; display: flex; flex-direction: column; gap: 4px; }
.im-cmd-list[hidden] { display: none; }
.im-cmd-list .url-help { margin: 0 0 2px; }
.im-success { margin-top: 4px; }
.im-rename-hint { color: var(--gold-ink); }
/* public/live.js hides the rename warning for director/editor grants via
   the hidden attribute - explicit rule, same pattern as .im-cmd-list. */
.im-rename-hint[hidden] { display: none; }
/* Role invite (Phase I): the invite card's "Director/Editor access to X"
   statement, in place of the control invite's scope list. */
.invite-role-title {
  margin: 6px 0 2px;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 16px;
}

/* Control invite's "you will be able to use" scope list - same divided-rows
   reset as .me-unlocks (account.js) rather than raw browser bullets, since
   this is often the very first thing an invited mod ever sees of the app. */
.invite-scope-list {
  list-style: none;
  margin: 4px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}
.invite-scope-list li {
  padding: 8px 0;
  border-bottom: 1px solid var(--line);
  font-size: 14px;
}
.invite-scope-list li:last-child { border-bottom: 0; }

/* Two distinct lists share this reset (Mod-Zugriff Activity feed and the
   Stufe K Recent-events feed) - kept as separate class names on purpose so
   Playwright locators for one can never ambiguously match the other. */
.activity-list, .recent-events-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.activity-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  padding: 10px 14px;
  border: 1.5px solid var(--line);
  border-radius: 10px;
  font-size: 13px;
  background: var(--card);
}
.activity-main code { background: var(--cream); border-radius: 6px; padding: 1px 6px; font-size: 12px; }
.activity-time { color: var(--muted); font-size: 12px; flex-shrink: 0; }

.shared-grant-card { display: flex; flex-direction: column; gap: 14px; }
.shared-scope-list { gap: 10px; }
.mod-error { margin-top: 4px; }

@media (max-width: 640px) {
  .activity-row { flex-direction: column; align-items: flex-start; gap: 4px; }
}

/* ---------- Status page indicator dot ----------
   The landing page (Plan E2 Task 3) moved to its own fully self-contained
   inline-CSS markup (lib/views/landing.js) and no longer shares this
   stylesheet - the "Signal Path" section that used to live here was
   removed along with lib/views/home.js. This rule survives because
   lib/views/status.js still uses it (studio status palette: --st-live /
   --st-preview). */
.st-dot { display: inline-block; width: 10px; height: 10px; border-radius: 50%; margin-right: 7px; vertical-align: -1px; }
.st-dot.st-ok { background: var(--st-preview); }
.st-dot.st-bad { background: var(--st-live); }

/* ---------- Legal pages ---------- */

.legal-page { max-width: 680px; padding: clamp(28px, 5vw, 48px) 0 48px; }
.legal-page h1 { font-size: clamp(26px, 4vw, 36px); }
.legal-page h2 { font-size: 17px; margin: 26px 0 8px; }
.legal-page p, .legal-page ul { margin: 0 0 10px; color: var(--ink-soft); font-size: 14px; }
.legal-page ul { padding-left: 20px; display: flex; flex-direction: column; gap: 5px; }
.legal-intro { color: var(--muted); }

.footer-legal a { color: var(--muted); font-weight: 700; }
.footer-legal a:hover { color: var(--ink); }

/* ---------- Stats dashboard (Phase P) ---------- */

.stats-tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 14px;
}
.stats-tile { padding: 14px 18px 12px; }
.stats-tile-label {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 13px;
  color: var(--muted);
}
.stats-tile-value {
  margin: 2px 0 0;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(26px, 4vw, 34px);
  line-height: 1.15;
  color: var(--ink);
}
.stats-tile-note { margin: 0; font-size: 11.5px; color: var(--muted); }
/* Status page audit fix: when a poll fails, the numbers other than the
   service dot itself keep showing their last-known values with nothing to
   mark them as frozen - dim the tile so degraded connectivity visibly
   propagates instead of reading as a confidently-rendered live number. */
.stats-tile-stale { opacity: 0.55; transition: opacity 0.2s ease; }

.stats-chart-card { display: flex; flex-direction: column; gap: 8px; }
.stats-legend { display: flex; gap: 16px; }
.stats-legend-item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--muted);
}
.stats-swatch { width: 10px; height: 10px; border-radius: 3px; }
.stats-swatch-follow { background: var(--stats-follow); }
.stats-swatch-sub { background: var(--stats-sub); }
.stats-chart-svg { width: 100%; height: auto; display: block; }
.stats-gridline { stroke: var(--line); stroke-width: 1; }
.stats-baseline { stroke: var(--muted); stroke-width: 1; opacity: 0.55; }
.stats-axis-text { fill: var(--muted); font-family: var(--font-body); font-size: 10px; }
.stats-bar-follow { fill: var(--stats-follow); }
.stats-bar-sub { fill: var(--stats-sub); }

.stats-session-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}
.stats-session-when { margin: 0; }
.stats-session-duration {
  flex-shrink: 0;
  font-family: var(--font-display);
  font-size: 12px;
  font-weight: 800;
  color: var(--muted);
  background: var(--cream);
  border: 1.5px solid var(--line);
  border-radius: 999px;
  padding: 3px 10px;
}
.stats-session-grid {
  margin: 12px 0 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
  gap: 10px 14px;
}
.stats-session-stat dt { font-size: 11.5px; font-weight: 700; color: var(--muted); }
.stats-session-stat dd {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 17px;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.stats-session-card .url-help { margin: 10px 0 0; }

/* ---------- Kick connect card + Stream info panel (Phase N, Task N2) ---------- */

/* same class-vs-[hidden] trap as the twitch-connect-card above: the connect
   link carries .btn-primary's inline-flex, which would beat the attribute */
.kick-connect-card [hidden] { display: none !important; }
.kick-connect-card .connect-status {
  font-size: 13px;
  font-weight: 700;
  color: var(--muted);
  margin: 10px 0 12px;
}
.kick-connect-card .connect-status.connected { color: var(--mint-ink); }
/* Kick's brand green (#53fc18) stays a small accent (the connected dot),
   never a fill - house palette everywhere else. */
.kick-connect-card .connect-status.connected::before { content: '● '; color: #53fc18; }

.stream-info-rows { display: grid; gap: 14px; margin-top: 12px; }
.stream-info-row {
  border: 1.5px solid var(--cream);
  border-radius: 12px;
  padding: 12px 14px;
  display: grid;
  gap: 10px;
}
.stream-info-platform { margin: 0; }
.stream-info-row-kick { border-left: 3px solid #53fc18; padding-left: 10px; }
.stream-info-fields {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 10px;
}
.stream-info-field { display: flex; flex-direction: column; gap: 4px; }
.stream-info-field span { font-size: 12px; font-weight: 700; color: var(--muted); }
.stream-info-field input:disabled { opacity: 0.6; cursor: not-allowed; }
.stream-info-actions { display: flex; align-items: center; gap: 10px; }
.stream-info-status { margin: 0; font-size: 13px; font-weight: 700; color: var(--mint-ink); }
.stream-info-status.stream-info-error { color: var(--danger); }
.stream-info-locked { margin: 0; }

/* everything this panel toggles carries an explicit rule so a class with its
   own display value can never beat the hidden attribute */
.stream-info-status[hidden],
.stream-info-card [hidden] { display: none !important; }

/* ---------- Custom user widgets (Phase O, Task O2) ---------- */

/* Editor page (/widgets/new, /widgets/edit/:id) - reuses the admin editor's
   .editor-split/.tabs/textarea.code/.stage skeleton, plus its own head row. */
.we-head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 12px;
  flex-wrap: wrap;
}
.we-preview-empty {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  margin: 0;
  padding: 20px;
  text-align: center;
  color: var(--stage-text);
  font-size: 14px;
  font-weight: 700;
}
.we-preview-empty[hidden] { display: none; }

/* /live "My widgets": rows are built client-side (textContent only). */
.mw-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}
.mw-list:empty { display: none; }
.mw-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  padding: 10px 0;
  border-bottom: 1px dashed var(--line, rgba(0, 0, 0, 0.12));
}
.mw-row:last-child { border-bottom: 0; }
.mw-info {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.mw-desc {
  color: var(--muted);
  font-size: 12.5px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 46ch;
}
.mw-actions-row { display: flex; gap: 8px; flex-wrap: wrap; }

.mw-import-json {
  width: 100%;
  min-height: 140px;
  resize: vertical;
  font-family: var(--font-mono);
  font-size: 12px;
  line-height: 1.5;
  background: var(--stage);
  color: var(--stage-text);
  border: 2px solid var(--ink);
  border-radius: 12px;
  padding: 10px 12px;
}
.mw-import-json:focus { border-color: var(--teal); }
