/* Woodhouse Cleaning Tracker.
 *
 * The design brief here is "work tool", not "app": neutral surfaces, one
 * accent, and colour spent only where it carries meaning - a state, a warning,
 * a clash. Everything else is typography, spacing and alignment.
 *
 * Two audiences pull in opposite directions and both are served deliberately:
 * a cleaner holding a phone one-handed in a wet bathroom needs large targets
 * and one obvious next action; the office on a laptop needs density and rows
 * that scan. So targets stay at 44px+ on touch, and the desktop breakpoints
 * tighten rows and turn stacked cards into tables.
 */

:root {
  /* Surfaces run page -> card -> sunken, so nesting reads without shadows. */
  --bg: #f4f5f7;
  --card: #ffffff;
  --sunken: #f7f8fa;
  --hover: #f1f3f6;

  /* Ink in four steps. Anything below --faint is decoration and shouldn't
     be carrying information in the first place. */
  --ink: #16191f;
  --ink-2: #444b57;
  --muted: #6a7280;
  --faint: #9aa1ac;

  --line: #e0e3e8;
  --line-2: #edeff3;

  --accent: #1d5fd0;
  --accent-ink: #ffffff;
  --accent-wash: #eef3fd;

  /* Status. The -wash tones are for fills, the solid for text and marks. */
  --done: #17714f;
  --done-wash: #e8f4ee;
  --warn: #96590a;
  --warn-wash: #fbf2e3;
  --danger: #b32222;
  --danger-wash: #fbecec;

  /* Guests arriving. Its own colour because it outranks the plan's own order,
     and neither the warning amber nor the danger red says that. */
  --checkin: #b0146b;
  --checkin-wash: #fcebf3;

  --r: 6px;
  --r-lg: 10px;

  --s1: 4px;
  --s2: 8px;
  --s3: 12px;
  --s4: 16px;
  --s5: 24px;
  --s6: 32px;

  --tap: 44px;
  --bar-h: 52px;
  --safe-top: env(safe-area-inset-top, 0px);
  --bar-total: calc(var(--bar-h) + var(--safe-top));

  --ring: 0 0 0 2px var(--card), 0 0 0 4px color-mix(in srgb, var(--accent) 55%, transparent);
  --lift: 0 8px 28px rgb(16 20 28 / .14);

  /* On a phone this is a hand-held app, not a page: a card wants a little
     depth under it rather than a hairline holding it to the background. */
  --raise: 0 1px 2px rgb(16 20 28 / .04), 0 4px 14px rgb(16 20 28 / .05);

  /* Motion. Three speeds and two curves, so everything that moves in this app
     moves the same way: -out for things arriving and settling, -spring for
     the one or two places where a tap deserves an answer with some life in
     it. Nothing here is longer than a third of a second - a cleaner tapping
     down a list of sixty items must never wait for the app to finish. */
  --dur-1: .12s;
  --dur-2: .2s;
  --dur-3: .3s;
  --ease-out: cubic-bezier(.2, .8, .3, 1);
  --ease-spring: cubic-bezier(.34, 1.36, .5, 1);
}

/* One switch for the whole app. Somebody who has asked their phone to stop
   animating things has asked for a reason, and a checklist works exactly as
   well without any of this. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0e1116;
    --card: #171b22;
    --sunken: #12161c;
    --hover: #1e232b;

    --ink: #e8ebf0;
    --ink-2: #bcc3ce;
    --muted: #8d96a3;
    --faint: #666f7c;

    --line: #272d37;
    --line-2: #1f242c;

    --accent: #5f9bff;
    --accent-ink: #08111f;
    --accent-wash: #15203200;

    --done: #4fc793;
    --done-wash: #10251d;
    --warn: #dda45c;
    --warn-wash: #2a2113;
    --danger: #ef8b84;
    --danger-wash: #2e1819;
    --checkin: #f78bbc;
    --checkin-wash: #33162a;

    --lift: 0 10px 34px rgb(0 0 0 / .5);
  }

  /* color-mix on a transparent wash washes out; set it plainly instead. */
  :root { --accent-wash: #16233a; }
}

* { box-sizing: border-box; }

/* Beats the UA stylesheet on elements this file gives an explicit display. */
[hidden] { display: none !important; }

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

body {
  margin: 0;
  font: 16px/1.55 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  background: var(--bg);
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
}

/* Counts, times and dates line up in columns only with tabular figures. */
.num, .stat b, .meter-num, table.sched, .shift, .avcell, .pindots {
  font-variant-numeric: tabular-nums;
}

body {
  touch-action: manipulation; /* kills double-tap zoom; pinch still works */
  -webkit-tap-highlight-color: transparent;
}

/* Controls shouldn't select their own label or raise the copy/define bubble
   when a thumb rests on them. Content text stays selectable. */
button, .btn, .tile, .crow, .task, .cell, .persontile, .card-toggle, .grouphead {
  user-select: none;
  -webkit-touch-callout: none;
}

/* With the tap highlight gone, every row has to say it was pressed. */
.tile:active, .crow:active, .task:active, .persontile:active,
.card-toggle:active, .brow:active { background: var(--line-2); }

:focus-visible { outline: none; box-shadow: var(--ring); border-radius: var(--r); }

::selection { background: color-mix(in srgb, var(--accent) 25%, transparent); }

/* ---------------------------------------------------------------- icons */

.ico {
  display: inline-block;
  width: 16px;
  height: 16px;
  flex: none;
  vertical-align: -3px;
  stroke: currentColor;
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
  fill: none;
}

.ico.lg { width: 20px; height: 20px; vertical-align: -4px; }

/* A single chevron that points down when its control is open, so no code has
   to swap glyphs about. */
[aria-expanded] > .chev { transition: transform .15s ease; }
[aria-expanded="true"] > .chev { transform: rotate(90deg); }

/* --------------------------------------------------------- test banner */

.testbar {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  padding: calc(7px + var(--safe-top)) var(--s4) 7px;
  background: var(--warn-wash);
  color: var(--warn);
  font-size: 12.5px;
  font-weight: 600;
  text-align: center;
  border-bottom: 1px solid color-mix(in srgb, var(--warn) 25%, var(--line));
}

/* ------------------------------------------------------------ structure */

header.bar {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: var(--s3);
  min-height: var(--bar-total);
  padding: calc(6px + var(--safe-top)) var(--s4) 6px;
  padding-left: max(var(--s4), env(safe-area-inset-left));
  padding-right: max(var(--s4), env(safe-area-inset-right));
  background: var(--card);
  border-bottom: 1px solid var(--line);
}

