/* ============================================================================
   Life Compass — "True North" design system
   Calm daylight. Whitespace organizes; nothing glows, nothing is dense.
   Tokens follow docs/design/DESIGN_LANGUAGE.md exactly.
   ========================================================================== */

:root {
  color-scheme: light;

  /* Surfaces & ink */
  --warm-white: #FDFCF9;
  --card: #F4F1E9;
  --card-sunk: #ECE8DD;   /* nested fill on a card (inputs, wells) */
  --card-raise: #FBFAF6;  /* lifted panels (inspector, modals) */
  --ink: #23281F;
  --ink-soft: #7B7768;
  --muted: #A39E8F;
  --hairline: rgba(35, 40, 31, 0.07); /* used sparingly; never a visible border */

  /* Brand */
  --evergreen: #2F5D50;
  --evergreen-deep: #274D42;
  --evergreen-tint: #E4ECDF;
  --evergreen-on: #F4F2EA;   /* text on evergreen */
  --evergreen-soft: #A8C5AE; /* labels on evergreen */
  --needle-coral: #E2704A;
  --coral-deep: #B84A28;

  /* Area colors — used EVERYWHERE (chips, charts, calendar) */
  --fam-bg: #FBE3D6;    --fam-fg: #B4552E;
  --earn-bg: #E4ECDF;   --earn-fg: #2F5D50;
  --learn-bg: #E3E9F4;  --learn-fg: #3D5A8F;
  --return-bg: #F2E6F0; --return-fg: #85477C;

  /* Signals */
  --due-overdue-bg: rgba(226, 112, 74, 0.15);
  --due-today-bg: #E4ECDF;
  --alert-bg: #FBE0DA;
  --alert-fg: #B23A2E;

  /* Type */
  --font: "Avenir Next", "Helvetica Neue", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;

  /* Shape */
  --r-xs: 8px;
  --r-sm: 10px;
  --r: 14px;
  --r-lg: 18px;
  --r-pill: 999px;

  /* Elevation — soft, warm, low-contrast */
  --shadow-row: 0 1px 2px rgba(35, 40, 31, 0.03);
  --shadow-card: 0 2px 10px rgba(35, 40, 31, 0.05);
  --shadow-lift: 0 8px 24px rgba(35, 40, 31, 0.10);
  --shadow-panel: -8px 0 40px rgba(35, 40, 31, 0.12);
  --shadow-hero: 0 10px 30px rgba(47, 93, 80, 0.16);

  /* Motion */
  --t-fast: 130ms;
  --t: 180ms;
  --ease: cubic-bezier(0.22, 1, 0.36, 1);

  /* Layout metrics */
  --sidebar-w: 248px;
  --main-max: 860px;
  --inspector-w: 340px;
  --topbar-h: 64px;
}

/* ---- reset ---------------------------------------------------------------- */
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
html { background: var(--warm-white); }
body {
  min-height: 100vh;
  min-height: 100dvh;
  background: var(--warm-white);
  color: var(--ink);
  font-family: var(--font);
  font-weight: 500;
  font-size: 15px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  font-variant-numeric: tabular-nums;
}
button { font-family: inherit; cursor: pointer; }
input, select, textarea { font-family: inherit; font-size: inherit; color: inherit; }
a { color: inherit; text-decoration: none; }
h1, h2, h3, h4 { margin: 0; font-weight: 700; letter-spacing: -0.02em; }
::selection { background: rgba(47, 93, 80, 0.16); }

/* ---- focus rings ---------------------------------------------------------- */
:focus { outline: none; }
:focus-visible {
  outline: 2.5px solid var(--evergreen);
  outline-offset: 2px;
  border-radius: 6px;
}

/* ---- scrollbars ----------------------------------------------------------- */
* { scrollbar-width: thin; scrollbar-color: var(--muted) transparent; }
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb {
  background: rgba(163, 158, 143, 0.5);
  border-radius: 999px;
  border: 3px solid transparent;
  background-clip: content-box;
}
*::-webkit-scrollbar-thumb:hover { background: rgba(163, 158, 143, 0.8); background-clip: content-box; }

/* ============================================================================
   APP SHELL — sidebar / main / inspector
   ========================================================================== */
.app-shell {
  display: grid;
  grid-template-columns: var(--sidebar-w) 1fr;
  min-height: 100vh;
  min-height: 100dvh;
  /* The rail folding is the one layout change he triggers by hand; the column
     slides rather than jumps so the page reads as the same page, narrower. */
  transition: grid-template-columns var(--t) var(--ease);
}
@media (prefers-reduced-motion: reduce) {
  .app-shell { transition: none; }
}

/* Old-world map face for the nameplate (John Fell's 17th-c. types, self-hosted) */
@font-face {
  font-family: 'IM Fell English SC';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/assets/fonts/im-fell-english-sc.woff2') format('woff2');
}

/* ---- sidebar -------------------------------------------------------------- */
.sidebar {
  position: sticky;
  top: 0;
  height: 100vh;
  height: 100dvh;
  display: flex;
  flex-direction: column;
  padding: 6px 14px 14px;
  gap: 4px;
  overflow-y: auto;
  overscroll-behavior: contain;
  /* How far the brand has condensed: 0 is the tall stack, 1 is the pinned bar.
     Written here rather than on .brand so app.js can drive it from the element
     it is already listening to, and so a declaration on .brand cannot shadow
     the inherited value. */
  --brand-t: 0;
}

/* ---- the brand, which shrinks in place ------------------------------------
   The rail is taller than any screen, so the mark is the first thing to leave
   when he reaches for something near the bottom of it — and the mark is how he
   calls Moneypenny, which makes scrolling it away an unaffordable thing to do.
   So it stays. Over the first 150px of scroll the tall vertical stack tweens
   into a compact horizontal bar pinned at the top, with the nav sliding
   underneath it.

   One element and one number: --brand-t runs 0 to 1 and every dimension below
   is derived from it, so there is no second state to fall out of step and
   nothing appears or disappears on the way. Both children are placed
   absolutely, because a flex column cannot tween into a flex row — the
   direction change is the one thing in this transition that would have to be a
   jump, and the whole point is that there is not one. */
