/* Pocket Advocate — the stylesheet every page loads.
 * Anything that only ever renders on an admin screen lives in admin.css, which
 * is served behind a role check. Keep it that way: a selector added here is
 * downloadable by anyone who visits the site, comments included.
 */

/* ---- [hidden] IS NOT NEGOTIABLE ------------------------------------------

   PROVEN, not theorised. Rendered with the glow-up sheet attached, an element
   carrying BOTH `hidden` and a layout class was still on the page:

     .stack        hidden -> display:grid          VISIBLE
     .stack-tight  hidden -> display:grid          VISIBLE
     .pill         hidden -> display:inline-flex   VISIBLE
     .cta          hidden -> display:flex          VISIBLE

   Any author `display` beats the UA stylesheet's `[hidden] { display:none }`,
   because a class selector outranks it. So every class that sets a display
   quietly un-hides anything the markup asked to hide. The booking flow found
   it first: `<div class="stack" hidden>` would have revealed the whole second
   half of step 1. case.js, book.js and admin-case.js use the `hidden`
   attribute 146 times between them.

   This sheet had already been patching it one element at a time, five times
   over - see the comment above `.folder-tabs > a[hidden]` further down, which
   explains the mechanism. That is a bug being fixed wherever it is noticed
   rather than where it lives. Those five rules are left in place; they cost
   nothing and they are still correct.

   It belongs HERE and not in glowup.css because the admin pages load
   site.css + admin.css and never load glowup.css - and admin-case.js is the
   heaviest user of `hidden` in the repo. A guard that skips the admin half
   is not a guard.

   `hidden` is the markup saying this is not on the page; no stylesheet gets
   to disagree, which is exactly what `!important` is for. Checked before
   making it absolute: `style.display` is assigned nowhere in any served
   script, so there is no inline display for this to overrule, and nothing in
   the repo deliberately displays a hidden element.
   ------------------------------------------------------------------------ */
[hidden] { display: none !important; }

/* Pocket Advocate neon system — single-theme by design (approved 2026-07-11).
   Same tokens as PROTOTYPE.html so the shipped app matches the prototype. */
/* Eric's mockup, 2026-08-25 (visual reference only, his copy untouched):
   deep navy ground with a violet cast, indigo panels, cyan and violet
   accents, and card edges that glow rather than merely outline. Only these
   VALUES moved - no rule names a colour, so the whole app follows, and the
   other three schemes keep their own values and stay coherent.
   --magenta keeps its NAME and takes a violet value: renaming a token would
   touch every rule that reads it, for nothing. */
:root {
  --bg: #050817;
  --bg-deep: #03050E;
  --panel: #0B1128;
  --panel-2: #131C3E;
  --ink: #F0F4FF;
  --dim: #AAB4D3;
  --muted: #7884AC;
  --soft: #DCE2F3;
  --line: #29386E;
  --cyan: #52E4FF;
  --cyan-dim: #3794AD;
  --magenta: #A981FF;
  --green: #48DB91;
  --blue: #A981FF;
  --danger: #FF7272;
  --gold: #FFCA58;
  --orange: #FFA962;
  /* DEPTH, NOT GLOW. These five keep their names because forty rules read
     them, and take shadow values instead of blooms: the spec's hierarchy
     comes from elevation, and the only thing left that truly glows is the
     one primary action (--el-cta). */
  --glow-c: 0 8px 28px rgba(0, 0, 0, .18);
  --glow-m: 0 8px 28px rgba(0, 0, 0, .18);
  --glow-g: 0 6px 20px rgba(0, 0, 0, .16);
  --glow-b: 0 6px 20px rgba(0, 0, 0, .16);
  --glow-o: 0 6px 20px rgba(0, 0, 0, .16);
  --glow-c-peak: 0 12px 34px rgba(82, 228, 255, .28), 0 0 0 1px rgba(82, 228, 255, .40);
  --wash-b: rgba(169, 129, 255, .05);
  --chip-wash: rgba(169, 129, 255, .12);
  --sig-paper: #FFFFFF;
  --sig-ink: #101828;
  --bar: rgba(5, 8, 23, .86);
  --on-accent: #071018;
}

/* ---------------------------------------------------------------- schemes
   Neon is the default and is the bare :root above. The other three retint the
   same tokens and nothing else: no rule anywhere in this sheet names a colour
   directly, so a scheme is a palette swap and every component follows.
   theme.js stamps data-scheme on <html>; a head snippet does it before first
   paint so a scheme never flashes neon on the way in. */
/* Calm: the same room with the lights down. Same hues, lower saturation and a
   softer glow, for reading in bed at 2am with a headache, which is when a lot
   of this gets read. */
:root[data-scheme="calm"] {
  --bg: #090C12;
  --bg-deep: #06080D;
  --panel: #111720;
  --panel-2: #19222E;
  --ink: #F2F4F7;
  --dim: #A9B1BE;
  --muted: #7F8998;
  --soft: #DEE1E7;
  --line: #2A3442;
  --cyan: #87A9FF;
  --cyan-dim: #5A72AA;
  --magenta: #A58DD7;
  --green: #70CA9A;
  --blue: #A58DD7;
  --danger: #F07878;
  --gold: #E6B95C;
  --orange: #EAA067;
  /* DEPTH, NOT GLOW. These five keep their names because forty rules read
     them, and take shadow values instead of blooms: the spec's hierarchy
     comes from elevation, and the only thing left that truly glows is the
     one primary action (--el-cta). */
  --glow-c: 0 8px 28px rgba(0, 0, 0, .18);
  --glow-m: 0 8px 28px rgba(0, 0, 0, .18);
  --glow-g: 0 6px 20px rgba(0, 0, 0, .16);
  --glow-b: 0 6px 20px rgba(0, 0, 0, .16);
  --glow-o: 0 6px 20px rgba(0, 0, 0, .16);
  --glow-c-peak: 0 12px 34px rgba(135, 169, 255, .28), 0 0 0 1px rgba(135, 169, 255, .40);
  --wash-b: rgba(165, 141, 215, .05);
  --chip-wash: rgba(165, 141, 215, .12);
  --sig-paper: #FFFFFF;
  --sig-ink: #101828;
  --bar: rgba(9, 12, 18, .86);
  --on-accent: #071018;
}

/* Paper: light, warm, like the folder itself. The one scheme where ink is dark
   on a pale ground, so every token inverts rather than dims. */
:root[data-scheme="paper"] {
  --bg: #F1ECE2;
  --bg-deep: #E8E0D3;
  --panel: #FAF7F0;
  --panel-2: #E8E0D3;
  --ink: #20252B;
  --dim: #5F6871;
  --muted: #7C817F;
  --soft: #32383F;
  --line: #D2C7B8;
  --cyan: #35598D;
  --cyan-dim: #6882A7;
  --magenta: #70598F;
  --green: #287250;
  --blue: #70598F;
  --danger: #A63F3F;
  --gold: #9D6819;
  --orange: #A05827;
  /* DEPTH, NOT GLOW. These five keep their names because forty rules read
     them, and take shadow values instead of blooms: the spec's hierarchy
     comes from elevation, and the only thing left that truly glows is the
     one primary action (--el-cta). */
  --glow-c: 0 8px 28px rgba(0, 0, 0, .10);
  --glow-m: 0 8px 28px rgba(0, 0, 0, .10);
  --glow-g: 0 6px 20px rgba(0, 0, 0, .09);
  --glow-b: 0 6px 20px rgba(0, 0, 0, .09);
  --glow-o: 0 6px 20px rgba(0, 0, 0, .09);
  --glow-c-peak: 0 12px 34px rgba(53, 89, 141, .28), 0 0 0 1px rgba(53, 89, 141, .40);
  --wash-b: rgba(112, 89, 143, .05);
  --chip-wash: rgba(112, 89, 143, .12);
  --sig-paper: #FFFFFF;
  --sig-ink: #101828;
  --bar: rgba(241, 236, 226, .86);
  --on-accent: #FFFFFF;
}

/* High contrast: black ground, pure hues, no glow to blur an edge. For a bad
   vision day, or bright sun on a phone in a hospital car park. */
:root[data-scheme="contrast"] {
  --bg: #000000;
  --bg-deep: #000000;
  --panel: #0D0D0D;
  --panel-2: #1B1B1B;
  --ink: #FFFFFF;
  --dim: #D2D2D2;
  --muted: #B6B6B6;
  --soft: #F2F2F2;
  --line: #666666;
  --cyan: #73E8FF;
  --cyan-dim: #4C95A3;
  --magenta: #C3A3FF;
  --green: #76F1AC;
  --blue: #C3A3FF;
  --danger: #FF8C8C;
  --gold: #FFD66A;
  --orange: #FFBA77;
  /* DEPTH, NOT GLOW. These five keep their names because forty rules read
     them, and take shadow values instead of blooms: the spec's hierarchy
     comes from elevation, and the only thing left that truly glows is the
     one primary action (--el-cta). */
  --glow-c: 0 8px 28px rgba(0, 0, 0, .18);
  --glow-m: 0 8px 28px rgba(0, 0, 0, .18);
  --glow-g: 0 6px 20px rgba(0, 0, 0, .16);
  --glow-b: 0 6px 20px rgba(0, 0, 0, .16);
  --glow-o: 0 6px 20px rgba(0, 0, 0, .16);
  --glow-c-peak: 0 12px 34px rgba(115, 232, 255, .28), 0 0 0 1px rgba(115, 232, 255, .40);
  --wash-b: rgba(195, 163, 255, .05);
  --chip-wash: rgba(195, 163, 255, .12);
  --sig-paper: #FFFFFF;
  --sig-ink: #101828;
  --bar: rgba(0, 0, 0, .86);
  --on-accent: #071018;
}

/* The body's two corner washes are baked rgba, not tokens, and on a pale or
   pure-black ground they read as smudges. Drop them for those two. */
:root[data-scheme="paper"] body,
:root[data-scheme="contrast"] body { background-image: none; }

/* Manila on a paper ground would be card stock on card stock, so the folder
   chrome cools and darkens its edges to stay a separate object on the desk.
   On high contrast it goes plain and hard-edged - the whole point of that
   scheme is that nothing is subtle. */
:root[data-scheme="paper"] {
  --manila: #E3D3AE;
  --manila-2: #EFE2C6;
  --manila-ink: #2A2113;
  --manila-edge: #9C8551;
  --manila-fold: rgba(60, 45, 15, .45);
  --manila-strong: #14100A;
  --manila-alert: #8A2B00;
  --manila-note: #14432F;
  --manila-flag: #4A1D6E;
}
:root[data-scheme="contrast"] {
  --manila: #F2E3B8;
  --manila-2: #FFF4D2;
  --manila-ink: #000000;
  --manila-edge: #000000;
  --manila-fold: rgba(0, 0, 0, .8);
  /* Still four distinct states, all of them far past the ratio this scheme
     exists for. Flattening them to black would cost the state, not gain
     contrast. */
  --manila-strong: #000000;
  --manila-alert: #5E1B00;
  --manila-note: #00301F;
  --manila-flag: #2E0B4A;
}

* { box-sizing: border-box; }
/* min-height, not height. `height: 100%` pins the body BOX to the viewport, and
   a sticky child can only travel inside its parent's box - so the header stuck
   for exactly one screenful and then scrolled away. On a 3000px booking page
   that meant the menu and the settings cog did not exist below y=1200, which
   is the part of the form where somebody is typing their date of birth. */
html,
body { min-height: 100%; }
/* Base type scale. Clients are often unwell or reading on a phone in a
   waiting room — everything below is sized in rem off this root. */
html { font-size: 17.5px; }
/* The page never scrolls sideways — anything wider than the screen scrolls
   inside its own box (nav tabs, calendar), not by dragging the whole page. */
html,
/* On BOTH html and body. iOS Safari pans the whole viewport sideways when
   anything extends the ROOT element, and a clip on body alone does not stop
   that (Eric, 2026-08-21, iPhone: "There's side to side scroll"). Nothing in
   this app ever wants the page itself scrolling horizontally; wide content
   scrolls inside its own container. */
html, body { overflow-x: hidden; overflow-x: clip; }
img,
video { max-width: 100%; }
body {
  margin: 0; background: var(--bg); color: var(--ink);
  font-family: "SF Pro Text", "Segoe UI", system-ui, -apple-system, sans-serif;
  line-height: 1.6; font-size: 1rem; -webkit-text-size-adjust: 100%;
  background-image:
    radial-gradient(1200px 500px at 85% -10%, rgba(45,226,255,.07), transparent 60%),
    radial-gradient(900px 500px at 0% 110%, rgba(255,79,216,.06), transparent 60%);
}
h1,
h2,
h3 { margin: 0 0 .5rem; line-height: 1.15; letter-spacing: -.02em; font-weight: 800; text-wrap: balance; }
h1 { font-size: clamp(1.9rem, 6vw, 2.6rem); }
h2 { font-size: 1.35rem; }
h3 { font-size: 1.06rem; }
p { margin: 0 0 .8rem; }
a { color: var(--cyan); }
.muted,
.dim { color: var(--dim); }
.small { font-size: .95rem; }

/* header */
header.bar {
  position: sticky; top: 0; z-index: 5;
  background: var(--bar); backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--line);
}
.bar-inner {
  max-width: 42rem; margin: 0 auto; padding: .55rem 1.1rem;
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
}
.brand {
  font: 800 1rem/1 "SF Pro Display", system-ui, sans-serif; letter-spacing: -.01em;
  color: var(--ink); text-decoration: none;
}
.brand .spark { color: var(--cyan); text-shadow: var(--glow-c); }
/* The signed-in nav holds up to six links. It used to scroll sideways with the
   scrollbar hidden, which on a phone put two thirds of it - including the
   settings cog the onboarding tells people to tap - past the right edge with
   nothing to say so. It does not scroll now: when the links stop fitting,
   nav-menu.js collapses them into the menu below. */
.tabs {
  display: flex; gap: .3rem; align-items: center; min-width: 0;
  overflow: hidden;
}
.tabs a {
  font: 600 .82rem/1 inherit; color: var(--dim); cursor: pointer; text-decoration: none;
  padding: .45rem .85rem; border-radius: 999px; border: 1px solid transparent;
  white-space: nowrap; flex-shrink: 0;
}
[data-nav-auth] { display: contents; }
@media (max-width: 480px) {
.bar-inner { gap: .5rem; padding-left: .8rem; padding-right: .8rem; }
.bar-inner:not(.collapsed) .tabs a { padding: .45rem .45rem; font-size: .8rem; }
}
.tabs a.active { color: var(--cyan); border-color: var(--cyan); box-shadow: var(--glow-c); }

/* The end of the bar that is always on screen: the menu button and the cog. */
.bar-acts { display: flex; align-items: center; gap: .4rem; flex: none; }
.nav-toggle {
  display: none;
  border: 1px solid var(--line); background: var(--panel-2); color: var(--ink);
  width: 2.5rem; height: 2.5rem; min-width: 2.5rem; border-radius: 12px;
  font-size: 1.05rem; line-height: 1; place-items: center; cursor: pointer; padding: 0;
}
.bar-inner.collapsed .nav-toggle { display: grid; }
/* Collapsed, the same links become a panel under the button. Same elements,
   restyled - not copies - so every listener on them still works. */
