/* ------------------------------------------------------------------
   Focusmaxxing — the design system.

   Shared by the extension (popup, blocked page, unsupported note) and
   the desktop app (shell, floating cards, ring window). The two copies —
   repo root and app/ — must stay identical; app/prepare.js copies this
   one across before every start and build.

   Two themes only. Dark is what :root declares, so it is what you get
   before anything has loaded; light is data-theme="light" on <html>.

   Tokens first, then the components every screen shares: cards,
   buttons, switches, fields, chips, sliders, sheets, toasts, lists.
   Layout belongs to popup.css and shell.css, not here.
   ------------------------------------------------------------------ */

@font-face { font-family: "Manrope"; font-weight: 400; font-display: swap; src: url(fonts/manrope-latin-400-normal.woff2) format("woff2"); }
@font-face { font-family: "Manrope"; font-weight: 500; font-display: swap; src: url(fonts/manrope-latin-500-normal.woff2) format("woff2"); }
@font-face { font-family: "Manrope"; font-weight: 600; font-display: swap; src: url(fonts/manrope-latin-600-normal.woff2) format("woff2"); }
@font-face { font-family: "Manrope"; font-weight: 700; font-display: swap; src: url(fonts/manrope-latin-700-normal.woff2) format("woff2"); }
@font-face { font-family: "Manrope"; font-weight: 800; font-display: swap; src: url(fonts/manrope-latin-800-normal.woff2) format("woff2"); }

/* ---------------- tokens ---------------- */

:root {
  --font: "Manrope", "Segoe UI Variable Text", "Segoe UI", system-ui, sans-serif;

  /* dark */
  --bg: #07090f;
  --bg2: #0d111b;
  --surface: rgba(255, 255, 255, 0.045);
  --surface-2: rgba(255, 255, 255, 0.08);
  --surface-3: rgba(255, 255, 255, 0.13);
  --border: rgba(255, 255, 255, 0.085);
  --border-strong: rgba(255, 255, 255, 0.18);
  --text: #f3f6ff;
  --muted: rgba(243, 246, 255, 0.6);
  --faint: rgba(243, 246, 255, 0.36);
  --shadow: 0 18px 50px rgba(0, 0, 0, 0.45);
  --knob: #ffffff;

  /* volt — blocked, on, go */
  --ac1: #b8ff3c;
  --ac2: #34f0c9;
  --ac3: #22c7ff;
  --on-ink: #071009;
  --accent-text: #6cf5d6;

  /* ember — the wait */
  --wait1: #ffb53d;
  --wait2: #ff6a1f;
  --wait-ink: #1c0a00;
  --wait-text: #ffb53d;

  --danger: #ff4d6d;
  --ok: #34f0c9;

  --grad: linear-gradient(135deg, var(--ac1), var(--ac2) 58%, var(--ac3));
  --grad-2: linear-gradient(135deg, var(--ac1), var(--ac2));
  --grad-wait: linear-gradient(135deg, var(--wait1), var(--wait2));
  --glow: 0 12px 32px -10px var(--ac1);
  --glow-strong: 0 18px 44px -10px var(--ac1);
  --glow-wait: 0 12px 30px -10px var(--wait2);
  --ambient: 0.3;

  --radius: 20px;
  --radius-sm: 14px;
  --radius-xs: 10px;
  --ease: cubic-bezier(0.2, 0.8, 0.2, 1);
  --spring: cubic-bezier(0.3, 1.5, 0.5, 1);
}

:root[data-theme="light"] {
  --bg: #f3f6fb;
  --bg2: #ffffff;
  --surface: rgba(255, 255, 255, 0.8);
  --surface-2: rgba(255, 255, 255, 0.98);
  --surface-3: rgba(13, 18, 32, 0.07);
  --border: rgba(13, 18, 32, 0.08);
  --border-strong: rgba(13, 18, 32, 0.17);
  --text: #0d1220;
  --muted: rgba(13, 18, 32, 0.62);
  --faint: rgba(13, 18, 32, 0.4);
  --shadow: 0 18px 50px rgba(30, 40, 90, 0.13);
  --knob: #ffffff;

  --ac1: #a9ee22;
  --ac2: #22dcb6;
  --ac3: #14b4ef;
  --accent-text: #0a8f78;
  --wait-text: #d9650a;
  --glow: 0 12px 30px -12px rgba(34, 220, 182, 0.8);
  --glow-strong: 0 18px 40px -12px rgba(34, 220, 182, 0.9);
  --ambient: 0.26;
}

/* ---------------- base ---------------- */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html,
body {
  height: 100%;
}

body {
  margin: 0;
  font-family: var(--font);
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  user-select: none;
  transition: background 0.35s, color 0.35s;
}

[hidden] {
  display: none !important;
}

button {
  font: inherit;
  color: inherit;
  border: 0;
  background: none;
  cursor: pointer;
  padding: 0;
  margin: 0;
  -webkit-tap-highlight-color: transparent;
}

button:disabled {
  cursor: default;
}

input,
select,
textarea {
  font: inherit;
  color: inherit;
}

input,
textarea {
  user-select: text;
}

svg {
  display: block;
}

::-webkit-scrollbar {
  width: 8px;
  height: 8px;
}

::-webkit-scrollbar-thumb {
  background: var(--surface-3);
  border-radius: 8px;
}

::-webkit-scrollbar-thumb:hover {
  background: var(--border-strong);
}

