/* Mobile-first. No external fonts or CDNs: everything here is local. */
:root {
  --bg: #f6f7f9;
  --surface: #ffffff;
  --ink: #16191d;
  --muted: #5c6673;
  --line: #d8dde4;
  --accent: #1f4e79;
  --accent-ink: #ffffff;
  --danger: #a3242c;
  --on-danger: #ffffff;
  --on-muted: #ffffff;
  /* For the one state that is neither a warning nor a problem. */
  --ok: #1b6b45;
  --radius: 10px;
  /* The iPhone's safe areas in the home-screen app (viewport-fit=cover):
     the home indicator, and the notch in landscape. There is no top one:
     the status bar is opaque ("black"), so the page starts below it. All
     are 0 in a browser tab, so every rule that adds them looks exactly as
     it did there. */
  --safe-right: env(safe-area-inset-right, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  --safe-left: env(safe-area-inset-left, 0px);
}
@media (prefers-color-scheme: dark) {
  :root {
    --bg: #14171a;
    --surface: #1d2126;
    --ink: #e8ebee;
    --muted: #9aa5b1;
    --line: #2e343b;
    --accent: #6fa8dc;
    --accent-ink: #10151a;
    --danger: #f08f95;
    /* The dark-mode danger and muted fills are pale, so they take dark
       text rather than white. */
    --on-danger: #14171a;
    --on-muted: #14171a;
    --ok: #74cea0;
  }
}
* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 16px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}
h1 { font-size: 1.35rem; margin: 0 0 .25rem; }
h2 { font-size: 1rem; margin: 1.5rem 0 .5rem; color: var(--muted);
     text-transform: uppercase; letter-spacing: .04em; }
.sub { color: var(--muted); margin: 0 0 1.25rem; }

/* Three columns: title left, week centred, logout right, all on one
   midline. The outer columns are equal so the week label sits in the true
   centre of the header rather than the centre of the leftover space. */
.bar {
  display: grid;
  /* The first column may shrink below its content so the title ellipsises
     instead of pushing the buttons off a narrow screen. The last stays
     1fr, because the buttons must never be squashed or clipped. */
  grid-template-columns: minmax(0, 1fr) auto 1fr;
  align-items: center;
  gap: .5rem;
  /* The same .5rem as the tab row below, so both rows are the same
     height and nothing shifts when moving between pages. */
  padding: .5rem 1rem;
  padding-right: calc(1rem + var(--safe-right));
  padding-left: calc(1rem + var(--safe-left));
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}
.bar > .bar-title { justify-self: start; }
.bar > .week-chip { justify-self: center; }
.bar > form { justify-self: end; display: flex; align-items: center; }
.bar-title {
  line-height: 1;
  white-space: nowrap;
  /* The buttons are the controls; the title is a label. When the row is
     too narrow for both, the title is what gives way. */
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  /* Stated rather than inherited from <strong>, so the workspace name on
     Settings is the same size, weight and colour as "WAR Notebook". */
  font-size: 1rem;
  font-weight: 700;
  color: var(--ink);
}

/* ONE button box for both toolbar rows: the header (Settings, Log out,
   Back to Capture) and the tab row below it (Capture, WAR, Reset WAR,
   Workspace, Your account, Accounts, Save settings). Every one of them
   carries .toolbar-btn, and nothing else is allowed to set their size --
   the two rows had drifted to 32px and 40px with different padding and
   font sizes, which is what this exists to stop.

   Colour is the only thing a role class may change; see the filled,
   outlined and danger variants below. The 44px tap target comes from an
   invisible overlay, so the painted button stays 40px and the row keeps
   its height. */
.toolbar-btn,
.toolbar-btn:hover,
.toolbar-btn:focus,
.toolbar-btn:active,
.toolbar-btn:visited {
  text-decoration: none;
}
.toolbar-btn {
  position: relative;
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: auto;
  height: 40px;
  /* The global button rule sets a top margin and a 44px min-height, and
     min-height beats height, so both are cleared here. */
  margin: 0;
  min-height: 0;
  /* One horizontal padding for every toolbar button on the page, set by
     width alone and never per row (audit L11). */
  padding: 0 var(--toolbar-btn-pad, 1rem);
  border: 1px solid transparent;
  border-radius: var(--radius);
  font-family: inherit;
  font-size: .8125rem;
  font-weight: 700;
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.toolbar-btn::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  height: 44px;
}

/* Filled: the header buttons and the primary action. */
.toolbar-btn--filled {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
}
.toolbar-btn--filled:active {
  background: color-mix(in srgb, var(--accent) 82%, #000);
}

/* Outlined: a destination you are not currently on. */
.toolbar-btn--outlined {
  background: transparent;
  border-color: var(--accent);
  color: var(--accent);
}

/* Danger: Reset WAR. */
.toolbar-btn--danger {
  background: var(--danger);
  border-color: var(--danger);
  color: var(--on-danger);
}

/* .header-btn and .tab keep their names for layout and for the selectors
   that already target them. Neither sets a size any more: .toolbar-btn is
   the only rule that does. */
.page {
  padding: 1.25rem calc(1rem + var(--safe-right)) calc(2rem + var(--safe-bottom)) calc(1rem + var(--safe-left));
  max-width: 42rem;
}

.auth {
  max-width: 22rem; margin: 0 auto;
  padding: 3rem calc(1.25rem + var(--safe-right)) calc(3rem + var(--safe-bottom)) calc(1.25rem + var(--safe-left));
}
.auth form { display: grid; gap: .35rem; margin-top: 1rem; }
/* Every labelled field keeps the same 8px gap to its control, so no
   label sits against a focus ring. */
label {
  display: block;
  margin-bottom: .5rem;
  font-size: .85rem;
  color: var(--muted);
}
input[type=text], input[type=password], input[type=search], .input-like {
  width: 100%; padding: .75rem; font-size: 1rem;
  /* Search boxes and button-backed fields look like every other text box,
     not like the browser's own pill-shaped search control. */
  -webkit-appearance: none; appearance: none; margin: 0; min-height: 0;
  /* Stated rather than left to the browser's form default, which is not
     the page font and differs between Safari and Chrome. A textarea can
     then be made to match a text box exactly. */
  font-family: inherit;
  background: var(--surface); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--radius);
}
input::placeholder, textarea::placeholder { color: var(--muted); opacity: 1; }
input:focus-visible, button:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}
button {
  margin-top: .75rem; padding: .8rem 1rem; font-size: 1rem; font-weight: 600;
  background: var(--accent); color: var(--accent-ink);
  border: 0; border-radius: var(--radius); cursor: pointer;
  min-height: 44px;
}
button.link {
  margin: 0; padding: 0; background: none; color: var(--accent);
  font-weight: 500; min-height: 0; text-decoration: underline;
}
.error {
  padding: .6rem .75rem; border-radius: var(--radius);
  background: color-mix(in srgb, var(--danger) 12%, transparent);
  color: var(--danger); border: 1px solid var(--danger); margin: 0;
}
.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.muted-note { color: var(--muted); font-size: .85rem; }
.empty { color: var(--muted); }
.week-chip {
  font-size: .8125rem; color: var(--muted); white-space: nowrap;
  font-variant-numeric: tabular-nums; line-height: 1;
}
/* Narrow phones get "11 - 17 Oct" so nothing can collide. The arrows
   cost about 60px, so the switch happens well before 400px. */
.week-short { display: none; }
@media (max-width: 520px) {
  .week-long { display: none; }
  .week-short { display: inline; }
}

/* --- capture ------------------------------------------------------------ */

.capture textarea {
  width: 100%; padding: .85rem; /* 16px minimum, or iOS zooms on focus */
  font: inherit; font-size: 16px; line-height: 1.45;
  background: var(--surface); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--radius);
  resize: vertical; min-height: 7.5rem;
}
#save-button.failed { background: var(--danger); }
/* htmx sets this while the save is in flight. */
#save-button[disabled] { opacity: .6; }

.field-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: .5rem;
  margin-top: .7rem;
}
/* Grid items default to min-width:auto, which lets a wide intrinsic control
   (iOS date inputs in particular) push past its column. */
.field-row > div { min-width: 0; }

/* One geometry for both controls, matching the text box: same border,
   radius, background and 16px type. iOS keeps its own height, padding and
   rounding on date inputs and selects unless appearance is reset. */