.brand {
  --brand-mark-max: calc(var(--sidebar-w) - 28px);
  --brand-mark: calc(var(--brand-mark-max) - var(--brand-t) * (var(--brand-mark-max) - 40px));
  /* The wordmark's own progress, and it is deliberately not --brand-t.
     Moving it up beside the mark in step with the shrink walks it straight
     through two hundred pixels of logo, and the frames in the middle read as a
     fault: three letters of "Life Compass" poking out past the rim. So it
     stays UNDER the mark — riding its bottom edge down as it shrinks, fully
     legible the whole way — and only moves across in the last third, once there
     is somewhere for it to go.

     Across FIRST, then up: two staggered progressions rather than one. Moving
     both at once puts the wordmark level with the mark while it is still partly
     behind it, and "ife Compass" is not a frame worth shipping. Sliding clear
     while still safely underneath, and rising only after, is never occluded at
     any point of the scroll. */
  --brand-slide: clamp(0, calc((var(--brand-t) - 0.58) / 0.22), 1);
  --brand-rise: clamp(0, calc((var(--brand-t) - 0.80) / 0.20), 1);
  --brand-word-size: calc(25px - var(--brand-t) * 8.5px);
  --brand-word-top: calc(
    4px + (1 - var(--brand-rise)) * (var(--brand-mark) + 10px) + var(--brand-rise) * 10px);
  /* Whichever of the two is taller: the mark, or the wordmark sitting under it. */
  --brand-bar: max(
    calc(4px + var(--brand-mark) + 12px),
    calc(var(--brand-word-top) + var(--brand-word-size) * 1.15 + 12px));
  position: sticky;
  top: 0;
  z-index: 3;
  flex: 0 0 auto;
  display: block;
  /* CONSTANT, and that is the load-bearing part. The bar drawn inside this box
     shrinks; the box itself never does. A brand that gave its height back as it
     condensed would shorten the rail by two hundred pixels, the browser would
     clamp the scroll position back to fit, --brand-t would fall, the rail would
     grow again — and the whole thing would judder under his thumb instead of
     condensing. Nothing below changes layout: the children are placed
     absolutely and the backdrop is a pseudo-element. */
  height: calc(var(--brand-mark-max) + 55px);
  margin-bottom: 10px;
  padding: 4px 0 0;
  text-align: center;
  /* The box stays full height while the bar inside it is small, so most of it
     is empty space sitting over the nav. It must not swallow clicks meant for
     what is showing through — only the painted part does, via ::before. */
  pointer-events: none;
}
.brand > * { pointer-events: auto; }
/* The bar itself: an opaque backdrop bleeding out into the rail's own side
   padding, so the active-item marker at left:-14px passes behind it rather than
   beside it. Its bottom edge is the horizon the nav disappears behind, and that
   edge only draws itself once there is something behind it to hide. */
.brand::before {
  content: "";
  position: absolute;
  z-index: -1;
  pointer-events: auto;
  top: -6px;
  left: -14px;
  right: -14px;
  height: calc(6px + var(--brand-bar));
  background: var(--warm-white);
  /* Late, on purpose. A horizon drawn over open space is a line with nothing
     behind it; it earns itself only once the nav has climbed far enough to be
     going under. */
  border-bottom: 1px solid color-mix(in srgb,
    var(--hairline) calc(clamp(0, (var(--brand-t) - 0.7) * 3.4, 1) * 100%), transparent);
}
/* The mark holds two stacked layers: the still logo, and — on real pointers
   only — a muted video of the same artwork in motion. Both fill the same box,
   so revealing the video never moves anything. A third layer goes on top while
   she is listening: the bezel canvas, clipped to this rounded box for free. */
.brand .brand-mark {
  position: absolute;
  z-index: 2;
  top: 4px;
  left: calc((var(--brand-mark-max) - var(--brand-mark)) / 2 * (1 - var(--brand-t)));
  width: var(--brand-mark);
  height: var(--brand-mark);
  padding: 0;
  border: 0;
  /* Opaque, and above the wordmark, because the wordmark passes BEHIND it on the
     way from under the mark to beside it. Two hundred and twenty pixels of logo
     cannot get out of the way of a wordmark travelling up and to the right —
     there is no path around it — so it goes through, and the mark occludes it
     the way anything in front of anything else would. */
  background: var(--warm-white);
  border-radius: calc(32px - var(--brand-t) * 20px);
  overflow: hidden;
  isolation: isolate;
}
.brand .brand-mark:focus-visible {
  border-radius: calc(32px - var(--brand-t) * 20px);
  outline-offset: 3px;
}
.brand .brand-mark > img,
.brand .brand-mark > video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  object-fit: cover;
}
/* Revealed only once the video is genuinely playing (see app.js), so a blocked
   or failed load leaves the still logo showing rather than a black square.
   Fading in also hides the small differences between the artwork and the
   model's first frame; after that the video stays up and simply freezes on
   mouseleave, so the motion reads as continuous across repeated hovers. */
.brand .brand-mark > video {
  opacity: 0;
  transition: opacity 220ms var(--ease, ease);
  pointer-events: none;
}
.brand .brand-mark.is-live > video { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .brand .brand-mark > video { display: none; }
}
/* Below the mark at full size, beside it once condensed — one slide, no wrap,
   no reflow. It stays centred in whatever space the mark has not taken: the box
   is pinned to the right edge and its left edge is pushed along by the mark, so
   the same `text-align: center` reads as "under the logo" at full size and
   "beside it" at the end, with no second rule to switch between. */
.brand .wordmark {
  position: absolute;
  z-index: 1;
  top: var(--brand-word-top);
  left: calc(var(--brand-slide) * (var(--brand-mark) + 12px));
  right: 0;
  text-align: center;
  font-family: 'IM Fell English SC', Georgia, serif;
  font-size: var(--brand-word-size);
  font-weight: 400;
  letter-spacing: 0.04em;
  color: var(--evergreen);
  line-height: 1.15;
  white-space: nowrap;
}

.nav-section-label {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  padding: 16px 12px 6px;
}

.nav-item {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 9px 12px;
  border: 0;
  background: transparent;
  font-family: inherit;
  border-radius: var(--r-sm);
  color: var(--ink-soft);
  font-size: 14.5px;
  font-weight: 500;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
  position: relative;
  user-select: none;
}
.nav-item .nav-icon { flex: 0 0 auto; width: 19px; height: 19px; color: var(--muted); transition: color var(--t-fast) var(--ease); }
.nav-item .nav-icon svg { display: block; width: 19px; height: 19px; }
.nav-item .nav-label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nav-item:hover { background: var(--card); color: var(--ink); }
.nav-item:hover .nav-icon { color: var(--ink-soft); }
.nav-item.is-active { background: var(--card); color: var(--ink); font-weight: 600; }
.nav-item.is-active .nav-icon { color: var(--evergreen); }
.nav-item.is-active::before {
  content: "";
  position: absolute;
  left: -14px;
  top: 50%;
  transform: translateY(-50%);
  width: 3px;
  height: 20px;
  border-radius: 0 3px 3px 0;
  background: var(--evergreen);
}
/* sheet-only chrome: hidden everywhere else (outranks .iconbtn's display) */
.sidebar:not(.is-sheet) .sheet-close { display: none; }
.nav-dot { display: none; }