.bar-inner.collapsed .tabs {
  position: absolute; top: 100%; right: .8rem; left: auto; z-index: 70;
  display: none; flex-direction: column; align-items: stretch; gap: .15rem;
  min-width: 12rem; padding: .4rem; margin-top: .3rem;
  background: var(--panel); border: 1px solid var(--cyan); border-radius: 14px;
  box-shadow: var(--glow-c), 0 12px 30px rgba(0, 0, 0, .45);
  max-height: calc(100dvh - 5rem); overflow-y: auto;
}
.bar-inner.collapsed .tabs.open { display: flex; }
/* Full-width rows, tall enough to hit. */
.bar-inner.collapsed .tabs a { padding: .7rem .9rem; border-radius: 10px; font-size: .92rem; }
.bar-inner { position: relative; }
.who { color: var(--dim); font-size: .82rem; }

main { max-width: 42rem; margin: 0 auto; padding: 1rem 1.1rem 3rem; }
/* A phone on its side has under 400px of height, and the case header alone -
   back link, title, subtitle, two tab rows - used almost all of it. */
@media (orientation: landscape) and (max-height: 460px) {
main { padding-top: .5rem; padding-bottom: 1.2rem; }
.case-head { margin-bottom: .2rem; }
.case-head h1, .case-head h2 { font-size: 1rem; margin-bottom: 0; }
/* The working line stays. It is the one thing in this header worth its
   pixels; everything around it gets tighter instead. */
.case-head .working-line { font-size: .74rem; margin-top: .05rem; }
.bar-inner { padding-top: .35rem; padding-bottom: .35rem; }
}

/* buttons - calm by default (PR 69 calm-neon pass). The ONE primary action
   on a screen adds .glow and carries the neon; every other button reads as a
   quiet control so the glow means "this is the thing to press". */
