/* ═══ 1. Fonts — self-hosted; the vendored sheet's @import was deleted for this. ═══
   Figtree ships from Google as a single variable face covering 400–700, not as three
   static weights, so it is one rule with a weight range rather than three rules. */
@font-face {
  font-family: 'Caprasimo'; font-style: normal; font-weight: 400; font-display: swap;
  src: url('/assets/fonts/caprasimo-400.woff2') format('woff2');
}
@font-face {
  font-family: 'Figtree'; font-style: normal; font-weight: 400 700; font-display: swap;
  src: url('/assets/fonts/figtree-var.woff2') format('woff2');
}

/* ═══ 2. Component retune — global, never per screen.
   The design system's defaults were tuned for a different density than the
   handoff's screens. The screenshots are the fidelity bar, so they win; the
   handoff's own rule is "do not restyle them per screen", so this happens once. ═══ */
.tag { font-size: 15px; padding: 6px 16px; }
.input { min-height: 46px; font-size: 16px; }
.card { background: var(--color-neutral-100); padding: var(--space-4); }
.nav { padding: var(--space-4) var(--space-6); }
/* margin-right: auto on the brand absorbs every pixel of free space before any flex item can
   grow, which is why the search field collapsed to its content width. The handoff wants the
   field to do the filling, so the brand's auto margin goes. The .nav prefix has to beat the
   sheet's own '.nav a' (0,1,1) font-size: 14px, which out-specifics a bare class — without it
   the 22px never rendered, and the wordmark drew at the links' 14px. */
.nav .nav-brand { font-size: 22px; margin-right: 0; }

/* Two variants the design system has no name for and the screens need. */
.btn-sm { padding: 6px 14px; font-size: 14px; }
.dialog-520 { width: min(520px, 100%); }
.dialog-560 { width: min(560px, 100%); }

/* The design system gives .dialog an author `display: flex`, and an author declaration beats the
   UA's `dialog:not([open]) { display: none }` at any specificity — so without this every dialog on
   the page renders inline, open or not. The same trap as an element shipping with `hidden` and a
   class that sets `display:`. */
dialog.dialog:not([open]) { display: none; }

/* The pill radius is for single-line controls; on a multi-line textarea it renders as a large oval. */
textarea.input { border-radius: var(--radius-md); }

/* ═══ 3. Monogram — a person's colour is a property of the person, not of their
   position in a list. Six slots by `id % 6`; every pair clears 4.5:1 for the
   13px/700 initials. Slot order interleaves the pale and deep tiers so that
   consecutive ids never land on the tightest pair. ═══ */
.monogram {
  display: inline-flex; align-items: center; justify-content: center;
  flex: none; border-radius: 50%; font-weight: 700; letter-spacing: .02em;
}
.mono-0 { background: var(--color-accent-200);   color: var(--color-accent-800); }
.mono-1 { background: var(--color-accent-2-400); color: var(--color-accent-2-900); }
.mono-2 { background: var(--color-neutral-300);  color: var(--color-neutral-800); }
.mono-3 { background: var(--color-accent-400);   color: var(--color-accent-900); }
.mono-4 { background: var(--color-accent-2-200); color: var(--color-accent-2-800); }
/* --color-text, not --color-neutral-900: on the heaviest fill in the set the ramp's own
   deep step gives 4.90:1, which passes but is the thinnest margin here. This gives 5.76:1. */
.mono-5 { background: var(--color-neutral-500);  color: var(--color-text); }

/* ═══ 4. Frame ═══ */
.app { display: flex; align-items: flex-start; }
.rail {
  width: 236px; flex: none; align-self: stretch;
  border-right: 1px solid var(--color-divider);
  padding: var(--space-6) var(--space-4);
  display: flex; flex-direction: column; gap: var(--space-2);
}
.rail-kicker {
  font-family: ui-monospace, monospace; font-size: 11px; text-transform: uppercase;
  letter-spacing: .08em; color: var(--color-neutral-700); padding-left: 12px;
}
.rail-row {
  display: flex; justify-content: space-between; gap: var(--space-2);
  padding: 8px 12px; border-radius: 999px;
  font-size: 15px; color: var(--color-neutral-800); text-decoration: none;
}
.rail-row:hover { background: var(--color-accent-100); }
.rail-row[aria-current='page'] {
  background: var(--color-accent-200); color: var(--color-accent-800); font-weight: 600;
}
.rail-divider { height: 1px; background: var(--color-divider); margin: var(--space-4) 12px; }
.rail-empty { padding: 8px 12px; font-size: 15px; color: var(--color-neutral-700); }
.rail-new { padding: 8px 12px; font-size: 15px; }
.rail-chips { display: flex; flex-wrap: wrap; gap: var(--space-2); padding: 0 12px; }
.rail-chips .tag { text-decoration: none; }
.rail-total { padding: var(--space-2) 12px 0; font-size: 15px; color: var(--color-neutral-700); }
.main { flex: 1; min-width: 0; padding: var(--space-6); }
/* The design system's search field is not itself a flex item — its form is — so the width has
   to be carried by the form and handed down. The field takes the free space up to 480px, and its
   auto right margin pushes On deck, Data, Add person and the account button to the right (14c). */
.nav-search-form { flex: 1 1 auto; min-width: 0; max-width: 480px; display: flex; margin: 0; margin-right: auto; }
.nav-search {
  flex: 1; min-height: 0; min-width: 0; text-overflow: ellipsis;
  background: var(--color-neutral-100); border: 1px solid var(--color-neutral-300);
  border-radius: 999px; padding: 9px 18px; font-size: 15px;
}
.nav-search:focus-visible { border-color: var(--color-accent); }
/* On deck and Data share one link style (14c). The .nav prefix beats the sheet's '.nav a' and
   '.nav a[aria-current=page]'. */
.nav .nav-link { font-size: 16px; color: var(--color-neutral-700); white-space: nowrap; display: inline-flex; align-items: center; gap: var(--space-2); }
.nav .nav-link:hover { color: var(--color-text); }
.nav .nav-link[aria-current='page'] { color: var(--color-text); font-weight: 600; }
/* The deck count: sage, because sage is the deck everywhere it appears. */
.nav-deck-count {
  min-width: 24px; height: 24px; padding: 0 7px; box-sizing: border-box; border-radius: 999px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--color-accent-2-200); color: var(--color-accent-2-900); font-size: 13px; font-weight: 700;
}
/* The account menu (14a/14c): a <details>, like the person page's ⋯ menu; app.js closes it on an
   outside click or Escape. */