.nav-badge {
  flex: 0 0 auto;
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  border-radius: 999px;
  background: var(--card-sunk);
  color: var(--ink-soft);
  font-size: 11.5px;
  font-weight: 700;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.nav-badge.is-alert { background: var(--alert-bg); color: var(--alert-fg); }
.nav-item.is-active .nav-badge { background: var(--evergreen); color: var(--evergreen-on); }

.nav-spacer { flex: 1 1 auto; min-height: 12px; }
.nav-foot { display: flex; flex-direction: column; gap: 2px; padding-top: 6px; }

/* Mobile "More" sheet controls — hidden on desktop */
.sidebar-scrim { display: none; }
.mobilebar { display: none; }

/* ---- the folded rail (desktop) ---------------------------------------------
   `.app-shell.nav-collapsed` (app.js, remembered per device) narrows the rail
   to a spine: the compass mark pinned at the top — it is how he calls
   Moneypenny, so it is the one thing a folded rail may never put away — the
   same items as icons with their names on hover, badges still counting, the
   fold control at the bottom. Nothing is removed; the labels step aside and
   the column slides. Scoped to the desktop: below 900px the rail IS the tab
   bar and has no fold. */
.nav-collapse {
  position: sticky;
  bottom: 0;
  z-index: 2;
  margin-top: 4px;
  background: var(--warm-white);
  color: var(--muted);
  /* A <button>, not an <a>: it centres its text unless told otherwise, and
     the label must sit where every other label sits. */
  text-align: left;
  width: 100%;
  /* When the rail overflows, this pins over the last item the way the brand
     pins over the first; a soft lip says so instead of a hard edge. */
  box-shadow: 0 -10px 10px -8px rgba(35, 40, 31, 0.07);
}
.nav-collapse:hover { color: var(--ink); }
.nav-collapse .nav-icon { transition: transform var(--t) var(--ease), color var(--t-fast) var(--ease); }
@media (min-width: 901px) {
  .app-shell.nav-collapsed { --sidebar-w: 72px; }
  .app-shell.nav-collapsed .sidebar { padding-left: 12px; padding-right: 12px; }
  /* The brand: one small state, no journey. app.js holds --brand-t at 0 while
     folded, so the scroll-linked condense — and the constant-height rule that
     protects it — has nothing to do here; the box is simply the mark's. */
  .app-shell.nav-collapsed .brand {
    --brand-bar: 58px;
    height: 62px;
    margin-bottom: 4px;
  }
  .app-shell.nav-collapsed .brand .brand-mark {
    left: 2px;
    top: 6px;
    width: 44px;
    height: 44px;
    border-radius: 12px;
  }
  .app-shell.nav-collapsed .brand .brand-mark:focus-visible { border-radius: 12px; }
  .app-shell.nav-collapsed .brand .wordmark { opacity: 0; pointer-events: none; }
  /* Sections stay separated by space, not by a rule: the label gives up its
     words and keeps a sliver of its height. */
  .app-shell.nav-collapsed .nav-section-label { height: 10px; padding: 0; overflow: hidden; color: transparent; }
  .app-shell.nav-collapsed .nav-item { justify-content: center; gap: 0; padding: 10px 0; }
  /* The name is still in the document — for assistive tech and for the
     tooltip that reads it — just not in the layout. */
  .app-shell.nav-collapsed .nav-item .nav-label {
    position: absolute; width: 1px; height: 1px; overflow: hidden;
    clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
  }
  .app-shell.nav-collapsed .nav-item.is-active::before { left: -12px; }
  .app-shell.nav-collapsed .nav-badge {
    position: absolute; top: 3px; right: 5px;
    min-width: 16px; height: 16px; padding: 0 4px; font-size: 10px;
  }
  .app-shell.nav-collapsed .nav-foot { padding-top: 4px; }
  /* The fold control's icon is the rail itself; folded, it points the way back out. */
  .app-shell.nav-collapsed .nav-collapse .nav-icon { transform: scaleX(-1); }
}
@media (max-width: 900px) {
  .nav-collapse { display: none !important; }
}
/* The name beside the icon, for a rail that has put its labels away. One
   element on body, placed by measurement (app.js): the rail clips its own
   overflow because it scrolls, so anything drawn inside an item would be cut
   off at the rail's edge. */
.rail-tip {
  position: fixed;
  z-index: 70;
  transform: translateY(-50%);
  padding: 6px 10px;
  border-radius: var(--r-sm);
  background: var(--ink);
  color: var(--warm-white);
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: 0.01em;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--t-fast) var(--ease);
  box-shadow: var(--shadow-lift);
}
.rail-tip.is-on { opacity: 1; }

/* ---- main column ---------------------------------------------------------- */
.main {
  min-width: 0;
  display: flex;
  flex-direction: column;
}
.topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  height: var(--topbar-h);
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 0 clamp(20px, 4vw, 40px);
  background: color-mix(in srgb, var(--warm-white) 88%, transparent);
  backdrop-filter: saturate(1.1) blur(8px);
  -webkit-backdrop-filter: saturate(1.1) blur(8px);
}
.topbar-title {
  font-size: 19px;
  font-weight: 700;
  letter-spacing: -0.02em;
  flex: 0 0 auto;
}
.topbar-meta {
  flex: 1 1 auto;
  display: flex;
  align-items: baseline;
  gap: 10px;
  min-width: 0;
}
.topbar-date {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--ink-soft);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.topbar-tz {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  white-space: nowrap;
}
.topbar-actions { flex: 0 0 auto; display: flex; align-items: center; gap: 8px; }

.view-scroll {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: 8px clamp(20px, 4vw, 40px) 120px;
}
.view {
  max-width: var(--main-max);
  margin: 0 auto;
  width: 100%;
  animation: view-in var(--t) var(--ease) both;
}
/* Brief documents read better edge-to-edge (Ben, 2026-07-19) */
.view.view-wide { max-width: none; }
@keyframes view-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }

/* ---- iconbutton ----------------------------------------------------------- */
.iconbtn {
  width: 38px;
  height: 38px;
  border: 0;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--ink-soft);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.iconbtn:hover { background: var(--card); color: var(--ink); }
.iconbtn svg { width: 20px; height: 20px; display: block; }
.iconbtn.is-primary { background: var(--evergreen); color: var(--evergreen-on); }
.iconbtn.is-primary:hover { background: var(--evergreen-deep); }

/* ============================================================================
   BUTTONS / INPUTS
   ========================================================================== */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 10px 18px;
  border: 0;
  border-radius: var(--r-pill);
  background: var(--evergreen);
  color: var(--evergreen-on);
  font-size: 14.5px;
  font-weight: 600;
  letter-spacing: -0.01em;
  transition: background var(--t-fast) var(--ease), transform var(--t-fast) var(--ease);
}
.btn:hover { background: var(--evergreen-deep); }
.btn:active { transform: scale(0.97); }
.btn[disabled] { opacity: 0.5; pointer-events: none; }
.btn-ghost {
  background: transparent;
  color: var(--ink-soft);
}
.btn-ghost:hover { background: var(--card); color: var(--ink); }
.btn-danger { background: transparent; color: var(--coral-deep); }
.btn-danger:hover { background: var(--alert-bg); color: var(--alert-fg); }
/* An action that must read as a button next to a status that must read as a
   status (Ben, 8/23: "put on hold has no outline around it"). */
.btn-outline { background: transparent; color: var(--evergreen); box-shadow: inset 0 0 0 1.5px var(--evergreen); }
.btn-outline:hover { background: var(--evergreen-tint); color: var(--evergreen-deep); }
.btn-outline-danger { color: var(--coral-deep); box-shadow: inset 0 0 0 1.5px var(--coral-deep); }
.btn-outline-danger:hover { background: var(--alert-bg); color: var(--alert-fg); box-shadow: inset 0 0 0 1.5px var(--alert-fg); }
.btn-sm { padding: 7px 13px; font-size: 13px; }