.field-row input,
.field-row select,
.archive-filters input,
.archive-filters select,
.archive-filters .input-like {
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  box-sizing: border-box;
  display: block;
  width: 100%;
  min-width: 0;
  height: 48px;
  margin: 0;
  padding: 0 .7rem;
  font-family: inherit;
  font-size: 16px; /* 16px minimum, or iOS zooms the page on focus */
  line-height: 46px; /* 48px less the two 1px borders, so text sits centred */
  background: var(--surface);
  background-image: none;
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.field-row input[type="date"] {
  text-align: left;
}
.field-row input[type="date"]::-webkit-date-and-time-value {
  text-align: left;
  margin: 0;
  padding: 0;
  line-height: 46px;
  min-height: 46px;
}
/* The native glyph is hidden because it cannot be restyled, but it is kept
   clickable and stretched across the field so a tap anywhere opens the
   picker even without JavaScript. app.js also calls showPicker(). */
.field-row input[type="date"]::-webkit-calendar-picker-indicator {
  position: absolute;
  inset: 0;
  width: auto;
  height: auto;
  margin: 0;
  padding: 0;
  opacity: 0;
  cursor: pointer;
}
.field-row input[type="date"]::-webkit-inner-spin-button,
.field-row input[type="date"]::-webkit-clear-button {
  display: none;
  -webkit-appearance: none;
}
.field-row select::-ms-expand { display: none; }

/* One select component, used everywhere. Every select in the app wears
   the native chevron suppressed and ours drawn instead, inset 16px with
   44px of room kept clear for it, so the glyph never crowds the edge. The
   Accounts page had no wrapper at all and fell back to the browser's own
   chevron, which sits hard against the border. */
select {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  padding: .75rem 2.75rem .75rem .75rem;
  font-family: inherit;
  font-size: 1rem;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  cursor: pointer;
}
select::-ms-expand { display: none; }

.select-wrap { position: relative; min-width: 0; }
/* The field grid sets its own height and padding for the row, which left
   the Status box with less clearance than its chevron needs. Only the
   right side is restated, so the row keeps its 48px height. */
.select-wrap select,
.field-row select,
.archive-filters select { padding-right: 2.75rem; }
.select-wrap::after,
.select-field::after {
  content: "";
  position: absolute;
  right: 1rem;
  top: 50%;
  width: .4rem;
  height: .4rem;
  margin-top: -.3rem;
  border-right: 2px solid var(--muted);
  border-bottom: 2px solid var(--muted);
  transform: rotate(45deg);
  pointer-events: none;
}
/* A select that is its own row rather than part of a field grid. */
.select-field { position: relative; display: block; }

/* While the unchosen placeholder is selected the select is :invalid, so
   it reads as a prompt rather than as a value. */
select[data-placeholder-first]:invalid,
select[data-placeholder-first]:has(option[value=""]:checked) { color: var(--muted); }
select[data-placeholder-first] option { color: var(--ink); }
select[data-placeholder-first] option[value=""] { color: var(--muted); }

/* The button beside such a select waits for the choice. */
button[data-needs-select][disabled] { opacity: .5; cursor: default; }

/* An empty date input renders blank on iOS and as a grey mm/dd/yyyy on
   desktop. Hide whatever the platform draws and show one prompt of our own.
   app.js toggles .filled. */
.date-wrap { position: relative; min-width: 0; }
.date-wrap input[type="date"] { padding-right: 2.1rem; cursor: pointer; }

/* The whole field is the way into our own picker. A transparent button
   over the input, because on iOS it is focus that summons the native
   wheel, and the only reliable way to stop that is for taps never to
   reach the input at all. The input keeps the value and the form keeps
   the input. */
.date-trigger {
  position: absolute;
  inset: 0;
  z-index: 2;
  margin: 0;
  padding: 0;
  min-height: 0;
  background: transparent;
  border: 0;
  border-radius: var(--radius);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.date-trigger:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
/* The popover hangs off the bottom edge of the field. */
.date-cal { position: absolute; top: 100%; left: 0; width: 100%; }
/* A calendar mark inside the right edge, drawn to the same weight and
   colour as the Status chevron. */
.date-wrap::after {
  content: "";
  position: absolute;
  right: .8rem;
  top: 50%;
  width: 13px;
  height: 12px;
  margin-top: -6px;
  border: 1.5px solid var(--muted);
  border-radius: 2px;
  box-shadow: inset 0 3px 0 0 var(--muted);
  pointer-events: none;
}
.date-wrap:not(.filled) input[type="date"] { color: transparent; }
.date-wrap:not(.filled) input[type="date"]:focus { color: var(--ink); }
.date-prompt {
  position: absolute;
  left: .72rem;
  top: 0;
  height: 48px;
  display: flex;
  align-items: center;
  font-size: 16px;
  color: var(--muted);
  pointer-events: none;
}
.date-wrap.filled .date-prompt { display: none; }
.date-wrap:focus-within .date-prompt { display: none; }

/* --- note list ---------------------------------------------------------- */

.notes { list-style: none; margin: 0; padding: 0; display: grid; gap: .75rem; }
.note {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: .75rem .85rem;
}
.note-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 1rem; margin-bottom: .35rem;
}
.note-meta { font-size: .8rem; color: var(--muted); }
.note-body { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; }
.note.editing textarea {
  width: 100%; padding: .6rem; font: inherit; font-size: 16px;
  background: var(--bg); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--radius);
}
.note.editing .field-row { margin-top: .6rem; }


.pill {
  display: inline-block; padding: .05rem .4rem; margin-left: .35rem;
  border-radius: 999px; font-size: .72rem; font-weight: 600;
  background: color-mix(in srgb, var(--accent) 16%, transparent);
  color: var(--accent);
}

/* --- tag chips ---------------------------------------------------------- */

.chips {
  list-style: none; margin: .5rem 0 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: .4rem;
}
.chip {
  display: inline-flex; align-items: center; gap: .1rem;
  max-width: 100%; min-width: 0;
  border: 1px solid var(--line); border-radius: 999px;
  background: var(--bg); font-size: .82rem; overflow: hidden;
}
.chip > span {
  padding: .25rem .2rem .25rem .6rem;
  min-width: 0; overflow-wrap: anywhere;
}
.chip-accept { max-width: 100%; overflow-wrap: anywhere; text-align: left; }
.chip.suggested { border-style: dashed; }
.chip-accept, .chip-x {
  margin: 0; min-height: 32px; padding: .25rem .6rem;
  background: none; color: var(--ink); font-size: .82rem; font-weight: 500;
  border: 0; border-radius: 0; cursor: pointer;
}
.chip-accept { color: var(--accent); font-weight: 600; }
.chip-x { color: var(--muted); padding-left: .35rem; }
.new-flag {
  font-size: .68rem; text-transform: uppercase; letter-spacing: .04em;
  color: var(--muted); font-weight: 600;
}
/* A left edge colour-codes the tag kind without relying on colour alone,
   since the kind is also readable from the tag name itself. */