.account-menu { position: relative; }
.account-button {
  list-style: none; cursor: pointer; width: 40px; height: 40px; border-radius: 999px;
  display: flex; align-items: center; justify-content: center;
  background: var(--color-accent-2-200); color: var(--color-accent-2-900);
  font-family: var(--font-heading); font-size: 18px; line-height: 1;
}
.account-button::-webkit-details-marker { display: none; }
.account-menu[open] > .account-button { background: var(--color-accent-2-700); color: var(--color-bg); }
.account-button:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }
.account-panel {
  position: absolute; right: 0; top: calc(100% + var(--space-2)); z-index: 30; width: 280px; box-sizing: border-box;
  background: var(--color-bg); border-radius: var(--radius-lg); box-shadow: var(--shadow-lg);
  padding: var(--space-3); display: flex; flex-direction: column; gap: var(--space-1);
}
.account-who { display: flex; flex-direction: column; gap: 2px; padding: var(--space-2) 12px; min-width: 0; }
.account-label { font-family: ui-monospace, monospace; font-size: 12px; letter-spacing: .08em; text-transform: uppercase; color: var(--color-neutral-700); }
.account-email { font-size: 15px; color: var(--color-text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.account-divider { height: 1px; background: var(--color-divider); margin: var(--space-1) 0; }
/* The Log out <form> carries no class in the design system, so it gets the UA default `form {
   margin-bottom: 1em }` with no top margin to match. In the nav that once shifted Log out ~7.5px
   above centre (the nav centres flex items on their margin box); in the menu it would open a gap
   under the row. */
.account-logout { margin: 0; }
.nav .account-row, .account-row {
  all: unset; box-sizing: border-box; display: flex; align-items: center; width: 100%; cursor: pointer;
  padding: 8px 12px; border-radius: 999px; font-family: var(--font-body); font-size: 15px; color: var(--color-text);
}
.nav .account-row:hover, .account-row:hover { background: var(--color-accent-200); color: var(--color-text); }
.nav .account-row[aria-current='page'] { font-weight: 600; }
.account-row:focus-visible { outline: 2px solid var(--color-accent); outline-offset: -2px; }
.rail-toggle { display: none; }

/* Below ~900px the rail collapses behind a disclosure. <details>/<summary>, so no script.
   The rail is the disclosure's SIBLING, not its child — a descendant selector here would
   parse cleanly and never collapse anything. */
@media (max-width: 900px) {
  .app { display: block; }
  .rail { width: auto; border-right: none; border-bottom: 1px solid var(--color-divider); }
  .rail-toggle { display: block; }
  .rail-toggle:not([open]) + .rail { display: none; }
  .rail-toggle > summary {
    list-style: none; cursor: pointer; padding: var(--space-3) var(--space-6);
    font-size: 15px; color: var(--color-neutral-800);
  }
  .rail-toggle > summary::-webkit-details-marker { display: none; }
}

/* ═══ /unfiled — screen 3c. Rows rather than cards, because the user is reading
   names, not evaluating people. ═══ */
.unfiled-head { display: flex; align-items: baseline; gap: var(--space-3); }
.unfiled-head h1 { font-size: 28px; margin: 0; }
.unfiled-head a { margin-left: auto; font-size: 15px; }
.unfiled-blurb { max-width: 640px; font-size: 16px; }
/* The pile's bands as links, each by its whole-pile count; the one on show is not a link. */
.unfiled-bands { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-4); margin: 0 0 var(--space-3); font-size: 15px; }
.unfiled-rows { display: flex; flex-direction: column; }
/* Band headings inside the pile: a kicker with the band's whole-pile count, then one line of what
   the band means. The seen-only foot holds the one button that acts on people nobody ticked. */
.unfiled-band-head { display: flex; align-items: baseline; gap: var(--space-3); margin: var(--space-4) 0 var(--space-2); }
.unfiled-band-head .muted { font-size: 13px; }
.unfiled-band-foot { margin: var(--space-3) 0 0; text-align: right; }
/* 56px tall, never wrapping: the date and thread count take what they need, and the name and
   address give way and clip. The first column is the checkbox cell, full row height. */
.unfiled-row {
  display: grid; grid-template-columns: 56px minmax(0,1.4fr) minmax(0,1.6fr) max-content max-content;
  column-gap: var(--space-4); align-items: center; min-height: 56px; padding: 0 var(--space-4) 0 0;
  border-bottom: 1px solid var(--color-divider); font-size: 16px;
}
.unfiled-row:has(:checked) { background: var(--color-accent-100); }

/* ═══ Selecting in data lists — handoff change 2026-10-02-list-selection, Turn 13 option 13a.
   Shared by /unfiled, /not-people, /dismissed and the browse grids; ui/listSelection.tsx renders
   the markup and app.js adds ranges, the cursor and its keys. ═══ */
/* The whole cell is the label, so a click anywhere in it toggles the row. user-select: none keeps
   a shift-click range from also painting a text selection across the rows between. */
.pick-cell {
  align-self: stretch; display: flex; align-items: center; justify-content: center;
  margin: 0; cursor: pointer; user-select: none; -webkit-user-select: none;
}
.pick-cell:hover { background: var(--color-accent-200); }
/* The themed box. The check is drawn in --color-bg (#f5ead8); a data URI cannot read a custom
   property, so the value is written out and must follow the token if it ever moves. */
.pick {
  appearance: none; -webkit-appearance: none; flex: none;
  width: 20px; height: 20px; margin: 0; border-radius: 6px;
  border: 2px solid var(--color-neutral-400); background: var(--color-bg) no-repeat center / 14px 14px;
  cursor: pointer;
}
.pick:checked {
  border-color: var(--color-accent); background-color: var(--color-accent);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14'%3E%3Cpath d='M2.5 7.5l3 3 6-7' fill='none' stroke='%23f5ead8' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}
.pick:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }
/* The keyboard cursor: a 3px edge inside the row's left side. */
[data-selection-row].is-cursor { box-shadow: inset 3px 0 0 var(--color-accent-700); }
/* Clipped cells and cells that keep their width; both stay on one line. */
.row-clip { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.row-fixed { white-space: nowrap; }
.kbd {
  display: inline-block; margin-left: 6px; padding: 0 5px; border: 1px solid currentColor; border-radius: 4px;
  font-family: ui-monospace, monospace; font-size: 12px; line-height: 16px; opacity: .75;
}
.shortcut-hint { display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-4); margin: var(--space-3) 0 0; font-size: 13px; color: var(--color-neutral-700); }
.shortcut-hint[hidden] { display: none; }
.shortcut-hint .kbd { margin: 0 4px 0 0; }
/* The undo toast after a dismiss or a not-a-person, created by app.js. */
.toast {
  position: fixed; left: 50%; bottom: var(--space-6); transform: translateX(-50%); z-index: 50;
  display: flex; align-items: center; gap: var(--space-4);
  background: var(--color-accent-800); color: var(--color-bg); border-radius: 999px;
  padding: 10px 20px; font-size: 16px; box-shadow: var(--shadow-md);
}
.toast button {
  all: unset; cursor: pointer; font-weight: 600; text-decoration: underline; color: inherit;
}
.toast button:focus-visible { outline: 2px solid var(--color-bg); outline-offset: 2px; }
/* Back at one end, Next at the other. The two slots always render, so space-between holds each
   link at its own edge whether or not the other exists — the empty slot keeps its end of the row
   rather than collapsing and letting the surviving link slide into the other's place. */
.pager { display: flex; justify-content: space-between; align-items: baseline; font-size: 16px; }
.selection-bar {
  display: flex; align-items: center; justify-content: space-between;
  background: var(--color-accent-200); border-radius: 999px;
  padding: 10px 20px; margin: 0 0 var(--space-4);
  font-size: 16px; color: var(--color-accent-800);
}
/* The bar ships with the `hidden` attribute so it is absent before anything is selected. An author
   rule beats the UA's `[hidden] { display: none }` whatever its specificity, so the base rule above
   would otherwise pin the bar open at "0 selected" — this restores the attribute's meaning.
   Revealed by CSS so the bar appears with or without script; app.js only fills the count.
   Scoped from the form rather than with a sibling combinator: the bar is rendered before the
   rows, and '~' only ever looks forwards. The :has() rule outranks [hidden], so the no-script
   path reveals the bar even though the attribute is still on the element. */
.selection-bar[hidden] { display: none; }
form:has(.unfiled-row input:checked) .selection-bar { display: flex; }

/* The presets are radios; :checked drives the selected look, so no script is involved. */
.presets { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.presets input[type='radio'] { position: absolute; opacity: 0; pointer-events: none; }
.presets label { cursor: pointer; border: 1px solid var(--color-neutral-300); background: none; }
.presets input[type='radio']:checked + label {
  background: var(--color-accent-200); color: var(--color-accent-800); font-weight: 700;
  border-color: var(--color-accent);
}
.presets input[type='radio']:focus-visible + label { outline: 2px solid var(--color-accent); outline-offset: 2px; }
.custom-weeks .input { width: 64px; }
.preset-help { font-size: 13px; color: var(--color-neutral-600); margin: var(--space-1) 0 0; }
.dialog-actions { display: flex; justify-content: flex-end; gap: var(--space-2); }

/* ═══ Person page — screen 3a. The identity row and the summary block. ═══ */
/* 3a spaces its main column with `gap: var(--space-6)`; our .main is a plain block, so the
   clearance the summary card needs has to be carried here. Only 3a and 4c use .identity-row,
   so this spaces those two screens and leaves every other page as it was. */
.identity-row { display: flex; align-items: flex-start; flex-wrap: wrap; gap: var(--space-6); margin-bottom: var(--space-6); }
/* The handoff's monogram is font-family: var(--font-heading) with no font-weight only at the
   large (56px/88px) avatar sizes — 36px monograms (Today's suggestion cards) stay in the shared
   .monogram rule's body face at weight 700, confirmed against every small monogram in the
   artifact. --font-heading (Caprasimo) ships in 400 only, so an explicit weight: 400 here
   overrides the shared rule's 700 rather than leaving a synthetic-bold display face. */
.monogram-88 { width: 88px; height: 88px; font-size: 32px; font-family: var(--font-heading); font-weight: 400; }
/* 4c's unfiled panel. The third monogram size, and it takes the large treatment: display face,
   weight 400 for the same reason as .monogram-88 above. */
.monogram-64 { width: 64px; height: 64px; font-size: 23px; font-family: var(--font-heading); font-weight: 400; }
/* 280px before the status panel beside it wraps below (handoff change 2026-09-26-person-header). */
.identity-text { flex: 1; min-width: 280px; }
.identity-name-row { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.identity-row h1 { font-family: var(--font-heading); font-weight: 400; font-size: 38px; margin: 0; line-height: 1; }
/* The name, renameable in place (issue #13). Since 11a the name itself is the button: a tint on
   hover says so, and there is no separate link. Inside 4c's .identity-name-row the form nests,
   leaving the unfiled tag where it was. */
.name-row { display: flex; align-items: baseline; gap: var(--space-3); min-width: 0; }
.name-row h1 { min-width: 0; overflow-wrap: anywhere; }
.name-button { all: unset; cursor: text; border-radius: 8px; }
.name-button:hover { background: var(--color-neutral-200); }
.name-button:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }
.name-editing { flex-wrap: wrap; }
/* Element + class, for the same specificity tie as textarea.hyk-field below: the field carries the
   name at something near the size it is read at, so the head does not lurch between the states. */
input.name-field { flex: 1 1 14ch; min-width: 0; font-family: var(--font-heading); font-size: 24px; }
.identity-role { font-size: 18px; color: var(--color-neutral-800); margin: var(--space-2) 0 0; }
.identity-review { margin: var(--space-2) 0 0; font-size: 15px; }
.tag-row { margin: var(--space-3) 0 0; display: flex; gap: var(--space-2); flex-wrap: wrap; }

/* The chip row (handoff changes 2026-09-13-group-add-chip and 2026-09-26-person-header): groups,
   + group, tags, + tag, in one wrapping row under the role line. No rule between them: groups are
   filled and tags outlined, which is the difference. position: relative so the picker can drop
   below the whole row rather than below one chip. */
.chip-row { margin: var(--space-3) 0 0; display: flex; gap: var(--space-2); flex-wrap: wrap; align-items: center; position: relative; }
/* The × sits inside the pill, not beside it where it read as a separator. It stays a submit button
   in its own form, so removing works with no script. Shown on hover or focus at desktop widths
   (below); the phone block shows it always. */
/* Groups with + group, tags with + tag. Each cluster is one item of the row at its full width, so a
   row too narrow for both breaks between them first; a cluster wider than the row on its own wraps
   inside itself. Not positioned, so the group picker still drops below the whole row. */
.chip-cluster { display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: center; min-width: 0; }
.tag-chip { display: inline-flex; align-items: center; position: relative; }
/* Every chip, add and deck action here is a one-button <form>, and the UA's `form` margin-bottom
   of 1em made each chip-row line 51px tall for a 35px chip. */
.chip-row form, .status-panel form { margin: 0; }
.tag-remove { position: absolute; right: 4px; top: 50%; transform: translateY(-50%);
  background: none; border: 0; padding: 2px; cursor: pointer; color: inherit; opacity: 0; line-height: 0; }
@media (min-width: 641px) {
  .tag-chip:hover .tag, .tag-chip:focus-within .tag { padding-right: 24px; }
  .tag-chip:hover .tag-remove, .tag-chip:focus-within .tag-remove { opacity: .7; }
}
.tag-remove:hover { opacity: 1; }
.tag-remove:focus-visible { opacity: 1; outline: 2px solid var(--color-accent); outline-offset: 1px; border-radius: 999px; }
.tag-add { display: inline-flex; }
/* + group and + tag are text, not chips: a dashed pill was taller than the chips beside it. They
   keep their elements — a <summary> and an input with a datalist. */
.chip-link { all: unset; cursor: pointer; font-size: 14px; color: var(--color-accent-700); padding: 4px 6px; border-radius: 999px; }
.chip-link:hover { background: var(--color-accent-100); color: var(--color-accent-800); }
.chip-link:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }
input.chip-link { width: 64px; cursor: text; }
input.chip-link::placeholder { color: var(--color-accent-700); opacity: 1; }
/* The dashed add chip, still used by On deck and search. */
.chip-add { border: 1px dashed var(--color-accent-400); background: none; cursor: pointer; color: var(--color-accent-700); font-family: inherit; font-size: 15px; padding: 6px 16px; }
.chip-add:hover { background: var(--color-accent-100); }
.chip-add:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }

/* The picker. A <details> that lays out as a chip: no marker, no block. */
.group-add-chip { display: inline-flex; align-items: center; }
.group-add-chip > summary { list-style: none; }
.group-add-chip > summary::-webkit-details-marker { display: none; }
/* With a script, an open picker swaps the summary for the chip input (app.js adds .is-open). */
.group-add-chip.is-open > summary { display: none; }
.group-add-chip.is-open > .chip-input { display: inline-flex; }
.chip-input { background: var(--color-accent-700); color: var(--color-bg); border: none; width: 120px; font-family: inherit; font-size: 15px; padding: 6px 16px; }
.chip-input:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }
.group-picker { position: absolute; top: 100%; left: 0; margin-top: var(--space-2); z-index: 20;
  width: min(300px, 100%); background: var(--color-bg); border: 1px solid var(--color-neutral-300);
  border-radius: var(--radius-md); box-shadow: var(--shadow-lg);
  padding: 6px; display: flex; flex-direction: column; gap: 2px; }
.group-picker-row { display: contents; }
.group-picker-option { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  padding: 8px 12px; border-radius: var(--radius-sm); font-size: 15px; color: var(--color-neutral-800);
  background: none; border: none; width: 100%; text-align: left; cursor: pointer; font-family: inherit; }
