@import url('fonts.css');

/*
 * app.css
 * =======
 * RejuvStem Design Standard v1.0.
 *
 * NO INLINE STYLE ANYWHERE. The CSP is `style-src 'self'`, so a `style=` on an
 * element is silently dropped by the browser and the page renders subtly wrong
 * with nothing in the console to explain it. Every rule lives here.
 *
 * THE GOLD RULE: gold fills and gold rules, but gold never spells. On a light
 * ground #B89150 fails contrast at text sizes, so text that wants to read as
 * gold uses --gold-text, which is the same hue darkened until it is legible.
 *
 * Square corners throughout. No border-radius in this file, deliberately.
 *
 * Fonts are system stacks. Google Fonts would need `font-src https://...` and a
 * network round trip on a login page; the display face is self-hosted later if
 * the difference proves to matter.
 */

:root {
  /* THE PLUGIN'S PALETTE, TAKEN VERBATIM rather than re-derived.
     Several of these had drifted when they were retyped by hand: --ink was
     #1A2420 against the real #2E2A24, --ink-soft was a green-grey rather
     than a warm brown, and the failure red was two shades off. None of
     those is visible on its own; together they made the board read as a
     near-miss of itself. */
  --forest:        #0A412A;
  --forest-deep:   #053520;
  --gold:          #B89150;
  --gold-deep:     #8A7340;
  --gold-text:     #7A623F;
  --gold-light:    #E6D2A4;
  --hair:          rgba(10, 65, 42, .11);
  /* THE EDGE OF SOMETHING YOU CAN CLICK. Wil, 11 October 2026: the quiet
     buttons "are blending too much to the background" - they used --hair,
     11% forest, which is a divider's weight, not a control's. This is 3.75:1
     on white and 3.47:1 on cream-warm, over the 3:1 a control's boundary
     needs (WCAG 1.4.11). Dividers keep --hair. */
  --edge:          #6F8A7D;
  --sand:          #D9CDAE;
  --sand-deep:     #C4B48E;
  --cream:         #F4EFE2;
  --cream-warm:    #FAF6EC;
  --cream-alt:     #EFE8D6;
  --ink:           #2E2A24;
  --ink-soft:      #3E382E;
  /* DARKER, TO READ LIKE THE EMAILS. Wil, 14 October 2026: "use the same
     darker font on the dashboard as the email for ease of reading". Running
     text - descriptions, intros, notes - moved up to --ink-soft, the email's
     body colour; what stays muted is captions and labels, darkened from
     #7A7161 (4.5:1 on cream-warm) to this (6.6:1). --ink-quiet keeps the old
     value for what should look switched off: disabled buttons and fields. */
  --ink-mute:      #5E574A;
  --ink-quiet:     #7A7161;
  --sage:         #7E9184;
  --white:         #FFFFFF;
  --fail:          #9B4423;
  --fail-tint:     #F7EAE3;
  --watch:         #8A6D1F;
  --watch-tint:    #F7F0DC;
  /* A form that came back. Green because that is what the fill has always
     meant here, light because a solid forest row read as a banner rather than
     as a state - three stacked was a dark block with the panel's own content
     inside it. Forest on this measures 9.35:1. */
  --done-tint:     #DEE9E1;
  --serif:         'Cormorant Garamond', Georgia, 'Times New Roman', serif;
  --sans:          'Inter', 'Helvetica Neue', Arial, sans-serif;
  --mono:          'IBM Plex Mono', 'Courier New', monospace;

  /* Kept from the app's own vocabulary, mapped onto the above. */
  --paper:        var(--white);
  --rule:         var(--hair);
  --error:        var(--fail);
  --error-bg:     var(--fail-tint);
  --ok:           #1D6550;
  --ink-faint:    var(--ink-mute);
  --forest-soft:  #1B5A3E;
  --gold-on-dark: var(--gold);
  --display:      var(--serif);
}

* { box-sizing: border-box; }

html, body { height: 100%; }

body {
  margin: 0;
  background: var(--cream);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

/* ------------------------------------------------------------ centred ---- */
.centred {
  min-height: 100%;
  display: grid;
  place-items: center;
  padding: 2rem 1rem;
}

.card {
  width: 100%;
  max-width: 26rem;
  background: var(--paper);
  border: 1px solid var(--rule);
  border-top: 3px solid var(--forest);
  padding: 2rem;
}

.card-wide { max-width: 34rem; }

/* THE BRAND BAND, full-bleed inside the card. The negative margin cancels the
   card's own 2rem padding so the band meets the border rather than floating in
   a white gutter - and the top edge lands on the card's existing 3px forest
   rule, which is now the same colour continuing.

   The mark is gold on forest because that is the asset as drawn: gold type on a
   light ground fails contrast, which is why this application's rule is that
   gold fills and rules but never spells on light. The band gives it the ground
   it was made for. */
.card-brand {
  margin: -2rem -2rem 1.7rem;
  padding: 1.35rem 2rem;
  background: var(--forest);
  text-align: center;
}

.card-brand-mark {
  display: inline-block;
  width: 100%;
  max-width: 13.5rem;
  height: auto;
}

/* --------------------------------------------------------------- type ---- */
h1 {
  font-family: var(--display);
  font-weight: 500;
  font-size: 1.9rem;
  line-height: 1.15;
  letter-spacing: -0.01em;
  margin: 0 0 0.4rem;
  color: var(--forest);
  text-wrap: balance;
}

h2 {
  font-family: var(--display);
  font-weight: 500;
  font-size: 1.35rem;
  margin: 1.8rem 0 0.5rem;
  color: var(--forest);
}

p { margin: 0 0 1rem; }
p.lede { color: var(--ink-soft); }
p:last-child { margin-bottom: 0; }

.eyebrow {
  font-family: var(--mono);
  font-size: 0.68rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  /* gold-text, not gold: this is type on a light ground */
  color: var(--gold-text);
  margin: 0 0 0.9rem;
}

.fine {
  font-size: 0.85rem;
  color: var(--ink-soft);
  margin-top: 1.2rem;
}

/* A LINK INSIDE BODY COPY. There was no rule for one until the sign-in card
   needed "set or reset your password": everything else on these pages is a
   button, so the first inline link rendered in browser-default blue - the one
   colour this brand does not have. */
.lede a, .fine a, .flash a {
  color: var(--forest);
  text-decoration: underline;
  text-underline-offset: 2px;
}

.lede a:hover, .fine a:hover, .flash a:hover { color: var(--forest-deep); }

/* -------------------------------------------------------------- forms ---- */
label {
  display: block;
  font-size: 0.82rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  margin-bottom: 0.35rem;
  color: var(--ink);
}

input[type="text"],
input[type="email"],
input[type="password"] {
  width: 100%;
  padding: 0.7rem 0.8rem;
  font-family: var(--sans);
  font-size: 1rem;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 0;
}

/* A HINT MUST NOT READ AS A VALUE. Nothing set these, so they took the
   browser's own grey - which on Chrome is dark enough that "28,000" in a price
   box and "Filled in from the catalog..." in the description both looked like
   text somebody had already put there. Lighter than the mute ink, and the
   explicit opacity is for Firefox, which applies one of its own on top. */
::placeholder {
  color: var(--ink-mute);
  opacity: 0.55;
}

input:focus-visible,
textarea:focus-visible,
button:focus-visible,
a:focus-visible {
  outline: 2px solid var(--forest);
  outline-offset: 2px;
}

/* Stacked fields. A single-field form never needed this - the button carries
   its own top margin - but the password forms put three in a column, and
   without it the label of the next field sits on the box above it. */
input + label { margin-top: 1.1rem; }

/* The six-digit code. Monospace and spaced, so a transposed pair is visible. */
input.code {
  font-family: var(--mono);
  font-size: 1.5rem;
  letter-spacing: 0.4em;
  text-align: center;
  padding: 0.7rem 0.4rem;
}

button, .button {
  display: inline-block;
  width: 100%;
  margin-top: 1.2rem;
  padding: 0.75rem 1rem;
  font-family: var(--sans);
  font-size: 0.95rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--cream);
  background: var(--forest);
  border: 1px solid var(--forest);
  border-radius: 0;
  cursor: pointer;
  text-align: center;
  text-decoration: none;
}

/* THE SEVENTH TIME THIS BUG HAS BEEN PATCHED, so this time at the root. The
   `display: inline-block` above is an author rule and the browser's
   `[hidden] { display: none }` is not, so ours wins on specificity and every
   `button.hidden = true` in this application silently does nothing: the
   attribute is set, the accessibility tree says gone, and the button is still
   on screen and still clickable. `.cal-tab`, `.stale`, `.fly-ghl`,
   `.line-form-note`, `.form-error`, `.progress` and `.btn-ghost` each carry
   their own one-line patch for exactly this. `.btn-primary` never got one, so
   the wizard's Send this button sat under the thank-you message.

   Restoring the browser's behaviour here retires the class. Nothing can want a
   hidden button visible. */
button[hidden], .button[hidden] { display: none; }

/* `label { display: block }` further down has the same problem waiting in it.
   Nothing hides a label with the attribute today; this is here so that the
   first person who tries does not spend an afternoon on it. */
label[hidden] { display: none; }

button:hover, .button:hover { background: var(--forest-deep); }

.button-quiet {
  color: var(--forest);
  background: var(--paper);
  border-color: var(--edge);
}
.button-quiet:hover { background: var(--cream); }

/* A LITERAL IN RUNNING TEXT - a password, a path, a field key. There was no
   rule for `code` at all, so the one place that needed it (the development
   password on the sign-in page) rendered as ordinary prose, which is the one
   thing a literal must not do: the reader has to be able to see where it
   starts and stops. */
code {
  font-family: var(--mono);
  font-size: 0.92em;
  padding: 0.1em 0.35em;
  background: var(--cream);
  border: 1px solid var(--rule);
  color: var(--ink);
}

/* The clinical schedule in the drawer. */
.appt-list { list-style: none; margin: 0 0 0.9rem; padding: 0; }

.appt {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 0.15rem 0.6rem;
  padding: 0.45rem 0.6rem;
  margin-bottom: 0.35rem;
  border-left: 3px solid var(--sand-deep);
  background: var(--cream-warm);
  font-size: 0.82rem;
}

.appt-when {
  grid-column: 1;
  font-family: var(--mono);
  font-size: 0.7rem;
  color: var(--ink-mute);
}

.appt-title { grid-column: 1; font-weight: 600; color: var(--ink); }
.appt-detail { grid-column: 1; color: var(--ink-soft); font-size: 0.75rem; }

.appt-drop {
  grid-column: 2;
  grid-row: 1 / span 3;
  width: auto;
  margin: 0;
  align-self: center;
  padding: 0.2rem 0.5rem;
  font-size: 0.7rem;
  color: var(--ink-soft);
  background: transparent;
  border: 1px solid var(--rule);
}

.appt-drop:hover { color: var(--fail); border-color: var(--fail); background: var(--fail-tint); }

/* THE QUICK ADD ROW. Date and time side by side, kind and title beneath -
   and on a narrow screen everything goes full width, because a date input
   squeezed into half a phone is a date input nobody can use. */
.appt-add {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.4rem;
  margin-bottom: 0.6rem;
}

.appt-add .edit-input:nth-child(3),
.appt-add .edit-input:nth-child(4) { grid-column: 1 / -1; }

/* Big enough to hit with a thumb. 16px on the input itself is not decoration:
   iOS zooms the whole page in on focus for anything smaller, and the drawer
   never comes back the same size. */
.appt-add .edit-input,
.appt-paste textarea {
  min-height: 2.6rem;
  font-size: 16px;
}

@media (max-width: 34rem) {
  .appt-add { grid-template-columns: 1fr; }
}

/* In the manager there is room for the whole row on one line. Scoped to the
   panel so the drawer's own two-column version is untouched - and behind a
   width query, because the panel is full screen on a phone and a phone has no
   more room here than the drawer did. */
@media (min-width: 44rem) {
  .pv .appt-add { grid-template-columns: 10rem 7.5rem 9rem 1fr; }
  .pv .appt-add .edit-input:nth-child(3),
  .pv .appt-add .edit-input:nth-child(4) { grid-column: auto; }
}

/* A label between the list and the editor in the schedule manager. A `div`,
   not a heading: the panel's name comes from its `aria-label`, and an <h4>
   with no <h3> above it inside a dialog is a worse outline than none. */
.appt-heading { margin: 1.4rem 0 0.5rem; }

/* Pasting a whole day. */
.appt-paste textarea { width: 100%; font-family: var(--mono); line-height: 1.6; }

.appt-preview { margin: 0.6rem 0; }

.appt-prow {
  display: grid;
  grid-template-columns: 4.5rem 1fr 7rem;
  gap: 0.5rem;
  align-items: center;
  padding: 0.3rem 0;
  border-bottom: 1px solid var(--rule);
  font-size: 0.8rem;
}

.appt-prow .edit-input { min-height: 2rem; font-size: 0.78rem; padding: 0.2rem 0.3rem; }

/* THE PREVIEW SHARES `.appt-when` AND `.appt-title` WITH THE LIST ABOVE, and
   those carry `grid-column: 1` for the stacked list layout. Unset here, the
   title landed in the 4.5rem time column and wrapped one word per line. Shared
   class names are cheap right up until two grids disagree about them. */
.appt-prow .appt-when { grid-column: 1; }
.appt-prow .appt-title { grid-column: 2; }

@media (max-width: 34rem) {
  .appt-prow { grid-template-columns: 5rem 1fr; }
  .appt-prow .edit-input { grid-column: 1 / -1; }
}

/* THE SAME COLOURS AS THE CALENDAR, so a row means the same thing in both
   places. Colour never carries it alone - the kind is written out on the
   calendar and chosen from a named list here. */
.appt.kind-pickup    { border-left-color: var(--gold); }
.appt.kind-consult   { border-left-color: var(--forest); }
.appt.kind-treatment { border-left-color: var(--fail); }
.appt.kind-hospital  { border-left-color: var(--watch); }
.appt.kind-rest      { border-left-color: var(--sage); }

/* Correcting a patient's details in the drawer. */
.edit-row { margin-bottom: 0.7rem; }

.edit-label {
  display: block;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--ink-soft);
  margin-bottom: 0.25rem;
}

.edit-input {
  width: 100%;
  padding: 0.5rem 0.6rem;
  font-family: var(--sans);
  font-size: 0.9rem;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 0;
}

/* THE WARNING CARRIES WEIGHT ON PURPOSE. It is the whole reason a local-only
   edit is safe to offer: somebody who does not read it will correct an address
   here and believe mail now goes to it. */
.edit-warn {
  border-left: 3px solid var(--watch);
  background: var(--watch-tint);
  padding: 0.6rem 0.75rem;
  margin-top: 0.9rem;
  color: var(--ink-soft);
}

/* The keyboard route to the same thing, in the drawer. */
.stage-row {
  display: flex;
  gap: 0.6rem;
  align-items: center;
}

.stage-row .stage-pick { flex: 1 1 auto; }

.stage-row button { width: auto; margin-top: 0; flex: 0 0 auto; }

/* -------------------------------------------------------- moving a card --- */
/* Press and hold a card and it lifts; the ghost follows the pointer and the
   column under it lights up. See move.js for why it is a hold rather than a
   plain drag - on a phone the cards are the column, and a plain drag has to
   stay available for scrolling. */

/* The card left behind. Kept in place rather than removed so the column does
   not reflow under the finger mid-gesture, which makes the drop land somewhere
   the person did not aim at. */
.card.is-lifted {
  opacity: 0.35;
  filter: grayscale(1);
}

/* What actually follows the pointer. `pointer-events: none` is load-bearing:
   elementFromPoint has to read the column underneath, not this. */
.card-ghost {
  position: fixed;
  z-index: 60;
  margin: 0;
  pointer-events: none;
  box-shadow: 0 10px 30px rgba(10, 65, 42, .3);
  transform: rotate(-1.5deg);
  opacity: 0.95;
}

/* Where it would land. A tinted body and a solid edge, because a border alone
   is easy to miss under a finger that is covering half the column. */
.col.is-target .col-body {
  background: var(--done-tint);
  outline: 2px solid var(--forest);
  outline-offset: -2px;
}

.col.is-saving .col-body { opacity: 0.6; }

/* No text selection while carrying, and no long-press menu on iOS - both turn
   a move into a mess halfway through. */
body.is-moving {
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
}

@media (prefers-reduced-motion: reduce) {
  .card-ghost { transform: none; }
}

/* -------------------------------------------------------- the calendar --- */
.cal-head {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  align-items: flex-end;
  justify-content: space-between;
  margin-bottom: 1.4rem;
}

/* THE VIEW CONTROLS, WHICH WERE NEVER STYLED AT ALL.
   -------------------------------------------------------------------------
   They were written as `class="tab"` - and `.tab` is defined in
   `board-layout.css`, which only `board.html` loads. So on the calendar they
   were browser-default links: blue, underlined, purple once visited, in a
   page that has no other blue in it. It rendered, it worked, and it looked
   like a 1998 index page sitting under a serif heading.

   A SEGMENTED CONTROL RATHER THAN THREE BUTTONS, because Month, Week and Day
   are one question with three answers - and the filled one is the answer, so
   which view you are in is readable without comparing the three.

   FOREST FILL, NOT GOLD. The board's tabs fill with gold, and they can: they
   sit on a forest ground. This bar is on cream, where gold as type is refused
   throughout this application - `test_gold_never_spells_on_a_light_ground`
   would fail, and it is right to. */
.cal-controls {
  display: flex;
  gap: 0.75rem;
  flex-wrap: wrap;
  align-items: center;
}

.cal-seg {
  display: flex;
  border: 1px solid var(--sand);
  background: var(--white);
}

.cal-tab {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 2.1rem;
  padding: 0 0.85rem;
  border-left: 1px solid var(--sand);
  font-family: var(--sans);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1.4px;
  text-transform: uppercase;
  color: var(--ink-mute);
  text-decoration: none;
  white-space: nowrap;
}

/* The group draws the outer border; the children draw the dividers. Without
   this the first child doubles the left edge and the strip sits a pixel out of
   line with everything under it. */
.cal-tab:first-child { border-left: 0; }

/* A BUTTON IN THE STRIP IS A TAB LIKE THE LINKS BESIDE IT (21 October 2026).
   "Go" and "Book a call" are <button>s, and the application's `button` rule
   gives every button a 1.2rem top margin, the full width and a forest border
   on four sides - so on the Sales Calendar they stood taller than the strip,
   with an empty band above "Book a call". */
button.cal-tab {
  width: auto;
  margin: 0;
  border: 0;
  border-left: 1px solid var(--sand);
  line-height: 1;
}
button.cal-tab:first-child { border-left: 0; }

.cal-tab:hover { background: var(--cream); color: var(--ink); }

/* Inset, because an outset ring on a segmented control is drawn over its
   neighbour and reads as the wrong item being focused. */
.cal-tab:focus-visible { outline: 2px solid var(--gold); outline-offset: -2px; }

.cal-tab.on,
.cal-tab.on:hover { background: var(--forest); color: var(--cream); }

/* One glyph rather than a word, so the box sets the width and the tracking
   that spaces capitals is turned off. */
.cal-arrow { padding: 0 0.75rem; font-size: 15px; letter-spacing: 0; }