.input, .textarea, .select {
  width: 100%;
  padding: 11px 13px;
  border: 0;
  border-radius: var(--r-sm);
  background: var(--card-sunk);
  color: var(--ink);
  font-size: 15px;
  font-weight: 500;
  transition: box-shadow var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.input::placeholder, .textarea::placeholder { color: var(--muted); }
.input:focus, .textarea:focus, .select:focus {
  outline: none;
  background: var(--card-raise);
  box-shadow: inset 0 0 0 2px var(--evergreen);
}
.textarea { resize: vertical; min-height: 84px; line-height: 1.5; }
.select {
  appearance: none;
  -webkit-appearance: none;
  padding-right: 34px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%237B7768' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
}
.field { display: flex; flex-direction: column; gap: 7px; }
.field-label {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}
.field-help { font-size: 12.5px; color: var(--ink-soft); font-weight: 500; }

/* date input with resolved-date helper */
.dateinput { display: flex; flex-direction: column; gap: 5px; }
.dateinput .resolved { font-size: 12px; font-weight: 600; color: var(--evergreen); min-height: 15px; }
.dateinput .resolved.is-empty { color: var(--muted); font-weight: 500; }
.dateinput .resolved-local { font-size: 11.5px; font-weight: 500; color: var(--muted); letter-spacing: 0.01em; }
.dateinput .resolved-local[hidden] { display: none; }

/* ---- segmented control ----------------------------------------------------
   For a setting whose ALTERNATIVES are the point. A select collapses a decision
   into its answer; this shows the whole decision at once, which is what a
   choice between "she may move your screen" and "leave it alone" needs. Reach
   for a select instead when the list is long enough that showing it all is
   noise — the sixteen time zones above are the example.

   Evergreen for the chosen segment, because that is who she is everywhere else
   in this app: the needle, the bezel and the orb all speak evergreen for her
   and coral for him, and a settings page about her ought to be in the same
   language as the instrument it configures. */
.seg {
  display: flex;
  gap: 3px;
  padding: 3px;
  background: var(--card-sunk);
  border-radius: var(--r-sm);
}
.seg-opt {
  flex: 1 1 0;
  min-width: 0;
  padding: 8px 10px;
  border: 0;
  border-radius: 7px;
  background: transparent;
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-soft);
  cursor: pointer;
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.seg-opt:hover { color: var(--ink); }
.seg-opt[aria-checked="true"] {
  background: var(--evergreen);
  color: var(--evergreen-on);
  box-shadow: var(--shadow-row);
}
.seg-opt[aria-checked="true"]:hover { color: var(--evergreen-on); }
.seg.is-off { opacity: 0.45; pointer-events: none; }

/* Narrow phones: three or four segments of prose do not fit across a screen,
   so they stack rather than truncate. A label clipped to "Ask about pro…" is
   not a choice he can make.

   `flex: 0 0 auto` is load-bearing, not tidying. In a row, `flex: 1 1 0` gives
   every segment an equal share of the width, which is the whole point. Turn the
   same rule on its side and the basis becomes a HEIGHT of zero — so the options
   collapsed to nothing and printed on top of each other, four labels in the
   space of one. It read as a rendering fault rather than a control. Caught by
   looking at it; every assertion in the suite passed. */
@media (max-width: 560px) {
  .seg { flex-direction: column; }
  .seg-opt { flex: 0 0 auto; text-align: left; }
}

/* ---- standing orders ------------------------------------------------------
   The one sentence in Settings that says what Moneypenny will actually do,
   assembled from five switches that no single row can speak for. It carries no
   box and no rule: at 15px against 11px uppercase labels it is already the
   loudest thing on the card, and a container would only make it look like a
   callout rather than like her terms. */
.mp-orders {
  margin: -4px 0 2px;
  font-size: 15px;
  line-height: 1.55;
  font-weight: 500;
  color: var(--ink);
  border-radius: var(--r-sm);
}
/* It changes under his hand as he moves the switches, and a paragraph that
   quietly rewrites itself is one he will not notice is live. */
.mp-orders.is-changed { animation: mp-orders-wash 900ms var(--ease) 1; }
@keyframes mp-orders-wash {
  0%   { background: var(--evergreen-tint); box-shadow: 0 0 0 6px var(--evergreen-tint); }
  100% { background: transparent; box-shadow: 0 0 0 6px transparent; }
}
@media (prefers-reduced-motion: reduce) {
  .mp-orders.is-changed { animation: none; }
}

/* toggle (flag / boolean) */
.toggle {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 8px 12px;
  border: 0;
  border-radius: var(--r-sm);
  background: var(--card-sunk);
  color: var(--ink-soft);
  font-size: 14px;
  font-weight: 600;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.toggle:hover { background: var(--card); }
.toggle.is-on { background: var(--due-overdue-bg); color: var(--coral-deep); }
.toggle svg { width: 16px; height: 16px; }

/* ============================================================================
   CHIPS — area / priority / due / generic
   ========================================================================== */
.chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 10px;
  border-radius: var(--r-pill);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  line-height: 1.35;
  white-space: nowrap;
  background: var(--card-sunk);
  color: var(--ink-soft);
}
.chip-soft { background: var(--card-sunk); color: var(--ink-soft); }
.chip-evergreen { background: var(--evergreen-tint); color: var(--evergreen); }

/* area chips */
.area-chip {
  display: inline-flex;
  align-items: center;
  padding: 4px 10px;
  border-radius: var(--r-pill);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  white-space: nowrap;
}
.area-fam { background: var(--fam-bg); color: var(--fam-fg); }
.area-earn { background: var(--earn-bg); color: var(--earn-fg); }
.area-learn { background: var(--learn-bg); color: var(--learn-fg); }
.area-return { background: var(--return-bg); color: var(--return-fg); }

/* a small solid dot in the area color (for legends / calendars) */
.area-dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%; flex: 0 0 auto; }
.area-dot.area-fam { background: var(--fam-fg); }
.area-dot.area-earn { background: var(--earn-fg); }
.area-dot.area-learn { background: var(--learn-fg); }
.area-dot.area-return { background: var(--return-fg); }

/* priority chips */
.prio {
  display: inline-flex;
  align-items: center;
  padding: 4px 9px;
  border-radius: var(--r-pill);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  white-space: nowrap;
}
.prio-urgent { background: var(--due-overdue-bg); color: var(--coral-deep); }
.prio-normal { background: var(--card-sunk); color: var(--ink-soft); }
.prio-low { background: transparent; color: var(--muted); }
.prio-missing { background: var(--alert-bg); color: var(--alert-fg); text-transform: lowercase; letter-spacing: 0.02em; }

/* due chips */
.due {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 9px;
  border-radius: var(--r-pill);
  font-size: 11.5px;
  font-weight: 700;
  white-space: nowrap;
}
.due-overdue { background: var(--due-overdue-bg); color: var(--coral-deep); }
.due-today { background: var(--due-today-bg); color: var(--evergreen); }
.due-soon { background: var(--card-sunk); color: var(--ink-soft); }

/* ============================================================================
   TASK ROW — the workhorse pill
   ========================================================================== */
.task-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 11px 14px;
  border-radius: var(--r);
  background: var(--card);
  margin-bottom: 8px;
  box-shadow: var(--shadow-row);
  transition: box-shadow var(--t-fast) var(--ease), transform var(--t-fast) var(--ease);
}
.task-row:hover { box-shadow: var(--shadow-card); }
.task-row.is-dragging { opacity: 0.4; }
.task-row.drop-before { box-shadow: inset 0 3px 0 -1px var(--evergreen), var(--shadow-row); }
.task-row.drop-after { box-shadow: inset 0 -3px 0 -1px var(--evergreen), var(--shadow-row); }