.group-picker-option:hover { background: var(--color-accent-100); }
.group-picker-option .count { font-size: 13px; color: var(--color-neutral-700); }
.group-picker-option[aria-selected='true'] { background: var(--color-accent-200); color: var(--color-accent-800); font-weight: 600; }
.group-picker-option[aria-selected='true'] .count { color: inherit; font-weight: 400; }
.group-picker-option[aria-disabled='true'] { color: var(--color-neutral-700); cursor: default; background: none; }
.group-picker-sep { border-top: 1px solid var(--color-divider); margin: 4px 8px; }
.group-picker-create { color: var(--color-accent-700); }
/* Without a script the create row is a text field and a button; with one the chip input drives it. */
.group-picker-create-form { display: flex; flex-direction: column; gap: 4px; }
.group-picker-create-input { font-size: 15px; padding: 6px 12px; }
.group-add-chip.is-open .group-picker-create-input { display: none; }

.tag-just-added { box-shadow: 0 0 0 2px var(--color-accent-2-800); }
.chip-undo { font-size: 14px; color: var(--color-neutral-700); padding-left: 4px; }
.chip-undo .linkish { font-size: inherit; color: inherit; }
a.tag { text-decoration: none; }
.summary { background: var(--color-neutral-100); border-radius: var(--radius-lg); padding: var(--space-6);
  box-shadow: var(--shadow-sm); display: flex; flex-direction: column; gap: var(--space-6); }
.summary-section + .summary-section { border-top: 1px solid var(--color-divider); padding-top: var(--space-6); }
.summary-columns { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-6); }
.kicker { font-family: ui-monospace, monospace; font-size: 11px; text-transform: uppercase;
  letter-spacing: .06em; color: var(--color-neutral-700); margin: 0 0 var(--space-2); }
.how-you-know { font-size: 19px; line-height: 1.5; margin: 0; text-wrap: pretty; }

/* How you know them, edited where it sits rather than behind a modal that covers the very facts —
   cadence, last contact, mutual context — you want in front of you while writing it. The field
   matches the sentence it replaces, so nothing moves between reading and writing. */
.hyk { display: flex; flex-direction: column; gap: 6px; }
/* The field wants marginally more air around it than the static sentence does. */
.hyk-editing, .hyk form { gap: 8px; }
.hyk-head { display: flex; align-items: baseline; gap: var(--space-3); }
.hyk .kicker { margin: 0; }
.hyk-edit { font-size: 14px; }
/* Empty, the placeholder is the affordance: it says what belongs here, which a bare field does not. */
.hyk-empty { display: block; }
/* Element + class ties styles.css's textarea.input (0,1,1); peopler.css loads second, so the tie
   goes here. A bare .hyk-field would lose its min-height and resize. Same trap as .lognote-body. */
textarea.hyk-field {
  min-height: 96px; font-size: 19px; line-height: 1.5;
  /* The field sits inside a card — a drag handle fights the layout. */
  resize: none;
}
/* One accented action. Save carries the accent; Cancel is a ghost, so the two do not compete over
   the same decision. The hint is pushed to the far end rather than crowding the buttons. */
.hyk-actions { display: flex; align-items: center; gap: var(--space-3); }
/* Scoped rather than changed on .btn-ghost everywhere: the dialogs' Cancel sits opposite a Save
   that is the only other thing in the box, where the accent costs nothing. Here it would be a
   second terracotta pull on the same decision, two words from the first. */
.hyk-actions .btn-ghost { color: var(--color-neutral-800); }
.hyk-hint { margin-left: auto; font-size: 14px; color: var(--color-neutral-700); }
.summary-lead { font-size: 19px; margin: 0; }
.summary-sub { font-size: 15px; color: var(--color-neutral-700); }

/* Screen 3a's dashed suggestion strip. It sits inside .summary-related, so the phone hides it with
   that section — screen 4a keeps the phone person page to the summary and the notes. */
.circle-strip {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-4);
  border: 1px dashed var(--color-neutral-400); border-radius: var(--radius-md);
  padding: 10px 16px; margin-top: var(--space-3);
}
.circle-strip p { margin: 0; font-size: 15px; }
.circle-strip-make { font-weight: 600; color: var(--color-accent-800); white-space: nowrap; }
.cadence-form { display: flex; gap: var(--space-2); align-items: center; margin: var(--space-3) 0 0; }
.select { border: 1px solid var(--color-neutral-400); border-radius: var(--radius-sm);
  padding: 6px 12px; font-size: 15px; background: var(--color-bg); }
.input-weeks { width: 6rem; }
.related { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.identifier-row { display: flex; flex-wrap: wrap; gap: var(--space-3); margin: 0; font-size: 15px; }
.rule-heavy { border: none; border-top: 2px solid var(--color-text); margin: var(--space-6) 0; }

.note { background: var(--color-accent-2-200); border-radius: var(--radius-md);
  padding: var(--space-4) var(--space-6); margin: 0 0 var(--space-4); }
.note-head { display: flex; gap: var(--space-3); align-items: baseline;
  font-size: 14px; color: var(--color-accent-2-900); margin: 0 0 var(--space-2); }
.note-pinned { font-weight: 700; text-transform: uppercase; letter-spacing: .06em; font-size: 12px; }
.note-actions { margin-left: auto; display: flex; gap: var(--space-3); align-items: baseline; }
.note-body { font-size: 17px; line-height: 1.5; margin: 0; }
/* A submit button that has to read as a link, because pinning is a POST and must not be a GET. */
.linkish { background: none; border: none; padding: 0; font: inherit; color: inherit;
  text-decoration: underline; cursor: pointer; }

.bars { display: flex; align-items: flex-end; justify-content: flex-start; gap: 6px; height: 78px; margin: var(--space-4) 0 var(--space-2); }
/* The 24px comes from `width`, never from a definite flex-basis. With `flex: 0 1 24px` WebKit
   ignores the row's `align-items: flex-end` and floats every bar 26.4px above the container
   floor — into the h2's line, where the bars paint over the subtitle, since a later sibling's
   background paints after an earlier sibling's text. Blink lays it out correctly, which is how
   it reached production. `flex-basis: auto` resolves to this width and still shrinks, so a long
   history compresses rather than overflowing. Measured cold-loading this page in both engines. */
.bar { flex: 0 1 auto; width: 24px; max-width: 24px; background: var(--color-neutral-400); border-radius: 4px 4px 0 0; }
.bar-recent { background: var(--color-accent-400); }
/* Screen 3a has this subtitle in the body face. It is nested in the <h2>, so without a rule of
   its own it inherits --font-heading and renders as a slab in the middle of the heading. */
.over-time h2 .summary-sub { font-family: var(--font-body); }
.recent-grid { display: grid; grid-template-columns: 110px 96px minmax(0, 1fr);
  gap: var(--space-2) var(--space-4); font-size: 15px; align-items: baseline; }

/* Screen 3a ends the person page with this: a rule across the column, then one quiet
   right-aligned link. .linkish underlines by default — it has to read as a link inside a note's
   action row — but here the design leaves it plain until hover, so it never competes with the
   accented "See all" just above it. */
.not-a-person-foot {
  border-top: 1px solid var(--color-divider);
  margin: var(--space-6) 0 0; padding: var(--space-4) 0 0; text-align: right;
}
.not-a-person-foot .linkish { font-size: 15px; color: var(--color-neutral-700); text-decoration: none; }
.not-a-person-foot .linkish:hover { text-decoration: underline; }

/* Unfiling joins the foot on the same argument: rare, close to destructive, and no business
   sitting above someone's name. It carries the rule of its own only when it stands alone — a
   record marked not-a-person renders no foot link above it. */
.unfile-foot {
  border-top: 1px solid var(--color-divider);
  margin: var(--space-6) 0 0; padding: var(--space-4) 0 0; text-align: right;
}
.not-a-person-foot + .unfile-foot { border-top: none; margin-top: var(--space-1); padding-top: 0; }
.unfile-foot .linkish { font-size: 15px; color: var(--color-neutral-700); text-decoration: none; }
.unfile-foot .linkish:hover { text-decoration: underline; }

/* Dismiss sits between the two: same treatment, and it drops its own rule when it follows one. */
.dismiss-foot { border-top: 1px solid var(--color-divider); margin: var(--space-6) 0 0; padding: var(--space-4) 0 0; text-align: right; }
.not-a-person-foot + .dismiss-foot, .dismiss-foot + .unfile-foot { border-top: none; margin-top: var(--space-1); padding-top: 0; }
.dismiss-foot .linkish { font-size: 15px; color: var(--color-neutral-700); text-decoration: none; }
.dismiss-foot .linkish:hover { text-decoration: underline; }

/* ═══ Today — screen 2a. Three columns that do not scroll together; the suggestions
   column empties as it is worked and refills tomorrow. ═══ */
.today {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-6);
  padding: var(--space-6);
  align-content: start;
}
.today-col > h2 { font-family: var(--font-heading); font-size: 22px; margin: 0 0 var(--space-2); }
.today-sub { font-size: 14px; color: var(--color-neutral-700); margin: 0 0 var(--space-4); }

/* The one person card: Today's suggestions and every browse grid. `.sugg-card` below keeps
   Today's own action styling — a shadow and the gap between stacked cards — on top of this. */
.person-card {
  background: var(--color-neutral-100);
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-md);
  padding: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
/* flex-start, not center: the pick control is pinned to the name's first line, and centering would
   drift the monogram against a name that wraps to two. */
.person-card-head { display: flex; align-items: flex-start; gap: var(--space-3); }
/* Same trap as .rem-who: .person-card-name is a flex item of .person-card-head, beside a
   fixed-size avatar, and an unresolved display name renders as one unbreakable email token. */
.person-card-name { font-size: 17px; font-weight: 600; text-decoration: none; color: var(--color-text); min-width: 0; overflow-wrap: anywhere; }
.person-card-line { margin: 0; font-size: 14px; color: var(--color-neutral-700); }
/* .tag-neutral's background is the same token as .person-card's own, so a neutral tag on a card
   renders as floating text with no visible boundary. Scoped to .person-card so the person page's
   own (unaffected) tag styling is untouched - same outline treatment the "unfiled" chip already
   uses beside it. */
.person-card .tag-neutral { background: transparent; border: 1px solid var(--color-accent); color: var(--color-accent); }
/* Selection is client-side (a checkbox in the card's own actions), so the server has no
   "selected" state to render — this is the only thing that can see it. 2px in place of the
   1px default, so the card does not move when it is picked. */
.person-card:has([data-selection-item]:checked) { border: 2px solid var(--color-accent); padding: calc(var(--space-4) - 1px); }

/* Today's own layering on top of the shared .person-card shape above: a shadow and the gap
   between stacked cards. Nothing here duplicates .person-card's own rules. */
.sugg-card {
  box-shadow: var(--shadow-sm);
  margin-bottom: var(--space-4);
}
.sugg-note { min-height: 68px; }
.sugg-actions { display: flex; align-items: flex-start; gap: var(--space-3); }
.sugg-actions > form:first-child { flex: 1; display: flex; flex-direction: column; gap: 8px; }
.note-contacted { display: flex; align-items: center; gap: 8px; font-size: 14px; }