header.bar h1 {
  flex: 1;
  margin: 0;
  font-size: 18px;
  font-weight: 640;
  letter-spacing: -0.012em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

#back, #home { gap: var(--s1); padding: 0 10px; }

@media (max-width: 560px) {
  .back-label, .signout-label, .home-label { display: none; }
  #signout { padding: 0 10px; }
  #back, #home { padding: 0 8px; }
  .who-name { max-width: 8ch; }
  header.bar { gap: var(--s2); }
  header.bar.has-back .who-text { display: none; }

  header.bar.has-back h1 {
    white-space: normal;
    font-size: 15px;
    line-height: 1.2;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
}

.who-box { display: flex; align-items: center; gap: var(--s2); min-width: 0; }
.who-text { display: flex; flex-direction: column; align-items: flex-end; line-height: 1.2; }

.who-name {
  font-size: 13px;
  font-weight: 620;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 14ch;
}

.who-role {
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .07em;
  color: var(--muted);
}

/* Role is a fact about permissions, so it gets one neutral treatment rather
   than three competing colours in the corner of every screen. */
.who-role.admin { color: var(--accent); }
.who-role.viewer { color: var(--warn); }

/* Deterministic tone per person out of a small, flat set: enough to tell two
   cleaners apart in a dense grid, not so much that the screen turns into a
   fruit salad. */
.avatar {
  flex: none;
  display: inline-grid;
  place-items: center;
  width: 26px;
  height: 26px;
  border-radius: var(--r);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .02em;
  user-select: none;
  background: var(--line-2);
  color: var(--ink-2);
  box-shadow: inset 0 0 0 1px var(--line);
}

.avatar[data-tone="0"] { background: #e7ecf5; color: #2d4470; }
.avatar[data-tone="1"] { background: #e4f0ea; color: #1f5943; }
.avatar[data-tone="2"] { background: #f3ebe2; color: #6a4a2c; }
.avatar[data-tone="3"] { background: #ece8f3; color: #4a3c70; }
.avatar[data-tone="4"] { background: #e3eff1; color: #23545c; }

@media (prefers-color-scheme: dark) {
  .avatar[data-tone="0"] { background: #1f2a3d; color: #a9c1ea; }
  .avatar[data-tone="1"] { background: #172c25; color: #8fcbb2; }
  .avatar[data-tone="2"] { background: #2c2419; color: #d3b28c; }
  .avatar[data-tone="3"] { background: #26213a; color: #b6a8dd; }
  .avatar[data-tone="4"] { background: #17292d; color: #92c0c8; }
}

.avatar.sm { width: 20px; height: 20px; font-size: 9px; border-radius: 4px; }
.avatar.lg { width: 36px; height: 36px; font-size: 13px; }


/* Section nav */
nav.nav {
  position: sticky;
  top: var(--bar-total);
  z-index: 15;
  display: flex;
  gap: var(--s1);
  padding: 0 var(--s4);
  padding-left: max(var(--s4), env(safe-area-inset-left));
  background: var(--card);
  border-bottom: 1px solid var(--line);
  overflow-x: auto;
  scrollbar-width: none;
}

nav.nav::-webkit-scrollbar { display: none; }

/* Desktop nav is an underlined tab row - the same visual language as the
   in-page tabs, so "which section" and "which view of it" read as one system
   instead of two kinds of pill competing. */
nav.nav button {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 46px;
  padding: 0 var(--s3);
  font-size: 15px;
  font-weight: 560;
  border: none;
  border-bottom: 2px solid transparent;
  border-radius: 0;
  background: none;
  color: var(--muted);
}

nav.nav .navicon { display: none; }
nav.nav button:hover { background: none; color: var(--ink-2); }

nav.nav button[aria-current="true"] {
  color: var(--accent);
  border-bottom-color: var(--accent);
  font-weight: 650;
}

main {
  max-width: 900px;
  margin: 0 auto;
  padding: var(--s4);
  padding-left: max(var(--s4), env(safe-area-inset-left));
  padding-right: max(var(--s4), env(safe-area-inset-right));
  /* Clears the floating report button, which would otherwise sit on top of
     the last row of whatever list is on screen. */
  padding-bottom: max(88px, env(safe-area-inset-bottom));
}

main.wide { max-width: 1320px; }

header.bar[hidden] ~ main { padding-top: calc(var(--s4) + var(--safe-top)); }

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

.card {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  margin-bottom: var(--s3);
  overflow: hidden;
}

.card > h2, .card-head {
  display: flex;
  align-items: center;
  gap: var(--s3);
  margin: 0;
  padding: 12px var(--s4);
  font-size: 15px;
  font-weight: 680;
  letter-spacing: -0.01em;
  color: var(--ink);
  background: var(--sunken);
  border-bottom: 1px solid var(--line);
}

.card-head .grow { letter-spacing: 0; }
.card > h2 .num, .card-head .num { font-size: 13.5px; font-weight: 600; color: var(--muted); }
.card-head button { min-height: 30px; padding: 0 10px; font-size: 12.5px; }

.card.danger { border-color: color-mix(in srgb, var(--danger) 32%, var(--line)); }
.card.danger > h2 { color: var(--danger); background: var(--danger-wash); }

/* A card header that is itself the fold control. */
.card-toggle {
  display: flex;
  align-items: center;
  gap: var(--s3);
  width: 100%;
  padding: 13px var(--s4);
  border: none;
  border-radius: 0;
  background: var(--sunken);
  color: var(--ink);
  font-size: 15px;
  font-weight: 620;
  text-align: left;
  min-height: 52px;
}

.card-toggle:hover { background: var(--hover); }
.card-toggle[aria-expanded="true"] { border-bottom: 1px solid var(--line); }
.card-toggle .chev { color: var(--faint); }

.pad { padding: var(--s4); }
.pad.tight { padding: var(--s3) var(--s4); }
.pad.flush-top { padding-top: 0; }
.pad.narrow { max-width: 460px; }

.row { display: flex; align-items: center; gap: var(--s2); }
.row.tight { gap: var(--s1); }
.row.loose { gap: var(--s3); }
.col { display: flex; flex-direction: column; gap: var(--s2); }
.spread { display: flex; align-items: center; justify-content: space-between; gap: var(--s3); }
.wrap { flex-wrap: wrap; }
.grow { flex: 1; min-width: 0; }
.muted { color: var(--muted); }
.small { font-size: 14px; }
.tiny { font-size: 12.5px; line-height: 1.45; }
.center { text-align: center; }
.nowrap { white-space: nowrap; }
.block { display: block; }
.stack > * + * { margin-top: var(--s3); }
.gap-top { margin-top: var(--s3); }
.gap-top-sm { margin-top: var(--s2); }

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

.eyebrow {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--muted);
}

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

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

button, .btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: var(--tap);
  padding: 0 var(--s4);
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--card);
  color: var(--ink);
  font: inherit;
  font-size: 15.5px;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  transition: background .12s, border-color .12s;
}

button:hover { background: var(--hover); }
button:active { background: var(--line-2); }
button[disabled] { opacity: .4; cursor: default; }
button[disabled]:hover { background: var(--card); }

button.primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
  font-weight: 620;
}

button.primary:hover { background: color-mix(in srgb, var(--accent) 86%, #000); }

button.destroy {
  background: var(--danger);
  border-color: var(--danger);
  color: #fff;
  font-weight: 620;
}

button.destroy:hover { background: color-mix(in srgb, var(--danger) 86%, #000); }

button.ghost { border-color: transparent; background: none; color: var(--ink-2); }
button.ghost:hover { background: var(--hover); }
button.ghost[disabled]:hover { background: none; }

button.danger { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 26%, var(--line)); }
button.danger:hover { background: var(--danger-wash); }

button.wide { width: 100%; }
button.sm { min-height: 34px; padding: 0 12px; font-size: 13.5px; font-weight: 560; }

/* Square icon-only control - reorder arrows, remove-photo. */
button.iconbtn {
  width: 34px;
  min-height: 34px;
  padding: 0;
  color: var(--muted);
}

button.iconbtn:hover { color: var(--ink); }

/* A button that reads as a link, for a name that opens an editor. */
button.linkish, a.linkish {
  border: none;
  background: none;
  padding: 0;
  min-height: 0;
  font: inherit;
  font-weight: 620;
  color: var(--ink);
  text-align: left;
}

a.linkish { text-decoration: none; }
button.linkish:hover, a.linkish:hover { background: none; color: var(--accent); text-decoration: underline; }

/* ------------------------------------------------------------- inputs */

input, select, textarea {
  width: 100%;
  min-height: 42px;
  padding: 9px 11px;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--card);
  color: var(--ink);
  /* 16px is the threshold below which iOS Safari zooms the page on focus. */
  font: 16px/1.4 inherit;
  font-family: inherit;
}

input:hover, select:hover, textarea:hover { border-color: color-mix(in srgb, var(--ink) 18%, var(--line)); }

input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 15%, transparent);
}

input::placeholder, textarea::placeholder { color: var(--faint); }
textarea { min-height: 88px; resize: vertical; line-height: 1.5; }

.field { display: block; }

.field > span {
  display: block;
  margin-bottom: 5px;
  font-size: 12.5px;
  font-weight: 620;
  color: var(--ink-2);
}

/* Written as `.field > span` too, so it has to out-specify the label rule
   above rather than being styled as one. */
.field-hint, .field > span.field-hint {
  display: block;
  margin: 5px 0 0;
  font-size: 11.5px;
  font-weight: 450;
  color: var(--muted);
  line-height: 1.45;
}

/* A form laid out as label / control pairs on desktop, stacked on a phone. */
.filters {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--s3);
  align-items: end;
}

/* A pair of time fields needs two columns' worth, or they stack and drag the
   whole row's baselines out of line. */
.filters .field.span2 { grid-column: span 2; }
.filters .field.span2 input { min-width: 0; }

/* Segmented control: the one and only "pick one of these" pattern. */
.seg {
  display: inline-flex;
  width: 100%;
  padding: 3px;
  gap: 3px;
  background: var(--sunken);
  border: 1px solid var(--line);
  border-radius: var(--r);
}

.seg-btn {
  flex: 1 1 0;
  flex-direction: column;
  gap: 1px;
  min-height: 38px;
  padding: 5px 10px;
  border: 1px solid transparent;
  border-radius: 4px;
  background: none;
  color: var(--muted);
  font-size: 13.5px;
  font-weight: 600;
  line-height: 1.25;
}

.seg-btn:hover { background: var(--hover); }

.seg-btn[aria-pressed="true"] {
  background: var(--card);
  border-color: var(--line);
  color: var(--ink);
  font-weight: 660;
}

.seg-btn[aria-pressed="true"]:hover { background: var(--card); }
.seg-btn .tiny { color: var(--faint); font-weight: 500; }
.seg-btn[aria-pressed="true"] .tiny { color: var(--muted); }

/* In-page tabs: underlined, matching the section nav. */
.tabs {
  display: flex;
  gap: var(--s4);
  margin-bottom: var(--s4);
  border-bottom: 1px solid var(--line);
}

.tabs button {
  min-height: 42px;
  padding: 0 2px 0;
  border: none;
  border-bottom: 2px solid transparent;
  border-radius: 0;
  background: none;
  color: var(--muted);
  font-size: 15px;
  font-weight: 570;
}

.tabs button:hover { background: none; color: var(--ink-2); }

.tabs button[aria-current="true"] {
  color: var(--accent);
  border-bottom-color: var(--accent);
  font-weight: 650;
}

.check-list { display: grid; gap: 6px; }

.check-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 11px;
  border: 1px solid var(--line);
  border-radius: var(--r);
  min-height: var(--tap);
  cursor: pointer;
}

.check-row:hover { background: var(--hover); }

.check-row input[type="checkbox"],
.check-row input[type="radio"] {
  width: 18px;
  min-height: 18px;
  height: 18px;
  flex: none;
  accent-color: var(--accent);
}

.check-row.on { background: var(--accent-wash); border-color: color-mix(in srgb, var(--accent) 40%, var(--line)); }

.switch-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: var(--s4);
  cursor: pointer;
}

/* Inputs are width:100% by default, which is right for a text field and
   ruinous for a checkbox sitting next to a paragraph. */
.switch-row input[type="checkbox"] {
  width: 18px;
  height: 18px;
  min-height: 18px;
  flex: none;
  margin-top: 2px;
  accent-color: var(--accent);
}

/* ------------------------------------------------------------ status */

.pill {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 9px;
  border-radius: 5px;
  font-size: 12.5px;
  font-weight: 650;
  white-space: nowrap;
  background: var(--line-2);
  color: var(--ink-2);
}

.pill.done { background: var(--done-wash); color: var(--done); }
.pill.open { background: var(--accent-wash); color: var(--accent); }
.pill.idle { background: var(--line-2); color: var(--muted); }
.pill.late { background: var(--warn-wash); color: var(--warn); }
.pill.note { background: var(--line-2); color: var(--ink-2); }

/* The two checklists. One is the routine job, one is the quick pass, so the
   quick one is the tinted exception rather than both shouting. */
.pill.checkin {
  background: var(--checkin-wash);
  color: var(--checkin);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--checkin) 28%, transparent);
}

.banner.checkin { background: var(--checkin-wash); color: var(--checkin); }

.pill.type-full { background: var(--line-2); color: var(--ink-2); }
.pill.type-check { background: var(--warn-wash); color: var(--warn); }

/* On the plan the priority number itself turns pink, so a check-in reads at a
   glance down a column of grey ones. */
.prio.checkin {
  background: var(--checkin-wash);
  color: var(--checkin);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--checkin) 35%, transparent);
}

.checkmark {
  flex: none;
  padding: 0 4px;
  border-radius: 3px;
  background: var(--checkin);
  color: var(--card);
  font-size: 9.5px;
  font-weight: 800;
  letter-spacing: .04em;
}

.prio {
  flex: none;
  display: inline-grid;
  place-items: center;
  min-width: 24px;
  height: 24px;
  padding: 0 6px;
  border-radius: 5px;
  background: var(--line-2);
  color: var(--ink-2);
  font-size: 13px;
  font-weight: 700;
}

.meter {
  display: block;
  height: 5px;
  border-radius: 99px;
  background: var(--line-2);
  overflow: hidden;
}

.meter > i { display: block; height: 100%; background: var(--accent); transition: width .2s; }
.meter.lg { height: 10px; }
.meter.full > i { background: var(--done); }

/* ------------------------------------------------------------- lists */

.list-item { padding: var(--s3) var(--s4); border-top: 1px solid var(--line-2); }
.list-item:first-child { border-top: none; }

.empty { padding: var(--s6) var(--s5); text-align: center; color: var(--muted); font-size: 14.5px; }
.empty b { display: block; color: var(--ink-2); font-size: 17px; margin-bottom: 5px; font-weight: 660; }

.banner { padding: var(--s3) var(--s4); font-size: 14.5px; line-height: 1.5; }
.banner.warn { background: var(--warn-wash); color: var(--warn); }
.banner.info { background: var(--accent-wash); color: var(--accent); }
.banner.done { background: var(--done-wash); color: var(--done); }
/* No modifier: a plain statement of where something stands, not an alert. */
.banner.plain { background: var(--sunken); color: var(--ink-2); }
.banner strong { font-weight: 680; }
.banner.inset { border-radius: var(--r); margin: 0; }

/* ------------------------------------------------------- job / building rows */

/* Overview and a cleaner's home list. These are the things you actually tap,
   so they are sized like buttons rather than table rows: the name big enough
   to read at arm's length, the status and count in their own right-hand
   column, and a chevron so it is obvious the row opens something. */
.tile {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--s3);
  width: 100%;
  min-height: 68px;
  padding: var(--s3) var(--s4);
  border: none;
  border-top: 1px solid var(--line-2);
  border-radius: 0;
  background: none;
  text-align: left;
  font-weight: 400;
}

