/* =====================================================================
   Polka 2.0 - the whole visual system, in one file.

   No framework, no build step, no web fonts. On shared hosting every
   external request is a round trip somebody waits for, and the system
   font stack is already on the device.

   The palette is deliberately cool and near-neutral. v1 was saffron and
   espresso, which read as a restaurant menu rather than an operations
   console, and which made every warning colour fight the background.
   Here the interface is grey and the only saturated colours in it mean
   something: blue is you, amber is attention, red is wrong, green is done.

   Every colour is a token defined on :root. Dark mode redefines the
   tokens and nothing else, so there is exactly one place a colour lives.
   ===================================================================== */

:root {
  color-scheme: light dark;

  /* Surfaces, from furthest back to nearest front.
     Four steps, not two: a card on a page on a ground reads as depth, and a
     single grey behind everything is what makes an interface look unfinished. */
  --bg:            #eceef5;
  --surface:       #ffffff;
  --surface-2:     #f5f6fb;
  --surface-sunk:  #e5e8f2;

  /* Ink. Four weights is enough for any interface. */
  --ink:           #14161f;
  --ink-2:         #474d5e;
  --ink-3:         #6a7183;
  --ink-faint:     #969cae;

  --line:          #e1e4ee;
  --line-strong:   #cbd0df;

  /* The brand triad. The three that make the mark, used deliberately:
     indigo carries the interface, teal marks anything settled or measured,
     amber marks anything waiting. They are never decorative. */
  --accent:        #4f46e5;
  --accent-ink:    #ffffff;
  --accent-soft:   #eef0fe;
  --accent-line:   #c9cbf8;
  --accent-deep:   #3730a3;

  --teal:          #0d9488;
  --teal-soft:     #e6f6f4;
  --teal-line:     #a8ded7;

  --amber:         #b45309;
  --amber-soft:    #fdf4e3;
  --amber-line:    #f2d9a8;

  /* Meaning. Never decoration. */
  --ok:            #0d9488;
  --ok-soft:       #e6f6f4;
  --ok-line:       #a8ded7;

  --warn:          #b45309;
  --warn-soft:     #fdf4e3;
  --warn-line:     #f2d9a8;

  --bad:           #be123c;
  --bad-soft:      #fdeaef;
  --bad-line:      #f6c3d1;

  --radius:        10px;
  --radius-sm:     7px;
  --radius-lg:     14px;

  /* Layered, not single. One shadow reads as a sticker; a tight contact
     shadow under a wider ambient one reads as an object on a surface. */
  --shadow-1:      0 1px 1px rgba(16, 20, 27, .04), 0 2px 6px rgba(16, 20, 27, .05);
  --shadow-2:      0 1px 2px rgba(16, 20, 27, .05), 0 8px 24px rgba(16, 20, 27, .07);
  --shadow-pop:    0 2px 4px rgba(16, 20, 27, .08), 0 20px 48px rgba(16, 20, 27, .18);

  /* A 4px rhythm. Every margin in the app is one of these. */
  --s1: 4px;  --s2: 8px;  --s3: 12px; --s4: 16px;
  --s5: 24px; --s6: 32px; --s7: 48px; --s8: 64px;

  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
          "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

  --nav-w: 220px;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg:           #0d1016;
    --surface:      #151a22;
    --surface-2:    #1b212b;
    --surface-sunk: #10151c;

    --ink:          #eef1f6;
    --ink-2:        #b3bcc9;
    --ink-3:        #8b95a3;
    --ink-faint:    #69727f;

    --line:         #262d38;
    --line-strong:  #38404d;

    --accent:       #8b85ff;
    --accent-ink:   #12121c;
    --accent-soft:  #1e1d3a;
    --accent-line:  #3a3670;
    --accent-deep:  #a5a0ff;

    --teal:         #2dd4bf;
    --teal-soft:    #102a28;
    --teal-line:    #1d4d47;

    --amber:        #f0b458;
    --amber-soft:   #2b2113;
    --amber-line:   #4f3d1d;

    --ok:           #2dd4bf;
    --ok-soft:      #102a28;
    --ok-line:      #1d4d47;

    --warn:         #f0b458;
    --warn-soft:    #2b2113;
    --warn-line:    #4f3d1d;

    --bad:          #fb7185;
    --bad-soft:     #2c161c;
    --bad-line:     #58272f;

    --shadow-1:     0 1px 2px rgba(0, 0, 0, .4);
    --shadow-2:     0 4px 16px rgba(0, 0, 0, .45);
    --shadow-pop:   0 12px 32px rgba(0, 0, 0, .55);
  }
}

:root[data-theme="dark"] {
  --bg:           #0d1016;
  --surface:      #151a22;
  --surface-2:    #1b212b;
  --surface-sunk: #10151c;
  --ink:          #eef1f6;
  --ink-2:        #b3bcc9;
  --ink-3:        #8b95a3;
  --ink-faint:    #69727f;
  --line:         #262d38;
  --line-strong:  #38404d;
  --accent:       #8b85ff;
  --accent-ink:   #12121c;
  --accent-soft:  #1e1d3a;
  --accent-line:  #3a3670;
  --accent-deep:  #a5a0ff;
  --teal:         #2dd4bf;
  --teal-soft:    #102a28;
  --teal-line:    #1d4d47;
  --amber:        #f0b458;
  --amber-soft:   #2b2113;
  --amber-line:   #4f3d1d;
  --ok:           #2dd4bf;
  --ok-soft:      #102a28;
  --ok-line:      #1d4d47;
  --warn:         #f0b458;
  --warn-soft:    #2b2113;
  --warn-line:    #4f3d1d;
  --bad:          #fb7185;
  --bad-soft:     #2c161c;
  --bad-line:     #58272f;
  --shadow-1:     0 1px 2px rgba(0, 0, 0, .4);
  --shadow-2:     0 4px 16px rgba(0, 0, 0, .45);
  --shadow-pop:   0 12px 32px rgba(0, 0, 0, .55);
}

/* --------------------------------------------------------------- reset */

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

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  font-family: var(--font);
  font-size: 15px;
  line-height: 1.55;
  color: var(--ink);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4, p, figure, ul, ol { margin: 0; }
ul, ol { padding: 0; list-style: none; }
img, svg { max-width: 100%; display: block; }

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

/* One focus style everywhere. Removing it is never the answer. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

/* Motion is meaning, so it stops when somebody asks it to. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ---------------------------------------------------------------- shell */

.shell { display: flex; min-height: 100dvh; max-width: 100%; }
/* Nothing may widen the page. Wide content scrolls inside its own
   container (.table-wrap), never by moving the whole layout. */
body { overflow-x: hidden; }

.nav {
  width: var(--nav-w);
  flex: 0 0 var(--nav-w);
  background: var(--surface-2);
  border-right: 1px solid var(--line);
  padding: var(--s4) var(--s3);
  display: flex;
  flex-direction: column;
  gap: var(--s5);
  position: sticky;
  top: 0;
  height: 100dvh;
  overflow-y: auto;
}

.brand {
  display: flex;
  align-items: center;
  gap: var(--s2);
  padding: var(--s1) var(--s2);
  font-weight: 700;
  font-size: 17px;
  letter-spacing: -.02em;
  color: var(--ink);
}
.brand:hover { text-decoration: none; }
.brand .dot {
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--accent);
  flex: 0 0 auto;
}

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

.nav-list a {
  display: flex;
  align-items: center;
  gap: var(--s3);
  /* 44px tall: a touch target, not a text link. */
  min-height: 44px;
  padding: 0 var(--s3);
  border-radius: var(--radius-sm);
  color: var(--ink-2);
  font-weight: 500;
  font-size: 14.5px;
  transition: background .15s ease, color .15s ease;
}
.nav-list a:hover { background: var(--surface); color: var(--ink); text-decoration: none; }
.nav-list a[aria-current="page"] {
  background: var(--surface);
  color: var(--accent);
  font-weight: 650;
  box-shadow: var(--shadow-1);
}
.nav-list svg { width: 18px; height: 18px; flex: 0 0 18px; stroke-width: 1.75; }

.nav-list .count {
  margin-left: auto;
  font-size: 12px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  background: var(--bad);
  color: #fff;
  border-radius: 999px;
  min-width: 20px;
  height: 20px;
  display: grid;
  place-items: center;
  padding: 0 6px;
}

.nav-foot { margin-top: auto; display: flex; flex-direction: column; gap: var(--s2); }

.main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }

.topbar {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: var(--s4) var(--s5);
  border-bottom: 1px solid var(--line);
  background: var(--surface);
  position: sticky;
  top: 0;
  z-index: 20;
}
.topbar h1 {
  font-size: 23px;
  font-weight: 700;
  letter-spacing: -.028em;
  line-height: 1.2;
  margin-right: auto;
}
.topbar .sub {
  display: block;
  font-size: 13px;
  font-weight: 400;
  color: var(--ink-3);
  letter-spacing: 0;
}

.page { padding: var(--s6) var(--s5) var(--s8); max-width: 1180px; width: 100%; }

/* ---------------------------------------------------------- workspace pill */

.ws-switch { position: relative; }

.ws-current {
  display: flex;
  align-items: center;
  gap: var(--s2);
  width: 100%;
  min-height: 44px;
  padding: var(--s2) var(--s3);
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  box-shadow: var(--shadow-1);
  color: var(--ink);
  font: inherit;
  font-weight: 600;
  font-size: 14px;
  cursor: pointer;
  text-align: left;
}
.ws-current:hover { border-color: var(--line-strong); background: var(--surface); }
.ws-current .chev { margin-left: auto; color: var(--ink-faint); }

.ws-menu {
  position: absolute;
  left: 0; right: 0; top: calc(100% + 6px);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-pop);
  padding: var(--s1);
  z-index: 60;
}
.ws-menu a {
  display: flex;
  align-items: center;
  gap: var(--s2);
  min-height: 40px;
  padding: 0 var(--s2);
  border-radius: var(--radius-sm);
  color: var(--ink);
  font-size: 14px;
}
.ws-menu a:hover { background: var(--surface-2); text-decoration: none; }
.ws-menu a[aria-current="true"] { color: var(--accent); font-weight: 600; }

/* A paused workspace looks paused everywhere it appears. */
.ws-paused { color: var(--ink-faint); }
.ws-paused::after {
  content: "paused";
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--warn);
  background: var(--warn-soft);
  border: 1px solid var(--warn-line);
  border-radius: 999px;
  padding: 1px 7px;
  margin-left: auto;
}

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

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-1);
}

.card + .card { margin-top: var(--s5); }