/* checkbox */
.tr-check {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  border: 0;
  padding: 0;
  background: transparent;
  box-shadow: inset 0 0 0 2px #C9C3B2;
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: box-shadow var(--t-fast) var(--ease), background var(--t-fast) var(--ease), transform var(--t-fast) var(--ease);
}
.tr-check:hover { box-shadow: inset 0 0 0 2px var(--evergreen); }
.tr-check svg {
  width: 12px;
  height: 12px;
  color: var(--evergreen-on);
  stroke-width: 3;
  opacity: 0;
  transform: scale(0.5);
  transition: opacity var(--t-fast) var(--ease), transform var(--t-fast) var(--ease);
}
.task-row.is-done .tr-check,
.tr-check.is-checking {
  background: var(--evergreen);
  box-shadow: inset 0 0 0 2px var(--evergreen);
}
.task-row.is-done .tr-check svg,
.tr-check.is-checking svg { opacity: 1; transform: scale(1); }
.tr-check.is-checking { animation: check-pop 250ms var(--ease); }
@keyframes check-pop { 0% { transform: scale(1); } 40% { transform: scale(0.82); } 100% { transform: scale(1); } }

/* flag */
.tr-flag {
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  border: 0;
  border-radius: 7px;
  background: transparent;
  color: var(--muted);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transition: opacity var(--t-fast) var(--ease), color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.task-row:hover .tr-flag { opacity: 1; }
.tr-flag:hover { background: var(--card-sunk); color: var(--ink-soft); }
.tr-flag svg { width: 15px; height: 15px; }
.tr-flag.is-on { opacity: 1; color: var(--needle-coral); }
.tr-flag.is-on svg { fill: var(--needle-coral); }

/* body */
.tr-body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.tr-title {
  font-size: 14.5px;
  font-weight: 500;
  line-height: 1.35;
  color: var(--ink);
  cursor: pointer;
  overflow-wrap: anywhere;
}
.tr-title:hover { color: var(--evergreen); }
.task-row.is-done .tr-title { text-decoration: line-through; color: var(--muted); }
.tr-sub {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  color: var(--ink-soft);
  min-width: 0;
}
.tr-sub .tr-project { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 500; }
.tr-repeat { flex: 0 0 auto; color: var(--muted); display: inline-flex; }
.tr-repeat svg { width: 13px; height: 13px; }

/* trailing meta cluster */
.tr-meta { flex: 0 0 auto; display: flex; align-items: center; gap: 7px; }

/* ============================================================================
   HERO CARD — the compass heading
   ========================================================================== */
.hero {
  background: var(--evergreen);
  color: var(--evergreen-on);
  border-radius: var(--r-lg);
  padding: 22px 24px;
  box-shadow: var(--shadow-hero);
}
.hero-k {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--evergreen-soft);
}
.hero-v { font-size: 19px; font-weight: 600; line-height: 1.4; margin-top: 6px; letter-spacing: -0.01em; }
.hero-stats { display: flex; flex-wrap: wrap; gap: 20px; margin-top: 16px; }
.hero-stat { font-size: 12.5px; color: #CFE0D2; display: flex; align-items: baseline; gap: 6px; }
.hero-stat b { color: #fff; font-size: 17px; font-weight: 700; }

/* ============================================================================
   GENERIC CARD / SECTIONS
   ========================================================================== */
.card {
  background: var(--card);
  border-radius: var(--r-lg);
  padding: 18px 20px;
  box-shadow: var(--shadow-row);
}
.section { margin-top: 30px; }
.section:first-child { margin-top: 6px; }
.section-header {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 0 4px 12px;
}
.section-header .sh-title {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
.section-header .sh-count {
  font-size: 11.5px;
  font-weight: 700;
  color: var(--muted);
  background: var(--card);
  border-radius: 999px;
  padding: 1px 8px;
}
.section-header .sh-rule { flex: 1 1 auto; }

.page-lead { margin: 4px 4px 22px; }
.page-lead .greeting { font-size: 27px; font-weight: 700; letter-spacing: -0.02em; }
.page-lead .subhead { font-size: 14px; color: var(--ink-soft); margin-top: 3px; }

/* ============================================================================
   EMPTY / LOADING / ERROR
   ========================================================================== */
.empty-state {
  text-align: center;
  padding: 56px 24px;
  color: var(--ink-soft);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
}
.empty-state .es-mark {
  width: 48px;
  height: 48px;
  color: var(--muted);
  opacity: 0.7;
}
.empty-state .es-mark svg { width: 48px; height: 48px; }
.empty-state .es-msg { font-size: 15px; font-weight: 500; max-width: 34ch; }

.loading {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 56px 24px;
  color: var(--muted);
  font-size: 14px;
  font-weight: 500;
}
.spinner {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  border: 2.5px solid var(--card-sunk);
  border-top-color: var(--evergreen);
  animation: spin 0.7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

.error-state {
  text-align: center;
  padding: 48px 24px;
  color: var(--ink-soft);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
}
.error-state .es-msg { font-size: 15px; font-weight: 500; max-width: 40ch; }

/* ============================================================================
   MODAL
   ========================================================================== */
.modal-scrim {
  position: fixed;
  inset: 0;
  z-index: 200;
  background: rgba(35, 40, 31, 0.28);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 10vh 20px 20px;
  animation: scrim-in var(--t) var(--ease) both;
}
@keyframes scrim-in { from { opacity: 0; } to { opacity: 1; } }
.modal {
  width: 100%;
  max-width: 480px;
  background: var(--card-raise);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-lift);
  max-height: 86vh;
  max-height: 86dvh;
  overflow-x: hidden;
  overflow-y: auto;
  overscroll-behavior: contain;
  animation: modal-in var(--t) var(--ease) both;
}
@keyframes modal-in { from { opacity: 0; transform: translateY(10px) scale(0.98); } to { opacity: 1; transform: none; } }
.modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 18px 20px 6px;
}
.modal-title { font-size: 17px; font-weight: 700; letter-spacing: -0.02em; }
.modal-body { padding: 12px 20px 20px; display: flex; flex-direction: column; gap: 16px; }
.modal-foot {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
  padding: 4px 20px 20px;
}
.modal-foot .spacer { flex: 1 1 auto; }

/* ============================================================================
   TOASTS
   ========================================================================== */
.chat-composer { bottom: 0; }

.toast-stack {
  position: fixed;
  left: 50%;
  bottom: 26px;
  transform: translateX(-50%);
  z-index: 400;
  display: flex;
  flex-direction: column-reverse;
  gap: 10px;
  align-items: center;
  pointer-events: none;
}
.toast {
  pointer-events: auto;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 18px;
  border-radius: var(--r-pill);
  background: var(--evergreen);
  color: var(--evergreen-on);
  font-size: 14px;
  font-weight: 600;
  box-shadow: var(--shadow-lift);
  max-width: min(88vw, 460px);
  animation: toast-in var(--t) var(--ease) both;
}
.toast.is-leaving { animation: toast-out var(--t) var(--ease) both; }
.toast.toast-alert { background: var(--coral-deep); }
.toast.toast-plain { background: var(--ink); }
.toast .toast-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--evergreen-soft); flex: 0 0 auto; }
@keyframes toast-in { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
@keyframes toast-out { to { opacity: 0; transform: translateY(14px); } }

/* ============================================================================
   CONFIRM BAR (inline destructive confirm)
   ========================================================================== */
.confirm-bar {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  border-radius: var(--r);
  background: var(--alert-bg);
  color: var(--alert-fg);
  font-size: 14px;
  font-weight: 600;
}
.confirm-bar .cb-msg { flex: 1 1 auto; }

/* ============================================================================
   SPOTLIGHT (tutorial overlay)
   ========================================================================== */
.spotlight-scrim {
  position: fixed;
  inset: 0;
  z-index: 500;
  pointer-events: auto;
  animation: scrim-in var(--t) var(--ease) both;
}
/* the dim is drawn with a big transparent-cutout box-shadow on the ring */
.spotlight-ring {
  position: fixed;
  z-index: 501;
  border-radius: var(--r);
  box-shadow: 0 0 0 9999px rgba(35, 40, 31, 0.55);
  outline: 2.5px solid var(--evergreen-on);
  outline-offset: 4px;
  pointer-events: none;
  transition: all var(--t) var(--ease);
}
.spotlight-card {
  position: fixed;
  z-index: 502;
  width: min(320px, 88vw);
  background: var(--card-raise);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-lift);
  padding: 18px 20px 16px;
  animation: modal-in var(--t) var(--ease) both;
}
.spotlight-card .sc-step {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--evergreen);
}
.spotlight-card .sc-title { font-size: 17px; font-weight: 700; margin-top: 6px; letter-spacing: -0.02em; }
.spotlight-card .sc-text { font-size: 14px; color: var(--ink-soft); margin-top: 6px; line-height: 1.5; }
.spotlight-card .sc-foot { display: flex; align-items: center; gap: 10px; margin-top: 16px; }
.spotlight-card .sc-foot .spacer { flex: 1 1 auto; }