.tile:first-of-type { border-top: none; }
.tile:hover { background: var(--hover); }
.tile[disabled] { opacity: 1; cursor: default; }
.tile[disabled]:hover { background: none; }

.tile-main { flex: 1; min-width: 0; }
.tile-title { display: flex; align-items: center; flex-wrap: wrap; gap: var(--s2); }
.tile .name { font-size: 17px; font-weight: 660; letter-spacing: -0.01em; color: var(--ink); }

.tile-end {
  flex: none;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 3px;
}

.tile-chev { flex: none; color: var(--faint); }

/* On a phone the right-hand column has to give the building name room, so the
   trimmings go: "14/22" under "In progress" is unambiguous on its own, and the
   sign-off time is repeated on the detail line underneath. */
@media (max-width: 560px) {
  .hide-narrow { display: none; }
  .tile { padding: var(--s3) var(--s3); gap: var(--s2); }
  .tile .name { font-size: 16.5px; }

  /* Without this the name is an unshrinkable block, so a long one pushes the
     order number onto a line of its own above it and the row reads as two
     half-rows. Given room to shrink, the number and the name share the first
     line and the name wraps underneath itself where it needs to. */
  .tile .name { flex: 1; min-width: 0; }

  /* The right-hand column is a status, not a headline. Trimming it on a phone
     is what buys the name enough width to stay on one line. */
  .tile-end .pill { font-size: 11.5px; padding: 2px 8px; }
  .tile-count { font-size: 12.5px; }
  .tile-chev { margin-left: -4px; }
}

/* One quiet green rule down the edge of anything finished. */
.tile.finished { box-shadow: inset 3px 0 0 var(--done); }
.tile-meta > .grow { min-width: 14ch; }

.tile-meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px var(--s2);
  margin-top: 5px;
  font-size: 13px;
  color: var(--muted);
}
.tile-count { font-size: 13px; font-weight: 650; color: var(--ink-2); }
/* The bar is the row's last line, spanning it end to end - inside the text
   column it stopped short of the count and read as an underline. */
.tile > .meter { flex: 1 1 100%; margin-top: 9px; }

.crow-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); }

.crow {
  display: block;
  min-height: 56px;
  padding: 11px var(--s3);
  border: none;
  border-top: 1px solid var(--line-2);
  border-radius: 0;
  background: none;
  text-align: left;
  font-weight: 400;
}

.crow:hover { background: var(--hover); }
.crow[disabled] { opacity: 1; cursor: default; }
.crow[disabled]:hover { background: none; }
.crow-top { display: flex; align-items: center; justify-content: space-between; gap: 6px; }
.crow-name { font-weight: 620; font-size: 15px; }
.crow-sub { display: block; margin-top: 2px; font-size: 12.5px; color: var(--muted); }
.crow-dot { flex: none; width: 7px; height: 7px; border-radius: 50%; background: var(--warn); }

/* ------------------------------------------------------------ home page */

/* The one line that answers "how is today going" before anything else. */
.headline {
  margin: 0;
  font-size: 20px;
  font-weight: 560;
  line-height: 1.3;
  letter-spacing: -0.015em;
  color: var(--ink-2);
}

.headline b { font-weight: 700; color: var(--ink); }
.headline + .meter { margin-top: var(--s3); }

/* ------------------------------------------------- who else is on today */

/* Names wrap into as many rows as they need: a big day is fifteen people and
   a quiet one is two, and neither should scroll or stretch. */
.crew-list { display: flex; flex-wrap: wrap; gap: var(--s2); }

.crew-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  padding: 5px 10px 5px 5px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--sunken);
  min-width: 0;
}

/* You, in the list of who is on - so the count reads as a crew, not as
   "everyone except me". */
.crew-chip.me {
  border-color: color-mix(in srgb, var(--accent) 45%, var(--line));
  background: color-mix(in srgb, var(--accent) 10%, var(--sunken));
}

.crew-who { display: flex; flex-direction: column; line-height: 1.25; min-width: 0; }
.crew-name { font-size: 13.5px; font-weight: 620; white-space: nowrap; }
.crew-who .tiny { white-space: nowrap; }

/* Tap-to-call, because a phone number you have to retype is not a shortcut. */
.callrow { display: grid; gap: var(--s2); }
.callrow .btn { justify-content: flex-start; gap: var(--s2); }
.callrow .btn .num { margin-left: auto; font-weight: 550; }

@media (min-width: 560px) { .callrow { grid-template-columns: 1fr 1fr; } }

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

/* Three figures, sized to be read at a glance rather than squinted at. */
.stats { display: grid; grid-template-columns: repeat(3, 1fr); border-top: 1px solid var(--line-2); }
.stat { padding: var(--s3) var(--s2); text-align: center; border-left: 1px solid var(--line-2); }
.stat:first-child { border-left: none; }
.stat b { display: block; font-size: 28px; font-weight: 700; line-height: 1.15; letter-spacing: -0.025em; }
.stat span { display: block; margin-top: 3px; font-size: 13px; font-weight: 550; color: var(--muted); }
.stat.is-warn b { color: var(--warn); }
.stat.is-done b { color: var(--done); }

