:root {
  --bg: #0e0f13;
  --panel: #16181e;
  --raise: #1d2028;
  /* The hover partner of --raise, for a soft control that lifts under the pointer. */
  --raise-2: #242834;
  --line: #262932;
  --line-2: #323644;
  /* --line-3 is the border a hoverable surface raises to; it was the literal #454a5a in
     four places, which is four chances to drift apart. */
  --line-3: #454a5a;
  --text: #eceef3;
  --muted: #9aa1b0;
  /* Dimmer than --muted on purpose: placeholder text is an example, not content. */
  --faint: #6c7383;
  /* The violet ramp, all one hue (252°) at full saturation, stepped by lightness.
     --accent and --accent-press are the two that ever sit *under* white text, so both
     clear 4.5:1 against --accent-ink: 5.0:1 and 4.6:1. The old pair (#7c5cff / #9076ff)
     read 4.3:1 and 3.4:1, and hover made the label harder to read than rest did.
     --accent-hi stays the bright end of the same ramp and is only ever ink or a ring,
     where it has 5.7:1 on --bg and 5.3:1 on --panel. */
  --accent: #704dff;
  --accent-press: #7857ff;
  --accent-hi: #9076ff;
  --accent-ink: #ffffff;
  --danger: #ff5d6c;
  --danger-press: #ff7884;
  --danger-ink: #1a0b0e;
  /* The soft end of the danger family, for warning prose rather than a filled control. */
  --warn-text: #ff9aa4;
  --ok: #46d19a;
  /* Work in progress: the saving dot, and nothing else. */
  --busy: #f6c55b;
  /* The tally light: the one colour that means "this is what OBS is showing". */
  --live: #ff4f64;
  --r: 10px;
  --r-sm: 7px;
  --ease: cubic-bezier(0.2, 0.8, 0.2, 1);
  color-scheme: dark;
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }
body {
  margin: 0; min-height: 100vh; background: var(--bg); color: var(--text);
  font: 400 15px/1.5 system-ui, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
  -webkit-font-smoothing: antialiased; font-variant-numeric: tabular-nums;
}
::selection { background: var(--accent); color: var(--accent-ink); }
input, select, button { font: inherit; color: inherit; }
button { cursor: pointer; }
:focus-visible { outline: 2px solid var(--accent-hi); outline-offset: 2px; }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.i { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; flex: none; }
* { scrollbar-width: thin; scrollbar-color: var(--line-2) transparent; }

/* Top bar */
.top { display: flex; align-items: center; justify-content: space-between; gap: 16px; height: 64px; padding: 0 24px; border-bottom: 1px solid var(--line); }
.logo { display: inline-flex; align-items: center; gap: 10px; color: var(--text); text-decoration: none; font-weight: 800; font-size: 18px; letter-spacing: -0.01em; }
.logo svg { width: 28px; height: 28px; }
.top-actions { display: flex; align-items: center; gap: 8px; }
.select select {
  appearance: none; height: 38px; min-width: 180px; max-width: 260px; padding: 0 34px 0 12px; border-radius: var(--r-sm);
  border: 1px solid var(--line-2); background: var(--panel) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M6 8l4 4 4-4' fill='none' stroke='%239aa1b0' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E") right 10px center / 16px no-repeat;
}

/* Buttons */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px; height: 38px; padding: 0 14px;
  border-radius: var(--r-sm); border: 1px solid transparent; font-weight: 600; white-space: nowrap; text-decoration: none;
  transition: background-color 150ms var(--ease), border-color 150ms var(--ease), color 150ms var(--ease), transform 100ms var(--ease);
}
.btn:active:not(:disabled) { transform: scale(0.98); }
.btn:disabled { opacity: 0.4; cursor: not-allowed; }
.btn-primary { background: var(--accent); color: var(--accent-ink); }
.btn-primary:hover { background: var(--accent-press); }
.btn-ghost { border-color: var(--line-2); background: transparent; }
.btn-ghost:hover { background: var(--raise); }
.btn-soft { background: var(--raise); border-color: var(--line-2); }
.btn-soft:hover:not(:disabled) { border-color: var(--line-3); background: var(--raise-2); }
.icon-only { width: 38px; padding: 0; }

/* Layout */
/* Two panes that fill the window: the settings scroll on their own, the wheel stays in view.
   A long tab or a 100-slice list never pushes the preview or the Spin button off screen. */
.app { display: grid; grid-template-columns: 420px minmax(0, 1fr); grid-template-rows: minmax(0, 1fr); height: calc(100dvh - 64px); min-height: 560px; }
.panel { position: relative; min-height: 0; overflow-y: auto; overscroll-behavior: contain; border-right: 1px solid var(--line); background: var(--panel); padding: 20px 20px 32px; }
.preview { display: grid; grid-template-rows: minmax(0, 1fr) auto; gap: 20px; padding: 24px 32px 28px; min-width: 0; min-height: 0; }