.card-head {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: var(--s4) var(--s5);
  border-bottom: 1px solid var(--line);
  /* A hair of tint separates the header from the body without a second
     border. Flat-on-flat is the specific thing that read as unfinished. */
  background: linear-gradient(var(--surface-2), var(--surface));
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
}
.card-head h2 {
  font-size: 16.5px;
  font-weight: 650;
  letter-spacing: -.015em;
  margin-right: auto;
}
.card-head .hint { font-size: 13px; color: var(--ink-3); font-weight: 400; }

.card-body { padding: var(--s5); }
.card-body > * + * { margin-top: var(--s5); }

/* A section that is not finished says so on its own edge. */
.card.is-incomplete { border-color: var(--bad-line); }
.card.is-incomplete .card-head { background: var(--bad-soft); }
.card.is-attention  { border-color: var(--warn-line); }
.card.is-attention .card-head { background: var(--warn-soft); }

/* ------------------------------------------------------------- the grid */

.grid { display: grid; gap: var(--s4); }
.grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }

@media (max-width: 900px) {
  .grid-3, .grid-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 620px) {
  .grid-2, .grid-3, .grid-4 { grid-template-columns: minmax(0, 1fr); }
}

/* ---------------------------------------------------------------- stats */

.stat {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--s4) var(--s4) var(--s4) var(--s5);
  box-shadow: var(--shadow-1);
  position: relative;
  overflow: hidden;
}
/* A colour rail rather than a coloured box: the number stays the loudest
   thing on the tile, which is the only reason the tile exists. */
.stat::before {
  content: "";
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 3px;
  background: var(--accent);
  opacity: .85;
}
.stat-ok::before   { background: var(--ok); }
.stat-warn::before { background: var(--warn); }
.stat-bad::before  { background: var(--bad); }
.stat .k {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.stat .v {
  font-size: 34px;
  font-weight: 700;
  letter-spacing: -.04em;
  line-height: 1.05;
  font-variant-numeric: tabular-nums;
  margin-top: 6px;
}
.stat .n { font-size: 13px; color: var(--ink-3); margin-top: 2px; }

/* --------------------------------------------------------------- alerts */

.alert {
  display: flex;
  align-items: flex-start;
  gap: var(--s3);
  padding: var(--s3) var(--s4);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  font-size: 14px;
}
.alert svg { width: 18px; height: 18px; flex: 0 0 18px; margin-top: 2px; }
.alert .body { flex: 1 1 auto; min-width: 0; }
.alert .body strong { display: block; font-weight: 600; }
.alert .body span { color: var(--ink-3); font-size: 13.5px; }

.alert-bad  { background: var(--bad-soft);  border-color: var(--bad-line);  color: var(--bad); }
.alert-warn { background: var(--warn-soft); border-color: var(--warn-line); color: var(--warn); }
.alert-ok   { background: var(--ok-soft);   border-color: var(--ok-line);   color: var(--ok); }
.alert-bad .body strong, .alert-warn .body strong, .alert-ok .body strong { color: inherit; }
.alert-bad .body span, .alert-warn .body span, .alert-ok .body span { color: inherit; opacity: .8; }

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

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  min-height: 40px;
  padding: 0 var(--s4);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  font-size: 14px;
  font-weight: 550;
  cursor: pointer;
  box-shadow: var(--shadow-1);
  white-space: nowrap;
  transition: background .13s ease, border-color .13s ease, transform .08s ease;
  touch-action: manipulation;
}
.btn:hover { background: var(--surface-2); text-decoration: none; }
.btn:active { transform: scale(.98); }
.btn svg { width: 16px; height: 16px; }

.btn-primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
  box-shadow: 0 1px 2px rgba(16, 20, 27, .12), inset 0 1px 0 rgba(255, 255, 255, .16);
}
.btn-primary:hover { background: var(--accent); filter: brightness(1.08); }

.btn-danger { color: var(--bad); border-color: var(--bad-line); }
.btn-danger:hover { background: var(--bad-soft); }

.btn-quiet { border-color: transparent; background: transparent; color: var(--ink-2); }
.btn-quiet:hover { background: var(--surface-2); color: var(--ink); }

.btn-sm { min-height: 32px; padding: 0 var(--s3); font-size: 13px; }

.btn[disabled], .btn[aria-disabled="true"] {
  opacity: .5;
  cursor: not-allowed;
  pointer-events: none;
}

.btn.is-busy { position: relative; color: transparent; }
.btn.is-busy::after {
  content: "";
  position: absolute;
  inset: 0;
  margin: auto;
  width: 15px; height: 15px;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  color: var(--ink-2);
  animation: spin .6s linear infinite;
}
.btn-primary.is-busy::after { color: var(--accent-ink); }
@keyframes spin { to { transform: rotate(360deg); } }

.btn-row { display: flex; flex-wrap: wrap; gap: var(--s2); }

/* ---------------------------------------------------------------- forms */

.field { display: flex; flex-direction: column; gap: var(--s2); }

/* The flex gap owns all vertical rhythm. Labels and inputs carry no margin
   of their own, so spacing cannot compound the way it did in v1. */
.field > * { margin: 0; }

.field label {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--ink-2);
}
.field .help { font-size: 13px; color: var(--ink-3); }
.field .err  { font-size: 13px; color: var(--bad); font-weight: 500; }

input[type="text"], input[type="email"], input[type="password"], input[type="number"],
input[type="time"], input[type="date"], input[type="search"], input[type="tel"],
select, textarea {
  width: 100%;
  min-height: 42px;
  padding: 9px var(--s3);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  /* 16px on small screens or iOS zooms the page on focus. */
  font-size: 15px;
  transition: border-color .13s ease, box-shadow .13s ease;
}
textarea { min-height: 88px; resize: vertical; line-height: 1.5; }

input:hover, select:hover, textarea:hover { border-color: var(--ink-faint); }
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
input::placeholder, textarea::placeholder { color: var(--ink-faint); }

input[aria-invalid="true"], select[aria-invalid="true"] {
  border-color: var(--bad);
  box-shadow: 0 0 0 3px var(--bad-soft);
}

/* A select must not look like a text box; it has a menu. */
select {
  appearance: none;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%236b7482' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--s3) center;
  padding-right: var(--s6);
}
/* The chevron is a data URI, so its colour cannot be a token. It is the one
   place in this file a colour is written twice, and it is written here. */
:root[data-theme="dark"] select {
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%238b95a3' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) select {
    background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%238b95a3' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  }
}

input[type="time"], input[type="date"] { font-variant-numeric: tabular-nums; }

/* Checkbox and radio, drawn rather than borrowed. */
.check {
  display: flex;
  align-items: flex-start;
  gap: var(--s3);
  min-height: 44px;
  padding: var(--s2) 0;
  cursor: pointer;
}
.check input {
  appearance: none;
  width: 20px; height: 20px;
  flex: 0 0 20px;
  margin-top: 2px;
  border: 1.5px solid var(--line-strong);
  border-radius: 5px;
  background: var(--surface);
  cursor: pointer;
  transition: background .13s ease, border-color .13s ease;
}
.check input[type="radio"] { border-radius: 50%; }
.check input:checked {
  background: var(--accent) url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E") center/13px no-repeat;
  border-color: var(--accent);
}
.check input[type="radio"]:checked {
  background-image: none;
  box-shadow: inset 0 0 0 4px var(--surface);
}
.check input:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.check .t { font-size: 14.5px; line-height: 1.45; }
.check .t small { display: block; color: var(--ink-3); font-size: 13px; }

/* The switch. Big enough to hit, obvious enough to read at a glance. */
.switch {
  display: flex;
  align-items: center;
  gap: var(--s3);
  min-height: 44px;
  cursor: pointer;
}
.switch input {
  appearance: none;
  width: 44px; height: 26px;
  flex: 0 0 44px;
  border-radius: 999px;
  background: var(--line-strong);
  position: relative;
  cursor: pointer;
  transition: background .18s ease;
}
.switch input::after {
  content: "";
  position: absolute;
  top: 3px; left: 3px;
  width: 20px; height: 20px;
  border-radius: 50%;
  background: #fff;
  box-shadow: var(--shadow-1);
  transition: transform .18s cubic-bezier(.2, .8, .3, 1);
}
.switch input {
  /* An off switch needs an edge of its own, or on a dark surface the track
     disappears and there is nothing to read as "off" either. */
  border: 1px solid var(--line-strong);
  box-sizing: border-box;
}
.switch input:checked {
  background: var(--ok);
  border-color: var(--ok);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--ok) 22%, transparent);
}
.switch input:checked::after { transform: translateX(18px); }
.switch input:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.switch .t { font-size: 14.5px; }
.switch .t small { display: block; color: var(--ink-3); font-size: 13px; }

/* Weekday picker: seven targets, each one big enough for a thumb. */
.days { display: flex; flex-wrap: wrap; gap: var(--s2); }
.days label {
  position: relative;
  display: grid;
  place-items: center;
  width: 46px; height: 44px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-2);
  cursor: pointer;
  user-select: none;
  transition: background .13s ease, border-color .13s ease, color .13s ease;
}
.days input { position: absolute; opacity: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; }
.days label:hover { border-color: var(--ink-faint); }
.days label:has(input:checked) {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
}
.days label:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Segmented control, for two to four exclusive choices. */
.seg { display: inline-flex; background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--radius-sm); padding: 3px; }
.seg label {
  position: relative;
  display: flex;
  align-items: center;
  min-height: 34px;
  padding: 0 var(--s3);
  border-radius: 5px;
  font-size: 13.5px;
  font-weight: 550;
  color: var(--ink-2);
  cursor: pointer;
  white-space: nowrap;
}
.seg input { position: absolute; opacity: 0; inset: 0; cursor: pointer; }
.seg label:has(input:checked) { background: var(--surface); color: var(--ink); box-shadow: var(--shadow-1); }

/* ---------------------------------------------------------------- tables */

.table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }

table.data { width: 100%; border-collapse: collapse; font-size: 14px; }
table.data th, table.data td {
  text-align: left;
  padding: var(--s3) var(--s4);
  border-bottom: 1px solid var(--line);
  vertical-align: middle;
}
table.data th {
  font-size: 11.5px;
  font-weight: 650;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink-3);
  background: var(--surface-2);
  border-bottom-color: var(--line-strong);
  white-space: nowrap;
  position: sticky;
  top: 0;
  z-index: 1;
}
table.data tbody tr:last-child td { border-bottom: 0; }
table.data tbody tr { transition: background .1s ease; }
table.data tbody tr:hover { background: var(--surface-2); }
/* A header must align the same way as the column under it, or the whole
   table reads as broken. These were td-only, so every numeric header sat
   left while its numbers sat right. */
table.data td.num,
table.data th.num { text-align: right; }
table.data td.num { font-variant-numeric: tabular-nums; }

table.data td.act,
table.data th.act { text-align: right; white-space: nowrap; }
table.data td.act { width: 1%; }

