/* =========================================================================
   SeeWork — web prototype
   Design tokens follow Apple's Human Interface Guidelines: the system colour
   palette, the macOS type scale (13px body, not 16px), standard control
   heights, and materials rather than flat greys for the sidebar and toolbar.
   Icons are hand-drawn SVG, never SF Symbols — the SF font licence doesn't
   cover shipping them in a web app.
   ========================================================================= */

/* ---------- Tokens ---------- */

:root {
  color-scheme: light dark;

  /* The brand palette. Kept as given wherever white sitting on the colour
     still reads; orange, yellow, green and teal are nudged darker in light
     mode only, because white on the originals falls under 3:1.
     Tools/build-palette.mjs derives these and checks every one. */
  --purple: #9a69b3;
  --on-purple: #160f19;
  --red: #f05a52;
  --on-red: #220d0b;
  --orange: #dc7a17;
  --on-orange: #1f1103;
  --yellow: #b98f02;
  --on-yellow: #1a1400;
  --green: #47a554;
  --on-green: #0a170c;
  --blue: #0098d6;
  --on-blue: #00151e;
  /* Not in the six, but the app needs the names. Mixed from neighbours so
     they belong to the same family. */
  --indigo: #557ec3;
  --on-indigo: #0c121b;
  --teal: #2ca3a3;
  --on-teal: #061717;
  --pink: #ce6079;
  --on-pink: #1d0d11;
  --brown: #855a2e;
  --on-brown: #ffffff;
  --gray:   #8e8e93;
  --on-gray: #ffffff;

  --accent: var(--blue);

  /* Surfaces */
  --bg-window: #eceef1;
  --bg-content: #ffffff;
  /* The surface cards sit *on*. Grey behind white is how macOS separates a
     grouped list from its background; two whites and a hairline is not
     separation, it's a rumour. */
  --bg-page: #f1f1f5;
  --bg-card: #ffffff;
  --bg-inset: rgba(120, 120, 128, 0.08);
  --bg-inset-strong: rgba(120, 120, 128, 0.14);
  --bg-sidebar: rgba(246, 246, 248, 0.68);
  --bg-titlebar: rgba(246, 246, 248, 0.72);
  --bg-hover: rgba(0, 0, 0, 0.045);

  /* Labels */
  --label: #000000;
  --label-secondary: rgba(60, 60, 67, 0.62);
  --label-tertiary: rgba(60, 60, 67, 0.36);
  --label-quaternary: rgba(60, 60, 67, 0.20);
  --on-accent: #ffffff;

  --separator: rgba(60, 60, 67, 0.14);
  --separator-opaque: #d8d8dc;

  --shadow-card: 0 1px 3px rgba(0, 0, 0, 0.07), 0 1px 1px rgba(0, 0, 0, 0.04),
                 0 0 0 0.5px rgba(0, 0, 0, 0.08);
  --shadow-raised: 0 8px 28px rgba(0, 0, 0, 0.16), 0 0 0 0.5px rgba(0, 0, 0, 0.08);
  --shadow-window: 0 24px 70px rgba(0, 0, 0, 0.22), 0 0 0 0.5px rgba(0, 0, 0, 0.14);

  /* Radii — Apple's continuous corners, approximated */
  --r-control: 6px;
  --r-field: 8px;
  --r-card: 12px;
  --r-panel: 16px;

  --sidebar-w: 216px;
  --titlebar-h: 52px;

  --font: -apple-system, BlinkMacSystemFont, "SF Pro Text", "SF Pro Display",
          "Helvetica Neue", "Segoe UI", system-ui, sans-serif;
  --font-rounded: -apple-system, "SF Pro Rounded", var(--font);

  --ease: cubic-bezier(0.32, 0.72, 0, 1);
}

/* System preference first; the explicit [data-theme] block below overrides it
   in both directions, so the in-app toggle always wins. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    /* The same six, lifted only where they'd vanish against a dark card. */
    --purple: #a275b9;
    --on-purple: #17101a;
    --red: #f05a52;
    --on-red: #220d0b;
    --orange: #e8841f;
    --on-orange: #201204;
    --yellow: #fcc50d;
    --on-yellow: #231c02;
    --green: #67be73;
    --on-green: #0e1b10;
    --blue: #0098d6;
    --on-blue: #00151e;
    --indigo: #6388c8;
    --on-indigo: #0e131c;
    --teal: #2ea9a9;
    --on-teal: #061818;
    --pink: #d26d84;
    --on-pink: #1d0f12;
    --brown: #ac743b;
    --on-brown: #181008;
    --gray:   #98989d;
    --on-gray: #ffffff;

    --bg-window: #1c1c1e;
    --bg-content: #1e1e20;
    --bg-page: #161618;
    --bg-card: #2a2a2c;
    --bg-inset: rgba(120, 120, 128, 0.20);
    --bg-inset-strong: rgba(120, 120, 128, 0.30);
    --bg-sidebar: rgba(38, 38, 41, 0.62);
    --bg-titlebar: rgba(38, 38, 41, 0.70);
    --bg-hover: rgba(255, 255, 255, 0.06);
  /* The selected segment needs to be lighter than the track, not darker. */
  --segmented-on: #48484a;

    --label: #ffffff;
    --label-secondary: rgba(235, 235, 245, 0.62);
    --label-tertiary: rgba(235, 235, 245, 0.34);
    --label-quaternary: rgba(235, 235, 245, 0.18);

    --separator: rgba(255, 255, 255, 0.12);
    --separator-opaque: #3a3a3c;

    --shadow-card: 0 1px 3px rgba(0, 0, 0, 0.36), 0 1px 1px rgba(0, 0, 0, 0.24),
                   0 0 0 0.5px rgba(255, 255, 255, 0.08);
    --shadow-raised: 0 10px 34px rgba(0, 0, 0, 0.5), 0 0 0 0.5px rgba(255, 255, 255, 0.08);
    --shadow-window: 0 24px 70px rgba(0, 0, 0, 0.6), 0 0 0 0.5px rgba(255, 255, 255, 0.10);
  }
}

/* Explicit override beats the media query in both directions. */
:root[data-theme="dark"] {
  /* The same six, lifted only where they'd vanish against a dark card. */
  --purple: #a275b9;
  --on-purple: #17101a;
  --red: #f05a52;
  --on-red: #220d0b;
  --orange: #e8841f;
  --on-orange: #201204;
  --yellow: #fcc50d;
  --on-yellow: #231c02;
  --green: #67be73;
  --on-green: #0e1b10;
  --blue: #0098d6;
  --on-blue: #00151e;
  --indigo: #6388c8;
  --on-indigo: #0e131c;
  --teal: #2ea9a9;
  --on-teal: #061818;
  --pink: #d26d84;
  --on-pink: #1d0f12;
  --brown: #ac743b;
  --on-brown: #181008;
  --gray:   #98989d;
  --on-gray: #ffffff;

  --bg-window: #1c1c1e;
  --bg-content: #1e1e20;
  --bg-page: #161618;
  --bg-card: #2a2a2c;
  --bg-inset: rgba(120, 120, 128, 0.20);
  --bg-inset-strong: rgba(120, 120, 128, 0.30);
  --bg-sidebar: rgba(38, 38, 41, 0.62);
  --bg-titlebar: rgba(38, 38, 41, 0.70);
  --bg-hover: rgba(255, 255, 255, 0.06);
  /* The selected segment needs to be lighter than the track, not darker. */
  --segmented-on: #48484a;

  --label: #ffffff;
  --label-secondary: rgba(235, 235, 245, 0.62);
  --label-tertiary: rgba(235, 235, 245, 0.34);
  --label-quaternary: rgba(235, 235, 245, 0.18);

  --separator: rgba(255, 255, 255, 0.12);
  --separator-opaque: #3a3a3c;

  --shadow-card: 0 1px 3px rgba(0, 0, 0, 0.36), 0 1px 1px rgba(0, 0, 0, 0.24),
                 0 0 0 0.5px rgba(255, 255, 255, 0.08);
  --shadow-raised: 0 10px 34px rgba(0, 0, 0, 0.5), 0 0 0 0.5px rgba(255, 255, 255, 0.08);
  --shadow-window: 0 24px 70px rgba(0, 0, 0, 0.6), 0 0 0 0.5px rgba(255, 255, 255, 0.10);
}

/* ---------- Reset ---------- */

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

html, body {
  height: 100%;
  margin: 0;
}

body {
  font-family: var(--font);
  font-size: 13px;
  line-height: 1.42;
  color: var(--label);
  background: var(--bg-window);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow: hidden;
}

@media (min-width: 1180px) {
  body {
    /* A desk behind the window, so the material in the chrome has something
       to be translucent against. */
    background:
      radial-gradient(1200px 700px at 15% -10%, rgba(88, 86, 214, 0.28), transparent 60%),
      radial-gradient(1000px 600px at 90% 110%, rgba(0, 122, 255, 0.24), transparent 60%),
      var(--bg-window);
    padding: 26px;
  }
}