/* Title */
.title-row { display: flex; align-items: center; gap: 12px; }
.title-input {
  flex: 1; min-width: 0; height: 44px; padding: 0 12px; margin-left: -12px; border: 1px solid transparent; border-radius: var(--r-sm);
  background: transparent; font-size: 22px; font-weight: 800; letter-spacing: -0.01em; text-overflow: ellipsis;
}
.title-input:hover { border-color: var(--line); }
.title-input:focus-visible { outline: none; border-color: var(--accent); background: var(--bg); }
.save { font-size: 13px; color: var(--muted); display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.save::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--ok); }
.save[data-tone="busy"]::before { background: var(--busy); }
.save[data-tone="error"] { color: var(--danger); }
.save[data-tone="error"]::before { background: var(--danger); }
.save[data-tone="view"]::before { background: var(--muted); }

/* Tabs. Five of them in a 420px panel: each column is as wide as its own word, with the
   spare room shared out, so "Chat entry" never runs up against "Style". */
.tabs { display: grid; grid-template-columns: repeat(5, auto); gap: 2px; margin: 16px 0 20px; padding: 4px; border-radius: var(--r); background: var(--bg); }
.tabs button { height: 34px; min-width: 0; padding: 0 6px; border: 0; border-radius: var(--r-sm); background: transparent; color: var(--muted); font-size: 13px; font-weight: 600; white-space: nowrap; transition: background-color 150ms var(--ease), color 150ms var(--ease); }
.tabs button:hover { color: var(--text); }
.tabs button[aria-selected="true"] { background: var(--raise); color: var(--text); box-shadow: inset 0 0 0 1px var(--line-2); }

/* Groups */
.pane { display: grid; gap: 22px; }
.group { display: grid; gap: 10px; }
.group.two { grid-template-columns: 1fr 1fr; }
.field { display: grid; gap: 10px; }
.group-label { font-size: 12px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted); }
.row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.hint { margin: 0; font-size: 13px; color: var(--muted); }
.hint.warn { color: var(--warn-text); }

/* Palettes */
.palettes { display: flex; gap: 8px; flex-wrap: wrap; }
.pal { display: inline-flex; align-items: center; gap: 8px; height: 32px; padding: 0 10px 0 6px; border-radius: 999px; border: 1px solid var(--line-2); background: var(--raise); font-size: 13px; font-weight: 600; }
.pal:hover { border-color: var(--line-3); }
.pal i { display: flex; width: 36px; height: 18px; border-radius: 999px; overflow: hidden; }
.pal i b { flex: 1; }