/* The card stacks on the width of its own column, not the viewport's. `.today` goes to three
   columns above 900px, so the column is (viewport - 342.6) / 3 — 227px at 1024, 286px at 1200,
   366px at 1440 — and the row above needs about 310px of column before `.not-now` and a usable
   note field stop fitting side by side. Below that the form shrank its own fields instead of
   overflowing (85px at 1024, placeholder clipped), which is why nothing measured it as a problem.
   See docs/drift/2026-09-20-the-suggestion-card-stacks-on-its-column.md for why this is a
   container query rather than a third viewport breakpoint — in short, the phone's column (358px at
   393) and the 1440 column (366px) are 8px apart, so no width threshold can tell them apart and
   the phone keeps its own rule below. */
.today-col { container-type: inline-size; }

@container (max-width: 320px) {
  .sugg-actions { flex-direction: column; }
  /* `align-self`, not `align-items: stretch` on the container: stretching every child would make
     "Not now" a full-width button. Only the form should fill the card. Without this the form is a
     flex item under `align-items: flex-start`, so it takes its content width and leaves the note
     field floating narrower than the card it sits in. */
  .sugg-actions > form:first-child { align-self: stretch; }
  /* Same reason as the 640px block below: once stacked, `.not-now` is a narrow button near the
     card's left edge, and `right: 0` anchors the 180px menu off the left of a 227px column. */
  .not-now-menu { right: auto; left: 0; }
}

.sugg-placeholder {
  border: 1px dashed var(--color-neutral-400);
  border-radius: var(--radius-md);
  padding: var(--space-4);
  margin-bottom: var(--space-4);
}
.sugg-undo { font-size: 14px; color: var(--color-neutral-700); margin-top: 4px; }
.sugg-empty {
  border: 1px dashed var(--color-neutral-400);
  border-radius: var(--radius-md);
  padding: var(--space-6);
  font-size: 16px;
}
/* The column's footer: one quiet line per person whose slot "No schedule" refilled today. */
.sugg-foot { display: flex; flex-direction: column; gap: var(--space-1); margin-top: var(--space-3); font-size: 13px; color: var(--color-neutral-600); }
.sugg-foot:empty { display: none; }

/* 4c's dashed block: the same treatment as Today's empty suggestions column above, because both
   say "there is a decision waiting here" rather than "something went wrong". */
.unfiled-panel {
  border: 1px dashed var(--color-neutral-400);
  border-radius: var(--radius-md);
  padding: var(--space-6);
  font-size: 16px;
  max-width: 620px;
  margin-bottom: var(--space-6);
}
/* On 4c the sentence has no .summary card to sit in, so it carries its own spacing, and takes the
   panel's measure so the two read as one column rather than a full-width line above a 620px box. */
.unfiled-hyk { max-width: 620px; margin: var(--space-6) 0; }
.unfiled-panel-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); margin-top: var(--space-4); }
.note-filing { margin-top: var(--space-4); }

/* The popover is a <details>, so it needs no script. It DOES need this: the design system gives
   several components an author `display:`, and an author declaration beats the UA's closed-state
   rule at any specificity — the trap that left every closed <dialog> rendered inline for two
   sub-projects. Never assume a closed state hides itself. */
.not-now { position: relative; }
.not-now > summary { list-style: none; cursor: pointer; }
.not-now > summary::-webkit-details-marker { display: none; }
details.not-now:not([open]) .not-now-menu { display: none; }
.not-now-menu {
  position: absolute; top: calc(100% + 4px); right: 0; z-index: 2;
  min-width: 180px; padding: 6px;
  background: var(--color-neutral-100);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
}
.not-now-item {
  display: block; width: 100%; text-align: left;
  padding: 9px 14px; border-radius: 999px; font-size: 15px;
  background: none; border: 0; cursor: pointer; color: var(--color-text);
}
.not-now-item:hover { background: var(--color-neutral-200); }

.rem-row { padding: var(--space-3) 0; border-bottom: 1px solid var(--color-divider); }
.rem-what { font-size: 16px; line-height: 1.45; margin: 0 0 4px; overflow-wrap: anywhere; }
.rem-foot { display: flex; align-items: baseline; gap: var(--space-3); font-size: 14px; color: var(--color-neutral-700); flex-wrap: wrap; }
/* A flex item's default min-width is auto, which refuses to shrink below its min-content width.
   A display name that never resolved renders as a raw email — one unbreakable token — so without
   `min-width: 0` that token forces this row wider than its grid track, pushing Done/Not-this into
   the next column. `overflow-wrap: anywhere` is the other half: once the item can shrink, the
   token itself has to be able to break, or it just overflows its own narrower box instead.
   `flex-wrap: wrap` on .rem-foot is the fallback if the attribution still needs more room than the
   row has — the controls drop to their own line rather than being crushed. */
.rem-who { margin-right: auto; min-width: 0; overflow-wrap: anywhere; }

.lately-note {
  background: var(--color-accent-2-200);
  border-radius: var(--radius-md);
  padding: var(--space-4);
  margin-bottom: var(--space-3);
}
/* The same monospace stack as .rail-kicker and .kicker. The handoff asks for a stack here, not a
   face — "a monospace stack (ui-monospace, monospace) ... only for small uppercase kickers at
   11-12px" — so there is no token to look up and none to add. */
.lately-kicker {
  font-family: ui-monospace, monospace; font-size: 11px; text-transform: uppercase;
  letter-spacing: .08em; color: var(--color-accent-2-900);
}
.lately-body { font-size: 16px; color: var(--color-accent-2-900); margin: 4px 0; }
/* Neither .lately-who nor .lately-name sits in a flex row, so an unbreakable display name cannot
   push a sibling out of position the way .rem-who's can — but plain inline text still overflows
   its own box by default, which is the same visible symptom (bleeding into the next column). */
.lately-who { font-size: 14px; color: var(--color-accent-2-900); overflow-wrap: anywhere; }
.lately-row { padding: var(--space-3) 0; border-bottom: 1px solid var(--color-divider); }
.lately-name { font-size: 16px; font-weight: 600; color: var(--color-text); text-decoration: none; overflow-wrap: anywhere; }
.lately-detail { font-size: 14px; color: var(--color-neutral-700); }

/* .linkish already has a rule above, in the "Person page — screen 3a" section (background: none;
   border: none; padding: 0; font: inherit; color: inherit; text-decoration: underline; cursor:
   pointer;) — reused as-is here for the Reminders row's Done/Not-this and the suggestion
   placeholder's undo, rather than duplicating the selector with a second, slightly different
   declaration. Confirmed the rendered colour is identical either way: both .rem-foot and
   .sugg-undo set --color-neutral-700 on the ancestor that .linkish's color: inherit picks up. */

@media (max-width: 900px) {
  .today { grid-template-columns: minmax(0, 1fr); }
}

/* Screen 4a's pinned "Log a note" footer. Hidden by default — the desktop rail already reaches
   every note-taking path, and a fixed-position bar has no business floating over the three-column
   grid. The media query below turns it back on for the phone. */
.phone-footer { display: none; }

/* The phone exists for adding notes, not for reading history: screen 4a drops the sparkline,
   the identifiers and the derived clusters rather than shrinking them. */