::-webkit-scrollbar-track,
::-webkit-scrollbar-corner {
  background: transparent;
}

:focus-visible {
  outline: 2px solid var(--ac2);
  outline-offset: 2px;
}

/* ---------------- the ambient glow behind everything ---------------- */

.ambient {
  position: fixed;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  z-index: 0;
}

.ambient i {
  position: absolute;
  border-radius: 50%;
  filter: blur(70px);
  opacity: var(--ambient);
  will-change: transform;
  animation: breathe 16s ease-in-out infinite alternate;
  transition: background 0.6s;
}

/* Mint and sky do the ambient work; the lime is saved for the things you
   can press, so it reads as a signal rather than a wash. */
.ambient .g1 {
  width: 62vmax;
  height: 62vmax;
  left: -24vmax;
  top: -28vmax;
  background: radial-gradient(circle at 45% 45%, var(--ac2), transparent 62%);
}

.ambient .g2 {
  width: 54vmax;
  height: 54vmax;
  right: -22vmax;
  bottom: -26vmax;
  background: radial-gradient(circle at 50% 50%, var(--ac3), transparent 62%);
  animation-delay: -7s;
  animation-duration: 20s;
}

.ambient .g3 {
  width: 34vmax;
  height: 34vmax;
  left: 46%;
  top: 34%;
  opacity: calc(var(--ambient) * 0.45);
  background: radial-gradient(circle at 50% 50%, var(--ac1), transparent 60%);
  animation-delay: -12s;
  animation-duration: 24s;
}

@keyframes breathe {
  from {
    transform: translate3d(0, 0, 0) scale(1);
  }
  to {
    transform: translate3d(4vmax, 3vmax, 0) scale(1.1);
  }
}

/* ---------------- the mark ---------------- */

/* The glowing square with the bolt in it. The brand, drawn once. */
.mark {
  width: 40px;
  height: 40px;
  border-radius: 13px;
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  color: var(--on-ink);
  background: var(--grad);
  box-shadow: 0 10px 26px -8px var(--ac1), inset 0 0 0 1px rgba(255, 255, 255, 0.28);
}

.mark svg {
  width: 58%;
  height: 58%;
  fill: currentColor;
}

/* A field you write several lines in. Used by the message list in both
   screens. */
.area {
  min-height: 90px;
  margin: 10px 0 8px;
}

/* ---------------- the message list ---------------- */

/* Your block messages: the app's settings and the extension's popup keep
   the same list, so the rows are drawn the same way in both.

   Past about half a dozen the list scrolls inside itself. Twelve of them
   should not push the rest of the settings off the bottom of the
   screen. */
.msgs {
  margin-top: 12px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-height: 244px;
  overflow-y: auto;
  /* So a row is never half under the scrollbar. */
  padding-right: 4px;
}

/* A media row is taller, because it carries a picture. */
.msgs.media {
  max-height: 300px;
}

.msg {
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-xs);
  padding: 8px 8px 8px 12px;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  /* Shift-clicking a range would otherwise drag a text selection across
     the rows instead of picking them. */
  user-select: none;
  transition: border-color 0.15s, background 0.15s;
}

.msg:hover {
  border-color: var(--border-strong);
}

/* Picked, and about to be removed together. */
.msg[data-picked="true"] {
  border-color: var(--ac1);
  background: color-mix(in srgb, var(--ac1) 12%, var(--surface));
}

/* Shown only while something is picked: how many, and what you can do
   with them. */
.picked {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 12px;
  font-size: 12.5px;
  font-weight: 700;
}

.picked > span {
  flex: 1;
  min-width: 0;
}

.msg span {
  flex: 1;
  min-width: 0;
  overflow-wrap: anywhere;
}

/* A media row is a message row with the picture on the front of it,
   so the two lists sit under one another and read as one thing. */
.shot {
  flex: none;
  display: grid;
  place-items: center;
  width: 52px;
  height: 40px;
  border-radius: 10px;
  overflow: hidden;
  background: var(--bg);
  border: 1px solid var(--border);
}

.shot img,
.shot video {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* A sound has no picture, so the square holds a note instead. */
.shot svg {
  width: 18px;
  height: 18px;
  fill: none;
  stroke: var(--muted);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* What it is and how big, under its name. */
.msg .about {
  display: block;
  margin-top: 1px;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--muted);
}

/* The button that opens the file dialog. Full width, because it is the
   whole of what this card does, with what it takes written under it. */
.pick-media {
  margin-top: 10px;
  width: 100%;
}

.formats {
  margin: 6px 0 0;
}

/* What a link is doing while you wait for it. */
.busy {
  margin: 8px 0 0;
}

.busy:empty {
  display: none;
}

/* ---------------- cards ---------------- */

.card {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  transition: transform 0.22s var(--ease), box-shadow 0.22s, border-color 0.22s, opacity 0.3s;
}

.card::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background: linear-gradient(135deg, rgba(255, 255, 255, 0.09), rgba(255, 255, 255, 0) 42%);
}

:root[data-theme="light"] .card::before {
  background: linear-gradient(135deg, rgba(255, 255, 255, 0.9), rgba(255, 255, 255, 0) 45%);
}

.card > * {
  position: relative;
}

.card.lift:hover {
  transform: translateY(-2px);
  border-color: var(--border-strong);
}

/* A card that matters more than the others: a soft gradient edge and a
   glow in the accent. */
.card.glow {
  border-color: transparent;
  box-shadow: 0 22px 54px -24px var(--ac1), var(--shadow);
}