/* Numbers line up on the decimal point, which is the only reason a column
   of figures is quicker to read than a list. */
table.data td.num,
table.data .nums { font-variant-numeric: tabular-nums; }
table.data .primary { font-weight: 600; }
table.data .second { color: var(--ink-3); font-size: 13px; }

/* --------------------------------------------------------------- badges */

.tag {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 22px;
  padding: 0 9px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .01em;
  background: var(--surface-2);
  border: 1px solid var(--line);
  color: var(--ink-2);
  white-space: nowrap;
}
.tag-ok   { background: var(--ok-soft);   border-color: var(--ok-line);   color: var(--ok); }
.tag-warn { background: var(--warn-soft); border-color: var(--warn-line); color: var(--warn); }
.tag-bad  { background: var(--bad-soft);  border-color: var(--bad-line);  color: var(--bad); }
.tag-info { background: var(--accent-soft); border-color: var(--accent-line); color: var(--accent); }

/* Status is never colour alone: every one of these carries its own word. */
.tag::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.tag-plain::before { display: none; }

/* ----------------------------------------------------------- empty state */

.empty {
  text-align: center;
  padding: var(--s7) var(--s5);
  color: var(--ink-3);
}
.empty svg { width: 34px; height: 34px; margin: 0 auto var(--s3); color: var(--ink-faint); }
.empty strong { display: block; color: var(--ink); font-size: 15px; font-weight: 600; margin-bottom: var(--s1); }
.empty p { font-size: 14px; max-width: 42ch; margin: 0 auto; }
.empty .btn { margin-top: var(--s4); }

/* ---------------------------------------------------------------- toast */