h1, h2, h3, h4, p, figure { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
button { font: inherit; color: inherit; }
input, textarea, select { font: inherit; color: inherit; }

:focus-visible {
  outline: 3px solid color-mix(in srgb, var(--accent) 55%, transparent);
  outline-offset: 1px;
  border-radius: 5px;
}

/* ---------- Window chrome ---------- */

.window {
  height: 100%;
  display: grid;
  grid-template-rows: var(--titlebar-h) minmax(0, 1fr);
  background: var(--bg-page);
  overflow: hidden;
}

@media (min-width: 1180px) {
  .window {
    max-width: 1440px;
    height: min(920px, calc(100vh - 52px));
    margin: 0 auto;
    border-radius: 12px;
    box-shadow: var(--shadow-window);
  }
}

.titlebar {
  display: grid;
  grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
  align-items: center;
  background: var(--bg-titlebar);
  backdrop-filter: saturate(180%) blur(30px);
  -webkit-backdrop-filter: saturate(180%) blur(30px);
  border-bottom: 0.5px solid var(--separator);
}

.titlebar__left {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 0 10px 0 14px;
  height: 100%;
}

.traffic { display: flex; gap: 8px; margin-right: 6px; }
.traffic__dot {
  width: 12px; height: 12px; border-radius: 50%;
  box-shadow: inset 0 0 0 0.5px rgba(0, 0, 0, 0.12);
}
.traffic__dot--close { background: #ff5f57; }
.traffic__dot--min { background: #febc2e; }
.traffic__dot--zoom { background: #28c840; }

.titlebar__main {
  display: flex;
  align-items: center;
  gap: 10px;
  height: 100%;
  padding: 0 14px 0 16px;
  min-width: 0;
}

/* Add actions sit next to the title. */
.titlebar__leading {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: none;
}
.titlebar__leading:empty { display: none; }

/* Everything else is pushed to the far end. */
.titlebar__actions {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 8px;
  flex: none;
}

/* ---------- Split view ---------- */

.split {
  display: grid;
  grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
  min-height: 0;
}

.window[data-sidebar="hidden"] .split,
.window[data-sidebar="hidden"] .titlebar { grid-template-columns: 0 minmax(0, 1fr); }
.window[data-sidebar="hidden"] .sidebar { transform: translateX(-100%); }
.window[data-sidebar="hidden"] .titlebar__left { padding-right: 4px; }

.sidebar {
  background: var(--bg-sidebar);
  backdrop-filter: saturate(180%) blur(30px);
  -webkit-backdrop-filter: saturate(180%) blur(30px);
  border-right: 0.5px solid var(--separator);
  overflow-y: auto;
  overflow-x: hidden;
  padding: 8px 10px 20px;
  transition: transform 0.3s var(--ease);
  scrollbar-width: thin;
}

.detail {
  background: var(--bg-page);
  overflow-y: auto;
  min-width: 0;
  scroll-behavior: smooth;
}

/* Wrapper the render loop replaces each time. It generates no box of its own,
   so full-height workspaces still measure against .detail. */
.detail__host { display: contents; }

.sidebar-scrim {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.3);
  z-index: 40;
}

/* ---------- Sidebar contents ---------- */

.side-section { margin-bottom: 12px; }

/* The header is a disclosure control, so it's a button. */
/* The heading is a row: the title collapses, and whatever sits beside it acts
   on the section. Siblings rather than nested, because a button inside a
   button is invalid and the inner one can't be reached by keyboard. */
.side-section__head {
  display: flex;
  align-items: center;
  /* Matched to `.side-row`'s own right padding, so the switch on a heading
     and the switches on the rows under it land on one line. */
  padding-right: 9px;
  gap: 4px;
}

.side-section__title {
  display: flex;
  align-items: center;
  gap: 4px;
  flex: 1;
  min-width: 0;
  border: 0;
  background: transparent;
  font-size: 11px;
  font-weight: 600;
  color: var(--label-tertiary);
  padding: 6px 10px 3px;
  letter-spacing: 0.01em;
  text-align: left;
  cursor: pointer;
  border-radius: 5px;
}
.side-section__title:hover { color: var(--label-secondary); }

/* Hidden until you go near the heading, like the chevron on the other side —
   a control on every section at rest would read as a row of buttons rather
   than a list of labels. Focus reveals it too, or it would be unreachable
   without a mouse. */
.side-section__more {
  display: grid;
  place-items: center;
  width: 18px;
  height: 18px;
  flex: none;
  padding: 0;
  border: 0;
  border-radius: 4px;
  background: transparent;
  color: var(--label-tertiary);
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.12s ease, background 0.12s ease;
}
.side-section:hover .side-section__more,
.side-section__more:focus-visible { opacity: 1; }
.side-section__more:hover { background: var(--bg-inset); color: var(--label); }

/* Hidden until you go near it, so the headings read as labels rather than a
   row of controls. Faded rather than removed, so the title doesn't shift
   sideways the moment it appears. */
.side-section__chevron {
  display: grid;
  place-items: center;
  color: var(--label-quaternary);
  opacity: 0;
  transition: transform 0.2s var(--ease), opacity 0.15s ease;
}
.side-section:hover .side-section__chevron,
.side-section__title:focus-visible .side-section__chevron {
  opacity: 1;
}

/* A shut section keeps it: without the chevron nothing says there's more
   underneath, and the dot only appears when your place is in there. */
.side-section--collapsed .side-section__chevron {
  opacity: 1;
  transform: rotate(-90deg);
}

/* Marks a shut section that holds wherever you are, so collapsing doesn't
   lose all trace of your place. */
.side-section__dot {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--accent);
  margin-left: 2px;
  display: none;
}
.side-section--collapsed .side-section__dot { display: block; }

/* The inner div is what clips and what animates. These two rules are only the
   resting states — open is `auto`, so the section still fits its content when
   rows are added or removed underneath it, and shut is zero.

   The movement between them is run from JS with the Web Animations API, on
   measured pixel heights. A CSS transition can't do it: `auto` is not an
   interpolable value, and the grid-template-rows 1fr → 0fr trick that gets
   around that didn't animate reliably here. Measuring is unglamorous and
   always works. */
.side-section__rows > div {
  overflow: hidden;
  min-height: 0;
}
.side-section--collapsed .side-section__rows > div { height: 0; }

.side-row {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 5px 9px;
  border: 0;
  background: transparent;
  border-radius: var(--r-control);
  font-size: 13px;
  text-align: left;
  cursor: pointer;
  color: var(--label);
  transition: background 0.12s ease;
}
.side-row:hover { background: var(--bg-hover); }
.side-row .icon { color: var(--accent); flex: none; }
.side-row__label {
  flex: 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.side-row__count {
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  color: var(--label-tertiary);
}
.side-row[aria-current="true"] {
  background: var(--accent);
  color: var(--on-accent);
}
.side-row[aria-current="true"] .icon,
.side-row[aria-current="true"] .side-row__count { color: var(--on-accent); }
.side-row[aria-current="true"] .side-row__count { opacity: 0.8; }

.side-row--add { color: var(--label-secondary); }
.side-row--add .icon { color: var(--label-tertiary); }

.side-badge {
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: 9px;
  background: var(--red);
  color: #fff;
  font-size: 10px;
  font-weight: 600;
  display: grid;
  place-items: center;
  font-variant-numeric: tabular-nums;
}
.side-row[aria-current="true"] .side-badge { background: rgba(255, 255, 255, 0.28); }

/* ---------- Sidebar edit mode ---------- */

/* Counts and badges are about what needs doing, which isn't the question
   while you're deciding what the sidebar should contain. They step out, and
   each row is left saying only what it is. */
.sidebar--editing .side-row__count,
.sidebar--editing .side-badge { display: none; }

/* Headings stop being disclosure controls while editing — every section is
   forced open, so there's nothing left to disclose — and become handles
   instead. The chevron goes with the job it was doing. */
.sidebar--editing .side-section__head { cursor: grab; }
.sidebar--editing .side-section__head:active { cursor: grabbing; }
.sidebar--editing .side-section__chevron { display: none; }

/* Matched to the heading it sits on rather than dimmer. At 18% opacity it
   was there but unreadable, which is the one thing a handle can't be — its
   whole job is to say the heading can be dragged. */
.side-section__grip {
  display: grid;
  place-items: center;
  color: var(--label-tertiary);
  flex: none;
}
.sidebar--editing .side-section__head:hover .side-section__grip {
  color: var(--label);
}

/* Left behind rather than hidden: you're moving it, and it helps to see
   where it currently sits as the others part around it. */
.side-section--dragging { opacity: 0.4; }
.side-section--dragging .side-section__head { cursor: grabbing; }

/* A row you've taken out. Still legible, because you have to read it to
   decide to put it back, but plainly no longer part of the sidebar. */
.side-row--off .side-row__label { color: var(--label-tertiary); }
.side-row--off .icon { opacity: 0.4; }

/* Tinted rather than filled: one of these sits on every row at once, and a
   column of solid red dots reads as a warning rather than a control.

   The glyph is mixed toward the label instead of being the raw colour — the
   same recipe the badges use. Straight --red on a pale red tint measures
   2.25:1, and this is a one-pixel stroke, so it needs the help. */
.side-row__toggle {
  display: grid;
  place-items: center;
  width: 16px;
  height: 16px;
  flex: none;
  border-radius: 50%;
  color: color-mix(in srgb, var(--red) 62%, var(--label));
  background: color-mix(in srgb, var(--red) 22%, transparent);
}
.side-row__toggle .icon { color: currentColor; }
.side-row--off .side-row__toggle {
  color: color-mix(in srgb, var(--green) 62%, var(--label));
  background: color-mix(in srgb, var(--green) 24%, transparent);
}
.side-row--off .side-row__toggle .icon { opacity: 1; }

/* The same switch on a whole section. Same colours as the row's, because it
   does the same thing at a larger scale and a second visual language for one
   more control would be a second thing to learn. */
.side-section__toggle {
  display: grid;
  place-items: center;
  width: 16px;
  height: 16px;
  flex: none;
  padding: 0;
  border: 0;
  border-radius: 50%;
  cursor: pointer;
  color: color-mix(in srgb, var(--red) 62%, var(--label));
  background: color-mix(in srgb, var(--red) 22%, transparent);
}
.side-section__toggle .icon { color: currentColor; }
.side-section--off .side-section__toggle {
  color: color-mix(in srgb, var(--green) 62%, var(--label));
  background: color-mix(in srgb, var(--green) 24%, transparent);
}

/* A section you've taken out, while you're still looking at it. It folds shut
   and drops to the foot of the list, so what's left at the top is what you
   actually use — the heading stays as the handle to bring it back.

   Same fold the collapse uses, so there's one mechanism rather than two that
   have to be kept looking alike. */
.side-section--off .side-section__title { color: var(--label-quaternary); }
.side-section--off .side-section__rows > div { height: 0; }
.side-section--off .side-section__chevron { display: none; }

/* ---------- Colour swatches ---------- */

.swatches {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
}
.swatch {
  width: 22px;
  height: 22px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--swatch);
  cursor: pointer;
  /* The ring is drawn outside the colour rather than around it, so choosing
     one doesn't change its size and shuffle the row along. */
  box-shadow: 0 0 0 0 var(--bg-card), 0 0 0 0 var(--swatch);
  transition: box-shadow 0.15s var(--ease);
}
.swatch[aria-checked="true"] {
  box-shadow: 0 0 0 2px var(--bg-card), 0 0 0 4px var(--swatch);
}
.swatch:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

.sidebar__hint {
  font-size: 11px;
  line-height: 1.45;
  color: var(--label-tertiary);
  padding: 5px 10px 0;
}

/* ---------- Generic layout ---------- */

.page {
  padding: 20px 24px 60px;
  max-width: 1100px;
}
.page--wide { max-width: none; }
.page--narrow { max-width: 720px; }

.stack { display: flex; flex-direction: column; gap: 16px; }
.row { display: flex; align-items: center; gap: 10px; }
.row--wrap { flex-wrap: wrap; }
.spacer { flex: 1; }

.section-heading {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 11px;
  font-weight: 600;
  color: var(--label-secondary);
  letter-spacing: 0.02em;
  text-transform: uppercase;
  margin-bottom: 8px;
}
.section-heading .icon { color: var(--label-tertiary); }
/* A heading is uppercase; a control that sits in one is not. Both inherit
   down without this, and "STREET VIEW" on a button reads as shouting. */
.section-heading .btn,
.section-heading .segmented { text-transform: none; letter-spacing: normal; }

.muted { color: var(--label-secondary); }
.dim { color: var(--label-tertiary); }
.caption { font-size: 11px; color: var(--label-secondary); }
.mono-nums { font-variant-numeric: tabular-nums; }
.title-lg { font-size: 22px; font-weight: 600; letter-spacing: -0.02em; }
.title-md { font-size: 15px; font-weight: 600; letter-spacing: -0.01em; }
.headline { font-size: 13px; font-weight: 600; }

/* ---------- Controls ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 26px;
  padding: 0 11px;
  border-radius: var(--r-control);
  border: 0.5px solid var(--separator);
  background: var(--bg-card);
  box-shadow: 0 0.5px 1px rgba(0, 0, 0, 0.06);
  font-size: 12.5px;
  font-weight: 500;
  cursor: pointer;
  white-space: nowrap;
  transition: filter 0.12s ease, background 0.12s ease;
}
.btn:hover { filter: brightness(0.97); }
.btn:active { filter: brightness(0.93); }
.btn[disabled] { opacity: 0.4; cursor: default; filter: none; }

.btn--primary {
  background: var(--accent);
  color: var(--on-accent);
  border-color: transparent;
}
.btn--primary:hover { filter: brightness(1.06); }
.btn--primary .icon { color: currentColor; }

.btn--plain {
  background: transparent;
  border-color: transparent;
  box-shadow: none;
  color: var(--accent);
  padding: 0 4px;
}
.btn--plain:hover { background: var(--bg-hover); filter: none; }

.btn--destructive { color: var(--red); }
.btn--large { height: 32px; padding: 0 14px; font-size: 13px; }

.icon-btn {
  display: grid;
  place-items: center;
  width: 28px;
  height: 26px;
  border: 0;
  border-radius: var(--r-control);
  background: transparent;
  color: var(--label-secondary);
  cursor: pointer;
  flex: none;
  transition: background 0.12s ease, color 0.12s ease;
}
.icon-btn:hover { background: var(--bg-hover); color: var(--label); }

.segmented {
  display: inline-flex;
  padding: 2px;
  gap: 2px;
  background: var(--bg-inset);
  border-radius: 8px;
}
.segmented button {
  border: 0;
  background: transparent;
  border-radius: 6px;
  padding: 3px 11px;
  font-size: 12px;
  font-weight: 500;
  cursor: pointer;
  color: var(--label);
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  gap: 5px;
}
.segmented button[aria-pressed="true"] {
  background: var(--segmented-on, var(--bg-card));
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.10), 0 0 0 0.5px rgba(0, 0, 0, 0.05);
}
/* For a card header, where the control is an accessory rather than the
   subject and shouldn't out-weigh the title beside it. */
.segmented--sm button { padding: 2px 9px; font-size: 11px; }

/* Two lines per segment, for switching between whole screens rather than
   filtering one list. The second line is what stops "Booking page" and
   "Customer app" reading as two names for the same thing. */
.segmented--wide { display: flex; width: 100%; }
.segmented--wide button {
  flex: 1;
  flex-direction: column;
  align-items: flex-start;
  gap: 1px;
  padding: 7px 11px;
  font-size: 13px;
  font-weight: 600;
  white-space: normal;
}
.segmented__sub {
  font-size: 11px;
  font-weight: 400;
  color: var(--label-secondary);
  line-height: 1.3;
}

.segmented button:disabled {
  color: var(--label-quaternary);
  cursor: default;
}
.segmented button:disabled[aria-pressed="true"] { background: transparent; box-shadow: none; }

/* ---------- Price list ---------- */

.price {
  padding: 10px 0;
  border-bottom: 0.5px solid var(--separator);
}
.price:last-child { border-bottom: 0; padding-bottom: 0; }
.price:first-child { padding-top: 0; }
.price__name { margin-bottom: 6px; }
.price__money { gap: 6px; flex-wrap: wrap; }
.price__note { margin: 6px 0 0; }

/* Something to paste elsewhere. Scrolls sideways in its own box rather than
   wrapping: a wrapped snippet is one somebody copies wrong. */
.code-block {
  margin: 0;
  padding: 11px 12px;
  border-radius: 8px;
  background: var(--bg-inset);
  border: 0.5px solid var(--separator);
  overflow-x: auto;
  font-size: 11.5px;
  line-height: 1.55;
  white-space: pre;
  color: var(--label-secondary);
}
.code-block code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }

/* ---------- Services (what they can book) ---------- */

/* Two lines: the name and how it's offered, then the sentence that sits under
   it on the booking page. Edited where it sits, like the rate card — a sheet
   per service is three clicks to fix a word a customer is reading now. */
.service {
  padding: 11px 0;
  border-bottom: 0.5px solid var(--separator);
}
.service:first-child { padding-top: 0; }
.service:last-child { border-bottom: 0; padding-bottom: 0; }

.service__top { gap: 8px; }
.service__bottom { gap: 4px; margin-top: 6px; padding-left: 25px; }

.service__icon { display: grid; place-items: center; width: 17px; flex: none; }
.service__name { flex: 1; min-width: 0; font-weight: 500; }
.service__blurb { flex: 1; min-width: 0; font-size: 12px; }
.service__public { flex: none; margin: 0; }

/* The move buttons are one glyph rotated, so there's one icon to maintain
   rather than a matched pair that can drift apart. */
[data-move-service][data-dir="up"] .icon { transform: rotate(-90deg); }
[data-move-service][data-dir="down"] .icon { transform: rotate(90deg); }
[data-move-service]:disabled { opacity: 0.3; cursor: default; }

/* ---------- Menus ---------- */

/* Positioned by script, so it starts wherever and is moved once it has a size
   to measure against the edges of the window. */
.menu {
  position: fixed;
  z-index: 200;
  min-width: 190px;
  padding: 4px;
  border-radius: 8px;
  background: var(--bg-card);
  border: 0.5px solid var(--separator);
  box-shadow:
    0 8px 28px rgba(0, 0, 0, 0.20),
    0 2px 6px rgba(0, 0, 0, 0.12);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .menu {
    box-shadow: 0 10px 34px rgba(0, 0, 0, 0.55), 0 2px 6px rgba(0, 0, 0, 0.4);
  }
}
:root[data-theme="dark"] .menu {
  box-shadow: 0 10px 34px rgba(0, 0, 0, 0.55), 0 2px 6px rgba(0, 0, 0, 0.4);
}

.menu__item {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 5px 9px;
  border: 0;
  border-radius: 5px;
  background: transparent;
  color: var(--label);
  font: inherit;
  font-size: 12.5px;
  text-align: left;
  cursor: pointer;
  white-space: nowrap;
}
.menu__item:hover,
.menu__item:focus-visible {
  background: var(--accent);
  color: #fff;
  outline: none;
}
.menu__item .icon { color: var(--label-tertiary); flex: none; }
.menu__item:hover .icon,
.menu__item:focus-visible .icon { color: #fff; }

/* Keeps the labels in a column when only some items carry a glyph. */
.menu__gap { width: 13px; flex: none; }

.menu__item--danger { color: var(--red); }
.menu__item--danger .icon { color: var(--red); }

.menu__rule {
  height: 0.5px;
  margin: 4px 6px;
  background: var(--separator);
}

/* ---------- Setup ---------- */

.setup__head { padding: 4px 2px 0; }

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

.setup-trade {
  display: grid;
  grid-template-columns: auto 1fr;
  grid-template-areas: "icon label" "icon eg";
  gap: 1px 11px;
  align-items: center;
  padding: 12px 13px;
  border: 1px solid var(--separator);
  border-radius: 11px;
  background: transparent;
  text-align: left;
  font: inherit;
  color: var(--label);
  cursor: pointer;
  transition: border-color 0.12s var(--ease), background 0.12s var(--ease);
}
.setup-trade:hover { background: var(--bg-inset); }

/* Selection is a border and a tint together, never colour alone — the trade's
   own glyph keeps its hue, so it can't double as the state. */
.setup-trade--on {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 8%, transparent);
  box-shadow: inset 0 0 0 1px var(--accent);
}

.setup-trade__icon { grid-area: icon; display: grid; place-items: center; }
.setup-trade__label { grid-area: label; font-weight: 600; font-size: 13.5px; }
.setup-trade__eg {
  grid-area: eg;
  font-size: 11.5px;
  color: var(--label-secondary);
  line-height: 1.35;
}

/* What a trade would put on the booking page, as a grid of glyphs rather than
   a stack of rows.

   It's the same question a home screen answers — what is there, at a glance —
   and the answer is short enough to be shaped rather than read. As rows it ran
   most of a screen tall for four words of content. Each service passes its own
   hue in as --tile. */
.setup-services {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(84px, 1fr));
  gap: 2px;
}