/* ------------------------------------------------------- period pickers */

.periodnav { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: var(--s2); }
.periodnav .pn-side { display: flex; gap: 6px; }
.periodnav .pn-left { justify-self: start; }
.periodnav .pn-right { justify-self: end; }
.periodnav .period-title { text-align: center; font-size: 17px; font-weight: 680; white-space: nowrap; }
.periodnav .period-sub { display: block; font-size: 12.5px; font-weight: 450; color: var(--muted); }

.periodnav button {
  min-height: 40px;
  padding: 0 12px;
  font-size: 14px;
  font-weight: 600;
  white-space: nowrap;
  border-color: var(--line);
}

.periodnav button.ghost { border-color: var(--line); background: var(--card); }
.periodnav button.ghost:hover { background: var(--hover); }

/* ------------------------------------------------- a building's checklist */

/* The progress line follows you down a sixty-item list, so "how far am I"
   never needs a scroll back to the top. */
.progresshead {
  position: sticky;
  top: var(--bar-total);
  z-index: 12;
  background: var(--card);
  border-bottom: 1px solid var(--line);
}

.progresshead .meter { border-radius: 0; height: 3px; }
.progresshead-inner { display: flex; align-items: center; gap: var(--s3); padding: 9px var(--s4); }
.progresshead .count { font-size: 14px; font-weight: 660; }

.taskwrap { border-top: 1px solid var(--line-2); }
.taskwrap:first-child { border-top: none; }

.task {
  display: flex;
  align-items: flex-start;
  gap: var(--s3);
  width: 100%;
  padding: 11px var(--s4);
  border: none;
  border-radius: 0;
  background: none;
  text-align: left;
  min-height: var(--tap);
  font-weight: 400;
}

.task:hover { background: var(--hover); }
.task.is-done { background: color-mix(in srgb, var(--done) 5%, transparent); }
.task.is-done:hover { background: color-mix(in srgb, var(--done) 8%, transparent); }
.task[disabled] { opacity: 1; cursor: default; }

.box {
  flex: none;
  width: 22px;
  height: 22px;
  margin-top: 1px;
  border: 1.75px solid var(--faint);
  border-radius: 5px;
  display: grid;
  place-items: center;
  color: transparent;
  transition: background .12s, border-color .12s;
}

.box .ico { width: 14px; height: 14px; stroke-width: 2.6; vertical-align: 0; }
.is-done .box { border-color: var(--done); background: var(--done); color: #fff; }

.task .item { font-weight: 600; font-size: 14.5px; }
.task.is-done .item { color: var(--ink-2); }
.task .desc { display: block; color: var(--muted); font-size: 12.5px; margin-top: 1px; }
.task .who { display: block; color: var(--muted); font-size: 11.5px; margin-top: 3px; }
.task .who:empty { display: none; }

/* The one action that ends the job, always within thumb reach. */
.actionbar {
  position: sticky;
  bottom: 0;
  z-index: 14;
  display: flex;
  gap: var(--s2);
  padding: 10px var(--s4);
  padding-bottom: calc(10px + env(safe-area-inset-bottom));
  margin: var(--s4) calc(var(--s4) * -1) 0;
  background: color-mix(in srgb, var(--card) 92%, transparent);
  backdrop-filter: blur(10px);
  border-top: 1px solid var(--line);
}

.actionbar .primary { flex: 1; min-height: 46px; }
.actionbar .ghost { flex: none; border-color: var(--line); }

/* The bar's only button when the job's own button is the screen above it. */
.actionbar .wide { flex: 1; min-height: 46px; }

/* ------------------------------------------- a building with nothing to tick */

/* Some buildings are a single job, and a checklist of one line saying "clean
   it" helps nobody. The screen for them is the button that ends the job:
   thumb-sized on a phone, and on a laptop a target you can hit without
   aiming. */
.oneshot {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s3);
  padding: var(--s6) var(--s4);
  text-align: center;
}

.oneshot-mark {
  display: grid;
  place-items: center;
  width: 54px;
  height: 54px;
  border-radius: 50%;
  background: var(--accent-wash);
  color: var(--accent);
}

.oneshot-mark .ico { width: 26px; height: 26px; vertical-align: 0; stroke-width: 2; }

.oneshot-lead {
  margin: 0;
  max-width: 34ch;
  color: var(--ink-2);
  font-size: 15.5px;
  line-height: 1.45;
}

/* Wide enough to be the obvious thing to press, capped so it doesn't become a
   slab across a desktop window. */
.oneshot-btn { width: 100%; max-width: 360px; min-height: 52px; font-size: 16px; }

/* Signed off: the same card, in the colour the rest of the app uses for done.
   Reopening is a correction, so it stops being the loudest thing on screen. */
.oneshot.is-done .oneshot-mark { background: var(--done-wash); color: var(--done); }

.oneshot.is-done .oneshot-btn {
  background: none;
  border-color: var(--line);
  color: var(--ink-2);
  font-weight: 600;
}

.oneshot.is-done .oneshot-btn:hover { background: var(--hover); }

/* ----------------------------------------------------------- item photos */

.shots {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s2);
  padding: 0 var(--s4) var(--s3) 51px;
}

.shots.needed { background: var(--warn-wash); padding-top: 10px; }
.shot-badge { flex: none; }

.shot {
  position: relative;
  display: inline-block;
  width: 56px;
  height: 56px;
  border-radius: var(--r);
  overflow: hidden;
  background: var(--line-2);
  box-shadow: inset 0 0 0 1px var(--line);
}

.shot img { width: 100%; height: 100%; object-fit: cover; display: block; }

.shot-x {
  position: absolute;
  top: 2px;
  right: 2px;
  width: 20px;
  min-height: 20px;
  height: 20px;
  padding: 0;
  border: none;
  border-radius: 4px;
  background: rgb(12 16 22 / .68);
  color: #fff;
}

.shot-x:hover { background: rgb(12 16 22 / .85); }
.shot-x .ico { width: 12px; height: 12px; vertical-align: 0; }

.shot-add { min-height: 38px; padding: 0 11px; font-size: 13px; border-style: dashed; color: var(--ink-2); }

/* ---------------------------------------------------------------- grids */

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

table.sched { border-collapse: separate; border-spacing: 0; width: 100%; font-size: 13px; }

/* Fixed layout so the seven day columns are equal instead of being sized by
   whatever happens to be written in them. The min-width is what makes the
   wrapper actually scroll on a phone: without it the table is squeezed to
   390px, "8am-4pm" overflows its 37px column, and the times end up printed
   across the name beside them. */
table.sched { table-layout: fixed; min-width: 660px; }

table.sched th, table.sched td {
  border-right: 1px solid var(--line-2);
  border-bottom: 1px solid var(--line-2);
  padding: 0;
  /* height on a td is a minimum, so this plus height:100% on the button is
     what makes a cell fill its row instead of floating in the middle of it. */
  height: 1px;
}

table.sched td > .cell { height: 100%; }

table.sched th:last-child, table.sched td:last-child { border-right: none; }
table.sched tr:last-child th, table.sched tr:last-child td { border-bottom: none; }

table.sched thead th {
  position: sticky;
  top: 0;
  z-index: 3;
  padding: 7px 6px;
  background: var(--sunken);
  font-size: 11.5px;
  font-weight: 680;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--muted);
  text-align: center;
  white-space: nowrap;
}

table.sched thead th small {
  display: block;
  margin-top: 1px;
  font-size: 11px;
  font-weight: 500;
  text-transform: none;
  letter-spacing: 0;
  color: var(--faint);
}

table.sched thead th.weekend { color: var(--faint); }
table.sched thead th.is-today { color: var(--accent); }
table.sched thead th.is-today small { color: var(--accent); }

table.sched th.rowhead {
  position: sticky;
  left: 0;
  z-index: 2;
  width: 26%;
  min-width: 148px;
  padding: 8px 10px;
  background: var(--card);
  border-right: 1px solid var(--line);
  text-align: left;
  font-size: 13.5px;
  font-weight: 600;
}

table.sched th.rowhead small { display: block; font-size: 11px; font-weight: 450; color: var(--faint); margin-top: 1px; }
table.sched thead th.corner { left: 0; z-index: 4; text-align: left; padding-left: 10px; background: var(--sunken); }

td.today-col { background: color-mix(in srgb, var(--accent) 4%, transparent); }
td.weekend { background: var(--sunken); }

.grouphead-row th { padding: 0 !important; background: var(--sunken); position: sticky; left: 0; }

.grouphead {
  display: flex;
  align-items: center;
  gap: var(--s2);
  width: 100%;
  padding: 7px 10px;
  border: none;
  border-radius: 0;
  background: var(--sunken);
  color: var(--muted);
  font-size: 11.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .05em;
  text-align: left;
  min-height: 34px;
  cursor: pointer;
}

.grouphead:hover { color: var(--ink-2); background: var(--hover); }
.grouphead .chev { color: var(--faint); }