.toasts {
  position: fixed;
  right: var(--s4);
  bottom: var(--s4);
  z-index: 200;
  display: flex;
  flex-direction: column;
  gap: var(--s2);
  pointer-events: none;
}
.toast {
  display: flex;
  align-items: center;
  gap: var(--s3);
  min-width: 240px;
  max-width: 380px;
  padding: var(--s3) var(--s4);
  border-radius: var(--radius);
  background: var(--ink);
  color: var(--bg);
  font-size: 14px;
  font-weight: 500;
  box-shadow: var(--shadow-pop);
  pointer-events: auto;
  animation: rise .22s cubic-bezier(.2, .8, .3, 1);
}
.toast-bad { background: var(--bad); color: #fff; }
.toast-ok  { background: var(--ok);  color: #fff; }
@keyframes rise { from { opacity: 0; transform: translateY(8px); } }

/* ---------------------------------------------------------------- modal */

dialog.sheet {
  width: min(560px, calc(100vw - 32px));
  max-height: min(88dvh, 780px);
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  color: var(--ink);
  box-shadow: var(--shadow-pop);
  overflow: hidden;
  /* A column, so the footer can be pinned to the bottom instead of being
     pushed past it. */
  display: flex;
  flex-direction: column;
}

/* Dialogs are display:none until opened, and the rule above would override
   that and leave every sheet on the page permanently visible. */
dialog.sheet:not([open]) { display: none; }

/* The form is the real child, so it has to be the column that flexes.
   Without this the body's max-height plus the header plus the footer could
   exceed the dialog's own max-height, and overflow:hidden cut the footer
   off - which is how the Save button disappeared. */
dialog.sheet > form {
  display: flex;
  flex-direction: column;
  min-height: 0;
  max-height: 100%;
  flex: 1 1 auto;
}
dialog.sheet::backdrop { background: rgba(10, 13, 18, .55); backdrop-filter: blur(2px); }
dialog.sheet .sheet-head {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: var(--s4) var(--s5);
  border-bottom: 1px solid var(--line);
}
dialog.sheet .sheet-head h2 { font-size: 16px; font-weight: 650; margin-right: auto; }
dialog.sheet .sheet-head { flex: 0 0 auto; }
dialog.sheet .sheet-foot { flex: 0 0 auto; }

/* Takes whatever is left and scrolls inside it. No fixed max-height: the
   height it may have depends on the header and footer, and only the browser
   knows those. min-height:0 is what allows a flex child to shrink below its
   content and actually scroll. */
dialog.sheet .sheet-body {
  padding: var(--s5);
  overflow-y: auto;
  overscroll-behavior: contain;
  flex: 1 1 auto;
  min-height: 0;
}
dialog.sheet .sheet-body > * + * { margin-top: var(--s4); }
dialog.sheet .sheet-foot {
  display: flex;
  justify-content: flex-end;
  gap: var(--s2);
  padding: var(--s4) var(--s5);
  border-top: 1px solid var(--line);
  background: var(--surface-2);
  /* Below the notch and the home bar on a phone. */
  padding-bottom: max(var(--s4), env(safe-area-inset-bottom));
}

/* --------------------------------------------------------------- details */

details.more {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface-2);
}
details.more > summary {
  display: flex;
  align-items: center;
  gap: var(--s2);
  min-height: 44px;
  padding: 0 var(--s4);
  font-size: 13.5px;
  font-weight: 600;
  color: var(--ink-2);
  cursor: pointer;
  list-style: none;
}
details.more > summary::-webkit-details-marker { display: none; }
details.more > summary::after {
  content: "";
  margin-left: auto;
  width: 8px; height: 8px;
  border-right: 1.75px solid currentColor;
  border-bottom: 1.75px solid currentColor;
  transform: rotate(45deg) translateY(-2px);
  transition: transform .18s ease;
}
details.more[open] > summary::after { transform: rotate(-135deg) translateY(-2px); }
details.more .inner { padding: 0 var(--s4) var(--s4); }
details.more .inner > * + * { margin-top: var(--s4); }

/* ----------------------------------------------------------------- misc */

.row { display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap; }
.row-end { margin-left: auto; }
.stack { display: flex; flex-direction: column; gap: var(--s4); }
.stack-tight { display: flex; flex-direction: column; gap: var(--s2); }

.muted { color: var(--ink-3); }
.small { font-size: 13px; }
.mono  { font-family: var(--mono); font-size: 13px; }
.nums  { font-variant-numeric: tabular-nums; }

.divider { height: 1px; background: var(--line); border: 0; }

.zone {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  font-weight: 600;
  color: var(--ink-3);
}

.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.skip {
  position: absolute;
  left: var(--s3); top: -60px;
  z-index: 300;
  padding: var(--s2) var(--s4);
  background: var(--accent);
  color: var(--accent-ink);
  border-radius: var(--radius-sm);
  font-weight: 600;
  transition: top .15s ease;
}
.skip:focus { top: var(--s3); text-decoration: none; }

/* ------------------------------------------------------------ responsive */

@media (max-width: 860px) {
  /* The sidebar becomes a drawer rather than a horizontal strip. The strip
     scrolled sideways, hid whatever did not fit, and hid the labels of what
     did - so the only way to find a section was to remember where it was. */
  .nav {
    position: fixed;
    inset: 0 auto 0 0;
    z-index: 70;
    width: min(84vw, 300px);
    /* Stated again, and opaque. A drawer that inherits a translucent
       background from a style preset shows the dimmed page through itself
       and reads as blurred rather than as a panel. */
    background: var(--surface-2);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    border-right: 1px solid var(--line-strong);
    transform: translateX(-100%);
    transition: transform .22s cubic-bezier(.32, .72, 0, 1);
    box-shadow: 0 0 60px rgba(0, 0, 0, .6);
    will-change: transform;
  }
  body.nav-open .nav { transform: none; }
  .nav-list a span.label { display: inline; }

  .page { padding: var(--s4); }
  .topbar { padding: var(--s3) var(--s4); }
  .card-body, .card-head { padding: var(--s4); }
}

/* Printing a shift plan is a real thing people do. */
@media print {
  .nav, .topbar .btn, .toasts { display: none !important; }
  body { background: #fff; }
  .card { box-shadow: none; border-color: #ccc; break-inside: avoid; }
}

/* ------------------------------------------------------------ sign in
   The first screen anybody sees, and until now the only one. It was a
   small card floating in grey, which reads as unfinished however correct
   the markup is. A screen needs somewhere for the eye to land. */

.signin {
  min-height: 100dvh;
  display: grid;
  place-items: center;
  padding: var(--s5);
  /* Two very soft washes of the accent, well under the text contrast
     threshold. Enough for the page to have a top and a bottom. */
  background:
    radial-gradient(90ch 60ch at 15% -10%, var(--accent-soft), transparent 60%),
    radial-gradient(70ch 50ch at 100% 110%, var(--surface-sunk), transparent 65%),
    var(--bg);
}

.signin-inner { width: min(430px, 100%); }

.signin-brand {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  margin-bottom: var(--s6);
}
.signin-brand .mark {
  width: 34px; height: 34px;
  border-radius: 10px;
  background: var(--accent);
  display: grid;
  place-items: center;
  box-shadow: 0 4px 14px color-mix(in srgb, var(--accent) 40%, transparent);
}
.signin-brand .mark span {
  width: 11px; height: 11px;
  border-radius: 50%;
  background: var(--accent-ink);
}

.signin .card { box-shadow: var(--shadow-2); }

.signin-head {
  padding: var(--s5) var(--s5) 0;
}
.signin-head h1 {
  font-size: 21px;
  font-weight: 680;
  letter-spacing: -.025em;
}
.signin-head p {
  margin-top: 6px;
  font-size: 14px;
  color: var(--ink-3);
}

.signin-body { padding: var(--s5); }
.signin-body > * + * { margin-top: var(--s4); }

.signin-foot {
  margin-top: var(--s5);
  text-align: center;
  font-size: 13px;
  color: var(--ink-faint);
}

/* Full-width and taller: this is the one button on the screen. */
.signin .btn-primary {
  width: 100%;
  min-height: 46px;
  font-size: 15px;
  font-weight: 600;
  margin-top: var(--s2);
}

/* ------------------------------------------------------------- icons
   Every icon() carries .ico. An inline SVG with no width fills its
   container, so a single icon dropped somewhere without a size rule
   renders full-page-wide - which is what happened to the workspace
   chevron and to every zone label in Settings. One rule, everywhere. */

.ico {
  width: 18px;
  height: 18px;
  flex: 0 0 18px;
  stroke-width: 1.8;
}
.btn .ico       { width: 16px; height: 16px; flex-basis: 16px; }
.btn-sm .ico    { width: 15px; height: 15px; flex-basis: 15px; }
.zone .ico      { width: 13px; height: 13px; flex-basis: 13px; }
.tag .ico       { width: 12px; height: 12px; flex-basis: 12px; }
.empty .ico     { width: 34px; height: 34px; flex-basis: 34px; }
.ws-current .ico, .ws-current .chev { width: 16px; height: 16px; flex-basis: 16px; }

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

.polka-mark { display: block; flex: 0 0 auto; }

.brand {
  gap: 9px;
  padding-left: var(--s2);
}
.brand .polka-mark { width: 26px; height: 26px; }

.signin-brand .polka-mark { width: 76px; height: 76px; }

/* -------------------------------------------- sidebar foot alignment
   The theme and sign-out buttons sit under the navigation, so they line
   up with it: same height, same padding, same left edge. Centred text
   under left-aligned items is the specific thing that looked wrong. */

.nav-foot {
  margin-top: auto;
  padding-top: var(--s3);
  border-top: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.nav-foot .btn {
  justify-content: flex-start;
  gap: var(--s3);
  min-height: 40px;
  padding: 0 var(--s3);
  font-size: 14px;
  font-weight: 500;
  color: var(--ink-3);
  background: transparent;
  border-color: transparent;
  box-shadow: none;
}
.nav-foot .btn:hover { background: var(--surface); color: var(--ink); }
.nav-foot .btn .ico { width: 18px; height: 18px; flex-basis: 18px; }

/* --------------------------------------------------- more colour, used
   The complaint was that it read as colourless. These are the places
   where colour carries meaning, so this is where it goes. */

.nav-list a[aria-current="page"] {
  background: var(--surface);
  color: var(--accent-deep);
  box-shadow: var(--shadow-1);
}
.nav-list a[aria-current="page"] .ico { color: var(--accent); }

/* A hairline of brand across the top of the console. */
.topbar {
  border-top: 3px solid transparent;
  border-image: linear-gradient(90deg, #4f46e5 0 33%, #0d9488 33% 66%, #f59e0b 66%) 1;
  border-bottom: 1px solid var(--line);
}

.zone {
  padding: 3px 9px;
  border-radius: 999px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  letter-spacing: .05em;
}
/* "this dashboard only" and "every dashboard" are different kinds of
   thing, so they are different colours rather than the same grey. */
#workspace .zone, #telegram .zone {
  background: var(--accent-soft); border-color: var(--accent-line); color: var(--accent-deep);
}
#schedule .zone, #ai .zone, #security .zone, #dashboards .zone, #migrate .zone {
  background: var(--amber-soft); border-color: var(--amber-line); color: var(--amber);
}

/* Stat rails cycle the triad, so a row of four is not four grey boxes. */
.grid > .stat:nth-child(3n+1)::before { background: var(--accent); }
.grid > .stat:nth-child(3n+2)::before { background: var(--teal); }
.grid > .stat:nth-child(3n+3)::before { background: var(--amber); }

.stat .k { color: var(--ink-3); }
.stat .v { color: var(--ink); }

/* The card header rail picks up the accent, quietly. */
.card-head { border-bottom-color: var(--line); }
.card-head h2 { color: var(--ink); }

/* Links inside prose look like links. */
.card-body a:not(.btn) { color: var(--accent-deep); font-weight: 550; }

/* Section index down the settings page reads as a list of places. */
.seg label:has(input:checked) { color: var(--accent-deep); }

/* ----------------------------------------------------------- the mark
   A round orange badge with a leaf, drawn as SVG so it is sharp at 24px
   and at 76px from the same source.

   It replaced a three-arc ring that flickered like a neon sign. The
   flicker was carefully kept under the three-flashes-per-second line in
   WCAG 2.3.1, but a piece of fruit does not flicker, and on the badge it
   read as a fault rather than as a style. A solid object gets a shadow. */
.polka-mark {
  filter: drop-shadow(0 2px 5px rgba(0, 0, 0, .3));
  transition: transform .24s cubic-bezier(.32, .72, 0, 1), filter .24s ease;
}
.brand:hover .polka-mark,
.signin-brand:hover .polka-mark {
  transform: rotate(-6deg) scale(1.06);
  filter: drop-shadow(0 4px 12px rgba(249, 115, 22, .45));
}

/* On the sign-in screen it is the only thing on the page, so it sits in
   its own pool of warm light. */
.signin-brand .polka-mark {
  filter: drop-shadow(0 6px 22px rgba(249, 115, 22, .38));
}
@media (prefers-reduced-motion: reduce) {
  .polka-mark,
  .brand:hover .polka-mark,
  .signin-brand:hover .polka-mark { transform: none; transition: none; }
}

/* ------------------------------------------------- the sign-in is dark
   Always, whatever the theme. Neon needs a dark wall behind it - the same
   mark on light grey is just a coloured ring. This is the one screen in
   the app that is a front door rather than a tool, so it is allowed to
   look like one; everything past the login follows the viewer's theme.

   The tokens are redeclared on the container, so every component inside
   picks the dark values up without knowing anything about it. */

.signin {
  --bg:           #0b0c12;
  --surface:      #14161f;
  --surface-2:    #1a1d28;
  --surface-sunk: #0e1017;

  --ink:          #f0f2f8;
  --ink-2:        #b6bccb;
  --ink-3:        #8b91a3;
  --ink-faint:    #6b7183;

  --line:         #272b38;
  --line-strong:  #3a3f50;

  --accent:       #8b85ff;
  --accent-ink:   #12121c;
  --accent-soft:  #1e1d3a;
  --accent-line:  #3a3670;
  --accent-deep:  #b9b5ff;

  --ok:    #2dd4bf;  --ok-soft:   #102a28;  --ok-line:   #1d4d47;
  --warn:  #f0b458;  --warn-soft: #2b2113;  --warn-line: #4f3d1d;
  --bad:   #fb7185;  --bad-soft:  #2c161c;  --bad-line:  #58272f;

  color: var(--ink);

  /* A wall, not a gradient: two very dim pools of the brand colours, as if
     the sign were throwing light onto brick. */
  background:
    radial-gradient(60ch 40ch at 50% 18%, rgba(79, 70, 229, .16), transparent 70%),
    radial-gradient(50ch 34ch at 78% 88%, rgba(13, 148, 136, .10), transparent 70%),
    radial-gradient(46ch 30ch at 18% 82%, rgba(245, 158, 11, .07), transparent 70%),
    #0b0c12;
}

.signin .card {
  background: color-mix(in srgb, var(--surface) 86%, transparent);
  backdrop-filter: blur(8px);
  border-color: var(--line);
  box-shadow: 0 2px 6px rgba(0, 0, 0, .5), 0 24px 60px rgba(0, 0, 0, .55);
}

.signin .card-head,
.signin-head { background: transparent; }

.signin input {
  background: var(--surface-sunk);
  border-color: var(--line-strong);
  color: var(--ink);
}
.signin input:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

.signin .btn-primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
  box-shadow: 0 0 24px rgba(139, 133, 255, .35), inset 0 1px 0 rgba(255, 255, 255, .2);
}

.signin-foot { color: var(--ink-faint); }

/* ---------------------------------------------- icons inside buttons
   Belt and braces. The SVG carries no width attribute, so if any rule
   here fails to match it fills the button. Both selectors are listed
   rather than relying on the class alone, and the size is stated on the
   element itself as well as the flex basis. */

.btn > svg,
.btn .ico,
.btn > .ico {
  width: 16px;
  height: 16px;
  min-width: 16px;
  flex: 0 0 16px;
  display: block;
  /* Inherit the button's text colour, so an icon on a coloured button is
     the same colour as the label beside it rather than a stray grey. */
  color: inherit;
  stroke: currentColor;
  stroke-width: 2;
}
.btn-sm > svg, .btn-sm .ico {
  width: 15px; height: 15px; min-width: 15px; flex-basis: 15px;
}

/* Icon and label are one object, so they sit close. 8px read as a gap
   between two things. */
.btn { gap: 7px; line-height: 1.1; }

/* The icon is optically a touch high next to capital letters. */
.btn > svg, .btn .ico { margin-top: -1px; }

/* A button that is only an icon stays square rather than going wide.
   Marked with a class, NOT with :has(> svg:only-child): :only-child counts
   ELEMENTS, so a button holding an icon and a text label still has the icon
   as its only element child. That selector matched every icon+text button in
   the app and squashed it to 40px with the label hanging outside. */
.btn-icon { padding: 0; width: 40px; }
.btn-icon.btn-sm { width: 32px; }

/* =====================================================================
   THEMES

   Two independent choices, because they answer different questions:

     data-theme   light or dark        how bright is the room
     data-accent  which colour         what does this place feel like

   Only the accent tokens change. Every component reads them, so a theme
   is six values, not a stylesheet - and adding a seventh theme is six
   more lines rather than an audit of the whole file.

   The logo is NOT themed. Its three colours are the mark; a logo that
   changes colour with a preference is not a logo.
   ===================================================================== */

/* Indigo */
:root[data-accent="indigo"] {
  --accent:      #4f46e5;
  --accent-soft: #eef0fe;
  --accent-line: #c9cbf8;
  --accent-deep: #3730a3;
  --accent-ink:  #ffffff;
}
:root[data-accent="indigo"][data-theme="dark"] {
  --accent:      #8b85ff;
  --accent-soft: #1e1d3a;
  --accent-line: #3a3670;
  --accent-deep: #b9b5ff;
  --accent-ink:  #12121c;
}
@media (prefers-color-scheme: dark) {
  :root[data-accent="indigo"]:not([data-theme="light"]) {
    --accent:      #8b85ff;
    --accent-soft: #1e1d3a;
    --accent-line: #3a3670;
    --accent-deep: #b9b5ff;
    --accent-ink:  #12121c;
  }
}
/* Ocean */
:root[data-accent="ocean"] {
  --accent:      #0284c7;
  --accent-soft: #e5f3fb;
  --accent-line: #b3ddf1;
  --accent-deep: #075985;
  --accent-ink:  #ffffff;
}
:root[data-accent="ocean"][data-theme="dark"] {
  --accent:      #4cb8f0;
  --accent-soft: #0d2534;
  --accent-line: #1c4560;
  --accent-deep: #8ed3f7;
  --accent-ink:  #12121c;
}
@media (prefers-color-scheme: dark) {
  :root[data-accent="ocean"]:not([data-theme="light"]) {
    --accent:      #4cb8f0;
    --accent-soft: #0d2534;
    --accent-line: #1c4560;
    --accent-deep: #8ed3f7;
    --accent-ink:  #12121c;
  }
}
/* Forest */
:root[data-accent="forest"] {
  --accent:      #047857;
  --accent-soft: #e4f4ee;
  --accent-line: #a9dcc9;
  --accent-deep: #065f46;
  --accent-ink:  #ffffff;
}
:root[data-accent="forest"][data-theme="dark"] {
  --accent:      #34d399;
  --accent-soft: #0d2a20;
  --accent-line: #1b4a39;
  --accent-deep: #7ee7bd;
  --accent-ink:  #12121c;
}
@media (prefers-color-scheme: dark) {
  :root[data-accent="forest"]:not([data-theme="light"]) {
    --accent:      #34d399;
    --accent-soft: #0d2a20;
    --accent-line: #1b4a39;
    --accent-deep: #7ee7bd;
    --accent-ink:  #12121c;
  }
}
/* Plum */
:root[data-accent="plum"] {
  --accent:      #7c3aed;
  --accent-soft: #f2ebfe;
  --accent-line: #d5bdf9;
  --accent-deep: #5b21b6;
  --accent-ink:  #ffffff;
}
:root[data-accent="plum"][data-theme="dark"] {
  --accent:      #a78bfa;
  --accent-soft: #231a3a;
  --accent-line: #432c6e;
  --accent-deep: #c4b1fd;
  --accent-ink:  #12121c;
}
@media (prefers-color-scheme: dark) {
  :root[data-accent="plum"]:not([data-theme="light"]) {
    --accent:      #a78bfa;
    --accent-soft: #231a3a;
    --accent-line: #432c6e;
    --accent-deep: #c4b1fd;
    --accent-ink:  #12121c;
  }
}
/* Ember */
:root[data-accent="ember"] {
  --accent:      #c2410c;
  --accent-soft: #fdeee5;
  --accent-line: #f6cbb0;
  --accent-deep: #9a3412;
  --accent-ink:  #ffffff;
}
:root[data-accent="ember"][data-theme="dark"] {
  --accent:      #fb923c;
  --accent-soft: #2e1a10;
  --accent-line: #57311c;
  --accent-deep: #fdba74;
  --accent-ink:  #12121c;
}
@media (prefers-color-scheme: dark) {
  :root[data-accent="ember"]:not([data-theme="light"]) {
    --accent:      #fb923c;
    --accent-soft: #2e1a10;
    --accent-line: #57311c;
    --accent-deep: #fdba74;
    --accent-ink:  #12121c;
  }
}
/* Rose */
:root[data-accent="rose"] {
  --accent:      #be123c;
  --accent-soft: #fdeaef;
  --accent-line: #f6c3d1;
  --accent-deep: #9f1239;
  --accent-ink:  #ffffff;
}
:root[data-accent="rose"][data-theme="dark"] {
  --accent:      #fb7185;
  --accent-soft: #2c161c;
  --accent-line: #58272f;
  --accent-deep: #fda4af;
  --accent-ink:  #12121c;
}
@media (prefers-color-scheme: dark) {
  :root[data-accent="rose"]:not([data-theme="light"]) {
    --accent:      #fb7185;
    --accent-soft: #2c161c;
    --accent-line: #58272f;
    --accent-deep: #fda4af;
    --accent-ink:  #12121c;
  }
}

/* ------------------------------------------------------- the picker */

.theme-pop {
  /* FIXED, not absolute. The sidebar scrolls (overflow-y: auto), and a
     scroll container clips its absolutely positioned children, so the
     panel was sliced at the sidebar edge and the part hanging outside
     could not be clicked. Fixed positioning takes it out of that context.

     Sized to sit INSIDE the sidebar: 220px wide less its 12px padding
     either side. A panel wider than the thing it belongs to overflows the
     viewport and puts a horizontal scrollbar on the whole page, which is
     what it was doing. */
  position: fixed;
  left: var(--s3);
  bottom: 92px;
  width: calc(var(--nav-w) - var(--s3) * 2);
  max-height: min(70dvh, 520px);
  overflow-y: auto;
  overscroll-behavior: contain;
  /* Above the sticky topbar (20) and the nav (30), below a modal. */
  z-index: 400;
  padding: var(--s3);
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  box-shadow: var(--shadow-pop);
}

.nav { z-index: 30; }

/* Compact: this is a preferences panel in a sidebar, not a page. */
.theme-pop h3 {
  font-size: 10px;
  letter-spacing: .08em;
  margin-bottom: 6px;
}
.theme-pop h3 + * { margin-bottom: var(--s3); }
.theme-pop > *:last-child { margin-bottom: 0; }

.mode-row { display: flex; gap: 5px; }
.mode-row button { min-height: 28px; font-size: 12px; padding: 0 2px; }

/* display:grid belongs here. It was lost when the preset block that used
   to carry it was rewritten, and without it the buttons stop being a
   grid: no columns, and the gap does nothing, so they touch. */
.styles { display: grid; grid-template-columns: repeat(3, 1fr); gap: 5px; }
.styles button { min-height: 28px; font-size: 11.5px; padding: 0 4px; }

.swatches { display: grid; grid-template-columns: repeat(6, 1fr); gap: 6px; }
.swatches button { min-height: 0; aspect-ratio: 1; border-width: 2px; }

@media (max-width: 860px) {
  /* The nav is a top bar here, so the panel drops from it and keeps the
     bottom of the screen clear. */
  .theme-pop {
    top: 60px;
    bottom: auto;
    left: auto;
    right: var(--s3);
    width: 232px;
  }
}

/* The look of the controls. Every size is stated once, in the compact
   block above; these are only colour, weight and shape. */
.theme-pop h3 {
  font-weight: 650;
  text-transform: uppercase;
  color: var(--ink-3);
}

.mode-row { display: flex; }
.mode-row button {
  flex: 1;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  color: var(--ink-2);
  /* family only: the shorthand "font: inherit" would also reset
     font-size, undoing the compact sizing set further up. */
  font-family: inherit;
  font-weight: 550;
  cursor: pointer;
}
.mode-row button[aria-pressed="true"] {
  background: var(--accent-soft);
  border-color: var(--accent-line);
  color: var(--accent-deep);
}

.swatches { display: grid; }
.swatches button {
  border-radius: 50%;
  border: 2px solid transparent;
  cursor: pointer;
  padding: 0;
  /* The swatch shows its own colour, not the current theme's, so you can
     see what you are choosing before you choose it. */
  background: var(--swatch);
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .12);
  transition: transform .12s ease;
}
.swatches button:hover { transform: scale(1.12); }
.swatches button[aria-pressed="true"] {
  border-color: var(--ink);
  box-shadow: inset 0 0 0 2px var(--surface);
}
.swatches button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.nav-foot { position: relative; }


/* =====================================================================
   STYLE PRESETS

   The accent themes above change six colour tokens. These change the
   SHAPE of the interface: corner radius, border weight, shadow, surface
   treatment, letterforms and how much air everything gets.

   All of it is still tokens. A preset is a block of variable values, not
   a second stylesheet, so a component written once obeys every preset
   without knowing any of them exist.

   Set with data-style on the root, alongside data-theme and data-accent.
   ===================================================================== */

/* ------------------------------------------------------------ 1. Studio
   The default. Quiet, layered, gets out of the way. */
:root[data-style="studio"] {
  --radius: 10px; --radius-sm: 7px; --radius-lg: 14px;
  --card-border: 1px;
  --card-bg: var(--surface);
  --head-bg: linear-gradient(var(--surface-2), var(--surface));
  --page-pad: var(--s6);
}

/* ----------------------------------------------------------- 2. Minimal
   No shadows, hairline rules, square corners, a lot of air. Everything
   is carried by spacing and type rather than by boxes. */
:root[data-style="minimal"] {
  --radius: 2px; --radius-sm: 2px; --radius-lg: 3px;
  --card-border: 1px;
  --card-bg: var(--surface);
  --head-bg: transparent;
  --page-pad: var(--s8);
  --shadow-1: none;
  --shadow-2: none;
  --shadow-pop: 0 8px 40px rgba(0, 0, 0, .16);
}
:root[data-style="minimal"] .card,
:root[data-style="minimal"] .btn,
:root[data-style="minimal"] .stat { box-shadow: none; }
:root[data-style="minimal"] .stat::before { display: none; }
:root[data-style="minimal"] .stat { border-left: 2px solid var(--accent); }
:root[data-style="minimal"] .card-body > * + * { margin-top: var(--s6); }
:root[data-style="minimal"] .topbar { border-image: none; border-top: 0; }

/* ------------------------------------------------------------- 3. Glass
   Translucent panels over a lit ground. It needs a background worth
   blurring, so the shell gets one. */
:root[data-style="glass"] {
  --radius: 16px; --radius-sm: 12px; --radius-lg: 20px;
  --card-border: 1px;
  --card-bg: color-mix(in srgb, var(--surface) 62%, transparent);
  --head-bg: color-mix(in srgb, var(--surface-2) 45%, transparent);
  --page-pad: var(--s6);
}
:root[data-style="glass"] body {
  background:
    radial-gradient(70ch 48ch at 8% -8%, color-mix(in srgb, var(--accent) 26%, transparent), transparent 70%),
    radial-gradient(60ch 40ch at 96% 10%, color-mix(in srgb, var(--ok) 20%, transparent), transparent 70%),
    radial-gradient(56ch 40ch at 45% 108%, color-mix(in srgb, var(--warn) 16%, transparent), transparent 70%),
    var(--bg);
  background-attachment: fixed;
}
:root[data-style="glass"] .card,
:root[data-style="glass"] .nav,
:root[data-style="glass"] .topbar,
:root[data-style="glass"] .stat {
  background: var(--card-bg);
  backdrop-filter: saturate(150%) blur(14px);
  -webkit-backdrop-filter: saturate(150%) blur(14px);
  border-color: color-mix(in srgb, var(--ink) 12%, transparent);
}
:root[data-style="glass"] table.data th { background: transparent; }

/* -------------------------------------------------------------- 4. Soft
   Claymorphism. Rounded, light, everything a raised pebble. */
:root[data-style="soft"] {
  --radius: 20px; --radius-sm: 14px; --radius-lg: 26px;
  --card-border: 0px;
  --card-bg: var(--surface);
  --head-bg: transparent;
  --page-pad: var(--s6);
  --shadow-1: 0 2px 4px rgba(16, 20, 27, .06), 0 6px 16px rgba(16, 20, 27, .07);
  --shadow-2: 0 4px 10px rgba(16, 20, 27, .07), 0 16px 40px rgba(16, 20, 27, .10);
}
:root[data-style="soft"] .card { border: 0; box-shadow: var(--shadow-2); }
:root[data-style="soft"] .card-head { border-bottom: 0; padding-bottom: var(--s3); }
:root[data-style="soft"] .card-body { padding-top: var(--s3); }
:root[data-style="soft"] .stat { border: 0; box-shadow: var(--shadow-1); }
:root[data-style="soft"] .btn { border: 0; box-shadow: var(--shadow-1); }
/* Typed inputs only - see the note on the same selector under Lab. A bare
   "input" here also repainted every checkbox on the page, the switch
   included, so an ON switch rendered in the panel colour and read as off. */
:root[data-style="soft"] input:not([type="checkbox"]):not([type="radio"]),
:root[data-style="soft"] select,
:root[data-style="soft"] textarea {
  border: 0;
  background: var(--surface-2);
  box-shadow: inset 0 1px 3px rgba(16, 20, 27, .07);
}

/* ------------------------------------------------------------- 5. Press
   Editorial. Heavy type, hard rules, no rounding, no shadow. The
   interface reads like a printed page. */
:root[data-style="press"] {
  --radius: 0px; --radius-sm: 0px; --radius-lg: 0px;
  --card-border: 2px;
  --card-bg: var(--surface);
  --head-bg: var(--ink);
  --page-pad: var(--s6);
  --shadow-1: none;
  --shadow-2: none;
}
:root[data-style="press"] .card { border: 2px solid var(--ink); box-shadow: none; }
:root[data-style="press"] .card-head { background: var(--ink); border-bottom: 0; }
:root[data-style="press"] .card-head h2 { color: var(--bg); }
:root[data-style="press"] .card-head .hint { color: var(--ink-faint); }
:root[data-style="press"] .topbar h1 {
  font-weight: 800;
  letter-spacing: -.04em;
  text-transform: uppercase;
}
:root[data-style="press"] .stat { border: 2px solid var(--ink); box-shadow: none; }
:root[data-style="press"] .stat::before { width: 6px; }
:root[data-style="press"] .stat .v { font-weight: 800; }
:root[data-style="press"] .btn { border-width: 2px; border-color: var(--ink); box-shadow: none; }
:root[data-style="press"] .btn-primary { border-color: var(--accent); }
:root[data-style="press"] table.data th {
  background: var(--ink);
  color: var(--bg);
  border-bottom: 0;
}

/* -------------------------------------------------------------- 6. Neon
   The sign-in language applied throughout: dark ground, lit edges,
   colour doing the work. Deliberately the loudest option. */
:root[data-style="neon"] {
  --radius: 12px; --radius-sm: 8px; --radius-lg: 16px;
  --card-border: 1px;
  --card-bg: var(--surface);
  --head-bg: transparent;
  --page-pad: var(--s6);
}
:root[data-style="neon"] body {
  background:
    radial-gradient(70ch 46ch at 12% -6%, color-mix(in srgb, var(--accent) 22%, transparent), transparent 68%),
    radial-gradient(56ch 38ch at 92% 96%, color-mix(in srgb, var(--ok) 16%, transparent), transparent 68%),
    var(--bg);
  background-attachment: fixed;
}
:root[data-style="neon"] .card {
  border-color: color-mix(in srgb, var(--accent) 34%, var(--line));
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent) 10%, transparent),
              0 12px 40px rgba(0, 0, 0, .22);
}
:root[data-style="neon"] .card-head {
  background: transparent;
  border-bottom-color: color-mix(in srgb, var(--accent) 24%, var(--line));
}
:root[data-style="neon"] .stat { border-color: color-mix(in srgb, var(--accent) 26%, var(--line)); }
:root[data-style="neon"] .stat .v {
  text-shadow: 0 0 18px color-mix(in srgb, var(--accent) 55%, transparent);
}
:root[data-style="neon"] .btn-primary {
  box-shadow: 0 0 22px color-mix(in srgb, var(--accent) 45%, transparent),
              inset 0 1px 0 rgba(255, 255, 255, .2);
}
:root[data-style="neon"] .nav-list a[aria-current="page"] {
  box-shadow: 0 0 16px color-mix(in srgb, var(--accent) 30%, transparent);
}