.setup-service {
  display: grid;
  justify-items: center;
  align-content: start;
  gap: 7px;
  padding: 11px 5px 10px;
  border-radius: 12px;
  text-align: center;
}

.setup-service__icon {
  width: 40px;
  height: 40px;
  display: grid;
  place-items: center;
  border-radius: 11px;
  background: color-mix(in srgb, var(--tile, var(--accent)) 15%, transparent);
  box-shadow: inset 0 0 0 0.5px
    color-mix(in srgb, var(--tile, var(--accent)) 26%, transparent);
  /* Mixed toward the label, not used neat — same recipe and same reason as a
     card's own glyph: a coloured mark on a tint of its own hue has nothing to
     push against, and the pale end of the palette washes out. */
  color: color-mix(in srgb, var(--tile, var(--accent)) 72%, var(--label));
}

.setup-service__label {
  font-size: 11.5px;
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: -0.005em;
}

/* ---------- Albums ---------- */

.gal-thumb {
  width: 34px; height: 34px;
  border-radius: 6px;
  overflow: hidden;
  flex: none;
  display: grid;
  place-items: center;
  background: var(--bg-inset);
  color: var(--label-tertiary);
}
.gal-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Fixed-height rows rather than a masonry. The admin grid is for *finding* a
   photograph and changing it, and a ragged edge makes that harder — the
   visitor's page is where the shapes get to be themselves. */