/* Flavors: a palette plus the style and sound that go with it */
.flavors { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 8px; }
.flav {
  display: flex; align-items: center; gap: 10px; min-height: 48px; padding: 7px 10px; text-align: left;
  border-radius: var(--r-sm); border: 1px solid var(--line-2); background: var(--raise);
  transition: border-color 150ms var(--ease), background-color 150ms var(--ease);
}
.flav:hover:not(:disabled) { border-color: var(--line-3); background: var(--bg); }
.flav i { display: flex; width: 22px; height: 34px; border-radius: 4px; overflow: hidden; flex: none; flex-direction: column; }
.flav i b { flex: 1; }
.flav span { display: grid; gap: 2px; font-size: 13px; font-weight: 700; min-width: 0; }
.flav em { font-style: normal; font-size: 11px; font-weight: 500; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.select.wide { display: grid; gap: 8px; margin-top: 4px; }
.select.wide select { width: 100%; min-width: 0; max-width: none; }

/* Slice rows */
.slices { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.slice { display: grid; grid-template-columns: 20px 34px minmax(0, 1fr) 92px 32px; align-items: center; gap: 8px; padding: 6px; border-radius: var(--r); background: var(--raise); border: 1px solid transparent; transition: border-color 150ms var(--ease); }
.slice:hover, .slice:focus-within { border-color: var(--line-2); }
.slice .label {
  height: 34px; min-width: 0; padding: 0 8px; border: 0; border-radius: var(--r-sm); background: transparent; font-weight: 600; text-overflow: ellipsis;
}
.slice .label:focus-visible { outline: none; background: var(--bg); box-shadow: inset 0 0 0 1px var(--accent); }
.stepper { display: grid; grid-template-columns: 26px 1fr 26px; align-items: center; height: 30px; border-radius: var(--r-sm); background: var(--bg); }
.stepper button { height: 30px; border: 0; background: transparent; color: var(--muted); font-size: 16px; line-height: 1; border-radius: var(--r-sm); }
.stepper button:hover:not(:disabled) { color: var(--text); background: var(--line); }
.stepper output { text-align: center; font-weight: 700; font-size: 14px; }
.grip { width: 20px; height: 32px; display: grid; place-items: center; padding: 0; border: 0; border-radius: var(--r-sm); background: transparent; color: var(--muted); cursor: grab; touch-action: none; }
.grip:hover:not(:disabled), .grip:focus-visible { color: var(--text); background: var(--line); }
.grip:disabled { cursor: default; opacity: 0.4; }
.slice.dragging { border-color: var(--accent); position: relative; z-index: 1; box-shadow: 0 12px 28px rgba(0, 0, 0, 0.45); cursor: grabbing; }
.slice.dragging .grip { cursor: grabbing; color: var(--text); }
.slices.sorting .slice { transition: transform 200ms cubic-bezier(0.2, 0.8, 0.2, 1), box-shadow 200ms var(--ease); }
.slices.sorting .slice.dragging { transition: box-shadow 200ms var(--ease); }
.slices.sorting .slice.dragging.settling { transition: transform 200ms cubic-bezier(0.2, 0.8, 0.2, 1), box-shadow 200ms var(--ease); box-shadow: none; }
@media (prefers-reduced-motion: reduce) { .slices.sorting .slice { transition: none; } }
.remove { width: 32px; height: 32px; display: grid; place-items: center; border: 0; border-radius: var(--r-sm); background: transparent; color: var(--muted); }
.remove:hover:not(:disabled) { color: var(--danger); background: rgba(255, 93, 108, 0.12); }
.empty { margin: 0; padding: 14px; border: 1px dashed var(--line-2); border-radius: var(--r); color: var(--muted); text-align: center; }
.add { display: flex; gap: 8px; }
.add input { flex: 1; min-width: 0; height: 38px; padding: 0 12px; border-radius: var(--r-sm); border: 1px solid var(--line-2); background: var(--bg); }
.add input::placeholder { color: var(--faint); }
.add input:focus-visible { outline: none; border-color: var(--accent); }

/* Color chips */
.chip { position: relative; display: inline-grid; place-items: center; width: 34px; height: 34px; flex: none; cursor: pointer; }
.chip input { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; }
.chip span { width: 26px; height: 26px; border-radius: 50%; background: var(--c, #fff); box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.18); transition: transform 150ms var(--ease); }
.chip:hover span { transform: scale(1.08); }
.chip:has(input:focus-visible) span { outline: 2px solid var(--accent-hi); outline-offset: 3px; }
.chip.off { opacity: 0.35; pointer-events: none; }

.swatch { display: inline-flex; align-items: center; gap: 4px; padding-right: 8px; font-size: 13px; color: var(--muted); cursor: pointer; }

/* Segmented control */
.seg { display: inline-grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 4px; padding: 4px; border-radius: var(--r); background: var(--bg); }
.group > .seg { display: grid; }
.seg button { min-height: 32px; padding: 4px 10px; line-height: 1.2; border: 0; border-radius: var(--r-sm); background: transparent; color: var(--muted); font-weight: 600; transition: background-color 150ms var(--ease), color 150ms var(--ease); }
.seg button:hover { color: var(--text); }
.seg button[aria-checked="true"] { background: var(--accent); color: var(--accent-ink); }
.f-rounded { font-family: "Nunito", ui-rounded, sans-serif; font-weight: 800 !important; }
.f-condensed { font-family: "Barlow Condensed", sans-serif; font-size: 16px; }
.f-mono { font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 13px; }
/* 11px, not 10: these chips name the option you are picking, so they are functional text
   and sit on the same floor as every other label. Silkscreen runs large for its em, which
   is why it is the smallest of the eight samples rather than the same size as the rest. */
.f-pixel { font-family: "Silkscreen", ui-monospace, monospace; font-size: 11px; letter-spacing: 0.01em; }
.f-creepy { font-family: "Creepster", "Barlow Condensed", sans-serif; font-size: 17px; }
.f-techno { font-family: "Orbitron", "Barlow Condensed", sans-serif; font-weight: 800 !important; font-size: 11px; }
.f-marker { font-family: "Shantell Sans", "Comic Sans MS", sans-serif; font-weight: 700 !important; font-size: 13px; }
/* Eight faces do not fit one row at sidebar width; the font picker runs four to a line. */
.seg.wrap { grid-auto-flow: row; grid-template-columns: repeat(4, 1fr); }
/* Five pointer shapes read better as 3 + 2 than as 4 + a single orphan. */
.seg.wrap-3 { grid-template-columns: repeat(3, 1fr); }

/* Range */
.range { display: inline-flex; align-items: center; gap: 10px; flex: 1; min-width: 150px; }
.range.wide { width: 100%; }
.range input { flex: 1; appearance: none; height: 6px; border-radius: 999px; background: linear-gradient(var(--accent), var(--accent)) 0 / var(--fill, 50%) 100% no-repeat, var(--bg); }
.range input::-webkit-slider-thumb { appearance: none; width: 18px; height: 18px; border-radius: 50%; background: #fff; box-shadow: 0 0 0 4px var(--accent); cursor: pointer; }
.range input::-moz-range-thumb { width: 18px; height: 18px; border: 0; border-radius: 50%; background: #fff; box-shadow: 0 0 0 4px var(--accent); cursor: pointer; }
.range output { min-width: 34px; text-align: right; font-weight: 700; font-size: 14px; }

/* Free-text style fields: the two curved hub lines, the wordmark and the logo URL. */
.text { display: flex; align-items: center; flex: 1; min-width: 150px; }
.text.wide { width: 100%; }
.text input { flex: 1; min-width: 0; height: 38px; padding: 0 12px; border-radius: var(--r-sm);
  border: 1px solid var(--line-2); background: var(--bg); color: var(--text); font: 14px inherit; }
.text input::placeholder { color: var(--muted); opacity: 0.75; }
.text input:focus-visible { outline: 2px solid var(--accent-hi); outline-offset: 1px; }
.text input:disabled { opacity: 0.5; }
/* A segmented control sharing a row with a colour chip takes the rest of the row. */
.row > .seg { flex: 1; display: grid; }
/* The hub's size and curved text only exist on the big hub, so they come and go with it. */
#hub-group { display: grid; gap: 10px; }
.hint code { padding: 1px 5px; border-radius: 4px; background: var(--bg); border: 1px solid var(--line-2); font-size: 0.92em; }

/* Switch */
.switch { display: flex; align-items: center; gap: 12px; cursor: pointer; font-weight: 500; }
.sound-preview { display: flex; flex-wrap: wrap; gap: 8px; }
.switch input { position: absolute; opacity: 0; pointer-events: none; }
.switch span { position: relative; width: 38px; height: 22px; flex: none; border-radius: 999px; background: var(--line-2); transition: background-color 150ms var(--ease); }
.switch span::after { content: ""; position: absolute; top: 3px; left: 3px; width: 16px; height: 16px; border-radius: 50%; background: #fff; transition: transform 180ms var(--ease); }
.switch input:checked + span { background: var(--accent); }
.switch input:checked + span::after { transform: translateX(16px); }
.switch input:focus-visible + span { outline: 2px solid var(--accent-hi); outline-offset: 2px; }
.switch input:disabled + span { opacity: 0.4; }

/* Copy fields */
.copy { display: flex; gap: 8px; }
.copy input { flex: 1; min-width: 0; height: 38px; padding: 0 12px; border-radius: var(--r-sm); border: 1px solid var(--line-2); background: var(--bg); color: var(--muted); font: 13px ui-monospace, "SF Mono", Menlo, monospace; text-overflow: ellipsis; }

/* Preview */
.stage-wrap { container-type: size; position: relative; display: grid; place-items: center; min-height: 0; border-radius: 14px; border: 1px solid var(--line);
  /* A transparency checkerboard, not decoration: it is how the streamer sees that the overlay
     background really is transparent before dropping it into OBS. Impeccable's detector reports
     this as repeating-stripes-gradient; the finding is a known false positive here. */
  background: #111318 repeating-conic-gradient(#15171d 0 25%, #111318 0 50%) 0 0 / 24px 24px; }
/* Sized from the pane, never from vh: inside the dashboard's frame vh is the frame's height,
   which is how the preview used to grow with the settings beside it. The 64px keeps the
   stage clear of the label above it; 640px is the most a preview needs, since it stands in
   for an 800px OBS source shown at a glance. */
.stage { width: min(100cqw - 48px, 100cqh - 64px, 640px); aspect-ratio: 1; }
.stage iframe { display: block; width: 100%; height: 100%; border: 0; background: transparent; color-scheme: normal; }
#stage-tag-text.warn { color: var(--danger, #ff8a8a); }
.stage-tag { position: absolute; top: 12px; left: 14px; font-size: 12px; font-weight: 600; color: var(--muted); }
/* Tall enough for the header, the winner bar and the results row, so the stage above does
   not shrink under the wheel at the moment it lands. */
.controls { display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: start; gap: 24px; min-height: 124px; }
.btn-spin { height: 56px; min-width: 180px; padding: 0 36px; border-radius: 14px; background: var(--accent); color: var(--accent-ink); font-size: 20px; font-weight: 800; letter-spacing: 0.01em; }
.btn-spin:hover:not(:disabled) { background: var(--accent-press); }
.btn-spin:disabled { background: var(--raise); color: var(--muted); opacity: 1; }
.history { display: grid; gap: 8px; min-width: 0; align-content: start; }
.history ol { display: flex; gap: 8px; margin: 0; padding: 0; list-style: none; overflow-x: auto; }
.history li { flex: none; max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding: 6px 12px; border-radius: 999px; background: var(--raise); border: 1px solid var(--line); font-weight: 600; font-size: 14px; }
.history li:first-child:not(.muted) { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.history li.muted { background: transparent; border-style: dashed; color: var(--muted); font-weight: 500; }

.boot { position: fixed; inset: 64px 0 0; display: grid; place-content: center; justify-items: center; gap: 14px; padding: 24px; color: var(--muted); text-align: center; }
.boot p { margin: 0; max-width: 44ch; }
.toast { position: fixed; left: 50%; bottom: 24px; transform: translate(-50%, 20px); padding: 10px 16px; border-radius: var(--r); background: #fff; color: #111318; font-weight: 600; opacity: 0; pointer-events: none; transition: opacity 180ms var(--ease), transform 220ms var(--ease); }
.toast.show { opacity: 1; transform: translate(-50%, 0); }

/* Credit, bottom right */
.credit { position: fixed; right: 16px; bottom: 14px; z-index: 5; display: flex; align-items: center; gap: 2px; padding: 4px 4px 4px 6px;
  border: 1px solid var(--line); border-radius: 999px; background: color-mix(in srgb, var(--panel) 88%, transparent); backdrop-filter: blur(6px); }
.credit a { display: inline-flex; align-items: center; color: var(--muted); text-decoration: none; border-radius: 999px; transition: color 150ms var(--ease), background-color 150ms var(--ease); }
.credit a:hover { color: var(--text); background: var(--raise); }
.credit-brand { gap: 7px; height: 30px; padding: 0 10px 0 4px; font-weight: 800; font-size: 12px; letter-spacing: 0.08em; }
.credit-brand img { display: block; width: 31px; height: 24px; }
.credit-link { justify-content: center; width: 30px; height: 30px; }
.credit-link svg { width: 18px; height: 18px; }
.preview { padding-bottom: 72px; }

/* Account: the header button, the My wheels drawer, the sign-in notice */
.account-btn { gap: 8px; padding: 0 12px 0 10px; }
.account-btn .avatar { display: inline-grid; place-items: center; width: 24px; height: 24px; border-radius: 50%; background: var(--accent); color: var(--accent-ink); font-size: 12px; font-weight: 800; }
.btn-danger { background: var(--danger); color: var(--danger-ink); }
.btn-danger:hover:not(:disabled) { background: var(--danger-press); }

dialog { color: var(--text); }
dialog::backdrop { background: rgba(6, 7, 10, 0.62); }
.drawer { margin: 0 0 0 auto; padding: 0; width: min(440px, 100vw); max-width: 100vw; height: 100dvh; max-height: 100dvh; border: 0; border-left: 1px solid var(--line); background: var(--panel); }
.drawer[open] { animation: drawer-in 220ms var(--ease); }
@keyframes drawer-in { from { transform: translateX(24px); opacity: 0; } }
.drawer-sheet { display: flex; flex-direction: column; gap: 16px; height: 100%; padding: 20px; }
.drawer-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.drawer-head h2 { margin: 0; font-size: 22px; font-weight: 800; letter-spacing: -0.01em; }
.drawer-who { margin: 2px 0 0; font-size: 14px; font-weight: 600; }
.drawer-who .muted { color: var(--muted); font-weight: 500; }
.drawer-new { align-self: flex-start; }
.drawer-foot { display: grid; gap: 10px; margin-top: auto; padding-top: 14px; border-top: 1px solid var(--line); }
.drawer-foot .btn { justify-self: start; }

.claim { display: grid; gap: 12px; padding: 14px; border-radius: var(--r); border: 1px solid color-mix(in srgb, var(--accent) 55%, var(--line)); background: color-mix(in srgb, var(--accent) 10%, var(--panel)); }
.claim p { margin: 0; font-size: 14px; }

.my-wheels { flex: 0 1 auto; list-style: none; margin: 0; padding: 0; display: grid; align-content: start; gap: 6px; overflow-y: auto; min-height: 0; }
.mw-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 6px; min-height: 56px; padding: 6px 6px 6px 4px; border-radius: var(--r); background: var(--raise); border: 1px solid transparent; transition: border-color 150ms var(--ease); }
.mw-row:hover, .mw-row:focus-within { border-color: var(--line-2); }
.mw-row[data-current="true"] { border-color: color-mix(in srgb, var(--accent) 60%, var(--line-2)); }
.mw-open { display: grid; gap: 1px; min-width: 0; padding: 6px 8px; border-radius: var(--r-sm); color: var(--text); text-decoration: none; }
.mw-open:hover .mw-title { text-decoration: underline; text-underline-offset: 3px; }
.mw-title { font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mw-meta { font-size: 12px; color: var(--muted); }
.mw-row[data-current="true"] .mw-meta { color: var(--accent-hi); font-weight: 600; }
.mw-actions { display: flex; gap: 2px; }
.mw-act { width: 34px; height: 34px; display: grid; place-items: center; padding: 0; border: 0; border-radius: var(--r-sm); background: transparent; color: var(--muted); }
.mw-act:hover:not(:disabled) { color: var(--text); background: var(--line); }
.mw-act.danger:hover:not(:disabled) { color: var(--danger); background: rgba(255, 93, 108, 0.12); }
.mw-edit { grid-column: 1 / -1; display: flex; gap: 6px; padding: 2px; }
.mw-edit input { flex: 1; min-width: 0; height: 38px; padding: 0 10px; border-radius: var(--r-sm); border: 1px solid var(--accent); background: var(--bg); font-weight: 600; }
.mw-edit input:focus-visible { outline: none; }
.mw-confirm { grid-column: 1 / -1; display: grid; gap: 10px; padding: 6px 8px; }
.mw-confirm p { margin: 0; font-size: 14px; }
.mw-empty { padding: 18px 14px; border: 1px dashed var(--line-2); border-radius: var(--r); color: var(--muted); font-size: 14px; text-align: center; }

.notice { width: min(400px, calc(100vw - 32px)); padding: 24px; border: 1px solid var(--line-2); border-radius: 14px; background: var(--panel); }
.notice[open] { display: grid; gap: 12px; animation: notice-in 180ms var(--ease); }
@keyframes notice-in { from { transform: translateY(8px); opacity: 0; } }
.notice h2 { margin: 0; font-size: 20px; font-weight: 800; }
.notice p { margin: 0; color: var(--muted); }
.notice .btn { justify-self: start; }

.rekey { display: grid; gap: 8px; }
.rekey .btn { justify-self: start; }

@media (max-width: 960px) {
  /* One column: back to one page that scrolls. The stage takes its width from the column
     and stops at 420px, which keeps Spin on the first screen of a tablet or a laptop. */
  .app { grid-template-columns: minmax(0, 1fr); grid-template-rows: none; height: auto; min-height: 0; }
  .panel { min-width: 0; overflow: visible; }
  .stage-wrap { container-type: inline-size; padding: 12px 12px 0; }
  .stage-tag, .onair { order: -1; }
  .stage-tag { position: static; justify-self: start; padding: 0 2px 4px; }
  .onair { position: static; justify-self: stretch; }
  .stage-wrap { gap: 4px; }
  .preview { order: -1; padding: 16px; }
  .panel { border-right: 0; border-top: 1px solid var(--line); }
  .stage { width: min(100%, 420px); }
  .top { padding: 0 14px; }
  .top-actions .btn span:not(.avatar) { display: none; }
  .top-actions .btn { width: 38px; padding: 0; }
  .select select { min-width: 0; max-width: 150px; }
  .preview { padding-bottom: 16px; }
  .credit { position: static; width: max-content; margin: 8px auto 20px; }
  .drawer { width: 100vw; border-left: 0; }
  .drawer-sheet { padding: 16px; }
  .mw-act { width: 40px; height: 40px; }
  .account-btn { padding: 0; }
}
/* Phones: Spin takes the full width, and the keyboard hint goes, there is no keyboard. */
@media (max-width: 560px) {
  .controls { grid-template-columns: minmax(0, 1fr); min-height: 0; }
  .btn-spin { width: 100%; }
  #stage-keys { display: none; }
  .tabs button { padding: 0 3px; }
}
/* Under 340px the five words no longer fit across: three to a row keeps each one whole. */
@media (max-width: 339px) {
  .tabs { grid-template-columns: repeat(3, 1fr); gap: 4px; }
}
/* Touch: the small row controls grow to a size a thumb can hit without leaving the row. */
@media (hover: none), (max-width: 560px) {
  .slice { grid-template-columns: 28px 34px minmax(0, 1fr) 108px 36px; gap: 4px; }
  .grip { width: 28px; height: 36px; }
  .remove { width: 36px; height: 36px; }
  .stepper { grid-template-columns: 34px 1fr 34px; height: 36px; }
  .stepper button { height: 36px; }
}
/* Phones: four header controls and the wordmark do not fit; the mark alone is the logo. */
@media (max-width: 520px) {
  .top { gap: 8px; }
  .logo span { display: none; }
  .top-actions { gap: 6px; min-width: 0; }
  .select { min-width: 0; }
  .select select { max-width: 132px; }
}
@media (prefers-reduced-motion: reduce) { *, *::before, *::after { transition-duration: 1ms !important; } }
@media (prefers-reduced-motion: reduce) { .drawer[open], .notice[open] { animation: none; } }

/* Slice list tools: shuffle, sort, edit as a list */
.btn-sm { height: 32px; padding: 0 10px; gap: 6px; font-size: 13px; }
.btn-xs { height: 28px; padding: 0 10px; font-size: 12px; }
.slice-tools { display: flex; flex-wrap: wrap; gap: 8px; }
.slice-tools .btn[aria-pressed="true"] { border-color: var(--accent); color: var(--text); }
.bulk { display: grid; gap: 10px; }
.bulk textarea { width: 100%; min-height: 220px; resize: vertical; padding: 10px 12px; border-radius: var(--r-sm); border: 1px solid var(--line-2);
  background: var(--bg); color: var(--text); font: 14px/1.5 ui-monospace, "SF Mono", Menlo, monospace; }
.bulk textarea:focus-visible { outline: none; border-color: var(--accent); }
.hint.vol { min-width: 84px; }

/* Click the preview to spin. Invisible; only the cursor says the wheel is a control. */
.stage { position: relative; }
.stage-hit { position: absolute; inset: 0; padding: 0; border: 0; border-radius: 50%; background: transparent; cursor: pointer; }
.stage-hit:disabled { cursor: default; }
.stage-hit[hidden] { display: none; }

/* Results: count, export, clear, and what to do with the winner */
.history-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; min-height: 28px; }
.history-head .count { margin-left: 4px; font-variant-numeric: tabular-nums; color: var(--text); }
.history-tools { display: flex; gap: 6px; }
.history-tools[hidden] { display: none; }
.winner-bar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 6px 6px 6px 12px; border-radius: var(--r); border: 1px solid var(--line-2); background: var(--raise); }
.winner-bar[hidden] { display: none; }
.winner-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 14px; color: var(--muted); }
.winner-name b { color: var(--text); }

/* ------------------------------------------------------------------ chat entry
   Three new shapes. The doorway at the top of the Slices tab (.cta), the Twitch
   connection's state (.conn), and the open/closed control (.entries).

   They share one idea: a block that says which state it is in, in words, with the
   one control that changes it. None of them is a card — they are inset panels on
   the sidebar's own surface, bordered rather than raised, because depth in this app
   belongs to the wheel stage and not to the settings beside it. */

.cta { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; padding: 12px 12px 12px 14px; border-radius: var(--r);
  border: 1px solid var(--line-2); background: var(--raise); }
.cta-main { flex: 1; min-width: 180px; display: grid; gap: 3px; }
.cta-title { display: flex; align-items: center; gap: 8px; margin: 0; font-size: 15px; font-weight: 700; }
.cta-title .i { color: var(--accent-hi); }
.cta-actions { display: flex; align-items: center; gap: 10px; }
/* Open: the strip takes the accent, because the count on it is live and the streamer
   needs to find it from across the room while they are talking to chat. */
.cta[data-state="open"] { border-color: color-mix(in srgb, var(--accent) 60%, var(--line-2));
  background: color-mix(in srgb, var(--accent) 12%, var(--panel)); }
.cta[data-state="open"] .cta-title .i { color: var(--accent-hi); }

/* The entrant count, wherever it appears. Tabular so it does not jitter as it climbs. */
.tally { display: inline-flex; align-items: center; gap: 6px; padding: 0 10px; height: 30px; border-radius: 999px;
  background: var(--bg); border: 1px solid var(--line-2); font-size: 14px; font-variant-numeric: tabular-nums; }
.tally .i { width: 16px; height: 16px; color: var(--muted); }
.tally[hidden] { display: none; }
.tally.big { height: 40px; padding: 0 14px; font-size: 20px; font-weight: 800; letter-spacing: -0.01em; }
.tally.big .i { width: 19px; height: 19px; }

.conn { display: grid; gap: 10px; padding: 14px; border-radius: var(--r); border: 1px solid var(--line-2); background: var(--raise); }
.conn[data-state="connected"] { border-color: color-mix(in srgb, var(--ok) 45%, var(--line-2)); }
.conn[data-state="unavailable"] { border-style: dashed; }
.conn-line { display: flex; align-items: center; gap: 8px; margin: 0; font-size: 15px; font-weight: 700; }
.conn[data-state="connected"] .conn-line::before { content: ""; width: 8px; height: 8px; flex: none; border-radius: 50%; background: var(--ok); }
.conn .row:empty { display: none; }
/* What Twitch is about to be asked for, in the streamer's words. A list, because
   consenting to three things is three decisions and a sentence hides that. */
.grants { display: grid; gap: 6px; margin: 0; padding: 0; list-style: none; font-size: 13px; color: var(--muted); }
.grants li { display: flex; gap: 9px; align-items: baseline; }
.grants li::before { content: ""; width: 5px; height: 5px; flex: none; margin-top: 7px; border-radius: 50%; background: var(--line-2); }

.entries { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap;
  padding: 12px; border-radius: var(--r); border: 1px solid var(--line-2); background: var(--raise);
  transition: border-color 200ms var(--ease), background-color 200ms var(--ease); }
.entries-state { display: flex; align-items: center; gap: 12px; min-width: 0; }
.entries-word { font-size: 14px; font-weight: 600; color: var(--muted); }
.entries[data-state="open"] { border-color: color-mix(in srgb, var(--accent) 60%, var(--line-2));
  background: color-mix(in srgb, var(--accent) 12%, var(--panel)); }
.entries[data-state="open"] .entries-word { color: var(--text); }
.entries[data-state="open"] .tally { border-color: color-mix(in srgb, var(--accent) 50%, var(--line-2)); }
/* Open and taking entries: the count breathes once a second, slowly, so a glance at
   the sidebar says "this is live" without anything moving fast enough to distract. */
.entries[data-state="open"] .tally.big { animation: live 2.4s var(--ease) infinite; }
@keyframes live {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 45%, transparent); }
  50% { box-shadow: 0 0 0 5px color-mix(in srgb, var(--accent) 0%, transparent); }
}
@media (prefers-reduced-motion: reduce) { .entries[data-state="open"] .tally.big { animation: none; } }

/* Four names for "who can enter" do not fit one row at sidebar width. */
.seg.wrap-2 { grid-template-columns: repeat(2, 1fr); }
/* Every step below the Twitch connection is dead until there is one; dimming the
   whole group says that once instead of once per control. */
#entry-controls:has(input:disabled), #entry-who-group:has(button:disabled) { opacity: 0.78; }

/* ---------- The wheel switcher ---------- */
/* The switcher is what gives way when the toolbar runs short: a long name truncates. */
.switcher { position: relative; min-width: 0; max-width: 280px; }
.switcher-btn { display: inline-flex; align-items: center; gap: 8px; height: 38px; max-width: 100%; padding: 0 10px 0 12px;
  border-radius: var(--r-sm); border: 1px solid var(--line-2); background: var(--panel); font-weight: 600;
  transition: border-color 150ms var(--ease), background-color 150ms var(--ease); }
.switcher-btn:hover, .switcher-btn[aria-expanded="true"] { border-color: var(--line-3); background: var(--raise); }
.switcher-title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.switcher-btn .i { color: var(--muted); }
.switcher-btn .switcher-live { color: var(--live); }
.switcher-pop { inset: auto; position: fixed; position-anchor: --switcher; top: anchor(bottom); right: anchor(right); margin: 6px 0 0;
  width: min(400px, calc(100vw - 24px)); max-height: min(70vh, 520px); padding: 6px; overflow: auto;
  border-radius: var(--r); border: 1px solid var(--line-2); background: var(--panel); color: var(--text);
  box-shadow: 0 18px 40px -16px rgba(0, 0, 0, 0.75), 0 2px 6px rgba(0, 0, 0, 0.4);
  opacity: 0; transform: translateY(-4px); transition: opacity 140ms var(--ease), transform 180ms var(--ease), display 180ms allow-discrete, overlay 180ms allow-discrete; }
.switcher-btn { anchor-name: --switcher; }
.switcher-pop:popover-open { opacity: 1; transform: none; }
@starting-style { .switcher-pop:popover-open { opacity: 0; transform: translateY(-4px); } }
/* Where anchor positioning is missing, the list still opens under the top bar on the right. */
@supports not (position-anchor: --a) { .switcher-pop { top: 58px; right: 12px; } }
.switcher-find { display: flex; align-items: center; gap: 8px; height: 36px; margin: 2px 2px 6px; padding: 0 10px;
  border-radius: var(--r-sm); border: 1px solid var(--line-2); background: var(--bg); color: var(--muted); }
.switcher-find:focus-within { border-color: var(--accent); }
.switcher-find input { flex: 1; min-width: 0; height: 100%; border: 0; background: transparent; color: var(--text); outline: none; }
.switcher-find input::placeholder { color: var(--muted); }
.switcher-list { display: grid; gap: 2px; margin: 0; padding: 0; list-style: none; }
.sw-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 4px; border-radius: var(--r-sm); }
.sw-row:hover, .sw-row:focus-within { background: var(--raise); }
.sw-open { display: grid; gap: 1px; min-width: 0; padding: 8px 10px; border-radius: var(--r-sm); color: var(--text); text-decoration: none; }
.sw-open:focus-visible { outline-offset: -2px; }
.sw-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
.sw-meta { font-size: 12px; color: var(--muted); }
.sw-meta:empty { display: none; }
.sw-row[data-current="1"] .sw-title::before { content: ""; display: inline-block; width: 6px; height: 6px; margin: 0 8px 2px 0; border-radius: 50%; background: var(--accent); vertical-align: middle; }
.sw-row[data-live="1"] .sw-meta { color: var(--live); font-weight: 700; }
.sw-air { margin-right: 4px; opacity: 0; transition: opacity 120ms var(--ease); }
.sw-row:hover .sw-air, .sw-row:focus-within .sw-air { opacity: 1; }
@media (hover: none) { .sw-air { opacity: 1; } }
.switcher-foot { margin: 6px 8px 4px; font-size: 12px; color: var(--muted); }

/* ---------- On stream ---------- */
.onair { position: absolute; top: 8px; right: 10px; display: flex; align-items: center; gap: 8px; max-width: calc(100% - 20px);
  padding: 4px 4px 4px 10px; border-radius: var(--r-sm); border: 1px solid var(--line-2); background: color-mix(in srgb, var(--panel) 92%, transparent); font-size: 13px; }
.onair > .i { color: var(--muted); width: 16px; height: 16px; }
.onair[data-here="true"] { border-color: color-mix(in srgb, var(--live) 55%, var(--line-2)); padding-right: 10px; }
.onair[data-here="true"] > .i { color: var(--live); }
.onair-line { margin: 0; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--muted); }
.onair-label { font-size: 11px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; }
/* One column: the chip sits on its own row, so it cannot cover the preview label. This has to
   follow the base .onair rule above, which would otherwise keep it absolute. */
@media (max-width: 960px) { .onair { position: static; justify-self: stretch; max-width: none; } }
.onair[data-here="true"] .onair-label { color: var(--live); }
.onair b { color: var(--text); font-weight: 700; }
.onair-cycle { font-variant-numeric: tabular-nums; }

/* ---------- The stage, in the Overlay tab ---------- */
#stage-none, #stage-some { display: grid; gap: 10px; }
#stage-none .btn { justify-self: start; }
.stage-wheels { display: grid; gap: 4px; margin: 0; padding: 0; list-style: none; }
.st-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 8px; min-height: 40px; padding: 4px 6px 4px 10px;
  border-radius: var(--r-sm); background: var(--raise); border: 1px solid transparent; }
.st-row[data-live="1"] { border-color: color-mix(in srgb, var(--live) 45%, var(--line-2)); }
.st-row .switch b { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
.st-tag { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 700; color: var(--live); }
.st-tag .i { width: 15px; height: 15px; }
.st-row .switch { min-width: 0; }
@media (max-width: 560px) {
  .switcher-btn { max-width: 150px; }
  /* A phone has no room to hang the list off the button: it spans the screen under the bar. */
  .switcher-pop { top: 58px; left: 12px; right: 12px; width: auto; }
}
.st-tag .btn { color: var(--text); }
/* The phone top bar shrinks its buttons to icons; the switcher's own buttons keep their words. */
.top-actions .switcher-pop .btn { width: auto; padding: 0 10px; }
.top-actions .switcher-pop .btn span { display: inline; }

/* ---------- The two OBS links in the toolbar ---------- */
/* Two lines, because the second line is what tells them apart: one wheel, or several. They
   keep their words at every width. A bare icon cannot say which link it copies. */
.top-actions { min-width: 0; }
.top-links { display: flex; gap: 8px; flex: none; }
.top-links:not(:has(> :not([hidden]))) { display: none; }
.top-actions .btn.top-link { width: auto; height: 46px; padding: 0 12px; text-align: left; }
.top-actions .btn.top-link .top-link-text { display: grid; gap: 1px; min-width: 0; line-height: 1.2; }
.top-link-text b { font-weight: 600; }
.top-link-text small { font-size: 12px; font-weight: 500; }
.btn-soft .top-link-text small { color: var(--muted); }
/* Too narrow for one row, phones included: the links take a row of their own, across the bar. */
@media (max-width: 640px) {
  .top { flex-wrap: wrap; align-content: center; gap: 6px 8px; height: auto; min-height: 64px; padding-top: 12px; padding-bottom: 12px; }
  .top .logo { margin-right: auto; }
  .top-actions { display: contents; }
  .top-links { order: 1; flex: 1 0 100%; }
  .top-actions .btn.top-link { flex: 1 1 0; min-width: 0; height: auto; min-height: 46px; padding: 5px 10px; white-space: normal; }
  .top-link > .i { display: none; }
}