/* ----------------------------------------------- what every preset drives */

.card {
  border-radius: var(--radius-lg);
  border-width: var(--card-border, 1px);
  background: var(--card-bg, var(--surface));
}
.card-head { background: var(--head-bg, transparent); }
.page { padding: var(--page-pad, var(--s6)) var(--s5) var(--s8); }

/* Vertical rhythm between the blocks on a page, stated once.
   .card + .card covered two cards in a row, but a card followed by a stat
   grid, an alert or a form had no gap at all - so every page that needed one
   grew its own inline margin-bottom, and any block that nobody had looked at
   yet still sat flush against the thing above it. Owning the gap here means
   it cannot be forgotten. */
.page > * + * { margin-top: var(--s5); }

/* And one level in. Settings wraps its ten sections in #settingsRoot, so the
   direct-child selector above stopped at the wrapper and every section inside
   it sat flush against the next - which is the missing gap under the search
   box and between the cards. A wrapper should not cost a page its rhythm, so
   the rule is stated for the wrapper as well rather than for one id. */
.page > div > .card + .card,
.page > div > * + .card,
#settingsRoot > * + * { margin-top: var(--s5); }
#settingsRoot { display: block; }

/* --------------------------------------------------------- the picker

   The style buttons only need their look here. Their SIZE and the grid
   they sit in are stated once, in the compact block near .theme-pop -
   these rules load after it, so restating a width here would silently
   win and make the panel too big again. That is exactly what happened. */