/* THE ATTRIBUTE HAS TO BEAT THE DISPLAY RULE. `display: inline-flex` above is
   an author rule and the user agent's `[hidden] { display: none }` is not, so
   `go.hidden = true` in calendar.js would set the attribute and change nothing
   on screen. This is the same fault that left the board's "has changed" notice
   permanently visible. */
.cal-tab[hidden] { display: none; }

/* THE OTHER END OF THE SAME ROW. One rule rather than a second flex container
   or an absolute position: the strip stays one row, so the buttons wrap under
   the filters on a narrow screen instead of overlapping them. */
.cal-right { margin-left: auto; }

/* WHOSE CLOCK A COLUMN OF TIMES IS ON, in the column's own label.

   It was on every row first, which repeats three letters down a table to
   answer a question asked once; then it was a footnote under the table, which
   is where somebody's eye has already left. The header is the one place that
   is said once AND next to the numbers.

   Quieter than the label it qualifies - it is an aside, not a second heading -
   and `localtime.js` fills it with whatever this browser calls its own zone.
   The header's `title` says what it means; the timestamps keep the full zone
   name in theirs. */
.zone-tag {
  font-weight: 400;
  letter-spacing: 0.04em;
  color: var(--ink-mute);
}

/* A STANDALONE STRIP NEEDS ITS OWN GAP, and only a standalone one.
   `.cal-controls` sits inside `.cal-head` on the Treatment Calendar, where the
   head already carries the space below it - a margin on the strip itself would
   be added inside that flex row and open the header up. On the Protocols
   screen there is no head: the strip is a direct child of the page and its
   bottom edge was touching the panel underneath.

   The child combinator is what tells the two apart, so neither needs a class
   it would only wear to say where it is. */
.page > .cal-controls { margin-bottom: 1.5rem; }

/* THE ONE ACTION ON THE PAGE, so it is filled rather than outlined - the same
   forest the current tab wears, because on this strip "filled" already means
   "this is the one". Gold would be the other candidate and is refused: this
   bar sits on cream, where gold as type fails its own contrast rule. */
.cal-tab.cal-go,
.cal-tab.cal-go:hover { background: var(--forest); color: var(--cream); }

/* Jumping to a date. The input carries no border of its own - the group draws
   it, the same as every other item in the strip - and inherits the strip's
   type so a native date field does not arrive in the browser's own font. */
.cal-jump { align-items: stretch; }

.cal-date {
  border: 0;
  background: transparent;
  padding: 0 0.6rem;
  font-family: var(--sans);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--ink);
  min-height: 2.1rem;
}

.cal-date:focus-visible { outline: 2px solid var(--gold); outline-offset: -2px; }

/* Sample data is a different KIND of thing from the view switcher - it changes
   what the calendar is showing, not how. It keeps the amber every sample
   affordance in this application uses, and stands on its own. */
.cal-sample { color: var(--watch); }
.cal-sample:hover { background: var(--watch-tint); color: var(--watch); }

/* The grid is seven columns whatever the view - a week is a month with one
   row, which keeps one set of markup and one set of rules rather than two that
   drift apart. */
.cal-grid {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 1px;
  background: var(--rule);
  border: 1px solid var(--rule);
}

.cal-dayname {
  background: var(--cream-alt);
  padding: 0.45rem 0.6rem;
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-mute);
}

.cal-cell {
  background: var(--paper);
  min-height: 7rem;
  padding: 0.4rem;
}

.cal-grid-week .cal-cell { min-height: 16rem; }

/* The neighbouring month's days. Dimmed rather than hidden: a trip arriving on
   the 31st of the month before is still a trip somebody is about to work. */
.cal-out { background: var(--cream-warm); }
.cal-out .cal-num { color: var(--ink-mute); }

.cal-today { box-shadow: inset 0 0 0 2px var(--forest); }

.cal-num {
  display: inline-block;
  font-family: var(--mono);
  font-size: 0.75rem;
  color: var(--ink-soft);
  text-decoration: none;
  margin-bottom: 0.3rem;
}

.cal-num:hover { color: var(--forest); text-decoration: underline; }

/* The sprite is a definitions block, not a picture. Without this it renders as
   a stack of glyphs at the top of the page, and it cannot be hidden with an
   inline style under `style-src 'self'`. Same rule `.sb-sprite` carries. */
.cal-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

.cal-item {
  display: block;
  margin-bottom: 0.25rem;
  padding: 0.25rem 0.35rem;
  font-size: 0.72rem;
  line-height: 1.35;
  border-left: 3px solid var(--sand-deep);
  background: var(--cream-warm);
}

/* THE SAME ANSWER THE BORDER COLOUR GIVES, IN A SECOND CHANNEL. The note below
   says colour alone means nothing to somebody who cannot separate these hues,
   which is why the label is always written out - a shape is the cheaper half
   of that promise, read before the words on a grid of forty-two cells.
   `currentColor` so it takes the row's own ink rather than needing a rule per
   kind. */
.cal-icon {
  width: 11px;
  height: 11px;
  vertical-align: -1px;
  margin-right: 3px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
  color: var(--ink-mute);
}

/* WHICH LEG OF THE TRIP, BY COLOUR AND POSITION BOTH. Colour alone would mean
   nothing to somebody who cannot separate these hues, so the label is always
   written out beside it. */
.cal-item.kind-arrival  { border-left-color: var(--forest); }
.cal-item.kind-checkin  { border-left-color: var(--gold); }
.cal-item.kind-checkout { border-left-color: var(--sand-deep); }
.cal-item.kind-departure{ border-left-color: var(--sage); }

/* The clinical kinds, matching the drawer exactly. */
.cal-item.kind-pickup    { border-left-color: var(--gold); }
.cal-item.kind-consult   { border-left-color: var(--forest); }
.cal-item.kind-treatment { border-left-color: var(--fail); }
.cal-item.kind-hospital  { border-left-color: var(--watch); }
.cal-item.kind-rest      { border-left-color: var(--sage); }

.cal-time {
  font-family: var(--mono);
  font-size: 0.68rem;
  color: var(--ink-mute);
  margin-right: 0.3rem;
}

/* A LINK THAT DOES NOT SHOUT. It is now the way to that patient's printable
   week, and an underlined blue name in every cell would turn a calendar into a
   list of links - so it keeps the weight it had and earns its underline on
   hover, which is where somebody is already pointing when they wonder. */
.cal-who {
  font-weight: 600;
  color: var(--ink);
  text-decoration: none;
}
.cal-who:hover { color: var(--forest); text-decoration: underline; }
.cal-who:focus-visible { outline: 2px solid var(--gold); outline-offset: 1px; }
.cal-what { display: block; color: var(--ink-soft); }
.cal-detail { display: block; color: var(--ink-mute); font-size: 0.68rem; }

.cal-day .cal-list { list-style: none; margin: 0; padding: 0; }

.cal-day .cal-item {
  font-size: 0.9rem;
  padding: 0.7rem 0.9rem;
  border: 1px solid var(--rule);
  border-left-width: 4px;
}

.cal-none { color: var(--ink-mute); padding: 2rem 0; }

/* The trips it could not place. Above the calendar, in the warning colour,
   because a patient missing from a calendar somebody trusts is the failure
   this screen most needs to avoid. */
.cal-unread {
  border-left: 3px solid var(--watch);
  background: var(--watch-tint);
  padding: 0.9rem 1rem;
  margin-bottom: 1.4rem;
}

.cal-unread h2 { margin: 0 0 0.3rem; font-size: 1rem; }
.cal-bad { display: block; font-family: var(--mono); font-size: 0.75rem; color: var(--ink-soft); }
.cal-foot { margin-top: 1.2rem; }

@media (max-width: 60rem) {
  .cal-grid { font-size: 0.9em; }
  .cal-cell { min-height: 5.5rem; }
}

/* ------------------------------------------------------------- stale ------ */
/* Shown when the ten-second poll finds the board has moved and the person is
   busy - a drawer open, or typing. Bottom left, out of the way of the drawer
   on the right and the masthead above. It is a notice, not a dialog: nothing
   is blocked and nothing takes focus. */
.stale {
  position: fixed;
  left: 1.25rem;
  bottom: 1.25rem;
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.6rem 0.7rem 0.6rem 1rem;
  background: var(--forest);
  color: var(--cream);
  border: 1px solid var(--forest-deep);
  box-shadow: 0 6px 24px rgba(10, 65, 42, .28);
  font-size: 0.85rem;
}

/* AN AUTHOR `display` BEATS THE BROWSER'S `[hidden] { display: none }`, and
   this rule sets one - so `stale.hidden = true` set the attribute, changed
   nothing, and the notice sat on screen from the moment the page loaded. It was
   reported as "this is blocking the settings", which it was, and moving it
   clear of the rail treated the symptom: it should not have been visible at
   all until the board actually moved. `.sb-scrim`, `.sb-tip`, `.sb-fly`,
   `.fly-ghl` and `.usearch-panel` already carry this guard; `test_a11y` now
   requires it of anything the scripts toggle. */
.stale[hidden] { display: none; }

.stale-go {
  width: auto;
  margin: 0;
  padding: 0.35rem 0.8rem;
  font-size: 0.8rem;
  color: var(--forest);
  background: var(--cream);
  border-color: var(--cream);
}

.stale-go:hover { background: var(--white); }

@media (max-width: 44rem) {
  .stale { left: 0.75rem; right: 0.75rem; bottom: 0.75rem; }
}

/* ---------------------------------------------------------------- qr ------ */
/* The enrolment code. White ground and a real quiet zone are not decoration -
   a scanner needs both to find the code, and the quiet zone is drawn into the
   SVG itself so that no container style can take it away. */
.qr-wrap {
  display: flex;
  justify-content: center;
  margin: 1.2rem 0;
}

.qr {
  width: 100%;
  max-width: 15rem;
  height: auto;
  background: var(--white);
  color: var(--ink);
  /* Crisp edges. Smoothing a 45-module code up to 240px turns every boundary
     into a grey ramp, which is exactly what a scanner has to threshold back
     out again. */
  image-rendering: pixelated;
  shape-rendering: crispEdges;
}

/* The typed-key fallback, folded away. It is the second-best route for almost
   everybody, so it should not be the first thing on the page - but it is the
   only route on a desktop authenticator with no camera. */
.by-hand > summary {
  cursor: pointer;
  font-size: 0.85rem;
  color: var(--ink-soft);
  margin-bottom: 0.6rem;
}

/* --------------------------------------------------------------- sso ------ */
/* The Google button. Outlined rather than solid so the page still has exactly
   one primary action - "Sign in" - and this reads as the alternative route it
   is, not as a competing call to action. */
.button-sso {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  color: var(--ink);
  background: var(--paper);
  border-color: var(--edge);
  margin-top: 0;
}

.button-sso:hover { background: var(--cream); }

.sso-mark { flex: 0 0 auto; }

/* A rule with a word sitting in it. The line is drawn by the pseudo-elements
   either side rather than by a border on the paragraph, so the gap around the
   word is the word's own width and stays right at any font size. */
.or {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin: 1.2rem 0;
  font-size: 0.78rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-mute);
}

.or::before, .or::after {
  content: "";
  flex: 1 1 auto;
  height: 1px;
  background: var(--rule);
}

/* -------------------------------------------------- staff notes ---------- */
/* Notes on PATIENT pages that only staff may read. See
   templates/public/_staff.html for why the gate is in the macro.

   Deliberately unlike the page around it - mono, a dashed rule, no gold, no
   serif. A note that wears the page's own clothes is one a person skims past
   as content; this one has to read as scaffolding at a glance, because the
   whole risk it guards against is somebody not noticing which audience is
   looking. It is defined here rather than per page so every internal note on
   every patient page looks the same. */
.staff-note { padding: 32px 56px 0; }

.staff-note-inner {
  border: 1px dashed var(--sand-deep);
  background: var(--cream-warm);
  padding: 20px 22px;
}

.staff-note-tag {
  margin: 0 0 10px;
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: 1.6px;
  text-transform: uppercase;
  color: var(--ink-mute);
}

.staff-note-head {
  margin: 0 0 8px;
  font-family: var(--sans);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--ink);
}

.staff-note-body {
  font-family: var(--sans);
  font-size: 13px;
  line-height: 1.75;
  color: var(--ink-soft);
}

.staff-note-body :last-child { margin-bottom: 0; }

@media (max-width: 640px) { .staff-note { padding: 24px 24px 0; } }

/* NEVER ON PAPER. A concierge printing a page to hand over should not hand
   over the production notes with it. */
/* The performance monitor joins it. A concierge printing a page to hand to a
   patient should not hand over the request timings either, and the panel sits
   at the END of the document, where a printer will happily give it a sheet of
   its own. */
@media print { .staff-note, .perf { display: none; } }

/* -------------------------------------------------------------- flash ---- */
.flash {
  padding: 0.7rem 0.9rem;
  margin-bottom: 1.2rem;
  font-size: 0.9rem;
  border-left: 3px solid var(--forest);
  background: var(--cream);
}

.flash-error {
  border-left-color: var(--error);
  background: var(--error-bg);
  color: var(--error);
}

/* ------------------------------------------------------------ secrets ---- */
.secret {
  font-family: var(--mono);
  font-size: 0.95rem;
  letter-spacing: 0.06em;
  word-break: break-all;
  padding: 0.75rem;
  background: var(--cream);
  border: 1px solid var(--rule);
}

.codes {
  list-style: none;
  padding: 0.9rem;
  margin: 0 0 1rem;
  background: var(--cream);
  border: 1px solid var(--rule);
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0.4rem 1rem;
}

.codes li {
  font-family: var(--mono);
  font-size: 0.95rem;
  letter-spacing: 0.04em;
}

.warn {
  border-left: 3px solid var(--gold);
  background: var(--cream);
  padding: 0.75rem 0.9rem;
  font-size: 0.88rem;
  margin-bottom: 1.2rem;
}

/* --------------------------------------------------------------- shell --- */
.masthead {
  background: var(--forest);
  color: var(--cream);
  /* a gold rule: a fill, which is what gold is for */
  border-bottom: 2px solid var(--gold);
}

.masthead-inner {
  max-width: 72rem;
  margin: 0 auto;
  padding: 0.9rem 1.5rem;
  display: flex;
  align-items: center;
  gap: 1.5rem;
  flex-wrap: wrap;
}

/* WHERE THE WORDMARK WAS. The bar carried "RejuvStem Concierge" and a Board /
   People nav; the rail on the left already has the brand mark and one link per
   board somebody may open, so the bar was a third voice saying what had
   already been said twice - and never said which page you were on.

   Same serif and size the wordmark had: this is still the top line of the
   page, and the only thing that changed is that it is now true of the page
   rather than of the product. `margin: 0` because it is an h1 and the page
   rule would otherwise push the forest band open by its own bottom margin. */
.mast-title {
  font-family: var(--display);
  font-size: 1.2rem;
  font-weight: 500;
  letter-spacing: 0.01em;
  color: var(--cream);
  margin: 0;
  margin-right: auto;
}

.whoami {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  color: var(--sand);
}

.logout {
  width: auto;
  margin: 0;
  padding: 0.35rem 0.7rem;
  font-size: 0.78rem;
  color: var(--sand);
  background: transparent;
  border: 1px solid var(--forest-soft);
}
.logout:hover { background: var(--forest-deep); color: var(--cream); }

main.page {
  max-width: 72rem;
  margin: 0 auto;
  padding: 2rem 1.5rem 4rem;
}
/* A PAGE THAT NEEDS THE WINDOW: a wide table whose last column is its
   actions. Set with `{% block page_class %} page-wide{% endblock %}`. */
main.page.page-wide { max-width: none; }

.empty {
  background: var(--paper);
  border: 1px solid var(--rule);
  padding: 3rem 2rem;
  text-align: center;
  color: var(--ink-soft);
}

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}


/* ------------------------------------------------------------- panels ---- */
.panel {
  background: var(--paper);
  border: 1px solid var(--rule);
  padding: 1.5rem;
  margin-bottom: 1.5rem;
}

.panel h2 { margin-top: 0; }

/* ------------------------------------------------------------- tables ---- */
.table-scroll { overflow-x: auto; }

table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
  min-width: 46rem;
}

th {
  text-align: left;
  font-size: 0.68rem;
  font-family: var(--mono);
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-soft);
  padding: 0.6rem 0.8rem;
  border-bottom: 1px solid var(--rule);
  white-space: nowrap;
}

td {
  padding: 0.8rem;
  border-bottom: 1px solid var(--rule);
  vertical-align: top;
}

tr:last-child td { border-bottom: none; }

tr.row-off { opacity: 0.55; }

td.num { font-family: var(--mono); font-size: 0.82rem; white-space: nowrap; }

.name { display: block; font-weight: 600; }

.sub {
  display: block;
  font-size: 0.78rem;
  color: var(--ink-soft);
}

/* A CELL IS NOT A SPAN. `.sub` is a utility for quiet text inside a line, and
   `display: block` is what makes it sit on its own; on a <td> the same
   declaration takes the cell out of the table's column model altogether. The
   symptom is not subtle once you know it - the cell stops lining up with the
   column above it and the browser parks the leftover width beside it - but from
   the outside it reads as "the table has a huge gap in it", which is a layout
   problem nobody thinks to solve by looking at a text colour.

   Two tables were wearing it: the facts list on the admin page and the Detail
   column of the intake log. Fixed here rather than by renaming the class in
   both templates, so the third one nobody has written yet is fixed too. */
td.sub, th.sub { display: table-cell; }

/* -------------------------------------------------------------- pills ---- */
.pill {
  display: inline-block;
  font-family: var(--mono);
  font-size: 0.62rem;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 0.15rem 0.4rem;
  margin-top: 0.3rem;
  color: var(--ink-soft);
  background: var(--cream);
  border: 1px solid var(--rule);
}

.pill-ok   { color: var(--ok);        border-color: var(--ok); }
/* gold-text, never gold: this is type on a light ground */
.pill-warn { color: var(--gold-text); border-color: var(--gold); }
.pill-off  { color: var(--error);     border-color: var(--error); }

/* -------------------------------------------------------------- forms ---- */
.row-form {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  align-items: flex-end;
}

.field { flex: 1 1 12rem; }

/* A ROW THAT IS ACTUALLY A ROW. Left to wrap on its own, a run of fields
   regroups with the window: the record read as four-then-four here and
   five-then-three on a wider screen, which is how Condition ended up beside
   Nights on somebody else's laptop. Grouping them explicitly means Protocol
   name / Type / Treatment / Condition stay one thought and the trip stays
   another, at every width that fits them.

   Below that width they still wrap - within their own row, so a narrow window
   splits a group rather than mixing two. */
.form-row {
  flex: 1 1 100%;
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  align-items: flex-end;
}
.field-narrow { flex: 0 0 auto; }

/* The one that holds the longest words on the form. "Accommodation - Renaissance
   Cancun" and "RegenX Cells - Facial Aesthetics w/ Hydrofacial" are real
   catalog names, and a box that shows half of one is a box somebody has to
   scroll inside to check what they picked. */
.field-wide { flex: 3 1 24rem; }

.field button { margin-top: 0; width: auto; }

/* THE DECIDING FIELDS, and the button that commits them. Wil, 6 September
   2026: the sales rep and the two prices are not informational, so they
   should not look like the eleven boxes above them that are.

   `flex: 1 1 100%` makes the strip its own line at the end of the wrapping
   row rather than something that floats to the right until the window
   narrows and it lands in the middle of the address. Tinted ground and a gold
   hairline, which is this application's "read this" without being an alarm. */