.card.glow::after {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: inherit;
  padding: 1px;
  pointer-events: none;
  background: var(--grad);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  opacity: 0.7;
}

.card.wait {
  border-color: transparent;
  box-shadow: 0 22px 54px -24px var(--wait2), var(--shadow);
}

.card.wait::after {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: inherit;
  padding: 1px;
  pointer-events: none;
  background: var(--grad-wait);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  opacity: 0.8;
}

.card.dim {
  opacity: 0.55;
}

/* ---------------- panels and folds ---------------- */

/* One big card holding a whole screen, with a plainer block inside it for
   each section. The website blocker is built this way (decided
   2026-09-07): the NSFW switch, the sites and the custom blocks are three
   blocks inside one, rather than cards floating separately. */
.card.panel {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 14px;
}

/* Only the blocks inside that card. The floating windows — the clock,
   the notes, the to-do list and the "closed" toast — call their own
   glass card a .pane too, and it is not this one. */
.card.panel > .pane {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
}

:root[data-theme="light"] .card.panel > .pane {
  background: rgba(255, 255, 255, 0.55);
}

.card.panel > .pane > .section-title {
  display: block;
  margin: 2px 8px 8px;
}

/* A site you open: a block of its own with its name and an arrow on it,
   and its switches inside once it is open. A block rather than a line in
   a list, so eight of them can be told apart at a glance (asked for
   2026-09-07); the block it sits in decides whether they run down one
   column or two. */
.fold {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  transition: border-color 0.18s, box-shadow 0.18s;
}

.fold:hover {
  border-color: var(--border-strong);
}

/* Open, it reads as the one you are working in — a firmer edge and a
   glow so faint it is nearly white. The accent is saved for what is
   actually blocked; a green ring around whatever you happened to open
   said nothing and fought everything. */
.fold[data-open="true"] {
  border-color: var(--border-strong);
  box-shadow: 0 14px 30px -24px rgba(233, 255, 246, 0.55);
}

:root[data-theme="light"] .fold[data-open="true"] {
  box-shadow: 0 14px 30px -22px rgba(13, 18, 32, 0.16);
}

.fold-head {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 12px 12px;
  border-radius: var(--radius-sm);
  color: var(--text);
  font-size: 14px;
  font-weight: 800;
  letter-spacing: -0.01em;
  text-align: left;
  transition: background 0.15s, color 0.15s;
}

.fold-head:hover {
  background: var(--surface-3);
}

/* A line under the name once the switches are showing, so the name reads
   as the block's own heading rather than the first row in it. */
.fold[data-open="true"] .fold-head {
  border-bottom: 1px solid var(--border);
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
}

.fold-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.fold-arrow {
  width: 17px;
  height: 17px;
  flex: 0 0 auto;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.2;
  stroke-linecap: round;
  stroke-linejoin: round;
  color: var(--faint);
  transition: transform 0.22s var(--ease), color 0.15s;
}

.fold-head:hover .fold-arrow {
  color: var(--text);
}

/* Pointing along the list when shut, down into it when open. */
.fold[data-open="true"] .fold-arrow,
.pane[data-open="true"] > .pane-head .fold-arrow {
  transform: rotate(90deg);
  color: var(--text);
}

.fold-body {
  display: none;
  padding: 2px 12px 8px;
}

.fold[data-open="true"] .fold-body {
  display: block;
}

/* Whether anything inside is switched on, said on the outside — so a
   shut block still tells you where your blocks are. */
.fold-live {
  display: none;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent-text);
}

.fold[data-live="true"] .fold-live {
  display: flex;
}

.fold-live i {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--ac1);
  box-shadow: 0 0 9px var(--ac1);
}

/* A block inside the card opens and shuts as well, on its own heading,
   so a long list can be put away without leaving the panel. */
.pane-head {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 2px 8px 8px;
  text-align: left;
  color: var(--muted);
  transition: color 0.15s;
}

.pane-head:hover {
  color: var(--text);
}

.pane-head > .section-title {
  flex: 1;
  min-width: 0;
  margin: 0;
  color: inherit;
}

.pane-head .fold-arrow {
  width: 15px;
  height: 15px;
}

.card.panel > .pane[data-open="false"] > .pane-head {
  padding-bottom: 2px;
}

.card.panel > .pane[data-open="false"] > .pane-body {
  display: none;
}

/* The rows slide in when you open a site yourself. The class goes on at
   the click and comes off the moment it has played, so redrawing the
   list — which both screens do on every saved change — puts an open
   site straight back instead of playing it again. */
.fold-body.opening {
  animation: fade-up 0.22s var(--ease);
}

.fold-body .row + .row {
  border-top: 1px solid var(--border);
}

/* ---------------- Exclude ---------------- */

/* A few switches can leave something out of what they block (sites.js,
   BLOCK_OPTIONS): a feed can keep the accounts you follow, Discord a few
   people and servers, Roblox the parts of its sidebar and top bar you
   tick. From the owner's drawing (2026-09-23): under the switch's name a
   small grey line — "Exclude:" and a compact dropdown saying what is
   left out — which opens a small panel under the row. The popup and the
   app build the same pieces, so they are drawn here once. */

/* The name and its Exclude line, one above the other, where the name
   alone used to be. */
.exclude-stack {
  flex: 1;
  min-width: 0;
}

.exclude-stack > .label {
  display: block;
}