.styles button {
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  color: var(--ink-2);
  /* family only: the shorthand "font: inherit" would also reset
     font-size, undoing the compact sizing set further up. */
  font-family: inherit;
  font-weight: 550;
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.styles button[aria-pressed="true"] {
  background: var(--accent-soft);
  border-color: var(--accent-line);
  color: var(--accent-deep);
}

/* --------------------------------------------------- buttons, finally
   A button with a leading icon needs LESS padding on the icon side than
   on the text side, or it reads as left-weighted: the icon is visually
   lighter than a capital letter, so equal padding is not equal weight.
   This is the last word on button internals in this file. */

.btn {
  gap: 8px;
  padding-left: var(--s4);
  padding-right: var(--s4);
  font-weight: 600;
}
.btn > svg:first-child,
.btn > .ico:first-child { margin-left: -2px; }

.btn-sm { padding-left: var(--s3); padding-right: var(--s3); }

/* The icon carries the same weight as the label beside it. At 16px a
   1.8 stroke looks like a hairline next to 600-weight text. */
.btn .ico { stroke-width: 2.1; }
.btn-primary .ico { stroke-width: 2.3; }

/* An icon-only button is square and centred, and says what it is to a
   screen reader through its .sr-only label. */
.btn-icon { padding: 0; }
.btn-icon > svg:first-child, .btn-icon > .ico:first-child { margin-left: 0; }

/* Disabled and busy states must not shift anything. */
.btn.is-busy { pointer-events: none; }

/* A row of buttons wraps rather than overflowing its container. */
.btn-row { display: flex; flex-wrap: wrap; gap: var(--s2); align-items: center; }

/* Buttons in a table cell stay on one line and never stretch the column. */
table.data td.act .btn-row { flex-wrap: nowrap; justify-content: flex-end; }

/* -------------------------------------------- icons in a card header
   The header icon is a landmark, not decoration: it lets the eye find a
   section while scrolling without reading. So it takes the accent, sits
   in a tinted square, and is the same size in every card. */

.card-head > .ico:first-child {
  width: 30px;
  height: 30px;
  min-width: 30px;
  flex: 0 0 30px;
  padding: 6px;
  border-radius: var(--radius-sm);
  background: var(--accent-soft);
  color: var(--accent-deep);
  box-sizing: border-box;
}
:root[data-style="press"] .card-head > .ico:first-child {
  background: transparent;
  color: var(--bg);
}
:root[data-style="minimal"] .card-head > .ico:first-child {
  background: transparent;
  padding: 0;
  width: 20px; height: 20px; min-width: 20px; flex-basis: 20px;
}

/* =====================================================================
   PICKING PEOPLE
   A group is a shortcut; the ticks are the answer. Laid out on the same
   4/8 rhythm as the rest, so a list of fourteen names still reads as one
   block rather than fourteen things.
   ===================================================================== */

.pick {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface-2);
  padding: var(--s3);
  display: grid;
  gap: var(--s3);
  max-height: 320px;
  overflow-y: auto;
}

.pick-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s2); }

.pick-count {
  font-size: 12px;
  font-weight: 600;
  color: var(--ink-2);
  font-variant-numeric: tabular-nums;
}
.pick-count.is-empty { color: var(--warn); }

.pick-group { display: grid; gap: var(--s2); }

.pick-group-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s2);
  padding-bottom: var(--s1);
  border-bottom: 1px solid var(--line);
}
.pick-group-name {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--ink-2);
}
.pick-group-name .ico { width: 15px; height: 15px; }

.pick-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(158px, 1fr));
  gap: var(--s2);
}

.pick-one {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--s2);
  min-height: 40px;
  padding: var(--s2) var(--s3);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
  font-size: 13px;
  cursor: pointer;
  user-select: none;
  transition: background .13s ease, border-color .13s ease;
}
.pick-one:hover { border-color: var(--ink-faint); }
.pick-one input { flex: none; margin: 0; cursor: pointer; accent-color: var(--accent); }
.pick-one:has(input:checked) { border-color: var(--accent); background: var(--accent-soft); }
.pick-one:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }

.pick-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pick-note { flex: none; font-size: 10px; font-weight: 600; color: var(--warn); }
.pick-one.is-silent { border-style: dashed; }

/* =====================================================================
   THE REMINDER GRID
   The table becomes the form. Nothing is written until Save, so the only
   state worth showing is which rows are pending.
   ===================================================================== */

.cell-edit { display: none; }
table.is-editing .cell-show { display: none; }
table.is-editing .cell-edit { display: grid; gap: var(--s2); }

.grid-in {
  width: 100%;
  min-height: 36px;
  padding: 6px var(--s2);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  font-size: 13px;
}
.grid-in:focus-visible { outline: 2px solid var(--accent); outline-offset: -1px; border-color: var(--accent); }
.grid-in-time { font-variant-numeric: tabular-nums; letter-spacing: .02em; }
.grid-note { font-size: 11px; color: var(--ink-3); }

.days-sm { gap: 3px; }
.days-sm label { width: 30px; height: 30px; font-size: 11px; border-radius: 5px; }

table.is-editing tbody tr.is-dirty > td { background: var(--accent-soft); }
table.is-editing tbody tr.is-dirty > td:first-child { box-shadow: inset 3px 0 0 var(--accent); }

#bulkToggle.is-on { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }

.bulk-bar {
  position: sticky;
  bottom: 0;
  z-index: 5;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--s2) var(--s3);
  padding: var(--s3);
  border-top: 1px solid var(--line);
  background: var(--surface);
}
.bulk-bar .btn-row { margin-left: auto; }
.bulk-count { font-size: 13px; font-weight: 600; font-variant-numeric: tabular-nums; }
.bulk-count.is-bad { color: var(--bad); }
.bulk-hint { font-size: 12px; color: var(--ink-3); }

@media (max-width: 640px) {
  .pick-grid { grid-template-columns: 1fr 1fr; }
  .bulk-bar .btn-row { margin-left: 0; width: 100%; }
}

/* =====================================================================
   NARROW SCREENS
   Two things were escaping the viewport: a card header laid out as one
   unwrappable row, and the mobile nav, which is a horizontal strip of
   everything the sidebar holds. Both are fixed by letting them wrap or
   shrink rather than by hiding anything - a settings page you cannot
   reach the New dashboard button on is worse than a tall one.
   ===================================================================== */