.field-decide {
  flex: 1 1 100%;
  display: flex;
  flex-wrap: wrap;
  /* ROOMIER THAN THE ROWS ABOVE, on purpose. Three fields crowded against the
     Save button read as an afterthought bolted to it; the whole point of the
     strip is that these are the deliberate values, so they get the space that
     says so. */
  gap: 1.75rem;
  align-items: flex-end;
  /* SPREAD, NOT HUDDLED AT THE RIGHT. Pushed to one end the three fields left
     a third of the strip empty and read as an afterthought stuck to the
     button; across the full width each one gets its own space and Save sits at
     the end of a sentence rather than on top of the last figure. */
  justify-content: space-between;
  margin-top: 0.35rem;
  padding: 1.1rem 1.25rem;
  background: var(--cream-warm);
  /* Square, like every other edge in this application. */
  border: 1px solid var(--gold-light);
}

.field-decide .field { flex: 0 1 13rem; }

/* Save takes the width of its own word. WITHOUT THIS IT DOES NOT: the rule
   above matches it too - it is a `.field` - so it inherited a 13rem basis, the
   four of them no longer fitted the line, and the button wrapped underneath
   the row it belongs to. Measured, not guessed: the button was at x=318 on a
   second line. */
.field-decide .field-narrow { flex: 0 0 auto; margin-left: 0.75rem; }
.field-decide label { color: var(--gold-text); }

/* Money reads down the column, not across: right-aligned and tabular, so
   25,000 and 2,500 are one glance apart instead of a digit count. */
.field-money input {
  text-align: right;
  font-variant-numeric: tabular-nums;
}


/* A checkbox in a row of text fields. `flex: 0 0 auto` so it takes the width of
   its own words rather than a twelfth of the row, and the label carries the box
   so the words are part of the hit target - a 13px checkbox on its own is a
   target somebody misses on a laptop trackpad and every time on a phone. */
.field-check { flex: 0 0 auto; padding-bottom: 0.75rem; }
.field-check label {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  margin: 0;
  font-size: 0.9rem;
  cursor: pointer;
}
.field-check input { width: auto; margin: 0; }

select {
  width: 100%;
  padding: 0.7rem 0.8rem;
  font-family: var(--sans);
  font-size: 1rem;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 0;
}

/* A TICK AND A WORD, ON ONE LINE. The global `label` rule is `display: block`
   at weight 600 - right for the caption above a text field, wrong for this -
   so each board name landed on its own line in bold with the box above it, in
   a table cell four words wide. These read as values in a row, not as headings
   for the boxes beside them.

   The whole label is the hit target, box and word together: a 13px checkbox on
   its own is missed on a trackpad and every time on a phone. */
.check-inline {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  margin: 0 0.7rem 0 0;
  font-size: 0.82rem;
  font-weight: 400;
  white-space: nowrap;
  cursor: pointer;
  color: var(--ink);
}
.check-inline input { width: auto; margin: 0; }

/* The caption over the pair in the Add form. A span, not a label: it names two
   controls and a `for` can only point at one. */
.check-label {
  display: block;
  font-size: 0.82rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  margin-bottom: 0.35rem;
  color: var(--ink);
}

/* The Add form's two boxes sit side by side under that caption rather than
   stacking, so the row keeps one height. */
.field-check .check-row { display: flex; gap: 0.9rem; align-items: center; }

.inline-form { display: inline-flex; gap: 0.3rem; align-items: center; }

/* THE ONE FORM THAT MAY WRAP. Two ticked words and a button is the widest cell
   in the row, and holding it on one line pushed the table past its container
   and cut the Actions column off at the right edge. The others stay on one
   line on purpose: a select with its Set button underneath reads as two
   separate controls. */
.inline-form.boards-form { flex-wrap: wrap; max-width: 12rem; }
.inline-form select { width: auto; padding: 0.3rem 0.4rem; font-size: 0.82rem; }

td.actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
}

button.mini {
  width: auto;
  margin: 0;
  padding: 0.3rem 0.6rem;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--forest);
  background: var(--paper);
  border: 1px solid var(--edge);
}

button.mini:hover { background: var(--cream); }

button.mini-danger { color: var(--error); border-color: var(--error); }
button.mini-danger:hover { background: var(--error-bg); }

/* ---------------------------------------------------------------- log ---- */
.log { list-style: none; padding: 0; margin: 0; }

.log li {
  padding: 0.5rem 0;
  border-bottom: 1px solid var(--rule);
  font-size: 0.85rem;
}

.log li:last-child { border-bottom: none; }

.log .when {
  font-family: var(--mono);
  font-size: 0.72rem;
  color: var(--ink-soft);
  margin-right: 0.6rem;
}

.flash-ok { border-left-color: var(--ok); }


/* ====================================================================== *
 * DEVELOPMENT CHROME                                                     *
 *                                                                        *
 * Two signals: a red favicon in the tab strip and a red banner at the top *
 * of the page. Both come from configuration rather than from the request, *
 * so a production box cannot be talked into wearing them. The page ground *
 * itself stays the same as live - the banner carries the warning, and     *
 * tinting every surface behind it only made the app harder to read.       *
 * ====================================================================== */

.devbar {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem 1rem;
  padding: 0.45rem 1.5rem;
  background: #C0281F;
  color: #FBEEEC;
  font-family: var(--mono);
  font-size: 0.72rem;
  line-height: 1.5;
  letter-spacing: 0.04em;
  border-bottom: 2px solid #8E3320;
}

.devbar-tag {
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

.devbar-text { opacity: 0.92; }

.devbar-host {
  margin-left: auto;
  opacity: 0.85;
  white-space: nowrap;
}

/* The banner is a block above the centred sign-in card, so the page needs to
   be a column that can give the remaining height to the card. Without this
   `.centred`'s min-height:100% plus the banner overflows the viewport and the
   card sits slightly off-centre with a scrollbar for no reason. */
body { display: flex; flex-direction: column; min-height: 100vh; }
.centred { flex: 1 1 auto; }
main.page { width: 100%; }

/* ====================================================================== *
 * THE BOARD                                                              *
 *                                                                        *
 * A list, not columns. Fourteen columns on a laptop gives each one about  *
 * eighty pixels, which fits a number and nothing a person can read. The   *
 * stage is a label on the card instead, and the list is sorted by the     *
 * rule in stages.py: attention first, then longest waiting.               *
 *                                                                        *
 * The left stripe carries the kind. It is the only thing on the card      *
 * doing colour-as-meaning, so it gets a shape too (width) rather than     *
 * relying on hue alone.                                                   *
 * ====================================================================== */

.board-head { margin-bottom: 1.2rem; }

.filters { display: flex; gap: 0.5rem; flex-wrap: wrap; margin-bottom: 1.5rem; }

.chip {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0.4rem 0.8rem;
  color: var(--ink-soft);
  background: var(--paper);
  border: 1px solid var(--rule);
  text-decoration: none;
}
.chip:hover { border-color: var(--forest); color: var(--forest); }
.chip-on { color: var(--cream); background: var(--forest); border-color: var(--forest); }

/* ---------------------------------------------------------------- cards --- */
.cards { list-style: none; padding: 0; margin: 0; display: grid; gap: 0.6rem; }

.card-item {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto auto;
  gap: 1.2rem;
  align-items: start;
  padding: 1rem 1.2rem;
  background: var(--paper);
  border: 1px solid var(--rule);
  border-left: 4px solid var(--rule);
}

.card-item.kind-attention { border-left-color: var(--error); }
.card-item.kind-action    { border-left-color: var(--forest); }
.card-item.kind-waiting   { border-left-color: var(--gold); }
.card-item.kind-done      { border-left-color: var(--sand); }

.card-item.is-overdue { background: var(--error-bg); }

.card-open {
  width: auto;
  margin: 0;
  padding: 0;
  background: none;
  border: none;
  color: var(--forest);
  font-family: var(--display);
  font-size: 1.2rem;
  font-weight: 500;
  text-align: left;
  cursor: pointer;
}
.card-open:hover .patient { border-bottom: 1px solid var(--gold); }

.stage-line { margin: 0.2rem 0 0; display: flex; gap: 0.5rem; align-items: baseline; }

.stage-num {
  font-family: var(--mono);
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.08em;
  color: var(--gold-text);
}

.stage-label { font-size: 0.92rem; font-weight: 600; }
.stage-desc { margin: 0.15rem 0 0; font-size: 0.82rem; color: var(--ink-soft); }

/* ---------------------------------------------------------------- clock --- */
.card-clock { text-align: right; white-space: nowrap; }

.days {
  font-family: var(--mono);
  font-size: 1.6rem;
  font-weight: 500;
  line-height: 1;
  color: var(--forest);
  font-variant-numeric: tabular-nums;
}
.is-overdue .days { color: var(--error); }

.days-unit {
  display: block;
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

/* ---------------------------------------------------------------- forms --- */
.card-forms { display: flex; gap: 0.3rem; }

.form-flag {
  display: grid;
  place-items: center;
  width: 1.7rem;
  height: 1.7rem;
  font-family: var(--mono);
  font-size: 0.72rem;
  font-weight: 500;
  border: 1px solid var(--rule);
}

/* A fill for received, an outline for outstanding: the difference is shape as
   well as colour, so it survives a monochrome screen and colour blindness. */
.form-in  { color: var(--forest); background: var(--done-tint); border-color: var(--sage); }
.form-out { color: var(--ink-faint, var(--ink-soft)); background: transparent; }

/* ----------------------------------------------------------------- meta --- */
.card-meta {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.2rem;
  text-align: right;
  white-space: nowrap;
}

.who { font-size: 0.8rem; font-weight: 600; }
.who-none { color: var(--ink-soft); font-weight: 400; font-style: italic; }

@media (max-width: 44rem) {
  .card-item { grid-template-columns: 1fr auto; }
  .card-forms { grid-column: 1; }
  .card-meta { grid-column: 2; }
}

/* --------------------------------------------------------------- drawer --- */
/* A flyout rather than an expanding card: expanding in place costs the vertical
   space that lets somebody see the queue, and the queue is what the board is
   for. */
.drawer {
  position: fixed;
  top: 0; right: 0; bottom: 0;
  width: min(28rem, 100%);
  background: var(--paper);
  border-left: 3px solid var(--forest);
  overflow-y: auto;
  padding: 1.5rem;
  z-index: 20;
}

.scrim {
  position: fixed;
  inset: 0;
  background: rgba(10, 65, 42, 0.28);
  z-index: 10;
  /* The dimming fades with the panel it belongs to. Slightly quicker than the
     slide, so the board is already settling as the drawer lands rather than
     darkening after it. `board.js` adds `.show` a frame after clearing
     `hidden`, for the reason written there. */
  opacity: 0;
  transition: opacity .14s ease-out;
}
.scrim.show { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .scrim { transition: none; }
}

.drawer-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  border-bottom: 2px solid var(--gold);
  padding-bottom: 0.6rem;
  margin-bottom: 1rem;
}

.drawer-head h2 { margin: 0; }

.drawer-close {
  width: auto;
  margin: 0;
  padding: 0.3rem 0.7rem;
  font-size: 0.75rem;
  color: var(--forest);
  background: transparent;
  border: 1px solid var(--rule);
}

.drawer-stage { display: flex; gap: 0.5rem; align-items: baseline; margin-bottom: 0.2rem; }

.drawer-clock {
  font-family: var(--mono);
  font-size: 0.82rem;
  color: var(--ink-soft);
  margin-bottom: 1.2rem;
}
.drawer-clock.is-overdue { color: var(--error); font-weight: 600; }

.drawer-section { margin-bottom: 1.5rem; }
.drawer-section h3 {
  font-family: var(--mono);
  font-size: 0.68rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin: 0 0 0.5rem;
}

/* A section lifted onto its own ground. History earns it: it is the answer to
   "what has actually happened to this patient", and as a plain list at the foot
   of the drawer it read as the least important thing rather than the record. */
.drawer-card {
  background: var(--cream-warm);
  border: 1px solid var(--sand);
  border-left: 2px solid var(--gold);
  padding: 0.9rem 1rem 1rem;
}

.drawer-card h3 { color: var(--gold-text); }

.drawer-card .drawer-list li { border-bottom-color: var(--sand); }

.drawer-list { list-style: none; padding: 0; margin: 0; }
/* The horizontal padding is for the filled rows, and the matching negative
   margin is so it costs the others nothing: a `.form-in` fill needs room
   before its label or the text sits hard against the edge of the green, while
   every row's text stays on the same line it was on. */
.drawer-list li {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.65rem 0.6rem;
  margin: 0 -0.6rem;
  border-bottom: 1px solid var(--rule);
  font-size: 0.85rem;
}

/* A row here is two facts on one line - what it is, and where it stands - and
   at 0.45rem they packed tightly enough that the eye ran them together. The
   filled `.form-in` rows made it worse: a fill needs more room around its text
   than a bare line does. */
.drawer-list li:last-child { border-bottom: none; }

/* A send that failed or was blocked. Quieter than a delivered one, because the
   list is read to find what went out and these are the rows that did not. */
.drawer-list li.contact-off .form-name { color: var(--ink-mute); }

/* SEPARATION BETWEEN FORMS. Three answered rows ran together into one block of
   green: the fill is what says "received", and with the rows touching, three
   receipts read as one panel rather than three facts. A gap breaks it back into
   rows, so the border between them is space rather than a hairline that the
   fill swallows anyway. */
.drawer-list.forms-list {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}

.drawer-list.forms-list li { border-bottom: 0; }

.form-right { display: flex; align-items: center; gap: 0.5rem; flex: none; }

/* Sits on the fill of an answered row. It carried gold-light text for the
   forest ground it used to sit on, which is nearly invisible on the light one
   that replaced it. */
.form-view {
  width: auto;
  margin-top: 0;
  padding: 0.2rem 0.55rem;
  border: 1px solid var(--gold);
  background: transparent;
  color: var(--gold-text);
  font-family: var(--sans);
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  cursor: pointer;
}

.form-view:hover { background: var(--gold); color: var(--forest); }

/* ------------------------------------------------ the answers panel --- */
.pv-loading, .pv-empty {
  margin: 0;
  font-family: var(--sans);
  font-size: 0.85rem;
  color: var(--ink-mute);
}

.answer-list {
  display: flex;
  flex-direction: column;
  margin-bottom: 1.3rem;
  border: 1px solid var(--sand);
}

/* A SHARE OF THE WIDTH, CAPPED - not a fixed column. This was `12rem`, which
   is a sensible label width in the 660px answers panel and about sixty per cent
   of the drawer, where the same component is 380px wide. So "September 7, 2026"
   wrapped onto two lines beside the word "Date", with half the row empty.
   The value is nearly always the longer half; the label is one or two words.

   `min(42%, 12rem)` is both readings at once: the share governs where it is
   narrow, the cap where it is wide, and the wide panel keeps exactly the
   column it had. */
.answer-row {
  display: grid;
  grid-template-columns: minmax(0, min(42%, 12rem)) minmax(0, 1fr);
  gap: 1rem;
  padding: 0.6rem 0.8rem;
  border-bottom: 1px solid var(--sand);
}

.answer-row:last-child { border-bottom: 0; }
.answer-row:nth-child(even) { background: var(--cream); }

.answer-key {
  font-family: var(--sans);
  font-size: 0.78rem;
  color: var(--ink-mute);
}

.answer-value {
  font-size: 0.88rem;
  color: var(--ink);
  word-break: break-word;
}
/* The photo or passport page itself, opening full size in a new tab. */
.answer-photo img {
  display: block;
  max-width: 100%;
  width: 220px;
  max-height: 220px;
  object-fit: contain;
  border: 1px solid var(--sand);
  background: var(--white);
}
.answer-photo a { color: var(--forest); }

@media (max-width: 560px) {
  .answer-row { grid-template-columns: 1fr; gap: 0.15rem; }
}
.drawer-list .fine { margin: 0; white-space: nowrap; }
.drawer-list .note-body { white-space: pre-wrap; }

.drawer-list li.form-in  .form-name { font-weight: 600; }
.drawer-list li.form-out .form-name { color: var(--ink-soft); }

/* `.fine` CARRIES ITS OWN COLOUR. On the forest fill this row used to have,
   it put --ink-soft on dark green and the date vanished into it; on the light
   fill that replaced it the opposite rule applies, so it comes back to a dark
   value.
   --gold-text measures 4.62:1 on `--done-tint` and clears AA. --ink-mute is
   the obvious choice for a muted line and reaches only 3.86:1, so it is
   deliberately not used. */
.drawer-list li.form-in .fine { color: var(--gold-text); }

/* Quiet: it is a way to see more, not a thing to do. */
.note-more {
  width: auto;
  margin: 0.5rem 0 0;
  padding: 0.25rem 0.6rem;
  border: 1px solid var(--sand);
  background: transparent;
  color: var(--gold-text);
  font-family: var(--sans);
  font-size: 0.72rem;
  cursor: pointer;
}

.note-more:hover { border-color: var(--gold); background: var(--cream); }

.note-form { display: flex; gap: 0.4rem; margin-bottom: 0.8rem; }
.note-form textarea {
  flex: 1;
  padding: 0.5rem;
  font-family: var(--sans);
  font-size: 0.88rem;
  border: 1px solid var(--rule);
  border-radius: 0;
  resize: vertical;
}
.note-form button { margin: 0; align-self: flex-end; }

body.drawer-open { overflow: hidden; }

/* ---------------------------------------------------------- stage groups --- */
/* Every stage renders, including the empty ones. A stage that appears only when
   it holds somebody is a stage nobody remembers exists, and the shape of the
   pipeline stops being readable at a glance - which is half of what a board is
   for. "0 patients" is information. */

.stage-group { margin-bottom: 1.6rem; }

.stage-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 0.2rem 1rem;
  align-items: baseline;
  padding-bottom: 0.4rem;
  border-bottom: 2px solid var(--forest);
  margin-bottom: 0.6rem;
}

.stage-key {
  grid-row: 1;
  font-family: var(--mono);
  font-size: 0.66rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--gold-text);
}

.stage-head h2 {
  grid-row: 2;
  grid-column: 1;
  margin: 0;
  font-size: 1.2rem;
}

.stage-blurb {
  grid-row: 3;
  grid-column: 1;
  margin: 0;
  font-size: 0.82rem;
  color: var(--ink-soft);
}

.stage-count {
  grid-row: 2;
  grid-column: 2;
  font-family: var(--mono);
  font-size: 0.76rem;
  color: var(--ink-soft);
  white-space: nowrap;
}

/* An empty stage is quieter but never invisible. */
.stage-empty .stage-head { border-bottom-color: var(--rule); }
.stage-empty .stage-head h2 { color: var(--ink-soft); font-weight: 400; }

.stage-none {
  margin: 0;
  padding: 0.5rem 0 0.7rem;
  font-family: var(--mono);
  font-size: 0.76rem;
  letter-spacing: 0.06em;
  color: var(--ink-faint, var(--ink-soft));
}

/* Off the fourteen. Sales owned, or a stage we could not place. */
.stage-parked .stage-head { border-bottom-color: var(--gold); }