/* ============================================================================
   SEARCH OVERLAY
   ========================================================================== */
.search-scrim {
  position: fixed;
  inset: 0;
  z-index: 300;
  background: rgba(35, 40, 31, 0.28);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 12vh 20px 20px;
  animation: scrim-in var(--t) var(--ease) both;
}
.search-panel {
  width: 100%;
  max-width: 560px;
  background: var(--card-raise);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-lift);
  overflow: hidden;
  animation: modal-in var(--t) var(--ease) both;
}
.search-input {
  width: 100%;
  padding: 18px 20px;
  border: 0;
  background: transparent;
  font-size: 18px;
  font-weight: 500;
}
.search-input:focus { outline: none; }
.search-results { max-height: 52vh; overflow-y: auto; padding: 6px; }
.search-group-label {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  padding: 12px 12px 6px;
}
.search-result {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border-radius: var(--r-sm);
  cursor: pointer;
}
.search-result:hover, .search-result.is-active { background: var(--card); }
.search-result .sr-title { flex: 1 1 auto; font-size: 14.5px; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.search-result .sr-kind { font-size: 10.5px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); }
.search-empty { padding: 26px; text-align: center; color: var(--muted); font-size: 14px; }

/* ============================================================================
   INSPECTOR (right slide-in panel)
   ========================================================================== */
.inspector-scrim {
  position: fixed;
  inset: 0;
  z-index: 90;
  background: rgba(35, 40, 31, 0.16);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--t) var(--ease);
}
.inspector {
  position: fixed;
  top: 0;
  right: 0;
  height: 100vh;
  height: 100dvh;
  width: var(--inspector-w);
  max-width: 92vw;
  z-index: 100;
  background: var(--card-raise);
  box-shadow: var(--shadow-panel);
  transform: translateX(100%);
  transition: transform var(--t) var(--ease);
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  overscroll-behavior: contain;
}
/* The inspector + scrim are siblings of .app-shell (see app.js mount order),
   so the open state must match with the sibling combinator too. */
.app-shell.inspector-open .inspector,
.app-shell.inspector-open ~ .inspector { transform: none; }
.app-shell.inspector-open .inspector-scrim,
.app-shell.inspector-open ~ .inspector-scrim { opacity: 1; pointer-events: auto; }
.inspector-head {
  position: sticky;
  top: 0;
  /* Above the body's rows: the complete-button's transform makes it a
     stacking context that otherwise paints over this sticky header while
     the body scrolls under it (caught on the phone, 8/25). */
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 16px 10px;
  background: var(--card-raise);
}
.inspector-head .ih-label {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
}
.inspector-body { padding: 6px 18px calc(40px + env(safe-area-inset-bottom)); display: flex; flex-direction: column; gap: 18px; }

/* On the phone the inspector is a bottom sheet, not a squeezed side panel —
   the desktop panel left a sliver of dashboard showing and a bare white band
   above DETAILS (8/25). The sheet rises from the bottom, stops clear of the
   status bar, and sizes to its content up to nearly the whole screen. */
@media (max-width: 640px) {
  .inspector {
    top: auto;
    left: 0;
    right: 0;
    bottom: 0;
    width: 100%;
    max-width: none;
    height: auto;
    max-height: calc(100dvh - max(28px, env(safe-area-inset-top) + 12px));
    border-radius: var(--r-lg) var(--r-lg) 0 0;
    box-shadow: var(--shadow-lift);
    transform: translateY(100%);
  }
  .inspector-head { padding-top: 18px; border-bottom: 1px solid var(--hairline); }
  /* The grabber: the sheet's one piece of furniture. */
  .inspector-head::before {
    content: "";
    position: absolute;
    top: 7px;
    left: 50%;
    transform: translateX(-50%);
    width: 36px;
    height: 4px;
    border-radius: var(--r-pill);
    background: var(--card-sunk);
  }
}
.insp-title { font-size: 20px; line-height: 1.25; }
.insp-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.insp-meta { display: flex; flex-direction: column; gap: 3px; }
.insp-note { font-size: 14px; color: var(--ink-soft); line-height: 1.5; white-space: pre-wrap; margin: 0; }
.insp-actions { display: flex; flex-wrap: wrap; gap: 10px; }