@media (max-width: 640px) {
  .identity-row { gap: var(--space-4); flex-wrap: wrap; }
  .monogram-88, .monogram-64 { width: 56px; height: 56px; font-size: 20px; }
  .identity-row h1 { font-size: 24px; }
  .identity-role { font-size: 15px; }
  /* `1fr` is `minmax(auto, 1fr)`, and that auto minimum floors the track at min-content — 354px
     here, inside a 287px box, which pushed the page to 407px and made iOS zoom the whole thing
     out. The desktop rule above already uses the safe spelling. */
  .summary-columns { grid-template-columns: minmax(0, 1fr); }
  /* The min-content the track was floored at: a nowrap row whose select is as wide as its longest
     option. Let it wrap, and let the select shrink rather than demand its widest option's width. */
  .cadence-form { flex-wrap: wrap; }
  .cadence-form .select { max-width: 100%; }

  /* Title, count and the sort link share a baseline row on the desktop. At 393px the link wraps
     into the count. Stack them: title, then count, then the link on its own full-width line. */
  .browse-head, .unfiled-head { flex-wrap: wrap; align-items: baseline; }
  .browse-head h1, .unfiled-head h1 { flex: 1 0 100%; }
  .browse-head a, .unfiled-head a { margin-left: 0; flex: 1 0 100%; min-height: 44px; display: inline-flex; align-items: center; }
  .unfiled-bands a, .unfiled-bands strong { min-height: 44px; display: inline-flex; align-items: center; }
  .over-time .bars, .over-time .recent-grid { display: none; }
  /* .related and .identifier-row sit inside a .summary-section carrying its own kicker and
     divider — hiding just the inner element leaves that kicker orphaned above empty space.
     Hide the whole section instead, via the modifier class SummaryBlock adds it. */
  .summary-related { display: none; }
  .summary-identifiers { display: none; }
  .note-actions a, .note-actions .linkish { min-height: 44px; display: inline-flex; align-items: center; }
  /* The rest of the phone's 44px touch targets: the how-you-know "edit" link, the identifiers
     "merge" link (its section is hidden above, but the rule stays general to every kicker
     link), and the cadence controls. Links get inline-flex to center their line-height inside
     the taller box; the select and button are already block-ish controls, so a plain
     min-height is enough. .hyk-edit is named separately because it sits beside the kicker in
     .hyk-head rather than inside it, so .kicker a no longer reaches it. */
  .kicker a, .hyk-edit { min-height: 44px; display: inline-flex; align-items: center; }
  /* Beside a 24px name at this width the field and its two buttons will not sit on one line, so
     the row wraps and the field takes the whole of the first one. */
  .name-row { gap: var(--space-2); }
  .name-button { min-height: 44px; display: inline-flex; align-items: center; }
  input.name-field { flex-basis: 100%; min-height: 44px; font-size: 19px; }
  .name-editing .btn-sm { min-height: 44px; }
  /* The empty state's placeholder link is a block of 19px text; a single unwrapped line of it is
     only ~28px tall, so the box needs the floor even though the text usually supplies it. */
  .hyk-empty { min-height: 44px; }
  .hyk-actions .btn { min-height: 44px; }
  .select { min-height: 44px; }
  .cadence-form .btn-sm { min-height: 44px; }

  /* The header is one row on the phone too, but the search field cannot share it: at 393px the
     other items leave it too little to show its own placeholder. So the field wraps onto its own
     line, full width, and the row above is brand · On deck · Data · Add person · (T) at full size. */
  .nav { flex-wrap: wrap; row-gap: var(--space-3); padding: var(--space-4); }
  .nav-search-form { order: 1; flex: 1 0 100%; max-width: none; margin: 0; }
  .nav .nav-brand { margin-right: auto; }
  .nav .nav-link { min-height: 44px; }
  .nav .nav-link { padding: 0 var(--space-2); }
  .account-button { width: 44px; height: 44px; }
  /* At 393px the five items need ~470px, so Add person and the button wrap to a second row, and
     the button lands at the left: a panel right-aligned to it would hang off the screen. Anchor
     the panel to the nav's left gutter instead; it opens just below the button's row. */
  .nav { position: relative; }
  .account-menu { position: static; }
  .account-panel { left: var(--space-4); right: auto; top: auto; margin-top: var(--space-2); }
  .nav .account-row { min-height: 44px; } /* .nav: the base rule's `all: unset` is (0,2,0) */

  /* Five columns need 312px of fixed track before the two flexible ones get anything, which at
     393px leaves the name ~11px and the address ~13px — their text then prints over the column
     beside them. The phone gets two columns: the control, and everything else stacked. */
  .unfiled-row {
    grid-template-columns: 28px minmax(0, 1fr);
    gap: var(--space-2) var(--space-4);
    align-items: start;
    padding: var(--space-3) 0;
  }
  .unfiled-row > * { grid-column: 2; }
  .unfiled-row > .pick-cell { grid-column: 1; grid-row: 1 / span 4; }
  .unfiled-row .pick { width: 24px; height: 24px; }
  /* The data lists are desktop-only: their keys mean nothing on a phone. */
  .kbd, .shortcut-hint { display: none; }

  /* Screen 4a. The phone is not a narrower desktop: it exists for adding notes and adding people.
     No rail, no Reminders column — and every touch target at least 44px. */
  .today-rem { display: none; }
  .today { padding: var(--space-4); padding-bottom: 96px; }
  .sugg-actions { flex-direction: column; }
  /* Stacked by design here rather than by fit, but with the same consequence: without this the
     form takes its content width under `align-items: flex-start` and the note field renders 197px
     wide inside a 321px card. Found by measuring, not by looking — issue #76. */
  .sugg-actions > form:first-child { align-self: stretch; }
  .sugg-actions .btn, .not-now > summary, .not-now-item { min-height: 44px; }
  /* With the column stacked above, .not-now is only as wide as the "Not now" button and sits
     near the card's left edge — right:0 (correct on desktop, where .not-now trails a wide flex:1
     form on the row) then anchors the menu's right edge to that narrow button and the 180px menu
     runs off the left of the viewport. Anchor from the left instead; the card is comfortably
     wider than 180px down to 320px, so nothing overflows. */
  .not-now-menu { right: auto; left: 0; }
  .phone-footer {
    display: block;
    position: fixed; left: 0; right: 0; bottom: 0;
    border-top: 1px solid var(--color-divider);
    background: var(--color-bg);
    padding: var(--space-4) var(--space-6);
  }
  /* A link, not a <button>: display: block would left-align its text, so flex-center it. */
  .phone-footer .btn { display: flex; justify-content: center; align-items: center; width: 100%; min-height: 52px; font-size: 17px; }
  .lognote { display: flex; flex-direction: column; gap: var(--space-4); padding: var(--space-4); }
  /* A bare class loses to the vendored sheet's textarea.input { min-height: 90px } — element +
     class, specificity (0,1,1), beats a class alone at (0,1,0), media query or not. Matching
     the element as well as the class ties the specificity; peopler.css loads after styles.css
     in the layout, so the tie then goes to this rule by source order. Same trap as the dialog
     display rule above — an author rule with equal-or-greater specificity, not the UA default,
     is what actually needs to be out-specified. */
  textarea.lognote-body { min-height: 150px; }
  .lognote-h { font-size: 17px; margin: 0; }
  .lognote-tags { display: flex; flex-wrap: wrap; gap: var(--space-2); }
  .lognote-tag {
    display: inline-flex; align-items: center; gap: 8px;
    padding: 8px 16px; border-radius: 999px; font-size: 15px; min-height: 44px;
    background: var(--color-neutral-200);
  }
  .lognote-happened { display: flex; align-items: center; gap: var(--space-3); }
  /* Save is the same kind of primary action at the end of a form as the pinned footer's Log a
     note button, so it gets the same phone treatment: full width, 52px, 17px — not just the
     44px floor. .btn's own min-height is 36px only implicitly (padding + line-height, no
     explicit min-height in the vendored sheet), so no specificity fight here — this simply
     wins as the more specific, later rule. */
  .lognote .btn-primary { display: block; width: 100%; min-height: 52px; font-size: 17px; }
}

/* ═══ Data — screen 4b, and the settings pages it opens. No screenshot exists for /google,
   /imports, /aliases or /jobs, so they borrow 4b's own vocabulary: neutral cards, dividered rows,
   heading-face band titles. The frame's .main already carries the page padding. ═══ */
.data-page { max-width: 900px; display: flex; flex-direction: column; gap: var(--space-8); }
.data-back { font-size: 15px; align-self: flex-start; }
.data-title { font-family: var(--font-heading); font-weight: 400; font-size: 44px; line-height: 1.05; margin: 0; }
/* The Data page's tabs (15a): plain pill links under the title. */
.data-head { display: flex; flex-direction: column; gap: var(--space-4); }
.data-tabs { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.data-tab {
  font-size: 16px; padding: 8px 18px; border-radius: 999px; color: var(--color-neutral-800); text-decoration: none;
}
.data-tab:hover { background: var(--color-accent-100); color: var(--color-neutral-800); text-decoration: none; }
.data-tab[aria-current='page'] { background: var(--color-accent-200); color: var(--color-accent-800); font-weight: 600; }
/* Users as cards (15a). One card per user so nothing truncates; status as tags; quiet actions. */
.user-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: var(--space-4); }
.user-card {
  background: var(--color-neutral-100); border-radius: var(--radius-lg); box-shadow: var(--shadow-sm);
  padding: var(--space-6); display: flex; flex-direction: column; gap: var(--space-4); min-width: 0;
}
.user-card > .tag { align-self: flex-start; }
.user-card .tag { font-size: 12px; }
/* .tag-neutral's background is the card's own token, so Admin would be bare text; one step darker
   keeps it a neutral chip, distinct from the outlined Disabled. */
.user-card .tag-neutral { background: var(--color-neutral-200); }
.user-card-title { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.user-card-name { font-size: 19px; font-weight: 600; overflow-wrap: anywhere; }
.user-card-you { font-size: 14px; color: var(--color-neutral-700); }
.user-card-email { font-size: 15px; color: var(--color-neutral-700); overflow-wrap: anywhere; margin-top: calc(-1 * var(--space-3)); }
.user-card-meta { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-6); margin: 0; }
.user-card-meta dt { font-family: ui-monospace, monospace; font-size: 12px; letter-spacing: .08em; text-transform: uppercase; color: var(--color-neutral-700); }
.user-card-meta dd { margin: 0; font-size: 15px; color: var(--color-neutral-800); }
.user-card-error { margin: 0; font-size: 14px; color: var(--color-accent-800); }
.user-card-actions { display: flex; flex-wrap: wrap; gap: var(--space-4); border-top: 1px solid var(--color-divider); padding-top: var(--space-3); margin-top: auto; }
.user-card-actions form { margin: 0; }
.user-action { all: unset; cursor: pointer; font-family: var(--font-body); font-size: 15px; color: var(--color-accent-800); }
.user-action-quiet { color: var(--color-neutral-700); }
.user-action:hover { text-decoration: underline; }
.user-action:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; border-radius: 4px; }
.user-add { background: var(--color-neutral-100); border-radius: var(--radius-lg); padding: var(--space-6); display: flex; flex-direction: column; gap: var(--space-4); }
.user-add-title { font-family: var(--font-heading); font-weight: 400; font-size: 22px; margin: 0; }
.user-add-row { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--space-4); margin: 0; }
.user-add-field { display: flex; flex-direction: column; gap: var(--space-1); min-width: 200px; }
.user-add-email { flex: 1.4; }
.user-add-name { flex: 1; }
.user-add-row .input { min-height: 44px; border-radius: 999px; }
.user-add-admin { display: inline-flex; align-items: center; gap: var(--space-2); min-height: 44px; }
.user-add-row .btn { flex: none; }
.user-add-help { margin: 0; font-size: 14px; color: var(--color-neutral-700); }
.data-lead { font-size: 16px; color: var(--color-neutral-800); max-width: 640px; margin: 0; }
.data-band { display: flex; flex-direction: column; gap: var(--space-4); }
.data-band-title { font-family: var(--font-heading); font-weight: 400; font-size: 28px; margin: 0; }
.data-card {
  background: var(--color-neutral-100); border-radius: var(--radius-md);
  padding: var(--space-4) var(--space-6); box-shadow: var(--shadow-sm);
}
/* Body face at 19px/600: the vendored sheet gives bare headings the display face, and a class
   (0,1,0) beats its element selector, so this holds without an element prefix. */
.data-card-title { font-family: var(--font-body); font-size: 19px; font-weight: 600; margin: 0; }
.data-list { display: flex; flex-direction: column; }
.data-list-row { padding: 14px 0; border-bottom: 1px solid var(--color-divider); font-size: 15px; }
.data-empty { font-size: 16px; color: var(--color-neutral-700); margin: 0; }
.data-hint { font-size: 14px; color: var(--color-neutral-700); }

.machinery { display: flex; flex-direction: column; }
.machinery-row {
  display: grid; grid-template-columns: minmax(0, 1fr) 170px 120px; gap: var(--space-4);
  align-items: center; padding: 14px 0; border-bottom: 1px solid var(--color-divider); font-size: 16px;
}
.machinery-name { font-weight: 600; }
.machinery-status { color: var(--color-neutral-800); }
.machinery-action form { margin: 0; }
/* Run now is a POST, so it is a button; .linkish strips it to text and this gives it the link
   colour. Later in the file than .linkish at equal specificity, so the colour holds. */
.machinery-link { font-size: 16px; color: var(--color-accent-800); text-decoration: none; }

.run-head { display: grid; grid-template-columns: minmax(0, 1fr) 260px 120px; gap: var(--space-4); align-items: baseline; }
.run-failed { font-size: 14px; color: var(--color-accent-800); }
.run-steps { margin: 6px 0 0; padding-left: var(--space-4); font-size: 14px; color: var(--color-neutral-700); }
.preview-text { white-space: pre-wrap; overflow-wrap: anywhere; font-size: 15px; line-height: 1.5; margin: var(--space-3) 0 0; }