/* ====================================================================== *
 * THE BOARD, AS COLUMNS                                                  *
 *                                                                        *
 * Fourteen stages side by side on a horizontal rail, mirroring the board  *
 * this replaces. An earlier version stacked them vertically on the        *
 * reasoning that fourteen columns on a laptop gives each one eighty       *
 * pixels; the answer is not fewer columns, it is a rail that scrolls and  *
 * columns wide enough to read. The shape of the pipeline is the thing a   *
 * concierge reads first, and stacking destroyed it.                       *
 * ====================================================================== */

.board-masthead { border-bottom: 2px solid var(--gold); }

.masthead-sub {
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--sand);
}

.masthead-ver {
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.1em;
  color: var(--forest-soft);
  margin-right: auto;
}

.masthead-right {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  flex-wrap: wrap;
}

.masthead-pipeline,
.masthead-updated {
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.06em;
  color: var(--sand);
}

.topbtn {
  font-family: var(--mono);
  font-size: 0.62rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 0.3rem 0.7rem;
  color: var(--cream);
  border: 1px solid var(--forest-soft);
  text-decoration: none;
}
.topbtn:hover { background: var(--forest-deep); }

/* ------------------------------------------------------------- subnav --- */
.subnav {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  flex-wrap: wrap;
  max-width: 100%;
  padding: 0.5rem 1.5rem;
  background: var(--forest-deep);
}

.subnav-key {
  font-family: var(--mono);
  font-size: 0.6rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--gold-on-dark);
}
.subnav-key-manage { margin-left: 0.8rem; }

.subnav a {
  font-size: 0.76rem;
  color: var(--sand);
  text-decoration: none;
  padding: 0.2rem 0.5rem;
  border: 1px solid transparent;
}
.subnav a:hover { color: var(--cream); border-color: var(--forest-soft); }

.subnav-right {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 0.7rem;
}

/* ------------------------------------------------------------- tabs ----- */
.viewtabs {
  display: flex;
  gap: 1.4rem;
  padding: 0.7rem 1.5rem 0;
  border-bottom: 1px solid var(--rule);
}

.viewtabs a {
  font-family: var(--mono);
  font-size: 0.68rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-soft);
  text-decoration: none;
  padding-bottom: 0.5rem;
  border-bottom: 2px solid transparent;
}
.viewtabs a.on { color: var(--forest); border-bottom-color: var(--forest); }

/* ------------------------------------------------------------ notice ---- */
.notice {
  margin: 0;
  padding: 0.6rem 1.5rem;
  background: var(--cream);
  border-bottom: 1px solid var(--rule);
  border-left: 3px solid var(--gold);
  font-size: 0.82rem;
  color: var(--ink-soft);
}

/* -------------------------------------------------------------- rail ---- */
.rail {
  display: flex;
  gap: 0.6rem;
  align-items: flex-start;
  overflow-x: auto;
  padding: 1rem 1.5rem 2rem;
}

.col {
  flex: 0 0 15rem;
  background: var(--paper);
  border: 1px solid var(--rule);
  min-height: 22rem;
  display: flex;
  flex-direction: column;
}

.col-empty { background: transparent; }

.col-head {
  padding: 0.8rem 0.9rem 0.7rem;
  border-bottom: 1px solid var(--rule);
}

.col-key {
  font-family: var(--mono);
  font-size: 0.6rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--gold-text);
}

.col-head h2 {
  margin: 0.15rem 0 0.2rem;
  font-size: 1rem;
  line-height: 1.2;
}
.col-empty .col-head h2 { color: var(--ink-soft); font-weight: 400; }

.col-blurb {
  margin: 0 0 0.5rem;
  font-size: 0.72rem;
  line-height: 1.45;
  color: var(--ink-soft);
}

.col-count {
  margin: 0;
  font-family: var(--mono);
  font-size: 0.66rem;
  color: var(--ink-soft);
}

.col-body { padding: 0.6rem; display: grid; gap: 0.5rem; }

.col-none {
  margin: 0;
  padding: 0.4rem 0.3rem;
  font-size: 0.78rem;
  font-style: italic;
  color: var(--ink-faint, var(--ink-soft));
}

.col-parked { border-color: var(--gold); }

/* ----------------------------------------------------------- the card --- */
.pcard {
  display: grid;
  gap: 0.35rem;
  width: 100%;
  margin: 0;
  padding: 0.6rem 0.7rem;
  text-align: left;
  background: var(--cream);
  border: 1px solid var(--rule);
  border-left: 3px solid var(--rule);
  cursor: pointer;
  font-family: var(--sans);
}

.pcard:hover { border-color: var(--forest); }

.pcard.kind-attention { border-left-color: var(--error); }
.pcard.kind-action    { border-left-color: var(--forest); }
.pcard.kind-waiting   { border-left-color: var(--gold); }
.pcard.kind-done      { border-left-color: var(--sand); }
.pcard.is-overdue     { background: var(--error-bg); }

.pcard-top { display: flex; align-items: baseline; gap: 0.4rem; }

.pcard-name {
  flex: 1;
  font-weight: 600;
  font-size: 0.88rem;
  color: var(--forest);
}

.pcard-age {
  font-family: var(--mono);
  font-size: 0.7rem;
  font-variant-numeric: tabular-nums;
  color: var(--ink-soft);
}
.is-overdue .pcard-age { color: var(--error); font-weight: 600; }

.pcard-chev { color: var(--ink-faint, var(--ink-soft)); font-size: 0.9rem; }

.pcard-sub { display: flex; align-items: center; gap: 0.4rem; }

.substate {
  font-family: var(--mono);
  font-size: 0.6rem;
  font-weight: 500;
  letter-spacing: 0.06em;
  padding: 0.1rem 0.32rem;
  color: var(--cream);
  background: var(--forest);
}
.kind-attention .substate { background: var(--error); }
.kind-waiting .substate   { background: var(--gold-text); }
.kind-done .substate      { background: var(--ok); }

.substate-text { font-size: 0.72rem; color: var(--ink-soft); }

.pcard-forms { display: flex; align-items: center; gap: 0.22rem; }

.pcard-forms .form-flag {
  width: 1.15rem;
  height: 1.15rem;
  font-size: 0.58rem;
}

.pcard-who {
  margin-left: auto;
  font-size: 0.68rem;
  color: var(--ink-soft);
}

/* ------------------------------------------------------- performance ----- */
/* Rendered on every page and collapsed by default. A monitor you have to switch
   on is a monitor nobody has running when the slow thing happens. */

.perf {
  position: fixed;
  right: 0;
  bottom: 0;
  z-index: 30;
  max-width: min(30rem, 100%);
  font-family: var(--mono);
  font-size: 0.68rem;
  background: var(--white);
  border: 1px solid var(--hair);
  border-right: none;
  border-bottom: none;
  color: var(--ink-soft);
}

.perf summary {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.35rem 0.8rem;
  cursor: pointer;
  list-style: none;
  white-space: nowrap;
}
.perf summary::-webkit-details-marker { display: none; }
.perf[open] summary { border-bottom: 1px solid var(--hair); }

.perf-dot {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: var(--ink-mute);
  flex: none;
}
.perf-good { background: var(--forest); }
.perf-fair { background: var(--watch); }
.perf-poor { background: var(--fail); }

.perf-sum { letter-spacing: 0.04em; }
.perf-browser { color: var(--ink-mute); }

.perf-body { padding: 0.6rem 0.8rem 0.8rem; }

.perf-grid {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.2rem 0.9rem;
  margin: 0;
}
.perf-grid dt { color: var(--ink-mute); }
.perf-grid dd { margin: 0; text-align: right; }
.perf-grid b { color: var(--ink); font-weight: 500; }

.perf-note {
  margin: 0.6rem 0 0;
  padding-top: 0.5rem;
  border-top: 1px solid var(--hair);
  font-family: var(--sans);
  font-size: 0.72rem;
  line-height: 1.5;
  color: var(--ink-mute);
  white-space: normal;
}

/* ====================================================================== *
 * THIN BAR AND PROFILE MENU                                              *
 *                                                                        *
 * Role on the left of the cluster, person on the right. No cart, and no  *
 * notification bell: a bell with nothing behind it teaches people to     *
 * ignore bells, and the one thing this application has to say - a card   *
 * needing attention - it says on the card, where the work is.            *
 * ====================================================================== */

.topbar {
  background: var(--cream-warm);
  border-bottom: 1px solid var(--hair);
}

.topbar-inner {
  max-width: 100%;
  padding: 0.3rem 1.1rem;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.7rem;
}

.rolepill {
  font-family: var(--mono);
  font-size: 0.6rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  padding: 0.18rem 0.6rem;
  border: 1px solid var(--gold);
  color: var(--gold-text);
  background: transparent;
}

/* A member is not a lesser admin, so it is a different colour rather than a
   faded one. */
.rolepill-member { border-color: var(--sage); color: var(--sage); }

/* ------------------------------------------------------------- avatar --- */
.avatar {
  display: grid;
  place-items: center;
  width: 1.9rem;
  height: 1.9rem;
  margin: 0;
  padding: 0;
  border-radius: 50%;
  border: none;
  background: var(--gold);
  color: var(--forest-deep);
  font-family: var(--sans);
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  cursor: pointer;
  flex: none;
}

.avatar:hover { background: var(--gold-deep); color: var(--cream); }

.avatar-lg {
  width: 2.6rem;
  height: 2.6rem;
  font-size: 0.9rem;
  cursor: default;
  margin-bottom: 0.4rem;
}
.avatar-lg:hover { background: var(--gold); color: var(--forest-deep); }

/* --------------------------------------------------------------- menu --- */
.profile { position: relative; }

.profile-menu {
  position: absolute;
  top: calc(100% + 0.45rem);
  right: 0;
  z-index: 40;
  min-width: 15rem;
  background: var(--white);
  border: 1px solid var(--hair);
  box-shadow: 0 2px 14px rgba(10, 65, 42, 0.10);
}

.profile-head {
  display: flex;
  flex-direction: column;
  padding: 0.9rem 1rem 0.8rem;
}

.profile-name {
  font-family: var(--sans);
  font-size: 0.92rem;
  font-weight: 600;
  color: var(--ink);
}

/* Smaller and regular, so the name reads first and the address is the
   confirmation rather than competing with it. */
.profile-email {
  font-family: var(--sans);
  font-size: 0.78rem;
  font-weight: 400;
  color: var(--ink-mute);
  word-break: break-all;
}

.profile-sep { height: 1px; background: var(--hair); }

.profile-item {
  display: block;
  width: 100%;
  margin: 0;
  padding: 0.62rem 1rem;
  font-family: var(--sans);
  font-size: 0.85rem;
  font-weight: 400;
  text-align: left;
  color: var(--ink);
  background: transparent;
  border: none;
  text-decoration: none;
  cursor: pointer;
}

.profile-item:hover { background: var(--cream); color: var(--forest); }
.profile-item-button { border-radius: 0; }

/* ====================================================================== *
 * ADMIN SETTINGS                                                         *
 * ====================================================================== */

.admin-page { max-width: 60rem; }

.admin-title { margin-bottom: 1.4rem; }
.admin-title span {
  font-family: var(--sans);
  font-size: 0.95rem;
  font-weight: 400;
  color: var(--ink-mute);
  margin-left: 0.4rem;
}

.admin-label {
  font-family: var(--mono);
  font-size: 0.62rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-mute);
  margin: 1.6rem 0 0.6rem;
}

.admin-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr));
  gap: 0.7rem;
}

.admin-card {
  display: block;
  padding: 0.9rem 1rem 1rem;
  background: var(--white);
  border: 1px solid var(--hair);
  border-left: 3px solid var(--forest);
  text-decoration: none;
  color: var(--ink);
}

.admin-card:hover { border-color: var(--forest); border-left-color: var(--gold); }

/* A tile that opens a placeholder is marked on its face. Looking like a
   feature and not being one costs a click and some trust. */
.admin-card-pending { border-left-color: var(--sand-deep); background: var(--cream-warm); }

.admin-card-head { display: flex; align-items: baseline; gap: 0.35rem; }

.admin-card-name {
  font-family: var(--serif);
  font-size: 1.15rem;
  font-weight: 500;
  color: var(--forest);
}

.admin-card-count {
  font-family: var(--mono);
  font-size: 0.78rem;
  color: var(--ink-mute);
  font-variant-numeric: tabular-nums;
}

.admin-card-desc {
  display: block;
  margin-top: 0.3rem;
  font-size: 0.82rem;
  line-height: 1.5;
  color: var(--ink-soft);
}

.admin-card-flag {
  display: inline-block;
  margin-top: 0.5rem;
  font-family: var(--mono);
  font-size: 0.58rem;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--gold-text);
}

/* ====================================================================== *
 * THE NAVBAR                                                             *
 *                                                                        *
 * One green bar: brand, navigation, identity. The pages and forms that    *
 * used to sit in a second row live behind one link now - eleven links     *
 * across the top of a working screen is a menu pretending to be           *
 * navigation, and it costs the vertical space the board needs.            *
 * ====================================================================== */

.navbar {
  background: var(--forest);
  border-bottom: 2px solid var(--gold);
}

.navbar-inner {
  display: flex;
  align-items: center;
  gap: 1.2rem;
  padding: 0.45rem 1.2rem;
  flex-wrap: wrap;
}

.navbrand {
  font-family: var(--serif);
  font-size: 1.3rem;
  font-weight: 400;
  letter-spacing: 0.5px;
  color: var(--cream);
  text-decoration: none;
  display: flex;
  align-items: center;
  gap: 0.45rem;
  flex: none;
}

.navbrand-sub {
  font-family: var(--sans);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 2.6px;
  text-transform: uppercase;
  color: var(--gold-light);
}

.navlinks {
  display: flex;
  align-items: center;
  gap: 0.15rem;
  flex-wrap: wrap;
  margin-right: auto;
}

.navlinks a {
  font-family: var(--sans);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--sand);
  text-decoration: none;
  /* Pinned rather than inherited. The start page loads the welcome sheet, whose
     body line-height differs from ours, which made the bar 0.6px taller there
     than on every other page. Nobody would see it, but a navbar whose height
     depends on which page is under it is a thing that gets worse. */
  line-height: 1.6;
  padding: 0.42rem 0.7rem;
  white-space: nowrap;
}

.navlinks a:hover { color: var(--cream); }

/* The current page is simply the heavier, brighter word. No rule under it and
   no fill behind it: a filled box in a row of plain words reads as something
   you press, and this is a label saying where you already are. */
.navlinks a.on {
  color: var(--cream);
  font-weight: 700;
}

/* Refresh is an action, not a place. Set apart so it does not read as a
   seventh destination. */
/* Selector carries `.navlinks a` on purpose. `.navlink-action` alone is one
   class and loses to `.navlinks a`, which is a class AND a type - so a bare
   .navlink-action padding rule is silently discarded. That is what the pair of
   !important flags here used to be papering over; matching the specificity
   says the same thing without them. */
.navlinks a.navlink-action {
  margin-left: 0.5rem;
  border: 1px solid var(--forest-soft);
  /* Less 1px a side, so the outlined box finishes the same height as the
     filled one rather than standing 2px prouder than every other link. */
  padding: calc(0.42rem - 1px) calc(0.7rem - 1px);
}
.navlinks a.navlink-action:hover { background: var(--forest-deep); }

.navright { display: flex; align-items: center; gap: 0.7rem; flex: none; }

/* On the green ground the role pill needs its own treatment: the light-ground
   gold-text would disappear here. */
.navright .rolepill { border-color: var(--gold); color: var(--gold-on-dark); }
.navright .rolepill-member { border-color: var(--sage); color: var(--sand); }

@media (max-width: 60rem) {
  .navbrand-sub { display: none; }
  .navlinks a { padding: 0.3rem 0.45rem; font-size: 0.75rem; }
}

/* --------------------------------------------------------- board meta --- */
.boardmeta {
  display: flex;
  gap: 1rem;
  padding: 0.5rem 1.5rem 0;
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.06em;
  color: var(--ink-mute);
}

/* ------------------------------------------------------- start screen --- */
.page.narrow { max-width: 44rem; }

.stat-row {
  display: flex;
  gap: 0.6rem;
  flex-wrap: wrap;
  margin: 1.2rem 0 1.8rem;
}

.stat {
  flex: 1 1 8rem;
  padding: 0.8rem 1rem;
  background: var(--white);
  border: 1px solid var(--hair);
  border-left: 3px solid var(--forest);
}

.stat-n {
  display: block;
  font-family: var(--mono);
  font-size: 1.5rem;
  font-weight: 500;
  color: var(--forest);
  font-variant-numeric: tabular-nums;
}

.stat-l {
  display: block;
  font-size: 0.72rem;
  color: var(--ink-mute);
  margin-top: 0.15rem;
}

.ticks { list-style: none; padding: 0; margin: 0 0 1.2rem; }

.ticks li {
  position: relative;
  padding: 0.3rem 0 0.3rem 1.4rem;
  font-size: 0.9rem;
  line-height: 1.55;
}

.ticks li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.85rem;
  width: 0.6rem;
  height: 1px;
  background: var(--forest);
}

/* Open items get the gold rule rather than the forest one: a different state,
   marked by a different colour, not by a fainter version of the same. */
.ticks-open li::before { background: var(--gold); }

.mono { font-family: var(--mono); font-size: 0.92em; }

/* ------------------------------------------------- navbar, second pass --- */
/* Three tracks: brand, links, identity. The middle one is centred against the
   VIEWPORT rather than against the space left over, which is what a plain flex
   row gives you - and which drifts left or right as the brand or the identity
   cluster changes width. */
.navbar-inner {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 1rem;
}

.navbrand { justify-self: start; gap: 0.55rem; }

/* The mandala is 70px square; the bar shows it at 26. Both dimensions are on
   the <img> so the row does not reflow while it loads. */
.navmark { width: 1.9rem; height: 1.9rem; display: block; flex: none; }

.navwordmark { display: flex; align-items: baseline; gap: 0.45rem; }

.navlinks { justify-self: center; margin-right: 0; }

.navright { justify-self: end; }

/* --------------------------------------------------------- navbar meta --- */
.navmeta {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  line-height: 1.25;
  font-family: var(--mono);
  font-size: 0.58rem;
  letter-spacing: 0.06em;
  white-space: nowrap;
}

.navmeta-pipe {
  font-family: var(--sans);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--sand);
}
/* `--forest-soft` is #1B5A3E, and this sits on #0A412A. Barely more than a
   shade apart, so "Updated 00:15" was effectively invisible on the green bar.
   The same cream the nav links use, dimmed - quiet without being unreadable. */
.navmeta-when { color: rgba(244, 239, 226, 0.62); }

/* Below this the three tracks stop fitting; the links wrap to their own row and
   stay centred rather than being squeezed into an unreadable strip. */
@media (max-width: 78rem) {
  .navbar-inner { grid-template-columns: auto 1fr; }
  .navlinks { grid-column: 1 / -1; grid-row: 2; justify-self: center; }
  .navright { grid-row: 1; }
}

@media (max-width: 40rem) {
  .navmeta { display: none; }
}

/* ======================================================================= *
 *  THE PATIENT DRAWER                                                     *
 *                                                                         *
 *  Slides in over the board rather than expanding a card in place. An      *
 *  expanding card costs the vertical space that lets somebody see the      *
 *  queue, and the queue is what the board is for.                          *
 * ======================================================================= */

.fly-sub {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 0.4rem;
}