.cell {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  width: 100%;
  min-height: 54px;
  padding: 5px 4px;
  border: none;
  border-radius: 0;
  background: none;
}

.cell:hover { background: var(--hover); }
.cell.on { background: var(--accent-wash); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 28%, transparent); }
.cell.on.t-check { background: var(--warn-wash); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--warn) 28%, transparent); }
.cell.complete { background: var(--done-wash); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--done) 28%, transparent); }
.cell[disabled] { opacity: 1; cursor: default; }
.cell[disabled]:hover { background: none; }
.cell.on[disabled]:hover { background: var(--accent-wash); }
.cell.complete[disabled]:hover { background: var(--done-wash); }

.cell-top { display: flex; align-items: center; gap: 4px; }
.cell .names { font-size: 11px; color: var(--muted); line-height: 1.25; text-align: center; }
.cell .tickmark { color: var(--done); }

/* A standing job. Quiet on purpose: it says how this square got here, which
   matters when you are wondering why, and never before that. */
.cell .repeatmark { color: var(--faint); display: inline-flex; }
.cell .repeatmark .ico { width: 12px; height: 12px; vertical-align: 0; }
.cell .tickmark .ico { width: 13px; height: 13px; stroke-width: 2.6; vertical-align: 0; }
.cell .mini { width: 100%; height: 3px; border-radius: 99px; background: color-mix(in srgb, var(--ink) 12%, transparent); overflow: hidden; }
.cell .mini > i { display: block; height: 100%; background: var(--accent); }
.cell .mini.full > i { background: var(--done); }
.cell .plus { color: var(--faint); opacity: 0; transition: opacity .12s; }
.cell:hover .plus { opacity: 1; }

/* One letter for which checklist, because a whole word won't fit in a cell. */
.typetag {
  flex: none;
  display: grid;
  place-items: center;
  width: 15px;
  height: 15px;
  border-radius: 3px;
  font-size: 9.5px;
  font-weight: 800;
}

.typetag.t-full { background: var(--ink-2); color: var(--card); }
.typetag.t-check { background: var(--warn); color: #fff; }

.legend { display: flex; flex-wrap: wrap; gap: var(--s4); padding: 10px var(--s4); border-top: 1px solid var(--line-2); }
.legend span { display: inline-flex; align-items: center; gap: 6px; font-size: 11.5px; color: var(--muted); }
.legend i { width: 11px; height: 11px; border-radius: 3px; display: inline-block; }
.legend i.sw.t-full { background: var(--accent-wash); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 40%, transparent); }
.legend i.sw.t-check { background: var(--warn-wash); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--warn) 40%, transparent); }
.legend i.sw.yes { background: var(--done-wash); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--done) 40%, transparent); }
.legend i.sw.no { background: var(--line-2); box-shadow: inset 0 0 0 1px var(--line); }
.legend i.sw.done { background: var(--done-wash); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--done) 40%, transparent); }
.legend i.sw.changed-sw { background: var(--card); box-shadow: inset 0 0 0 2px var(--accent); }
.legend i.sw.shift-sw { background: var(--ink-2); height: 3px; border-radius: 2px; }
.legend.flat { border-top: none; padding: 0; gap: var(--s3); }

/* --------------------------------------------------------- availability */

table.avail td { padding: 7px 6px; text-align: center; vertical-align: middle; }
.avcell { font-size: 12.5px; font-weight: 600; line-height: 1.3; }
.avcell.yes { color: var(--ink-2); }
.avcell.no { color: var(--faint); font-weight: 450; }
.avcell .tiny { display: block; color: var(--faint); font-weight: 450; margin-top: 1px; }
.avcell.no .tiny.clash { color: var(--warn); font-weight: 620; }
table.avail td.dimmed { opacity: .28; }

/* Each square of the grid is the button that changes that one day. */
button.avcell {
  display: block;
  width: 100%;
  min-height: 46px;
  padding: 5px 4px;
  border-color: transparent;
  background: none;
  font-size: 12.5px;
}
button.avcell:hover { border-color: var(--line); background: var(--hover); }
button.avcell[disabled] { opacity: 1; }
button.avcell[disabled]:hover { border-color: transparent; background: none; }
/* Changed for that one day: outlined, so it reads at a glance without
   taking a colour that already means something. */
button.avcell.changed { border-color: var(--accent); }
.avcell .avwhy { color: var(--accent); font-style: italic; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* The one button that hands a week to the staff. Full width where that is
   the only sensible size, capped on a desktop so it stops being a slab. */
.approve { max-width: 420px; margin-inline: auto; }

/* --------------------------------------------------------------- roster */

.cell.rcell { min-height: 52px; }
.cell.rcell.off-day { background: var(--sunken); }
.cell.rcell.off-day:hover { background: var(--hover); }

.shift { display: flex; flex-direction: column; align-items: center; line-height: 1.3; width: 100%; }
.shift b { font-size: 12.5px; font-weight: 680; white-space: nowrap; }
.shift .tiny {
  color: var(--muted);
  max-width: 100%;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.shift + .shift { margin-top: 4px; padding-top: 4px; border-top: 1px dashed var(--line); }
.shift.clash b { color: var(--warn); }
.shift-marks { display: flex; gap: 3px; align-items: center; }
.shift-marks .ico { width: 12px; height: 12px; vertical-align: 0; }
.shift .warnmark { color: var(--warn); }
.offtext { color: var(--faint); font-size: 11px; font-weight: 620; letter-spacing: .04em; }
.offtext.add { opacity: .45; font-size: 15px; font-weight: 400; }
.cell:hover .offtext.add { opacity: 1; color: var(--accent); }
.shiftrow { border: 1px solid var(--line); border-radius: var(--r); }

/* --------------------------------------------------- checklist editing */

.brow { display: block; width: 100%; text-align: left; border: none; border-top: 1px solid var(--line-2); border-radius: 0; background: none; font-weight: 400; padding: var(--s3) var(--s4); }
.brow:first-of-type { border-top: none; }
.brow:hover { background: var(--hover); }
.brow strong { font-size: 14.5px; font-weight: 640; }

.itemrow { padding: 10px var(--s4); }
.itemrow strong { font-size: 14px; font-weight: 620; }

.reorder { display: grid; gap: 5px; max-height: 56vh; overflow-y: auto; overscroll-behavior: contain; }

.reorder-row {
  display: flex;
  align-items: center;
  gap: var(--s2);
  padding: 6px 10px;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--card);
  transition: transform .15s ease;
}

.reorder-row .grow { min-width: 0; }
.reorder-row strong { display: block; }
.reorder-sub { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; }

/* The handle is the only part that starts a drag, so the list still scrolls
   with a finger anywhere else - and touch-action stops the page scrolling
   out from under the row being dragged. */
.grip {
  flex: none;
  display: grid;
  place-items: center;
  width: 30px;
  height: 36px;
  margin-left: -6px;
  color: var(--faint);
  cursor: grab;
  touch-action: none;
  user-select: none;
}
.grip .ico { stroke-width: 3.2; }

.reorder-row.dragging {
  position: relative;
  z-index: 2;
  transition: none;
  border-color: var(--accent);
  box-shadow: 0 6px 18px rgb(0 0 0 / .16);
  cursor: grabbing;
}

/* A day's heading on the plan doubles as the way into putting it in order. */
button.dayhead {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  width: 100%;
  min-height: 0;
  padding: 2px 0;
  border: none;
  background: none;
  font: inherit;
  color: inherit;
  text-transform: inherit;
  letter-spacing: inherit;
  cursor: pointer;
}
button.dayhead:hover { background: var(--hover); color: var(--accent); }
.dayhead-hint {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  font-size: 9.5px;
  color: var(--accent);
}
.dayhead-hint .ico { width: 11px; height: 11px; }

/* ------------------------------ incomplete cleans from the last planned day */

.incomplete { border-color: color-mix(in srgb, var(--warn) 40%, var(--line)); }
.incomplete > h2 { background: var(--warn-wash); color: var(--warn); }
.incomplete > h2 .grow { display: inline-flex; align-items: center; gap: 6px; }
.incomplete > h2 .num { color: var(--warn); }

/* ------------------------------------------------------ people (admin) */

/* A phone gets stacked cards; a laptop gets a table, because reading twenty
   people is a scanning job and cards make you hunt. */
.people-row { display: block; }
.people-row .actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.people-row .actions button { min-height: 32px; padding: 0 10px; font-size: 12.5px; }

@media (min-width: 720px) {
  .people-row { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1.4fr) auto; align-items: center; gap: var(--s4); }
  .people-row .actions { margin-top: 0; justify-content: flex-end; flex-wrap: nowrap; }
}

.people-avail { font-size: 12.5px; color: var(--muted); }

/* ---------------------------------------------------------- login screen */

.loginmark {
  max-width: 560px;
  margin: 6vh auto var(--s3);
  text-align: center;
  font-size: 15px;
  font-weight: 680;
  letter-spacing: -0.01em;
  color: var(--muted);
}

.login { max-width: 360px; margin: 0 auto; }
.loginmark + .login { margin-top: 0; }
.padgap { display: block; }
.login h2 { justify-content: center; }
.login.wide-login { max-width: 560px; }
.pad.flush-bottom { padding-bottom: 0; }