.gal-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 10px;
}

.gal-tile {
  margin: 0;
  border-radius: 10px;
  overflow: hidden;
  background: var(--bg-inset);
  border: 0.5px solid var(--separator);
  position: relative;
  cursor: grab;
}
.gal-tile:active { cursor: grabbing; }
.gal-tile--dragging { opacity: 0.4; }

.gal-tile__view {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  aspect-ratio: 4 / 3;
}
.gal-tile__view img { width: 100%; height: 100%; object-fit: cover; display: block; }

.gal-tile__cover {
  position: absolute;
  top: 6px; left: 6px;
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 2px 6px;
  border-radius: 5px;
  font-size: 10px;
  font-weight: 600;
  background: rgba(0, 0, 0, 0.62);
  color: #fff;
}

.gal-tile__bar {
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 5px 5px 5px 7px;
}

.gal-tile__caption {
  flex: 1;
  min-width: 0;
  border: 0;
  background: none;
  font: inherit;
  font-size: 11px;
  color: var(--label);
  padding: 2px 0;
}
.gal-tile__caption::placeholder { color: var(--label-quaternary); }
.gal-tile__caption:focus { outline: none; color: var(--label); }

/* The lightbox inside a sheet. Capped by viewport height so a tall portrait
   doesn't push the sheet's own buttons off the bottom of the screen. */
.gal-viewer { text-align: center; }
.gal-viewer img {
  max-width: 100%;
  max-height: 62vh;
  border-radius: 8px;
  display: block;
  margin: 0 auto;
}
.gal-viewer .caption { margin: 10px 0 0; }

/* ---------- Place pane ---------- */

.place__stage {
  height: 260px;
  background: var(--bg-inset);
  display: grid;
  place-items: center;
}
.place__canvas { width: 100%; height: 100%; }

/* Google's own controls sit inside the canvas, so the corners have to be
   clipped by the card rather than the map. */
.place .card__body--flush { overflow: hidden; }

/* The same clipping for the pane as a band, which has no flush card body to
   do it — the frame is the edge instead. */
.place__frame {
  border-radius: 8px;
  overflow: hidden;
  border: 0.5px solid var(--separator);
}
.place__where { margin: 8px 0 0; }

.place__note {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 20px 28px;
  text-align: center;
  max-width: 34em;
  color: var(--label-secondary);
  font-size: 12px;
  line-height: 1.5;
}
.place__note .icon { color: var(--label-tertiary); }
.place__note p { margin: 0; }
.place__note code {
  font-size: 11px;
  padding: 1px 4px;
  border-radius: 4px;
  background: var(--bg-card);
}
.place__note .btn { text-decoration: none; }

.field, .select, textarea.field {
  width: 100%;
  height: 26px;
  padding: 0 8px;
  border-radius: var(--r-control);
  border: 0.5px solid var(--separator-opaque);
  background: var(--bg-card);
  font-size: 13px;
}
textarea.field { height: auto; padding: 6px 8px; resize: vertical; line-height: 1.45; }
.field:focus, .select:focus, textarea.field:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 25%, transparent);
}
.select { appearance: none; padding-right: 22px;
  background-image: linear-gradient(transparent, transparent); }

.search {
  position: relative;
  display: flex;
  align-items: center;
}
.search .icon {
  position: absolute;
  left: 7px;
  color: var(--label-tertiary);
  pointer-events: none;
}
.search input {
  height: 26px;
  width: 100%;
  padding: 0 8px 0 26px;
  border-radius: 13px;
  border: 0.5px solid transparent;
  background: var(--bg-inset);
  font-size: 12.5px;
}
.search input:focus {
  outline: none;
  background: var(--bg-card);
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent);
}

.switch {
  appearance: none;
  width: 38px; height: 22px;
  border-radius: 11px;
  background: var(--bg-inset-strong);
  position: relative;
  cursor: pointer;
  flex: none;
  transition: background 0.2s var(--ease);
}
.switch::after {
  content: "";
  position: absolute;
  top: 2px; left: 2px;
  width: 18px; height: 18px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
  transition: transform 0.2s var(--ease);
}
.switch:checked { background: var(--green); }
.switch:checked::after { transform: translateX(16px); }

/* ---------- Cards ---------- */

.card {
  background: var(--bg-card);
  border-radius: var(--r-card);
  box-shadow: var(--shadow-card);
  overflow: hidden;
}
/* A tinted bar, so a card announces what it's about before you read it.
   `--card-colour` is set per card and falls back to the accent, which means
   every card gets one and only the ones with something to say differ.

   A tint rather than a solid fill: the title sits on it at full label colour,
   and a saturated bar would force white text and turn a quiet page into a set
   of traffic lights. */
.card__header {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 11px 14px;
  border-bottom: 0.5px solid var(--separator);
  background: color-mix(in srgb, var(--card-colour, var(--accent)) 16%, transparent);
}
/* Only the card's own glyph. Accessory buttons in the header keep the accent,
   because they're links and links are blue everywhere else in the app.

   Mixed toward the label rather than used neat — the same recipe as a badge,
   and for the same reason: a coloured glyph sitting on a tint of its own hue
   has almost nothing to push against, and the pale end of the palette washed
   out to under 3:1 against its own header. */
.card__header > .icon {
  color: color-mix(in srgb, var(--card-colour, var(--accent)) 72%, var(--label));
}
.card__title {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: -0.005em;
}
.card__body { padding: 12px 14px; }
.card__body--flush { padding: 0; }
.card__footer {
  padding: 8px 14px;
  border-top: 0.5px solid var(--separator);
  font-size: 11px;
  color: var(--label-secondary);
}

.card--tappable { cursor: pointer; }

/* A section inside a card, for when several things are one thing. The
   negative margin runs the hairline out to the card's own edges — a divider
   that stops short reads as a mistake rather than a separation. */