.fly-stage {
  font-family: var(--mono);
  font-size: 0.6rem;
  letter-spacing: 0.08em;
  padding: 1px 5px;
  color: var(--cream);
  border: 1px solid rgba(244, 239, 226, 0.4);
}

.fly-stage-label { font-size: 0.8rem; color: var(--cream); }
.fly-clock { font-size: 0.75rem; color: var(--sand); }
.fly-clock.is-overdue { color: var(--gold-light); font-weight: 600; }

/* -------------------------------------------------------- the rows --- */
/* A two-column reading table, not a form. Label left in small caps, value
   right in the reading face, one hairline between each. */
.fly-contact { margin-bottom: 1.4rem; }

/* The edit sits under the details it edits, aligned with their values rather
   than with the labels: it belongs to the column it changes. */
.fly-contact .fly-chip { margin-top: 0.6rem; }

.fly-row {
  display: grid;
  grid-template-columns: 7.5rem minmax(0, 1fr);
  gap: 0.6rem;
  align-items: baseline;
  padding: 0.55rem 0;
  border-bottom: 1px solid var(--hair);
}

.fly-key {
  font-family: var(--sans);
  font-size: 0.72rem;
  color: var(--ink-mute);
  line-height: 1.4;
}

.fly-value { font-size: 0.85rem; color: var(--ink); word-break: break-word; }

/* NO RESTING UNDERLINE. The row already carries a divider, so a border on the
   value as well drew a second line a few pixels above the first and the block
   read as ruled paper. The border stays in the box as `transparent` rather than
   being dropped: hover and keyboard focus turn it gold without the row
   changing height, so the affordance survives without the clutter. */
.fly-link {
  font-size: 0.85rem;
  color: var(--forest);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  word-break: break-word;
}

.fly-link:hover,
.fly-link:focus-visible { border-bottom-color: var(--gold); }

.fly-panel { margin-bottom: 1.4rem; }

.fly-panel-title {
  margin: 0 0 0.2rem;
  padding-bottom: 0.35rem;
  border-bottom: 1px solid var(--gold);
  font-family: var(--sans);
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--gold-text);
}

/* ------------------------------------------------------- send block --- */
.fly-sublabel {
  margin: 0.8rem 0 0.4rem;
  font-family: var(--sans);
  font-size: 0.6rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-mute);
}

.fly-chips { display: flex; flex-wrap: wrap; gap: 0.4rem; }

/* `width: auto` because `button` is width:100% globally. A chip that fills its
   container is not a chip - it reads as the section's primary action, which is
   the opposite of what a chip is for. The mail rows already made this override
   locally; it belongs on the chip itself. */
.fly-chip {
  width: auto;
  margin-top: 0;
  padding: 0.35rem 0.7rem;
  border: 1px solid var(--edge);
  background: var(--white);
  color: var(--forest);
  font-size: 0.78rem;
  text-decoration: none;
  white-space: nowrap;
}

.fly-chip:hover { border-color: var(--gold); background: var(--cream); }

/* One row per template: what it is on the left, preview and send on the right.
   Every patient email is listed, so the row has to carry enough for somebody to
   tell at a glance whether this is the one - the stage it belongs to, what that
   stage is for, and whether it has already gone. */
.mail-intro {
  margin: 0 0 0.7rem;
  font-family: var(--sans);
  font-size: 0.78rem;
  line-height: 1.5;
  color: var(--ink-soft);
}

.mail-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.8rem;
  flex-wrap: wrap;
  padding: 0.7rem 0;
  border-bottom: 1px solid var(--sand);
}

.mail-row:last-of-type { border-bottom: 0; }

/* The rows for the stage this card is on. A left rule rather than a fill:
   the recommendation should read as emphasis, not as a warning. */
.mail-row-now {
  border-left: 2px solid var(--gold);
  padding-left: 0.7rem;
  margin-left: -0.7rem;
}

.mail-main { flex: 1 1 11rem; min-width: 0; }

.mail-head { display: flex; align-items: baseline; gap: 0.45rem; flex-wrap: wrap; }

/* Tabular so the numbers form a column the eye can run down. */
.mail-stage {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  font-size: 0.72rem;
  color: var(--ink-mute);
}

.mail-title {
  font-family: var(--sans);
  font-size: 0.88rem;
  font-weight: 600;
  color: var(--forest);
}

.mail-now {
  font-family: var(--sans);
  font-size: 0.58rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--forest);
  background: var(--gold-light);
  padding: 0.1rem 0.35rem;
}

.mail-desc {
  margin: 0.2rem 0 0;
  font-family: var(--sans);
  font-size: 0.78rem;
  line-height: 1.5;
  color: var(--ink-soft);
}

.mail-flags {
  margin: 0.25rem 0 0;
  font-family: var(--sans);
  font-size: 0.72rem;
  line-height: 1.45;
}

/* Back in the left column, under the description it belongs to. `nowrap` is
   gone with it: the line now carries a timestamp and a relative time, which is
   longer than the column at a phone width and should wrap rather than push the
   row sideways. */
.mail-sent {
  margin: 0.25rem 0 0;
  font-family: var(--sans);
  font-size: 0.72rem;
  line-height: 1.45;
}

.mail-flags { color: var(--ink-mute); }
.mail-sent { color: var(--gold-text); font-weight: 600; }

.mail-fail {
  flex: 1 0 100%;
  margin: 0.35rem 0 0;
  font-family: var(--sans);
  font-size: 0.75rem;
  color: var(--forest);
}

/* Already sent at least once: still pressable, but it should not look like the
   next thing to do. */
.mail-acts .mini-done {
  background: var(--white);
  color: var(--forest);
  border: 1px solid var(--sand);
}

/* The right-hand column: when it last went, and the two controls, both ranged
   right so the buttons of every row line up as one column down the panel. */
.mail-side {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.35rem;
  flex: none;
  text-align: right;
  /* `margin-left: auto` IS WHAT RANGES IT RIGHT, and it has to be a margin
     rather than `justify-content` on the row. The drawer is 328px and this
     column is 195px, so it wraps onto a line of its own - where a rule on the
     row does nothing, because the wrapped line has one item on it. An auto
     margin absorbs that line's free space, so it ends up right whether it
     shares a line with the description or sits under it. */
  margin-left: auto;
}

.mail-acts { display: flex; align-items: center; gap: 0.4rem; flex: none; }

/* SEND AND SEND AGAIN ARE THE SAME WIDTH. Sized to the longer of the two, so a
   row that has been sent does not shunt its Preview a centimetre left of every
   other row's. Preview matches them, so the pair is one shape at every row. */
.mail-acts .mini,
.mail-acts .fly-chip {
  min-width: 5.9rem;
  text-align: center;
}

/* Preview is a <button> now that it opens a panel rather than a tab, so it
   needs the same escape from the global button width as the Close above. */
.mail-acts .fly-chip {
  width: auto;
  margin-top: 0;
  cursor: pointer;
}

/* Disabled reads as "already done" or "not ready", not as broken. */
.mail-acts .mini:disabled {
  opacity: 0.55;
  cursor: default;
}

/* ------------------------------------------------ the email preview --- */
/* Sits above the drawer rather than replacing it, so closing the preview puts
   you back on the card you were reading with nothing to find again. */
.pv-back {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 3vh 16px;
  background: rgba(10, 65, 42, 0.42);
}

.pv {
  width: min(660px, 100%);
  max-height: 94vh;
  display: flex;
  flex-direction: column;
  background: var(--paper);
  border: 1px solid var(--sand);
}

/* WIDER, FOR THE PANELS THAT ARE WORK RATHER THAN READING. The email preview
   is a page being read and 660px is a comfortable measure for it. The schedule
   manager and the full email list are a table and a queue: a date, a time, a
   kind and a title is one line here and a stack of four at the drawer's width,
   which is most of why those editors were painful in the drawer. */
.pv-wide { width: min(820px, 100%); }

.pv-head {
  flex: none;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  padding: 1rem 1.2rem;
  border-bottom: 1px solid var(--sand);
}

.pv-who {
  font-family: var(--sans);
  font-size: 0.62rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--gold-text);
}

.pv-what {
  font-family: var(--serif);
  font-size: 1.35rem;
  line-height: 1.2;
  color: var(--forest);
  margin-top: 0.15rem;
}

/* `button` is width:100% with a top margin globally, which would stretch this
   across the header and shove the titles into a two-line wrap. Same override
   `.field button` and `.note-form button` already make. */
.pv-close {
  flex: none;
  width: auto;
  margin-top: 0;
  padding: 0.3rem 0.8rem;
  border: 1px solid var(--sand);
  background: var(--white);
  color: var(--forest);
  font-family: var(--sans);
  font-size: 0.75rem;
  cursor: pointer;
}

.pv-close:hover { border-color: var(--gold); background: var(--cream); }

.pv-body {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: 1.1rem 1.2rem 1.4rem;
}

.pv-label {
  font-family: var(--sans);
  font-size: 0.6rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-mute);
  margin-bottom: 0.35rem;
}

.pv-subject {
  font-family: var(--serif);
  font-size: 1.1rem;
  color: var(--ink);
  margin-bottom: 1.2rem;
}

/* The frame carries the email's own cream, so the border is the only edge it
   needs. Tall enough to show the ask without scrolling inside a scroll. */
.pv-frame {
  display: block;
  width: 100%;
  height: 620px;
  border: 1px solid var(--sand);
  background: var(--cream);
}

.pv-foot {
  flex: none;
  padding: 0.75rem 1.2rem;
  border-top: 1px solid var(--sand);
  background: var(--cream);
  font-family: var(--sans);
  font-size: 0.75rem;
  line-height: 1.5;
  color: var(--ink-mute);
}

/* ---------------------------------------------------------- footer --- */
/* Pinned, so the way out to the CRM does not depend on scrolling to the end
   of a long trip. */
.fly-foot {
  flex: none;
  padding: 0.9rem 1.2rem;
  border-top: 1px solid var(--rule);
  background: var(--paper);
}

.fly-ghl {
  display: block;
  padding: 0.75rem 1rem;
  background: var(--forest);
  color: var(--cream);
  font-family: var(--sans);
  font-size: 0.8rem;
  text-align: center;
  text-decoration: none;
}

.fly-ghl:hover { background: var(--forest-deep); }
.fly-ghl[hidden] { display: none; }

/* ------------------------------------------------------- sample mode --- */
/* The same reasoning as the development bar: a state you can be in without
   noticing needs to be impossible to miss. This one is gold rather than red,
   because sample data is not an error - it is a demo, and the bar is there so
   nobody mistakes it for the real board. */
.samplebar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.7rem;
  padding: 0.5rem 1.2rem;
  background: var(--watch-tint);
  border-bottom: 1px solid var(--watch);
  font-size: 0.78rem;
  color: var(--watch);
}

.samplebar-tag {
  font-family: var(--mono);
  font-size: 0.6rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  padding: 0.15rem 0.45rem;
  background: var(--watch);
  color: var(--cream);
}

.samplebar-out { margin-left: auto; color: var(--watch); }


.fly-muted { color: var(--ink-mute); }

/* WhatsApp CARRIED A SAGE RULE so it was findable without a logo we would have
   to ship and licence. That reasoning held while every value was underlined and
   this one was a different colour; with the resting underlines gone it was the
   only line left in the block and read as a mistake rather than a marker. The
   row is labelled "WhatsApp", which is what actually tells them apart - the two
   rows otherwise show the same digits. */
.fly-wa:hover,
.fly-wa:focus-visible { border-bottom-color: var(--gold); }


/* ====================================================================== *
 * BANDED SECTIONS - THE PATIENT-FACING SHEETS                            *
 *                                                                        *
 * Every page a patient is sent is one long sheet of stacked sections, and *
 * on a phone they arrived as one undifferentiated column: five blocks of  *
 * white on welcome, four of cream at the foot of the Center. Where one    *
 * ended and the next began was carried entirely by the eyebrow-and-title  *
 * rhythm, which only works if you are reading rather than scanning.       *
 *                                                                        *
 * So the sections alternate tone: white paper, warm cream, white paper.   *
 * Two tones and a sand hairline at every join, which survives a dim       *
 * screen at arm's length where the tint alone does not.                   *
 *                                                                        *
 * Here rather than in the per-page files because all four sheets load     *
 * app.css and all four had the same problem. The classes are applied in   *
 * the templates - on the Center by a Jinja counter, since its sections    *
 * are conditional and hard-coding the parity would put two creams side by *
 * side the moment a patient finished their forms.                         *
 *                                                                        *
 * THE FULL-BLEED FOREST BLOCKS (.hero, .reassure, .questions) AND THE     *
 * FOOTER ARE NOT BANDS. They already break the column, so counting them   *
 * into the alternation would only make the two tones land at random.      *
 * ====================================================================== */

/* The page sections close with zero bottom padding - they lean on the next
   block's top padding for the gap. That is invisible until a section carries a
   background, at which point the tint stops at the last line of text. One rule
   here rather than a padding fix per block, so a section added later cannot
   reintroduce it. */
.sheet .band { padding-bottom: 44px; }

.sheet .band-tint { background: var(--cream-warm); }
.sheet .band-plain { background: var(--white); }

/* THE CARDS INSIDE A TINTED BAND FLIP TO PAPER. Every sheet fills its cards -
   the numbered to-dos, the reading list, the timeline, the do-and-don't
   columns - with the same warm cream the tinted band now uses, so on those
   sections the card and its ground became one colour and only the sand border
   was left holding them apart.

   Done by moving the token rather than by naming each card, because the cards
   are page-specific classes across four stylesheets and a new one would have
   been missed. Scoped to the children so the band's own fill, which reads the
   token first, keeps the tint. */
.sheet .band-tint > * { --cream-warm: var(--white); }

/* Hairline only where two bands actually meet, so the first band under the
   masthead and the first after a forest block stay clean. */
.sheet .band + .band { border-top: 1px solid var(--sand); }

@media (max-width: 760px) {
  .sheet .band { padding-bottom: 32px; }
}

@media print {
  /* The tint is the only thing distinguishing the sections on paper too, and
     a printer that drops backgrounds would collapse them all to white. */
  .sheet .band { break-inside: avoid; }
}


/* ====================================================================== *
 * THE WEBHOOK ADDRESS                                                    *
 *                                                                        *
 * On the Intake page, and it is not decoration: this line is copied by    *
 * hand into a GoHighLevel field, and a character lost on the way is a     *
 * paid patient who silently never arrives. So the URL gets its own row    *
 * rather than sitting inside a sentence - it is a value to be taken, not  *
 * prose to be read - and the button beside it is how it should be taken.  *
 * ====================================================================== */

.hookline {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  flex-wrap: wrap;
  margin: 0.8rem 0 1rem;
  padding: 0.6rem 0.75rem;
  background: var(--cream);
  border: 1px solid var(--sand);
  /* The gold edge the rest of the application uses to mean "this is the thing
     on the panel", stood on its left side. */
  border-left: 3px solid var(--gold);
}

.hookline-verb {
  font-family: var(--mono);
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  color: var(--cream);
  background: var(--forest);
  padding: 0.15rem 0.4rem;
  flex: 0 0 auto;
}

/* WRAPS AT THE SLASHES RATHER THAN OVERFLOWING. A URL is one unbroken token to
   a text layout engine, so on a narrow panel it either pushes the button off
   the row or scrolls out of sight - and a URL you cannot see all of is one you
   cannot check before you paste it. */
.hookline-url {
  font-family: var(--mono);
  font-size: 0.8rem;
  color: var(--ink);
  flex: 1 1 20ch;
  min-width: 0;
  overflow-wrap: anywhere;
  /* Selectable as a unit for anybody who copies by hand out of habit. */
  user-select: all;
}

/* BOTH HALVES OF THE SIGN-IN RULE, not just one. app.css gives every button
   `width: 100%; margin-top: 1.2rem`. Resetting only the margin left the Copy
   button 1026px wide, which pushed it onto a row of its own underneath the URL
   and made the whole line three times taller than it should be - the same
   symptom manager.css describes for its own controls. */
.hookline .button-quiet { flex: 0 0 auto; width: auto; margin-top: 0; }

/* The secret's state, beside the header it belongs to. `baseline` because it
   sits inside a line of monospace and a middle-aligned pill rides high
   against it. */
.ticks .pill { margin-left: 0.5rem; vertical-align: baseline; }


/* ====================================================================== *
 * THE FACTS TABLE                                                        *
 *                                                                        *
 * Two or three columns where the first holds a short label and the last   *
 * holds a long value. `table-layout: auto` splits the free width in       *
 * proportion to content, which on the admin page gave the label column    *
 * 370px to hold the word "Version" - so every row had a third of its      *
 * width empty between a label and the value it names, and the eye had to  *
 * cross it eight times.                                                   *
 *                                                                        *
 * `width: 1%` IS THE TRICK THAT DID NOT WORK HERE, and it is worth saying  *
 * why rather than leaving somebody to try it again. It usually means       *
 * "shrink to content": the browser satisfies the 1%, finds the content     *
 * needs more, and gives it that. Against this table - `width: 100%` with   *
 * a `min-width: 46rem` under it - the auto algorithm honoured the 1%       *
 * literally: the cell box collapsed to its padding, the label text spilled *
 * out past its own border, and the value column still started 400px away.  *
 * Measurably worse than the gap it was meant to close.                     *
 *                                                                         *
 * So the width is stated. It is a FLOOR, not a cap - a column with         *
 * `nowrap` grows past a specified width when its content needs more, so a  *
 * longer label added later widens the column instead of overflowing it.    *
 * ====================================================================== */

/* `table-layout: fixed` STOOD HERE AND HAS BEEN REMOVED. It was reached for
   while chasing the gap between these two columns, and it did not close it -
   because the gap was never a layout-algorithm problem. The label cell carried
   `.sub`, whose `display: block` had taken it out of the column model; see the
   note beside that rule above. Fixed layout only added a second bug, clipping
   the monospace paths in the scopes table, which auto layout sizes correctly.

   What is left is a width floor. `nowrap` means a column grows past it when a
   longer label arrives, rather than overflowing. */
.facts-table td:first-child,
.facts-table th:first-child {
  width: 11rem;
  white-space: nowrap;
  /* The gap between a label and its value is now this, and only this. */
  padding-right: 2rem;
}

/* The scope names and the call paths are both monospace and both fixed-ish, so
   they get their own floors and the sentence in the middle takes what is left -
   which is the column that actually benefits from the room. */
.scopes-table td:first-child,
.scopes-table th:first-child { width: 15rem; }

.scopes-table td:last-child,
.scopes-table th:last-child { width: 16rem; white-space: nowrap; }

.scope-name { font-size: 0.8rem; color: var(--forest); font-weight: 500; }
.scope-call { font-size: 0.75rem; }

.scopes-note { padding: 0 0 0.9rem; max-width: 68ch; }

/* The amber the search box uses for a sample row, for the same job: something
   that is not wrong but has not been confirmed. */
.pill-watch { color: var(--watch); border-color: var(--watch); }

/* ------------------------------------------------------- the drawer photo ---
   The patient's photo, beside their name. Square, per section 06; sand
   hairline; a fixed box so a slow or failed image leaves a gap of the same
   size rather than reflowing the header while the concierge is reading it. */
.drawer-photo {
  display: block;
  width: 88px;
  height: 88px;
  object-fit: cover;
  border: 1px solid var(--sand);
  background: var(--cream);
  flex: none;
}
.drawer-head-row { display: flex; align-items: flex-start; gap: 16px; }