/* The line takes the width its row gives it and never asks for more
   (`contain: inline-size`): what the dropdown says can be every item of a
   pick list or a string of long names, and a width of its own would push
   the row, the site's block and the column it stands in out past the
   card, taking every switch with them (review B1, 2026-09-24). Held to
   the row, a long choice is cut short with an ellipsis instead. */
.exclude-line {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  margin-top: 3px;
  font-size: 11px;
  font-weight: 600;
  color: var(--faint);
  contain: inline-size;
}

.exclude-word {
  flex: 0 0 auto;
}

/* The dropdown: small, in the muted ink, never the accent — the accent
   is for what is blocked, and this is what is let through. */
.exclude-pick {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  min-width: 0;
  max-width: 100%;
  min-height: 22px;
  padding: 2px 5px 2px 8px;
  border-radius: 7px;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--muted);
  font-size: 11px;
  font-weight: 700;
  transition: color 0.15s, border-color 0.15s, background 0.15s;
}

.exclude-pick:hover,
.exclude-pick[aria-expanded="true"] {
  color: var(--text);
  border-color: var(--border-strong);
}

.exclude-pick[aria-expanded="true"] {
  background: var(--surface-3);
}

.exclude-pick:focus-visible {
  outline: 2px solid var(--text);
  outline-offset: 2px;
}

.exclude-now {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Nothing left out: an empty box with its arrow, as drawn. */
.exclude-now:empty {
  width: 14px;
}

.exclude-pick svg {
  flex: 0 0 auto;
  width: 13px;
  height: 13px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.4;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: transform 0.2s var(--ease);
}

.exclude-pick[aria-expanded="true"] svg {
  transform: rotate(180deg);
}

/* Where the plan withholds it, the tag stands where the dropdown was —
   a size down, to sit on a line of small grey words. */
.exclude-line > .plan-tag {
  padding: 3px 8px;
  font-size: 9.5px;
}

/* The panel under the row. Held to the row's width the same way, so a
   long name on a chip is cut short inside the panel rather than widening
   it. */
.exclude-panel {
  contain: inline-size;
  margin: 0 0 8px;
  padding: 8px 10px 10px;
  border-radius: var(--radius-xs);
  background: var(--surface);
  border: 1px solid var(--border);
  font-size: 12.5px;
}

/* It slides in when you open it yourself, and only then, the way a
   site's rows do (.fold-body.opening): both screens redraw the list on
   every saved change, the panel with it, and an open panel is put
   straight back rather than played again (2026-09-25). */
.exclude-panel.opening {
  animation: fade-up 0.18s var(--ease);
}

/* The row after an open panel keeps the line between rows. */
.fold-body .exclude-panel + .row {
  border-top: 1px solid var(--border);
}

.exclude-choices {
  display: grid;
  gap: 1px;
}

/* A pick list runs down two columns, so twelve items are not a
   scroll of their own. */
.exclude-choices.two {
  grid-template-columns: 1fr 1fr;
  column-gap: 8px;
}

.exclude-choice {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  padding: 5px 6px;
  border-radius: 8px;
  font-weight: 600;
  color: var(--text);
  cursor: pointer;
  transition: background 0.15s;
}

.exclude-choice:hover {
  background: var(--surface-3);
}

.exclude-choice > span {
  min-width: 0;
  overflow-wrap: anywhere;
}

/* A tick in the ink, never the accent — the same rule as the dropdown:
   a ticked item is something let through, and the accent is for what is
   blocked. */
.exclude-choice > input {
  flex: 0 0 auto;
  width: 15px;
  height: 15px;
  margin: 0;
  accent-color: var(--text);
  cursor: inherit;
}

/* One that would add to a block that is holding: still shown, plainly
   out of reach. */
.exclude-choice[data-off="true"] {
  opacity: 0.45;
  cursor: default;
}

.exclude-choice[data-off="true"]:hover {
  background: none;
}

/* A box to type names into, its Add, and the names as chips. */
.exclude-add {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 6px;
}

/* The box and its Add stand the same height. */
.exclude-add .field {
  flex: 1;
  min-width: 0;
  height: 32px;
  padding: 0 10px;
  font-size: 12.5px;
}

.exclude-add .btn {
  flex: 0 0 auto;
  height: 32px;
  padding: 0 12px;
}

.exclude-add .field:disabled {
  opacity: 0.45;
}

.exclude-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 8px;
}

.exclude-chips:empty {
  display: none;
}

.exclude-chip {
  display: inline-flex;
  align-items: center;
  gap: 1px;
  max-width: 100%;
  padding: 2px 2px 2px 10px;
  border-radius: 999px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  font-size: 12px;
  font-weight: 700;
  color: var(--text);
}

.exclude-chip > span {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.exclude-chip > .remove {
  width: 22px;
  height: 22px;
  border-radius: 999px;
}

.exclude-chip > .remove svg {
  width: 12px;
  height: 12px;
}

/* Why nothing can be added right now, and the plan it needs if that is
   the reason. */
.exclude-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-top: 8px;
}

.exclude-foot:empty,
.exclude-foot:not(:has(> :not([hidden]))) {
  display: none;
}

.exclude-foot > .hint {
  margin: 0;
}

/* ---------------- text styles ---------------- */

.section-title {
  font-size: 11.5px;
  font-weight: 800;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--muted);
}

.note {
  margin: 0;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--muted);
}

.hint {
  font-size: 12px;
  line-height: 1.5;
  color: var(--faint);
}