.chip.kind-person  { border-left: 3px solid #7a5ea8; }
.chip.kind-org     { border-left: 3px solid #1f6f5c; }
.chip.kind-loe     { border-left: 3px solid var(--accent); }
.chip.kind-location{ border-left: 3px solid #8a6324; }

.suggestions { margin-top: .6rem; }

/* htmx request indicator: dim the element that is waiting. */
.htmx-request { opacity: .65; transition: opacity .1s linear; }

/* --- new tag prompt ----------------------------------------------------- */

.new-tags { margin-top: .6rem; display: grid; gap: .5rem; }
.new-tag-row {
  padding: .5rem .6rem; border-radius: var(--radius);
  background: color-mix(in srgb, var(--accent) 7%, transparent);
  border: 1px dashed var(--line);
}
.new-tag-row .chips { margin-top: .35rem; }

/* --- meeting invite intake ---------------------------------------------- */

.invite-actions {
  display: flex; flex-wrap: wrap; align-items: center; gap: .75rem;
  margin-top: .9rem;
}
#fill-from-invite {
  margin: 0; width: auto; padding: 0 .9rem; min-height: 44px;
  font-size: .9rem;
}
#fill-from-invite[hidden] { display: none; }
.file-button {
  display: inline-flex; align-items: center; min-height: 44px;
  padding: 0 .9rem; border-radius: var(--radius);
  border: 1px solid var(--line); background: var(--surface);
  color: var(--accent); font-size: .9rem; font-weight: 600; cursor: pointer;
}
.file-button input[type="file"] {
  position: absolute; width: 1px; height: 1px; opacity: 0;
  overflow: hidden; clip: rect(0 0 0 0);
}
.invite-notice {
  margin: .75rem 0 0; padding: .5rem .65rem; border-radius: var(--radius);
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  color: var(--ink); font-size: .85rem;
}

/* --- compact capture stack ---------------------------------------------- */

.capture textarea { min-height: 6rem; }
.capture #save-button { margin-top: .8rem; }
.page.capture { padding-top: .9rem; }
.capture h2 { margin: 1.1rem 0 .5rem; }

/* --- invite drop zone ---------------------------------------------------- */

.drop-zone {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 56px;
  margin-top: .8rem;
  padding: 0 .75rem;
  border: 1px dashed var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  text-align: center;
  cursor: pointer;
}
/* The label fills the box so a tap anywhere opens the picker. */
.drop-zone label {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  font-size: .875rem;
  line-height: 1.25;
  color: var(--muted);
  cursor: pointer;
}
/* Hidden completely: no native "Choose file" button anywhere. */
.drop-zone input[type="file"] {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  opacity: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
  -webkit-appearance: none;
  appearance: none;
}
.drop-zone input[type="file"]::-webkit-file-upload-button {
  -webkit-appearance: none;
  appearance: none;
  display: none;
}
.drop-zone.dragover {
  border-color: var(--accent);
  border-style: solid;
  background: color-mix(in srgb, var(--accent) 10%, var(--surface));
}
.drop-zone.dragover label { color: var(--accent); }
.drop-zone.has-file label { color: var(--ink); font-weight: 600; }
.drop-zone.busy label { opacity: .6; }

/* Busy state while a photo is being transcribed. */
.drop-status {
  position: absolute; inset: 0; margin: 0;
  display: flex; align-items: center; justify-content: center;
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  color: var(--accent); font-size: .875rem; font-weight: 600;
}
.drop-status[hidden] { display: none; }
.drop-zone.working { border-color: var(--accent); border-style: solid; }

/* Per-note "Add context" picker: the label is the control, input hidden. */
.note-actions { display: inline-flex; align-items: center; gap: .9rem; }
.note-actions .link { cursor: pointer; }
.context-input {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  opacity: 0; overflow: hidden; clip: rect(0 0 0 0); border: 0;
}

.drop-text[hidden] { display: none; }


/* --- shared button system ------------------------------------------------ */

.btn,
.btn:hover,
.btn:focus,
.btn:active,
.btn:visited { text-decoration: none; }

.btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin: 0;
  padding: .55rem 1.1rem;
  min-height: 0;
  background: var(--accent);
  border: 0;
  border-radius: var(--radius);
  font-family: inherit;
  font-size: .9rem;
  font-weight: 700;
  line-height: 1;
  color: var(--accent-ink);
  white-space: nowrap;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.btn-muted { background: var(--muted); color: var(--on-muted); }
.btn-danger { background: var(--danger); color: var(--on-danger); }
.btn:active { filter: brightness(.88); }

/* Card-header size: 28px painted, 44px touchable. */
.btn-sm {
  height: 28px;
  padding: 0 .6rem;
  font-size: .75rem;
}
.btn-sm::after {
  content: "";
  position: absolute;
  left: 0; right: 0; top: 50%;
  transform: translateY(-50%);
  height: 44px;
}

.note-head { align-items: center; }
.note-actions { display: inline-flex; align-items: center; gap: .4rem; }

/* Save and Cancel together; Delete pushed to the far edge. */
.edit-actions {
  display: flex; align-items: center; gap: .6rem;
  margin-top: .75rem;
}
.edit-actions .push-right { margin-left: auto; }

/* A note tagged with more than one GAMBLER thread: the offer to split it. */
.note-notice {
  display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
  margin-top: .6rem; padding: .5rem .65rem; border-radius: var(--radius);
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  font-size: .85rem;
}
.note-notice p { margin: 0; flex: 1 1 12rem; }
.split-part { margin-top: .75rem; }

/* Compact drop zone inside the edit form. */
.drop-zone-sm {
  height: 48px;
  margin-top: .7rem;
}
.drop-zone-sm label { font-size: .8rem; }

/* --- week navigation ----------------------------------------------------- */

.week-nav { display: inline-flex; align-items: center; gap: .3rem; min-width: 0; }
.week-arrow {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 28px;
  color: var(--accent); font-size: 1.15rem; line-height: 1;
  text-decoration: none; border-radius: var(--radius);
  -webkit-tap-highlight-color: transparent;
  position: relative;
}
.week-arrow::after {
  content: ""; position: absolute; left: -6px; right: -6px; top: 50%;
  transform: translateY(-50%); height: 44px;
}
.week-arrow:active { background: color-mix(in srgb, var(--accent) 14%, transparent); }

.week-banner {
  margin: 0 0 .8rem; padding: .55rem .7rem;
  border-radius: var(--radius); font-size: .85rem;
  background: color-mix(in srgb, var(--accent) 9%, transparent);
  color: var(--ink);
}
.week-banner a { color: var(--accent); font-weight: 600; }

/* --- save flash ---------------------------------------------------------- */

.flash {
  display: flex; align-items: center; gap: .75rem; flex-wrap: wrap;
  margin-bottom: .8rem; padding: .6rem .7rem;
  border-radius: var(--radius); font-size: .875rem;
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  color: var(--ink);
}
.flash-link { color: var(--accent); font-weight: 700; }
.flash-ask { display: block; }
.flash-ask p { margin: 0 0 .55rem; }
.flash-actions { display: flex; gap: .5rem; flex-wrap: wrap; }

/* --- tabs ---------------------------------------------------------------- */

.tabs {
  display: flex;
  align-items: center;
  gap: .4rem;
  /* Same horizontal padding as the header and the page, so the tabs line up
     with the content below and Reset lines up with Log out above. */
  padding: .5rem calc(1rem + var(--safe-right)) .5rem calc(1rem + var(--safe-left));
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}
/* Sizing comes from .toolbar-btn; this is the selected state only. */
.tab.is-active {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
}

/* --- WAR view ------------------------------------------------------------ */

/* The subtitle only renders for a final week, so the gap below the
   heading lives on the container rather than on it. */
.war-head { margin-bottom: 1rem; }
.war-head h1 { font-size: 1.15rem; margin: 0; }
.war-head .sub { margin: .15rem 0 0; }

/* A grid, so a button's width comes from its column and never from its
   label. "Copy for export" becoming "Copying..." used to resize the button
   and shove its neighbours along. */
.war-actions {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: .5rem;
  margin-bottom: 1rem;
}
@media (max-width: 429px) {
  .war-actions { grid-template-columns: repeat(2, 1fr); }
}
.war-busy { margin: 0 0 1rem; color: var(--accent); font-weight: 600; font-size: .875rem; }
.war-busy.htmx-indicator { display: none; }
.war-busy.htmx-request, .htmx-request .war-busy { display: inline-flex; }

.flags {
  margin: 0 0 1.1rem; padding: .7rem .85rem; border-radius: var(--radius);
  background: color-mix(in srgb, var(--danger) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--danger) 35%, transparent);
}
.flags h2 { margin: 0 0 .35rem; color: var(--danger); }
.flags ul { margin: 0; padding-left: 1.1rem; font-size: .9rem; }

.entries { list-style: none; margin: 0 0 1.2rem; padding: 0; display: grid; gap: .85rem; }
.entry {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: .85rem;
}
.entry-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: .75rem; flex-wrap: wrap;
}
.entry-head h3 { margin: 0; font-size: .95rem; line-height: 1.35; flex: 1 1 14rem; }
.entry-actions { display: inline-flex; gap: .35rem; flex-wrap: wrap; }
.entry-body { margin: .6rem 0; white-space: pre-wrap; }
.entry-sources { margin: .15rem 0 .6rem; font-size: .85rem; color: var(--muted); overflow-wrap: anywhere; }
.entry-sources a { color: var(--accent); }
.entry-poc, .entry-meta { margin: .15rem 0; font-size: .8rem; color: var(--muted); }
.objectives { margin: .15rem 0 .35rem; padding-left: 1.1rem; font-size: .8rem; color: var(--muted); }
.entry.editing label { display: block; margin-top: .6rem; }
.entry.editing input[type=text], .entry.editing textarea {
  width: 100%; padding: .6rem; font: inherit; font-size: 16px;
  background: var(--bg); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--radius);
}
.entry-toggles { display: flex; gap: 1.2rem; margin-top: .7rem; font-size: .9rem; }
.entry-toggles label { display: inline-flex; align-items: center; gap: .4rem; margin: 0; }

.loe-group { margin-bottom: 1rem; }
.loe-group h3 {
  margin: 0 0 .45rem; font-size: .8rem; text-transform: uppercase;
  letter-spacing: .04em; color: var(--muted);
}
.pill.carried { background: color-mix(in srgb, var(--danger) 16%, transparent); color: var(--danger); }

.synopsis { margin-bottom: 1.2rem; }
/* A week keeps every synopsis, newest first, each a card like an entry. */
.synopses-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: .75rem; flex-wrap: wrap;
}
.synopsis-list { list-style: none; margin: 0 0 1.2rem; padding: 0; display: grid; gap: .85rem; }
.synopsis-list > .synopsis {
  margin: 0; background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: .85rem;
}
.synopsis-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: .75rem; flex-wrap: wrap; margin-bottom: .6rem;
}
.synopsis-head .entry-meta { margin: 0; flex: 1 1 14rem; }
.synopsis.editing .edit-actions { margin-top: .9rem; }
.synopsis textarea, .synopsis input[type=text] {
  width: 100%; padding: .6rem; font: inherit; font-size: 16px;
  background: var(--surface); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--radius);
}
/* The hidden copy source keeps .visually-hidden's 1px box. The rule above
   is more specific and gave it the full width plus padding, which pushed
   the page 30px wider than the screen. */
.synopsis textarea.visually-hidden { width: 1px; height: 1px; padding: 0; border: 0; }
.copy-row { display: flex; gap: .5rem; align-items: center; margin-bottom: .6rem; }
.copy-row input { flex: 1 1 auto; }

/* Balance the week label: equal space from the tab bar above and the first
   row of buttons below. 16px on phones, 20px from tablet width up. */
.page.war { padding-top: 1rem; }
.war-head { margin-bottom: 1rem; }
@media (min-width: 768px) {
  .page.war { padding-top: 1.25rem; }
  .war-head { margin-bottom: 1.25rem; }
}

/* --- answerable draft questions ------------------------------------------ */

.flag-list { list-style: none; margin: 0 0 .85rem; padding: 0; display: grid; gap: .7rem; }
.flag {
  padding: .6rem .7rem; border-radius: var(--radius);
  background: var(--surface); border: 1px solid var(--line);
}
.flag-entry {
  display: inline-block; font-size: .72rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .03em;
  color: var(--accent); text-decoration: none;
}
.flag-entry-none { color: var(--muted); }
.flag-question { margin: .25rem 0 .45rem; font-size: .875rem; }
.flag textarea {
  width: 100%; padding: .5rem; font: inherit; font-size: 16px;
  background: var(--bg); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--radius);
}
/* So a jumped-to card is not hidden under the sticky-feeling header. */
.entry { scroll-margin-top: 1rem; }

/* Breathing room around "Apply answers": equal space above it and below it
   to the edge of the questions box. 16px on phones, 20px from tablet up.
   The margin goes on the button, made block-level for the purpose: .btn is
   inline-flex, and vertical margins on an inline box are ignored. */
.flag-list { margin-bottom: 0; }
.flags { padding-bottom: 1rem; }
.flags button[type="submit"] {
  display: flex;
  width: fit-content;
  margin-top: 1rem;
}
@media (min-width: 768px) {
  .flags { padding-bottom: 1.25rem; }
  .flags button[type="submit"] { margin-top: 1.25rem; }
}