/* ---------------------------------------------------------- the protocol ---
   What Sales sent across at intake. The details are free text and can carry
   the shape somebody typed, so newlines survive; the PDF is a link out to
   wherever Sales hosts it, styled as a button because that is what it does. */
/* THE PROTOCOL, WHICH IS A DOCUMENT AND NOT A PARAGRAPH. It used to be one
   grey block with `white-space: pre-line` holding the newlines - honest, and
   the two lines a concierge actually hunts for, the treatment and the hotel,
   sat at the same weight as everything else. `protocolBlock` in board.js reads
   the punctuation Sales already types and builds real elements, so the
   pre-line is gone with the text node it was there for. */
.protocol-details {
  font-size: 14px;
  line-height: 1.65;
  color: var(--ink-soft);
  background: var(--cream-warm);
  border: 1px solid var(--sand);
  padding: 0.85rem 1rem;
  margin-bottom: 0.75rem;
}

/* The first line names the protocol. Serif, because it is a title and every
   other title in this application is. */
.protocol-name {
  margin: 0 0 0.5rem;
  padding-bottom: 0.45rem;
  font-family: var(--display);
  font-size: 1.05rem;
  line-height: 1.3;
  color: var(--ink);
  border-bottom: 1px solid var(--sand);
}

.protocol-line { margin: 0 0 0.35rem; }
.protocol-line:last-child { margin-bottom: 0; }

/* THE SAME SHAPE THE TRIP ANSWERS USE - label left, value right - but a
   narrower cap. Those labels are phrases ("Arrival flight number"); a protocol
   label is one word, "Hotel" or "Add-on" or "Transport" at its longest, and
   measured at 410px the 9rem column this started at pushed "FireFly
   photobiomodulation, 2 sessions" onto two lines for nothing. The box has
   already taken 2rem of the drawer before this grid gets any. */
.protocol-row {
  display: grid;
  grid-template-columns: minmax(0, min(38%, 6.5rem)) minmax(0, 1fr);
  gap: 0.75rem;
  padding: 0.18rem 0;
}

.protocol-key {
  font-family: var(--sans);
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-mute);
}

.protocol-value { color: var(--ink); word-break: break-word; }

.protocol-list { margin: 0.25rem 0 0.35rem; padding-left: 1.1rem; }
.protocol-list li { margin-bottom: 0.2rem; }

@media (max-width: 560px) {
  .protocol-row { grid-template-columns: 1fr; gap: 0.1rem; }
}

/* A REVISION IS THE ONE THING WORTH INTERRUPTING FOR. Everything else this
   section says is reference; this says the thing you remember reading is not
   the thing that is there now. Gold rule on the left rather than a filled
   panel - loud enough to catch the eye at the top of the drawer, quiet enough
   that a card carrying it does not look broken. */
.protocol-revised {
  font-size: 13px;
  line-height: 1.55;
  color: var(--ink);
  background: var(--cream-warm);
  border-left: 3px solid var(--gold-deep);
  padding: 0.6rem 0.85rem;
  margin: 0.75rem 0 0;
}

.protocol-pdf {
  display: inline-block;
  padding: 0.6rem 1.1rem;
  background: var(--gold-deep);
  color: #FFFFFF;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 1.4px;
  text-transform: uppercase;
  text-decoration: none;
}
.protocol-pdf:hover { background: var(--forest); }
.protocol-pdf:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }

/* ------------------------------------------------------------ following --- */
/* Who hears about a patient. The primary is the person who owns the card; the
   rest are watching. See 0024_followers.sql for why round that way. */
.watch-list { list-style: none; margin: 0 0 0.6rem; padding: 0; }

.watch {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.4rem 0.5rem;
  margin-bottom: 0.3rem;
  background: var(--cream-warm);
  border-left: 3px solid var(--sand-deep);
  font-size: 0.85rem;
}

/* THE OWNER, MARKED IN THE SAME CHANNEL THE BOARD USES for a card that needs
   attention: a gold edge. One glance answers "who has this". */
.watch-primary { border-left-color: var(--gold); }

.watch-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }

.watch-tag {
  font-size: 0.62rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--gold-text);
}

.watch .mini { flex: 0 0 auto; }


/* A line's description, under the row that adds it. Full width because it is
   prose - two lines of a paragraph in a 12rem box is a box nobody reads before
   pressing Add, which is the entire point of showing it here.

   It is always present rather than revealed on selection: a box that appears
   is a box somebody misses, and with JavaScript off the server still copies
   the catalog's own description, so an empty one is never a lost description. */
.field-full { flex: 1 1 100%; }

/* THE SAME BOX AS EVERY OTHER FIELD. A textarea inherits none of the rule
   above - that one names inputs by type - so left alone it renders in the
   browser's own monospace with the browser's own border, on a form where
   nothing else does. Padding, face, size, colour and border are copied from
   `input[type="text"]` deliberately: they have to stay in step, and the two
   rules sit far enough apart in this file that a comment is the only thing
   that will say so. */
.field-full textarea {
  width: 100%;
  /* Four lines rather than two. These descriptions are a paragraph - the
     Human Regenerator's runs to sixty words - and editing one through a slot
     that shows two lines of it is why nobody edits one. */
  min-height: 7rem;
  padding: 0.7rem 0.8rem;
  font-family: var(--sans);
  font-size: 1rem;
  line-height: 1.5;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 0;
  resize: vertical;
}

/* WHOSE WORDS THESE ARE. The note under the box says whether what is showing
   came from the catalog or was typed here, because a rep who edits a
   description is editing this protocol only - the catalog is untouched, and
   somebody has to be told that before they worry about it. */
.field-full .fine { margin: 0.35rem 0 0; }

/* --------------------------------------------- a protocol's line items ---- */

/* A column header that only a screen reader needs. The actions column has no
   heading worth printing above two small buttons, and an empty `<th>` tells a
   screen reader nothing at all. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.line-alloc { white-space: nowrap; }

/* The grip column: as narrow as the handle, and the cursor says what it is
   for before anybody tries. */
.line-grip { width: 1px; padding-right: 0; }

.grip {
  width: auto;
  margin: 0;
  padding: 0.15rem 0.35rem;
  font-size: 0.9rem;
  line-height: 1;
  letter-spacing: 0.08em;
  color: var(--ink-mute);
  background: transparent;
  border: 1px solid transparent;
  cursor: grab;
}

.grip:hover { color: var(--forest); border-color: var(--rule); }
.grip:active { cursor: grabbing; }

/* The row under the cursor while something is being dropped on it. A line, not
   a fill: the row has to stay readable while it is being aimed at. */
tr.drop-above td { box-shadow: inset 0 2px 0 var(--forest); }
tr.drop-below td { box-shadow: inset 0 -2px 0 var(--forest); }
tr.lifting td { opacity: 0.45; }

/* STACKED AND EQUAL. Two small buttons of different widths side by side read
   as two unrelated things; one above the other, both the width of the wider
   word, read as what they are - the two things you can do to this line.
   `stretch` is what makes them equal without either being given a width. */
.line-actions {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0.35rem;
  width: 6.5rem;
}

.line-actions .mini,
.line-actions form,
.line-actions form button { width: 100%; text-align: center; }

/* Edit is a link doing a button's job - it has to look like Remove beside it
   rather than like body text that happens to be clickable. */
a.mini {
  display: inline-block;
  padding: 0.3rem 0.6rem;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--forest);
  text-decoration: none;
  border: 1px solid var(--rule);
}

a.mini:hover { background: var(--cream); }

/* THE FORM SAYS WHICH LINE IT IS HOLDING. Editing and adding are the same
   controls, so without this the only difference between "about to add a
   fourth service" and "about to rewrite the first" is the word on the button -
   which is exactly the kind of difference somebody misses. */
.editing-line {
  border-left: 3px solid var(--gold);
  padding-left: 0.9rem;
}

.line-form-note { margin: 0 0 0.6rem; }
.line-form-note[hidden] { display: none; }

.form-error {
  margin: 0.6rem 0 0;
  color: var(--error);
}

.form-error[hidden] { display: none; }

/* ------------------------------------------------------ work in flight ---

   AN INDETERMINATE BAR, AND IT IS NOT A LIE. Nothing on the server reports how
   far through it is - two PDFs are rendered and then the page reloads - so a
   bar that filled from 0 to 100 would be inventing a number. This one says
   "still working" and nothing more, which is the only true thing available.
   The label on the button carries the meaning. */
.progress {
  width: 100%;
  height: 3px;
  margin-top: 0.75rem;
  overflow: hidden;
  background: var(--cream-alt);
}

/* An author `display` beats the browser's `[hidden] { display: none }` - the
   rule this application has already been caught by once, on the board's
   "changed" notice, which sat on screen permanently while its attribute said
   hidden. */
.progress[hidden] { display: none; }

.progress span {
  display: block;
  width: 35%;
  height: 100%;
  background: var(--forest);
  animation: progress-slide 1.1s linear infinite;
}

@keyframes progress-slide {
  from { transform: translateX(-100%); }
  to   { transform: translateX(300%); }
}

/* Somebody who has asked their machine to stop moving things gets a bar that
   is simply there. It still says "working"; it just does not travel. */
@media (prefers-reduced-motion: reduce) {
  .progress span { width: 100%; animation: none; opacity: 0.55; }
}

/* ------------------------------------------- questions about the cards --- */
/* SAME PLACE AS THE SAMPLE BAR AND FOR THE SAME REASON: something you need to
   know BEFORE you read the board belongs above the board. Where it differs is
   tone. The sample bar states a fact about the whole screen; this one asks
   questions with answers beside them, and most of those answers are "yes, that
   is right" - a returning patient gets a second card on purpose.

   So it is gold, not red. Nothing here is broken. Colouring it as an error
   would teach people to clear it rather than read it, and the one time it
   matters is the time somebody clears it without looking. */
.issuebar {
  padding: 0.6rem 1.2rem 0.75rem;
  background: var(--watch-tint);
  border-bottom: 1px solid var(--watch);
  font-size: 0.78rem;
  color: var(--watch);
}

.issuebar-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.7rem;
  margin-bottom: 0.5rem;
}

.issuebar-tag {
  font-family: var(--mono);
  font-size: 0.6rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  padding: 0.15rem 0.45rem;
  background: var(--watch);
  color: var(--cream);
}

.issue {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 0.5rem 1.1rem;
  padding: 0.5rem 0;
  border-top: 1px solid rgba(0, 0, 0, .08);
}

.issue-what { flex: 1 1 15rem; min-width: 0; }
.issue-what strong { display: block; font-size: 0.8rem; }

/* The sentence that lets somebody decide without opening either card. It is
   the whole point of the row, so it is not hidden behind a hover. */
.issue-why {
  display: block;
  margin-top: 0.15rem;
  font-size: 0.72rem;
  line-height: 1.45;
  opacity: 0.85;
}

.issue-cards { display: flex; flex-wrap: wrap; gap: 0.6rem; }

.issue-card {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  padding: 0.35rem 0.55rem;
  background: var(--white);
  border: 1px solid rgba(0, 0, 0, .1);
}

.issue-name { font-weight: 600; color: inherit; }

/* Email and phone are shown because they are usually what settles it: two
   cards reaching the same person at different addresses is the case a CRM
   cannot see. */
.issue-meta {
  font-size: 0.68rem;
  opacity: 0.75;
  font-variant-numeric: tabular-nums;
}

.issue-keep { margin-left: auto; }

.issue-act {
  font: inherit;
  font-size: 0.7rem;
  padding: 0.2rem 0.55rem;
  background: var(--watch);
  color: var(--cream);
  border: 0;
  cursor: pointer;
}

/* "Keep separate" is the answer most often correct, so it is present without
   being the loudest thing in the row. */
.issue-act-quiet {
  background: transparent;
  color: var(--watch);
  border: 1px solid var(--watch);
}

.issue-act:hover { filter: brightness(1.08); }
.issue-act:focus-visible { outline: 2px solid var(--gold-light); outline-offset: 2px; }

/* ------------------------------------------------ a choice with weight --- */
/* Two options where one of them sends real mail to a real person. The
   difference is not a preference, so it is not a dropdown: both are on screen
   with their consequence written next to them, and the safe one is selected
   before anybody arrives. */
.choices { display: grid; gap: 0.6rem; margin: 0.9rem 0 1.1rem; }

.choice {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.15rem 0.6rem;
  padding: 0.7rem 0.85rem;
  border: 1px solid var(--sand);
  cursor: pointer;
}

.choice:hover { background: var(--cream); }
.choice input { grid-row: 1 / span 2; align-self: start; margin-top: 0.2rem; }
.choice-what { font-weight: 600; }

.choice-why {
  grid-column: 2;
  font-size: 0.78rem;
  line-height: 1.5;
  color: var(--ink-soft, #6b6b6b);
}

/* The selected option carries the accent, so which one is armed is readable
   from across a desk rather than by finding a filled dot. */
.choice:has(input:checked) { border-color: var(--forest); background: var(--cream); }
.choice:focus-within { outline: 2px solid var(--gold-light); outline-offset: 2px; }


/* ------------------------------------------------------ the sales mirror ---
   Phase one of the sales board, 2 October 2026: leads read from Lead
   Connector. Everything here is an addition for that board; no concierge rule
   is changed. */
.mirror-note {
  margin: 0 0 1rem;
  padding: .7rem .9rem;
  border: 1px solid var(--sand);
  border-left: 3px solid var(--gold);
  background: var(--cream-warm);
  font-size: .86rem;
  color: var(--ink-soft);
}

/* The next call: the one line on a lead with a time on it. */
.card-call {
  font-family: var(--mono);
  font-size: .72rem;
  color: var(--forest);
}

/* Where the lead came from and what it is worth. A value, not a sentence. */
.card-src {
  font-family: var(--mono);
  font-size: .68rem;
  color: var(--ink-mute);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}

/* Dormant leads, folded under their column. */
.col-dormant { margin-top: .6rem; }
.col-dormant > summary {
  cursor: pointer;
  padding: .45rem .2rem;
  font-family: var(--mono);
  font-size: .72rem;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--ink-mute);
}
.col-dormant > summary:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 2px;
}
.col-dormant[open] > summary { margin-bottom: .4rem; }
.card-quiet { opacity: .78; margin-bottom: .4rem; }
.card-quiet:hover, .card-quiet:focus-visible { opacity: 1; }

/* The drawer's "Lead" section: label and value, side by side. */
.lead-facts {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: .3rem .9rem;
  margin: 0 0 .9rem;
  font-size: .88rem;
}
.lead-facts dt { color: var(--ink-mute); }
.lead-facts dd { margin: 0; color: var(--ink); overflow-wrap: anywhere; }

/* How many leads a view holds, on its tab. A value, so mono. */
.tab-n {
  margin-left: .35rem;
  font-family: var(--mono);
  font-size: .78em;
  opacity: .75;
}
.mirror-empty { border-left-color: var(--forest); }
.mirror-empty a { color: var(--forest); text-decoration: underline; }

/* The lead's actions in the drawer: Showed, No show, Claim. */
.lead-actions { display: flex; flex-wrap: wrap; gap: .5rem; margin: 0 0 .5rem; }
/* SIDE BY SIDE, sharing the row. Wil, 6 October 2026: Showed and No show are
   one decision with two answers, so they sit together; a lone action (Claim,
   Send next-step email) still takes the whole row. */
.lead-actions > .button { flex: 1 1 0; width: auto; min-width: 7rem; margin: 0; }
.lead-note { min-height: 1.2em; margin: 0 0 .8rem; }

/* The sales board's Calls view: a list by day. */
.calls { max-width: 60rem; margin: 0 auto 2rem; }
.calls-note, .calls-none { color: var(--ink-mute); margin: 0 0 1rem; }
.calls-day { margin: 0 0 1.5rem; }
.calls-head {
  display: flex; align-items: baseline; gap: .6rem;
  margin: 0 0 .5rem; padding-bottom: .35rem;
  border-bottom: 1px solid var(--hair);
  font-size: 1.15rem;
}
.calls-day.is-today .calls-head { border-bottom-color: var(--forest); }
.calls-date, .calls-count { font-family: var(--mono); font-size: .78rem; color: var(--ink-mute); }
.calls-count { margin-left: auto; }
.card.calls-row {
  display: grid; grid-template-columns: 4rem minmax(0, 1fr) minmax(0, 14rem) 9rem;
  align-items: center; gap: .9rem;
  width: 100%; max-width: none; margin: 0 0 .4rem; text-align: left;
}
.calls-time { font-family: var(--mono); font-size: 1rem; font-variant-numeric: tabular-nums; }
.calls-who { display: flex; flex-direction: column; min-width: 0; }
.calls-kind, .calls-where { font-size: .82rem; color: var(--ink-mute); }
.calls-where { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.calls-row.is-over .calls-time { color: var(--ink-mute); text-decoration: line-through; }
@media (max-width: 640px) {
  .card.calls-row { grid-template-columns: 3.5rem minmax(0, 1fr); }
  .calls-where, .calls-row .card-who { grid-column: 2; }
}

/* The sales board's "Needs cleaning up" banner, above the stages. */
.checks {
  margin: 0 0 1rem; padding: .6rem .8rem;
  background: var(--watch-tint); border-left: 3px solid var(--watch);
  color: var(--watch); font-size: .78rem;
}
.checks-row { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem .5rem; }
.check {
  display: inline-flex; align-items: baseline; gap: .4rem;
  padding: .2rem .55rem;
  background: var(--white); border: 1px solid rgba(0, 0, 0, .12);
  color: var(--ink-soft); text-decoration: none;
}
.check:hover { border-color: var(--watch); }
.check:focus-visible { outline: 2px solid var(--forest); outline-offset: 1px; }
.check.is-on { background: var(--watch); border-color: var(--watch); color: var(--cream); }
.check-n { font-family: var(--mono); font-weight: 600; font-variant-numeric: tabular-nums; color: var(--watch); }
.check.is-on .check-n { color: var(--cream); }
.checks-do { margin: .55rem 0 0; color: var(--ink-soft); }
.checks-do a { margin-left: .5rem; color: var(--forest); text-decoration: underline; }

/* Sales letters: each letter's facts beside its framed preview. */
.letter { margin-top: 1.25rem; }
.letter-head { display: flex; align-items: baseline; gap: .8rem; flex-wrap: wrap; }
.letter-state {
  font-family: var(--mono); font-size: .68rem; letter-spacing: .12em;
  text-transform: uppercase; padding: .15rem .45rem;
  background: var(--watch-tint); color: var(--watch); border: 1px solid var(--watch);
}
.letter-facts { display: grid; grid-template-columns: 6.5rem minmax(0, 1fr); gap: .35rem 1rem; margin: .75rem 0 1rem; }
.letter-facts dt { font-family: var(--mono); font-size: .72rem; text-transform: uppercase; letter-spacing: .08em; color: var(--ink-mute); padding-top: .15rem; }
.letter-facts dd { margin: 0; }
.letter-frame {
  display: block; width: 100%; max-width: 680px; height: 1180px;
  border: 1px solid var(--hair); background: var(--cream);
}

/* Consultations: Dr. Zermeno's list. One row per consultation, the time on the
   left in mono, the two buttons on the right. */
.consult {
  display: grid; grid-template-columns: 8rem minmax(0, 1fr) auto;
  align-items: center; gap: .5rem 1rem;
  padding: .7rem 0; border-top: 1px solid var(--hair);
}
.consult:first-of-type { border-top: 0; }
.consult-when { display: flex; flex-direction: column; font-family: var(--mono); }
.consult-day { font-size: .72rem; text-transform: uppercase; letter-spacing: .08em; color: var(--ink-mute); }
.consult-time { font-size: 1.05rem; font-variant-numeric: tabular-nums; }
.consult-due .consult-day { color: var(--watch); }
.consult-who { display: flex; flex-direction: column; min-width: 0; }
.consult-name { font-weight: 600; }
.consult-meta { font-size: .82rem; color: var(--ink-mute); overflow-wrap: anywhere; }
.consult-act { display: flex; gap: .5rem; }
.consult-act form { margin: 0; }
.consult-n { font-family: var(--mono); font-size: .8rem; color: var(--ink-mute); margin-left: .4rem; }
.consult-none, .consult-note { color: var(--ink-mute); }
@media (max-width: 640px) {
  .consult { grid-template-columns: 5.5rem minmax(0, 1fr); }
  .consult-act { grid-column: 1 / -1; }
}

/* "Viewing as" - an administrator seeing the app as somebody else. Loud on
   purpose, and on every page: while it shows, nothing can be changed. */
.viewas {
  position: sticky; top: 0; z-index: 60;
  display: flex; flex-wrap: wrap; align-items: center; gap: .5rem 1rem;
  padding: .55rem 1rem;
  background: var(--watch); color: var(--cream);
  font-size: .85rem;
}
.viewas-tag {
  font-family: var(--mono); font-size: .66rem; letter-spacing: .14em;
  text-transform: uppercase; padding: .1rem .4rem;
  border: 1px solid var(--cream);
}
.viewas-out { margin: 0 0 0 auto; }
.viewas-out button {
  font: inherit; font-weight: 600; padding: .25rem .7rem; cursor: pointer;
  background: var(--cream); color: var(--watch); border: 0;
}
.viewas-out button:focus-visible { outline: 2px solid var(--cream); outline-offset: 2px; }

/* The sales board's "N more - See all" under a column, and the page that shows
   one stage in full: its cards in a grid across the page rather than a column
   hundreds of cards long. */
.col-more { margin: .5rem 0 .25rem; font-size: .8rem; color: var(--ink-mute); }
.col-more a { color: var(--forest); text-decoration: underline; }
.stage-only a { margin-left: .5rem; color: var(--forest); text-decoration: underline; }
.board.board-one {
  /* THE WHOLE STAGE SCROLLS, NOT A COLUMN INSIDE IT. The board is a fixed-height
     rail that only scrolls sideways, and its columns scroll themselves; with one
     stage laid out as a grid neither applies, so without this the cards below
     the fold could not be reached at all. Found by Wil on 6 October 2026. */
  display: block; overflow-x: hidden; overflow-y: auto;
  cursor: default; touch-action: auto;
}
.board.board-one .col-body { touch-action: auto; cursor: default; }
.board.board-one .col { width: auto; max-width: none; }
.board.board-one .col-body {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr));
  gap: .6rem; align-items: start;
  max-height: none; overflow: visible;
}
.board.board-one .col { max-height: none; }
.board.board-one .col-body > .col-more,
.board.board-one .col-body > .col-dormant,
.board.board-one .col-body > .col-none { grid-column: 1 / -1; }