.warn {
  margin: 0;
  font-size: 12px;
  line-height: 1.4;
  color: var(--danger);
  min-height: 1em;
}

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

.tnum {
  font-variant-numeric: lining-nums tabular-nums;
}

/* ---------------- buttons ---------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 11px 18px;
  border-radius: var(--radius-sm);
  font-weight: 700;
  font-size: 13.5px;
  letter-spacing: 0.01em;
  white-space: nowrap;
  color: var(--text);
  background: var(--surface-2);
  border: 1px solid var(--border);
  transition: transform 0.15s var(--ease), box-shadow 0.2s, background 0.2s, border-color 0.2s, color 0.2s, opacity 0.2s;
}

.btn svg {
  width: 17px;
  height: 17px;
}

.btn:hover {
  background: var(--surface-3);
  border-color: var(--border-strong);
}

.btn:active {
  transform: scale(0.97);
}

.btn:disabled {
  opacity: 0.45;
  pointer-events: none;
}

.btn-primary {
  color: var(--on-ink);
  border-color: transparent;
  background: var(--grad);
  box-shadow: var(--glow), inset 0 0 0 1px rgba(255, 255, 255, 0.3);
}

.btn-primary:hover {
  background: var(--grad);
  border-color: transparent;
  box-shadow: var(--glow-strong), inset 0 0 0 1px rgba(255, 255, 255, 0.45);
  transform: translateY(-1px);
}

.btn-primary:active {
  transform: scale(0.97);
}

.btn-wait {
  color: var(--wait-ink);
  border-color: transparent;
  background: var(--grad-wait);
  box-shadow: var(--glow-wait), inset 0 0 0 1px rgba(255, 255, 255, 0.28);
}

.btn-wait:hover {
  background: var(--grad-wait);
  border-color: transparent;
  transform: translateY(-1px);
}

.btn-ghost {
  background: transparent;
  border-color: transparent;
  color: var(--muted);
}

.btn-ghost:hover {
  color: var(--text);
  background: var(--surface);
  border-color: transparent;
}

.btn-outline {
  background: transparent;
}

.btn-danger {
  color: var(--danger);
}

.btn-danger:hover {
  background: color-mix(in srgb, var(--danger) 14%, transparent);
  border-color: color-mix(in srgb, var(--danger) 40%, transparent);
}

.btn-delete {
  color: #fff;
  border-color: transparent;
  background: linear-gradient(135deg, #ff6b87, #e11d48);
  box-shadow: 0 12px 30px -10px #f43f5e, inset 0 0 0 1px rgba(255, 255, 255, 0.16);
}

.btn-delete:hover {
  background: linear-gradient(135deg, #ff6b87, #e11d48);
  border-color: transparent;
}

.btn-sm {
  padding: 7px 12px;
  font-size: 12px;
  border-radius: var(--radius-xs);
}

.btn-lg {
  padding: 15px 28px;
  font-size: 16px;
  border-radius: 18px;
}

.btn-block {
  width: 100%;
}

.btn-icon {
  width: 40px;
  height: 40px;
  padding: 0;
  border-radius: 12px;
}

.btn-icon.sm {
  width: 32px;
  height: 32px;
  border-radius: 10px;
}

.btn-icon.sm svg {
  width: 15px;
  height: 15px;
}

.btn-round {
  width: 60px;
  height: 60px;
  padding: 0;
  border-radius: 50%;
}

.btn-round svg {
  width: 22px;
  height: 22px;
}

.btn-round.big {
  width: 84px;
  height: 84px;
}

.btn-round.big svg {
  width: 30px;
  height: 30px;
}

/* A dashed "add something" button. Reads as a place to put something. */
.btn-dashed {
  width: 100%;
  border-style: dashed;
  border-color: var(--border-strong);
  background: transparent;
  color: var(--muted);
}

.btn-dashed:hover {
  color: var(--text);
  border-style: solid;
  border-color: var(--ac2);
  background: color-mix(in srgb, var(--ac2) 8%, transparent);
  box-shadow: 0 10px 26px -14px var(--ac2);
}

.btn-dashed .plus {
  font-size: 16px;
  font-weight: 800;
  line-height: 1;
  color: var(--accent-text);
}

/* Reads like a link, acts like a button. */
.linky {
  display: inline-block;
  font-size: 12px;
  font-weight: 600;
  color: var(--muted);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: var(--faint);
}

.linky:hover {
  color: var(--accent-text);
  text-decoration-color: currentColor;
}

/* ---------------- switches ---------------- */

/* Four phases. on = blocked (volt), counting / armed = the wait (ember,
   with the countdown inside), off = open (neutral glass). The colour is
   the state; the only text inside is the number. */
.toggle,
.sw {
  position: relative;
  flex: 0 0 auto;
  width: 52px;
  height: 30px;
  border-radius: 30px;
  background: var(--surface-3);
  border: 1px solid var(--border);
  cursor: pointer;
  transition: background 0.25s, box-shadow 0.25s, border-color 0.25s;
  -webkit-tap-highlight-color: transparent;
}

/* Only the off state; the coloured states below must still win. */
:root[data-theme="light"] .toggle:not([data-phase]),
:root[data-theme="light"] .sw:not([data-phase]),
:root[data-theme="light"] .toggle[data-phase="off"],
:root[data-theme="light"] .sw[data-phase="off"] {
  background: rgba(13, 18, 32, 0.16);
}