.card-band {
  margin: 14px -14px 0;
  padding: 14px 14px 0;
  border-top: 0.5px solid var(--separator);
}

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
  gap: 16px;
  align-items: start;
}

/* ---------- Today widgets ---------- */

/* Packed, not ruled. The same reasoning as the detail panes: a grid row is
   as tall as its tallest widget, so a five-row Reminders card beside the
   month calendar leaves a hole under it, and Today — the one page that's
   meant to be taken in at a glance — reads as half-empty.

   `columns: 340px` rather than a count, so the number of columns still comes
   from the width the way `auto-fill` did. The cost is that the order runs
   down a column and then to the next, instead of along a row: the widget you
   put second now sits under the first rather than beside it. That's inherent
   to packing, it's what the detail panes already do, and the order you
   dragged them into is still the order you read them in. */
.widgets {
  columns: 340px;
  column-gap: 16px;
}
/* No row-gap in multi-column, so the spacing is a margin. `break-inside` is
   what stops a widget being sawn in half at the foot of a column — which
   would also saw its remove button off. */
.widgets > .widget {
  break-inside: avoid;
  margin-bottom: 16px;
}

/* Lifted further off the page than a plain card: these are objects you can
   pick up and move, so they should look like it even when you can't. */
.card--widget {
  border-radius: 14px;
  box-shadow:
    0 4px 14px rgba(0, 0, 0, 0.07),
    0 1px 3px rgba(0, 0, 0, 0.06),
    0 0 0 0.5px rgba(0, 0, 0, 0.05);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .card--widget {
    box-shadow:
      0 6px 18px rgba(0, 0, 0, 0.42),
      0 1px 3px rgba(0, 0, 0, 0.3),
      0 0 0 0.5px rgba(255, 255, 255, 0.07);
  }
}
:root[data-theme="dark"] .card--widget {
  box-shadow:
    0 6px 18px rgba(0, 0, 0, 0.42),
    0 1px 3px rgba(0, 0, 0, 0.3),
    0 0 0 0.5px rgba(255, 255, 255, 0.07);
}

.widget { position: relative; }

.widget--editing { cursor: grab; }
.widget--editing:active { cursor: grabbing; }
.widget--editing .card--widget {
  outline: 1.5px dashed color-mix(in srgb, var(--accent) 45%, transparent);
  outline-offset: 3px;
  transition: transform 0.15s var(--ease), box-shadow 0.15s var(--ease);
}
.widget--editing:hover .card--widget {
  transform: translateY(-2px);
  box-shadow: var(--shadow-raised);
}

/* In edit mode the card's own controls would compete with dragging it. */
.widget--editing .card * { pointer-events: none; }
.widget--editing .widget__remove { pointer-events: auto; }

.widget--dragging { opacity: 0.4; }
.widget--dragging .card--widget { transform: scale(0.98); }

.widget__remove {
  position: absolute;
  top: -7px;
  left: -7px;
  z-index: 4;
  width: 22px;
  height: 22px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--red);
  color: #fff;
  cursor: pointer;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
}
.widget__remove:hover { filter: brightness(1.1); }

/* ---------- Mini calendar (Today widget) ---------- */

.mini-cal {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 2px;
}
.mini-cal__dow {
  text-align: center;
  font-size: 10px;
  font-weight: 600;
  color: var(--label-tertiary);
  padding-bottom: 4px;
}
.mini-cal__day {
  display: flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: 7px;
  background: transparent;
  color: var(--label);
  font-size: 11.5px;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
  /* Square-ish, because the tint is the point: a day is a block of colour to
     be read at a glance, not a line of text with a background. */
  min-height: 30px;
}
.mini-cal__day:hover { background: var(--bg-hover); }
.mini-cal__day--outside { color: var(--label-quaternary); }

/* How full the day is. Both the colour and the strength of the tint are set
   per day, so one rule covers the whole scale and the key below the grid can
   reuse it.

   The scale ramps in density as well as hue, and that isn't decoration: in
   light mode the palette's yellow is a dark mustard (it has to be legible as
   text), which at equal opacity sits far too close to the orange to tell
   apart at 30px. Getting denser as it gets hotter separates the three
   whatever the hues are doing, and reads as "more" on its own.

   A tint rather than a solid fill, so the date on top keeps its contrast. */
.mini-cal__day--busy {
  background: color-mix(in srgb, var(--load) var(--load-mix), transparent);
}
.mini-cal__day--busy:hover {
  background: color-mix(in srgb, var(--load) var(--load-mix-hover), transparent);
}

/* Today marks the numeral rather than filling the cell — the cell background
   belongs to the heat scale now, and red there would mean two things at once.
   Matches how the full Calendar marks today. */
.mini-cal__day--today > span {
  min-width: 19px;
  height: 19px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--red);
  color: #fff;
  font-weight: 600;
}

.mini-cal__key {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 4px;
  margin-top: 9px;
  font-size: 10px;
  color: var(--label-tertiary);
}
.mini-cal__key i {
  width: 17px;
  height: 10px;
  border-radius: 3px;
  /* The same mix the days use, so the key is a sample rather than an
     approximation of one. */
  background: color-mix(in srgb, var(--load) var(--load-mix), transparent);
}

/* ---------- Rows ---------- */

.rows { display: flex; flex-direction: column; }

.list-row {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 9px 14px;
  border: 0;
  background: transparent;
  text-align: left;
  cursor: pointer;
  border-top: 0.5px solid var(--separator);
  transition: background 0.1s ease;
  color: inherit;
}
.rows > .list-row:first-child { border-top: 0; }
.list-row:hover { background: var(--bg-hover); }

/* A row that holds a second line of controls rather than being one button.
   It isn't clickable itself — the things inside it are — so it drops the
   pointer and the hover, which would otherwise promise something the row
   doesn't do. */
.list-row--stack {
  display: block;
  cursor: default;
}
.list-row--stack:hover { background: transparent; }
.list-row[aria-current="true"] { background: color-mix(in srgb, var(--accent) 12%, transparent); }
/* These are spans so the whole row can be a <button>; without stacking them
   the title and subtitle run together on one line. */