.data-row, .data-deadline {
  display: flex; align-items: center; gap: var(--space-6);
  border-radius: var(--radius-md); padding: var(--space-4) var(--space-6);
}
.data-row { background: var(--color-neutral-100); box-shadow: var(--shadow-sm); }
.data-row-text { flex: 1; min-width: 0; }
.data-row-sentence, .data-card-line { font-size: 15px; color: var(--color-neutral-800); margin: 4px 0 0; }
.data-count { font-family: var(--font-heading); font-size: 26px; }
/* The empty span keeps a zero row's count aligned with the rows that have an Open. */
.data-open { min-width: 80px; text-align: center; }
.data-deadline { background: var(--color-accent-200); }
.data-deadline .data-card-title, .data-deadline .data-row-sentence { color: var(--color-accent-900); }
.data-deadline form { margin: 0; }
.data-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-4); }
.data-card-wide { grid-column: 1 / -1; }
.data-card-title a { color: inherit; text-decoration: none; }
.data-flash { background: var(--color-neutral-100); border-radius: var(--radius-md); padding: var(--space-3) var(--space-6); font-size: 16px; margin: 0; }
.data-flash-error { background: var(--color-accent-200); color: var(--color-accent-900); }
.temp-password code { font-size: 18px; letter-spacing: 0.04em; user-select: all; }
.data-inline-form { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); margin: 0; }
/* The legacy block's bare `label` rule adds a right margin and inline-block; a class beats it. */
.data-field { display: inline-flex; align-items: center; gap: var(--space-2); margin: 0; font-size: 16px; }
.data-number { width: 7rem; }
.data-foot { margin: 0; border-top: 1px solid var(--color-divider); padding-top: var(--space-4); display: flex; justify-content: flex-end; }
.data-upload { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); margin: var(--space-3) 0 0; }
.snapshot-row { display: grid; grid-template-columns: minmax(0, 1fr) 90px 130px 90px; gap: var(--space-4); align-items: baseline; }
.alias-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: var(--space-4); align-items: baseline; }

/* The whole row is the label, so the tap target is the row and not the 13px checkbox. The legacy
   block's bare `label` rule sets inline-block and a right margin; this class beats both. */
.data-pick {
  display: grid; grid-template-columns: 24px minmax(0, 1fr) 90px minmax(0, 1fr); gap: var(--space-4);
  align-items: center; margin: 0; padding: 14px 0; border-bottom: 1px solid var(--color-divider);
  font-size: 15px; cursor: pointer;
}
.data-pick:has(:checked) { background: var(--color-accent-100); }
.data-pick-main { display: flex; flex-direction: column; min-width: 0; overflow-wrap: anywhere; }

/* The decision queues — /review, /not-people and /merge (screen 5d). 5d's card is a label around a
   radio, so the card is the tap target and the selected border is CSS reading the radio, as the
   person card's is (docs/drift/2026-09-10-card-selection-is-css-only.md). */
.queue-head { display: flex; align-items: baseline; gap: var(--space-3); flex-wrap: wrap; }
.queue-head h1 { font-size: 28px; margin: 0; }
.queue-count { font-size: 16px; color: var(--color-neutral-700); }
.merge-panel { display: flex; flex-direction: column; gap: var(--space-4); margin: 0; }
.merge-lead { font-size: 16px; margin: 0; }
.merge-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: var(--space-4); }
/* margin 0 and display flex out-specify the legacy block's bare `label` rule, as .data-pick does. */
.merge-card {
  display: flex; flex-direction: column; gap: 2px; margin: 0;
  background: var(--color-neutral-100); border-radius: var(--radius-md); padding: var(--space-4);
  border: 2px solid transparent; cursor: pointer;
}
.merge-card:has(:checked) { border-color: var(--color-accent); }
.merge-card-head { display: flex; align-items: center; gap: var(--space-2); margin-bottom: var(--space-2); }
.merge-card-head input { accent-color: var(--color-accent); width: 16px; height: 16px; margin: 0; }
.merge-card-name { font-size: 17px; font-weight: 600; }
.merge-card-line { font-size: 15px; line-height: 1.5; overflow-wrap: anywhere; }
.merge-card-carries { font-size: 15px; line-height: 1.5; border-top: 1px solid var(--color-divider); padding-top: var(--space-2); margin-top: var(--space-2); }
/* One sentence per side; the checked side's shows. CSS cannot compare a radio's value to an
   attribute, so the match is by position, written out for six — the most sides a panel takes. */
.merge-outcome { font-size: 16px; margin: 0; display: none; }
.merge-grid:has(.merge-card:nth-child(1) :checked) ~ .merge-outcomes > .merge-outcome:nth-child(1),
.merge-grid:has(.merge-card:nth-child(2) :checked) ~ .merge-outcomes > .merge-outcome:nth-child(2),
.merge-grid:has(.merge-card:nth-child(3) :checked) ~ .merge-outcomes > .merge-outcome:nth-child(3),
.merge-grid:has(.merge-card:nth-child(4) :checked) ~ .merge-outcomes > .merge-outcome:nth-child(4),
.merge-grid:has(.merge-card:nth-child(5) :checked) ~ .merge-outcomes > .merge-outcome:nth-child(5),
.merge-grid:has(.merge-card:nth-child(6) :checked) ~ .merge-outcomes > .merge-outcome:nth-child(6) { display: block; }
.merge-actions,
.review-answers { display: flex; justify-content: flex-end; gap: var(--space-2); }
.review { display: flex; flex-direction: column; gap: var(--space-4); }
/* Body face at 19px/600, out-specifying the vendored sheet's bare h2. */
.review h2 { font-family: var(--font-body); font-size: 19px; font-weight: 600; margin: 0; }
.data-pick-short { grid-template-columns: 24px minmax(0, 1fr) 150px; }
/* A data-pick row with a checkbox cell is no longer one big label: the cell is the target, the
   cell runs the full row height, and the hint keeps its width rather than wrapping. */
.data-pick:has(> .pick-cell) {
  grid-template-columns: 56px minmax(0, 1fr) max-content; column-gap: var(--space-4);
  align-items: stretch; min-height: 56px; padding: 0 var(--space-4) 0 0; cursor: auto;
}
.data-pick:has(> .pick-cell) > .data-pick-main { justify-content: center; padding: 8px 0; }
.data-pick:has(> .pick-cell) > .data-hint { align-self: center; }
/* The .data-pick twin of the .unfiled-row rule: the bar shows without script once a row is ticked. */
form:has(.data-pick input:checked) .selection-bar { display: flex; }

/* The Data pages at phone width. Rows stack — text, then count and Open beneath — grids go to one
   column, and every tappable thing gets sub-project 1's 44px floor, here rather than deferred. */
@media (max-width: 640px) {
  .data-title { font-size: 32px; }
  .data-tab { min-height: 44px; display: inline-flex; align-items: center; box-sizing: border-box; }
  .user-action { min-height: 44px; display: inline-flex; align-items: center; }
  .data-row, .data-deadline { flex-wrap: wrap; gap: var(--space-3); padding: var(--space-4); }
  .data-row-text { flex-basis: 100%; }
  .data-count { margin-right: auto; }
  .data-grid { grid-template-columns: minmax(0, 1fr); }
  .machinery-row, .run-head, .snapshot-row { grid-template-columns: minmax(0, 1fr); gap: 4px; }
  .data-pick { grid-template-columns: 24px minmax(0, 1fr); }
  .data-pick > .data-hint { grid-column: 2; }
  .data-back, .data-open, .machinery-link, .data-card-line a, .data-card-title a,
  .data-pick-main a, .data-page .btn {
    min-height: 44px; min-width: 44px; display: inline-flex; align-items: center;
  }
  .data-pick { min-height: 44px; }
  .data-number, .data-upload input[type='file'] { min-height: 44px; }
  .merge-grid { grid-template-columns: minmax(0, 1fr); }
  .merge-card { min-height: 44px; }
  .merge-actions,
  .review-answers { flex-direction: column; align-items: stretch; }
  .merge-actions .btn { min-height: 44px; width: 100%; }
  .review-answers .btn { min-height: 44px; width: 100%; }
  .data-pick-short { grid-template-columns: 24px minmax(0, 1fr); min-height: 44px; }
  .data-pick-short > .data-hint { grid-column: 2; }
  .data-pick:has(> .pick-cell) { grid-template-columns: 56px minmax(0, 1fr); }
  .data-pick:has(> .pick-cell) > .pick-cell { grid-row: 1 / span 2; }
  .data-pick:has(> .pick-cell) > .data-hint { grid-column: 2; padding-bottom: 8px; }
  /* Back and Next are two short words; the 44px floor is a width as well as a height for them. */
  .pager a { min-height: 44px; min-width: 44px; display: inline-flex; align-items: center; }
}

/* ═══ Search (5a), and Turn 8's filters as chips. Rows are labels so the row is the tap target,
   as .data-pick is; the bar is revealed by :has() so it works without script. The switch is a
   link, because scope is a parameter of the address like everything else on this page. ═══ */
.search-page { max-width: 880px; gap: var(--space-4); }
.search-head { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.search-head h1 { font-size: 26px; margin: 0; }
.search-of { font-size: 15px; color: var(--color-neutral-700); }
.switch { margin-left: auto; display: inline-flex; align-items: center; gap: var(--space-3); font-size: 15px; color: var(--color-neutral-800); text-decoration: none; }
.switch-track { width: 44px; height: 26px; border-radius: 999px; background: var(--color-neutral-300); position: relative; transition: background .15s; }
.switch-knob { position: absolute; top: 3px; left: 3px; width: 20px; height: 20px; border-radius: 50%; background: var(--color-neutral-100); transition: left .15s; }
.switch[aria-checked='true'] .switch-track { background: var(--color-accent); }
.switch[aria-checked='true'] .switch-knob { left: 21px; }
.switch:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; border-radius: 999px; }
.result-count { margin: 0; font-size: 15px; }
.result-cols { display: grid; grid-template-columns: 28px 44px minmax(0, 1fr) 150px; gap: var(--space-4); padding-bottom: 6px; border-bottom: 1px solid var(--color-divider); }
.result-cols .kicker { grid-column: 4; margin: 0; }
.result-rows { display: flex; flex-direction: column; }
/* margin 0 and display grid out-specify the legacy block's bare `label` rule, as .data-pick does. */
.result-row {
  display: grid; grid-template-columns: 28px 44px minmax(0, 1fr) 150px; gap: var(--space-4);
  align-items: center; margin: 0; padding: 14px 0; border-bottom: 1px solid var(--color-divider); cursor: pointer;
}
.result-row:has(:checked) { background: var(--color-accent-100); }
.result-main { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.result-name { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-2); font-size: 17px; font-weight: 600; }
.result-name a { color: var(--color-text); text-decoration: none; }
.result-name a:hover { text-decoration: underline; }
.result-line { font-size: 15px; font-weight: 400; color: var(--color-neutral-800); }
.result-evidence { font-size: 15px; color: var(--color-neutral-800); }
.result-where { font-size: 13px; color: var(--color-neutral-600); }
.result-main .tag-row { margin: 0; }
.result-contact { font-size: 15px; color: var(--color-neutral-800); }
mark { background: var(--color-accent-200); color: var(--color-accent-900); padding: 0 1px; border-radius: 2px; }
/* The third such rule, after .unfiled-row and .data-pick. */
form:has(.result-row input:checked) .selection-bar { display: flex; }
.search-foot { border-top: 1px solid var(--color-divider); padding-top: var(--space-4); display: flex; flex-direction: column; gap: var(--space-2); }
.search-foot-line { margin: 0; font-size: 16px; color: var(--color-accent-800); }
.search-foot-line a { color: inherit; }
.search-notes > summary { list-style: none; cursor: pointer; }
.search-notes > summary::-webkit-details-marker { display: none; }
.search-note-list { list-style: none; margin: var(--space-3) 0 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); font-size: 15px; }
.search-loosen { margin: 0; font-size: 16px; }