.toggle .knob,
.sw .knob {
  position: absolute;
  top: 3px;
  left: 3px;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--knob);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.35);
  transition: transform 0.32s var(--spring);
}

.toggle[data-phase="on"],
.sw[data-phase="on"],
.toggle[data-pick="true"],
.sw[data-pick="true"] {
  background: var(--grad-2);
  border-color: transparent;
  box-shadow: 0 8px 22px -6px var(--ac1), inset 0 0 0 1px rgba(255, 255, 255, 0.22);
}

.toggle[data-phase="on"] .knob,
.sw[data-phase="on"] .knob,
.toggle[data-pick="true"] .knob,
.sw[data-pick="true"] .knob {
  transform: translateX(22px);
}

.toggle[data-phase="counting"],
.sw[data-phase="counting"],
.toggle[data-phase="armed"],
.sw[data-phase="armed"] {
  background: var(--grad-wait);
  border-color: transparent;
  box-shadow: var(--glow-wait), inset 0 0 0 1px rgba(255, 255, 255, 0.22);
}

.toggle[data-phase="counting"] .knob,
.sw[data-phase="counting"] .knob,
.toggle[data-phase="armed"] .knob,
.sw[data-phase="armed"] .knob {
  transform: translateX(22px);
}

.toggle[data-phase="counting"],
.sw[data-phase="counting"] {
  cursor: default;
  animation: pulse-wait 1.1s ease-in-out infinite;
}

.toggle[data-phase="armed"],
.sw[data-phase="armed"] {
  animation: pulse-armed 1.6s ease-in-out infinite;
}

@keyframes pulse-wait {
  50% {
    box-shadow: 0 0 0 5px color-mix(in srgb, var(--wait2) 22%, transparent), var(--glow-wait);
  }
}

@keyframes pulse-armed {
  50% {
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--wait1) 28%, transparent), var(--glow-wait);
  }
}

.toggle .word,
.sw .num {
  position: absolute;
  top: 0;
  left: 0;
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  font-size: 11px;
  font-weight: 800;
  color: var(--wait-ink);
  pointer-events: none;
  font-variant-numeric: lining-nums tabular-nums;
}

.toggle:focus-visible,
.sw:focus-visible {
  outline: 2px solid var(--text);
  outline-offset: 2px;
}

/* No slide-in on the very first paint — a page should open already
   showing the saved state, not animate into it. */
body:not(.ready) .toggle,
body:not(.ready) .sw,
body:not(.ready) .knob {
  transition: none !important;
  animation-duration: 0s !important;
}

/* ---------------- fields ---------------- */

.field,
.input {
  width: 100%;
  padding: 11px 14px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  font-size: 13.5px;
  font-weight: 500;
  outline: none;
  transition: border-color 0.2s, box-shadow 0.2s, background 0.2s;
}

.field::placeholder,
.input::placeholder {
  color: var(--faint);
}

.field:focus,
.input:focus {
  border-color: var(--ac2);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--ac2) 22%, transparent);
}

.field.bad,
.input.bad {
  border-color: var(--danger);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--danger) 20%, transparent);
}

.field.mini,
.input.mini {
  width: auto;
  padding: 8px 11px;
  font-size: 13px;
  border-radius: var(--radius-xs);
}

textarea.field,
textarea.input {
  resize: vertical;
  line-height: 1.5;
}

.label-k {
  display: block;
  font-size: 11.5px;
  font-weight: 800;
  color: var(--muted);
  letter-spacing: 0.07em;
  text-transform: uppercase;
  margin-bottom: 7px;
}

/* ---------------- segmented control and chips ---------------- */

.seg {
  display: inline-flex;
  gap: 4px;
  padding: 4px;
  border-radius: var(--radius-sm);
  background: var(--surface-3);
  border: 1px solid var(--border);
}

.seg button {
  padding: 8px 14px;
  border-radius: 10px;
  font-weight: 700;
  font-size: 13px;
  color: var(--muted);
  transition: all 0.2s var(--ease);
}

.seg button:hover {
  color: var(--text);
}

.seg button.on,
.seg button[aria-pressed="true"] {
  color: var(--on-ink);
  background: var(--grad-2);
  box-shadow: 0 6px 16px -6px var(--ac1);
}

.seg button:disabled {
  opacity: 0.5;
}

.chip {
  padding: 9px 12px;
  border-radius: 12px;
  font-weight: 700;
  font-size: 12.5px;
  color: var(--muted);
  background: var(--surface);
  border: 1px solid var(--border);
  transition: all 0.18s var(--ease);
  font-variant-numeric: lining-nums tabular-nums;
  text-align: center;
}

.chip:hover {
  color: var(--text);
  border-color: var(--border-strong);
}

.chip:active {
  transform: scale(0.95);
}

.chip[aria-pressed="true"],
.chip.on {
  color: var(--on-ink);
  background: var(--grad-2);
  border-color: transparent;
  box-shadow: 0 8px 20px -8px var(--ac1);
}

.chip:disabled {
  opacity: 0.5;
}

/* ---------------- the slider ---------------- */

.range,
input[type="range"] {
  --pct: 50%;
  width: 100%;
  height: 6px;
  margin: 0;
  border-radius: 6px;
  appearance: none;
  -webkit-appearance: none;
  outline: none;
  background: linear-gradient(90deg, var(--ac1), var(--ac2) var(--pct), var(--surface-3) var(--pct));
  cursor: pointer;
}