/* Half one: tap your name. Tiles rather than rows - a face-sized target with
   the badge people already know from the roster, so finding yourself is
   recognition rather than reading. */
.people-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); gap: var(--s2); }

.persontile {
  flex-direction: column;
  gap: 6px;
  min-height: 108px;
  padding: var(--s3) var(--s2);
  border-color: var(--line);
  background: var(--card);
  font-weight: 500;
  text-align: center;
}

.persontile:hover { background: var(--hover); border-color: color-mix(in srgb, var(--accent) 40%, var(--line)); }
.persontile .avatar.lg { width: 44px; height: 44px; font-size: 15px; border-radius: var(--r-lg); }
.persontile .ptname { font-size: 15.5px; font-weight: 650; line-height: 1.2; }

.persontile .ptfull {
  font-size: 11.5px;
  font-weight: 450;
  color: var(--muted);
  line-height: 1.2;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
  white-space: nowrap;
}

/* Cleaners, then the office, each under its own heading. */
.login-sechead {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin: 0;
  padding: var(--s3) var(--s4) 0;
  font-size: 11.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--muted);
}
.login-section + .login-section { border-top: 1px solid var(--line-2); }

/* Whoever used this phone last, marked rather than moved to the front. */
.persontile.last { border-color: color-mix(in srgb, var(--accent) 55%, var(--line)); }

/* One name is the whole tile; no point repeating it underneath. */
.persontile .ptname + .ptfull:only-of-type { display: none; }

/* Half two: the pad, with the name it belongs to at the top. */
.pinhead {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: var(--s3) var(--s4);
  background: var(--sunken);
  border-bottom: 1px solid var(--line);
}

.pinhead strong { display: block; font-size: 16.5px; font-weight: 680; letter-spacing: -0.01em; }
.pinhead .small { display: block; }

.pinpad { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s2); }
.pinpad button { height: 60px; font-size: 21px; font-weight: 600; }
.pinpad button.ghost { border-color: transparent; background: none; color: var(--muted); }
.pinpad button.wide { grid-column: span 3; height: 52px; font-size: 16px; }

/* Fixed slots when the length is known: it says how many digits to expect and
   turns the last one into the submit. */
.pinslots {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 10px;
  min-height: 30px;
  letter-spacing: 8px;
  font-size: 22px;
  color: var(--accent);
}

.pinslots > i {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: 2px solid var(--line);
  transition: background .12s, border-color .12s;
}

.pinslots > i.on { background: var(--accent); border-color: var(--accent); }
.pinslots.working { opacity: .5; }

.installhint {
  display: flex;
  align-items: center;
  gap: var(--s2);
  max-width: 360px;
  margin: var(--s3) auto 0;
  padding: 10px var(--s3);
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--card);
  color: var(--muted);
  font-size: 12.5px;
}

.installhint button { min-height: 30px; padding: 0 10px; font-size: 12.5px; }

/* -------------------------------------------------------------- overlays */

/* Labelled, not a bare "+": a circle with a plus in it is a guess, and the
   one thing this button does - report a problem - is worth saying out loud. */
.fab {
  position: fixed;
  right: 20px;
  bottom: 20px;
  z-index: 18;
  gap: var(--s2);
  height: 52px;
  min-height: 52px;
  padding: 0 20px;
  border: none;
  border-radius: 99px;
  background: var(--accent);
  color: var(--accent-ink);
  font-size: 15.5px;
  font-weight: 650;
  box-shadow: var(--lift);
}