/* Filters as chips. Terracotta is the accent family — filters; groups are sage, tags grey. The
   picker borrows the group picker's box and option rules and adds a second step inside it. */
.filter-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); position: relative; }
.chip-filter { display: inline-flex; align-items: center; gap: 6px; }
.chip-x { color: inherit; text-decoration: none; font-size: 17px; line-height: 1; padding: 0 2px; }
.filter-clear { font-size: 14px; color: var(--color-neutral-700); }
.filter-add-chip { display: inline-flex; align-items: center; }
.filter-add-chip > summary { list-style: none; }
.filter-add-chip > summary::-webkit-details-marker { display: none; }
.filter-add-chip.is-open > summary { display: none; }
.filter-add-chip.is-open > .chip-input { display: inline-flex; }
.chip-step { background: var(--color-accent-700); color: var(--color-bg); font-size: 15px; padding: 6px 4px 6px 16px; border-radius: 999px 0 0 999px; white-space: nowrap; }
.chip-step + .chip-input { border-radius: 0 999px 999px 0; padding-left: 6px; }
.filter-picker { width: min(340px, 100%); }
/* Chrome's newer <details> rendering keeps a closed panel's cached layout instead of zeroing it,
   so a probe's getBoundingClientRect still finds the picker rows at their open-state size. An
   explicit display: none on the closed panel itself, rather than relying on the element's own
   content model, keeps it truly absent. */
.filter-add-chip:not([open]) > .filter-picker { display: none; }
.filter-step { display: flex; flex-direction: column; }
.filter-step > summary { list-style: none; }
.filter-step > summary::-webkit-details-marker { display: none; }
.filter-presets { display: flex; flex-direction: column; gap: 2px; padding-left: 12px; }
a.filter-preset, a.filter-picker-row { text-decoration: none; }
.filter-exact { display: flex; flex-direction: column; }
.filter-exact > summary { list-style: none; }
.filter-exact > summary::-webkit-details-marker { display: none; }
.filter-exact-form, .filter-text-form { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); padding: 8px 12px; font-size: 14px; }
.filter-exact-form label { margin: 0; display: inline-flex; align-items: center; gap: 4px; }
.filter-exact-form .input, .filter-text-form .input { min-height: 36px; font-size: 14px; }
.group-choices { display: flex; flex-direction: column; gap: 2px; max-height: 40vh; overflow: auto; margin: 0 0 var(--space-3); }
.group-choice { display: flex; align-items: center; gap: var(--space-3); margin: 0; padding: 8px 12px; border-radius: var(--radius-sm); font-size: 15px; cursor: pointer; }
.group-choice:hover { background: var(--color-accent-100); }
.group-choice input { accent-color: var(--color-accent); margin: 0; }
.group-choice .count { margin-left: auto; font-size: 13px; color: var(--color-neutral-700); }

/* The search page at phone width: two columns, the date under the text, every target 44px. */
@media (max-width: 640px) {
  .search-head .switch { margin-left: 0; flex-basis: 100%; min-height: 44px; }
  .result-cols { display: none; }
  .result-row { grid-template-columns: 28px 44px minmax(0, 1fr); min-height: 44px; }
  .result-row input[type='checkbox'] { width: 20px; height: 20px; }
  .result-contact { grid-column: 3; font-size: 14px; color: var(--color-neutral-700); }
  .result-name a { min-height: 44px; display: inline-flex; align-items: center; }
  .chip-x { min-height: 44px; min-width: 44px; display: inline-flex; align-items: center; justify-content: center; }
  .filter-clear { min-height: 44px; display: inline-flex; align-items: center; }
  .filter-step > summary { min-height: 44px; }
  .filter-preset { min-height: 44px; }
  .filter-exact > summary { min-height: 44px; }
  a.filter-picker-row { min-height: 44px; }
  .filter-exact-form .btn, .filter-text-form .btn, .filter-exact-form .input, .filter-text-form .input { min-height: 44px; }
  .search-notes > summary { min-height: 44px; display: inline-flex; align-items: center; }
  .search-loosen a { min-height: 44px; display: inline-flex; align-items: center; }
  .search-foot-line a { min-height: 44px; display: inline-flex; align-items: center; }
  .group-choice { min-height: 44px; }
}

/* ═══ /people/new — screen 5c. A 560px panel on the page ground; label-over-input fields at the
   retuned 46px; the duplicate cards under a 2px rule, in the review queue's stacked shape. ═══ */
.add-panel {
  max-width: 560px; background: var(--color-bg); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md); padding: var(--space-8);
  display: flex; flex-direction: column; gap: var(--space-6);
}
.add-panel h1 { font-size: 32px; margin: 0; }
.add-form { display: flex; flex-direction: column; gap: var(--space-4); margin: 0; }
.add-panel .field { display: flex; flex-direction: column; gap: var(--space-2); margin: 0; }
.add-panel .field-label { font-size: 15px; color: var(--color-neutral-800); margin: 0; }
.add-panel .field textarea.input { min-height: 96px; resize: none; }
.add-panel .field-pair { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-4); }
.add-panel .field-error { font-size: 14px; color: var(--color-accent-800); }
.field .input[aria-invalid='true'] { border-color: var(--color-accent); }
.dup-block { border-top: 2px solid var(--color-text); padding-top: var(--space-6); display: flex; flex-direction: column; gap: var(--space-4); }
.dup-card {
  background: var(--color-neutral-100); border-radius: var(--radius-md); box-shadow: var(--shadow-sm);
  padding: var(--space-4) var(--space-6); margin: 0; display: flex; flex-direction: column; gap: var(--space-3);
}
.dup-name { font-family: var(--font-body); font-size: 19px; font-weight: 600; margin: 0; }
.dup-line { font-size: 16px; line-height: 1.5; margin: 0; }
.dup-actions, .dup-separate { display: flex; gap: var(--space-2); margin: 0; }
/* The header's fourth item. Its own class so the phone block can reach it without touching .btn. */
.nav .nav-add { white-space: nowrap; }

/* 5c on the phone: the panel fills the width, Company and Role stack, the actions keep their row,
   and every button clears the 44px floor. The header's button shrinks with the search field first
   (PR #47's rule); the probe says whether it still fits at 400px. */
@media (max-width: 640px) {
  .add-panel { max-width: none; padding: var(--space-6) var(--space-4); border-radius: 0; box-shadow: none; }
  .add-panel .field-pair { grid-template-columns: minmax(0, 1fr); }
  .add-panel .btn { min-height: 44px; }
  .dup-actions .btn { min-height: 44px; }
  .dup-separate .btn { min-height: 44px; }
  .nav-add { min-height: 44px; display: inline-flex; align-items: center; }
}

/* ═══ Login. One column, centred, at the width of a comfortable field — the only screen with no
   nav to sit under and nothing else on it. It rendered browser defaults until now: a 21px
   password field and a 29px button, on the first screen anyone meets on a phone. ═══ */
.login { display: flex; flex-direction: column; gap: var(--space-3); max-width: 360px; }
.login-label { font-size: 15px; color: var(--color-neutral-700); }
.login .btn { min-height: 48px; }

/* ═══ Legacy pages: element styling for screens not yet rebuilt.
   Delete each rule as its screen lands in sub-projects 2–6. ═══ */
fieldset { border: 1px solid var(--color-divider); border-radius: var(--radius-md); margin: 0 0 var(--space-4); }
label { display: inline-block; margin-right: var(--space-2); }
textarea { width: 100%; min-height: 4rem; }
table { border-collapse: collapse; width: 100%; font-size: 14px; }
th, td { text-align: left; padding: var(--space-2); border-bottom: 1px solid var(--color-divider); vertical-align: top; }
button { font: inherit; cursor: pointer; }
.muted { color: var(--color-neutral-700); }
.error { color: var(--color-accent-800); }
.inline { display: inline; }
.banner {
  background: var(--color-accent-100); border: 1px solid var(--color-accent-300);
  border-radius: var(--radius-md); padding: var(--space-3) var(--space-4);
}

/* ═══ Browse: the one grid behind clusters and cadence tiers. ═══ */
.browse-head { display: flex; align-items: baseline; gap: var(--space-3); margin-bottom: var(--space-4); }
.browse-head h1 { font-size: 28px; margin: 0; }
.browse-head .muted { font-size: 16px; }
.browse-head a { margin-left: auto; font-size: 15px; }
.browse-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-4); }
/* The control sits with the name, pinned right and aligned to the name's first line, so it lands
   in the same place on every card whatever the name wraps to — it used to trail the card's last
   element, which put it at a different height on each one, on the phone and the desktop alike. */
.person-card-pick {
  margin-left: auto; align-self: flex-start;
  display: inline-flex; align-items: center; justify-content: flex-end;
  min-width: 44px; min-height: 44px; margin-top: calc(var(--space-2) * -1); user-select: none; -webkit-user-select: none;
}
.person-card-pick input { width: 20px; height: 20px; margin: 0; accent-color: var(--color-accent); }
.tag-row-state { margin: var(--space-2) 0 0; }
.deck-card-chip { margin: var(--space-2) 0 0; }
.browse-desc { max-width: 640px; font-size: 16px; }

@media (max-width: 900px) { .browse-grid { grid-template-columns: minmax(0, 1fr); } }

.group-edit, .group-add { margin-bottom: var(--space-4); }
.group-edit form, .group-add form { display: flex; gap: var(--space-2); align-items: center; margin-top: var(--space-2); }
.group-add input[type='search'] { margin-top: var(--space-2); width: min(420px, 100%); }

.add-results { list-style: none; margin: var(--space-2) 0 0; padding: 0; }
.add-results li { padding: 6px 0; border-bottom: 1px solid var(--color-divider); }
.add-results form { display: flex; align-items: center; gap: var(--space-3); }

/* Screen 5b's suggestions panel. Tokens only — --color-accent-2-* already exist in the vendored
   sheet, so this introduces no new colour. */
.group-suggestions {
  background: var(--color-accent-2-200); border-radius: var(--radius-md);
  padding: var(--space-4) var(--space-6); margin-bottom: var(--space-4);
}
/* The vendored sheet styles bare h2, so this rule has to out-specify it rather than merely exist:
   .group-suggestions h2 is (0,1,1) against h2's (0,0,1), and peopler.css loads second. */
.group-suggestions h2 { font-size: 17px; font-weight: 600; color: var(--color-accent-2-900); margin: 0; }
.group-suggestions ul { list-style: none; margin: var(--space-3) 0 0; padding: 0; }
.group-suggestions li {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  padding: 10px 0; border-top: 1px solid var(--color-accent-2-300); font-size: 16px;
}
.group-suggestions a { text-decoration: none; color: var(--color-text); }
.group-suggestion-actions { display: flex; align-items: center; gap: var(--space-4); }
.group-suggestion-add { font-weight: 600; }

.cluster-promote { margin: 0 0 var(--space-4); font-size: 15px; }

/* Sub-project 1's spec says a phone layout ships with the screen that needs it; #35 shipped the
   browse view without one, leaving a 13px checkbox against a 44px floor. This pays that debt and
   takes nothing away: the rail collapses behind a disclosure at 900px rather than disappearing, so
   group and cluster pages are reachable here. */