/* The recovery tools under the lead's next step, and the choice they ask. */
.lead-tools { margin-top: .1rem; }
.lead-tools > .button { font-size: .82rem; padding-top: .45rem; padding-bottom: .45rem; }
.lead-ask {
  display: flex; flex-wrap: wrap; align-items: center; gap: .5rem;
  margin: .25rem 0 .5rem; padding: .6rem; background: var(--cream-warm);
  border: 1px solid var(--sand);
}
.lead-ask-label { font-weight: 600; font-size: .85rem; flex: 1 1 100%; }
.lead-ask-select { flex: 1 1 10rem; padding: .45rem .5rem; font: inherit; }
.lead-ask > .button { flex: 0 0 auto; width: auto; }
.lead-ask-warn { flex: 1 1 100%; margin: 0; font-size: .82rem; color: var(--ink-soft); }
.lead-ask-words { flex: 1 1 100%; padding: .45rem .5rem; font: inherit; }
.lead-ask-words[hidden] { display: none; }
.rl-at.ll-said { white-space: normal; max-width: 22rem; }
.card-tried { font-size: 11px; color: var(--ink-mute); }
.card-draft { font-size: 11px; font-weight: 600; color: var(--ink-soft); }

/* The sales board's slim bar: the cleaning-up flags and the sort as two
   dropdowns, and on a one-stage page, where you are. One row on a phone. */
.leadbar {
  display: flex; flex-wrap: wrap; align-items: center; gap: .5rem .75rem;
  margin: 0 0 .75rem;
}
.dd { position: relative; }
.dd > summary {
  list-style: none; cursor: pointer; user-select: none;
  display: inline-flex; align-items: center; gap: .45rem;
  padding: .4rem .75rem; font-size: .85rem;
  background: var(--white); border: 1px solid var(--sand); color: var(--ink-soft);
}
.dd > summary::-webkit-details-marker { display: none; }
.dd > summary::after { content: "\25BE"; font-size: .75rem; color: var(--ink-mute); }
.dd > summary:focus-visible { outline: 2px solid var(--forest); outline-offset: 1px; }
.dd.is-on > summary { background: var(--watch); border-color: var(--watch); color: var(--cream); }
.dd.is-on > summary::after { color: var(--cream); }
.dd-n { font-family: var(--mono); font-weight: 600; font-variant-numeric: tabular-nums; }
.dd.is-on > summary .dd-n { color: var(--cream); }
.dd-menu {
  position: absolute; z-index: 40; top: calc(100% + 4px); left: 0;
  min-width: 18rem; max-width: calc(100vw - 2rem);
  background: var(--white); border: 1px solid var(--sand);
  box-shadow: 0 6px 18px rgba(0, 0, 0, .12); padding: .3rem 0;
}
.dd-item {
  display: flex; gap: .6rem; align-items: baseline;
  padding: .45rem .8rem; font-size: .85rem; color: var(--ink-soft); text-decoration: none;
}
.dd-item:hover, .dd-item:focus-visible { background: var(--cream-warm); outline: none; }
.dd-item.is-on { font-weight: 600; color: var(--forest); }
.dd-item .dd-n { color: var(--watch); min-width: 2.5rem; text-align: right; }
.leadbar-clear { font-size: .82rem; color: var(--forest); text-decoration: underline; }
.leadbar-stage { font-size: .85rem; color: var(--ink-soft); }
.leadbar-stage a { margin-left: .5rem; color: var(--forest); text-decoration: underline; }
.leadbar-do { margin: -.35rem 0 .75rem; font-size: .82rem; color: var(--ink-mute); }

/* Playbook: one panel per flag - the count and the way to those leads in the
   head, then why, then the steps in order, then the drawer buttons. */