.list-row__main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.list-row__title {
  display: block;
  font-size: 13px;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.list-row__sub {
  display: block;
  font-size: 11px;
  color: var(--label-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.list-row__trailing {
  text-align: right;
  flex: none;
  font-size: 12px;
  line-height: 1.35;
  font-variant-numeric: tabular-nums;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 2px;
}
/* A <br> inside a flex column collapses; the trailing slot uses one instead
   of nested elements, so give it something to break on. */
.list-row__trailing br { display: none; }
.list-row .chevron { color: var(--label-quaternary); flex: none; }

.time-chip {
  flex: none;
  width: 52px;
  font-size: 12px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--label-secondary);
}

/* Pinned to the foot of a list column and floating over the rows as they
   scroll past. Sticky rather than fixed so it stays inside its own pane. */
.list-total {
  position: sticky;
  bottom: 0;
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 9px 14px;
  font-size: 11.5px;
  color: var(--label-secondary);
  background: var(--bg-titlebar);
  backdrop-filter: saturate(180%) blur(20px);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  border-top: 0.5px solid var(--separator);
  box-shadow: 0 -1px 8px rgba(0, 0, 0, 0.05);
}
/* The label and count stack so a narrow column doesn't squeeze the figure. */
.list-total__meta {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.list-total__label {
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-size: 10px;
  color: var(--label-tertiary);
}
.list-total__count {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.list-total__value {
  flex: none;
  font-size: 16px;
  font-weight: 600;
  color: var(--label);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}

/* ---------- Badges ---------- */

.badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  height: 18px;
  padding: 0 7px;
  border-radius: 9px;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.01em;
  white-space: nowrap;
  background: color-mix(in srgb, var(--badge-color, var(--gray)) 16%, transparent);
  /* 72/28 is the weakest mix that clears 4.5:1 for every colour in the
     palette, in both appearances — solved for, not guessed. The tint keeps
     the hue; the text goes as dark as the background demands, and inverts
     for free in dark mode because --label does. */
  color: color-mix(in srgb, var(--badge-color, var(--gray)) 72%, var(--label));
}
.badge--solid {
  background: var(--badge-color, var(--gray));
  color: #fff;
}
.badge--dot::before {
  content: "";
  width: 6px; height: 6px;
  border-radius: 50%;
  background: currentColor;
}

.avatar {
  width: 30px; height: 30px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: 11.5px;
  font-weight: 600;
  flex: none;
  letter-spacing: 0.01em;
  background: var(--gray);
  color: var(--on-gray);
}

/* Each fill carries whichever text colour actually reads on it. Derived and
   checked by Tools/build-palette.mjs — white for the darker colours, a deep
   version of the same hue for the lighter ones. */
.avatar--blue   { background: var(--blue);   color: var(--on-blue); }
.avatar--indigo { background: var(--indigo); color: var(--on-indigo); }
.avatar--purple { background: var(--purple); color: var(--on-purple); }
.avatar--pink   { background: var(--pink);   color: var(--on-pink); }
.avatar--orange { background: var(--orange); color: var(--on-orange); }
.avatar--green  { background: var(--green);  color: var(--on-green); }
.avatar--teal   { background: var(--teal);   color: var(--on-teal); }
.avatar--brown  { background: var(--brown);  color: var(--on-brown); }
.avatar--lg { width: 44px; height: 44px; font-size: 15px; }
.avatar--sm { width: 24px; height: 24px; font-size: 10px; }

/* ---------- Tags ---------- */

.tag-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.tag-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 24px;
  padding: 0 6px 0 10px;
  border-radius: 12px;
  font-size: 12px;
  font-weight: 500;
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  color: color-mix(in srgb, var(--accent) 72%, var(--label));
  white-space: nowrap;
}
/* The label is the way through to everybody who has this tag, now that the
   sidebar no longer lists them. Styled to disappear into the chip: it should
   read as the chip being clickable, not as a button inside a button. */
.tag-chip__open {
  border: 0;
  padding: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  cursor: pointer;
}
.tag-chip:hover { background: color-mix(in srgb, var(--accent) 22%, transparent); }

.tag-chip__x {
  display: grid;
  place-items: center;
  width: 16px; height: 16px;
  border: 0;
  border-radius: 50%;
  padding: 0;
  background: transparent;
  color: inherit;
  opacity: 0.55;
  cursor: pointer;
}
.tag-chip__x:hover { opacity: 1; background: color-mix(in srgb, var(--accent) 22%, transparent); }

/* Tags already in use elsewhere, offered so spellings stay consistent. */
.tag-chip--suggest {
  padding: 0 10px 0 7px;
  border: 0.5px dashed var(--separator-opaque);
  background: transparent;
  color: var(--label-secondary);
  cursor: pointer;
}
.tag-chip--suggest:hover {
  border-style: solid;
  border-color: var(--accent);
  color: var(--accent);
}

/* Group membership, shown on the client. Filing is done in the sidebar, so
   this only states where they are and offers a way out. */
.group-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 24px;
  padding: 0 6px 0 9px;
  border-radius: 12px;
  font-size: 12px;
  font-weight: 500;
  white-space: nowrap;
  background: color-mix(in srgb, var(--group-color) 16%, transparent);
  color: color-mix(in srgb, var(--group-color) 72%, var(--label));
}

/* ---------- Dragging a client into a group ---------- */

.list-row[draggable="true"] { cursor: grab; }
.list-row[draggable="true"]:active { cursor: grabbing; }

/* While a name is in flight, every group row shows it will take it. */
.is-dragging-client .side-row--drop-target {
  outline: 1.5px dashed color-mix(in srgb, var(--accent) 55%, transparent);
  outline-offset: -1.5px;
}
.side-row--drop-over {
  background: var(--accent) !important;
  color: var(--on-accent) !important;
  outline-color: transparent !important;
}
.side-row--drop-over .icon,
.side-row--drop-over .side-row__count { color: var(--on-accent) !important; }

/* Remove-from-group, only shown inside a group. */
.row-remove {
  display: grid;
  place-items: center;
  width: 20px; height: 20px;
  border-radius: 50%;
  color: var(--label-tertiary);
  cursor: pointer;
}
.row-remove:hover {
  background: color-mix(in srgb, var(--red) 16%, transparent);
  color: var(--red);
}

/* ---------- Picture of the day ---------- */

.potd {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: var(--bg-inset);
  cursor: zoom-in;
  line-height: 0;
  /* A fixed frame so one tall photo can't stretch the card past its
     neighbours in the grid. The image fills it and crops. */
  aspect-ratio: 4 / 3;
  overflow: hidden;
}
.potd img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Phone photos carry their rotation in EXIF; honour it rather than
     showing them on their side. This is the browser default, said out loud
     because the whole widget depends on it. */
  image-orientation: from-image;
  transition: transform 0.4s var(--ease);
}
.potd:hover img { transform: scale(1.02); }

/* ---------- Empty states ---------- */

.empty {
  display: grid;
  place-items: center;
  gap: 6px;
  padding: 48px 20px;
  text-align: center;
  color: var(--label-secondary);
}
.empty .icon { color: var(--label-quaternary); }
.empty h3 { font-size: 15px; font-weight: 600; color: var(--label); }
.empty p { font-size: 12px; max-width: 34ch; }

/* ---------- Weather ---------- */

/* The greeting and the forecast share a line; the chip drops underneath when
   there isn't room for both. */
.today-header {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
}