.btn {
  display: inline-block; cursor: pointer; border-radius: 10px; border: 1px solid var(--line);
  font: 700 .92rem/1.2 inherit; letter-spacing: .01em;
  padding: .72rem 1.2rem; text-decoration: none; min-height: 44px;
  color: var(--ink); background: var(--panel-2);
}
.btn:hover { border-color: var(--cyan-dim); }
.btn.glow { background: var(--cyan); border-color: var(--cyan); color: var(--on-accent); box-shadow: var(--glow-c); }
.btn:disabled { opacity: .35; cursor: not-allowed; box-shadow: none; }
.btn.ghost { background: transparent; color: var(--cyan); border-color: var(--cyan); box-shadow: none; }
.btn.quiet { background: transparent; color: var(--dim); border-color: var(--line); box-shadow: none; }
.btn.mag { background: transparent; border-color: var(--magenta); color: var(--magenta); box-shadow: none; }
.btn.mag.glow { background: var(--magenta); border-color: var(--magenta); color: #1A0413; box-shadow: var(--glow-m); }
.btn.mag.glow:disabled { box-shadow: none; }
.actions { margin-top: 1.1rem; display: flex; gap: .55rem; flex-wrap: wrap; align-items: center; }

/* Setup guide + notifications prompt stand out from ordinary panels. */
.setup-guide,
.push-prompt { border-color: var(--cyan); box-shadow: var(--glow-c); }

/* Glowing golden diamond — unread-chat marker on the Chat tab. */
.diamond {
  color: var(--gold); font-size: .72em; vertical-align: middle; margin-left: .1rem;
  text-shadow: 0 0 6px rgba(255,204,85,.9), 0 0 12px rgba(255,204,85,.5);
  animation: diamond-pulse 1.8s ease-in-out infinite;
}
@keyframes diamond-pulse {
  0%, 100% { opacity: .7; }
  50% { opacity: 1; text-shadow: 0 0 11px rgba(255,204,85,1), 0 0 20px rgba(255,204,85,.7); }
}
@media (prefers-reduced-motion: reduce) {
.diamond { animation: none; }
}

/* Settings cog + panel. */
.cog-btn {
  border: 1px solid var(--line); background: var(--panel-2); color: var(--ink);
  width: 2.5rem; height: 2.5rem; min-width: 2.5rem; border-radius: 50%; font-size: 1.05rem;
  display: grid; place-items: center; cursor: pointer; padding: 0; flex-shrink: 0;
}
/* The card is taller than a phone. It did not scroll, so its bottom sat off
   the screen with no way to reach it - and what was cut off was "Open to my
   dashboard", the switch for looking at the client's side. */
.settings-overlay {
  position: fixed; inset: 0; z-index: 60; display: flex; align-items: flex-start; justify-content: center;
  padding: 3.5rem 1.1rem 1.1rem; background: rgba(4,6,10,.6); backdrop-filter: blur(4px);
  overflow-y: auto; overscroll-behavior: contain;
}
.settings-card {
  background: var(--panel); border: 1px solid var(--cyan); box-shadow: var(--glow-c);
  border-radius: 16px; padding: 1.1rem 1.2rem; max-width: 26rem; width: 100%;
  margin-bottom: 1.1rem;
}
/* Radios and checkboxes in a form are 13px by default, which is smaller than
   anything else you are asked to press. */
input[type="radio"], input[type="checkbox"] { width: 1.15rem; height: 1.15rem; accent-color: var(--cyan); }
/* Bare radio and checkbox rows only. :has() takes its argument's specificity,
   so without the :not() this outranked .chip-label and stripped the booking
   call-method pills of their padding and their 44px. */
label:has(> input[type="radio"]):not([class]),
label:has(> input[type="checkbox"]):not([class]) {
  display: flex; align-items: flex-start; gap: .5rem; padding: .35rem 0; min-height: 2.2rem;
}

.toggle-row { display: flex; align-items: center; justify-content: space-between; gap: 1rem; margin-top: .6rem; }
.switch {
  width: 2.7rem; height: 1.55rem; min-width: 2.7rem; border-radius: 999px; border: 1px solid var(--line);
  background: var(--line); position: relative; cursor: pointer; padding: 0; transition: background .15s;
}
.switch::after {
  content: ""; position: absolute; top: .17rem; left: .2rem; width: 1.15rem; height: 1.15rem;
  border-radius: 50%; background: var(--panel); transition: left .15s;
}
.switch.on { background: var(--cyan); box-shadow: var(--glow-c); }
.switch.on::after { left: calc(100% - 1.35rem); background: var(--panel); }
.seg button.on { border-color: var(--cyan); color: var(--cyan); box-shadow: var(--glow-c); }

/* Gentle first-run intro overlay. */
.intro-overlay {
  position: fixed; inset: 0; z-index: 60; display: flex; align-items: center; justify-content: center;
  padding: 1.2rem; background: rgba(4,6,10,.8); backdrop-filter: blur(6px);
}
.intro-card {
  background: var(--panel); border: 1px solid var(--cyan); box-shadow: var(--glow-c);
  border-radius: 16px; padding: 1.3rem 1.2rem; max-width: 26rem; width: 100%;
}
.intro-card h2 { margin: 0 0 .5rem; }
.intro-card p { color: var(--soft); }
.intro-steps { margin: .5rem 0 .6rem 1.1rem; padding: 0; line-height: 1.6; }
.intro-steps li { margin-bottom: .3rem; }
.intro-actions { display: flex; gap: .6rem; justify-content: flex-end; margin-top: 1.1rem; }
.intro-dots { display: flex; gap: .35rem; margin-bottom: .7rem; }
.intro-dots span { width: .5rem; height: .5rem; border-radius: 50%; background: var(--line); }
.intro-dots span.on { background: var(--cyan); box-shadow: var(--glow-c); }

/* The "?" beside the case title, and the panel it opens. */
/* Post-payment reassurance at the top of a live case. */
.confirm-banner { border-color: var(--cyan); box-shadow: var(--glow-c); }

/* "This is a web app ?" — deliberately quiet, sitting above the tiers. */
.webapp-note {
  margin: .2rem 0 -.35rem; font-size: .82rem; letter-spacing: .02em;
  color: var(--dim); display: flex; align-items: center; gap: .1rem;
}
.webapp-note .help-dot { width: 1.25rem; height: 1.25rem; min-width: 1.25rem; font-size: .76rem; }

.help-dot {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.5rem; height: 1.5rem; min-width: 1.5rem; padding: 0; margin-left: .4rem;
  vertical-align: middle; border-radius: 50%; cursor: pointer;
  border: 1px solid var(--cyan); background: transparent; color: var(--cyan);
  font: 700 .88rem/1 inherit;
}
/* Focus keeps the outline every other control has. It used to be cancelled and
   replaced with an 18%-opacity glow on a button that already has a cyan ring -
   focused and unfocused were indistinguishable, on the control the onboarding
   explicitly tells people to press. */
.help-dot:hover { box-shadow: var(--glow-c); }
/* A thumb-sized target. It is a "?" that opens the explanation of the whole
   page, not a decoration. */
.help-dot { width: 2.5rem; height: 2.5rem; min-width: 2.5rem; font-size: 1.05rem; }
/* The naming sheet. Capped and scrollable for the same reason everything else
   on a phone is: with a keyboard up there are two hundred usable pixels, and a
   card that overflows puts both of its buttons somewhere unreachable. */
/* The overlay reserves 3.5rem above the card for the page behind it. With a
   keyboard up there is not 3.5rem to spare, so this one sheet takes it back:
   the card starts near the top and gets the whole screen minus its own margin,
   which is what puts both buttons inside the viewport. */
.settings-overlay:has(> .rename-card),
.settings-overlay:has(> .sig-sheet) { padding-top: 1.1rem; align-items: flex-start; }
.rename-card,
.sig-sheet { max-height: calc(100dvh - 2.2rem); overflow-y: auto; }
.rename-card .actions,
.sig-sheet .actions {
  position: sticky; bottom: -1.1rem; padding: .6rem 0 .2rem; margin-bottom: -.2rem;
  background: var(--panel); box-shadow: 0 -14px 16px -6px var(--panel);
}
/* Sign has further to fall than Rename ever did: the records sheet runs to
   roughly 1800px on a 390px phone before the document itself is expanded. */

/* Long-form content: cap the height and scroll inside the card so the close
   button never leaves the screen on a short phone. */
.help-card { max-width: 30rem; max-height: calc(100dvh - 5rem); overflow-y: auto; }
.help-card h4 { margin: 1.1rem 0 .4rem; color: var(--cyan); font-size: .95rem; }
.help-card p { color: var(--soft); margin: .5rem 0; }
.help-card .row { position: sticky; top: -1.1rem; z-index: 1;
  background: var(--panel); padding: 1.1rem 0 .6rem; margin: -1.1rem 0 0; }
.help-list { margin: .4rem 0 .6rem 1.15rem; padding: 0; color: var(--soft); line-height: 1.6; }
.help-list li { margin-bottom: .4rem; }
.help-os { margin: .8rem 0 .2rem !important; color: var(--ink) !important; }

/* surfaces */
.card,
.panel {
  background: var(--panel); border: 1px solid var(--line); border-radius: 14px;
  padding: .95rem 1.05rem; margin-bottom: .9rem;
}
/* Wraps. A row that cannot wrap pushes its last child off the right edge on a
   narrow phone: the CASE / DELIVERED pill was sliced by the screen edge on the
   chats list, and the calendar's Today button sat entirely off it. */
.row { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
.price {
  font: 700 1.05rem/1.3 ui-monospace, "SF Mono", Menlo, monospace;
  color: var(--ink); white-space: nowrap; font-variant-numeric: tabular-nums;
}
.error { color: var(--danger); font-size: .95rem; margin: .6rem 0; }
.ok { color: var(--cyan); font-size: .95rem; margin: .6rem 0; }

/* forms */
input[type=email],
input[type=tel],
input[type=text],
/* password was missing from this list, so the one password field outside
   signin.html (the ChatGPT key in Settings) painted as a bare browser box on
   the admin pages, which do not load glowup.css. */
input[type=password],
input[type=date],
input[type=url],
input[type=datetime-local],
select,
textarea {
  width: 100%; padding: .6rem .75rem; border: 1px solid var(--line); border-radius: 10px;
  background: var(--panel-2); color: var(--ink); font: inherit;
}
label { display: block; margin-bottom: .3rem; font-size: .9rem; color: var(--dim); }
label.inline { display: inline-flex; align-items: center; gap: .4rem; margin: 0; font-size: .95rem; }

/* landing */
.hero-eyebrow {
  font: 600 .95rem/1 ui-monospace, "SF Mono", Menlo, monospace; letter-spacing: .3em;
  color: var(--cyan); text-shadow: var(--glow-c);
  text-align: center; text-indent: .3em; /* offsets the trailing letter-space so it centers true */
}
.landing { display: flex; flex-direction: column; justify-content: flex-start; gap: 2rem; min-height: min(calc(100dvh - 8rem), 40rem); padding-top: .5rem; }
.hero-eyebrow { margin-bottom: .7rem; }
/* The hero sits up top like a title, with clear air before the packages. */
.landing > div:first-child { margin-bottom: 1rem; }
.landing h1 { font-size: clamp(1.5rem, 5.4vw, 2.05rem); line-height: 1.2; margin: 0 0 .6rem; }
.hero-sub { color: var(--soft); font-size: 1.08rem; margin: 0; line-height: 1.6; }
.pack {
  position: relative; display: grid; grid-template-columns: 1fr auto; gap: .4rem .8rem;
  background: var(--panel); border: 1px solid var(--line); border-radius: 14px;
  padding: 1.25rem 1.3rem; text-decoration: none; color: var(--ink); margin: 0;
  /* One of these cards is a <button> (it opens a sheet rather than
     navigating), so the three have to be indistinguishable: a button brings
     its own font, centres its text and shrink-wraps. */
  width: 100%; text-align: left; font: inherit; cursor: pointer;
}
.pack h3 { margin: 0; font-size: 1.2rem; }
.pack .desc { grid-column: 1 / -1; color: var(--soft); font-size: 1rem; margin: 0; line-height: 1.5; }
/* The follow-up add-on: its own line inside the case card, not a trailing clause.
   Also used standalone on the review screen, so it is not scoped to .pack. */
.notice-box {
  margin: .55rem 0 0; padding: .6rem .75rem;
  border: 1px dashed var(--magenta); border-radius: 10px;
  background: rgba(255,79,216,.06); color: var(--soft); font-size: .95rem; line-height: 1.45;
}
.pack .notice-box { grid-column: 1 / -1; }
.notice-box strong { color: var(--magenta); }
/* A pending booking request is orange, not magenta — it is a status, not an offer. */
.notice-box.pending { border-style: solid; border-color: var(--orange); background: rgba(255,162,62,.07); }
.notice-box.pending strong { color: var(--orange); }

/* <summary> styled as a button: strip the marker and keep it inline. */
summary.btn { list-style: none; display: inline-block; }
summary.btn::-webkit-details-marker { display: none; }
@media (max-width: 600px) {
.pack { grid-template-columns: 1fr; gap: .25rem; }
.pack .price { justify-self: start; }
.pack .desc,
.pack .notice-box { margin-top: .35rem; }
}
.pack .price { font-size: 1.5rem; font-weight: 700; white-space: nowrap; }
.pack.rec-c {
  border-color: var(--cyan);
  box-shadow: var(--glow-c), inset 0 0 30px rgba(45,226,255,.05);
}
.pack.rec-c .price { color: var(--cyan); }
.pack.rec-m {
  border-color: var(--blue);
  box-shadow: var(--glow-b), inset 0 0 30px rgba(108,158,255,.05);
}
.pack.rec-m .price { color: var(--blue); }
/* PR 69 calm pass: the breathe animations are retired - the cards hold a
   steady inset glow instead. The keyframes and their reduced-motion pair
   were kept "for reference" and deleted on 2026-08-25: nothing referenced
   them, so the media block overrode nothing, and they shipped to every
   visitor on every page. */
/* The mockup frames the hero and the closing call in glowing cards rather
   than letting them float on the ground. Same token glow as the pricing
   cards, so all three schemes follow. */
.land-sec.hero,
.land-sec.closing {
  background: var(--panel); border: 1px solid var(--line); border-radius: 16px;
  padding: 1.15rem 1rem;
  /* Two declarations, not one list: "none, inset ..." is invalid CSS
     grammar, so on contrast (--glow-b: none) the browser was discarding the
     whole thing. The wash is a token now, so contrast turns it transparent
     on purpose instead of by parse failure. */
  box-shadow: var(--glow-b);
  background-image: radial-gradient(circle at 50% 0%, var(--wash-b), transparent 70%);
}
.land-sec.closing { text-align: center; }

/* The one breathing element on the landing page (Eric, 2026-08-25: "A soft
   glow should ease in and out in intensity over book a case"). Opt-in via
   .pulse, on that one button only - the one-glow-per-screen rule holds. */
@keyframes glow-pulse {
  0%, 100% { box-shadow: var(--glow-c); }
  50%      { box-shadow: var(--glow-c-peak); }
}
.btn.pulse { animation: glow-pulse 3.2s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) {
  .btn.pulse { animation: none; }
}
/* The About sheets (service-about.js): TL;DR first, depth underneath. */
.settings-card.about-card { max-width: 34rem; }
/* dvh, not vh: on iOS vh is the LARGE viewport, so this ran ~10% taller than
   intended with the URL bar showing. The rest of the file already uses dvh. */
.settings-card.about-card .about-body { max-height: min(68dvh, 34rem); }
.about-tldr {
  border: 1px solid var(--cyan-dim); border-radius: 12px;
  background: var(--panel-2); padding: .7rem .85rem; margin: 0 0 .8rem;
}
.about-list { margin: .2rem 0 .4rem; padding-left: 1.15rem; }
.about-list li { margin: .3rem 0; }
.pack-about { text-align: center; margin: -.3rem 0 1rem; }
/* .pack has margin:0 and the About buttons only sit under two of the three
   cards, so without this the Hands-Off and chat cards butt borders. */
.pack + .pack { margin-top: 1rem; }
/* The signing sheet: the finger-signature box, its pad, and the red a
   field wears when the completeness gate sends them back to it. */
.sig-box {
  display: flex; align-items: center; justify-content: center;
  width: 100%; min-height: 72px; padding: .5rem;
  border: 1px dashed var(--line); border-radius: 12px;
  background: var(--panel-2); color: var(--dim); cursor: pointer;
}
.sig-canvas {
  display: block; width: 100%; border: 1px solid var(--line);
  border-radius: 10px; background: var(--sig-paper); padding: 0;
  /* Without this the first stroke scrolls the sheet instead of drawing. */
  touch-action: none;
}
.field-bad,
.field-bad:focus {
  border-color: var(--danger) !important;
  /* A 1px hue shift was the only signal, which on contrast is a luminance
     step of one pixel. Width carries it for a red-deficient reader too. */
  border-width: 2px !important;
}
/* The Hands-Off readiness checklist (readiness.js), both sides. */
.ready-list { list-style: none; margin: .2rem 0 .6rem; padding: 0; }
.ready-list li { display: flex; gap: .4rem; margin: .3rem 0; color: var(--soft); }
.ready-list li.is-done { color: var(--green); }
.tag {
  position: absolute; top: -.6rem; left: .9rem;
  font: 700 .64rem/1 ui-monospace, "SF Mono", Menlo, monospace; letter-spacing: .18em;
  padding: .22rem .55rem; border-radius: 999px; background: var(--bg);
}
.tag.c { color: var(--green); border: 1px solid var(--green); box-shadow: var(--glow-g); }
.tag.m { color: var(--magenta); border: 1px solid var(--magenta); box-shadow: var(--glow-m); }
/* "Speedy" orange: italic with a slight forward lean, like it's already moving.
   The inner span counter-skews so the letters stay upright. */
.tag.o {
  color: var(--orange); border: 1px solid var(--orange); box-shadow: var(--glow-o);
  font-style: italic; transform: skewX(-8deg); letter-spacing: .2em;
}
.tag.o > span { display: inline-block; transform: skewX(8deg); }
.fine { font-size: .9rem; color: var(--dim); margin: 0; line-height: 1.5; }
.fine strong { color: var(--ink); }
@media (max-height: 700px) {
.landing { gap: .85rem; }
.landing h1 { font-size: 1.45rem; }
.hero-sub { font-size: .98rem; }
.pack { padding: .7rem .9rem; }
.pack h3 { font-size: 1.08rem; }
.pack .desc { font-size: .92rem; }
.btn { padding: .6rem 1rem; font-size: .92rem; }
.fine { font-size: .84rem; }
}

/* wizard */
.crumbs { display: flex; flex-wrap: wrap; gap: .3rem; margin: 0 0 1rem; padding: 0; list-style: none; }
.crumbs li {
  font: 600 .62rem/1 ui-monospace, "SF Mono", Menlo, monospace; letter-spacing: .08em;
  padding: .28rem .55rem; border-radius: 999px; border: 1px solid var(--line); color: var(--dim);
  text-transform: uppercase;
}
.crumbs li.now { border-color: var(--cyan); color: var(--cyan); box-shadow: var(--glow-c); }
.crumbs li.done { border-color: var(--cyan-dim); color: var(--cyan-dim); }

.choice {
  display: block; border: 1px solid var(--line); border-radius: 12px;
  padding: .85rem 1rem; margin-bottom: .7rem; cursor: pointer; background: var(--panel);
}
.choice.selected { border-color: var(--cyan); box-shadow: var(--glow-c); }

/* Each day is its own card. Runs of times used to blur into one another,
   so the day is now a labelled container rather than a faint heading. */
.day {
  margin-bottom: .9rem; padding: .8rem .85rem .85rem;
  background: var(--panel); border: 1px solid var(--line); border-radius: 14px;
}
.day h3 {
  display: flex; align-items: center; gap: .5rem;
  font: 700 .95rem/1.2 inherit; letter-spacing: .01em; text-transform: none;
  color: var(--cyan); margin: 0 0 .6rem; padding-bottom: .5rem;
  border-bottom: 1px solid var(--line);
}
.day h3::before {
  content: ''; width: .3rem; height: 1.05rem; border-radius: 999px;
  background: var(--cyan); box-shadow: var(--glow-c); flex: none;
}
.day h3 .count { margin-left: auto; font-weight: 600; font-size: .8rem; color: var(--dim); }
.slots { display: flex; flex-wrap: wrap; gap: .5rem; }
/* Full width, like the call-method chips above them on the same page. As an
   inline-block a slot was 180px inside a 282px card, so two thirds of every
   row looked like a target and was not. */
.slot {
  display: block; width: 100%; border: 1px solid var(--line); border-radius: 10px;
  background: var(--panel-2); color: var(--ink); font: inherit; font-size: 1rem;
  font-weight: 600; padding: .55rem .8rem; cursor: pointer; text-align: left;
  font-variant-numeric: tabular-nums; min-height: 44px;
}
.slot:hover { border-color: var(--cyan); }
.slot .local { display: block; font-size: .78rem; font-weight: 400; color: var(--dim); margin-top: .15rem; }
.slot.selected { border-color: var(--cyan); box-shadow: var(--glow-c); }
.slot.booked { opacity: .45; cursor: default; border-style: dashed; }

.chip-label {
  display: inline-flex; align-items: center; gap: .4rem; border: 1px solid var(--line);
  border-radius: 999px; padding: .38rem .85rem; margin: 0 .35rem .45rem 0; cursor: pointer;
  background: var(--panel); font-size: .94rem; color: var(--ink); min-height: 44px;
}
.chip-label.selected { border-color: var(--cyan); color: var(--cyan); box-shadow: var(--glow-c); }

/* case dashboard */
.timeline { list-style: none; margin: .8rem 0; padding: 0; }
.timeline li { display: flex; gap: .55rem; align-items: baseline; padding: .24rem 0; font-size: .94rem; color: var(--dim); }
.t-dot { width: .55rem; height: .55rem; border-radius: 50%; border: 2px solid var(--line); flex: none; }
.timeline li.done { color: var(--ink); }
.timeline li.done .t-dot { background: var(--cyan); border-color: var(--cyan); box-shadow: var(--glow-c); }
.timeline li.now { color: var(--cyan); font-weight: 600; }
.timeline li.now .t-dot { border-color: var(--cyan); box-shadow: var(--glow-c); }

.filelist { list-style: none; margin: .5rem 0 0; padding: 0; }
.filelist li {
  display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center;
  gap: .15rem .8rem;
  padding: .55rem 0; border-bottom: 1px solid var(--line); font-size: .94rem;
}
.filelist li:last-child { border-bottom: none; }
.filelist .fname { min-width: 0; display: flex; align-items: center; gap: .5rem; }
/* The file name wraps rather than ellipsing. On a phone the old clamp cut
   "sleep-study-9-8-25.pdf" down to "sl...", which is the one thing in the row
   anybody needs to read. The row wraps with it, so the date and size drop to a
   second line rather than squeezing the name off the screen. */
.filelist .fname { flex: 1 1 12rem; }
.filelist .fname a {
  min-width: 0; max-width: none;
  overflow-wrap: anywhere; white-space: normal;
}
.filelist .fmeta { color: var(--dim); font-size: .82rem; white-space: nowrap; }
.kind-pill {
  font: 700 .58rem/1 ui-monospace, monospace; letter-spacing: .12em;
  padding: .18rem .45rem; border-radius: 999px; border: 1px solid var(--line); color: var(--dim);
  margin-right: .45rem;
}
.kind-pill.report { border-color: var(--magenta); color: var(--magenta); }
.kind-pill.recording { border-color: var(--cyan); color: var(--cyan); }

.status-pill {
  font: 700 .62rem/1 ui-monospace, monospace; letter-spacing: .12em;
  padding: .25rem .55rem; border-radius: 999px; border: 1px solid var(--cyan); color: var(--cyan);
  text-transform: uppercase; white-space: nowrap;
}
.status-pill.closed { border-color: var(--dim); color: var(--dim); }

.dropzone {
  border: 1px dashed var(--line); border-radius: 12px; padding: 1rem; text-align: center;
  color: var(--dim); font-size: .93rem; background: var(--panel-2);
}
.dropzone.busy { opacity: .5; }
progress { width: 100%; accent-color: var(--cyan); }
.about h2 { color: var(--cyan); font-size: 1.05rem; margin-top: 2.4rem; }
/* Fading rule under the page title — a soft element break that dissolves at
   both edges, brightest in the middle. */
.title-rule {
  border: 0; height: 2px; margin: .7rem 0 1.3rem;
  background: linear-gradient(90deg,
    rgba(45,226,255,0) 0%, rgba(45,226,255,.55) 25%,
    rgba(45,226,255,.75) 50%,
    rgba(45,226,255,.55) 75%, rgba(45,226,255,0) 100%);
  box-shadow: 0 0 12px rgba(45,226,255,.2);
}
.mission {
  border-left: 3px solid var(--cyan); border-radius: 3px;
  padding: .55rem .9rem .55rem 1rem; margin-top: .6rem;
  background: linear-gradient(90deg, rgba(45,226,255,.07), transparent 70%);
}
.mission p { margin: 0; }

.footer-disclaimer,
footer.legal {
  margin-top: 2.2rem; padding-top: 1rem; border-top: 1px solid var(--line);
  font-size: .74rem; color: var(--dim);
}

a.btn:focus-visible,
button:focus-visible,
.slot:focus-visible,
.choice:focus-visible,
.chip-label:focus-visible,
.tabs a:focus-visible,
.pack:focus-visible {
  outline: 2px solid var(--magenta); outline-offset: 2px;
}

/* chat (Phase 3) */
.chat-log {
  max-height: 45vh; min-height: 8rem; overflow-y: auto;
  display: flex; flex-direction: column; gap: .45rem; padding: .6rem 0;
}
.msg { max-width: 82%; padding: .5rem .75rem; border-radius: 12px; font-size: .95rem; line-height: 1.45; overflow-wrap: anywhere; }
.msg .msg-meta { display: block; font-size: .66rem; color: var(--dim); margin-top: .15rem; }
.msg.me { align-self: flex-end; background: rgba(45,226,255,.12); border: 1px solid var(--cyan-dim); border-bottom-right-radius: 4px; }
.msg.them { align-self: flex-start; background: var(--panel-2); border: 1px solid var(--line); border-bottom-left-radius: 4px; }
/* Message text keeps the line breaks it was typed with. */
.msg-text { white-space: pre-wrap; }
.msg { position: relative; }
/* Status reaction sitting under a message ("Eric is reading…"). */
.msg-react {
  display: inline-flex; align-items: center; gap: .3rem; margin-top: .35rem;
  padding: .18rem .5rem; border-radius: 999px; font-size: .7rem; line-height: 1.3;
  border: 1px solid var(--cyan-dim); background: rgba(45,226,255,.1); color: var(--cyan);
}
/* A plain emoji rides the bottom corner of the bubble, the way every other
   chat app does it, rather than taking a line of its own. */
.msg-emoji-react {
  position: absolute; bottom: -.55rem; font-size: .85rem; line-height: 1;
  padding: .12rem .25rem; border-radius: 999px;
  background: var(--panel); border: 1px solid var(--line);
}
.msg.them .msg-emoji-react { right: -.3rem; }
.msg.me .msg-emoji-react { left: -.3rem; }

/* Pass flag: an outline until tapped, then filled red and unmistakable. */
.pass-flag {
  display: inline-flex; align-items: center; gap: .25rem; margin-top: .35rem;
  padding: .14rem .5rem; border-radius: 999px; font-size: .66rem; letter-spacing: .04em;
  border: 1px dashed var(--dim); background: none; color: var(--dim); cursor: pointer;
}
.pass-flag.on {
  border: 1px solid #FF5D5D; background: #B3261E; color: #fff; font-weight: 700;
}
.pass-flag:disabled { cursor: default; opacity: .95; }
.msg .pass-flag { margin-left: .35rem; }

/* Long-pressable messages suppress the iOS callout and selection so a held
   press opens the menu instead of Copy/Look Up. "Copy text" in the menu gives
   that back. */
/* Selection is blocked ONLY where a finger is the pointer.
   Eric, 2026-08-25: "Selecting and copying text in [a wide page] and elsewhere
   for copying and pasting is damned near impossible."
   (The bracket replaces one word of his. Every client browser downloads this
   file, so the admin-only vocabulary must not appear in it even inside a
   comment; tools/blindness-audit.mjs is what catches it.)

   It was impossible, not nearly: every message carries .react-target, so this
   rule made every word in every chat unselectable on every device. It exists
   because on a touchscreen a long press has to be able to open the message
   menu without iOS throwing its own text-selection callout over the top - a
   real problem, but only a touchscreen's.

   On a mouse there is no conflict at all: the menu opens on right-click and
   on a held press, neither of which is how you drag out a selection. So the
   suppression is scoped to coarse pointers and text is selectable everywhere
   else. Copying a client's own words out of a chat is ordinary work. */
@media (pointer: coarse) {
  .msg.react-target { -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; }
}

/* ---- long-press message menu (Messenger-style: emoji bar, then a sheet) ---- */
/* margin:auto on the child rather than align-items:center on the box: centred
   flex content that is taller than its container overflows BOTH ends, and the
   top one cannot be scrolled back to. */
.msg-menu-overlay {
  position: fixed; inset: 0; z-index: 70; display: flex;
  justify-content: center; padding: 1.1rem; overflow-y: auto;
  background: rgba(4,6,10,.72); backdrop-filter: blur(5px);
}
.msg-menu { margin: auto; }
.msg-menu { width: 100%; max-width: 26rem; }
.react-bar {
  display: flex; justify-content: space-between; gap: .2rem; margin: 0 auto .7rem;
  padding: .45rem .6rem; border-radius: 999px; width: fit-content; max-width: 100%;
  background: var(--panel); border: 1px solid var(--line); box-shadow: 0 8px 26px rgba(0,0,0,.45);
}
/* Six of these at 2.6rem need 311px of a 320px screen, and a flex child cannot
   shrink below its own min-width, so the last one hung outside the pill on the
   dimmed backdrop. The row gets to shrink now, down to a size still worth
   aiming a thumb at. */
.react-emoji-btn {
  border: 0; background: none; cursor: pointer; padding: .15rem .2rem;
  font-size: 1.75rem; line-height: 1; border-radius: 50%;
  flex: 0 1 auto; min-width: 2.4rem; min-height: 2.6rem;
  transition: transform .12s ease;
}
@media (max-width: 360px) {
  .react-bar { gap: 0; padding: .45rem .35rem; }
  .react-emoji-btn { min-width: 2rem; font-size: 1.5rem; }
}
.react-emoji-btn:hover,
.react-emoji-btn:focus-visible { transform: scale(1.25); outline: none; }
.react-emoji-btn.on { transform: scale(1.2); background: rgba(45,226,255,.16); }
.msg-menu-sheet {
  background: var(--panel); border: 1px solid var(--line); border-radius: 16px;
  padding: .5rem; box-shadow: 0 8px 26px rgba(0,0,0,.45);
  /* Taller than it was. On a 667px phone the status list runs to about 510px
     of rows, and at 60vh the last three - Save, Copy text and Cancel - all sat
     below the fold of a box with nothing to say it scrolled. */
  max-height: min(72dvh, 34rem); overflow-y: auto;
}
/* Cancel never scrolls away. It is the way out of a sheet that opened without
   being asked for, so it is the one row that has to be on screen. */
.msg-menu-sheet .msg-menu-row.cancel {
  position: sticky; bottom: -.5rem; margin-top: .15rem;
  background: var(--panel); box-shadow: 0 -10px 12px -6px var(--panel);
}
.msg-menu-head { margin: .4rem .55rem .35rem; font-size: .72rem; letter-spacing: .04em;
  text-transform: uppercase; color: var(--dim); }
.msg-menu-note { margin: .4rem .55rem .2rem; font-size: .72rem; color: var(--dim); }
.msg-menu-row {
  display: flex; align-items: center; gap: .65rem; width: 100%; text-align: left;
  padding: .62rem .6rem; min-height: 2.6rem; border-radius: 11px; cursor: pointer; font-size: .92rem;
  border: 0; background: none; color: var(--ink);
}
.msg-menu-row:hover,
.msg-menu-row:focus-visible { background: var(--panel-2); outline: none; }
.msg-menu-row.on { color: var(--cyan); }
.msg-menu-row.cancel { justify-content: center; color: var(--dim); margin-top: .2rem;
  border-top: 1px solid var(--line); border-radius: 0 0 11px 11px; }
.react-emoji { font-size: 1.05rem; line-height: 1; width: 1.3rem; text-align: center; }
.edit-card { max-width: 28rem; }
.edit-box {
  width: 100%; resize: vertical; min-height: 6rem; line-height: 1.5;
  font: inherit; font-size: .95rem;
}
/* One declaration. It used to be two - this one and a second at the authority
   rules ~300 lines down - and the shipped button was an accident of the
   cascade: padding and font from the later rule, min-height from this one.
   The tap target is 44px on purpose; the padding is small on purpose. */
.btn.tiny { font-size: .72rem; padding: .2rem .5rem; min-height: 44px; }
.btn.tiny.on { border-color: var(--magenta); color: var(--magenta); }

/* Full-screen chat. The ⤢/✕ button sits in the top-right corner of the
   thread; full mode takes the real visible viewport (dvh survives the iOS
   toolbars) and locks the page behind it. */
.chat-root { position: relative; padding-top: .2rem; }
.chat-expand {
  position: absolute; top: -.4rem; right: 0; z-index: 6;
  width: 2.4rem; height: 2.4rem; border-radius: 10px; cursor: pointer;
  border: 1px solid var(--cyan-dim); background: var(--panel-2); color: var(--cyan);
  font-size: 1.3rem; font-weight: 700; line-height: 1;
}
.chat-expand:hover,
.chat-expand:focus-visible { border-color: var(--cyan); box-shadow: var(--glow-c); outline: none; }
/* Inside a folder page the tab strip sits directly above, and top:-.4rem put
   this button on top of a tab. Two controls in the same place on a phone is a
   mis-tap, so within a page the chat starts a little lower and the button
   stays inside its own box. */

.chat-root.chat-full {
  position: fixed; inset: 0; z-index: 75; margin: 0; border-radius: 0;
  height: 100vh; height: 100dvh;
  display: flex; flex-direction: column;
  background: var(--bg);
  padding: calc(env(safe-area-inset-top, 0px) + .6rem) .8rem
           calc(env(safe-area-inset-bottom, 0px) + .6rem);
}
.chat-root.chat-full .chat-expand {
  top: calc(env(safe-area-inset-top, 0px) + .6rem); right: .8rem;
  color: var(--ink); font-size: .95rem;
}
/* The in-composer control becomes the way out, and says so. */
.chat-root.chat-full .chat-form [data-expand] { color: var(--cyan); }
.chat-root.chat-full .chat-log { max-height: none; flex: 1; margin-top: .2rem; }
/* Full screen means FULL: bubbles stretch nearly wall to wall so far more
   text fits per line, and the hint + export chrome get out of the way — the
   screen belongs to the messages and the composer. */
.chat-root.chat-full .msg { max-width: 96%; }
.chat-root.chat-full [data-hint],
.chat-root.chat-full p:has(> [data-export]) { display: none; }
body.chat-full-open { overflow: hidden; }

/* The message box gets its own row, and the controls sit under it.
   They all used to share one line. Once the row held four controls the box
   itself was down to fifty pixels on a phone and twenty-seven at 320 - two
   characters a line, a tower of them, unusable for the one thing the screen is
   for. Nothing was cut; the row was. */
.chat-form {
  display: flex; flex-wrap: wrap; gap: .5rem; margin-top: .4rem; align-items: flex-end;
}
/* A composer you can actually read what you typed in: it grows to five-ish
   lines before it starts scrolling, instead of marching the words off the left. */
.chat-form textarea {
  order: -1; flex: 1 1 100%; min-width: 0;
  resize: none; overflow-y: auto; font: inherit; font-size: .95rem;
  line-height: 1.45; min-height: 2.9rem; max-height: 9rem; padding: .6rem .7rem;
}
/* Both buttons sit level with the bottom of the growing box. */
.chat-form .attach-btn,
.chat-form .btn { flex: none; align-self: flex-end; height: 2.9rem; }
/* Send to the far end of the control row, away from the icons, so the one
   button that is not undoable is not the one your thumb brushes. */
.chat-form .btn[type="submit"] { margin-left: auto; }
/* Wide enough for one line and everything still fits on it. */
@media (min-width: 560px) {
.chat-form textarea { order: 0; flex: 1 1 12rem; }
}
.chat-notice { border-top: 1px solid var(--line); padding-top: .6rem; }

/* Messages sent while the lane chips existed (2026-08-22, for a day) keep
   their marks; a clinical one still reads as what it was at a glance. */
.msg.lane-hot { border-left: 3px solid var(--magenta); }

/* A link inside a message. Underlined rather than colour-only, because it
   sits on two different bubble colours and colour alone is not a signal
   everyone can see. Long URLs break instead of stretching the bubble. */
.msg-link {
  color: inherit; text-decoration: underline; text-underline-offset: 2px;
  overflow-wrap: anywhere; word-break: break-word;
}
.msg-link:hover, .msg-link:focus-visible { text-decoration-thickness: 2px; }
/* Small confirmations. A native alert() for "saved" is an unstyled system
   dialog on an iOS home-screen app, and it needs dismissing before anything
   else can happen - a modal interruption to say a thing went well. */
.pa-toast {
  position: fixed; left: 50%; bottom: calc(env(safe-area-inset-bottom, 0px) + 1.2rem);
  transform: translateX(-50%); z-index: 90; max-width: min(26rem, calc(100vw - 2rem));
  padding: .65rem 1rem; border-radius: 999px;
  background: var(--panel); border: 1px solid var(--cyan); box-shadow: var(--glow-c);
  color: var(--ink); font-size: .88rem; text-align: center;
  animation: pa-toast-in .18s ease-out;
}
.pa-toast.bad { border-color: var(--magenta); box-shadow: var(--glow-m); }
@keyframes pa-toast-in { from { opacity: 0; transform: translate(-50%, .5rem); } }
@media (prefers-reduced-motion: reduce) { .pa-toast { animation: none; } }

.p-dot { display: inline-block; width: .6rem; height: .6rem; border-radius: 50%; background: var(--line); margin-right: .3rem; }
.p-dot.on { background: var(--cyan); box-shadow: var(--glow-c); }
.p-label { font-size: .8rem; color: var(--dim); }
.expectation { font-size: .85rem; color: var(--dim); font-style: italic; }
.case-picker { display: flex; flex-wrap: wrap; gap: .35rem; margin-bottom: .8rem; }

/* chat attachments */
.attach-btn {
  display: inline-flex; align-items: center; justify-content: center; cursor: pointer;
  width: 2.6rem; border: 1px solid var(--line); border-radius: 10px; background: var(--panel-2);
  font-size: 1.05rem; margin: 0;
}
.msg-img { max-width: 100%; max-height: 220px; border-radius: 10px; display: block; margin-top: .25rem; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; }
.file-chip {
  display: inline-block; margin-top: .25rem; padding: .4rem .6rem; border-radius: 10px;
  border: 1px solid var(--line); background: var(--panel-2); color: var(--cyan);
  font-size: .85rem; text-decoration: none; overflow-wrap: anywhere;
  user-select: none; -webkit-user-select: none; -webkit-touch-callout: none;
}

/* google reviews strip (landing) */
.reviews-strip { overflow: hidden; position: relative; touch-action: pan-y; cursor: grab; }
.reviews-strip:active { cursor: grabbing; }
.reviews-track { display: flex; gap: .6rem; width: max-content; animation: rv-scroll 40s linear infinite; user-select: none; -webkit-user-select: none; will-change: transform; }
@keyframes rv-scroll { to { transform: translateX(-50%); } }
@media (prefers-reduced-motion: reduce) {
.reviews-track { animation: none; }
.reviews-strip { overflow-x: auto; }
}
.review {
  flex: 0 0 15rem; background: var(--panel); border: 1px solid var(--line);
  border-radius: 12px; padding: .55rem .75rem;
}
.review .stars { color: var(--magenta); font-size: .72rem; letter-spacing: .12em; }
.review .rv-text { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; color: var(--soft); font-size: .8rem; line-height: 1.4; margin: .15rem 0; }
.review .rv-name { color: var(--dim); font-size: .7rem; }
.reviews-label { font: 600 .62rem/1 ui-monospace, monospace; letter-spacing: .16em; color: var(--dim); margin: 0 0 .3rem; }

/* full reviews (reviews page) */
.review-full .stars { color: var(--magenta); font-size: .8rem; letter-spacing: .12em; }
.review-full .rv-full-text { color: var(--soft); font-size: .95rem; line-height: 1.55; margin: .4rem 0 0; }

/* ================================================================
   PR 69 components (calm-neon pass). Landing sections, the booking
   step rail, agreement expanders, FAQ, folded-in sign-in, and the
   case page's sticky jump chips. Additive only: nothing above moves.
   ================================================================ */
/* Smooth in-page jumps (case page chips, hash links). Guarded so
   reduced-motion users get instant jumps. */
@media (prefers-reduced-motion: no-preference) {
html { scroll-behavior: smooth; }
}
/* Every in-page jump on this site landed underneath the sticky header, which
   is the header's own height of the thing you jumped to, gone. The demo strip
   stacks on top of that and publishes its own measured height. */
html { scroll-padding-top: calc(var(--demo-bar-h, 0px) + 4.4rem); }

/* ---- landing / about ---- */
/* Consistent vertical rhythm between landing and about sections. */
.land-sec { margin-top: 2.8rem; }
.land-sec > h2 { color: var(--cyan); font-size: 1.2rem; margin-bottom: .9rem; }

.hero { padding: .9rem 0 .3rem; }
.hero h1 { font-size: clamp(1.9rem, 6vw, 2.6rem); line-height: 1.15; margin: 0 0 .7rem; }
.hero .hero-sub { font-size: 1.12rem; }
.hero .actions { margin-top: 1.25rem; }
/* Credential block: same visual language as .mission on the about page. */
.hero-cred {
  margin: 1.1rem 0 0; padding: .65rem .95rem;
  border-left: 3px solid var(--cyan); border-radius: 3px;
  background: linear-gradient(90deg, rgba(45,226,255,.07), transparent 70%);
  color: var(--soft); font-size: .95rem; line-height: 1.55;
}
.hero-cred p { margin: 0; }

/* "How it works": numbered circles on a rail, like .steps-list but sized
   for the landing page's three big steps. */
.hiw { list-style: none; margin: 1rem 0 0; padding: 0; counter-reset: hiw; }
.hiw li {
  counter-increment: hiw; position: relative; padding: 0 0 1.4rem 2.8rem;
  border-left: 1px solid var(--line); margin-left: .9rem;
}
.hiw li:last-child { border-left-color: transparent; padding-bottom: .2rem; }
.hiw li::before {
  content: counter(hiw); position: absolute; left: -.95rem; top: -.05rem;
  width: 1.8rem; height: 1.8rem; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font: 700 .8rem/1 ui-monospace, "SF Mono", Menlo, monospace; color: var(--cyan);
  background: var(--bg); border: 1px solid var(--cyan); box-shadow: var(--glow-c);
}
.hiw h3 { margin: 0 0 .25rem; font-size: 1.06rem; }
.hiw p { margin: 0; color: var(--soft); font-size: .98rem; line-height: 1.6; }

/* The mockup's step rows (Eric, 2026-08-25): each step is a panel with an
   icon chip on the left, hung off the same numbered rail. Structure and
   colour only - not one word of the copy moved. */
.hiw li.step-row { padding-left: 2rem; }
/* The chip lives INSIDE the card, as in the mockup: on a 390px phone every
   pixel the chip takes outside the card comes off the text column. */
.hiw .step-card {
  display: flex; gap: .6rem; align-items: flex-start;
  background: var(--panel); border: 1px solid var(--line); border-radius: 14px;
  padding: .7rem .8rem; margin: 0;
}
.hiw .step-card .step-text { flex: 1; min-width: 0; margin: 0; }
.icon-chip {
  flex: none; width: 2.1rem; height: 2.1rem; border-radius: 10px;
  display: flex; align-items: center; justify-content: center; font-size: 1rem;
  background: var(--panel-2); border: 1px solid var(--line);
  box-shadow: inset 0 0 18px var(--chip-wash);
}

/* "What happens after you book": a plain list, cyan markers. */
.after-list { margin: .6rem 0 0; padding-left: 1.15rem; color: var(--soft); }
.after-list li { margin-bottom: .55rem; font-size: .98rem; line-height: 1.6; }
.after-list li::marker { color: var(--cyan); }

/* Pricing card call-to-action: a span styled like a calm outline button
   inside the .pack anchor (the whole card is the link). */
.pack .pack-cta { grid-column: 1 / -1; justify-self: start; }
.pack-cta {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 44px; margin-top: .55rem; padding: .55rem 1.2rem;
  border: 1px solid var(--cyan); border-radius: 10px;
  color: var(--cyan); font: 700 .92rem/1.2 inherit; letter-spacing: .01em;
}
.pack:hover .pack-cta,
.pack:focus-visible .pack-cta { background: rgba(45,226,255,.1); }

/* FAQ expanders ("Straight answers"). */
/* FAQ expanders ("Straight answers"). */
.faq { border: 1px solid var(--line); border-radius: 12px; background: var(--panel); margin-bottom: .6rem; }
.faq > summary {
  display: flex; align-items: center; justify-content: space-between; gap: .6rem;
  cursor: pointer; list-style: none; padding: .8rem 1.05rem; min-height: 44px;
  font-weight: 700; color: var(--ink);
}
.faq > summary::-webkit-details-marker { display: none; }
.faq > summary::after { content: '\25B8'; color: var(--cyan); flex: none; font-size: .8rem; }
.faq[open] > summary { color: var(--cyan); }
.faq[open] > summary::after { content: '\25BE'; }
.faq-a { padding: 0 1.05rem .85rem; color: var(--soft); font-size: .96rem; line-height: 1.6; }
.faq-a p { margin: 0 0 .5rem; }
.faq-a p:last-child { margin-bottom: 0; }

/* ---- booking ---- */
/* Step rail: three numbered stops, always visible above the current step. */
.step-rail { display: flex; gap: .35rem; margin: .2rem 0 1.5rem; padding: 0; list-style: none; }
.step-rail li {
  flex: 1; display: flex; align-items: center; gap: .5rem; min-width: 0;
  padding: .45rem .3rem .55rem; border-bottom: 2px solid var(--line); color: var(--dim);
}
.step-rail .n {
  display: flex; align-items: center; justify-content: center; flex: none;
  width: 1.7rem; height: 1.7rem; border-radius: 50%;
  font: 700 .78rem/1 ui-monospace, "SF Mono", Menlo, monospace;
  border: 1px solid var(--line); background: var(--panel); color: var(--dim);
}
.step-rail .t {
  font-size: .8rem; font-weight: 600; letter-spacing: .02em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.step-rail li.now { color: var(--cyan); border-bottom-color: var(--cyan); }
.step-rail li.now .n { border-color: var(--cyan); color: var(--cyan); background: var(--bg); box-shadow: var(--glow-c); }
.step-rail li.done { color: var(--cyan-dim); border-bottom-color: var(--cyan-dim); }
.step-rail li.done .n { border-color: var(--cyan-dim); color: var(--cyan-dim); }
@media (max-width: 480px) {
/* Phones: wrap the labels instead of ellipsizing them — "One agree…" is
     exactly the kind of truncation this flow exists to avoid. */
.step-rail .t { font-size: .74rem; letter-spacing: 0; white-space: normal; line-height: 1.15; overflow: visible; text-overflow: clip; }
}
/* Narrow phones: the badge goes above the label rather than beside it. Beside
   it, the label had about fifty pixels for the word "agreement" and, with
   nothing to clip it, simply printed across the next step's number - the rail
   read "2 One agreeme③ Payme". */
@media (max-width: 400px) {
.step-rail li { flex-direction: column; align-items: center; gap: .25rem; text-align: center; }
.step-rail .t { min-width: 0; overflow-wrap: anywhere; }
}

/* Agreement expander: title + plain-words line in the summary, the verbatim
   legal body in a scroll box (same look as .waiver-body, shorter), and a
   checkbox row that the JS enables after open + scroll-to-end. */
/* The two services, side by side on the payment step. A choice this size
   deserves to be seen next to its price, not buried in a link. Stacks on a
   narrow phone, because two four-figure cards side by side at 320px are two
   unreadable cards. */
/* The two authorisations, on the client's own case page. Deliberately plain:
   this is the most consequential thing they will do here, and a form that
   looks like a sales card is a form people skim. */
.authority h3 { margin: 0 0 .3rem; }
.auth-row { padding: .8rem 0; border-top: 1px solid var(--line); }
.auth-row:first-of-type { border-top: 0; }
.auth-head { display: flex; align-items: baseline; justify-content: space-between; gap: .6rem; }
.auth-on { font: 600 .72rem/1 ui-monospace, monospace; color: var(--cyan); }
.auth-off { font: 600 .72rem/1 ui-monospace, monospace; color: var(--dim); }
.auth-item {
  display: flex; gap: .5rem; align-items: center; justify-content: space-between;
  flex-wrap: wrap; margin: .45rem 0; font-size: .85rem;
}
.auth-item-acts { display: flex; gap: .35rem; flex: none; }
/* The full form, as it will actually read.
   It WAS monospace, on the reasoning that a document is not prose. That was
   wrong twice over: the thing a records department reads is typeset, and at
   .74rem monospace on a 390px phone it read as a terminal dump, which is what
   Eric said out loud on 2026-08-26. Same document model as the printed page
   renders, so the preview and the paper cannot drift.
   Tokens here, unlike the print window: this one is inside the app and has to
   follow the scheme the reader chose. */
.auth-doc {
  margin: 0;
  font: .82rem/1.6 var(--font-body, ui-serif, Georgia, serif);
  color: var(--soft);
}
.auth-doc h1 {
  font-size: .95rem; line-height: 1.35; margin: 0 0 .7rem;
  text-transform: uppercase; letter-spacing: .02em;
  border-bottom: 1px solid var(--line); padding-bottom: .4rem; color: var(--ink);
}
.auth-doc h2 {
  font-size: .66rem; letter-spacing: .12em; text-transform: uppercase;
  margin: 1rem 0 .25rem; color: var(--dim);
}
.auth-doc p { margin: 0 0 .45rem; }
.auth-doc .doc-line { margin: 0 0 .15rem; }
.auth-doc .doc-meta {
  margin: 0 0 .8rem; display: grid; grid-template-columns: auto 1fr; gap: .1rem .5rem;
}
.auth-doc .doc-meta dt { color: var(--dim); }
.auth-doc .doc-meta dd { margin: 0; color: var(--ink); font-weight: 600; }
/* A run of underscores is somewhere to write on paper. On screen it is a
   line, because forty-six underscore glyphs is not a form field. */
.auth-doc .doc-rule {
  display: inline-block; min-width: 9rem; height: 1.05em;
  border-bottom: 1px solid var(--line); vertical-align: baseline;
}
.auth-doc .doc-checks, .auth-doc .doc-bullets { list-style: none; margin: .35rem 0 .5rem; padding: 0; }
.auth-doc .doc-checks li { display: flex; gap: .5rem; margin: 0 0 .5rem; }
.auth-doc .doc-box {
  flex: none; width: .9rem; height: .9rem; margin-top: .18rem;
  border: 1.5px solid var(--dim); border-radius: 2px; display: block; position: relative;
}
.auth-doc .doc-box.is-on { border-color: var(--ink); }
.auth-doc .doc-box.is-on::after {
  content: ''; position: absolute; left: .24rem; top: 0;
  width: .26rem; height: .56rem; border: solid var(--ink);
  border-width: 0 2px 2px 0; transform: rotate(42deg);
}
.auth-doc .doc-check-body { display: block; }
.auth-doc .doc-check-body strong { color: var(--ink); }
.auth-doc .doc-note { display: block; color: var(--dim); font-size: .95em; }
/* Says the state to a screen reader, since the box itself is decoration. */
.auth-doc .doc-sr {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap;
}
.auth-doc .doc-bullets li { margin: 0 0 .2rem .15rem; }
.auth-doc .doc-bullets li::before { content: '\2022'; margin-right: .45rem; color: var(--dim); }
.auth-doc .doc-rights { margin: .25rem 0 .5rem; padding-left: 1.15rem; }
.auth-doc .doc-rights li { margin: 0 0 .35rem; }
.auth-doc .doc-sig { margin-top: 1rem; border-top: 1px solid var(--line); padding-top: .6rem; }

.tier-pick { display: flex; gap: .6rem; margin: 0 0 .9rem; flex-wrap: wrap; }
.tier-card {
  flex: 1 1 14rem; min-width: 0; text-align: left; cursor: pointer;
  display: flex; flex-direction: column; gap: .3rem;
  padding: .8rem .9rem; border-radius: 12px;
  background: var(--card, rgba(255, 255, 255, .03));
  border: 1px solid rgba(255, 255, 255, .14);
  color: inherit; font: inherit;
}
.tier-card:hover:not(:disabled) { border-color: rgba(255, 255, 255, .3); }
.tier-card.on { border-color: var(--cyan); box-shadow: inset 0 0 0 1px var(--cyan); }
.tier-card:disabled { opacity: .55; cursor: not-allowed; }
.tier-card .tier-name { font-weight: 700; }
.tier-card .tier-price { font: 700 1.35rem/1 ui-monospace, monospace; color: var(--cyan); }
.tier-card.on .tier-price { color: var(--magenta); }
.tier-card .tier-what { font-size: .78rem; line-height: 1.45; color: var(--dim); }
.tier-card:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }

.agreement {
  border: 1px solid var(--line); border-radius: 14px; background: var(--panel);
  margin-bottom: .8rem;
}
.agreement > summary {
  display: grid; grid-template-columns: 1fr auto; align-items: center; column-gap: .6rem;
  cursor: pointer; list-style: none; padding: .8rem 1.05rem; min-height: 44px;
}
.agreement > summary::-webkit-details-marker { display: none; }
.agreement > summary::after {
  content: '\25B8'; grid-column: 2; grid-row: 1;
  color: var(--cyan); font-size: .8rem;
}
.agreement[open] > summary::after { content: '\25BE'; }
.agreement[open] > summary { border-bottom: 1px solid var(--line); }
.agreement-title { grid-column: 1; grid-row: 1; font-weight: 700; color: var(--ink); }
.agreement-plain { grid-column: 1; grid-row: 2; font-size: .88rem; font-weight: 400; color: var(--dim); line-height: 1.45; }
/* Was 14rem: a six-line window onto about twelve screens of legal text, inside
   a page that also scrolls. Nothing is added below it and nothing overlays it,
   because the JS unlocks the acknowledgement from this box's own scrollHeight. */
.agreement-body {
  max-height: min(58dvh, 30rem); overflow-y: auto; margin: .75rem 1.05rem 0;
  border: 1px solid var(--line); border-radius: 12px;
  padding: 1rem 1.1rem; background: var(--bg); font-size: .96rem;
}
.agreement-body h3 { color: var(--cyan); font-size: .88rem; letter-spacing: .06em; text-transform: uppercase; margin-top: .9rem; }
.agreement-body h3:first-child { margin-top: 0; }
.agreement-check {
  display: flex; align-items: center; gap: .6rem;
  margin: .5rem .55rem .55rem; padding: .45rem .5rem; min-height: 44px;
  border-radius: 10px; cursor: pointer; font-size: .95rem; color: var(--dim);
}
.agreement-check input[type=checkbox] {
  /* 1.3rem is 22.75px at the 17.5px root, and the `min-width: 1.3rem` here
     was more specific than the 24px floor further down, so this one box stayed
     under it while every other checkbox grew. It is the box that gates paying
     for the chat line. */
  width: 1.4rem; height: 1.4rem; min-width: 24px; min-height: 24px;
  margin: 0; accent-color: var(--cyan);
}
.agreement-check:has(input:disabled) { cursor: not-allowed; opacity: .75; }
.agreement-check:has(input:checked) { color: var(--cyan); background: rgba(45,226,255,.07); }

/* Step 3 price block: the number is the headline, the included line under it. */
.price-line {
  margin: 1rem 0; padding: .95rem 1.1rem;
  border: 1px solid var(--cyan-dim); border-radius: 14px;
  background: rgba(45,226,255,.05);
  color: var(--soft); font-size: .98rem; line-height: 1.55;
}
.price-line .price,
.price-line .amount,
.price-line strong:first-child {
  display: block; font: 800 1.75rem/1.15 inherit; color: var(--cyan);
  font-variant-numeric: tabular-nums; white-space: nowrap; margin: 0 0 .2rem;
}

/* ---- shared form rows / folded-in sign-in ---- */
/* Touch-friendly checkbox row: the whole bordered row is the hit area. */
.check-row {
  display: flex; align-items: flex-start; gap: .6rem;
  margin: .4rem 0; padding: .6rem .7rem; min-height: 44px;
  border: 1px solid var(--line); border-radius: 10px; background: var(--panel-2);
  cursor: pointer; font-size: .92rem; line-height: 1.5; color: var(--soft);
}
.check-row input[type=checkbox] {
  width: 1.25rem; height: 1.25rem; min-width: 1.25rem; margin: .15rem 0 0;
  accent-color: var(--cyan);
}
.check-row:has(input:checked) { border-color: var(--cyan-dim); color: var(--ink); }

/* The sign-in card folded into booking and subscribe (js/inline-auth.js). */
.auth-inline { border-color: var(--cyan-dim); }
.auth-inline h3 { margin-bottom: .35rem; }
.auth-inline input[type=email],
.auth-inline input[type=text] { min-height: 44px; }
/* Each section is labeled in the scroll itself — the chips alone shouldn't
   be the only way to know which block you're looking at. */
.case-sec-h { color: var(--cyan); font-size: 1.02rem; letter-spacing: .04em; margin: 0 0 .55rem; }

/* Keyboard focus for the new interactive rows (same magenta as the rest). */
.agreement > summary:focus-visible,
.faq > summary:focus-visible {
  outline: 2px solid var(--magenta); outline-offset: 2px;
}
.check-row:focus-within,
.agreement-check:focus-within {
  outline: 2px solid var(--magenta); outline-offset: 2px;
}

/* ---- image lightbox: tap a chat image to expand it ---- */
.lightbox {
  position: fixed; inset: 0; z-index: 80;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: .6rem;
  padding: max(1rem, env(safe-area-inset-top)) 1rem max(1rem, env(safe-area-inset-bottom));
  background: rgba(4, 6, 10, .93); backdrop-filter: blur(6px);
}
.lightbox img {
  max-width: 100%; max-height: 82vh; max-height: 82dvh; object-fit: contain;
  border-radius: 10px; border: 1px solid var(--line);
}
.lightbox-x {
  position: absolute; top: max(.6rem, env(safe-area-inset-top)); right: .8rem;
  border: 1px solid var(--line); background: var(--panel-2); color: var(--ink);
  border-radius: 50%; width: 2.4rem; height: 2.4rem; font-size: 1rem; cursor: pointer;
}
.lightbox-bar {
  margin: 0; display: flex; gap: .9rem; align-items: center; max-width: 100%;
  color: var(--dim); font-size: .85rem;
}
.lightbox-bar span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 60vw; }
body.lightbox-open { overflow: hidden; }
/* The signing sheet locks the page behind it, the same as the lightbox above
   and the full chat. Eric, 2026-08-27, signing on a phone: "when signing the
   screen moves around". The overlay already contains its own scroll
   (overscroll-behavior: contain), but the DOCUMENT behind it was still free
   to move, so a drag that started a hair outside the canvas scrolled the case
   page under the sheet while he was trying to write his name. The canvas has
   had touch-action: none all along, which is why it looked like a quirk of
   whichever browser he happened to be in. */
body.sheet-open { overflow: hidden; }
.msg-img { cursor: zoom-in; }

/* ------------------------------------------------------------- what's new
   App Store style: the version, then a short list of what actually changed
   for whoever is reading it. Dismissible, and it never comes back. */
.whats-new h3 { margin: .2rem 0 .1rem; }
.whats-new h3 + ul { margin-top: .35rem; }
.whats-new-list { margin: 0 0 1rem; padding-left: 1.15rem; }
.whats-new-list li { margin: 0 0 .45rem; font-size: .93rem; line-height: 1.45; }

/* ------------------------------------------------------------- the follow-up
   Offered from the case rather than at checkout, so it reaches somebody who has
   read their report and knows whether they want more - not somebody still
   deciding whether to trust him at all. Quieter than the review card on
   purpose: this one is asking for money. */
.followup-offer {
  margin: 1rem 0 0;
  padding: 1.15rem 1.15rem 1.1rem;
  /* Lighter border and a softer glow: the card should read as an offer, not
     as an alert. */
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--panel-2);
}
.followup-offer h3 {
  margin: 0 0 .45rem;
  font-size: 1.12rem;
  line-height: 1.3;
  /* "Your follow-up is reserved." broke at the hyphen and left one word on the
     second line. Balanced wrapping keeps a short heading looking deliberate. */
  text-wrap: balance;
}
.followup-offer p {
  margin: 0 0 .55rem;
  font-size: .93rem;
  /* Body text was set tight for a card this wide. */
  line-height: 1.62;
  color: var(--soft);
}
/* "Same case. Same file. No starting over." Understated, not a second
   heading: it carries weight by sitting apart, not by shouting. */