.play { margin-top: 1rem; }
.play-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: .4rem 1rem; }
.play-head h2 { margin: 0; }
.play-n { font-family: var(--mono); font-size: .78rem; color: var(--watch); }
.play-go { margin-left: auto; font-size: .85rem; color: var(--forest); text-decoration: underline; }
.play-why { max-width: 70ch; color: var(--ink-soft); margin: .5rem 0; }
.play-steps { max-width: 70ch; margin: .25rem 0 .75rem 1.2rem; padding: 0; }
.play-steps li { margin: .25rem 0; }
.play-buttons { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem; margin: 0; }
.play-label { font-size: .8rem; color: var(--ink-mute); margin-right: .25rem; }
.play-btn {
  font-size: .8rem; padding: .2rem .55rem;
  background: var(--forest); color: var(--cream);
}
.play-btn.play-soon { background: transparent; color: var(--ink-quiet, #7A7161); border: 1px dashed var(--sand); }

/* The flags and the sort in the green bar: closed, they read as two more tabs. */
.tabs .dd > summary {
  font-family: var(--sans); font-size: 11px; font-weight: 600; letter-spacing: 1.2px;
  text-transform: uppercase; padding: 7px 12px;
  background: transparent; color: var(--gold-light); border: 1px solid var(--gold);
}
.tabs .dd > summary::after { color: var(--gold-light); }
.tabs .dd > summary:hover { background: rgba(184, 145, 80, .18); color: var(--cream); }
.tabs .dd.is-on > summary { background: var(--gold); color: var(--forest-deep); }
.tabs .dd.is-on > summary .dd-n, .tabs .dd.is-on > summary::after { color: var(--forest-deep); }
.tabs .dd-menu { text-transform: none; letter-spacing: 0; right: 0; left: auto; }
.tabs .dd-sort .dd-menu { min-width: 13rem; }

/* The note, with its way out. */
.mirror-note-dismiss { display: flex; align-items: flex-start; gap: 1rem; }
.mirror-note-dismiss p { margin: 0; flex: 1; }
.mirror-note-dismiss form { margin: 0; }
.mirror-note-x {
  font: inherit; font-size: .9rem; line-height: 1; padding: .3rem .45rem; cursor: pointer;
  background: transparent; border: 1px solid var(--sand); color: var(--ink-mute);
}
.mirror-note-x:hover { color: var(--ink); border-color: var(--ink-mute); }

/* "Needs cleaning up" SET APART from the tabs: a flag, the watch colours, and
   a gap before it, so it reads as a warning and not as one more view. */
.tabs .dd-flags { margin-left: 18px; }
.tabs .dd-flags > summary {
  background: var(--watch-tint); color: var(--watch); border-color: var(--watch-tint);
}
.tabs .dd-flags > summary::after { color: var(--watch); }
.tabs .dd-flags > summary:hover { background: var(--cream); color: var(--watch); }
.tabs .dd-flags > summary .dd-n { color: var(--fail); }
.tabs .dd-flags.is-on > summary { background: var(--watch); color: var(--cream); border-color: var(--watch); }
.tabs .dd-flags.is-on > summary .dd-n, .tabs .dd-flags.is-on > summary::after { color: var(--cream); }
.dd-flag { font-size: 13px; line-height: 1; }

/* The action under a sales column's name: what the advisor does here. */
.col-desc.col-action { font-weight: 600; color: var(--forest); font-size: 11.5px; }


/* PRICES HIDDEN ON THIS SCREEN (12 October 2026, for a screen-shared demo):
   /prices/hide sets it, /prices/show takes it off. */
body.hide-prices .money,
body.hide-prices .field-money { display: none !important; }

/* Dr. Zermeño's sample patients (12 October 2026): tagged, and their
   pre-screening link in the page's own green. */
.consult-sample { margin-left: 6px; padding: 1px 7px; font-family: var(--mono); font-size: 10px;
  letter-spacing: .12em; text-transform: uppercase; font-weight: 500; vertical-align: 2px;
  color: var(--forest); background: var(--cream-alt); border: 1px solid var(--edge); }
.consult-meta a { color: var(--forest); font-weight: 600; }

/* Editing a person's name and email on People (12 October 2026). */
.person-edit { margin-top: 4px; }
.person-edit summary { cursor: pointer; font-size: .78rem; font-weight: 600; color: var(--forest); }
.person-edit-form { display: grid; gap: 6px; margin-top: 6px; min-width: 14rem; }
.person-edit-form label { display: grid; gap: 2px; font-size: .75rem; color: var(--ink-mute); }
.person-edit-form input { margin: 0; padding: .3rem .45rem; font-size: .85rem; }
.person-edit-form .mini { justify-self: start; }

/* NO TIME ZONE CHOSEN YET (13 October 2026). A red tint (Wil: "let's use a
   red tint"), lighter than the dev bar's solid red so the two read apart. */
.tzbar {
  display: flex; flex-wrap: wrap; align-items: center; gap: .4rem 1rem;
  padding: .55rem 1.5rem;
  background: #FBEAE8; color: #8E3320;
  border-bottom: 1px solid #E8B4AC;
  font-size: .86rem;
}
.tzbar a { font-weight: 600; color: #8E3320; text-decoration: underline; }
/* A red warning triangle with a white "!", drawn in CSS so it needs no image. */
.tzbar-icon {
  flex: none; display: inline-block; width: 20px; height: 18px;
  background: #C0281F; color: #FFFFFF;
  clip-path: polygon(50% 0, 100% 100%, 0 100%);
  font: 700 11px/21px var(--sans, Arial, sans-serif); text-align: center;
}
@media (max-width: 640px) {
  .tzbar { padding: .4rem 1rem; font-size: .78rem; gap: .2rem .6rem; line-height: 1.4; }
}

/* Settings, under the profile photo. */
.settings-zone select { max-width: 26rem; }
.settings-now { margin: .3rem 0 0; font-size: .85rem; color: var(--ink-mute); }
.settings-browser { margin: .6rem 0 0; font-size: .85rem; color: var(--ink-soft); }
.settings-browser:empty { display: none; }

/* Admin settings: the keys' latest check (14 October 2026). The key's name and
   what it stops stay in a narrow first column so the status stays in view. */
.key-table td:first-child { width: 13rem; min-width: 10rem; }
.key-table td:first-child .sub { display: block; font-size: .78rem; line-height: 1.35; }
.key-table td:nth-child(3) { min-width: 12rem; }

/* THE SIGN-OFF ON EVERY DOCUMENT A PATIENT SIGNS (public/_standard.html
   signed_footer; Wil, 19 October 2026): the gold lockup over the address. */
.signed-footer { padding: 40px 24px 48px; text-align: center; border-top: 1px solid var(--sand); background: var(--white); }
.signed-footer-logo { display: block; width: 230px; max-width: 70%; height: auto; margin: 0 auto 16px; }
.signed-footer p { margin: 0 0 6px; font-size: 13px; color: var(--ink-mute); }
.signed-footer-note { margin-top: 12px !important; font-size: 12px !important; color: var(--ink-quiet) !important; }

/* ---------------------------------------------------------- recent leads ---
   Who came in, whether they booked a Discovery call, and how long it took
   (Wil, 19 October 2026). Tiles first, then the filters, then one row a lead. */
.rl-tiles { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: .8rem; margin: 1.2rem 0; }
.rl-tile { padding: 1rem 1.1rem; display: flex; flex-direction: column; gap: .2rem; border-top: 3px solid var(--forest); }
.rl-n { font-family: var(--serif); font-size: 2.1rem; line-height: 1; color: var(--forest); font-variant-numeric: lining-nums tabular-nums; }
.rl-l { font-size: .82rem; color: var(--ink-soft); }
.rl-filters { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem .8rem; margin: 0 0 1rem; }
.rl-filters label { font-family: var(--mono); font-size: .64rem; letter-spacing: .12em; text-transform: uppercase; color: var(--ink-mute); }
.rl-filters select { width: auto; max-width: 100%; margin: 0; padding: .4rem .5rem; border: 1px solid var(--rule-strong); background: var(--paper); font: inherit; font-size: .88rem; }
.rl-filters .rl-go { width: auto; margin-top: 0; padding: .4rem .9rem; }
.rl-wrap { overflow-x: auto; padding: 0; }
.rl-table { width: 100%; min-width: 46rem; border-collapse: collapse; font-size: .88rem; }
.rl-table th { text-align: left; padding: .6rem .8rem; background: var(--cream-alt); font-family: var(--mono); font-size: .64rem;
               letter-spacing: .1em; text-transform: uppercase; color: var(--ink-soft); font-weight: 500; border-bottom: 1px solid var(--rule-strong); }
.rl-table td { padding: .6rem .8rem; border-bottom: 1px solid var(--rule); vertical-align: top; }
.rl-table tbody tr:last-child td { border-bottom: 0; }
.rl-name a { color: var(--forest); font-weight: 600; text-decoration: none; }
.rl-name a:hover { text-decoration: underline; }
.rl-when { white-space: nowrap; color: var(--ink-soft); font-variant-numeric: tabular-nums; }
.rl-took { white-space: nowrap; font-weight: 600; color: var(--forest); font-variant-numeric: tabular-nums; }
.rl-waiting .rl-took { color: var(--ink-mute); font-weight: 400; }
.rl-at { display: block; margin-top: .2rem; font-size: .78rem; color: var(--ink-soft); white-space: nowrap; font-variant-numeric: tabular-nums; }
.rl-started { display: block; margin-top: .15rem; font-size: .78rem; font-weight: 600; color: var(--gold-text); }
@media (max-width: 760px) { .rl-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* ------------------------------------------------------------ follow-up ---
   The booking follow-up's status page: one row a lead, one column a step. */
.fu-state { margin: 1rem 0; }
.fu-state p { margin: 0; }
.fu-state .fu-started { margin-top: .3rem; font-size: .88rem; color: var(--ink-soft); font-variant-numeric: tabular-nums; }
.fu-state form { margin-top: .7rem; display: flex; align-items: center; gap: .8rem; flex-wrap: wrap; }
.fu-start { width: auto; margin: 0; padding: .5rem 1rem; }
.fu-note { font-size: .85rem; color: var(--ink-soft); }
.fu-table { min-width: 60rem; }
.fu-sub { display: block; font-size: .78rem; color: var(--ink-mute); font-weight: 400; margin-top: .1rem; }
.fu-step { white-space: nowrap; }
.fu-when { display: block; margin-top: .2rem; font-size: .78rem; color: var(--ink-soft); font-variant-numeric: tabular-nums; }
.fu-plan { font-size: .82rem; font-weight: 600; color: var(--forest); }
.fu-off { font-size: .82rem; color: var(--ink-mute); }
.fu-stopped td { background: var(--cream-warm); }
.fu-stop { white-space: normal; text-align: left; }
.fu-after { display: block; margin-top: .3rem; font-size: .78rem; color: var(--ink-soft); }
.fu-status form { margin-top: .4rem; }
.fu-status button.mini { margin: 0; }
/* each step a button that opens its preview */
.fu-cell { display: block; width: 100%; margin: 0; padding: .35rem .45rem; text-align: left; background: transparent;
           color: inherit; border: 1px solid transparent; font: inherit; font-weight: 400; letter-spacing: 0; cursor: pointer; }
.fu-cell:hover, .fu-cell:focus-visible { background: var(--cream); border-color: var(--rule-strong); }
.fu-peek { display: block; margin-top: .25rem; font-size: .7rem; font-weight: 600; letter-spacing: .04em; color: var(--forest);
           opacity: 0; transition: opacity .12s; }
.fu-cell:hover .fu-peek, .fu-cell:focus-visible .fu-peek { opacity: 1; }
@media (hover: none) { .fu-peek { opacity: 1; } }
.fu-dialog { width: min(760px, 94vw); max-height: 90vh; padding: 0; border: 1px solid var(--rule-strong);
             border-top: 3px solid var(--gold); background: var(--paper); color: var(--ink); }
.fu-dialog::backdrop { background: rgba(5, 53, 32, .45); }
.fu-dialog-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 1rem;
                  padding: 1rem 1.2rem .8rem; border-bottom: 1px solid var(--rule); }
.fu-dialog-head h2 { margin: .1rem 0 0; font-size: 1.35rem; }
.fu-dialog-head button.mini { width: auto; margin: 0; flex: none; }
.fu-dialog-kicker { margin: 0; font-family: var(--mono); font-size: .64rem; letter-spacing: .12em; text-transform: uppercase; color: var(--gold-text); }
.fu-dialog-subject { margin: .4rem 0 0; font-size: .9rem; color: var(--ink-soft); }
.fu-sms { margin: 1.2rem; padding: .9rem 1rem; max-width: 26rem; white-space: pre-wrap; font-size: .95rem; line-height: 1.5;
          background: var(--ok-tint); border-left: 3px solid var(--ok); }
.fu-frame { display: block; width: 100%; height: min(70vh, 720px); border: 0; background: var(--cream); }
.fu-sms[hidden], .fu-frame[hidden], .fu-dialog-subject[hidden] { display: none; }

/* THE TITLE BAND FOLLOWS A FULL-WIDTH PAGE (19 October 2026): with only the
   page widened, the title sat in the old centred column and read as floating. */
body:has(main.page-wide) .masthead-inner { max-width: none; }

/* ------------------------------------------------- the sales calendar --- */
/* THE SALES CALENDAR (19 October 2026): everybody's calls on a time grid, and
   a strip at the left of each day saying who the round robin offers a new
   booking to first. Each advisor keeps the color the availability page gave
   them. Gold is a fill and an edge here, never type: the light-ground rule. */
.adv-kyle     { --adv: var(--forest); --adv-tint: #DCE7E0; --adv-ink: var(--cream); }
.adv-tavia    { --adv: var(--gold);   --adv-tint: #F3E8CF; --adv-ink: var(--ink); }
.adv-cristina { --adv: #5E7466;       --adv-tint: #E3E9E4; --adv-ink: var(--cream); }
.adv-clinical { --adv: #6B5B7A;       --adv-tint: #ECE7F0; --adv-ink: var(--cream); }
.adv-shared   { --adv: var(--sand-deep); --adv-tint: var(--cream-alt); --adv-ink: var(--ink); }
.adv-other    { --adv: var(--edge);   --adv-tint: var(--cream-alt); --adv-ink: var(--cream); }

.sc-dot { display: inline-block; width: .7rem; height: .7rem; border-radius: 50%;
          background: var(--adv, var(--edge)); margin-right: .35rem; vertical-align: -1px; }

.sc-legend { display: flex; flex-wrap: wrap; gap: .5rem 1.4rem; align-items: center;
             margin: -.4rem 0 1.2rem; font-size: .85rem; }
.sc-key em { font-style: normal; color: var(--ink-mute); margin-left: .3rem; font-size: .78rem; }
.sc-legend-note { flex-basis: 100%; margin: 0; max-width: 72ch; }
.sc-pick { width: auto; margin: 0; border: 0; border-radius: 0; font-size: .8rem;
           padding: 0 .6rem; background: var(--white); min-height: 2.1rem; }

.sc-week { display: grid; grid-template-columns: 4.2rem repeat(7, minmax(8.5rem, 1fr));
           overflow-x: auto; border: 1px solid var(--sand); background: var(--white); }
.sc-week.sc-one { grid-template-columns: 4.2rem minmax(16rem, 1fr); }
.sc-corner, .sc-head { border-bottom: 1px solid var(--sand); }
.sc-head { padding: .55rem .6rem; border-left: 1px solid var(--sand); min-width: 0; }
.sc-head.sc-today, .sc-body.sc-today { background-color: var(--cream-warm); }
.sc-date { font-family: var(--sans); font-weight: 600; font-size: .8rem; letter-spacing: 1px;
           text-transform: uppercase; color: var(--ink); text-decoration: none; }
.sc-head.sc-today .sc-date { color: var(--forest); text-decoration: underline; text-underline-offset: 3px; }
.sc-shifts { list-style: none; margin: .35rem 0 0; padding: 0; font-size: .72rem;
             color: var(--ink-soft); line-height: 1.45; }
.sc-shifts .sc-dot { width: .55rem; height: .55rem; }
.sc-off { color: var(--ink-quiet); }

.sc-times, .sc-body { position: relative; height: calc(var(--hours, 10) * 4.5rem); }
.sc-body { border-left: 1px solid var(--sand);
           background-image: repeating-linear-gradient(to bottom, var(--hair) 0 1px, transparent 1px 4.5rem); }
.sc-hour { position: absolute; right: .5rem; transform: translateY(-.15rem);
           font-size: .68rem; color: var(--ink-mute); font-variant-numeric: tabular-nums; }

.sc-line { position: absolute; top: 0; bottom: 0; left: 0; width: 1.1rem;
           background: var(--cream-alt); }
.sc-one .sc-line { width: 8rem; }
.sc-seg { position: absolute; left: 0; right: 0; background: var(--adv); color: var(--adv-ink);
          overflow: hidden; display: flex; flex-direction: column; align-items: center;
          padding-top: .25rem; font-size: .66rem; line-height: 1.2; text-align: center;
          border-bottom: 1px solid var(--white); }
.sc-seg b { font-weight: 700; letter-spacing: .5px; }
.sc-seg small { font-size: .62rem; opacity: .9; margin-top: .15rem; }
.sc-calls { position: absolute; top: 0; bottom: 0; left: 1.3rem; right: .2rem; }
.sc-one .sc-calls { left: 8.4rem; }
.sc-call { position: absolute; left: 0; width: 100%; box-sizing: border-box; overflow: hidden;
           background: var(--adv-tint); border-left: 4px solid var(--adv);
           padding: .15rem .35rem; font-size: .72rem; line-height: 1.25; min-height: 1.2rem;
           border-bottom: 1px solid var(--white); }
.sc-call .sc-name { display: block; font-weight: 600; color: var(--ink); text-decoration: none;
                    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sc-call a.sc-name:hover { text-decoration: underline; }
.sc-time { font-variant-numeric: tabular-nums; color: var(--ink-mute); font-size: .68rem; }
.sc-who { display: block; color: var(--ink-mute); font-size: .66rem; }
.sc-cancelled, .sc-cancelled .sc-name { text-decoration: line-through; opacity: .6; }

.sc-list { margin-top: 1.4rem; overflow-x: auto; }
.sc-list h2 { margin-top: 0; }

.sc-month .cal-cell { min-height: 7.5rem; }
.sc-on { display: flex; gap: .15rem; margin: .1rem 0 .3rem; }
.sc-on .sc-dot { width: .5rem; height: .5rem; margin: 0; }
.sc-mcall { font-size: .72rem; line-height: 1.3; padding: .1rem .3rem; margin-bottom: .15rem;
            background: var(--adv-tint); border-left: 3px solid var(--adv);
            white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sc-mcall .sc-name { color: var(--ink); text-decoration: none; font-weight: 600; }
.sc-list a { color: var(--forest); font-weight: 600; text-underline-offset: 2px; }

/* BOOK A CALL from the Sales Calendar (20 October 2026). A native dialog:
   focus is held inside it and Escape closes it, with no code of ours. */
.sc-book-open { border: 0; cursor: pointer; }
/* THE SALES CALENDAR'S STRIP TAKES THE WHOLE ROW under the heading, so
   `.cal-right` can put Show cancelled and Book a call at its far end. */
.sc-controls { flex: 1 1 100%; }
.sc-book { width: min(40rem, calc(100vw - 2rem)); max-height: calc(100vh - 2rem); padding: 0;
           border: 1px solid var(--sand-deep); border-radius: 0; background: var(--cream-warm);
           color: var(--ink); }
.sc-book::backdrop { background: rgba(5, 53, 32, .45); }
.sc-book form { display: flex; flex-direction: column; gap: 0; }
.sb-head { padding: 1.2rem 1.4rem .6rem; border-bottom: 1px solid var(--sand); }
.sb-head h2 { margin: 0 0 .3rem; }
.sb-head p { margin: 0; }
.sb-body { padding: .6rem 1.4rem; display: grid; gap: .9rem; }
.sb-body[hidden], .sb-results[hidden], .sb-chosen[hidden], .sc-book [hidden] { display: none; }
.sb-step { border: 0; padding: 0; margin: 0; display: grid; gap: .45rem; min-width: 0; }
.sb-step legend { font-family: var(--sans); font-weight: 600; font-size: .8rem; letter-spacing: 1px;
                  text-transform: uppercase; color: var(--forest); margin-bottom: .35rem; padding: 0; }
.sb-step legend .fine { text-transform: none; letter-spacing: 0; font-weight: 400; }
.sb-step label { font-size: .82rem; color: var(--ink-soft); display: grid; gap: .2rem; }
.sb-step input[type=search], .sb-step input[type=email], .sb-step input[type=tel],
.sb-step input[type=text], .sb-step select, .sb-step textarea {
  width: 100%; box-sizing: border-box; margin: 0; border-radius: 0; font-size: .9rem;
  font-family: var(--sans); padding: .55rem .7rem; border: 1px solid var(--edge);
  background: var(--white); color: var(--ink); min-height: 2.5rem; }
.sb-step input:focus-visible, .sb-step select:focus-visible, .sb-step textarea:focus-visible {
  outline: 2px solid var(--forest); outline-offset: 1px; }
.sb-step input[readonly] { background: var(--cream-alt); }
.sb-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)); gap: .6rem; }
.sb-results { list-style: none; margin: 0; padding: 0; border: 1px solid var(--sand);
              background: var(--white); max-height: 12rem; overflow-y: auto; }
.sb-results li.fine { padding: .5rem .7rem; }
.sb-pick { display: flex; flex-direction: column; align-items: flex-start; width: 100%; margin: 0;
           padding: .45rem .7rem; border: 0; border-bottom: 1px solid var(--hair); border-radius: 0;
           background: none; text-align: left; cursor: pointer; color: var(--ink); font-size: .85rem; }
.sb-pick:hover, .sb-pick:focus-visible { background: var(--cream); }
.sb-pick span { color: var(--ink-mute); font-size: .76rem; }
.sb-chosen { margin: 0; font-size: .88rem; display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; }
.sb-chosen .button-quiet { width: auto; margin: 0; }
.sb-radio { display: flex !important; align-items: center; gap: .5rem; grid-template-columns: none; font-size: .9rem !important; }
.sb-radio input { width: auto; margin: 0; }
.sb-times { display: flex; flex-wrap: wrap; gap: .35rem; }
.sb-time { width: auto; margin: 0; padding: .35rem .7rem; border: 1px solid var(--sand-deep); border-radius: 0;
           background: var(--white); color: var(--ink); font-size: .82rem; cursor: pointer;
           font-variant-numeric: tabular-nums; }
.sb-time:hover { background: var(--cream); }
.sb-time[aria-pressed="true"] { background: var(--forest); border-color: var(--forest); color: var(--cream); }
.sb-say { margin: 0; padding: 0 1.4rem; min-height: 1.2rem; font-size: .88rem; }
.sb-say.sb-bad { color: var(--fail); }
.sb-say.sb-ok { color: var(--forest); font-weight: 600; padding-block: .9rem; }
.sb-foot { display: flex; gap: .6rem; padding: .8rem 1.4rem 1.2rem; }
.sb-foot .button, .sb-foot .button-quiet { width: auto; margin: 0; }

/* THE ERROR LOG (20 October 2026). The tiles are links to their own kind. */
a.el-tile { text-decoration: none; color: inherit; }
.rl-tiles.el-tiles { grid-template-columns: repeat(5, minmax(0, 1fr)); }
@media (max-width: 760px) {
  .rl-tiles.el-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .el-tiles > :last-child:nth-child(odd) { grid-column: span 2; }
}
a.el-tile:hover, a.el-tile.on { border-top-color: var(--gold); }
.el-first { display: block; font-size: .72rem; color: var(--ink-mute); }
.el-times { font-variant-numeric: tabular-nums; text-align: right; }
.el-pill { text-transform: capitalize; margin-right: .3rem; }
.el-pill-error { color: var(--fail); border-color: var(--fail); background: var(--fail-tint); }
.el-pill-warning { color: var(--watch); border-color: var(--watch); background: var(--watch-tint); }
.el-pill-notice { color: var(--ink-mute); border-color: var(--sand-deep); }
.el-detail summary { cursor: pointer; color: var(--forest); font-weight: 600; }
.el-detail pre { white-space: pre-wrap; word-break: break-word; max-width: 46rem; max-height: 22rem;
                 overflow: auto; font-family: var(--mono); font-size: .74rem; line-height: 1.45;
                 background: var(--cream-alt); padding: .7rem .8rem; margin: .4rem 0 0; }

/* ------------------------------------------------- the sales dashboard --- */
/* THE SALES DASHBOARD (20 October 2026). Flags first, coloured by how much
   they matter; then the numbers against their normal; then the charts. Gold
   fills and rules only - never type on cream. */
.sd-h { font-family: var(--serif); font-size: 1.35rem; font-weight: 600; color: var(--forest);
        margin: 0 0 .3rem; }
.sd-flags { margin: 1rem 0 1.4rem; }
.sd-flag-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(19rem, 1fr)); gap: .8rem; }
.sd-flag { background: var(--white); border: 1px solid var(--sand); border-left: 5px solid var(--edge);
           padding: .9rem 1rem; display: flex; flex-direction: column; gap: .35rem; min-width: 0; }
.sd-flag header { display: flex; justify-content: space-between; gap: .6rem; align-items: baseline; }
.sd-flag h3 { margin: 0; font-family: var(--sans); font-size: 1rem; font-weight: 700; color: var(--ink); }
.sd-flag p { margin: 0; font-size: .86rem; line-height: 1.45; color: var(--ink-soft); }
.sd-level { font-family: var(--mono); font-size: .62rem; letter-spacing: .14em; text-transform: uppercase;
            font-weight: 600; }
.sd-since { font-size: .72rem; color: var(--ink-mute); }
.sd-urgent { border-left-color: var(--fail); background: var(--fail-tint); }
.sd-urgent .sd-level { color: var(--fail); }
.sd-watch { border-left-color: var(--gold); }
.sd-watch .sd-level { color: var(--watch); }
.sd-notice { border-left-color: var(--edge); }
.sd-notice .sd-level { color: var(--ink-mute); }
.sd-leads { list-style: none; margin: .1rem 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: .25rem .5rem;
            font-size: .82rem; }
.sd-leads a { color: var(--forest); font-weight: 600; text-decoration: none; }
.sd-leads a:hover { text-decoration: underline; }
.sd-more { color: var(--ink-mute); }
.sd-go { align-self: flex-start; margin-top: .2rem; font-size: .8rem; font-weight: 600; color: var(--forest);
         text-decoration: none; border-bottom: 1px solid var(--gold); }
.sd-calm { border-left: 5px solid var(--forest); }

/* FOUR TO A ROW: the volume across the top, the work underneath. */
.sd-tiles { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: .8rem;
            margin: 0 0 1.4rem; }
@media (max-width: 60rem) { .sd-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 30rem) { .sd-tiles { grid-template-columns: 1fr; } }
.sd-tile { padding: 1rem 1.1rem; display: flex; flex-direction: column; gap: .2rem;
           border-top: 3px solid var(--forest); min-width: 0; }
.sd-n { font-family: var(--serif); font-size: 2.1rem; line-height: 1; color: var(--forest);
        font-variant-numeric: lining-nums tabular-nums; }
.sd-l { font-size: .84rem; color: var(--ink); font-weight: 600; }
.sd-normal { font-size: .76rem; color: var(--ink-mute); }
.sd-tone-low { border-top-color: var(--fail); }
.sd-tone-low .sd-n { color: var(--fail); }
.sd-tone-high { border-top-color: var(--gold); }

.sd-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(28rem, 1fr)); gap: .9rem;
           margin-bottom: 1.4rem; }
@media (max-width: 34rem) { .sd-grid { grid-template-columns: 1fr; } }
.sd-chart { padding: 1rem 1.1rem; min-width: 0; }
.sd-chart .fine { margin: 0 0 .6rem; }
.sd-svg { width: 100%; height: auto; display: block; }
.sd-band { fill: var(--done-tint); }
.sd-grid-line { stroke: var(--hair); stroke-width: 1; }
.sd-mean { stroke: var(--forest); stroke-width: 1; stroke-dasharray: 4 4; opacity: .6; }
.sd-bar { fill: var(--forest); }
.sd-bar-low { fill: var(--fail); }
.sd-bar-high { fill: var(--gold); }
.sd-bar-partial { fill: var(--sage); opacity: .55; }
.sd-bar-over { fill: var(--gold); }
.sd-limit { stroke: var(--ink); stroke-width: 2; }
.sd-tick { font-family: var(--sans); font-size: 11px; fill: var(--ink-mute); }
.sd-row-label { font-family: var(--sans); font-size: 13px; fill: var(--ink); }
.sd-row-value { font-family: var(--sans); font-size: 13px; fill: var(--ink); font-weight: 600; }
.sd-row-pct { font-weight: 400; fill: var(--ink-mute); }

.sd-table-wrap { padding: 1rem 1.1rem; margin-bottom: 1.2rem; }
.sd-table td:not(:first-child), .sd-table th:not(:first-child) { text-align: right;
                                                                  font-variant-numeric: tabular-nums; }
.sd-team td { font-weight: 700; background: var(--cream-alt); }
.sd-hot { color: var(--fail); font-weight: 700; }
.sd-foot { margin-top: .4rem; max-width: 80ch; }
.sd-sample-link { margin: 0 0 .4rem; }
.sd-sample-link a { color: var(--forest); font-weight: 600; }
.sd-bar-cancelled { fill: var(--sand-deep); }
.sd-wide { grid-column: 1 / -1; }
.sd-wide .sd-svg { max-height: 12rem; }
.sd-aim-note { margin: -.1rem 0 .6rem; }

/* ------------------------------------------------------ team signatures ---
   One design, each person's details, each with Copy (21 October 2026). The
   preview is a frame on white, because that is what a signature sits on. */
.sig-h { font-family: var(--serif); font-weight: 400; font-size: 1.35rem; color: var(--forest); margin: 1.6rem 0 .7rem; }
.sig-how { padding: 1rem 1.3rem; max-width: 48rem; }
.sig-how .sig-h { margin-top: 0; }
.sig-steps { margin: 0 0 .6rem; padding-left: 1.3rem; display: grid; gap: .3rem; }
.sig-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 26rem), 1fr)); gap: 1rem; }
.sig-card { padding: 1rem 1.1rem; display: flex; flex-direction: column; gap: .6rem; min-width: 0; }
.sig-hidden { opacity: .6; }
.sig-head { display: flex; justify-content: space-between; align-items: flex-start; gap: .8rem; flex-wrap: wrap; }
.sig-name { display: block; font-weight: 600; color: var(--ink); }
.sig-title { display: block; font-size: .82rem; color: var(--ink-soft); }
.sig-copy { width: auto; margin: 0; padding: .55rem .9rem; font-size: .85rem; }
.sig-frame { width: 100%; height: 16rem; border: 1px solid var(--sand); background: var(--white); }
.sig-said { margin: 0; min-height: 1em; }
.sig-edit summary { cursor: pointer; font-size: .85rem; color: var(--forest); font-weight: 600; }
.sig-form { display: grid; grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr)); gap: .5rem .8rem; margin-top: .6rem; align-items: end; }
.sig-form label { display: grid; gap: .2rem; font-size: .78rem; margin: 0; }
.sig-form input { width: 100%; padding: .45rem .55rem; font: inherit; border: 1px solid var(--rule); background: var(--paper); }
.sig-form .button, .sig-form-hide .button { width: auto; margin: 0; }
.sig-form-hide { margin-top: .6rem; }
.sig-add { padding: 1rem 1.3rem; margin-top: 1.6rem; }
.sig-add .sig-h { margin-top: 0; }