.range::-webkit-slider-thumb,
input[type="range"]::-webkit-slider-thumb {
  appearance: none;
  -webkit-appearance: none;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--knob);
  border: 0;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4), 0 0 0 4px color-mix(in srgb, var(--ac2) 30%, transparent);
  transition: box-shadow 0.2s, transform 0.15s;
}

.range:hover::-webkit-slider-thumb,
input[type="range"]:hover::-webkit-slider-thumb {
  transform: scale(1.08);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4), 0 0 0 6px color-mix(in srgb, var(--ac2) 32%, transparent);
}

.range:disabled,
input[type="range"]:disabled {
  cursor: default;
  background: linear-gradient(90deg, var(--faint), var(--faint) var(--pct), var(--surface-3) var(--pct));
}

.range:disabled::-webkit-slider-thumb,
input[type="range"]:disabled::-webkit-slider-thumb {
  background: var(--muted);
  box-shadow: none;
  transform: none;
}

.scale {
  display: flex;
  justify-content: space-between;
  margin-top: 8px;
  font-size: 11px;
  font-weight: 600;
  color: var(--faint);
  font-variant-numeric: lining-nums tabular-nums;
}

/* ---------------- list rows ---------------- */

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

.list-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 11px 14px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--surface);
  transition: background 0.15s, border-color 0.15s, box-shadow 0.15s;
}

.list-row.clickable {
  cursor: pointer;
}

.list-row.clickable:hover {
  background: var(--surface-2);
  border-color: var(--border-strong);
}

.list-row.sel {
  border-color: var(--ac2);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--ac2) 20%, transparent), 0 10px 30px -14px var(--ac2);
}

.list-row .grow {
  flex: 1;
  min-width: 0;
}

.list-row .name {
  font-weight: 700;
  font-size: 14px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.list-row .desc {
  font-size: 12px;
  color: var(--muted);
  margin-top: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.list-row .radio {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: 2px solid var(--border-strong);
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  transition: all 0.15s;
}

.list-row.sel .radio {
  border-color: transparent;
  background: var(--grad-2);
}

.list-row.sel .radio::after {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--on-ink);
}

.tag {
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 3px 7px;
  border-radius: 7px;
  background: var(--surface-3);
  color: var(--muted);
}

.tag.ai {
  color: var(--on-ink);
  background: var(--grad-2);
}

.play-btn {
  width: 38px;
  height: 38px;
  border-radius: 50%;
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  background: var(--surface-2);
  border: 1px solid var(--border);
  transition: all 0.15s;
}

.play-btn svg {
  width: 15px;
  height: 15px;
}

.play-btn:hover {
  background: var(--surface-3);
  border-color: var(--border-strong);
}

.play-btn.playing {
  color: var(--on-ink);
  background: var(--grad-2);
  border-color: transparent;
  box-shadow: 0 8px 18px -6px var(--ac1);
}

/* The little bin / × on a row. Only red once you are on it. */
.drop,
.remove {
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
  border-radius: 9px;
  display: grid;
  place-items: center;
  color: var(--muted);
  transition: background 0.15s, color 0.15s, opacity 0.15s;
}

.drop svg,
.remove svg {
  width: 15px;
  height: 15px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.drop:hover:not(:disabled),
.remove:hover:not(:disabled) {
  color: var(--danger);
  background: color-mix(in srgb, var(--danger) 14%, transparent);
}

.drop:disabled,
.remove:disabled {
  opacity: 0.25;
}

/* ---------------- sheets ---------------- */

.sheet-backdrop {
  position: fixed;
  inset: 0;
  z-index: 100;
  background: rgba(2, 4, 10, 0.6);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  animation: fade-in 0.2s;
}

:root[data-theme="light"] .sheet-backdrop {
  background: rgba(20, 30, 60, 0.28);
}

.sheet {
  position: relative;
  width: min(560px, 100%);
  max-height: calc(100% - 20px);
  display: flex;
  flex-direction: column;
  background: var(--bg2);
  border: 1px solid var(--border-strong);
  border-radius: 26px;
  box-shadow: 0 40px 100px rgba(0, 0, 0, 0.55), 0 0 80px -30px var(--ac2);
  animation: pop 0.3s cubic-bezier(0.2, 0.9, 0.3, 1.2);
  overflow: hidden;
}

:root[data-theme="light"] .sheet {
  box-shadow: 0 40px 100px rgba(30, 40, 90, 0.25);
}

.sheet-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 20px 22px 6px;
  gap: 12px;
}

.sheet-head h2 {
  margin: 0;
  font-size: 20px;
  font-weight: 800;
  letter-spacing: -0.01em;
}

.sheet-body {
  padding: 12px 22px;
  overflow: auto;
  flex: 1;
}

.sheet-foot {
  display: flex;
  gap: 10px;
  padding: 14px 22px 20px;
  border-top: 1px solid var(--border);
}

.sheet-foot .grow,
.row .grow {
  flex: 1;
}

.row {
  display: flex;
  align-items: center;
  gap: 12px;
}

.row.between {
  justify-content: space-between;
}

/* ---------------- what the plan withholds ---------------- */

/* A locked thing is drawn, not hidden (decided 2026-09-12, with the
   licence): the row or button stays with its name, greyed the way the
   countdown slider is greyed under its day lock, and a small lock with
   the plan word sits where the switch was or beside the button. A click
   on any of it goes to the plan card and nowhere else. A switch that is
   on is never drawn this way — off is never the plan's to withhold. */

/* The greying. On a control it is the whole control; on a row it is the
   name and not the bin beside it, which still works. */
.btn.locked,
.seg.locked,
.chip.locked,
.linky.locked,
input.locked,
.row.locked > .label,
.row.locked > .exclude-stack > .label,
.sound-pick.locked > .ic,
.sound-pick.locked > .grow,
.sound-pick.locked > .chev {
  opacity: 0.45;
}

/* Greyed, and flat: nothing about it should look pressable, but it is
   still there to be clicked, so the pointer stays. */
.btn.locked,
.btn.locked:hover,
.btn.locked:active,
.btn-primary.locked,
.btn-primary.locked:hover,
.sound-pick.locked:hover {
  transform: none;
  box-shadow: none;
  border-color: var(--border);
  cursor: pointer;
}

.btn-primary.locked {
  color: var(--text);
  background: var(--surface-2);
}

.seg.locked button,
.chip.locked {
  cursor: pointer;
}

.seg.locked button:hover {
  color: var(--muted);
}

/* The lock and the plan word. A button, so it can be reached from the
   keyboard; drawn as a small pill in the muted ink, never the accent —
   the accent is for what is blocked, and this is what is not on offer. */
.plan-tag {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  flex: 0 0 auto;
  /* Stays a pill whatever it lands in: beside a button in a row, or on
     its own line under one that fills the width. */
  align-self: center;
  padding: 5px 10px;
  border-radius: 999px;
  font-size: 10.5px;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--muted);
  background: var(--surface-2);
  border: 1px solid var(--border);
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s, background 0.15s;
}