.followup-offer .fu-emphasis {
  margin: 0 0 .2rem;
  font-size: .89rem;
  color: var(--soft);
  font-weight: 600;
  letter-spacing: .015em;
}
/* Price and button are one decision, so they are one row. */
.followup-offer .fu-buy {
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
  margin: .95rem 0 .7rem;
  padding-top: .95rem;
  border-top: 1px solid var(--line);
}
.followup-offer .price {
  font-size: 1.65rem;
  font-weight: 800;
  line-height: 1;
  color: var(--magenta);
  font-variant-numeric: tabular-nums;
}
.followup-offer .btn { flex: 0 1 auto; }
/* One step dimmer than the body, so the terms sit under the offer rather
   than competing with it. */
.followup-offer .fu-fine {
  margin: 0;
  font-size: .82rem;
  line-height: 1.55;
  color: var(--dim);
}
.followup-offer p:last-of-type { margin-bottom: 0; }

/* Coming back from Stripe, before the webhook has necessarily landed. The
   same card resolved, not a bright new box: nothing is wrong, and a green
   outlined alert says something is. */
.followup-offer.is-done { border-color: var(--line); }
.followup-offer .fu-tick {
  margin-right: .4rem;
  font-size: .95rem;
  color: var(--green);
}

/* On a narrow phone the price and the button will not sit side by side without
   squeezing both. Stacking them is the better answer than a cramped row, and a
   full-width button is an easier target for someone who is unwell. It stays
   one decision because it stays inside one bordered block. */