/* --- shared "Working" indicator ------------------------------------------
   Used wherever the app is waiting on Claude: drafts, synopses, applying
   answers, and reading dropped files. CSS only, no images, no script. */

.working {
  display: inline-flex;
  align-items: center;
  color: var(--accent);
  font-weight: 600;
}
/* Inline flow for the label and the dots, so the dots can sit on the text
   baseline rather than being centred as flex items. */
.working-inner { display: inline-block; }
.working-dots {
  display: inline-block;
  /* An empty inline-block's baseline is its bottom edge, which is exactly
     where a typed full stop sits. */
  vertical-align: baseline;
  margin: 0 0 0 .2em;
  padding: 0;
}
.working-dots i {
  display: inline-block;
  vertical-align: baseline;
  width: .2em;
  height: .2em;
  /* Horizontal only: nothing here may move a dot up or down. */
  margin: 0 0 0 .14em;
  border-radius: 50%;
  background: currentColor;
  opacity: .25;
  animation: working-dot 1.2s ease-in-out infinite;
}
.working-dots i:first-child { margin-left: 0; }
.working-dots i:nth-child(2) { animation-delay: .2s; }
.working-dots i:nth-child(3) { animation-delay: .4s; }

@keyframes working-dot {
  0%, 70%, 100% { opacity: .25; }
  35% { opacity: 1; }
}

/* Reduced motion: no spin, no fade. A plain "Working..." instead. */
/* Reduced motion: no fading. A plain "Working..." instead. */
@media (prefers-reduced-motion: reduce) {
  .working-dots i { animation: none; opacity: 1; }
}

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

/* Right-aligned in the tab row, across from Capture and WAR. */
/* Pinned to the right of its row; the colour comes from the variant. */
.tab-reset { margin-left: auto; }
.tab-reset[disabled] { opacity: .5; cursor: not-allowed; }
.btn[disabled] { opacity: .5; cursor: not-allowed; }

/* --- stale draft markers --------------------------------------------------- */

.flash-stale {
  background: color-mix(in srgb, var(--danger) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--danger) 30%, transparent);
}
.entry-stale {
  margin: .4rem 0 0;
  font-size: .78rem;
  font-weight: 700;
  color: var(--danger);
}

/* --- label spacing --------------------------------------------------------
   Every labelled field gets the same 8px gap, so no label sits against a
   focus ring. */

.field-row > div > label,
.entry.editing label,
.synopsis label,
.flag label {
  display: block;
  margin-bottom: .5rem;
}
/* Checkbox labels are inline and must not inherit the gap. */
.entry-toggles label,
.drop-zone label,
.tab,
label.visually-hidden {
  margin-bottom: 0;
}
.entry.editing label { margin-top: .9rem; }
.synopsis label.sub { margin-top: .9rem; }
.synopsis label.sub:first-child { margin-top: 0; }

.date-hint {
  margin: .35rem 0 0;
  font-size: .75rem;
  font-weight: 600;
  color: var(--accent);
}
.date-hint[hidden] { display: none; }

/* Disabled actions keep their place but read as unavailable. */
.btn.is-disabled,
.btn[disabled] {
  background: var(--muted);
  border-color: var(--muted);
  color: var(--on-muted);
  opacity: .55;
  cursor: not-allowed;
  pointer-events: none;
}
.flag-apply { display: flex; align-items: center; gap: .75rem; flex-wrap: wrap; }
.flag-error { color: var(--danger); font-size: .85rem; font-weight: 600; }

/* --- buttons whose labels change ------------------------------------------
   Fixed box, centred text, ellipsis if a label ever outgrows it. Covers
   "Copying.../Copied", "Working...", and "Save failed, tap to retry". */

.war-actions .btn,
.flags button[type="submit"],
#save-button {
  display: block;
  box-sizing: border-box;
  width: 100%;
  min-width: 0;
  height: 44px;
  padding: 0 .7rem;
  line-height: 44px;
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Apply answers sits on its own, so it gets a fixed width rather than a
   grid column. */
.flags button[type="submit"] { width: 11rem; }
/* The synopsis copy buttons keep the small size but a steady width. */
.synopsis .btn-sm,
.copy-row .btn-sm {
  width: 8rem;
  padding: 0 .5rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  display: block;
  line-height: 28px;
  text-align: center;
}

.note-also {
  margin: .35rem 0 0;
  font-size: .75rem;
  color: var(--muted);
}

/* --- week picker and calendar popover ------------------------------------ */

.week-picker {
  display: inline-flex;
  align-items: center;
  gap: .3rem;
  margin: 0;
  padding: .2rem .4rem;
  min-height: 0;
  background: transparent;
  border: 0;
  border-radius: 6px;
  font: inherit;
  font-size: .8125rem;
  color: var(--muted);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.week-picker:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); }
.week-picker:active { background: color-mix(in srgb, var(--accent) 18%, transparent); }
.week-caret {
  width: .38rem; height: .38rem;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: translateY(-.1em) rotate(45deg);
}

.cal-backdrop { position: fixed; inset: 0; z-index: 40; }
.cal-popover {
  position: absolute;
  z-index: 41;
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  margin-top: .4rem;
  width: 19rem;
  padding: .7rem;
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 10px 30px rgba(0, 0, 0, .18);
}
#week-calendar { position: relative; }

.cal-head {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: .5rem;
}
.cal-title { font-weight: 700; font-size: .9rem; }
.cal-arrow {
  margin: 0; padding: 0; width: 32px; height: 32px; min-height: 0;
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent; border: 0; border-radius: 6px;
  color: var(--accent); font-size: 1.1rem; line-height: 1; cursor: pointer;
}
.cal-arrow:active { background: color-mix(in srgb, var(--accent) 14%, transparent); }

.cal-names {
  display: grid; grid-template-columns: repeat(7, 1fr);
  font-size: .68rem; color: var(--muted); text-align: center;
  margin-bottom: .2rem;
}

.cal-grid { display: grid; gap: 2px; }
.cal-row {
  position: relative;
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  border-radius: 6px;
}
/* The whole week lights up, because a week is the unit of work here. */
.cal-row.is-hover { background: color-mix(in srgb, var(--accent) 12%, transparent); }
.cal-row.is-viewing { background: var(--accent); }
.cal-row.is-viewing .cal-day { color: var(--accent-ink); }
.cal-row.is-final { background: color-mix(in srgb, var(--accent) 7%, transparent); }
.cal-row.is-final.is-viewing { background: var(--accent); }