.weather-chip {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 15px 8px 11px;
  border: 0;
  border-radius: 15px;
  color: #fff;
  background: linear-gradient(135deg, var(--sky-top, #3d8bd8), var(--sky-bottom, #1c5aa8));
  box-shadow: var(--shadow-card);
  cursor: pointer;
  text-align: left;
  transition: filter 0.15s ease, transform 0.15s var(--ease);
}
.weather-chip:hover { filter: brightness(1.07); }
.weather-chip:active { transform: scale(0.98); }
.weather-chip__temp {
  font-size: 25px;
  font-weight: 300;
  line-height: 1;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}
.weather-chip__meta { display: flex; flex-direction: column; gap: 1px; }
.weather-chip__condition { font-size: 12px; font-weight: 600; line-height: 1.25; }
.weather-chip__place { font-size: 10.5px; opacity: 0.8; line-height: 1.25; }


.weather {
  border-radius: var(--r-panel);
  padding: 16px 18px;
  color: #fff;
  background: linear-gradient(135deg, var(--sky-top, #3d8bd8), var(--sky-bottom, #1c5aa8));
  box-shadow: var(--shadow-card);
  position: relative;
  overflow: hidden;
}
.weather::after {
  /* A soft highlight, the way Apple's weather cards catch light. */
  content: "";
  position: absolute;
  top: -60px; right: -30px;
  width: 220px; height: 220px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 255, 255, 0.20), transparent 70%);
  pointer-events: none;
}
.weather__top {
  display: flex;
  align-items: flex-start;
  gap: 16px;
  flex-wrap: wrap;
}
.weather__temp {
  font-size: 46px;
  font-weight: 300;
  line-height: 1;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
}
.weather__label { font-size: 11px; opacity: 0.78; text-transform: uppercase; letter-spacing: 0.04em; }
.weather__condition { font-size: 14px; font-weight: 600; }
.weather__place { font-size: 12px; opacity: 0.78; }
.weather__metrics {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(84px, 1fr));
  gap: 10px 16px;
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid rgba(255, 255, 255, 0.18);
}
.weather__metric-label { font-size: 10px; opacity: 0.72; text-transform: uppercase; letter-spacing: 0.04em; }
.weather__metric-value { font-size: 13px; font-weight: 600; font-variant-numeric: tabular-nums; }
.weather__caveat {
  margin-top: 12px;
  font-size: 11px;
  opacity: 0.78;
  display: flex;
  gap: 6px;
  align-items: flex-start;
  line-height: 1.4;
}
.weather__glyph { flex: none; opacity: 0.95; }

/* ---------- Master / detail workspaces ---------- */

.workspace {
  display: grid;
  grid-template-columns: minmax(240px, 300px) minmax(0, 1fr);
  height: 100%;
  min-height: 0;
}
.workspace__list {
  border-right: 0.5px solid var(--separator);
  overflow-y: auto;
  background: var(--bg-content);
  min-height: 0;
}
.workspace__list-header {
  position: sticky;
  top: 0;
  z-index: 2;
  padding: 10px 12px;
  display: flex;
  gap: 8px;
  align-items: center;
  background: var(--bg-titlebar);
  backdrop-filter: saturate(180%) blur(20px);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  border-bottom: 0.5px solid var(--separator);
}
.workspace__detail {
  overflow-y: auto;
  min-height: 0;
  /* Measured against itself, not the window. The pane is whatever's left
     after the list column, so a viewport media query would be guessing —
     and guessing wrong every time the sidebar is hidden. */
  container-type: inline-size;
}

/* The same for a full-width view. Two containers, not one: a page inside a
   workspace measures the pane it's in, a page on its own measures the whole
   detail area, and the nearest one always wins. */
.detail { container-type: inline-size; }

/* A detail pane is tall and half-empty: six cards in a single column, 900px
   of width doing nothing, and nearly two screens of scrolling to read one
   client. Given the room, they go two abreast.

   Cards only. The heading, the row of stats and the weather panel are the
   page talking about itself and run the full width, which also breaks the
   grid into bands and keeps the reading order obvious. */
/* 820 on the container, which after the page's own 24px padding and the
   16px gutter leaves columns of 378px and up. Tried it lower to catch a
   1280 laptop's detail pane: at 680 the columns come out near 330 and an
   email address stops fitting in its own field, which is a poor trade for
   half a screen of scrolling. */
@container (min-width: 820px) {
  /* Multi-column rather than a grid, so the cards pack against each other.
     A grid puts them in rows, and a row is as tall as its tallest card — so
     a Notes box beside a job list leaves a hole under it and the page reads
     as half-empty, which is the thing this was meant to fix. Columns have no
     rows to line up with: each card starts where the one above it ended. */
  .detail__host .page.stack {
    display: block;
    columns: 2;
    column-gap: 16px;
  }
  /* No row-gap in multi-column, so the spacing is a margin. `break-inside`
     is what stops a card being sawn in half at the foot of a column. */
  .detail__host .page.stack > * {
    break-inside: avoid;
    margin-bottom: 16px;
  }
  /* The page talking about itself — heading, stats, the weather panel — plus
     anything that asks for the width. Spanning also splits the columns into
     bands, which is what keeps the reading order followable. */
  .detail__host .page.stack > *:not(.card),
  .detail__host .page.stack > .card--full {
    column-span: all;
  }
}

.group-header {
  position: sticky;
  top: 46px;
  z-index: 1;
  padding: 5px 14px;
  font-size: 11px;
  font-weight: 600;
  color: var(--label-secondary);
  background: color-mix(in srgb, var(--bg-content) 88%, transparent);
  backdrop-filter: blur(12px);
  border-bottom: 0.5px solid var(--separator);
}

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

.form-grid {
  display: grid;
  grid-template-columns: 130px minmax(0, 1fr);
  gap: 9px 12px;
  align-items: center;
}
.form-grid > label {
  font-size: 12px;
  color: var(--label-secondary);
  text-align: right;
}
.form-grid > .full { grid-column: 1 / -1; }

.kv { display: grid; grid-template-columns: auto 1fr; gap: 4px 12px; font-size: 12px; }
.kv dt { color: var(--label-secondary); }
.kv dd { margin: 0; font-variant-numeric: tabular-nums; }

/* ---------- Calendar ---------- */

/* The month owns the pane, so the page is a column with the grid taking
   whatever the heading and the key leave. It stops being a document you
   scroll and becomes a thing you look at, which is the only reason to draw a
   month rather than a list. */
.cal-page {
  display: flex;
  flex-direction: column;
  gap: 12px;
  height: 100%;
  max-width: none;
  padding: 16px 20px 14px;
  box-sizing: border-box;
}

.cal {
  --cell-min: 92px;
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  border-top: 0.5px solid var(--separator);
  border-left: 0.5px solid var(--separator);
  border-radius: var(--r-card);
  overflow: hidden;
  box-shadow: var(--shadow-card);
  background: var(--bg-card);
}
.cal__dow {
  padding: 7px 8px;
  font-size: 10.5px;
  font-weight: 600;
  color: var(--label-secondary);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  text-align: center;
  border-right: 0.5px solid var(--separator);
  border-bottom: 0.5px solid var(--separator);
  background: var(--bg-inset);
}
.cal__cell {
  min-height: var(--cell-min);
  padding: 5px 6px;
  border-right: 0.5px solid var(--separator);
  border-bottom: 0.5px solid var(--separator);
  display: flex;
  flex-direction: column;
  gap: 3px;
  background: transparent;
  text-align: left;
  cursor: default;
}
.cal__cell--outside { background: var(--bg-inset); }
.cal__cell--outside .cal__num { color: var(--label-tertiary); }
.cal__num {
  font-size: 11.5px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  align-self: flex-start;
  width: 20px; height: 20px;
  display: grid; place-items: center;
  border-radius: 50%;
}
.cal__cell--today .cal__num { background: var(--red); color: #fff; font-weight: 600; }
.cal__event {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 10.5px;
  padding: 2px 5px;
  border-radius: 5px;
  background: var(--bg-inset);
  cursor: pointer;
  border: 0;
  width: 100%;
  text-align: left;
  color: inherit;
}
.cal__event:hover { background: var(--bg-inset-strong); }
.cal__event span {
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cal__dot { width: 6px; height: 6px; border-radius: 50%; flex: none; }

/* The full-height month. `--rows` comes from the view, because a five-week
   month and a six-week one have to divide the same height differently, and
   `minmax(0, 1fr)` rather than `1fr` so a stacked day can't push its row
   taller than its share and knock the grid off the bottom of the pane. */
.cal--fill {
  flex: 1;
  min-height: 0;
  grid-template-rows: auto repeat(var(--rows, 6), minmax(0, 1fr));
}
.cal--fill .cal__cell { min-height: 0; overflow: hidden; }

/* Given the room, the chips can be read rather than deciphered. */
.cal--fill .cal__event { font-size: 11px; padding: 3px 6px; }
.cal--fill .cal__num { font-size: 12.5px; }

/* What wouldn't fit. A count on its own would say a day is busier than the
   cell admits and leave you nowhere to go, so it's a button: it opens the
   day. */
.cal__more {
  align-self: flex-start;
  font-size: 10.5px;
  font-weight: 550;
  color: var(--label-secondary);
  background: none;
  border: 0;
  padding: 1px 5px 2px;
  border-radius: 5px;
  cursor: pointer;
}
.cal__more:hover { background: var(--bg-inset-strong); color: var(--label); }

/* ---------- Travel / map ---------- */

.map {
  position: relative;
  height: 300px;
  border-radius: var(--r-card);
  overflow: hidden;
  box-shadow: var(--shadow-card);
  background:
    linear-gradient(0deg, rgba(0,0,0,0.03), rgba(0,0,0,0.03)),
    repeating-linear-gradient(0deg, transparent 0 23px, color-mix(in srgb, var(--label) 6%, transparent) 23px 24px),
    repeating-linear-gradient(90deg, transparent 0 23px, color-mix(in srgb, var(--label) 6%, transparent) 23px 24px),
    color-mix(in srgb, var(--teal) 12%, var(--bg-card));
}
.map__note {
  position: absolute;
  left: 10px; bottom: 10px;
  font-size: 10.5px;
  padding: 3px 8px;
  border-radius: 10px;
  background: var(--bg-card);
  box-shadow: var(--shadow-card);
  color: var(--label-secondary);
}
.pin {
  position: absolute;
  transform: translate(-50%, -50%);
  width: 24px; height: 24px;
  border-radius: 50%;
  display: grid; place-items: center;
  font-size: 11px; font-weight: 700;
  color: #fff;
  background: var(--accent);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3), 0 0 0 2px var(--bg-card);
}
.pin--problem { background: var(--red); }
.pin--start { background: var(--green); }

.leg {
  display: flex;
  gap: 10px;
  padding: 9px 14px;
  border-top: 0.5px solid var(--separator);
  align-items: center;
}
.leg__rule {
  width: 22px;
  display: grid;
  place-items: center;
  color: var(--label-tertiary);
}

/* ---------- Messages ---------- */

.messages {
  display: grid;
  grid-template-columns: minmax(250px, 320px) minmax(0, 1fr);
  height: 100%;
  min-height: 0;
}
.thread {
  display: flex;
  flex-direction: column;
  min-height: 0;
  height: 100%;
}
/* A request's pane, in the same column a thread would fill. A thread pins its
   composer to the bottom and scrolls the middle; this just scrolls.

   It has to be its own container, or the two-column rule measures the whole
   detail area — list column included — and splits a 340px pane in half
   because it thinks it has 900. */
.thread--pane { container-type: inline-size; }
.thread--pane .thread__scroll { padding: 0; }

.thread__header {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 16px;
  border-bottom: 0.5px solid var(--separator);
  background: var(--bg-titlebar);
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
}
.thread__scroll {
  flex: 1;
  overflow-y: auto;
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-height: 0;
}
.bubble-row { display: flex; }
.bubble-row--mine { justify-content: flex-end; }
.bubble {
  max-width: min(68%, 460px);
  padding: 7px 12px;
  border-radius: 17px;
  font-size: 13px;
  line-height: 1.42;
  white-space: pre-wrap;
  word-break: break-word;
  background: var(--bg-inset);
  color: var(--label);
}
.bubble-row--mine .bubble { background: var(--accent); color: #fff; }
.bubble__meta {
  font-size: 10px;
  color: var(--label-tertiary);
  padding: 2px 8px 8px;
  display: flex;
  gap: 5px;
  align-items: center;
}
.bubble-row--mine + .bubble__meta { justify-content: flex-end; }
.bubble__subject { font-weight: 600; margin-bottom: 3px; opacity: 0.9; }

.pay-card {
  margin-top: 8px;
  border-radius: 12px;
  padding: 10px 12px;
  background: rgba(255, 255, 255, 0.18);
  border: 1px solid rgba(255, 255, 255, 0.28);
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.bubble-row:not(.bubble-row--mine) .pay-card {
  background: var(--bg-card);
  border-color: var(--separator);
}
.pay-card__amount { font-size: 20px; font-weight: 600; letter-spacing: -0.02em; }

.composer {
  border-top: 0.5px solid var(--separator);
  padding: 10px 14px;
  display: flex;
  gap: 8px;
  align-items: flex-end;
  background: var(--bg-content);
}
.composer textarea {
  flex: 1;
  min-height: 30px;
  max-height: 120px;
  padding: 6px 12px;
  border-radius: 15px;
  border: 0.5px solid var(--separator-opaque);
  background: var(--bg-card);
  resize: none;
  font-size: 13px;
  line-height: 1.4;
}
.composer textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent);
}
.send-btn {
  width: 28px; height: 28px;
  border-radius: 50%;
  border: 0;
  background: var(--accent);
  color: #fff;
  display: grid; place-items: center;
  cursor: pointer;
  flex: none;
}
.send-btn[disabled] { background: var(--bg-inset-strong); color: var(--label-tertiary); cursor: default; }

.channel-dot {
  width: 18px; height: 18px;
  border-radius: 50%;
  display: grid; place-items: center;
  color: #fff;
  flex: none;
}

.banner {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 8px 12px;
  border-radius: var(--r-field);
  font-size: 11.5px;
  background: color-mix(in srgb, var(--orange) 14%, transparent);
  color: color-mix(in srgb, var(--orange) 72%, var(--label));
  line-height: 1.42;
}
.banner .icon { flex: none; margin-top: 1px; }
.banner--info {
  background: color-mix(in srgb, var(--blue) 12%, transparent);
  color: color-mix(in srgb, var(--blue) 72%, var(--label));
}
.banner--good {
  background: color-mix(in srgb, var(--green) 14%, transparent);
  color: color-mix(in srgb, var(--green) 72%, var(--label));
}

/* ---------- Charts ---------- */

.bars {
  display: flex;
  align-items: flex-end;
  gap: 8px;
  height: 150px;
  padding-top: 8px;
}
.bars__col { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 6px; height: 100%; justify-content: flex-end; }
.bars__pair {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: 3px;
  width: 100%;
  height: 100%;
}
.bars__bar {
  flex: 1;
  max-width: 18px;
  border-radius: 4px 4px 2px 2px;
  background: linear-gradient(180deg, var(--accent), color-mix(in srgb, var(--accent) 62%, transparent));
  min-height: 3px;
  transition: height 0.4s var(--ease);
}
.bars__bar--out {
  background: linear-gradient(180deg, var(--orange), color-mix(in srgb, var(--orange) 58%, transparent));
}
.bars__key {
  display: flex;
  gap: 14px;
  align-items: center;
  font-size: 11px;
  color: var(--label-secondary);
  margin-top: 10px;
}
.bars__swatch {
  width: 9px; height: 9px;
  border-radius: 3px;
  display: inline-block;
  margin-right: 5px;
  vertical-align: -1px;
}
.bars__label { font-size: 10px; color: var(--label-secondary); }

.donut { display: grid; place-items: center; position: relative; }
.donut__center {
  position: absolute;
  text-align: center;
}

.meter {
  height: 6px;
  border-radius: 3px;
  background: var(--bg-inset);
  overflow: hidden;
}
.meter__fill { height: 100%; border-radius: 3px; background: var(--accent); }

/* ---------- Expenses: upload, receipts ---------- */

.dropzone {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  width: 100%;
  padding: 26px 20px;
  border: 1.5px dashed var(--separator-opaque);
  border-radius: var(--r-card);
  background: transparent;
  color: var(--label-secondary);
  cursor: pointer;
  text-align: center;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.dropzone:hover {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 6%, transparent);
}
.dropzone .icon { color: var(--accent); }
.dropzone__title { font-size: 13px; font-weight: 600; color: var(--label); }
.dropzone__hint { font-size: 11px; max-width: 42ch; line-height: 1.45; }

/* Shown while files are dragged over the page. Never takes pointer events —
   otherwise crossing it fires dragleave on the view underneath and the
   highlight flickers. */
.drop-overlay {
  position: fixed;
  inset: 0;
  z-index: 150;
  display: grid;
  place-items: center;
  pointer-events: none;
  background: color-mix(in srgb, var(--accent) 12%, rgba(0, 0, 0, 0.18));
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  animation: fade 0.14s var(--ease);
}
.drop-overlay[hidden] { display: none; }
.drop-overlay__card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding: 30px 44px;
  border-radius: var(--r-panel);
  background: var(--bg-card);
  box-shadow: var(--shadow-raised);
  border: 2px dashed var(--accent);
  font-size: 14px;
  font-weight: 600;
}
.drop-overlay__card .icon { color: var(--accent); }

.receipt {
  display: flex;
  gap: 14px;
  align-items: flex-start;
}
.receipt__thumb {
  flex: none;
  padding: 0;
  border: 0.5px solid var(--separator);
  border-radius: 10px;
  overflow: hidden;
  background: var(--bg-inset);
  cursor: zoom-in;
  line-height: 0;
}
.receipt__thumb img {
  display: block;
  width: 108px;
  height: 132px;
  object-fit: cover;
}
.receipt__file {
  flex: none;
  width: 108px;
  height: 132px;
  display: grid;
  place-items: center;
  border-radius: 10px;
  background: var(--bg-inset);
  border: 0.5px solid var(--separator);
  color: var(--label-tertiary);
}

/* The month headers in the expense list carry a running total on the right. */
.group-header { display: flex; align-items: center; gap: 8px; }

/* ---------- Sheets ---------- */

.sheet-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.28);
  backdrop-filter: blur(2px);
  display: grid;
  place-items: start center;
  padding: 8vh 16px 16px;
  z-index: 100;
  animation: fade 0.18s var(--ease);
}
.sheet {
  width: min(560px, 100%);
  max-height: 80vh;
  overflow-y: auto;
  background: var(--bg-card);
  border-radius: var(--r-panel);
  box-shadow: var(--shadow-raised);
  animation: rise 0.24s var(--ease);
}
.sheet__header {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 14px 18px;
  border-bottom: 0.5px solid var(--separator);
}
.sheet__body { padding: 16px 18px; }
.sheet__footer {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding: 12px 18px;
  border-top: 0.5px solid var(--separator);
}