@media (max-width: 380px) {
  .followup-offer .fu-buy {
    flex-direction: column;
    align-items: stretch;
    gap: .65rem;
  }
  .followup-offer .fu-buy .price { line-height: 1; }
  .followup-offer .fu-buy .btn { width: 100%; }
}

/* ---------------------------------------------------------------- the review
   Opens under the documents once the report has landed. Five stars, a few
   words, and a thank-you that fades away on its own rather than sitting there
   waiting to be dismissed. */
.delivered-tick { flex: none; font-size: .95rem; }
.review-card {
  margin: 1rem 0 0; padding: 1rem 1rem 1.1rem;
  border: 1px solid var(--cyan-dim); border-radius: 14px;
  background: var(--panel-2); box-shadow: var(--glow-c);
}
.review-card h3 { margin: 0 0 .5rem; }
.review-card p { font-size: .93rem; }
.review-card .follow-note {
  margin: .7rem 0 0; padding: .55rem .7rem;
  border-left: 3px solid var(--magenta); border-radius: 0 8px 8px 0;
  background: var(--panel);
}
.review-card textarea {
  width: 100%; margin: .2rem 0 .6rem;
  padding: .6rem .7rem; font: inherit; font-size: .95rem;
  color: var(--ink); background: var(--panel);
  border: 1px solid var(--line); border-radius: 10px; resize: vertical;
}