.cal-day {
  position: relative;
  margin: 0; padding: 0; min-height: 0;
  height: 34px;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  background: transparent; border: 0; border-radius: 6px;
  color: var(--ink); font: inherit; font-size: .8rem; cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.cal-day.is-outside { color: var(--muted); opacity: .55; }
.cal-row.is-viewing .cal-day.is-outside { color: var(--accent-ink); opacity: .7; }
.cal-day.is-today .cal-num {
  font-weight: 700;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.cal-dot {
  position: absolute; bottom: 4px;
  width: 4px; height: 4px; border-radius: 50%;
  background: var(--accent);
}
.cal-row.is-viewing .cal-dot { background: var(--accent-ink); }
.cal-final {
  position: absolute; right: -.1rem; top: 50%; transform: translateY(-50%);
  font-size: .7rem; color: var(--accent);
}
.cal-row.is-viewing .cal-final { color: var(--accent-ink); }

.cal-this-week { margin-top: .6rem; width: 100%; height: 40px; }

/* On a phone it is a bottom sheet with bigger targets. */
@media (max-width: 520px) {
  .cal-backdrop { background: rgba(0, 0, 0, .35); }
  .cal-popover {
    position: fixed;
    top: auto;
    bottom: 0;
    left: 0;
    right: 0;
    width: auto;
    transform: none;
    margin: 0;
    padding: .9rem calc(1rem + var(--safe-right)) calc(1rem + var(--safe-bottom)) calc(1rem + var(--safe-left));
    border-radius: var(--radius) var(--radius) 0 0;
    border-bottom: 0;
  }
  .cal-day { height: 44px; font-size: .9rem; }
  .cal-arrow { width: 44px; height: 44px; }
  .cal-this-week { height: 44px; }
}

/* --- synopsis body ------------------------------------------------------- */

/* Rendered as text rather than a textarea so the placeholder can be marked.
   The raw body lives in a hidden textarea next to it for copying. */
.synopsis-body {
  width: 100%;
  min-height: 14rem;
  max-height: 28rem;
  overflow-y: auto;
  padding: .6rem;
  font: inherit;
  font-size: 16px;
  /* Keeps the blank lines between the greeting, intro and sections. */
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.synopsis-body mark.placeholder {
  padding: 0 .2rem;
  border-radius: 4px;
  background: color-mix(in srgb, var(--danger) 22%, transparent);
  color: var(--ink);
  font-weight: 700;
}
.placeholder-hint { margin: .4rem 0 .6rem; }
.placeholder-hint code {
  padding: 0 .25rem;
  border-radius: 4px;
  background: color-mix(in srgb, var(--danger) 18%, transparent);
  font-size: .95em;
}

/* --- accounts, workspace switching, and notices ------------------------- */

/* The header's third column used to be the logout form itself. It now
   holds the workspace switcher and the Settings link beside it, so the
   grid's end alignment moves to the wrapper. Nothing is behind a
   disclosure: the capture screen's rule is that everything is visible. */
.bar > .bar-right {
  justify-self: end;
  display: flex;
  align-items: center;
  gap: .4rem;
}
.bar > .bar-right form { margin: 0; display: flex; align-items: center; }

/* Matches the painted height of .header-btn beside it, so the header keeps
   one line of controls at one size. */
/* The header's own size, on top of the shared select look. It needs the
   chevron drawn too: the shared rule suppresses the native one for every
   select, so a select without a wrapper would have none at all. */
.workspace-switch { position: relative; }
.workspace-switch::after {
  content: "";
  position: absolute;
  right: .6rem;
  top: 50%;
  width: .32rem;
  height: .32rem;
  margin-top: -.24rem;
  border-right: 2px solid var(--muted);
  border-bottom: 2px solid var(--muted);
  transform: rotate(45deg);
  pointer-events: none;
}
.workspace-switch select {
  height: 32px;
  min-height: 0;
  max-width: 8rem;
  padding: 0 1.5rem 0 .5rem;
  font-size: .85rem;
  font-weight: 600;
}

/* A statement of fact rather than a problem, so it borrows the accent
   rather than the danger colour. */
.notice {
  margin: 0 0 1rem;
  padding: .6rem .8rem;
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
  color: var(--ink);
  font-size: .9rem;
}
/* Full width under the header, where a week banner would sit. */
.notice-bar { margin: 0; border-radius: 0; border-left: 0; border-right: 0; }

.note-line { color: var(--muted); font-size: .9rem; }
.muted-line { color: var(--muted); font-size: .82rem; }
.field-hint { margin: .1rem 0 0; color: var(--muted); font-size: .8rem; }
/* A hint belongs to the field above it, so the next field needs air.
   Without this the hint and the following label ran together. */
.field-hint + label { margin-top: .9rem; }

.pill-off { background: color-mix(in srgb, var(--muted) 18%, transparent); color: var(--muted); }
.pill-ok { background: color-mix(in srgb, var(--ok) 16%, transparent); color: var(--ok); }
.pill-warn { background: color-mix(in srgb, var(--danger) 16%, transparent); color: var(--danger); }

button.danger { background: var(--danger); color: var(--on-danger); }

/* A button that reads as a link: used for "log out instead" under a forced
   password change, where a second solid button would compete with Save. */
.link-button {
  display: inline-block;
  margin: 0; padding: 0;
  background: none; border: 0;
  min-height: 0;
  color: var(--accent); font-size: .9rem; font-weight: 500;
  text-decoration: underline; cursor: pointer;
}
.auth-secondary { margin-top: 1rem; }
.auth-secondary button { margin-top: 0; }

/* --- the accounts page -------------------------------------------------- */

/* No week navigation here, so the middle column is empty. Everything else
   about the bar is shared, so the header is the same height on every page
   and switching between Capture and Settings shifts nothing. */
/* Flex rather than the grid: three header buttons are ~268px, which left a
   320px phone no room for the title, and an auto grid track cannot give
   way, so the buttons painted over it (audit M10). Here the buttons drop
   to a second line, still on the right, only when title and buttons do
   not fit side by side; otherwise it is the same single row as before.
   The vertical padding moves onto the items so each line is the toolbar
   row's height. */
.bar-plain {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  column-gap: .5rem;
  row-gap: 0;
  padding-top: 0;
  padding-bottom: 0;
}
.bar-plain > span:empty { display: none; }
.bar-plain > .bar-title {
  flex: 1 1 auto;
  line-height: 40px;
  padding: .5rem 0;
}
.bar-plain > .bar-right {
  flex: 0 0 auto;
  margin-left: auto;
  padding: .5rem 0;
}

/* A bare grid item defaults to justify-self: stretch, which is what made
   this button fill its whole 1fr column. */
.bar-plain > .header-btn,
.bar-plain > form { justify-self: end; width: auto; }

/* Two buttons and a title do not fit a phone at full length, so the
   labels shorten, exactly as the week label does. */
.btn-short { display: none; }
@media (max-width: 520px) {
  .btn-long { display: none; }
  .btn-short { display: inline; }
}

/* --- the header on a phone ----------------------------------------------
   Three things on one line needed about 420px: the title, the week
   selector and the controls. Below that the centred week selector grew
   into the title and the left arrow went off the edge of the screen.
   Below 600px the header becomes two bands instead, each one the shape of
   the toolbar row underneath: same side padding, same divider, same
   height. The week selector gets a band of its own, which is also where
   the room for full-size arrows comes from.

   Flex with a wrapped third item rather than a second <header>: one
   element, so there is one place where the markup can get out of step,
   and the order is set here rather than in the template. */
@media (max-width: 599px) {
  .bar:not(.bar-plain) {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    /* The bands carry the vertical padding now, so the rule above them
       cannot double it. */
    padding: 0 calc(1rem + var(--safe-right)) 0 calc(1rem + var(--safe-left));
    gap: .4rem;
    row-gap: 0;
  }
  /* flex-basis 0, not auto: a wrapping flex line breaks on the items'
     basis sizes, so a title asking for its full text width pushed the
     controls onto a third line at 320px instead of ellipsising. At 0 it
     never forces a break and takes whatever the controls leave. */
  .bar:not(.bar-plain) > .bar-title {
    order: 1;
    flex: 1 1 0;
    min-width: 0;
    padding: .5rem 0;
  }
  .bar:not(.bar-plain) > .bar-right {
    order: 2;
    flex: 0 0 auto;
    padding: .5rem 0;
  }
  /* flex-basis 100% is what puts it on a line of its own. The negative
     side margins pull the band out through the header's padding so its
     divider runs edge to edge, like the one under the toolbar; the
     padding puts the content back where it was. */
  .bar:not(.bar-plain) > .week-nav {
    order: 3;
    flex: 1 0 100%;
    justify-content: center;
    margin: 0 calc(-1rem - var(--safe-right)) 0 calc(-1rem - var(--safe-left));
    /* .4rem, not .5rem: with 44px arrows the band comes out the same
       height as the toolbar row below it. */
    padding: .4rem calc(1rem + var(--safe-right)) .4rem calc(1rem + var(--safe-left));
    /* The divider between the two bands, matching the one under them. */
    border-top: 1px solid var(--line);
  }

  /* Full-size tap targets, now that there is room for them. The arrows
     were 26x28 with an invisible 44px strip; on their own line they can
     simply be 44px, which is also what makes them visible when pressed. */
  .bar:not(.bar-plain) > .week-nav .week-arrow {
    width: 44px;
    height: 44px;
    font-size: 1.35rem;
  }
  .bar:not(.bar-plain) > .week-nav .week-arrow::after { content: none; }
  /* A row of its own is room for the real label, so the abbreviation is
     not used here: "11 - 17 October 2026", the same words as the desktop
     header. The longest week spans two months and two years -- "27
     December 2026 - 2 January 2027" -- and that is what has to fit at
     320px, so the size is tied to the viewport and shrinks rather than
     abbreviating. The ellipsis below it is the last resort and, at these
     sizes, is never reached. */
  .bar:not(.bar-plain) > .week-nav .week-long { display: inline; }
  .bar:not(.bar-plain) > .week-nav .week-short { display: none; }
  .bar:not(.bar-plain) > .week-nav .week-chip {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    /* Last resort: the label gives way before anything overlaps. */
    min-width: 0;
    overflow: hidden;
    padding: 0 .3rem;
    font-size: clamp(.8rem, 3.4vw, .9375rem);
    text-overflow: ellipsis;
  }
}

/* --- the workspace switcher on a phone ---------------------------------
   In the top row it had about 5.5rem and showed "Fort Brag". With more
   than one workspace, below 600px, it moves down to the week row instead:
   top row title | Settings, Log out; then a divider; then switcher on the
   left and the week selector on the right. When the two do not fit side by
   side the week selector wraps to a band of its own below the switcher
   (app.js sets .week-own-row), the same band a one-workspace header has.
   The markup does not move, so the desktop header is untouched: on a phone
   .bar-right dissolves (display: contents) so its children can be placed
   in different rows. Nobody with one workspace gets .has-switch. */
@media (max-width: 599px) {
  .bar.has-switch { column-gap: 0; }
  .bar.has-switch > .bar-right { display: contents; }
  /* Settings and Log out, still on the right of the top row with the gaps
     .bar-right gave them. Margin rather than padding on Settings, so the
     painted button keeps its size and the row its height. */
  .bar.has-switch > .bar-right > .header-btn {
    order: 2;
    margin: .5rem 0 .5rem .4rem;
  }
  .bar.has-switch > .bar-right > form:not(.workspace-switch) {
    order: 2;
    margin-left: .25rem;
    padding: .5rem 0;
  }
  /* The break after the top row, drawn as the divider between the bands.
     flex-basis 100% puts it on a line of its own; the negative margins run
     it edge to edge. */
  .bar.has-switch::after {
    content: "";
    order: 3;
    flex: 1 0 100%;
    height: 1px;
    margin: 0 calc(-1rem - var(--safe-right)) 0 calc(-1rem - var(--safe-left));
    background: var(--line);
  }
  .bar.has-switch .workspace-switch {
    order: 4;
    flex: 0 1 auto;
    min-width: 0;
    max-width: 100%;
    padding: .4rem 0;
  }
  /* The whole name, at full size: no max-width and nothing cut off. The
     height is the week arrows', so the shared row stays one height. */
  .bar.has-switch .workspace-switch select {
    max-width: 100%;
    height: 44px;
    font-size: .9375rem;
  }
  /* Beside the switcher: on the right, no band of its own. Its basis is
     its full width, so a label that does not fit wraps the whole selector
     to the next line rather than shrinking it here. */
  .bar.has-switch > .week-nav {
    order: 5;
    flex: 1 1 auto;
    justify-content: flex-end;
    margin: 0 0 0 .4rem;
    padding: .4rem 0;
    border-top: 0;
  }
  /* Wrapped: exactly the band a one-workspace header has. */
  .bar.has-switch.week-own-row > .week-nav {
    flex: 1 0 100%;
    justify-content: center;
    margin: 0 calc(-1rem - var(--safe-right)) 0 calc(-1rem - var(--safe-left));
    padding: .4rem calc(1rem + var(--safe-right)) .4rem calc(1rem + var(--safe-left));
    border-top: 1px solid var(--line);
  }
}

.admin {
  max-width: 46rem; margin: 0 auto;
  padding: 1rem calc(1rem + var(--safe-right)) calc(3rem + var(--safe-bottom)) calc(1rem + var(--safe-left));
}
.admin .card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1rem;
  margin-bottom: 1rem;
}
.admin .card h2 { margin-top: 0; }

/* The one-time password is the only thing on this page that cannot be
   recovered, so it is set apart. */
.one-time { border-color: var(--accent); }
.credentials { margin: .5rem 0; display: grid; grid-template-columns: auto 1fr; gap: .3rem .8rem; }
.credentials dt { color: var(--muted); font-size: .85rem; }
.credentials dd { margin: 0; }
.credentials code {
  font-size: 1rem; font-weight: 600;
  padding: .15rem .4rem; border-radius: 6px;
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  user-select: all;
}

.admin-table { width: 100%; border-collapse: collapse; font-size: .92rem; }
.admin-table th {
  text-align: left; font-size: .75rem; text-transform: uppercase;
  letter-spacing: .04em; color: var(--muted); font-weight: 600;
  padding: 0 .5rem .4rem 0; border-bottom: 1px solid var(--line);
}
/* One divider per person, on the row, spanning every column. It used to
   be on each cell, and the actions cell was a flex container, which takes
   a cell out of the table layout: its border then sat at a different
   height from its neighbours and the line looked broken. The last row
   keeps its line too, so the button under a list sits below a divider
   like every row above it. */
.admin-table tbody tr { border-bottom: 1px solid var(--line); }
.admin-table td {
  padding: .55rem .5rem .55rem 0;
  border-bottom: 0;
  /* Every cell centres on the row, so the name block, the workspaces,
     the status pill and the buttons all line up. */
  vertical-align: middle;
}
/* The account column keeps enough room for a name and its badge on one
   line each; the workspaces column is the one that wraps, because a list
   of names reads fine over two lines and a person's name does not. */
.admin-table .admin-who { min-width: 12rem; }
.admin-who strong { display: block; line-height: 1.3; }
.admin-sub { display: flex; align-items: center; gap: .35rem; flex-wrap: wrap; }
.admin-sub .pill { margin-left: 0; }
/* A status is one short phrase; breaking it over two lines just looks
   like a mistake. */
.admin-table .pill { white-space: nowrap; }

/* Two slots, packed to the right of the cell: Reset password, then
   Disable or Enable. The second slot stays even when that button is
   absent, so every Reset password shares one column. */
.admin-table .admin-actions { width: 16rem; padding-right: 0; }
.admin-actions-group {
  display: grid;
  grid-template-columns: max-content 5.75rem;
  column-gap: .4rem;
  align-items: center;
  justify-content: end;
  width: 100%;
}
.admin-actions-group > * { justify-self: end; }
.admin-actions-group > :last-child {
  justify-self: stretch;
  display: flex;
  justify-content: flex-end;
}
.admin-actions form { margin: 0; }

/* Card-size, so a row stays compact. */
.row-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  margin: 0;
  height: 32px;
  min-height: 0;
  padding: 0 .7rem;
  background: var(--accent);
  border: 1px solid var(--accent);
  border-radius: 8px;
  color: var(--accent-ink);
  font-family: inherit;
  font-size: .8125rem;
  font-weight: 600;
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
}
.row-btn--danger { background: var(--danger); border-color: var(--danger); color: var(--on-danger); }

.stacked { display: grid; gap: var(--stacked-gap, .35rem); }
/* Selects are styled once, above; these only need their layout. The
   wrapper is what flexes, since it carries the chevron. */
.inline-form .select-field { flex: 1 1 auto; min-width: 0; }
.checkbox {
  display: flex; align-items: center; gap: .5rem;
  margin: .6rem 0 0; color: var(--ink); font-size: .95rem;
}
.checkbox input { width: auto; margin: 0; }

.member-list { list-style: none; margin: 0 0 1rem; padding: 0; }
.member-list li {
  display: flex; align-items: center; justify-content: space-between;
  gap: .5rem; padding: .5rem 0; border-bottom: 1px solid var(--line);
}
.member-list form { margin: 0; }
.member-list button { margin: 0; min-height: 36px; padding: .4rem .7rem; font-size: .85rem; }

.inline-form { display: flex; gap: .5rem; align-items: center; }
.inline-form button { margin: 0; }

/* The accounts page is read on a phone as often as a laptop: the actions
   column would otherwise push the table into a horizontal scroll. */
/* 700px, not 560: with a fixed actions column and a minimum on the
   account column, the table was still wider than a 600px window and set
   the whole page scrolling sideways. It becomes cards while there is
   still room for them to look right. */
@media (max-width: 700px) {
  /* Each person becomes a card: the row keeps its one border, the cells
     stack inside it, and the fixed actions column gives up its width. */
  .admin-table thead { display: none; }
  .admin-table, .admin-table tbody, .admin-table tr, .admin-table td { display: block; width: 100%; }
  .admin-table tbody tr { padding: .7rem 0; }
  .admin-table td { padding: .15rem 0; }
  .admin-table .admin-actions { width: auto; text-align: left; margin-top: .5rem; }
  .admin-actions-group { justify-content: start; width: max-content; }
  /* With the header row gone, a cell holding only a number says nothing.
     The column heading comes back as a label in front of it. */
  .admin-table td[data-label]::before {
    content: attr(data-label) ": ";
    color: var(--muted);
  }
}

@media (max-width: 560px) {
  .bar > .bar-right { gap: .25rem; }
  .workspace-switch select { max-width: 5.5rem; font-size: .8rem; }
}

/* Who wrote a note, in a workspace where that is not obvious. Quieter than
   the date beside it: it is context, not the thing you are scanning for. */
.note-author {
  margin-left: .4rem;
  color: var(--muted);
  font-size: .8rem;
}
.note-author::before { content: "· "; }

/* --- Settings and Account: forms, textareas and action buttons --------- */

.settings-page,
.account-page,
.admin-page {
  --action-gap: 20px;
  --stacked-gap: .35rem;
}

@media (max-width: 560px) {
  .settings-page,
  .account-page,
  .admin-page { --action-gap: 16px; }
}

/* A multi-line box is a text box that happens to be taller, so it matches
   one: same font, background, border, radius and padding. Left alone it
   gets the browser's textarea defaults, which on these pages meant a
   monospace font and a different border. */
.settings-page textarea,
.account-page textarea {
  display: block;
  width: 100%;
  padding: .75rem;
  font-family: inherit;
  font-size: 1rem;
  line-height: 1.5;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  /* Grows with its content, so three rows is a floor and not a ceiling.
     Horizontal dragging would only break the column it sits in. */
  resize: vertical;
  min-height: calc(3 * 1.5em + 1.5rem + 2px);
  overflow-y: hidden;
}
.settings-page textarea:focus-visible,
.account-page textarea:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* The in-page action button: what Save settings looks like. Written as a
   class as well so a link can be one, and so the two cannot drift apart.
   Sized to its text: a button that spans the column reads as a banner. */
.settings-page .action-btn,
.account-page .action-btn,
.admin-page .action-btn,
.settings-page button[type="submit"],
.account-page button[type="submit"],
.admin-page .card > form > button[type="submit"],
.admin-page .modal button[type="submit"] {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: auto;
  min-height: 44px;
  padding: .8rem 1rem;
  font-family: inherit;
  font-size: 1rem;
  font-weight: 600;
  line-height: 1;
  color: var(--accent-ink);
  background: var(--accent);
  border: 0;
  border-radius: var(--radius);
  text-decoration: none;
  cursor: pointer;
}
.settings-page .action-btn:hover,
.account-page .action-btn:hover,
.admin-page .action-btn:hover { text-decoration: none; }

/* The rule above is specific enough to have taken the danger colour off
   the "Use the standard template" button, so it is restated here. */
.settings-page button[type="submit"].danger,
.account-page button[type="submit"].danger,
.admin-page button[type="submit"].danger {
  background: var(--danger);
  color: var(--on-danger);
}

/* An action button that is not in a form, such as "Change your password",
   still needs the gap from the heading it sits under. The .5rem is the
   heading's own bottom margin, which an inline-flex box does not collapse
   with. */
.settings-page .card > .action-btn,
.account-page .card > .action-btn {
  margin-top: calc(var(--action-gap) - .5rem);
}

/* A grid item stretches by default, which is what made the Save button on
   Your account span the whole form. */
.admin-page .modal .stacked > button[type="submit"],
.settings-page .stacked > button[type="submit"],
.settings-page .stacked > .action-btn,
.account-page .stacked > button[type="submit"],
.account-page .stacked > .action-btn { justify-self: start; }

/* An action button belongs to the section whose content it acts on, and
   needs the same gap on both sides to say so. "Save settings" had 16px
   above it and nothing below, so it touched the Export template section
   and read as that section's button rather than as the form's. */

/* The save button for the whole form, between the last section it saves
   and the next section down. Forms are zeroed first because a browser
   default bottom margin would land on only one side of it. */
.settings-page > form,
.account-page > form { margin: 0; }

.settings-page > form > button[type="submit"],
.account-page > form > button[type="submit"] {
  display: block;
  /* The preceding card's 1rem bottom margin collapses into this, so the
     larger of the two wins and the gap above matches the gap below. */
  margin: var(--action-gap) 0;
}

/* Inside a section the gap goes on the form, not on the button. A button
   is inline-block, so its margin does not collapse out through the form,
   and putting it there stacked on top of the preceding paragraph's margin
   instead of merging with it. The form is a block, so it merges. */
.settings-page .card > form,
.account-page .card > form { margin: var(--action-gap) 0 0; }

.settings-page .card > form > button[type="submit"],
.account-page .card > form > button[type="submit"] { margin: 0; }

/* A grid form already separates its rows, so that gap is taken off rather
   than added to. */
.settings-page .card > form.stacked > button[type="submit"],
.account-page .card > form.stacked > button[type="submit"] {
  margin-top: calc(var(--action-gap) - var(--stacked-gap));
}

/* A section whose form is followed by something else, such as the
   export template's buttons, needs the same gap below the form as above
   it. That margin is the space above the button row. */
.settings-page .card > form:not(:last-child),
.account-page .card > form:not(:last-child) {
  margin-bottom: var(--action-gap);
}

/* The export-template buttons use the shared inline row. On a phone the
   two labels do not fit, so the row wraps and Upload stays second. */
.settings-page .inline-form { flex-wrap: wrap; }

/* --- a confirmation that clears itself ---------------------------------- */

/* A saved-confirmation has nothing to say once it has been read, and it
   should not sit on top of the form it refers to. It holds for five
   seconds, then fades and collapses so it leaves no gap behind. Errors are
   not given this class: those stay until they are dealt with. */
.notice-timed {
  overflow: hidden;
  max-height: 12rem;
  animation: notice-dismiss .45s ease-in 5s forwards;
}

@keyframes notice-dismiss {
  from { opacity: 1; }
  to {
    opacity: 0;
    max-height: 0;
    margin-bottom: 0;
    padding-top: 0;
    padding-bottom: 0;
    border-top-width: 0;
    border-bottom-width: 0;
  }
}

/* Still clears itself, just without the fade. */
@media (prefers-reduced-motion: reduce) {
  .notice-timed { animation-duration: .01s; }
}

/* --- the event date picker ---------------------------------------------- */

/* The same popover as the header's week calendar, with one difference:
   the unit is a day, so a single rounded square fills rather than the
   whole week row. Everything else -- the month header, the S-S columns,
   the muted adjacent months, the underlined today, the colours in both
   schemes -- comes from the .cal-* rules above, unchanged. */

/* Anchored to the field's left edge rather than centred on it: a date
   field is narrow, and a popover centred on it would hang off the side. */
.date-cal .cal-popover,
.week-field-cal .cal-popover {
  left: 0;
  right: auto;
  transform: none;
  max-width: calc(100vw - 2rem);
}

/* Not enough room below, so it opens upwards instead. */
.date-cal .cal-popover.is-above {
  top: auto;
  bottom: 100%;
  margin: 0 0 .4rem;
}

/* A day picker has no week to light up. */
.cal-popover.is-day .cal-row { background: none; }
.cal-popover.is-day .cal-day:hover {
  background: color-mix(in srgb, var(--accent) 12%, transparent);
}
.cal-popover.is-day .cal-day.is-selected {
  background: var(--accent);
  color: var(--accent-ink);
}
.cal-popover.is-day .cal-day.is-selected.is-outside {
  color: var(--accent-ink);
  opacity: .8;
}
/* Today underlined, except where the filled square already says it. */
.cal-popover.is-day .cal-day.is-selected.is-today .cal-num {
  text-decoration: none;
}

/* Two buttons where the week calendar has one, in the same style. */
.cal-foot { display: flex; gap: .5rem; margin-top: .6rem; }
.cal-foot-btn { flex: 1 1 0; margin: 0; width: auto; height: 40px; }

@media (max-width: 520px) {
  /* A bottom sheet, like the header calendar. The rules above are more
     specific than the shared sheet rule, so the parts they overrode are
     restated here. */
  .date-cal .cal-popover,
  .week-field-cal .cal-popover {
    left: 0;
    right: 0;
    width: auto;
    max-width: none;
  }
  .date-cal .cal-popover.is-above {
    top: auto;
    bottom: 0;
    margin: 0;
  }
  .cal-foot-btn { height: 44px; }
}

/* --- the settings toolbar ----------------------------------------------- */

/* The same .tabs row as Capture / WAR / Reset WAR, so the two toolbars are
   one component. These rules only add the right-hand group, which the WAR
   row does not have. */
/* The destinations take the room that is left and scroll inside it, so
   they can never widen the row and set the whole page scrolling. */
.tabs-nav {
  display: flex;
  align-items: center;
  gap: .4rem;
  flex: 1 1 auto;
  min-width: 0;
  overflow-x: auto;
  /* No scrollbar furniture: it is a short strip, not a region to manage. */
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.tabs-nav::-webkit-scrollbar { display: none; }

.tabs-right {
  /* Where Reset WAR sits. */
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: .5rem;
  flex: 0 0 auto;
}

/* The one place a width is pinned: "Saving..." and "Saved" must not
   resize the button and shove the row about. */
.tab-save {
  width: 8.5rem;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Grey until a field changes. is-ready is the blue button with white text. */
.tab-save {
  background: var(--muted);
  border-color: var(--muted);
  color: #fff;
  opacity: .55;
  cursor: default;
}
.tab-save.is-ready {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
  opacity: 1;
  cursor: pointer;
}

/* Below 600px the row cannot hold three destinations and an action at a
   usable size, so it stops trying: the tabs take the whole row as equal
   columns, and the action moves to a bar fixed at the bottom of the
   screen where a thumb already is. Above 600px the single row stays. */
@media (max-width: 599px) {
  .tabs { gap: .3rem; flex-wrap: nowrap; }

  /* The tabs share the whole row, each growing from its own label's
     width. Equal columns stopped working at four (System, for
     administrators): "Workspace" needs more than a quarter of a 375px
     row, while the four labels together fit with room to spare. */
  .tabs-nav {
    flex: 1 1 auto;
    width: 100%;
    overflow-x: visible;
    gap: .3rem;
  }
  .tabs-nav .tab {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    display: block;
    line-height: 38px;
    text-align: center;
  }

  /* The same element, moved rather than duplicated: the button keeps its
     form association and the dirty-state JS keeps finding it. */
  .tabs-right {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 30;
    margin: 0;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: .35rem;
    /* Clear of the iPhone home indicator, and of the notch in landscape. */
    padding: .6rem calc(1rem + var(--safe-right)) calc(.6rem + var(--safe-bottom)) calc(1rem + var(--safe-left));
    background: var(--surface);
    border-top: 1px solid var(--line);
  }
  .tabs-right .tab-save { width: 100%; }

  /* So the last field is not left under the bar. */
  .settings-page,
  .account-page { padding-bottom: calc(8rem + var(--safe-bottom)); }

  /* Capture, WAR and Reset WAR share the row the same way, so they can
     never overlap either. Reset keeps its place at the end. */
  .tabs > .tab { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
}

/* Below 600px every toolbar button, header and tab row alike, takes the
   tighter padding, so four tabs fit at 320px and no row mixes two values.
   Reset WAR reads "Reset" under 520px. */
@media (max-width: 599px) {
  :root { --toolbar-btn-pad: .5rem; }
}

/* The settings pages keep the page gap the toolbar used to leave. */
.settings-page > .notice:first-child,
.account-page > .notice:first-child { margin-top: 0; }

/* --- modal dialogs -------------------------------------------------------
   One style for every dialog in the app: a card lifted off the page, the
   same surface, radius and border as the cards behind it, centred and
   never wider than a phone's gutters allow. */
.modal {
  width: min(28rem, calc(100vw - 2rem));
  max-height: calc(100vh - 3rem);
  padding: 1.1rem;
  overflow-y: auto;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 12px 32px rgb(0 0 0 / 28%);
}
.modal::backdrop { background: rgb(0 0 0 / 45%); }
.modal h2 { margin: 0 0 .6rem; }
/* The buttons sit on one line at the end, cancel first: the quiet one is
   where a thumb lands by accident. */
.modal-actions {
  display: flex;
  align-items: center;
  gap: .6rem;
  /* Settings and Admin set the gap; elsewhere, such as WAR, it is 20px. */
  margin-top: var(--action-gap, 20px);
}
.modal-actions .row-btn { height: 44px; padding: 0 1rem; }
/* The grid rule that stops a .stacked button stretching applies here too. */
.modal .stacked > .modal-actions { justify-self: start; }

/* A select that takes more than one value gets no chevron: it is a list,
   not a dropdown, and the arrow would promise something it does not do. */
.multi-select {
  width: 100%;
  padding: .5rem .6rem;
  font-family: inherit;
  font-size: 1rem;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

/* The rename field in a workspace row: as wide as the cell allows, with
   the button beside it. */
.rename-field { min-width: 0; }
.admin-who .inline-form { gap: .4rem; }

/* The one-line summary on a card that links somewhere else, such as
   "15 rules · last edited 14 Oct 2026 by Scott". Darker than a hint: it is
   the content of the card, not a note about it. */
.summary-line { margin: 0; font-size: .95rem; color: var(--ink); }

/* The writing rules editor. Tall enough to hold a page of rules without
   scrolling inside a small box, and it still grows with the content: the
   floor is here, app.js raises the ceiling. */
.rules-editor { min-height: 60vh; }
/* The page is one tall field, so the card needs no inner heading gap. */
.rules-page .card > label:first-child { margin-top: 0; }

/* The drop zone inside a form row, rather than under the capture box. */
.drop-zone-inline { margin-top: .2rem; }
.drop-zone.is-chosen { border-style: solid; border-color: var(--accent); }
.drop-zone.is-chosen label { color: var(--ink); }

/* A refused upload. The zone keeps its shape and stays pressable, so the
   message reads as "try again here" rather than as a dead end. It grows
   to fit a sentence: a fixed 56px box would clip the longer ones. */
.drop-zone.is-error {
  height: auto;
  min-height: 56px;
  padding: .6rem .75rem;
  border-style: solid;
  border-color: var(--danger);
  background: color-mix(in srgb, var(--danger) 8%, var(--surface));
}
.drop-zone.is-error label { color: var(--danger); font-weight: 600; }

/* --- input-like buttons ------------------------------------------------------
   A button that opens a picker but reads as a text box: the box comes from
   the shared input rule above, this only lines its label up and marks it
   as a picker. */
.input-like {
  display: flex; align-items: center; justify-content: space-between; gap: .5rem;
  text-align: left; cursor: pointer; font-weight: 400;
  /* A button resets line-height; a text box inherits it. Match the box. */
  line-height: inherit;
}
.input-like.is-empty { color: var(--muted); }
.input-like::after {
  content: ""; flex: 0 0 auto;
  width: .4rem; height: .4rem; margin: -.2rem .2rem 0 0;
  border-right: 2px solid var(--muted); border-bottom: 2px solid var(--muted);
  transform: rotate(45deg);
}

/* --- disclosure ---------------------------------------------------------------
   A collapsible section: a full-width header at least 44px tall with a
   chevron that turns when the section opens. Never used on Capture, which
   collapses nothing. */
.disclosure > summary { list-style: none; }
.disclosure > summary::-webkit-details-marker { display: none; }
.disclosure-head {
  display: flex; align-items: center; justify-content: space-between; gap: .75rem;
  min-height: 44px; padding: 0 .25rem; cursor: pointer;
  font-weight: 700; border-bottom: 1px solid var(--line);
  -webkit-tap-highlight-color: transparent;
}
.disclosure-head::after {
  content: ""; flex: 0 0 auto;
  width: .45rem; height: .45rem; margin-right: .35rem;
  border-right: 2px solid var(--muted); border-bottom: 2px solid var(--muted);
  transform: rotate(-45deg);
  transition: transform .15s ease;
}
.disclosure[open] > .disclosure-head::after { transform: rotate(45deg); }
.disclosure-head:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) {
  .disclosure-head::after { transition: none; }
}

/* --- archive (step 5) ------------------------------------------------------ */

.archive .muted { color: var(--muted); }
.archive-search { margin: 0 0 1.2rem; }
/* Stacked on a phone; one row on a wider screen. */
.archive-filters { display: grid; gap: .75rem; }
.archive-field { display: grid; gap: 0; min-width: 0; }
.week-field { position: relative; }
/* The Capture field rule above sets display: block for inputs; this button
   lays out its label and chevron in a row. */
.archive-filters .input-like { display: flex; }
.week-field-cal { position: relative; }
.archive-search-actions { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; }
.archive-search-actions .btn { margin: 0; }
.btn.is-disabled { opacity: .5; pointer-events: none; }
.week-field-btn [data-week-label] { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* A tablet gets two fields to a row, search across the top. */
@media (min-width: 600px) {
  .archive-filters { grid-template-columns: 1fr 1fr; align-items: end; }
  .archive-field-q { grid-column: 1 / -1; }
  .archive-search-actions { min-height: 48px; }
}
/* A desktop gets the whole filter bar on one row, so the list page is
   allowed wider than the usual reading column; the lists themselves keep
   that column. */
@media (min-width: 1000px) {
  .page.archive.is-list { max-width: 72rem; }
  .archive-filters {
    grid-template-columns: minmax(15rem, 2fr) repeat(2, minmax(12rem, 1.6fr)) minmax(9rem, 1.1fr) auto;
  }
  .archive-field-q { grid-column: auto; }
  .archive-search-actions { flex-wrap: nowrap; }
}
.archive.is-list > h2,
.archive-weeks-head,
.archive-years,
.archive-hits { max-width: 40rem; }
.archive-weeks a,
.archive-hits a { color: var(--accent); }
.archive-weeks-head { display: flex; justify-content: space-between; align-items: center; gap: .5rem; }
.archive-years { margin: 0 0 1.2rem; }
.archive-month { margin-left: .75rem; }
.archive-month > .disclosure-head { font-weight: 600; }
.archive-month .archive-weeks { margin: .6rem 0 .8rem; }
.archive-hits, .archive-weeks, .archive-notes { list-style: none; margin: 0 0 1.2rem; padding: 0; display: grid; gap: .6rem; }
.archive-hits li, .archive-weeks li, .archive-notes li {
  padding: .55rem .7rem; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface); min-width: 0; overflow-wrap: anywhere;
}
.archive-weeks li { display: flex; flex-wrap: wrap; justify-content: space-between; gap: .3rem .8rem; }
.archive-week-meta { font-size: .85rem; color: var(--muted); }
.archive-snippet { margin: .35rem 0 0; font-size: .9rem; }
.archive-snippet mark { background: color-mix(in srgb, var(--accent) 22%, transparent); color: inherit; border-radius: 3px; }
.archive-notes p { margin: .25rem 0 0; white-space: pre-wrap; }
.archive-week-head { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: .5rem; }
.archive-actions { display: flex; flex-wrap: wrap; gap: .5rem; }

/* --- tags (step 5) --------------------------------------------------------- */

.tags-page .tag-kind { margin-top: 1.4rem; }
.tag-rows { list-style: none; margin: 0; padding: 0; display: grid; gap: .6rem; }
.tag-rows > li { border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface); padding: .6rem .7rem; min-width: 0; }
.tag-rows summary { cursor: pointer; display: flex; flex-wrap: wrap; gap: .3rem .7rem; align-items: baseline; }
.tag-name { font-weight: 600; overflow-wrap: anywhere; }
.tag-aliases, .tag-usage { font-size: .85rem; color: var(--muted); overflow-wrap: anywhere; }
.tag-forms { display: grid; gap: .8rem; margin-top: .7rem; }
.tag-forms form, .tag-new { display: grid; gap: .4rem; }
.tag-forms label, .tag-new label { display: grid; gap: .2rem; font-size: .85rem; }
.tag-forms input, .tag-forms select, .tag-new input, .tag-new select { max-width: 100%; }

/* --- the System page (/admin/system) ------------------------------------
   Cards, tables, pills and hints are the shared ones; these rules only
   draw the inline SVG diagrams, and every colour in them is a token, so
   they follow the colour scheme like everything else. The viewBox is 340
   units wide, about one unit per pixel at 375px, so text stays legible on
   a phone and grows no further than 26rem on a wide screen. */
.diagram-defs { position: absolute; width: 0; height: 0; overflow: hidden; }
.diagram {
  display: block;
  width: 100%;
  max-width: 26rem;
  height: auto;
  margin: .75rem auto 0;
  font-family: inherit;
}
.diagram .box { fill: var(--bg); stroke: var(--line); stroke-width: 1.25; }
.diagram .box--accent { stroke: var(--accent); stroke-width: 1.5; }
.diagram .box--external { stroke: var(--accent); stroke-dasharray: 5 3; }
.diagram .box--async { stroke: var(--muted); stroke-dasharray: 4 3; }
.diagram .zone { fill: none; stroke: var(--muted); stroke-dasharray: 6 4; }
.diagram .band {
  fill: color-mix(in srgb, var(--accent) 10%, transparent);
  stroke: var(--accent);
}
.diagram .t { fill: var(--ink); font-size: 13px; font-weight: 700; }
.diagram .s { fill: var(--muted); font-size: 11px; }
.diagram .label { fill: var(--muted); font-size: 10.5px; }
.diagram .edge { fill: none; stroke: var(--muted); stroke-width: 1.5; }
.diagram .edge--async { stroke-dasharray: 5 4; }
.diagram .edge--out { stroke: var(--accent); }
.diagram .edge--many { stroke: var(--accent); stroke-dasharray: 5 4; }
.diagram .edge--sync { stroke-dasharray: 2 3; }
.diagram-arrowhead { fill: var(--muted); }

.system-sub {
  margin: 1.25rem 0 0;
  font-size: .9rem;
}
.system-ports { margin-top: 1rem; }

.system-legend {
  list-style: none;
  margin: .75rem 0 .5rem;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: .35rem 1rem;
  color: var(--muted);
  font-size: .8rem;
}
.legend-line {
  display: inline-block;
  width: 1.5rem;
  vertical-align: middle;
  border-top: 2px solid var(--muted);
}
.legend-line--many { border-top-style: dashed; border-color: var(--accent); }
.legend-line--sync { border-top-style: dotted; }

/* Label and value side by side; stacked once the labels would squeeze
   the values. */
.system-status {
  display: grid;
  grid-template-columns: minmax(9rem, auto) 1fr;
  gap: .45rem 1rem;
  margin: 0 0 .75rem;
  font-size: .92rem;
}
.system-status dt { color: var(--muted); }
.system-status dd { margin: 0; min-width: 0; overflow-wrap: anywhere; }
.system-peers { list-style: none; margin: 0; padding: 0; }
.system-peers li + li { margin-top: .2rem; }
@media (max-width: 420px) {
  .system-status { grid-template-columns: 1fr; gap: 0; }
  .system-status dd { margin-bottom: .55rem; }
}