/* A card header is a title, a badge, sometimes a search box and an action.
   On a narrow screen there is no room for them side by side, and without
   this the action is simply pushed off the edge of the card. */
.card-head { flex-wrap: wrap; }
.card-head h2 { min-width: 0; }
.card-head .zone { white-space: nowrap; }
.card-head input[type="search"] { min-width: 0; flex: 1 1 160px; }

/* Nothing may make the page itself scroll sideways. A table or a code block
   that is genuinely too wide scrolls inside its own box; the page does not.

   clip, not hidden: overflow-x: hidden turns the body into a scroll container,
   and a sticky header inside a scroll container stops sticking. clip trims the
   overflow without creating one, which is the whole difference here.

   min-width on the columns matters as much: a flex child defaults to
   min-width: auto, so one wide table inside it pushes the whole column past
   the edge of the screen no matter what the parent says. */
.shell, .main { min-width: 0; }
.page { min-width: 0; }
body { overflow-x: clip; }

@media (max-width: 860px) {
  /* The nav strip has to fit rather than scroll: a scrollbar under the logo
     reads as a broken layout, and the items it hid were never found. */
  .nav { overflow-x: visible; flex-wrap: wrap; row-gap: var(--s2); }
  .nav-list { flex-wrap: wrap; }
  .ws-switch { min-width: 0; flex: 1 1 150px; }
  .ws-current { min-height: 40px; }
}

@media (max-width: 860px) {
  /* The action in a card header takes its own full-width line rather than
     being squeezed past the edge of the card. This is the one the New
     dashboard button kept escaping through. */
  .card-head > .btn,
  .card-head > form,
  .card-head > input[type="search"] { flex: 1 0 100%; }
  .card-head > .btn { justify-content: center; }
  .card-head .zone { flex: 0 1 auto; min-width: 0; }
}

@media (max-width: 640px) {
  /* The mark alone is enough: it is the same mark on the sign-in page and
     on the tab, and the word next to it was costing a third of the row. */
  .brand span { display: none; }
  .brand .polka-mark { width: 24px; height: 24px; }

  .nav { padding: var(--s2); gap: var(--s2); }
  .nav-list a { min-height: 38px; padding: 0 var(--s2); }
  .nav-foot { gap: var(--s1); }

  .topbar { padding: var(--s3); gap: var(--s2); }
  .topbar h1 { font-size: 19px; }
  .topbar .sub { font-size: 12px; }

  .page { padding: var(--s3) var(--s3) var(--s6); }
  .card-body, .card-head { padding: var(--s3); }
  .card-head { gap: var(--s2); row-gap: var(--s2); }

  .grid-2, .grid-3 { grid-template-columns: minmax(0, 1fr); }
  .pick-grid { grid-template-columns: minmax(0, 1fr); }
  .sheet { width: 100%; max-width: 100%; border-radius: var(--radius) var(--radius) 0 0; }
}

/* =====================================================================
   THE MENU BUTTON
   Three lines that become a cross. Drawn rather than iconographic so it
   can animate between the two states, which is what tells you the drawer
   is a thing that opened and not a page that changed.
   ===================================================================== */

.burger {
  display: none;
  flex: 0 0 auto;
  width: 42px;
  height: 42px;
  padding: 11px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  cursor: pointer;
}
.burger span {
  display: block;
  height: 2px;
  border-radius: 2px;
  background: var(--ink-2);
  transition: transform .2s cubic-bezier(.32, .72, 0, 1), opacity .14s linear;
}
.burger span + span { margin-top: 5px; }
.burger:hover { border-color: var(--accent); }
.burger:hover span { background: var(--accent); }
.burger:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

body.nav-open .burger span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
body.nav-open .burger span:nth-child(2) { opacity: 0; }
body.nav-open .burger span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* Flat, not blurred. The blur made the whole screen look soft-focus rather
   than dimmed, and on a phone it is the most expensive thing on the page for
   an effect nobody asked for. A darker plain scrim separates the drawer from
   the page better and costs nothing. */
.nav-scrim {
  position: fixed;
  inset: 0;
  z-index: 40;
  background: rgba(0, 0, 0, .62);
  animation: scrim-in .18s ease-out;
}
@keyframes scrim-in { from { opacity: 0; } to { opacity: 1; } }

@media (max-width: 860px) {
  .burger { display: block; }
}
@media (min-width: 861px) {
  .nav-scrim { display: none; }
}

/* =====================================================================
   SIGNAL - red, gold, black
   Red carries the action, gold carries attention. Both are loud, so the
   ground is nearly black and everything else is kept quiet: on a screen
   this saturated, one more colour is one too many.
   ===================================================================== */

:root[data-accent="signal"] {
  --accent:      #d61f2b;
  --accent-soft: #fdeced;
  --accent-line: #f7c3c6;
  --accent-deep: #a8151f;
  --accent-ink:  #ffffff;
  --gold:        #b8860b;
  --gold-soft:   #fdf4e0;
}
:root[data-accent="signal"][data-theme="dark"] {
  --accent:      #ff3b46;
  --accent-soft: #2a0f12;
  --accent-line: #5c1c22;
  --accent-deep: #ff7a82;
  --accent-ink:  #12080a;
  --gold:        #f5b73d;
  --gold-soft:   #2a1f0c;
}
@media (prefers-color-scheme: dark) {
  :root[data-accent="signal"]:not([data-theme="light"]) {
    --accent:      #ff3b46;
    --accent-soft: #2a0f12;
    --accent-line: #5c1c22;
    --accent-deep: #ff7a82;
    --accent-ink:  #12080a;
    --gold:        #f5b73d;
    --gold-soft:   #2a1f0c;
  }
}
/* Every other accent still needs the two gold tokens, or a Lab page in
   Ocean would render with nothing where the gold should be. */
:root { --gold: #b8860b; --gold-soft: #fdf4e0; }
:root[data-theme="dark"] { --gold: #f5b73d; --gold-soft: #2a1f0c; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) { --gold: #f5b73d; --gold-soft: #2a1f0c; }
}

/* =====================================================================
   LAB - the futuristic preset
   An instrument panel rather than a document: a near-black ground,
   hairlines in gold, headings set like a readout, and depth that answers
   the pointer. Everything that moves moves on transform and opacity only,
   so it stays on the compositor and costs a phone nothing. All of it is
   switched off under prefers-reduced-motion, at the bottom.
   ===================================================================== */

:root[data-style="lab"] {
  --radius: 12px; --radius-sm: 8px; --radius-lg: 16px;
  --card-border: 1px;
  --card-bg: var(--surface);
  --head-bg: linear-gradient(180deg,
              color-mix(in srgb, var(--gold) 7%, var(--surface-2)),
              var(--surface));
  --page-pad: var(--s6);
  --lab-line: color-mix(in srgb, var(--gold) 34%, transparent);
  --lab-glow: color-mix(in srgb, var(--accent) 42%, transparent);
}

/* The ground itself. Polka's dark theme is a blue-black, which fights red
   and gold - both are warm, and a cool ground makes them look dirty. Lab
   swaps in a neutral near-black so the two brand colours are the only hues
   on the screen. Light mode is left alone: on white, warmth is not the
   problem it is on black. */
:root[data-style="lab"][data-theme="dark"] {
  --bg:          #08080a;
  --surface:     #141418;
  --surface-2:   #1e1e24;
  --line:        #34343d;
  --line-strong: #4e4e5a;
}
@media (prefers-color-scheme: dark) {
  :root[data-style="lab"]:not([data-theme="light"]) {
    --bg:          #08080a;
    --surface:     #141418;
    --surface-2:   #1e1e24;
    --line:        #34343d;
    --line-strong: #4e4e5a;
  }
}

/* A ground with something behind it: two very wide, very faint pools of
   the two brand colours, over a grid fine enough to read as texture
   rather than as a table. Fixed, so it does not slide with the page. */
:root[data-style="lab"] body {
  background-image:
    radial-gradient(80ch 50ch at 4% -10%, color-mix(in srgb, var(--accent) 20%, transparent), transparent 68%),
    radial-gradient(70ch 44ch at 98% 4%, color-mix(in srgb, var(--gold) 16%, transparent), transparent 66%),
    linear-gradient(color-mix(in srgb, var(--ink) 6%, transparent) 1px, transparent 1px),
    linear-gradient(90deg, color-mix(in srgb, var(--ink) 6%, transparent) 1px, transparent 1px);
  background-size: auto, auto, 46px 46px, 46px 46px;
  background-attachment: fixed;
}

/* Every panel is an instrument: a hairline of both brand colours across
   the top edge, and a border that lifts towards gold when you are on it. */
:root[data-style="lab"] .card {
  position: relative;
  transition: border-color .18s ease, box-shadow .18s ease;
}
:root[data-style="lab"] .card::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 1px;
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  background: linear-gradient(90deg, transparent, var(--accent) 18%, var(--gold) 62%, transparent);
  opacity: .9;
  pointer-events: none;
}
:root[data-style="lab"] .card:hover {
  border-color: var(--lab-line);
  box-shadow: 0 18px 44px -28px var(--lab-glow);
}

/* Column headings and the small print read as a readout. */
:root[data-style="lab"] table.data th,
:root[data-style="lab"] .card-head .hint {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 11px;
  letter-spacing: .09em;
  text-transform: uppercase;
}

/* Depth that answers the pointer. A small lift reads as a physical key
   rather than as another colour change. */