.stars { display: flex; gap: .15rem; margin: .5rem 0 .7rem; }
.star {
  padding: .1rem .2rem; font-size: 2rem; line-height: 1;
  color: var(--line); background: none; border: 0; cursor: pointer;
  transition: color .15s ease-out, transform .15s cubic-bezier(.2, .8, .3, 1);
}
.star:hover { transform: scale(1.12); }
.star.on { color: var(--gold); text-shadow: 0 0 12px rgba(255, 204, 85, .5); }
.star:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; border-radius: 4px; }

/* Fades in, holds long enough to read, then drifts up and away. Nothing to
   dismiss: they are finished, and a button would say otherwise. */
.thanks {
  margin: 1.4rem 0; text-align: center;
  font-size: 1.15rem; font-weight: 700; color: var(--cyan);
  animation: thanks 4s ease-in-out forwards;
}
@keyframes thanks {
  0% { opacity: 0; transform: translate3d(0, 6px, 0); }
  12% { opacity: 1; transform: none; }
  70% { opacity: 1; transform: none; }
  100% { opacity: 0; transform: translate3d(0, -10px, 0); }
}

@media (prefers-reduced-motion: reduce) {
.star,
.star:hover { transition: none; transform: none; }
.thanks { animation: none; }
}

/* --------------------------------------------------------- appearance picker
   Each swatch is painted from that scheme's OWN tokens, applied to the swatch
   rather than to the page, so a swatch can never drift out of sync with what
   picking it actually does. */
.scheme-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .5rem; }
.scheme-swatch {
  display: grid; gap: .18rem; padding: .6rem .65rem; text-align: left;
  color: var(--ink); background: var(--panel-2); cursor: pointer;
  border: 1px solid var(--line); border-radius: 10px; font: inherit;
}
.scheme-swatch.on { border-color: var(--cyan); box-shadow: var(--glow-c); }
.scheme-name { font-weight: 700; font-size: .9rem; }
.scheme-blurb { line-height: 1.3; }
.scheme-chip {
  display: flex; gap: 3px; align-items: center;
  height: 22px; margin-bottom: .15rem; padding: 4px 5px;
  border-radius: 6px; border: 1px solid var(--line);
}
.scheme-chip i { flex: 1; height: 100%; border-radius: 2px; }
/* THESE LITERALS ARE LOAD-BEARING AND MUST BE UPDATED BY HAND. A preview
   has to show a scheme you are not currently in, so it cannot read tokens.
   Change any scheme's --bg/--line/--cyan/--magenta/--ink above and change
   its swatch here in the same commit. (Missed once already: after the
   2026-08-25 restyle this block still advertised the old near-black and hot
   magenta for a scheme that is now deep navy and violet.) */