@media (max-width: 640px) {
  .browse-head a { min-height: 44px; display: inline-flex; align-items: center; }
  /* "Select all" is in the count line under the selection bar, not in .browse-head — the rule above
     never reaches it, and Chrome measured it at 18px. Attribute selector because the anchor carries
     no class of its own. */
  [data-select-all] { min-height: 44px; display: inline-flex; align-items: center; }
  .person-card-name { min-height: 44px; display: inline-flex; align-items: center; }
  /* The label is the hit area; the box inside it grows from the UA's ~13px to something a thumb
     can find. Both are needed: a 44px label around a 13px box is still a 13px box to look at. */
  .person-card-pick { min-height: 44px; }
  .person-card-pick input[type='checkbox'] { width: 20px; height: 20px; }
  .selection-bar {
    flex-direction: column; align-items: stretch; gap: var(--space-2);
    border-radius: var(--radius-md); padding: var(--space-3) var(--space-4);
  }
  .selection-bar .btn { min-height: 44px; width: 100%; }
  .dialog-actions .btn { min-height: 44px; }
  .group-edit > summary, .group-add > summary { min-height: 44px; display: inline-flex; align-items: center; }
  .group-edit .btn, .group-add .btn, .add-results .btn { min-height: 44px; }
  .group-edit .input, .group-add .input { min-height: 44px; }
  .group-suggestions li { flex-direction: column; align-items: flex-start; }
  .group-suggestions .linkish { min-height: 44px; display: inline-flex; align-items: center; }
  /* Issue #41: the candidate's name link is the row's widest target and the one you tap to check
     a face, but it carries no class of its own and .linkish above only reaches Add/Dismiss.
     Measured at 18px. The li already goes column here, so a taller link costs no horizontal room. */
  .group-suggestions a { min-height: 44px; display: inline-flex; align-items: center; }
  .cluster-promote .btn { min-height: 44px; }
  /* The rail is behind the 900px disclosure here, not gone — its rows are phone targets too. */
  .rail-toggle > summary { min-height: 44px; display: flex; align-items: center; }
  .rail-row, .rail-new { min-height: 44px; display: flex; align-items: center; }
  .rail-chips .tag { min-height: 44px; display: inline-flex; align-items: center; }
  /* #35's chrome on the person page: the tag row renders on the phone (screen 4a shows it), and
     its remove button is a 12px icon today. */
  /* The × is always shown here — no hover on a phone — and static, beside the name it removes. */
  .tag-remove { position: static; transform: none; opacity: .7; min-height: 44px; min-width: 44px; display: inline-flex; align-items: center; justify-content: center; }
  .chip-link { min-height: 44px; min-width: 44px; display: inline-flex; align-items: center; justify-content: center; box-sizing: border-box; }
  .chip-add, .chip-input, .group-picker-option, .group-picker-create-input, .deck-on { min-height: 44px; }
  /* The group chips are links, and the rail's chips already got this. */
  .chip-row a.tag { min-height: 44px; display: inline-flex; align-items: center; }
  /* Issue #74: the cadence presets are radios you tap, and the label is the whole target. One rule
     for six call sites — /people/new, the person page's file and note dialogs and its how-you-know
     editor, and the filing dialogs on /unfiled, /search, /browse and /dismissed — because they all
     render the one CadencePresets. Measured at 180x37 before this. inline-flex rather than a bare
     min-height: the labels are inline, and an inline box ignores a height it is given. */
  .presets label.tag { min-height: 44px; display: inline-flex; align-items: center; }
}

/* Signature suggestions on the person page */
.signature-block { margin: 1rem 0; }
.signature-rows { list-style: none; margin: 0; padding: 0; }
.signature-row { padding: .5rem 0; border-top: 1px solid var(--color-divider); }
.signature-main { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.signature-fact { font-weight: 600; margin-right: auto; }

/* On deck (turn 9). Lifted from docs/reference/design-handoff/changes/2026-09-25-on-deck/reference.html. */
.deck-tray { background: var(--color-accent-2-100); border-radius: var(--radius-lg); padding: var(--space-4) var(--space-6); display: flex; flex-direction: column; gap: 12px; }
.deck-tray-head { display: flex; justify-content: space-between; align-items: baseline; gap: var(--space-4); flex-wrap: wrap; }
.deck-tray-head h2 { font-family: var(--font-heading); font-weight: 400; font-size: 24px; margin: 0; color: var(--color-accent-2-900); }
.deck-tray-sub { font-size: 14px; color: var(--color-accent-2-900); }
.deck-pills { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; list-style: none; margin: 0; padding: 0; }
.deck-pill { display: inline-flex; align-items: center; gap: 8px; background: var(--color-bg); border-radius: 999px; padding: 6px 14px 6px 10px; box-shadow: var(--shadow-sm); font-size: 15px; cursor: grab; }
.deck-pill:focus-within, .deck-pill:focus-visible { outline: 2px solid var(--color-accent-2-700); outline-offset: 2px; }
.deck-pill .grip { color: var(--color-neutral-500); font-size: 13px; }
.deck-pill .pos { font-family: ui-monospace, monospace; font-size: 12px; color: var(--color-accent-2-800); }
.deck-pill.is-waiting { background: none; box-shadow: none; border: 1px dashed var(--color-accent-2-400); padding: 5px 14px 5px 10px; color: var(--color-neutral-800); }
.deck-pill.is-dragging { opacity: .4; }
.deck-pill .move { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
.deck-pill:focus-within .move { position: static; width: auto; height: auto; clip: auto; background: none; border: none; font: inherit; color: var(--color-accent-2-800); cursor: pointer; padding: 0 2px; }
.deck-cut { display: inline-flex; align-items: center; gap: 6px; padding: 0 6px; }
.deck-cut::before { content: ''; width: 2px; height: 26px; background: var(--color-accent-2-500); border-radius: 2px; }
.deck-cut span { font-family: ui-monospace, monospace; font-size: 11px; letter-spacing: .06em; text-transform: uppercase; color: var(--color-accent-2-800); }
.deck-empty { font-size: 15px; color: var(--color-accent-2-900); }
.deck-lists { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-6); }
@media (max-width: 900px) { .deck-lists { grid-template-columns: minmax(0, 1fr); } }
.deck-list h3 { font-family: var(--font-heading); font-weight: 400; font-size: 22px; margin: 0; }
.deck-list-sub { font-size: 14px; color: var(--color-neutral-700); margin: 2px 0 8px; }
.deck-row { display: flex; align-items: center; gap: 10px; padding: 10px 0; border-bottom: 1px solid var(--color-divider); }
.deck-row-who { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.deck-row-name { font-size: 16px; font-weight: 600; display: flex; gap: 6px; align-items: center; color: var(--color-text); text-decoration: none; }
.deck-row-meta { font-size: 13px; color: var(--color-neutral-700); }
.deck-list-more { padding: 10px 0; font-size: 14px; }
.deck-on { background: var(--color-accent-2-700); color: var(--color-bg); border: none; cursor: pointer; font-family: inherit; }
.deck-on:hover { background: var(--color-accent-2-800); }
.deck-on:focus-visible { outline: 2px solid var(--color-accent-2-700); outline-offset: 2px; }
.deck-file-first { font-size: 13px; color: var(--color-neutral-700); }
.reminder-mark { display: inline-flex; color: var(--color-accent-700); }
.reminder-mark svg { width: 15px; height: 15px; }

/* The person page header's status panel (handoff change 2026-09-26-person-header): where they
   stand, when you last spoke, and Log a note. Neutral, and sage only on deck — sage means the
   deck. The ⋯ menu is a <details>: a plain toggle with no script. */
.status-panel { width: 260px; flex: none; box-sizing: border-box; border-radius: var(--radius-lg); padding: var(--space-4);
  display: flex; flex-direction: column; gap: 12px; background: var(--color-neutral-200); color: var(--color-neutral-900); }
.status-panel.is-on-deck { background: var(--color-accent-2-100); color: var(--color-accent-2-900); }
.status-head { display: flex; justify-content: space-between; align-items: center; gap: 8px; min-height: 40px; }
.status-title { font-family: var(--font-heading); font-size: 20px; }
.status-line { font-size: 14px; line-height: 1.5; }
.status-menu { position: relative; }
.status-menu summary { list-style: none; cursor: pointer; width: 40px; height: 40px; border-radius: 999px;
  display: flex; align-items: center; justify-content: center; color: inherit; }
.status-menu summary::-webkit-details-marker { display: none; }
.status-menu summary:hover { background: color-mix(in oklch, var(--color-accent-2-500) 18%, transparent); }
.status-menu summary:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }
.status-menu-list { position: absolute; right: 0; top: 100%; margin-top: 4px; z-index: 20; min-width: 160px;
  background: var(--color-bg); border: 1px solid var(--color-neutral-300); border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg); padding: 6px; display: flex; flex-direction: column; gap: 2px; }
.status-menu-list button { all: unset; cursor: pointer; padding: 8px 12px; border-radius: 10px; font-size: 15px; color: var(--color-text); }
.status-menu-list button:hover { background: var(--color-accent-100); }
.status-menu-list button:focus-visible { outline: 2px solid var(--color-accent); outline-offset: -2px; }
.status-menu-list button[disabled] { opacity: .45; cursor: default; background: none; }

.deck-page { display: flex; flex-direction: column; gap: var(--space-6); }
.deck-move, .deck-chip { display: contents; }
.deck-pill .move { background: none; border: none; }
.today-col-head { display: flex; justify-content: space-between; align-items: baseline; gap: var(--space-3); }
.today-deck-link { font-size: 14px; }

/* Today's second step lives inside .not-now's menu: each choice is its own <details>. */
.not-now-step > summary { list-style: none; }
.not-now-step > summary::-webkit-details-marker { display: none; }
details.not-now-step:not([open]) > form { display: none; }
.not-now-step > form { display: flex; flex-direction: column; gap: 2px; padding-left: var(--space-2); }
.not-now-back { padding: 8px 12px 4px; font-size: 13px; color: var(--color-neutral-700); background: none; border: none; text-align: left; cursor: pointer; font-family: inherit; }

/* The deck's phone targets. Its rules sit after the other phone blocks, so this block comes after
   them too, or the desktop rules above would win on source order. Links become inline-flex to
   centre their line inside the taller box; the buttons take a plain min-height. The tray's ↑/↓
   only show on :focus-within, and are a thumb target then. */
@media (max-width: 640px) {
  .not-now-back { min-height: 44px; }
  .today-deck-link, .deck-list-more, .group-deck-all .linkish { min-height: 44px; display: inline-flex; align-items: center; }
  .deck-pill:focus-within .move { min-height: 44px; min-width: 44px; }
  /* The status panel takes the whole line under the identity. Its Log a note stays: the pinned
     phone footer is Today's, not this page's. */
  .identity-text { min-width: 0; }
  .status-panel { width: 100%; }
  .status-panel .btn { min-height: 44px; }
  .status-panel .btn-primary { min-height: 52px; font-size: 17px; }
  .status-menu summary { width: 44px; height: 44px; }
  .status-menu-list button { min-height: 44px; box-sizing: border-box; display: flex; align-items: center; }
}