@keyframes fade { from { opacity: 0; } }
@keyframes rise { from { opacity: 0; transform: translateY(10px) scale(0.98); } }

/* ---------- Toasts ---------- */

.toast-host {
  position: fixed;
  bottom: 22px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  gap: 8px;
  z-index: 200;
  pointer-events: none;
}
.toast {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 14px;
  border-radius: 20px;
  background: var(--bg-card);
  box-shadow: var(--shadow-raised);
  font-size: 12.5px;
  animation: rise 0.24s var(--ease);
}
.toast .icon { color: var(--green); }

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

/* Too narrow for two columns side by side. The list stacks above the detail
   rather than being hidden — hiding it leaves no way to pick anything else. */
@media (max-width: 900px) {
  .workspace, .messages {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: minmax(140px, 34vh) minmax(0, 1fr);
  }
  .workspace__list {
    border-right: 0;
    border-bottom: 0.5px solid var(--separator);
  }
}

@media (max-width: 760px) {
  :root { --sidebar-w: 236px; }
  .window { grid-template-rows: 46px minmax(0, 1fr); }
  .titlebar { grid-template-columns: auto minmax(0, 1fr); }
  .traffic { display: none; }
  .split { grid-template-columns: minmax(0, 1fr); }
  .sidebar {
    position: fixed;
    top: 46px; bottom: 0; left: 0;
    width: var(--sidebar-w);
    z-index: 50;
  }
  .window[data-sidebar="hidden"] .split,
  .window[data-sidebar="hidden"] .titlebar { grid-template-columns: auto minmax(0, 1fr); }
  /* Toolbar buttons drop to icons so the view title survives. A button with
     no icon keeps its label — otherwise it would render as an empty box. */
  .titlebar .btn:has(.icon) .btn__label { display: none; }
  .titlebar .btn:has(.icon) { padding: 0 9px; }
  .titlebar__leading { gap: 6px; }

  .page { padding: 14px 14px 50px; }
  .grid { grid-template-columns: minmax(0, 1fr); }
  .form-grid { grid-template-columns: minmax(0, 1fr); }
  .form-grid > label { text-align: left; }
  .cal { --cell-min: 62px; font-size: 10px; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* =========================================================================
   Sign-in.

   Only ever on screen when js/config.js exists and nobody is signed in —
   the standalone demo never renders this at all.

   Sits above everything on its own layer rather than as a "view", because
   it is not part of the app: the app behind it has no data yet, and a
   half-drawn shell showing through would suggest otherwise.
   ========================================================================= */

.signin {
  position: fixed;
  inset: 0;
  z-index: 9000;
  display: grid;
  place-items: center;
  padding: 24px;
  background: var(--bg-window);
  overflow: auto;
}

.signin__card {
  width: 100%;
  max-width: 360px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 28px;
  border-radius: var(--r-panel);
  background: var(--bg-card);
  box-shadow: var(--shadow-raised);
}

.signin__title {
  margin: 0;
  font-size: 24px;
  font-weight: 700;
  letter-spacing: -0.02em;
}

.signin__blurb {
  margin: 0 0 4px;
  font-size: 13px;
  color: var(--label-secondary);
}

.signin__label {
  display: flex;
  flex-direction: column;
  gap: 5px;
  font-size: 12px;
  font-weight: 600;
  color: var(--label-secondary);
}

.signin__input {
  width: 100%;
  padding: 9px 11px;
  font: inherit;
  font-size: 14px;
  color: var(--label);
  background: var(--bg-inset);
  border: 1px solid transparent;
  border-radius: var(--r-field);
}

.signin__input:focus {
  outline: none;
  border-color: var(--accent);
  background: var(--bg-content);
}

.signin__error {
  margin: 0;
  font-size: 12.5px;
  color: var(--red);
}

.signin__go {
  margin-top: 4px;
  padding: 10px 14px;
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  color: var(--on-accent);
  background: var(--accent);
  border: none;
  border-radius: var(--r-field);
  cursor: pointer;
}

.signin__go:disabled { opacity: 0.6; cursor: default; }

/* The limitation, stated where it will be read. Bordered rather than grey
   small print, because "your edits are not saved" is not a footnote. */
.signin__note {
  margin-top: 4px;
  padding: 10px 12px;
  font-size: 12px;
  line-height: 1.45;
  color: var(--label-secondary);
  background: var(--bg-inset);
  border-left: 3px solid var(--orange, #f0a020);
  border-radius: var(--r-control);
}

.signin__note strong { color: var(--label); }

.signin__demo {
  padding: 6px;
  font: inherit;
  font-size: 12.5px;
  color: var(--label-secondary);
  background: none;
  border: none;
  cursor: pointer;
  text-decoration: underline;
}

.signin__demo:hover { color: var(--label); }