.sc-neon { background: #060A1C; border-color: #242F63; }
.sc-neon i:nth-child(1) { background: #37E1FF; }
.sc-neon i:nth-child(2) { background: #A97BFF; }
.sc-neon i:nth-child(3) { background: #EDF1FF; }
.sc-calm { background: #0B0E14; border-color: #232C3F; }
.sc-calm i:nth-child(1) { background: #6FC7D8; }
.sc-calm i:nth-child(2) { background: #D98BC4; }
.sc-calm i:nth-child(3) { background: #E4E9F2; }
.sc-paper { background: #F3EEE3; border-color: #DDD4C2; }
.sc-paper i:nth-child(1) { background: #0E6E86; }
.sc-paper i:nth-child(2) { background: #A8367F; }
.sc-paper i:nth-child(3) { background: #23201A; }
.sc-contrast { background: #000; border-color: #6A6A6A; }
.sc-contrast i:nth-child(1) { background: #4FE9FF; }
.sc-contrast i:nth-child(2) { background: #FF77E0; }
.sc-contrast i:nth-child(3) { background: #FFF; }
.uploads .filelist { margin: 0; }
.uploads .filelist li {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center; gap: .6rem; padding: .5rem 0;
}
.uploads .filelist .fname { display: block; }
/* The name gets the whole column. "sl..." is not a file name. */
.uploads .filelist .fname a {
  max-width: none; white-space: normal; overflow-wrap: anywhere;
  overflow: visible; text-overflow: clip;
}
.uploads .filelist .fmeta { padding-left: .1rem; }
/* Five sources, five colours: report, recording, upload, chat, saved. */
.kind-pill.chat { color: var(--green); border-color: var(--green); }
.kind-pill.saved { color: var(--gold); border-color: var(--gold); }
@keyframes rd-sweep {
  0% { transform: translate3d(-110%, 0, 0); }
  100% { transform: translate3d(300%, 0, 0); }
}

/* ============================================================================
   The case folder - card stock in a neon room.

   Owned by folder.js (public/js/folder.js). Merge target: public/css/site.css.

   Manila lives on the CHROME only: the folder card on the shelf, the cut tabs,
   the rim and the crease of the open folder. The pages inside stay the app's
   own dark panels, so a case reads as paper resting on the desk rather than a
   cream slab dropped into the dark.

   The five variables below are the whole manila palette. Retint them and every
   piece of folder chrome follows.
   ========================================================================= */
:root {
  --manila: #DEC791;        /* the card stock */
  --manila-2: #F0DFB0;      /* the lit top edge of a fold */
  --manila-ink: #2A2113;    /* type printed on the stock */
  --manila-edge: #A98F5F;   /* cut edges, rims, the back cover */
  --manila-fold: rgba(10, 7, 2, .7); /* the crease where a flap meets a body */
  /* Ink for EMPHASIS on the stock. The dashboard was writing the app's own
     dark-ground tokens straight onto a light folder: measured on the shelf,
     the report-due date came out rgb(228,233,242) on rgb(222,199,145), which
     is 1.35:1, and CHECK-IN DUE came out 1.28:1. A flag whose entire job is to
     be noticed was the least readable thing on the screen. These four are the
     same four states in ink that belongs on paper. */
  --manila-strong: #14100A; /* a date, a time, anything emphasised   ~11:1 */
  --manila-alert: #8A2B00;  /* act now: CHECK-IN DUE, TELEHEALTH      ~6:1 */
  --manila-note: #14432F;   /* settled and good: a booked follow-up   ~8:1 */
  --manila-flag: #4A1D6E;   /* money in flight: awaiting, paid        ~7:1 */
}

/* The work clock on a case card. Sits on the tab band, top right, where the
   thumb reaches without covering the name or the read. A real control on a
   card that is otherwise a link, so it has to look pressable and be big
   enough to hit: 44px of touch target around a small dot. */
.folder-clock {
  position: absolute; top: 0; right: 4px; z-index: 2;
  display: inline-flex; align-items: center; gap: .3rem;
  min-height: 1.9rem; padding: 0 .6rem;
  border-radius: 999px; cursor: pointer;
  font: 600 .72rem/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  /* Legible, not decorative. At .62 with no label this read as part of the
     card stock, which is why Eric went looking for a toggle and found
     nothing. */
  /* Dimmed by COLOUR, never opacity. `opacity: .85` here blended the ink
     toward the tab band and, on the paper scheme where that band is #9C8551,
     pulled the clock to 3.05:1 measured in the rendered pixels. It is the same
     mistake .folder-flags had, and the same fix: opacity composites the whole
     element, so a child cannot undo it and the number is never what the token
     says. --manila-strong on the paper band is 5.31:1 before the wash below
     darkens it further. */
  color: var(--manila-strong);
  background: rgba(0, 0, 0, .10);
  -webkit-tap-highlight-color: transparent;
}
/* The visible pill stays small on a manila tab; the TAP target is 44px, which
   is what the comment above this rule always claimed and the padding never
   delivered. */
.folder-clock::before {
  content: ''; position: absolute; top: 50%; left: 50%;
  width: 100%; height: 44px; min-width: 44px;
  transform: translate(-50%, -50%);
}
.folder-clock:hover { background: rgba(0, 0, 0, .16); }
.folder-clock .fc-dot {
  width: .5rem; height: .5rem; border-radius: 50%;
  border: 1.5px solid currentColor; flex: none;
}
/* Running: filled, coloured, and quietly pulsing, so a shelf of eight cards
   answers "what am I on right now" from across the room. */
.folder-clock.on {
  /* The running state set opacity:1 to undo the .85 above; with the dimming
     gone there is nothing to undo. */
  /* Measured on the tab band, #0B6E4F came out at 2.02:1, on the one badge
     whose whole job is to answer "am I on the clock right now" from across
     the room. Same green family, dark enough to read on card stock, and the
     wash carries a little more colour so the pill reads as running before
     the number does. */
  color: var(--manila-note);
  background: rgba(16, 185, 129, .26);
}
.folder-clock.on .fc-dot { background: currentColor; animation: fc-pulse 2.4s ease-in-out infinite; }
.folder-clock.busy { opacity: .4; pointer-events: none; }
.folder-clock:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }
@keyframes fc-pulse { 50% { opacity: .35; } }
@media (prefers-reduced-motion: reduce) {
  .folder-clock.on .fc-dot { animation: none; }
}

.folder {
  /* --fld-tab keeps the tab band and the paper edges peeking behind it in sync. */
  --fld-tab: 1.68rem;
  position: relative; display: block; isolation: isolate;
  /* perspective gives the flap real depth on open. Safe here and only here:
     the shelf never holds the chat, which is position:fixed in full screen. */
  perspective: 900px;
  padding: 0 3px 5px; margin: 0;
  text-decoration: none; color: var(--manila-ink);
  border-radius: 12px 14px 14px 14px;
  background: var(--manila-edge);
  box-shadow: 0 1px 0 rgba(255, 255, 255, .25) inset, 0 6px 16px rgba(0, 0, 0, .45);
  transition: transform 140ms ease, box-shadow 140ms ease;
}
.folder:hover,
.folder:focus-visible {
  transform: translateY(-2px);
  box-shadow: 0 1px 0 rgba(255, 255, 255, .25) inset, 0 10px 22px rgba(0, 0, 0, .5),
              0 0 0 1px var(--cyan-dim), var(--glow-c);
}
.folder:focus-visible { outline: 2px solid var(--magenta); outline-offset: 3px; }

/* Sheets inside the folder, peeking above the flap next to the tab. */
.folder::before,
.folder::after {
  content: ''; position: absolute; z-index: 0; pointer-events: none;
  top: calc(var(--fld-tab) - .5rem); height: 1rem; right: .7rem; width: 42%;
  border-radius: 4px 4px 0 0;
  background: linear-gradient(180deg, rgba(250, 247, 238, .95), rgba(226, 219, 200, .8));
  box-shadow: 0 1px 2px rgba(0, 0, 0, .3);
}
.folder::after { right: 1.3rem; width: 32%; top: calc(var(--fld-tab) - .34rem); opacity: .8; }
@keyframes badge-in {
  from { opacity: 0; transform: translate3d(0, -3px, 0) scale(.9); }
  to { opacity: 1; transform: none; }
}
/* A dark chip on card stock: the pill keeps its own neon border and text, and
   stops fighting the manila underneath it. */
.folder .status-pill {
  grid-column: 2; justify-self: end;
  background: rgba(8, 11, 19, .88); box-shadow: 0 1px 3px rgba(0, 0, 0, .35);
}

/* The open: the flap swings up on its bottom edge, the sheets lift and fan,
   the whole card comes toward you. Then folder.js navigates. */
/* The folder-opening animation is gone (Eric, 2026-08-21). It set
   pointer-events: none for the length of the animation and then navigated, and
   the class was never taken off - so coming back to the shelf restored a page
   whose folders could no longer be tapped at all. A folder opens on the first
   tap now, with nothing in between. */

  100% { transform: scale(1.07) translateY(-11px); }
}
@keyframes fld-flap {
  0% { transform: rotateX(0deg); }
  100% { transform: rotateX(-54deg) translateY(-2px); }
}
@keyframes fld-tab {
  0% { transform: none; }
  100% { transform: translateY(-3px) rotate(-.6deg); }
}
}
}

/* ------------------------------------------------------- the open workspace */
/* Never give .case-folder transform, filter, backdrop-filter, contain, or
   will-change: transform. The full-screen chat is position:fixed and lives in
   one of its pages, and any of those would pin it to this box instead of the
   screen. position:relative is fine and is all this needs. */
.case-folder {
  position: relative;
  padding: .1rem .4rem .55rem;
  border: 1px solid var(--manila-edge); border-top: none;
  border-radius: 0 0 14px 14px;
  box-shadow: inset 0 7px 13px -9px var(--manila-fold);
}
/* Two sheet edges showing under the closed bottom of the folder. */
.case-folder::after {
  content: ''; position: absolute; pointer-events: none;
  left: 1.5rem; right: 1.5rem; bottom: -5px; height: 5px;
  border-radius: 0 0 9px 9px;
  background: var(--manila); opacity: .5;
  box-shadow: 0 4px 0 -1px var(--manila-edge);
}

/* The tab strip. z-index 4: under the header at 5, per the budget.
   The offset is the header's real height, measured and published by nav-menu.js,
   so the strip docks against the bar instead of floating with a transparent
   band above it that page content slid through. */
.folder-tabs {
  /* Below the bar's BOTTOM, not its height. The bar itself sticks under the
     demo strip when there is one, so the strip's height has to be in the sum
     as well: measuring only the bar's own height docked the tabs 62px behind
     it at 320. Both numbers are published by the code that owns them. */
  /* Eric, 2026-08-21: "Tabs should not travel. They should stay at the top."
     Sticky meant they rode down the page, clipped against the bar, and left
     a band of chrome over the content. They sit at the top of the folder and
     scroll away with it. */
  position: static; z-index: 4;
  display: flex; align-items: flex-end; gap: .22rem;
  min-height: calc(44px + .75rem);
  margin: 0 -.4rem .7rem; padding: .4rem .5rem 0;
  overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none;
  background: var(--bar); backdrop-filter: blur(8px);
  border-bottom: 2px solid var(--manila-edge);
}
.folder-tabs::-webkit-scrollbar { display: none; }
/* An author `display` rule beats the UA stylesheet's [hidden] { display:none },
   so the tabs of a closed group stayed in the layout: twelve tabs sharing a
   320px row instead of four. This is the rule that makes the second tier
   actually a second tier. */
.folder-tabs > a[hidden] { display: none; }
.folder-tabs > a {
  position: relative; flex: 0 0 auto;
  display: inline-flex; align-items: center; gap: .34rem;
  min-height: 44px; padding: .5rem .9rem;
  white-space: nowrap; text-decoration: none;
  font-weight: 700; font-size: .82rem; line-height: 1; letter-spacing: .01em;
  color: var(--manila-ink);
  /* Flat stock behind the label, the darker cut only along the base: a tab that
     recedes without dropping its label onto a shadow. */
  background: linear-gradient(180deg, var(--manila) 0 78%, var(--manila-edge));
  /* The cut: a trapezoid a touch wider at the base, sliced from card stock. */
  clip-path: polygon(.5rem 0, calc(100% - .5rem) 0, 100% 100%, 0 100%);
}
/* Staggered cuts: the tabs behind the front one sit lower, so the strip has the
   ragged top edge of a real set of files. */
.folder-tabs > a:nth-child(3n+2) { margin-top: .3rem; }
.folder-tabs > a:nth-child(3n+3) { margin-top: .15rem; }
/* The front-most tab: full height, lit stock, and pushed 2px over the rim so it
   joins the page body below instead of sitting on a shelf above it. */
.folder-tabs > a.on {
  margin-top: 0; margin-bottom: -2px; padding-bottom: calc(.5rem + 2px);
  background: linear-gradient(180deg, var(--manila-2), var(--manila));
  /* drop-shadow, not box-shadow: the glow has to follow the cut, and box-shadow
     is clipped away by clip-path. */
  filter: drop-shadow(0 -1px 6px var(--cyan-dim));
}
.folder-tabs > a.on::after {
  content: ''; position: absolute; top: 0; left: .5rem; right: .5rem; height: 2px;
  border-radius: 2px; background: var(--cyan);
}
/* Inset so the clip-path can't shave the focus ring off. */
.folder-tabs > a:focus-visible { outline: 2px solid var(--magenta); outline-offset: -3px; }
.ftab-ic { font-size: .95rem; line-height: 1; }
.ftab-t { line-height: 1; }
/* Inside the folder the strip only has to say "this one": the shelf already
   said which kind of thing changed. The tab he is on never carries a dot. */
.ftab-dot {
  width: 7px; height: 7px; margin-left: .1rem; flex: none;
  border-radius: 50%; background: var(--cyan);
  box-shadow: 0 0 6px rgba(45, 226, 255, .85);
}
.ftab-dot[hidden] { display: none; }
/* Only the label recedes on the tabs behind the front one: fading the tab
   itself would fade its card stock into the dark page behind it. */
.folder-tabs > a .ftab-ic,
.folder-tabs > a .ftab-t { opacity: .82; }
.folder-tabs > a.on .ftab-ic,
.folder-tabs > a.on .ftab-t { opacity: 1; }

/* Said once, on the first case a client ever opens. Tabs are a real interface
   and nobody arrives knowing that tapping the paper turns the page. */
.nav-hint {
  display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
  margin: 0 0 .7rem; padding: .6rem .75rem;
  font-size: .85rem; line-height: 1.5; color: var(--soft);
  background: var(--panel-2);
  border: 1px solid var(--line); border-left: 3px solid var(--cyan); border-radius: 10px;
}
.nav-hint .btn { padding: .28rem .7rem; font-size: .78rem; }

/* A caller-supplied button in the composer row, sized and shaped exactly like
   the paperclip beside it so the row stays one row. */
button.attach-btn {
  font: inherit;
  color: inherit;
  cursor: pointer;
}

/* The update card is a guided tour now: one page of their case per card, in
   tab order, paged through with Next. A wall of text about how to stop
   scrolling is the wrong shape for the thing it is explaining. */
.whats-new .wn-step {
  margin: 0 0 .3rem; font-size: .72rem; letter-spacing: .1em;
  text-transform: uppercase; color: var(--dim);
  font-variant-numeric: tabular-nums;
}
.whats-new .wn-body { margin: .4rem 0 0; font-size: .95rem; line-height: 1.65; color: var(--soft); }
.whats-new .wn-dots { display: flex; gap: .3rem; margin: .9rem 0 0; }
.whats-new .wn-dots span {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--line);
}
.whats-new .wn-dots span.on { background: var(--cyan); box-shadow: var(--glow-c); }
.whats-new .actions { display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; }

/* ---------------------------------------------------------------- the demo
   Loud on purpose. A demo that looks exactly like the product is the point and
   also the risk: nobody should get a paragraph into reading a case before
   working out that none of it happened. */
.demo-bar {
  /* Above the page header (5), below every dialog. It used to sit at 90, over
     the settings panel, the naming sheet and the guided tour, so a modal's
     heading opened underneath a banner. A strip that says "this is a demo" is
     not more important than the thing you just opened. */
  position: sticky; top: 0; z-index: 40;
  /* flex-start, not center: the text wraps to three lines on a phone and a
     centred dot floats in the middle of them. */
  display: flex; align-items: flex-start; gap: .6rem; flex-wrap: wrap;
  padding: .5rem .8rem;
  font-size: .8rem; line-height: 1.45;
  color: #07090F; background: var(--gold);
}
.demo-bar .demo-what { flex: 1 1 14rem; min-width: 0; }
.demo-dot {
  width: 8px; height: 8px; flex: none; margin-top: .38rem; border-radius: 50%;
  background: #07090F;
}
.demo-acts { display: flex; gap: .4rem; flex: none; }
/* The doubled class is deliberate. This strip is the one LIGHT surface in a
   dark app, so its buttons carry near-black ink while every other button in
   the app carries near-white. `.demo-bar .btn` and `.btn.ghost` have the same
   specificity, so the winner was decided by which sheet came last, and the day
   the glow-up sheet landed after site.css the demo buttons went to var(--ink):
   measured at rgb(228,233,242) on rgb(220,187,120), about 1.3:1, which is not
   readable. Doubling the class puts this at (0,3,0) so a generic button rule
   in a later sheet cannot take the strip's ink with it. */
.demo-bar.demo-bar .btn {
  padding: .22rem .6rem; font-size: .75rem;
  color: #07090F; border-color: rgba(7, 9, 15, .45); background: rgba(255, 255, 255, .35);
}
.demo-bar.demo-bar .btn:hover { background: rgba(255, 255, 255, .6); }
/* Scrolled: the sentence goes and the word, the dot and the buttons close up
   onto one row. Same strip, a third of the height. */
.demo-bar.slim { align-items: center; }
.demo-bar.slim .demo-more { display: none; }
.demo-bar.slim .demo-what { flex: 0 0 auto; }
.demo-bar.slim .demo-dot { margin-top: 0; }
/* The sticky header sits under the strip rather than over it. The height comes
   from the strip itself, measured after it wraps: a fixed value was short by
   about ninety pixels on a phone, so the header slid underneath and disappeared
   as soon as anything scrolled. */
body.has-demo-bar .bar { top: var(--demo-bar-h, 2.4rem); }

/* --------------------------------------------------------- saved messages
   Both sides get this, and on both sides it is private: the bookmark lives in
   a subtree the browser cannot read, and nothing is written back to the
   message, so saving one tells the other person nothing.

   Prefixed sv- throughout. Never a bare .saved: that is already a file-kind
   pill on the Documents list. */
.sv-list { list-style: none; margin: .8rem 0 0; padding: 0; display: flex; flex-direction: column; gap: .7rem; }
.sv-item {
  position: relative;
  padding: .7rem .8rem .8rem;
  background: var(--panel-2);
  border: 1px solid var(--line); border-radius: 12px;
}
.sv-head { display: flex; align-items: baseline; gap: .5rem; margin-bottom: .4rem; }
.sv-who { font-weight: 700; font-size: .8rem; color: var(--ink); }
.sv-when { font-size: .75rem; color: var(--dim); }
.sv-drop {
  margin-left: auto; flex: none;
  width: 1.9rem; height: 1.9rem; padding: 0;
  font: inherit; font-size: .85rem; line-height: 1; cursor: pointer;
  color: var(--dim); background: none;
  border: 1px solid var(--line); border-radius: 8px;
}
.sv-drop:hover, .sv-drop:focus-visible { color: var(--danger); border-color: var(--danger); outline: none; }
.sv-text {
  margin: 0 0 .55rem; padding: .5rem .65rem;
  border-left: 3px solid var(--cyan-dim); border-radius: 0 8px 8px 0;
  background: var(--panel);
  font-size: .9rem; line-height: 1.55; color: var(--soft);
  white-space: pre-wrap; overflow-wrap: anywhere;
}
.sv-note-label { display: block; font-size: .75rem; color: var(--dim); }
.sv-note {
  display: block; width: 100%; margin-top: .25rem;
  padding: .5rem .6rem; font: inherit; font-size: .9rem; line-height: 1.5;
  color: var(--ink); background: var(--panel);
  border: 1px solid var(--line); border-radius: 9px; resize: vertical;
}
.sv-note:focus-visible { border-color: var(--cyan); outline: none; }
/* Where the save got to. Absent when there is nothing to say. */
.sv-pill {
  position: absolute; right: .8rem; bottom: .35rem;
  font-size: .7rem; color: var(--dim);
}
.sv-pill.ok { color: var(--green); }
.sv-pill.bad { color: var(--danger); }
.sv-pill[hidden] { display: none; }
/* The bookmark on a message in the chat. Only the person who saved it has it
   in their list, so only they ever see this. */
.sv-mark { margin-left: .35rem; font-size: .72rem; opacity: .75; }

/* ---------------------------------------------------------------- two tiers
   Eric asked for grouping that kills side scroll and left-to-right travel and
   keeps every page two taps away. So: a group row that never changes, and a
   page row showing only the open group.

   The rule that makes it work is that a group holds at most four pages, so the
   page row always fits across a 320px screen without scrolling and without a
   single label being cut short. */
.folder-groups {
  /* Static, like the tab row below it: tabs do not travel (Eric, 2026-08-21),
     and one strip sticking while its partner scrolls away is worse than
     either. Same horizontal edges as the tab row, or the two rows sit 7px
     out of line and read as a mistake. */
  display: flex; gap: .3rem;
  margin: 0 0 .1rem; padding: .45rem .1rem .35rem;
  background: var(--bar);
}
.fgrp {
  position: relative; flex: 1 1 0; min-width: 0;
  display: inline-flex; align-items: center; justify-content: center; gap: .35rem;
  min-height: 40px; padding: .4rem .5rem;
  font: inherit; font-weight: 700; font-size: .78rem; line-height: 1.15;
  letter-spacing: .02em; text-align: center;
  color: var(--dim); background: var(--panel-2);
  border: 1px solid var(--line); border-radius: 10px;
  cursor: pointer;
}
.fgrp-ic { font-size: 1rem; line-height: 1; flex: none; }
 /* Never mid-word: "Advis / or" is not a label. If a group name will not fit
    it gets a shorter name, not a hyphenless break. */
.fgrp-t { min-width: 0; overflow-wrap: normal; }
.fgrp.on {
  color: var(--ink); background: var(--panel);
  border-color: var(--cyan-dim); box-shadow: inset 0 -2px 0 var(--cyan);
}
.fgrp:focus-visible { outline: 2px solid var(--magenta); outline-offset: 2px; }
/* A page dot inside a closed group cannot be seen, so it surfaces here. */
.fgrp-dot {
  width: 7px; height: 7px; flex: none;
  border-radius: 50%; background: var(--cyan);
  box-shadow: 0 0 6px rgba(45, 226, 255, .85);
}
.fgrp-dot[hidden] { display: none; }

/* The page row sits under the group row, not under the header. */
.case-folder.two-tier .folder-tabs { top: calc(3rem + 52px); }

/* Four tabs across a narrow phone: share the width evenly and stack the label
   under the icon so the word still fits whole. Truncating a label is not an
   option - a tab reading "Unansw..." is worse than no tab at all. */
.folder-tabs.tight { gap: .18rem; overflow-x: visible; padding-left: .35rem; padding-right: .35rem; }
.folder-tabs.tight > a {
  /* Basis is the label's own width, not zero. On an equal-shares row every tab
     got the same 64px whether it said "Chat" or "Unanswered", so the long ones
     overflowed while the short ones sat half empty. Growing from content means
     a row that fits gives each label at least what it needs, and a row that
     does not shrinks the wide ones first. */
  flex: 1 1 auto; min-width: 0;
  flex-direction: column; gap: .2rem;
  /* Four tabs across 320px leaves 64px of tab. Measured: "Overview" and
     "Summary" are 75-76px at the full size, so the label needs both a smaller
     size and its padding back. */
  padding: .42rem .18rem;
  font-size: .72rem;
  text-align: center;
  /* The trapezoid cut eats half a rem from each side, which at this width is
     most of the difference between a label that fits and one that does not. */
  clip-path: polygon(.25rem 0, calc(100% - .25rem) 0, 100% 100%, 0 100%);
}
.folder-tabs.tight > a .ftab-t {
  white-space: normal; line-height: 1.15; overflow-wrap: break-word;
}
.folder-tabs.tight .ftab-dot {
  position: absolute; top: .3rem; right: .3rem; margin: 0;
}
/* A 320px screen is a small screen and something has to give. It gives here,
   in a step down of type size, rather than in a truncated label or a row that
   scrolls sideways. */
@media (max-width: 360px) {
  /* The icon goes, and the label gets what it paid for: readable type and
     enough side padding to clear the trapezoid's slanted corners. It was
     11.2px with two pixels of padding, which reads as clipped even where it
     is not. The group row above keeps its icons - that is the row where the
     icon is doing the work. */
  /* The icon goes, and what it was using goes to the label: readable type and
     enough side padding to clear the trapezoid's slanted corners, which taper
     to about four pixels by the height the label sits at. It was 11.2px with
     three pixels of padding, which reads as clipped even where it is not. The
     group row above keeps its icons - that is the row where the icon is doing
     the work. */
  .folder-tabs.tight > a { font-size: .68rem; padding: .42rem .32rem; gap: 0; }
  .folder-tabs.tight > a .ftab-ic { display: none; }
  .fgrp { font-size: .7rem; padding: .4rem .3rem; }
}
/* Four groups across a phone: the label goes under the icon here too. */
.folder-groups.many .fgrp { flex-direction: column; gap: .15rem; padding: .35rem .25rem; }

/* The client's copy of the same engine: same tabs, same flips, no manila. The
   folder is the advocate's working object, not something a client should have to
   read as a metaphor. Add `plain` to the container (`el.classList.add('plain')`
   any time after mountPages) and the chrome drops away, leaving the app's own
   pill tabs. Nothing in folder.js changes. */
.case-folder { padding: 0; border: none; border-radius: 0; box-shadow: none; }
.case-folder::after { content: none; }
.case-folder .folder-tabs {
  align-items: center; gap: .4rem; min-height: 0;
  margin: 0 0 1rem; padding: .5rem .1rem .55rem;
  border-bottom: none;
}
.case-folder .folder-tabs > a {
  /* Tight enough that the client's three pills all fit at 390px. They scroll
     if they ever do not, but a tab half off the edge reads as a broken page
     rather than as more to scroll to. */
  margin: 0; padding: .3rem .7rem; gap: .3rem;
  font-weight: 600; font-size: .82rem;
  color: var(--dim); background: none; filter: none;
  border: 1px solid var(--line); border-radius: 999px; clip-path: none;
}
.case-folder .folder-tabs > a.on {
  color: var(--cyan); background: none;
  border-color: var(--cyan); box-shadow: var(--glow-c);
}
.case-folder .folder-tabs > a.on::after { content: none; }
.case-folder .folder-tabs > a .ftab-ic,
.case-folder .folder-tabs > a .ftab-t { opacity: 1; }

/* ------------------------------------------------------------- flip a page */
.fpage[hidden] { display: none !important; }
/* The shuffle. Tapping the right half sends the page he was on to the back of
   the pile and brings the next sheet up from under it, so it arrives slightly
   small, tilted, and low, then straightens as it lands on top. 280ms: long
   enough to read as paper moving, short enough that eight fast taps land eight
   flips instead of queueing behind an animation. */
.fpage.f-in-r { animation: fpage-shuffle-r 280ms cubic-bezier(.22, .72, .24, 1) both; }
.fpage.f-in-l { animation: fpage-shuffle-l 280ms cubic-bezier(.22, .72, .24, 1) both; }
.fpage.f-fade { animation: fpage-fade 190ms ease-out both; }
/* The pile itself gives a little as a sheet is pulled off it. */
.case-folder.shuffling::after { animation: fld-pile 280ms ease-out both; }

/* The hard guard. folder.js marks any page holding the full-screen chat, and
   these rules make a transform impossible on it even if a future flip class
   says otherwise. */
.fpage.no-transform,
.case-folder.no-transform { transform: none !important; }
.fpage.no-transform.f-in-l,
.fpage.no-transform.f-in-r,
.fpage.no-transform.enter,
.case-folder.no-transform.enter { animation-name: fpage-fade !important; }

/* The workspace settling, as if the folder had just been set down. */
.case-folder.enter,
.fpage.enter { animation: fld-settle 380ms cubic-bezier(.2, .8, .3, 1) both; }

@keyframes fpage-shuffle-r {
  0% { opacity: 0; transform: translate3d(26px, 9px, 0) scale(.966) rotate(1.1deg); }
  60% { opacity: 1; }
  100% { opacity: 1; transform: none; }
}
@keyframes fpage-shuffle-l {
  0% { opacity: 0; transform: translate3d(-26px, 9px, 0) scale(.966) rotate(-1.1deg); }
  60% { opacity: 1; }
  100% { opacity: 1; transform: none; }
}
/* Only the two sheet edges under the folder move, never .case-folder itself:
   it holds the position:fixed chat and must stay untransformed. */
@keyframes fld-pile {
  0% { transform: translate3d(0, -3px, 0); opacity: .28; }
  100% { transform: none; opacity: .5; }
}
@keyframes fpage-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes fld-settle {
  from { opacity: 0; transform: translate3d(0, 10px, 0); }
  to { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
.folder { transition: none; }
.folder:hover,
.folder:focus-visible { transform: none; }

.fpage.f-in-l,
.fpage.f-in-r,
.fpage.f-fade,
.case-folder.shuffling::after,
.case-folder.enter,
.fpage.enter { animation: none; }
.folder-tabs { scroll-behavior: auto; }
}

/* Phones: the drawer tabs give back their stagger before the name has to
   truncate, and the strip tightens up. */
@media (max-width: 420px) {
.folder-tabs { gap: .16rem; padding-left: .35rem; padding-right: .35rem; }
.folder-tabs > a { padding: .5rem .75rem; font-size: .8rem; }
}

/* ============================================================================
   The private notebook - a sheet of lined paper on a dark desk.

   Owned by notes.js (public/js/notes.js). Merge target: public/css/site.css.

   The paper is genuinely light in a dark app on purpose: it is a sheet of
   paper lying on the desk, not a panel, so it carries its own ink colour
   rather than borrowing the room's. The four variables below are the whole
   sheet. Retint them and the paper follows.
   ========================================================================= */
:root {
  --paper: #F6EEDC;                 /* the stock */
  --paper-ink: #1E1A14;             /* what he writes with */
  --paper-rule: rgba(93, 143, 178, .55);  /* the printed blue ruling */
  --paper-margin: rgba(196, 106, 106, .7); /* the red margin rule */
}

/* ============================================================
   Landing components (Eric, 2026-08-25: "You did nothing for the
   visual aesthetics like I asked", against a mockup with drawn
   icons, chip-and-chevron rows and a trust bar).

   The tokens were already his. What was missing was everything
   made OUT of them: the page was a text document with two flat
   rectangles on it, 6,700px tall on a phone.
   ============================================================ */

/* One icon size, inherited colour. Every <use> below is 24x24. */
.ico { width: 1.25rem; height: 1.25rem; flex: none; fill: none;
  stroke: currentColor; stroke-width: 1.7; vertical-align: -.22em; }

/* --- the hero ------------------------------------------------ */
/* The h1 was running to four lines on a phone and pushing the
   buttons under the fold. Smaller ceiling, tighter leading, and
   `balance` so the break lands somewhere a person would choose. */
.land-sec.hero { position: relative; overflow: hidden; padding: 1.3rem 1.05rem 1.15rem; }
/* The root font is 17.5px here, not 16, so every rem below is ~9%
   bigger than it reads. Sized against that, not against a default. */
.landing h1,
.land-sec.hero h1 {
  font-size: 1.22rem; line-height: 1.2;
  margin: 0 0 .45rem; letter-spacing: -.01em;
}
.land-sec.hero .hero-sub { font-size: .84rem; line-height: 1.5; }
.land-sec.hero .hero-eyebrow { font-size: .62rem; letter-spacing: .2em; text-align: left; text-indent: 0; }
/* The artwork sits top-right and the headline wraps to its LEFT rather
   than under it. Measured, not eyeballed: at full width the h1 box ran
   x39-352 straight through the art at x193, so "next" was printed over
   a shield. The 62% cap is what clears it on a 390px phone. */
/* A FLOAT, not a box cap. Capping the headline's width cleared the art
   but cost a line: every line got the narrow measure, including the ones
   that sit below the art with nothing beside them. Floating it lets the
   first lines wrap around it and the rest run full width, which is the
   whole point of the shape. */
.hero-art {
  float: right; shape-outside: inset(0 0 12% 8%); shape-margin: .5rem;
  width: 8.4rem; height: auto; opacity: .55; pointer-events: none;
  margin: -.1rem -.9rem .2rem .55rem;
}
@media (min-width: 26rem) { .hero-art { width: 10.2rem; opacity: .6; } }
/* Flex and grid children do not wrap around a float, they sit under it.
   By this point the float is spent anyway; clearing says so out loud. */
.land-sec.hero .acts { clear: both; }
.hero-cred {
  font-size: .8rem; line-height: 1.45; color: var(--soft);
  border-left: 2px solid var(--cyan); padding: .1rem 0 .1rem .7rem; margin: .65rem 0 0;
}

/* --- the two doors ------------------------------------------- */
/* Full-width rows, not shrink-wrapped buttons: an icon to say what
   it is, the price in the label, a chevron to say it goes somewhere.
   The gradient edge is a border-box layer under a panel background,
   which is how you get a gradient border without a wrapper element. */
.acts { display: flex; flex-direction: column; gap: .55rem; margin-top: 1rem; }
.act {
  display: flex; align-items: center; gap: .7rem;
  padding: .78rem .85rem; border-radius: 14px; text-decoration: none;
  color: var(--ink); font-weight: 600; font-size: .98rem;
  background:
    linear-gradient(var(--panel-2), var(--panel-2)) padding-box,
    linear-gradient(120deg, var(--cyan), var(--magenta)) border-box;
  border: 1px solid transparent;
  min-height: 56px;
}
.act:hover, .act:focus-visible { background:
    linear-gradient(var(--panel), var(--panel)) padding-box,
    linear-gradient(120deg, var(--cyan), var(--magenta)) border-box; }
.act-ico {
  display: grid; place-items: center; width: 2.3rem; height: 2.3rem; flex: none;
  border-radius: 11px; border: 1px solid var(--line); background: var(--bg);
}
.act-c { box-shadow: var(--glow-c); } .act-c .act-ico { color: var(--cyan); }
.act-m { box-shadow: var(--glow-m); } .act-m .act-ico { color: var(--magenta); }
.act-label { flex: 1; }
.act-chev { color: var(--dim); width: 1.1rem; height: 1.1rem; }
/* Eric, 2026-08-25: the soft glow eases in and out over "book a case".
   It moved from .btn to .act with the button; one glow per screen. */
.act.pulse { animation: glow-pulse 3.2s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) { .act.pulse { animation: none; } }

/* --- the trust bar ------------------------------------------- */
/* Four at once on any phone: at 2x2 the eye reads it as two facts
   and a second row it may not bother with. */
.trust {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: .3rem;
  list-style: none; margin: -1rem 0 0; padding: .75rem .4rem;
  background: var(--panel); border: 1px solid var(--line); border-radius: 14px;
}
.trust li {
  display: flex; flex-direction: column; align-items: center; gap: .3rem;
  text-align: center; font-size: .64rem; line-height: 1.25; color: var(--dim);
}
.trust .ico { width: 1.35rem; height: 1.35rem; color: var(--cyan); }
.trust li:nth-child(2) .ico { color: var(--green); }
.trust li:nth-child(3) .ico { color: var(--magenta); }
.trust li:nth-child(4) .ico { color: var(--gold); }

/* --- the numbered steps -------------------------------------- */
/* The rail is what makes three cards read as one sequence. It is
   drawn on the row rather than between them so the last step has
   no tail hanging off the bottom. */
.icon-chip {
  display: grid; place-items: center; width: 2.1rem; height: 2.1rem; flex: none;
  border-radius: 10px; border: 1px solid var(--line);
  background: var(--bg); color: var(--cyan);
}
/* No rail here: .hiw already draws one from the numbered markers, and
   adding a second put two parallel lines down the left of every step. */

/* --- the closing line ---------------------------------------- */
.close-line {
  display: flex; align-items: center; gap: .7rem; text-align: left;
  margin: 0 0 .9rem; padding: .7rem .8rem; font-size: .9rem; line-height: 1.4;
  border-radius: 14px; border: 1px solid var(--line); background: var(--panel-2);
}
.close-ico {
  display: grid; place-items: center; width: 2.2rem; height: 2.2rem; flex: none;
  border-radius: 999px; border: 1px solid var(--magenta); color: var(--magenta);
  background: var(--bg); box-shadow: var(--glow-m);
}
.close-line > span:nth-child(2) { flex: 1; }
.close-arrow { color: var(--cyan); }

/* ---- 24px is the floor for anything a finger has to hit ------------------
   Measured at 390px on the pages the earlier sweep never opened: the US
   residency box that GATES SIGN-IN was 13x20, the subscribe terms box 23x23,
   the wordmark 161x21, and the Google reviews link 77x19. min-, not fixed:
   this only grows the ones that are too small and leaves every control that
   already clears the floor exactly where it is. */
input[type="checkbox"],
input[type="radio"] { min-width: 24px; min-height: 24px; }
.bar .brand,
#google-link-slot a { display: inline-flex; align-items: center; min-height: 44px; }

/* ---- THE ALERT BADGE ------------------------------------------------------
   CHECK-IN DUE, TELEHEALTH and NEEDS RESCHEDULE are the three things on the
   shelf that mean act now, and they were coloured ink on card stock: the
   loudest state in the app rendered as the quietest thing on it. Measured
   before this, CHECK-IN DUE was 1.28:1 against the stock.

   It is a badge now, deliberately NOT made of the folder's own materials,
   because a warning that looks like part of the paper is not a warning.

   The literals are the one allowed exception alongside the manila palette.
   This red is a fixed alert colour and must not follow a scheme: under paper
   it would tint the single thing that has to look identical every time he
   glances at the shelf. Measured 9.82:1, white on #7A271F.

   Appended at the end of the file on purpose. The folder chrome is being
   reworked in another worktree, and a block at the tail of a stylesheet is the
   one place a merge cannot tangle. */
.fld-alert {
  display: inline-flex; align-items: center;
  min-height: 36px; padding: .2rem .55rem;
  border-radius: 8px;
  background: #7A271F; color: #FFFFFF;
  border: 1px solid #52140F;
  font-weight: 800; letter-spacing: .045em;
  box-shadow: 0 4px 12px rgba(80, 20, 15, .28);
}