:root[data-style="lab"] .btn,
:root[data-style="lab"] .stat,
:root[data-style="lab"] .nav-list a {
  transition: transform .16s cubic-bezier(.32, .72, 0, 1), box-shadow .16s ease,
              border-color .16s ease, background .16s ease;
}
:root[data-style="lab"] .btn:hover { transform: translateY(-1px) scale(1.012); }
:root[data-style="lab"] .btn:active { transform: translateY(1px) scale(.985); }
:root[data-style="lab"] .btn-primary {
  box-shadow: 0 1px 0 color-mix(in srgb, #fff 22%, transparent) inset,
              0 8px 22px -12px var(--lab-glow);
}
:root[data-style="lab"] .btn-primary:hover {
  box-shadow: 0 1px 0 color-mix(in srgb, #fff 28%, transparent) inset,
              0 12px 30px -12px var(--lab-glow);
}

/* The stat tiles tilt very slightly. Two degrees is enough to be felt and
   not enough to be noticed. */
:root[data-style="lab"] .stat:hover {
  transform: perspective(700px) rotateX(2deg) translateY(-2px);
  border-color: var(--lab-line);
}

/* The current section is lit rather than merely filled. */
:root[data-style="lab"] .nav-list a[aria-current="page"] {
  background: linear-gradient(90deg, var(--accent-soft), transparent);
  box-shadow: inset 2px 0 0 var(--accent);
}
:root[data-style="lab"] .nav-list a:hover { transform: translateX(2px); }

/* ------------------------------------------------------- icons that move
   Small, brief, and tied to what the icon means: the plus opens, the
   refresh turns, the send flies. An icon that animates for no reason is a
   distraction; one that animates towards its own verb is a label. */

:root[data-style="lab"] .btn .ico,
:root[data-style="lab"] .nav-list a .ico,
:root[data-style="lab"] .card-head > .ico:first-child {
  transition: transform .22s cubic-bezier(.32, .72, 0, 1), color .18s ease;
}
:root[data-style="lab"] .nav-list a:hover .ico { transform: scale(1.12); }
:root[data-style="lab"] .card-head:hover > .ico:first-child { transform: rotate(-6deg) scale(1.06); }
:root[data-style="lab"] .btn:hover .ico { transform: scale(1.1); }
:root[data-style="lab"] .btn:hover .ico-refresh { transform: rotate(180deg); }
:root[data-style="lab"] .btn:hover .ico-plus { transform: rotate(90deg); }
:root[data-style="lab"] .btn:hover .ico-send { transform: translate(2px, -2px); }
:root[data-style="lab"] .btn:hover .ico-trash { transform: rotate(-8deg); }
:root[data-style="lab"] .btn:hover .ico-chev { transform: translateY(2px); }

/* The icon of the page you are on keeps a slow pulse, so a panel that is
   waiting never looks frozen. Opacity only: nothing reflows. */
:root[data-style="lab"] .nav-list a[aria-current="page"] .ico {
  animation: lab-pulse 2.6s ease-in-out infinite;
}
@keyframes lab-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: .62; }
}

/* Panels arrive rather than appear. Staggered by position and capped at
   six, because the seventh is below the fold and would only be delaying
   itself into view. */
:root[data-style="lab"] .page > .card,
:root[data-style="lab"] .page > .grid {
  animation: lab-rise .4s cubic-bezier(.32, .72, 0, 1) both;
}
:root[data-style="lab"] .page > *:nth-child(2) { animation-delay: .04s; }
:root[data-style="lab"] .page > *:nth-child(3) { animation-delay: .08s; }
:root[data-style="lab"] .page > *:nth-child(4) { animation-delay: .12s; }
:root[data-style="lab"] .page > *:nth-child(5) { animation-delay: .16s; }
:root[data-style="lab"] .page > *:nth-child(6) { animation-delay: .20s; }
@keyframes lab-rise {
  from { opacity: 0; transform: translateY(10px) scale(.995); }
  to   { opacity: 1; transform: none; }
}

/* Gold marks the things that are on. */
:root[data-style="lab"] .tag-ok { border-color: color-mix(in srgb, var(--gold) 40%, transparent); }
:root[data-style="lab"] .switch input:checked {
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--ok) 24%, transparent),
              0 0 16px -4px color-mix(in srgb, var(--ok) 70%, transparent);
}

/* The mark in the corner gets the same treatment as the sign-in page. */
:root[data-style="lab"] .brand:hover .polka-mark { transform: rotate(8deg) scale(1.06); }
:root[data-style="lab"] .brand .polka-mark { transition: transform .24s cubic-bezier(.32, .72, 0, 1); }

/* Motion is decoration, and decoration is the first thing to go. */
@media (prefers-reduced-motion: reduce) {
  :root[data-style="lab"] .page > .card,
  :root[data-style="lab"] .page > .grid,
  :root[data-style="lab"] .nav-list a[aria-current="page"] .ico { animation: none; }
  :root[data-style="lab"] .btn:hover,
  :root[data-style="lab"] .btn:active,
  :root[data-style="lab"] .stat:hover,
  :root[data-style="lab"] .nav-list a:hover,
  :root[data-style="lab"] .brand:hover .polka-mark { transform: none; }
  .nav { transition: none; }
  .nav-scrim { animation: none; }
  .burger span { transition: none; }
}

/* =====================================================================
   LAB, TURNED UP
   Separating three near-black greys buys almost nothing: on a dark screen
   the eye reads colour and edge long before it reads a five per cent
   difference in lightness. So the contrast is carried by the two brand
   colours doing real work in the places people actually look - the header
   of a panel, the row of column names, the section you are in, the button
   you are about to press - rather than by pushing the greys apart until
   the whole thing turns muddy.
   ===================================================================== */

:root[data-style="lab"] {
  /* Was 7%, which was invisible. A card header should read as a header. */
  --head-bg: linear-gradient(180deg,
              color-mix(in srgb, var(--gold) 15%, var(--surface-2)),
              var(--surface));
}

/* Column names in gold, and a rule under them. A table is the densest thing
   on any of these screens and it was the flattest. */
:root[data-style="lab"] table.data th {
  color: var(--gold);
  background: color-mix(in srgb, var(--gold) 7%, var(--surface-2));
  border-bottom: 1px solid color-mix(in srgb, var(--gold) 30%, transparent);
}
:root[data-style="lab"] table.data tbody tr:hover > td {
  background: color-mix(in srgb, var(--accent) 7%, transparent);
}

/* The section you are in is the single most useful thing on the screen and
   it was a faint wash. Now it is lit from the left in red and carries the
   only gold text in the list. */
:root[data-style="lab"] .nav-list a[aria-current="page"] {
  background: linear-gradient(90deg,
              color-mix(in srgb, var(--accent) 26%, transparent),
              transparent 78%);
  box-shadow: inset 3px 0 0 var(--gold);
  color: var(--ink);
  font-weight: 650;
}
:root[data-style="lab"] .nav-list a[aria-current="page"] .ico { color: var(--gold); }
:root[data-style="lab"] .nav-list a:hover {
  background: color-mix(in srgb, var(--ink) 7%, transparent);
}

/* The primary action is the loudest thing on the page, so it is allowed to
   be loud: a real gradient with a lit top edge, not a flat fill. */
:root[data-style="lab"] .btn-primary {
  background: linear-gradient(180deg,
              color-mix(in srgb, #fff 12%, var(--accent)),
              var(--accent));
  border-color: color-mix(in srgb, #000 22%, var(--accent));
}

/* A quiet button still needs an edge, or a row of them reads as text. */
:root[data-style="lab"] .btn { border-color: var(--line-strong); }
:root[data-style="lab"] .btn:hover { border-color: var(--gold); color: var(--ink); }

/* Status pills were washed out. They are the only colour in a long table,
   so they should carry it. */
:root[data-style="lab"] .tag {
  border-width: 1px;
  font-weight: 650;
  letter-spacing: .02em;
}
:root[data-style="lab"] .tag-ok   { background: color-mix(in srgb, var(--ok) 20%, transparent); }
:root[data-style="lab"] .tag-warn { background: color-mix(in srgb, var(--warn) 22%, transparent); }
:root[data-style="lab"] .tag-info { background: color-mix(in srgb, var(--accent) 20%, transparent); }

/* The page title, which was the same weight as everything under it. */
:root[data-style="lab"] .topbar h1 { letter-spacing: -.03em; }
:root[data-style="lab"] .topbar {
  border-bottom: 1px solid var(--line);
  box-shadow: 0 1px 0 color-mix(in srgb, var(--gold) 16%, transparent);
}

/* The nav is a distinct surface from the page, and says so at its edge. */
:root[data-style="lab"] .nav {
  border-right: 1px solid var(--line-strong);
  box-shadow: inset -1px 0 0 color-mix(in srgb, var(--gold) 10%, transparent);
}

/* Stat tiles: the number is the point, so it gets the gold. */
:root[data-style="lab"] .stat strong,
:root[data-style="lab"] .stat .big { color: var(--gold); }

/* Inputs were the same colour as the card they sit in.
   
   Typed inputs only. A bare "input" selector here also caught every checkbox
   and radio on the page - including the switch, whose whole job is to turn
   green when it is on. Both selectors weigh exactly the same, this one comes
   later in the file, so it won and every switch stayed dark while its knob
   slid across: on, and looking off. The same rule was quietly flattening the
   weekday chips and the people picker too. */
:root[data-style="lab"] input:not([type="checkbox"]):not([type="radio"]),
:root[data-style="lab"] select,
:root[data-style="lab"] textarea {
  background: var(--bg);
  border-color: var(--line-strong);
}
:root[data-style="lab"] input:not([type="checkbox"]):not([type="radio"]):focus-visible,
:root[data-style="lab"] select:focus-visible,
:root[data-style="lab"] textarea:focus-visible {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 26%, transparent);
}

/* =====================================================================
   THE SECTION INDEX
   Ten settings sections is a long scroll, and a search box only helps
   somebody who already knows the word to type. This is the other half:
   the list of places, stuck to the top, so reaching the scheduler is one
   tap rather than a thumb drag. It carries a red dot for anything
   unfinished, which is the thing worth knowing before you go looking.
   ===================================================================== */

.jump {
  position: sticky;
  top: 0;
  z-index: 15;
  display: flex;
  gap: var(--s2);
  flex-wrap: wrap;
  padding: var(--s3) 0;
  margin-bottom: var(--s5);
  background: var(--bg);
  border-bottom: 1px solid var(--line);
}

.jump a {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  min-height: 36px;
  padding: 0 var(--s3);
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface);
  color: var(--ink-2);
  font-size: 13px;
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
  transition: border-color .14s ease, color .14s ease, background .14s ease;
}
.jump a:hover {
  border-color: var(--accent);
  color: var(--ink);
  text-decoration: none;
}
.jump a .ico { width: 15px; height: 15px; flex: none; }
.jump a .dot {
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--bad);
  flex: none;
}
.jump a.has-problem { border-color: var(--bad-line); color: var(--ink); }

/* Landing on a section should not put its heading under the sticky bar. */
#settingsRoot > section { scroll-margin-top: 84px; }

/* The section you have just jumped to says so for a moment, so a jump on a
   long page does not leave you wondering whether anything happened. */
#settingsRoot > section:target {
  animation: jump-landed 1.6s ease-out;
}
@keyframes jump-landed {
  0%   { box-shadow: 0 0 0 2px var(--accent); }
  100% { box-shadow: 0 0 0 2px transparent; }
}

@media (max-width: 640px) {
  /* Scrolls sideways here rather than stacking into five rows and eating
     the screen it was meant to save. */
  .jump {
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
    padding-bottom: var(--s2);
  }
  .jump::-webkit-scrollbar { display: none; }
  .jump a span { display: none; }
  .jump a { padding: 0 var(--s2); min-width: 40px; justify-content: center; }
  .jump a .ico { width: 17px; height: 17px; }
}

@media (prefers-reduced-motion: reduce) {
  #settingsRoot > section:target { animation: none; }
}