.fab:hover { background: color-mix(in srgb, var(--accent) 86%, #000); }
.fab .ico { width: 20px; height: 20px; stroke-width: 2.4; vertical-align: 0; }

.toast {
  position: fixed;
  left: 50%;
  bottom: 24px;
  transform: translateX(-50%);
  z-index: 60;
  max-width: min(440px, calc(100vw - 32px));
  padding: 10px var(--s4);
  border-radius: var(--r);
  background: var(--ink);
  color: var(--bg);
  font-size: 13.5px;
  font-weight: 550;
  box-shadow: var(--lift);
  animation: rise var(--dur-2) var(--ease-out);
}

@keyframes rise { from { opacity: 0; transform: translate(-50%, 12px); } }
@keyframes sink { to { opacity: 0; transform: translate(-50%, 8px); } }
.toast.going { animation: sink var(--dur-2) ease forwards; }
.toast.bad { background: var(--danger); color: #fff; }

.err { color: var(--danger); font-size: 13px; font-weight: 560; }
.err:empty { display: none; }
.thumb { max-width: 220px; border-radius: var(--r); margin-top: var(--s2); display: block; }

code.phrase {
  font: 600 12.5px ui-monospace, SFMono-Regular, Menlo, monospace;
  background: var(--danger-wash);
  color: var(--danger);
  padding: 1px 6px;
  border-radius: 4px;
}

.banner code, .note code { font: 600 12px ui-monospace, SFMono-Regular, Menlo, monospace; }

/* ==================================================================== motion

   Everything that moves is in one place, so the app has a single vocabulary
   rather than an animation invented per screen.

   The screen entrance is driven by a class the router adds, not by the cards
   existing: several screens refresh themselves on a timer, and a page that
   fades itself back in every 45 seconds is a page that looks broken. */

@keyframes card-in { from { opacity: 0; transform: translateY(8px); } }
@keyframes fade-out { to { opacity: 0; } }
@keyframes sheet-out { to { transform: translateY(12px); opacity: 0; } }
@keyframes sheet-out-mobile { to { transform: translateY(100%); } }
@keyframes box-pop { 40% { transform: scale(1.25); } }
@keyframes row-settle { from { background: color-mix(in srgb, var(--done) 22%, transparent); } }
@keyframes pop-in { from { opacity: 0; transform: scale(.8); } }
@keyframes shimmer { to { background-position: 200% 0; } }

/* Cards arrive together but not quite: a few milliseconds apart is enough to
   read as one screen settling instead of a list of things appearing. Capped
   at six, because a stagger long enough to notice is a screen that is slow. */
.screen-in > * { animation: card-in var(--dur-3) var(--ease-out) both; }
.screen-in > :nth-child(1) { animation-delay: 0ms; }
.screen-in > :nth-child(2) { animation-delay: 25ms; }
.screen-in > :nth-child(3) { animation-delay: 50ms; }
.screen-in > :nth-child(4) { animation-delay: 75ms; }
.screen-in > :nth-child(5) { animation-delay: 100ms; }
.screen-in > :nth-child(n + 6) { animation-delay: 120ms; }

/* Press feedback. The colour flash alone reads as a hover on a phone, where
   there is no such thing; a fraction of a millimetre of give reads as a
   button being pushed. */
.tile, .crow, .task, .persontile, .card-toggle, .brow, .cell, button, .btn {
  transition: transform var(--dur-1) var(--ease-out), background var(--dur-1) linear;
}

.tile:active, .crow:active, .task:active, .persontile:active,
.card-toggle:active, .brow:active { transform: scale(.995); }

button:active, .btn:active, .cell:active { transform: scale(.97); }
button:disabled:active, .btn:disabled:active { transform: none; }

/* Ticking an item is the thing this app is for, so it is the one interaction
   that answers back: the box springs shut and the row settles out of green.
   The class comes off on its own, so a re-render never replays it. */
.task.just-done .box { animation: box-pop var(--dur-3) var(--ease-spring); }
.task.just-done { animation: row-settle .45s var(--ease-out); }
.box .ico {
  transform: scale(.5);
  opacity: 0;
  transition: transform var(--dur-2) var(--ease-spring), opacity var(--dur-1) linear;
}

.is-done .box .ico { transform: scale(1); opacity: 1; }

/* Who ticked it, arriving after the tick rather than with it. */
.task .who:not(:empty) { animation: card-in var(--dur-2) var(--ease-out); }

.fab { animation: pop-in var(--dur-2) var(--ease-spring); }
.fab:active { transform: scale(.94); }

/* Loading. A shimmer that is shaped like the screen it is standing in for
   says "this is coming" in a way that the word "Loading" cannot. */
.skeleton { padding: var(--s4); display: grid; gap: var(--s3); }

.skeleton i {
  display: block;
  height: 14px;
  border-radius: 99px;
  background: linear-gradient(90deg,
    var(--line-2) 25%, var(--hover) 37%, var(--line-2) 50%);
  background-size: 200% 100%;
  animation: shimmer 1.4s linear infinite;
}

.skeleton i.tall { height: 42px; border-radius: var(--r); }
.skeleton i.half { width: 55%; }
.skeleton i.third { width: 32%; }

/* Modal sheet */
.sheet-bg {
  position: fixed;
  inset: 0;
  background: rgb(10 13 18 / .45);
  z-index: 40;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  animation: fade .12s ease;
}

@keyframes fade { from { opacity: 0; } }
@media (min-width: 660px) { .sheet-bg { align-items: center; } }

/* Closing is animated too, so a sheet leaves the way it arrived instead of
   blinking out. The node is dropped by a timer either way - the motion is a
   nicety and must never be the thing that decides whether it goes. */
.sheet-bg.closing { animation: fade-out var(--dur-2) ease forwards; pointer-events: none; }
.sheet-bg.closing .sheet { animation: sheet-out-mobile var(--dur-2) ease-in forwards; }

@media (min-width: 660px) {
  .sheet-bg.closing .sheet { animation-name: sheet-out; }
}

.sheet {
  background: var(--card);
  width: 100%;
  max-width: 460px;
  max-height: 92vh;
  overflow-y: auto;
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  padding-bottom: env(safe-area-inset-bottom);
  box-shadow: var(--lift);
  /* Up from the bottom edge on a phone, where that is where sheets come
     from; a small settle on a desktop, where it is a dialog in the middle. */
  animation: slide var(--dur-3) var(--ease-out);
}

@keyframes slide { from { transform: translateY(100%); } }

@media (min-width: 660px) {
  .sheet { border-radius: var(--r-lg); animation-name: slide-centre; }
  @keyframes slide-centre { from { transform: translateY(12px); opacity: 0; } }
}

/* The grab handle every phone sheet has. Decoration, but the kind that tells
   you which edge the thing came from and which way to push it back. */
@media (max-width: 659px) {
  .sheet::before {
    content: '';
    display: block;
    width: 36px;
    height: 4px;
    margin: 8px auto 0;
    border-radius: 99px;
    background: var(--line);
  }
}

.sheet-head {
  position: sticky;
  top: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  padding: var(--s4) var(--s4) var(--s3);
  background: var(--card);
  border-bottom: 1px solid var(--line-2);
}

.sheet-head strong { font-size: 15.5px; font-weight: 660; letter-spacing: -0.01em; }
.sheet-head .small { color: var(--muted); }
.dialog-body { font-size: 13.5px; line-height: 1.55; color: var(--ink-2); margin: 0; }
.dialog-body strong { color: var(--ink); font-weight: 640; }

/* A choice that is the whole point of the sheet - tapped with a thumb,
   outdoors, sometimes in gloves. */
.bigchoice {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  width: 100%;
  min-height: 60px;
  padding: 11px var(--s4);
  text-align: left;
}

.bigchoice strong { font-size: 15.5px; font-weight: 650; }
.bigchoice .small { color: var(--muted); font-weight: 450; }

/* ------------------------------------------------------- mobile bottom nav */

/* Below 768px the section nav moves to a fixed tab bar at the bottom: thumb
   reach matters more than a strip of vertical space, and it stops the
   header/testbar/nav stack eating half the screen before any content shows.
   Last in the file so it wins over the earlier nav.nav and main rules. */
@media (max-width: 767px) {
  nav.nav {
    position: fixed;
    inset: auto 0 0 0;
    top: auto;
    z-index: 25;
    justify-content: space-around;
    gap: 0;
    padding: 3px max(4px, env(safe-area-inset-left)) calc(3px + env(safe-area-inset-bottom));
    background: color-mix(in srgb, var(--card) 95%, transparent);
    backdrop-filter: blur(12px);
    border-bottom: none;
    border-top: 1px solid var(--line);
    overflow-x: visible;
  }

  nav.nav button {
    flex: 1 1 0;
    flex-direction: column;
    justify-content: center;
    gap: 1px;
    min-height: 54px;
    padding: 5px 2px;
    border-bottom: none;
    border-radius: var(--r);
    font-size: 11.5px;
    font-weight: 620;
  }

  nav.nav button[aria-current="true"] { color: var(--accent); border-bottom-color: transparent; }
  nav.nav .navicon { display: block; }
  nav.nav .navlabel { white-space: nowrap; }

  /* The icon sits in a pill that only exists on the current tab. It grows in
     rather than appearing, so moving between tabs reads as one thing sliding
     across the bar rather than two things blinking. */
  nav.nav .navicon {
    display: grid;
    place-items: center;
    width: 56px;
    height: 28px;
    border-radius: 99px;
    transition: background var(--dur-2) var(--ease-out),
      transform var(--dur-2) var(--ease-spring);
  }

  nav.nav .navicon .ico {
    width: 22px;
    height: 22px;
    vertical-align: 0;
    transition: stroke-width var(--dur-2) linear;
  }

  nav.nav button[aria-current="true"] .navicon {
    background: var(--accent-wash);
    transform: translateY(-1px);
  }

  nav.nav button[aria-current="true"] .ico { stroke-width: 2.2; }
  nav.nav button:active .navicon { transform: scale(.9); }

  main { padding-bottom: calc(66px + env(safe-area-inset-bottom)); }
  .toast { bottom: calc(74px + env(safe-area-inset-bottom)); }
  .fab { bottom: calc(74px + env(safe-area-inset-bottom)); right: 14px; }

  /* The sticky action bar sits above the tab bar, not under it. */
  .actionbar { bottom: calc(56px + env(safe-area-inset-bottom)); padding-bottom: 10px; }
  main { padding-bottom: calc(120px + env(safe-area-inset-bottom)); }

  .stat b { font-size: 24px; }
  .stat span { font-size: 12px; }
  .crow-list { grid-template-columns: 1fr; }

  /* On a phone a card is an object you are holding, not a region of a page:
     a little depth under it beats a hairline drawn around it. */
  .card {
    border-color: transparent;
    border-radius: 14px;
    box-shadow: var(--raise);
    margin-bottom: var(--s3);
  }

  /* Except the two that are furniture rather than content - a bar with a
     shadow reads as a card that has come loose. */
  .actionbar, .progresshead { box-shadow: none; }
}

/* Dark mode has no light to cast, so depth there is a lighter edge instead of
   a darker shadow - the same job done the way the medium allows. */
@media (max-width: 767px) and (prefers-color-scheme: dark) {
  .card { border-color: var(--line); box-shadow: none; }
}

/* On a phone the checklist is the whole screen, so its progress line sits
   directly under the app bar with nothing between. */
@media (max-width: 767px) {
  .progresshead { top: var(--bar-total); }
}

/* ============================================================= printing */

.printonly { display: none; }

/* A4 landscape: seven day columns plus a name column only fit once the page
   furniture goes and the type steps down. Elements are removed rather than
   hidden so nothing leaves a gap. */
@media print {
  /* Buildings run down the page, not across it, so portrait is simply more
     rows per sheet - there are seven day columns either way. The roster is
     the exception: its squares carry start and finish times, which need the
     wider sheet, so it asks for a landscape page of its own. */
  @page { size: A4 portrait; margin: 9mm 8mm; }
  @page rostersheet { size: A4 landscape; margin: 9mm 8mm; }
  #printarea.print-landscape { page: rostersheet; }

  header.bar, nav.nav, .fab, .toast, .testbar, .noprint, .sheet-bg,
  .card-toggle, .legend, .actionbar { display: none !important; }

  html, body { background: #fff; color: #000; }
  main, main.wide { max-width: none; padding: 0; }

  .printonly { display: block; }

  .print-title { font-size: 13pt; font-weight: 700; letter-spacing: .02em; margin: 0 0 5pt; text-align: center; }
  .print-foot { font-size: 7.5pt; color: #444; margin: 5pt 0 0; text-align: center; }

  main > .card:not(#printarea) { display: none !important; }
  main > .tabs, main > .row, main > p { display: none !important; }

  #printarea { border: none; background: #fff; margin: 0; padding: 0; }
  .grid-wrap { overflow: visible !important; }

  /* The min-width that makes the grid scroll on a phone would push it off the
     side of the page here; paper has a fixed width and no scrollbar. Fixed
     layout is what makes it space-efficient: left to itself the browser hands
     a column with a long note half the sheet, and the other six get slivers. */
  table.sched {
    width: 100%;
    min-width: 0;
    table-layout: fixed;
    font-size: 8pt;
  }

  table.sched th, table.sched td { border-color: #999 !important; padding: 2pt 2.5pt; }
  table.sched thead th { background: #eee !important; color: #000; position: static !important; }

  /* The name column takes what it needs and no more - every millimetre it
     gives back is shared between the seven days. Fixed layout reads its
     widths off the first row, so the size has to be set on the header cell:
     a width on the row headings below it would be ignored. */
  table.sched thead th.corner { width: 27%; }
  table.sched.roster thead th.corner { width: 17%; }

  table.sched th.rowhead {
    position: static !important;
    background: #fff !important;
    font-size: 8.5pt;
    line-height: 1.15;
    overflow-wrap: anywhere;
  }
  table.sched th.rowhead small { color: #555; font-size: 7pt; }

  /* A plan that runs onto a second page needs its days named there too. */
  table.sched thead { display: table-header-group; }

  td.today-col, td.weekend, .cell.rcell.off-day { background: #fff !important; }

  .cell.rcell { min-height: 0; background: #fff !important; box-shadow: none; border: none; padding: 1pt; }
  .shift b { font-size: 9pt; color: #000; }
  .shift .tiny { color: #444; }
  .shift .warnmark { color: #000; }
  .offtext { color: #666; font-size: 8pt; }

  /* The "+" and the preference star are on-screen affordances - there is
     nothing to tap on paper, and they'd read as marks that mean something. */
  .offtext.add, .prefstar { display: none !important; }

  /* Today is a screen idea. On a printed sheet it is just another column. */
  table.sched thead th.is-today,
  table.sched thead th.is-today small { color: #000 !important; }

  /* --- the weekly cleaning plan --- */

  /* A group folded shut on screen is still part of the week. Printing the
     plan with buildings silently missing would be worse than useless. */
  table.sched tr[data-group] { display: table-row !important; }
  .grouphead-row th { padding: 4pt 2.5pt 1pt !important; }
  .grouphead-row .grouphead {
    font-size: 8.5pt; font-weight: 700; color: #000;
    padding: 0; min-height: 0; background: none;
  }
  .grouphead-row .chev, .grouphead-row .num { display: none !important; }

  /* A heading stranded at the foot of a page belongs to nothing. */
  .grouphead-row { break-after: avoid; }

  /* Colour is how the two cleans tell themselves apart on screen; on paper,
     which is often greyscale, the word does it instead. The tinted fills and
     coloured outlines print as muddy boxes, so every square is plain white
     and the writing inside it carries the meaning. */
  .cell, .cell.on, .cell.on.t-check, .cell.complete {
    background: #fff !important;
    border: none;
    box-shadow: none !important;
  }
  .cell .typetag, .cell .plus, .cell .mini { display: none !important; }
  /* Why a job is on the plan is an office question, and the printed sheet has
     no legend to explain the mark. */
  .cell .repeatmark { display: none !important; }

  /* An unscheduled day is a blank square. On screen it is a 54px tap target;
     on paper that target is dead space, and with seven of them a row it is
     what stops a page holding more buildings. */
  .cell { min-height: 0; padding: 0; gap: 1pt; }
  .cell-top { gap: 2pt; }
  /* Ink, not pink: the plan gets printed on a mono printer. */
  .checkmark { background: #000 !important; color: #fff !important; font-size: 6.5pt; }
  .prio.checkin { box-shadow: none !important; }
  /* The screen badge is a fixed 24px so it stays a tap-sized dot; on paper
     that height sets the height of every row it appears in. */
  .cell .prio {
    background: #000 !important; color: #fff !important;
    font-size: 7.5pt; min-width: 11pt; height: auto; padding: 0 1.5pt;
    line-height: 1.25;
  }
  .cell .celltype { font-size: 8pt; color: #000; display: block; margin-top: 0; line-height: 1.1; }
  .cell .names {
    font-size: 6.5pt; color: #444; line-height: 1.15;
    white-space: normal; overflow: visible; overflow-wrap: anywhere;
  }
  .cell .tickmark { color: #000; }
  .cell .tickmark .ico { width: 8pt; height: 8pt; }

  /* On screen a note is clipped to keep the row tight; on paper nothing
     should be silently cut out of a document people work from. */
  .shift .tiny { white-space: normal; overflow: visible; }

  table.sched tr { break-inside: avoid; }
}

/* Anything hidden from checklists reads as switched off rather than deleted -
   one rule, instead of an inline opacity at each call site. */
[data-off="1"] { opacity: .5; }

/* Helper text under a label is a second line, not a run-on sentence. Set
   once here rather than as a display:block at every call site. */
.check-row .grow > .tiny,
.check-row .grow > .small,
.switch-row .grow > strong,
.switch-row .grow > .small,
.people-row .grow > strong,
.people-row .grow > .tiny,
.bigchoice .small,
.seg-btn .tiny,
.field .field-hint { display: block; }

.switch-row .grow > strong { font-size: 14px; font-weight: 640; margin-bottom: 2px; }
.people-row .grow > strong { font-size: 14.5px; font-weight: 620; }
.check-row .grow > .tiny { margin-top: 1px; color: var(--muted); }

/* ------------------------------------------------- rostering preferences */

/* A day someone would rather work. Deliberately quiet: it is a preference,
   not a rule, and it must never read as loudly as an availability clash. */
.prefstar { display: inline-flex; color: var(--accent); }
.prefstar .ico { width: 12px; height: 12px; vertical-align: 0; fill: currentColor; stroke-width: 0; }
.legend .prefstar { margin-right: 2px; }

.avcell.preferred { color: var(--accent); }
.avcell .prefstar { margin-right: 3px; vertical-align: -1px; }

/* An empty roster cell on a day they'd rather work - a nudge towards the
   person who wants the shift, without shouting at anyone. */
.cell.rcell.wants { background: color-mix(in srgb, var(--accent) 4%, transparent); }
.cell.rcell.wants .prefstar { opacity: .7; }

/* Hours rostered against the weekly target they gave. */
table.sched th.rowhead .num.over { color: var(--warn); font-weight: 700; }

/* Beats `.check-row .grow > .tiny`, which would otherwise mute the one line
   on that row that needs to stand out. */
.warntext, .check-row .grow > .tiny.warntext { color: var(--warn); }

/* --------------------------------------------- one person's availability */

/* A month at a time, Monday first, like a paper calendar on the fridge. Every
   square is a button: the colour says can or can't, the text says the hours,
   an outline says "changed for this day" and a bar along the bottom says
   they're already on the roster. */
.cal {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 4px;
}

.calhead {
  font-size: 11px;
  font-weight: 650;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--muted);
  text-align: center;
  padding-bottom: 2px;
}

button.calday {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  gap: 2px;
  min-height: 58px;
  padding: 6px 2px 8px;
  border-radius: var(--r);
  font-size: 12px;
  font-weight: 600;
}

.calnum { font-size: 15px; font-weight: 650; line-height: 1; white-space: nowrap; }
.callabel { font-size: 11px; line-height: 1.1; white-space: nowrap; }

button.calday.yes { background: var(--done-wash); border-color: color-mix(in srgb, var(--done) 30%, var(--line)); color: var(--ink); }
button.calday.yes .callabel { color: var(--done); }
button.calday.no { background: var(--line-2); color: var(--muted); }
button.calday.no .callabel { font-weight: 500; }
button.calday:hover { filter: brightness(.97); }
button.calday.changed { box-shadow: inset 0 0 0 2px var(--accent); border-color: var(--accent); }
button.calday.today .calnum {
  background: var(--ink); color: var(--card);
  border-radius: 999px; padding: 2px 6px; margin-top: -2px;
}
button.calday.past, button.calday[disabled] { opacity: .45; cursor: default; filter: none; }
/* Already somebody's to work from, so the calendar says so. */
.calshift {
  position: absolute;
  left: 25%; right: 25%; bottom: 3px;
  height: 3px; border-radius: 2px;
  background: var(--ink-2);
}
button.calday.picked {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
  box-shadow: var(--ring);
}
button.calday.picked .callabel { color: var(--accent-ink); }

@media (max-width: 380px) {
  .cal { gap: 3px; }
  button.calday { min-height: 52px; }
  .callabel { font-size: 10px; }
}

/* The bar that follows a selection of days: how many, and the one thing
   to do with them. Sticks above the bottom nav so it is never scrolled away. */
.selbar {
  position: sticky;
  bottom: calc(var(--s3) + env(safe-area-inset-bottom, 0px));
  z-index: 5;
  display: flex;
  align-items: center;
  gap: var(--s3);
  margin: 0 0 var(--s4);
  padding: var(--s2) var(--s2) var(--s2) var(--s4);
  border-radius: var(--r-lg);
  background: var(--card);
  border: 1px solid var(--line);
  box-shadow: var(--lift);
}
@media (max-width: 767px) {
  .selbar { bottom: calc(68px + env(safe-area-inset-bottom, 0px)); }
}

.changerow { display: flex; align-items: center; gap: var(--s3); }
.changerow .grow { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; }

/* The usual week: seven rows, each the button that changes that weekday. */
.usualdays { display: grid; gap: 6px; }

button.usualday {
  justify-content: flex-start;
  gap: var(--s3);
  min-height: 52px;
  padding: 0 var(--s3);
  text-align: left;
  color: var(--muted);
  background: var(--sunken);
}
button.usualday.on {
  background: var(--done-wash);
  border-color: color-mix(in srgb, var(--done) 30%, var(--line));
}
button.usualday[disabled] { opacity: 1; }
.usualday-state { margin-left: auto; font-size: 14px; font-weight: 600; }
button.usualday.on .usualday-state { color: var(--done); }

.myday-mark {
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  flex: none;
  border-radius: 50%;
  background: var(--line-2);
  color: var(--faint);
}
.usualday.on .myday-mark { background: var(--done); color: #fff; }
.myday-name { font-size: 15.5px; font-weight: 650; color: var(--ink-2); line-height: 1.2; }
.usualday.on .myday-name { color: var(--ink); }

/* The answers for a day, two to a row on anything wider than a phone. */
.daychoices { display: grid; gap: 8px; }
@media (min-width: 420px) { .daychoices { grid-template-columns: 1fr 1fr; } }
.bigchoice[aria-pressed="true"] { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); background: var(--accent-wash); }
.bigchoice.no strong { color: var(--danger); }
.hoursbox { max-width: 120px; }

.custom-hours summary { cursor: pointer; font-weight: 600; color: var(--accent); padding: 6px 0; }
.custom-hours input[type="time"] { flex: 1; min-width: 0; }