/* tag picker (quick capture) */
.tagpick { display: flex; flex-wrap: wrap; gap: 8px; }
button.chip {
  border: 0;
  cursor: pointer;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
button.chip:hover { background: var(--card); color: var(--ink); }
.chip.is-picked { background: var(--evergreen); color: var(--evergreen-on); }
.chip.is-picked:hover { background: var(--evergreen-deep); color: var(--evergreen-on); }

/* ============================================================================
   RESPONSIVE — collapse sidebar to bottom bar under 900px
   ========================================================================== */
@media (max-width: 900px) {
  .app-shell { grid-template-columns: 1fr; }

  .sidebar {
    position: fixed;
    inset: auto 0 0 0;
    top: auto;
    height: auto;
    width: 100%;
    flex-direction: row;
    align-items: center;
    justify-content: space-around;
    gap: 2px;
    padding: 8px 8px calc(8px + env(safe-area-inset-bottom));
    background: color-mix(in srgb, var(--warm-white) 92%, transparent);
    backdrop-filter: saturate(1.1) blur(10px);
    -webkit-backdrop-filter: saturate(1.1) blur(10px);
    box-shadow: 0 -1px 20px rgba(35, 40, 31, 0.08);
    z-index: 60;
    overflow: visible;
  }
  /* On mobile the sidebar shows only primary items + a More button, as a tab bar.
     Opening More turns it into a bottom sheet: a grabber, a compact brand row
     with a close button, and a two-column grid of tiles grouped by section. */
  .sidebar.is-sheet {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px 10px;
    align-content: start;
    height: auto;
    max-height: 86dvh;
    inset: auto 0 0 0;
    border-radius: 22px 22px 0 0;
    overflow-y: auto;
    padding: 10px 16px calc(20px + env(safe-area-inset-bottom));
    animation: sheet-up var(--t) var(--ease) both;
  }
  @keyframes sheet-up { from { transform: translateY(30px); opacity: 0; } to { transform: none; opacity: 1; } }
  .sidebar.is-sheet::before {
    content: "";
    grid-column: 1 / -1;
    justify-self: center;
    width: 44px;
    height: 4px;
    border-radius: 999px;
    background: rgba(163, 158, 143, 0.45);
  }

  .brand { display: none; }
  /* Inside the sheet the brand goes back to being an ordinary row: the rail is
     a tab bar down here, there is nothing to scroll, and nothing to condense.
     The absolute placement that makes the desktop transition possible has to be
     unwound explicitly, or both children would sit on top of each other. */
  .sidebar.is-sheet .brand {
    grid-column: 1 / -1; position: static; height: auto; margin-bottom: 0;
    display: flex; flex-direction: row; align-items: center; gap: 12px;
    padding: 2px 2px 4px; text-align: left; pointer-events: auto;
  }
  .sidebar.is-sheet .brand::before { display: none; }
  .sidebar.is-sheet .brand .brand-mark {
    position: relative; top: auto; left: auto;
    width: 44px; height: 44px; max-width: none; border-radius: 12px;
  }
  .sidebar.is-sheet .brand .wordmark {
    position: static; transform: none; font-size: 21px;
  }
  .sidebar.is-sheet .sheet-close { display: inline-flex; margin-left: auto; }

  .nav-section-label, .nav-spacer, .nav-foot { display: none; }
  .sidebar.is-sheet .nav-section-label { display: block; grid-column: 1 / -1; padding: 10px 4px 0; }
  .sidebar.is-sheet .nav-foot { grid-column: 1 / -1; display: grid; grid-template-columns: 1fr 1fr; gap: 8px 10px; padding-top: 12px; }

  .nav-item {
    flex-direction: column;
    gap: 3px;
    padding: 6px 8px;
    font-size: 10px;
    font-weight: 600;
    border-radius: var(--r-sm);
    flex: 1 1 0;
    min-width: 0;
    text-align: center;
  }
  .nav-item.is-active::before { display: none; }
  .nav-item .nav-label { font-size: 10px; }
  .sidebar.is-sheet .nav-item {
    flex-direction: row;
    gap: 11px;
    padding: 13px;
    font-size: 15px;
    text-align: left;
    min-height: 48px;
    background: var(--card);
    border-radius: var(--r);
    box-shadow: var(--shadow-row);
  }
  .sidebar.is-sheet .nav-item .nav-label { font-size: 14.5px; }
  .sidebar.is-sheet .nav-item.is-active { background: var(--evergreen-tint); color: var(--evergreen); font-weight: 600; }
  .sidebar.is-sheet .nav-item.is-active .nav-icon { color: var(--evergreen); }

  /* items not in the primary set are hidden in bar mode, shown in sheet */
  .sidebar:not(.is-sheet) .nav-item[data-primary="0"] { display: none; }
  /* The bar is the rail turned on its side, but the rail is ordered by section
     and the bar is not — so each tab carries its own slot (--nav-order, set in
     app.js) and More is pinned last. Scoped to bar mode: in the sheet the rail's
     own grouped order is the right one. */
  .sidebar:not(.is-sheet) .nav-item { order: var(--nav-order, 50); }
  .sidebar:not(.is-sheet) .mobilebar { order: 90; }
  .nav-badge { position: absolute; top: 2px; right: calc(50% - 22px); transform: none; min-width: 16px; height: 16px; font-size: 10px; }
  .sidebar.is-sheet .nav-badge { position: static; }

  .mobilebar { display: flex; }
  .sidebar.is-sheet .mobilebar { display: none; }

  /* attention dot on More: Review (or future signals) waiting inside the sheet */
  .mobilebar .nav-dot { display: block; position: absolute; top: 3px; right: calc(50% - 18px); width: 8px; height: 8px; border-radius: 50%; background: var(--needle-coral); opacity: 0; transition: opacity var(--t-fast) var(--ease); }
  .mobilebar .nav-dot.is-on { opacity: 1; }

  .sidebar-scrim { display: block; position: fixed; inset: 0; z-index: 55; background: rgba(35,40,31,0.28); opacity: 0; pointer-events: none; transition: opacity var(--t) var(--ease); }
  .sidebar-scrim.is-open { opacity: 1; pointer-events: auto; }

  .view-scroll { padding-bottom: 140px; }
  .topbar { padding: 0 20px; }
  .inspector { width: 100%; }

  /* keep toasts clear of the bottom tab bar */
  .toast-stack { bottom: calc(90px + env(safe-area-inset-bottom)); }

  /* the assistant composer sticks above the tab bar, not under it */
  .chat-composer { bottom: calc(72px + env(safe-area-inset-bottom)); z-index: 5; }

  /* modals: use the small screen fully, keep footer reachable */
  .modal-scrim { padding: 4vh 12px calc(12px + env(safe-area-inset-bottom)); }
  .modal { max-height: calc(92dvh - 4vh); }
}

/* ============================================================================
   SMALL SCREENS — task rows stack so titles stay fully readable
   ========================================================================== */
@media (max-width: 480px) {
  /* the views carry their own dates; the topbar keeps title + actions only */
  .topbar-meta { display: none; }
}
@media (max-width: 640px) {
  .task-row { flex-wrap: wrap; row-gap: 6px; }
  .tr-body { flex: 1 1 0; min-width: 55%; }
  .tr-meta { margin-left: auto; flex-wrap: wrap; justify-content: flex-end; max-width: 100%; }
}

/* ============================================================================
   TOUCH — no hover on phones/tablets: reveal hover-gated controls, grow targets,
   and keep inputs at 16px so iOS Safari does not zoom on focus
   ========================================================================== */
@media (hover: none) {
  .tr-flag { opacity: 1; }
  .pj-row .pj-flag, .pj-row .pj-more { opacity: 1; }
  .ib-file { opacity: 1 !important; }
}
@media (pointer: coarse) {
  .iconbtn { width: 44px; height: 44px; }
  .tr-check { width: 24px; height: 24px; }
  .tr-flag { width: 30px; height: 30px; }
  .ac-remove { width: 28px; height: 28px; }
  input:not([type="checkbox"]):not([type="radio"]), select, textarea { font-size: 16px !important; }
}

/* ============================================================================
   REDUCED MOTION
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  .spinner { animation-duration: 0.7s !important; }
}

/* ============================================================================
   PRINT — clean page, no chrome
   ========================================================================== */
@media print {
  .sidebar, .topbar-actions, .inspector, .inspector-scrim, .toast-stack,
  .modal-scrim, .search-scrim, .spotlight-scrim, .spotlight-ring,
  .spotlight-card, .sidebar-scrim, .tr-flag, .tr-check { display: none !important; }
  .app-shell { grid-template-columns: 1fr; }
  .topbar { position: static; height: auto; padding: 0 0 12px; backdrop-filter: none; }
  .view-scroll { overflow: visible; padding: 0; }
  .view { max-width: none; }
  .task-row, .card, .hero { box-shadow: none !important; break-inside: avoid; }
  .hero { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
  body { background: #fff; }
}

/* ==== quick capture round 1 ==== */
.qc-hidden { display: none; }

/* inline new-project flow (folder-first picker) */

/* grouped tag picker */
.tagpicker { display: flex; flex-direction: column; gap: 12px; }
.tp-filters { display: flex; flex-wrap: wrap; gap: 7px; }
.tp-filter {
  border: 0;
  cursor: pointer;
  padding: 5px 12px;
  border-radius: var(--r-pill);
  background: var(--card-sunk);
  color: var(--ink-soft);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.tp-filter:hover { background: var(--card); color: var(--ink); }
.tp-filter.is-active { background: var(--evergreen); color: var(--evergreen-on); }
.tp-groups { display: flex; flex-direction: column; gap: 12px; }
.tp-group { display: flex; flex-direction: column; gap: 7px; }
.tp-group.qc-hidden { display: none; }
.tp-group-head {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}
.tp-chips { display: flex; flex-wrap: wrap; gap: 8px; }

/* note + attachment toggles */
.capture-extras { display: flex; flex-wrap: wrap; gap: 10px; }
.capture-note { min-height: 66px; }

/* attachment chips */
.attach-body { display: flex; flex-direction: column; gap: 8px; }
.attach-list { display: flex; flex-wrap: wrap; gap: 8px; }
.attach-chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  max-width: 100%;
  padding: 6px 8px 6px 12px;
  border-radius: var(--r-pill);
  background: var(--card-sunk);
  color: var(--ink);
  font-size: 13px;
  font-weight: 600;
}
.attach-chip .ac-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 220px; }
.attach-chip .ac-size { color: var(--muted); font-size: 11.5px; font-variant-numeric: tabular-nums; }
.attach-chip .ac-remove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  border: 0;
  cursor: pointer;
  border-radius: var(--r-pill);
  background: transparent;
  color: var(--ink-soft);
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.attach-chip .ac-remove:hover { background: var(--alert-bg); color: var(--alert-fg); }
.attach-msg { font-size: 12.5px; font-weight: 600; line-height: 1.45; color: var(--coral-deep); }

/* ---- when she may not be landing --------------------------------------------
   The honest-failure strip in Moneypenny's panel. On 2026-08-17 she talked to
   Ben senior for forty minutes while every one of her actions leaked out as
   text and nothing was written down, and the screen said nothing at all. This
   is what says it.

   It appears only when the server has SAID an action leaked —
   public/js/voice/health.js owns the rule, and the rule is deliberately hard
   to satisfy. That is what earns it this much coral: a warning he might see
   twice a year can afford to be the loudest thing in the panel, and one he
   would see weekly could not.

   It is an ordinary flex child between the head and the transcript, never an
   overlay, so it cannot cover the transcript or the orb — not in the docked
   panel, not in the full-screen column, and not in the bottom dock on a phone,
   where covering the microphone button would mean the warning had hidden the
   way out of the thing it is warning about.

   It lives in this file rather than in injectCss() because it is the app's own
   coral and the tokens are defined at the top of it — and because a stylesheet
   has no pair of backticks in it waiting to end the module. */
.mp-alert {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 10px 12px 11px 14px;
  background: var(--alert-bg);
  border-bottom: 1px solid rgba(178, 58, 46, 0.18);
  /* Arriving is half the message, so it arrives where he can see it happen. */
  animation: mp-alert-in 220ms var(--ease) 1;
}
@keyframes mp-alert-in {
  0%   { opacity: 0; transform: translateY(-5px); }
  100% { opacity: 1; transform: none; }
}
.mp-alert-body { flex: 1 1 auto; min-width: 0; }
.mp-alert-title {
  font-size: 12.5px;
  font-weight: 700;
  letter-spacing: 0.01em;
  color: var(--alert-fg);
}
.mp-alert-text {
  margin-top: 2px;
  font-size: 11.5px;
  line-height: 1.45;
  color: var(--coral-deep);
}
/* A word, not a second ✕. The sentence above it tells him to press the ✕ at the
   top of the panel to end the conversation; a ✕ of its own here, which only
   makes the message go away, is the misread this whole surface exists to stop. */
.mp-alert-x {
  flex: 0 0 auto;
  border: 0;
  cursor: pointer;
  padding: 3px 8px;
  border-radius: var(--r-pill);
  background: transparent;
  color: var(--coral-deep);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  transition: background var(--t-fast) var(--ease);
}
.mp-alert-x:hover { background: rgba(178, 58, 46, 0.12); }
.mp-alert-x:focus-visible { outline: 2px solid var(--alert-fg); outline-offset: 1px; }

/* Full screen on a phone leaves her a few centimetres at the foot of the screen
   and gives the brief the rest, so the strip hands back what it can spare
   without becoming something he has to squint at. */
@media (max-width: 720px) {
  body.lc-voice-stage .mp-alert { padding: 8px 10px 9px 12px; }
  body.lc-voice-stage .mp-alert-text { font-size: 11px; }
}

@media (prefers-reduced-motion: reduce) {
  .mp-alert { animation: none; }
  .mp-alert-x { transition: none; }
}

/* ============================================================================
   THE PROJECT PICKER (public/js/picker.js — shared with the EA pages)
   The picker ships its own rules with --pk-* tokens that default to the EA
   pages' palette; here they are re-pointed at True North. #app outranks the
   picker's own class-level token declarations, and the inspector lives under
   #app too, so the same rules hold in the panel and in the inbox.
   ========================================================================== */
#app .ea-pick, #app .ea-picker {
  --pk-bg: var(--card);
  --pk-card: var(--card-raise);
  --pk-ink: var(--ink);
  --pk-mut: var(--muted);
  --pk-line: rgba(35, 40, 31, 0.10);
  --pk-accent: var(--evergreen);
  --pk-accent-ink: var(--evergreen-on);
  --pk-green: var(--evergreen);
  --pk-amber: var(--coral-deep);
  --pk-radius: var(--r-sm);
  --pk-font: var(--font);
}
#app .ea-picker { box-shadow: var(--shadow-lift); font-weight: 500; }
#app .ea-picker-q { font-weight: 500; }
#app .ea-row.item .nm { font-weight: 500; }
#app .ea-row.item.sel .nm { font-weight: 700; }
#app .ea-row.grp.area { color: var(--ink-soft); }
#app .ea-row.create { color: var(--evergreen); }
/* inside a task row (inbox filing) the panel takes the row's full width */
#app .task-row + .ea-picker { margin: 4px 0 8px; max-width: none; }

/* the trigger: a select-shaped button that says the project and where it lives */
.pk-trigger { display: flex; align-items: center; gap: 8px; text-align: left; cursor: pointer; }
.pk-trigger .pk-val { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pk-trigger .pk-crumb { flex: 0 1 auto; max-width: 48%; color: var(--muted); font-size: 12px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pk-trigger.is-empty .pk-val { color: var(--muted); }
.pk-trigger[aria-expanded="true"] { box-shadow: inset 0 0 0 2px var(--evergreen); background: var(--card-raise); }
.ea-mount { display: flex; flex-direction: column; }

/* the finished-project question (reactivate.js) */
.react-ask { display: flex; flex-direction: column; gap: 10px; }
.react-ask-line { margin: 0; font-size: 15px; font-weight: 600; line-height: 1.45; color: var(--ink); }
.react-ask-detail { margin: 0; font-size: 13.5px; line-height: 1.5; color: var(--ink-soft); }