.plan-tag svg {
  width: 11px;
  height: 11px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.plan-tag:hover {
  color: var(--text);
  border-color: var(--border-strong);
  background: var(--surface-3);
}

.plan-tag:focus-visible {
  outline: 2px solid var(--text);
  outline-offset: 2px;
}

/* One that sits inside a wide button — the ringtone picker — stays a
   pill at the right, before the chevron. */
.sound-pick > .plan-tag {
  margin-left: auto;
}

/* ---------------- toasts ---------------- */

#toast-root {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 26px;
  z-index: 300;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  pointer-events: none;
}

.toast {
  pointer-events: auto;
  padding: 11px 16px;
  border-radius: var(--radius-sm);
  font-weight: 600;
  font-size: 13px;
  background: var(--bg2);
  border: 1px solid var(--border-strong);
  box-shadow: var(--shadow);
  animation: pop 0.25s;
  max-width: min(520px, 90vw);
  text-align: center;
}

.toast.err {
  border-color: var(--danger);
  color: var(--danger);
}

.toast.ok {
  border-color: var(--ac2);
}

/* ---------------- progress ---------------- */

.progress {
  height: 6px;
  border-radius: 6px;
  background: var(--surface-3);
  overflow: hidden;
  margin-top: 12px;
}

.progress i {
  display: block;
  height: 100%;
  width: 40%;
  border-radius: 6px;
  background: var(--grad-2);
  animation: slide 1.1s ease-in-out infinite;
}

.spin {
  width: 20px;
  height: 20px;
  border: 3px solid var(--surface-3);
  border-top-color: var(--ac2);
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}

.empty {
  padding: 40px 20px;
  text-align: center;
  color: var(--muted);
  font-size: 13.5px;
}

.empty .orb {
  width: 72px;
  height: 72px;
  margin: 0 auto 14px;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, rgba(255, 255, 255, 0.6), transparent 42%), var(--grad);
  box-shadow: 0 18px 50px -10px var(--ac1);
  animation: float 4s ease-in-out infinite;
}

/* ---------------- motion ---------------- */

@keyframes fade-in {
  from {
    opacity: 0;
  }
}

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

@keyframes pop {
  from {
    opacity: 0;
    transform: translateY(14px) scale(0.96);
  }
}

@keyframes float {
  50% {
    transform: translateY(-8px);
  }
}

@keyframes slide {
  from {
    transform: translateX(-120%);
  }
  to {
    transform: translateX(280%);
  }
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

@keyframes pulse {
  50% {
    transform: scale(1.06);
  }
}

@keyframes ripple {
  from {
    transform: scale(1);
    opacity: 0.9;
  }
  to {
    transform: scale(1.6);
    opacity: 0;
  }
}

/* Cards arrive one after another rather than all at once. */
.rise {
  animation: fade-up 0.4s var(--ease) both;
}

.rise:nth-child(2) { animation-delay: 0.04s; }
.rise:nth-child(3) { animation-delay: 0.08s; }
.rise:nth-child(4) { animation-delay: 0.12s; }
.rise:nth-child(5) { animation-delay: 0.16s; }
.rise:nth-child(6) { animation-delay: 0.2s; }
.rise:nth-child(7) { animation-delay: 0.24s; }
.rise:nth-child(8) { animation-delay: 0.28s; }
.rise:nth-child(9) { animation-delay: 0.32s; }
.rise:nth-child(n + 10) { animation-delay: 0.36s; }

@media (prefers-reduced-motion: reduce) {
  .ambient i,
  .rise,
  .toggle,
  .sw,
  .fold-body,
  .empty .orb {
    animation: none !important;
  }
}

/* ---------------- utilities ---------------- */

.hidden {
  display: none !important;
}

.center {
  text-align: center;
}

.mt {
  margin-top: 12px;
}

.drag {
  -webkit-app-region: drag;
}

.nodrag {
  -webkit-app-region: no-drag;
}
