/* Nharaunda design system — master prompt §7. Tokens are law; do not invent new hexes. */
@font-face{font-family:"Zilla Slab";src:url("/static/fonts/zilla-slab-600.woff2") format("woff2");font-weight:600;font-display:swap}
@font-face{font-family:"Zilla Slab";src:url("/static/fonts/zilla-slab-700.woff2") format("woff2");font-weight:700;font-display:swap}
@font-face{font-family:"Plex Sans";src:url("/static/fonts/plex-sans-400.woff2") format("woff2");font-weight:400;font-display:swap}
@font-face{font-family:"Plex Sans";src:url("/static/fonts/plex-sans-500.woff2") format("woff2");font-weight:500;font-display:swap}
@font-face{font-family:"Plex Sans";src:url("/static/fonts/plex-sans-600.woff2") format("woff2");font-weight:600;font-display:swap}
@font-face{font-family:"Plex Mono";src:url("/static/fonts/plex-mono-400.woff2") format("woff2");font-weight:400;font-display:swap}
@font-face{font-family:"Plex Mono";src:url("/static/fonts/plex-mono-500.woff2") format("woff2");font-weight:500;font-display:swap}

:root{
  --paper:#EDE6D6; --paper-dim:#E2D9C4; --white:#FAF7EF;
  --ink:#20242B; --ink-soft:#4A4E57;
  --rust:#A6472A; --rust-dark:#7E3620;
  --green:#2F5D50; --green-dark:#204039;
  --clay:#8B7355;
  --amber:#B8862F; --amber-text:#8a6216;
  /* §7 pins this hex as of ADR-009: ink at 14%, a cool hairline — the value the
     established prototypes use. Not clay; see plan amendment A1. */
  --line:rgba(32,36,43,.14);
  --radius:10px;

  /* Beauty pass step 1 (spec 2026-08-31): the elevation scale. Two levels only —
     resting and lifted — cast in ink's own hue so depth reads as paper over
     paper, never grey smoke. Surfaces take these from here; a shadow written
     anywhere else is the selector-scoped fix BUILD-LESSONS warns about. */
  --shadow-rest:0 1px 2px rgba(32,36,43,.05),0 3px 10px rgba(32,36,43,.06);
  --shadow-lift:0 2px 6px rgba(32,36,43,.09),0 12px 28px rgba(32,36,43,.11);

  /* ── The type ladder (ADR-043's renovation, step 1) ──────────────────────
     Outside feedback said the app "looks like old Wikipedia". The cause was
     measured, and it was not the palette (ADR-043 ruled on that): it was that
     the design system barely reached the screen. On 2026-08-27 this file held
     ZERO clamp() steps against the landing page's 13, and the largest type on
     a manager's board was 19px. Zilla Slab was loaded, paid for in bytes, and
     rendered at near body size.

     So the ladder lives here, in tokens, in ONE file — every screen that
     exists or will exist inherits it, which is the whole reason it is not
     expressed as per-screen selectors. That lesson is already written twice
     in BUILD-LESSONS: a fix expressed as a selector is scoped to the markup
     that existed when it was written.

     Fluid by construction: a heading GROWS with the screen instead of being
     redrawn for it, so there is no breakpoint at which a title is wrong.
     Values are the specimen sheet's, not invented here. */
  --step-display:clamp(2rem,5vw,2.9rem);      /* 32 → 46px  Zilla 700 */
  --step-section:clamp(1.35rem,2.6vw,1.7rem); /* 21.6 → 27px Zilla 600 */
  --step-card:1.2rem;                         /* 19px       Zilla 600 */
  --step-body:1rem;                           /* 16px       Plex Sans */
  --step-meta:.72rem;                         /* 11.5px     Plex Mono */
  --step-figure:clamp(1.75rem,4vw,2.4rem);    /* 28 → 38px  Zilla 700, tabular */

  /* Optical correction, not decoration: Zilla Slab's default fit opens up as
     it grows, so the display step tightens more than the section step. Body
     and metadata are never tracked negative — metadata tracks POSITIVE, which
     is the mono-for-metadata signature §7 says to keep. */
  --track-display:-.022em;
  --track-section:-.012em;
  --track-meta:.12em;

  /* ── The spacing rhythm ──────────────────────────────────────────────────
     4/8/12/16/24/32/48. Not a scale anyone must consult a table for — the
     point is that gaps are chosen from seven values instead of typed fresh,
     so vertical rhythm survives contact with a screen nobody has built yet. */
  --sp-1:4px; --sp-2:8px; --sp-3:12px; --sp-4:16px;
  --sp-6:24px; --sp-8:32px; --sp-12:48px;
}
*{box-sizing:border-box}
/* 1.6 rather than 1.5: at the 68ch measure the extra leading is what makes a
   paragraph read as prose instead of as a form field. */
body{margin:0;background:var(--paper);color:var(--ink);
     font-family:"Plex Sans",system-ui,sans-serif;
     font-size:var(--step-body);line-height:1.6}
.mono{font-family:"Plex Mono",ui-monospace,monospace}

/* The ladder applied. h1 and h2 carried NO size in this file until now — they
   fell through to the browser's 2em/1.5em, which is exactly why 59 headings
   across the templates cap themselves inline to look deliberate (42 at 19px,
   17 at 17px — count them and the audit's number is the same one). Removing
   those caps is step 2's job; until they are gone an inline size still wins,
   so this change is additive and moves only the screens that never capped.
   The staging is deliberate: the scale lands first and proves itself on the
   uncapped screens, the caps come off second. */
/* `.display` is the FACE, not the step — and it must stay that way. It is
   carried by 50 h1s, 42 h2s, 30 spans and 7 paragraphs, which is to say it
   means "set this in Zilla Slab", never "set this at 46px". Hanging the
   display step on the class instead of the element was tried here first and
   was wrong twice over: a class beats an element on specificity, so every
   `<h2 class="display">` section heading in the product would have jumped to
   the display step the moment step 2 removed its inline cap — and every
   `<span class="display">` inside a sentence with it. The steps go on the
   ELEMENTS; the class keeps its one job. */
h1,h2,.display{font-family:"Zilla Slab",Georgia,serif;font-weight:600;margin:0}
/* `h1.display` and not bare `h1`: the SAME specificity trap, caught a second
   time by measuring rather than reading. With the weight on `h1` alone the
   shared `.display` rule above (a class, 0-1-0) outranked it (0-0-1) and every
   page title rendered at 600 while the specimen sheet says 700 — the ladder
   was landing its size and quietly losing its weight. Measure the computed
   style; do not trust the cascade you intended. */
h1,h1.display,.t-display{font-size:var(--step-display);font-weight:700;
  line-height:1.05;letter-spacing:var(--track-display);text-wrap:balance}
h2,.t-section{font-size:var(--step-section);line-height:1.15;
  letter-spacing:var(--track-section);text-wrap:balance}
.t-display,.t-section{font-family:"Zilla Slab",Georgia,serif}
/* A card title is a fixed step, not a fluid one: cards sit in a column whose
   width barely changes, so growing their titles with the VIEWPORT would size
   them against a measurement they do not occupy. Deliberately NOT hung on h3
   either — every h3 in these templates is a 14px Plex Sans sub-label, so
   sizing the element would have re-faced them. Opt-in by class, which is what
   `.mark` already was; it now reads its 19px from the token rather than
   repeating it. */
.t-card,.mark{font-family:"Zilla Slab",Georgia,serif;font-weight:600;
  font-size:var(--step-card);line-height:1.3}
/* The metadata register, available to any screen that wants it — mono, small,
   tracked open, uppercase. §7 calls this a signature; a token makes it one. */
.t-meta{font-family:"Plex Mono",ui-monospace,monospace;font-weight:500;
  font-size:var(--step-meta);line-height:1.5;
  letter-spacing:var(--track-meta);text-transform:uppercase}

.shell{max-width:680px;margin:0 auto;padding:0 18px 96px}
/* The 68ch reading measure, held at CLASS level — every paragraph the shell renders,
   on every screen that exists or will exist. It lived in per-screen selectors twice
   (.row-desc after the Gate 4 pass, two list ids after the 6b pass) and both times the
   next screens shipped without it: a fix expressed as a selector is scoped to the
   markup that existed when it was written (BUILD-LESSONS, 2026-08-16 coda). Form
   controls are unaffected — they are not paragraphs. */
.shell p{max-width:68ch}

/* The ledger leaf (design pass, 2026-08-16) — the landing page's ruled-gutter device,
   inherited by the app so marketing and product carry one design language. A sectioned
   screen wraps each section in .entry: the heading (.mark) folds inline on a phone —
   IDENTICAL to the pre-pass h2, so the drilled 390 layout does not move — and from
   960px it becomes a mono eyebrow in a ruled right-aligned gutter, content set beside
   it at the reading measure. Structure as information: these labels are the register's
   section names, not decoration. */
.mark{margin:var(--sp-4) 0 var(--sp-2)}
.mark span{display:block}
@media (max-width:959.98px){.entry + .entry .mark{margin-top:28px}}
@media (min-width:960px){
  .entry{display:grid;grid-template-columns:148px minmax(0,68ch);gap:0 34px;
    margin-top:30px}
  .entry .mark{font-family:"Plex Mono",ui-monospace,monospace;font-weight:500;
    font-size:11.5px;letter-spacing:.13em;text-transform:uppercase;
    color:var(--ink-soft);text-align:right;border-right:1px solid var(--line);
    padding:8px 34px 0 0;margin:0}
  .entry .mark span{position:sticky;top:18px}
  .entry .body{min-width:0}
  /* A full-width bar is a phone affordance; beside the gutter it reads as a banner.
     48px height holds; width returns to the content. */
  .entry .btn{display:inline-flex;align-items:center;justify-content:center;
    width:max-content;min-width:240px;padding:0 32px}
}
.appbar{display:flex;justify-content:space-between;align-items:center;
        padding:20px 18px 12px;border-bottom:1px solid var(--line);background:var(--white)}
/* Weight is explicit: only 600 and 700 are subsetted, so leaving it to inherit 400 asks
   for a face that was never loaded. The wordmark is 700. */
.wordmark{font-family:"Zilla Slab",Georgia,serif;font-weight:700;font-size:19px;
  /* An anchor since the site-navigation spec (2026-08-16) — dressed as the mark,
     not as a link: the ink stays ink, no underline. The href is the affordance. */
  color:var(--ink);text-decoration:none}
.wordmark span{color:var(--rust)}
.estate-pill{font-family:"Plex Mono",monospace;font-size:11px;color:var(--green-dark);
  background:rgba(47,93,80,.14);border:1px solid var(--green);padding:3px 8px;
  border-radius:10px;white-space:nowrap}

.card{position:relative;background:var(--white);border:1px solid var(--line);
      border-radius:var(--radius);padding:14px;margin:10px 0;
      border-left-width:4px;border-left-style:solid}
.card.s-open{border-left-color:var(--rust)}
.card.s-assigned{border-left-color:var(--amber)}
.card.s-resolved{border-left-color:var(--green)}
/* Keeps a long category title clear of the stamp above it (ADR-009). */
.card-head{padding-right:112px}

/* Signature (master §7, positioned by ADR-009): a physical register stamp pressed onto
   the card, never a pill. It is absolutely positioned on purpose — the −6° only reads as
   a stamp when it floats free over the surface. Do not put it back in a flex row. */
/* The stamp, at confidence (renovation step 4). Everything §7 pins is unchanged:
   absolutely positioned over the card's top-right corner, −6°, mono uppercase,
   bordered, floating free rather than sitting in a flex row. What changes is scale
   and inset — 11px jammed 10/12px into the corner read as a BADGE, which is the one
   thing ADR-009 says it must not be. A stamp pressed onto paper sits in from the
   edge with clear paper around it; that clearance is most of what makes it read as
   pressed rather than as a chip stuck on.

   12.5px is deliberately a step ABOVE the metadata token: the stamp is §7's
   signature element, and the same section says it should not also be the least
   legible thing on the screen. Tracking moves to the metadata track (.12em) so it
   speaks the same mono register as the rest of the ladder.

   The card-head clearances below were re-measured against the grown stamp, not
   guessed — a title running under the stamp is exactly the breakage ADR-009's
   padding rule exists to prevent. */
.stamp{position:absolute;top:14px;right:16px;transform:rotate(-6deg);
       border:1.5px solid currentColor;border-radius:3px;padding:3px 10px;
       font-family:"Plex Mono",monospace;font-size:12.5px;font-weight:500;
       text-transform:uppercase;letter-spacing:.12em}
.stamp.s-open{color:var(--rust)}
.stamp.s-assigned{color:var(--amber-text)}
.stamp.s-resolved{color:var(--green)}

/* One button recipe for BOTH elements it is worn by. `display:block` centred a
   <button>'s label for free (buttons centre their own content) and left a
   <a class="btn"> with its text jammed top-left, still underlined — which is
   exactly how "Make a payment" shipped on the resident desk. Eight templates had
   already patched around it with an inline `display:flex;align-items:center;…`,
   one per session that hit it; the ninth and tenth anchors were simply missed.
   Flexbox centring belongs here, once, so an anchor and a button are the same
   object and no future caller has to know this. */
.btn{display:inline-flex;align-items:center;justify-content:center;text-align:center;
     text-decoration:none;
     width:100%;min-height:48px;border:none;border-radius:999px;
     font-family:"Plex Sans",sans-serif;font-size:16px;font-weight:600;cursor:pointer}
.btn-primary{background:var(--rust);color:var(--white)}
.btn-primary:hover{background:var(--rust-dark)}
.btn-secondary{background:var(--green);color:var(--white)}
.btn-secondary:hover{background:var(--green-dark)}
.btn:disabled{background:var(--paper-dim);color:var(--ink-soft);cursor:not-allowed}

label{display:block;font-size:13px;font-weight:600;margin:14px 0 4px}
input,select,textarea{width:100%;min-height:48px;padding:10px 12px;background:var(--white);
  border:1px solid var(--clay);border-radius:8px;font-family:inherit;font-size:16px;color:var(--ink)}
/* One field recipe (design pass, 2026-08-16): the browser's default select chrome was
   the last stock widget on a hand-set page. appearance:none + a chevron drawn from two
   gradient squares — no images, no data: URIs, so the CSP and A2.7 stay untouched. */
select{appearance:none;-webkit-appearance:none;padding-right:40px;
  background-image:linear-gradient(45deg,transparent 49%,var(--ink-soft) 51%),
    linear-gradient(135deg,var(--ink-soft) 49%,transparent 51%);
  background-position:right 20px top 55%,right 14px top 55%;
  background-size:6px 6px,6px 6px;background-repeat:no-repeat}
textarea{min-height:110px;resize:vertical}
.hint{font-size:13px;color:var(--ink-soft);margin-top:4px}
.field-error{font-size:13px;color:var(--rust);font-weight:600;margin-top:4px}
.counter{font-size:12px;color:var(--ink-soft);text-align:right}

.empty{background:var(--white);border:1px dashed var(--line);border-radius:var(--radius);
       padding:22px;text-align:center;color:var(--ink-soft)}
.example-band{background:var(--amber);color:var(--ink);font-family:"Plex Mono",monospace;
  font-size:12px;font-weight:500;text-transform:uppercase;letter-spacing:.1em;
  text-align:center;padding:6px}

/* The base link, so an unclassed <a> can never fall back to browser blue
   (recomposition F4, still shipping on the login hint, the fund/election rows
   and every "Back to your desk" as of the 2026-08-31 walk). Lowest possible
   specificity: every classed link treatment in this file wins over it, so the
   rule only reaches the anchors nothing else styled. */
a{color:var(--rust-dark);text-underline-offset:3px}

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

/* The 44px touch floor for inline door links (walk 2026-08-18: the invite door
   measured 17px tall at 375px). Padding grows the finger's target; the negative
   margins give the space back, so the visual line does not move. */
.touch-door{display:inline-block;padding:14px 8px;margin:-14px -8px}
@media (prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}}

/* --- Rate prompt (master §3.1): inline on the card, never a modal --------- */
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);white-space:nowrap;border:0}
.rate{margin-top:12px;padding-top:12px;border-top:1px dashed var(--line)}
.stars-input{border:0;padding:0;margin:0;display:flex;gap:6px}
/* The radio itself stays in the accessibility tree and keyboard order — only its
   default rendering is replaced, so this is a restyle, not a hidden control. */
.stars-input input{position:absolute;opacity:0;width:1px;height:1px}
.stars-input label{margin:0;flex:1}
.stars-input span{display:flex;align-items:center;justify-content:center;min-height:44px;
  border:1px solid var(--clay);border-radius:8px;font-family:"Plex Mono",monospace;
  font-size:15px;cursor:pointer;background:var(--white)}
.stars-input input:checked + span{background:var(--green);color:var(--white);
  border-color:var(--green)}
.stars-input input:focus-visible + span{outline:3px solid var(--rust);outline-offset:2px}
.rated{display:flex;align-items:center;gap:8px;margin:10px 0 0;font-size:13px;
  color:var(--ink-soft)}
.rated .stars{color:var(--rust);font-size:15px;letter-spacing:1px}
.rated-comment{margin:4px 0 0;font-size:13px;font-style:italic;color:var(--ink-soft);
  border-left:2px solid var(--line);padding-left:8px}

/* --- Manager board ---------------------------------------------------------
   A professional surface. The resident's card is built for reading one ticket;
   these rows are built for scanning twenty, so the type and spacing tighten and
   the stamp scales down. Same tokens, different density — never a second palette. */
.card.row{padding:11px 13px;margin:8px 0}
/* 11px, not 10: the stamp is the signature element (§7), and 10px mono uppercase was
   the smallest type in the product — the one element the product is remembered by
   should not also be the least legible thing on the screen. Density survives: the
   row stamp stays a step under the card stamp's 11px/2px-8px by padding alone. */
/* The board row keeps its density: the manager scans twenty of these, so the row
   stamp stays one step under the card stamp — by scale and padding, never by
   dropping below the 11px floor the 2026-08-16 pass set for the signature. */
.card.row .stamp{top:11px;right:13px;font-size:11.5px;padding:2px 8px}
.card.row .card-head{padding-right:100px}
.row-id{font-size:11px;color:var(--ink-soft);margin:0}
.row-title{font-size:15px;margin:1px 0 0}
.row-desc{font-size:14px;margin:4px 0 0}
.row-provider{font-size:12px;color:var(--green-dark);margin:5px 0 0}
/* Age is the manager's signal, so a stale one is marked in text weight and colour,
   never colour alone. */
.age-stale{color:var(--rust);font-weight:500}
.row-actions{display:flex;gap:8px;margin-top:9px;align-items:center}
/* min-width:0 is load-bearing. A flex item defaults to min-width:auto, so the select
   refuses to shrink below its longest option ("EXAMPLE Boreholes · borehole drilling")
   and pushes "Mark resolved" off the right edge — 450px of content in a 390px viewport,
   which is a horizontal scrollbar on the screen master §7 designs for first. Found in a
   browser; no server-rendered test can see a layout overflow. */
.row-actions .assign{min-height:44px;font-size:14px;flex:1 1 auto;width:auto;margin:0;
  min-width:0}
.row-actions .resolve{min-height:44px;font-size:14px;width:auto;flex:0 0 auto;padding:0 14px}
.gate-note{margin-top:6px;font-size:12px}

/* The card foot (renovation step 3). A hairline and one derived fact, set in the
   mono metadata register — the card gets a bottom edge that is a RULE rather than
   just where the padding stops, and the fact sitting on it is the one the manager
   is actually triaging by.

   "Waiting N days" and not the bare "N days" already in the identity line: the
   identity line answers "which ticket is this", and age there is one field among
   four. The foot answers "why is this still on my board", which is a different
   question and deserves the sentence rather than the number. Both read the SAME
   `age_days` — one derivation, two places, never two clocks (the ADR-023 ledger-day
   rule the identity line already follows).

   No new query: `age_days` is already on every row projection.

   Rust at the stale threshold, and never colour alone — the word "still" carries
   the same claim the colour does, which is the §7 triple-encoding rule applied to
   a fact rather than to a status. */
.card-foot{display:flex;flex-wrap:wrap;gap:0 14px;align-items:center;
  justify-content:space-between;
  margin:10px -13px -11px;padding:0 13px;border-top:1px solid var(--line);
  color:var(--ink-soft)}
/* The trail form loses its own top margin inside the foot: the rule now provides
   the separation the margin used to, and both would double it. */
.card-foot .trail-toggle-form{margin:0}
.card-foot .waiting{font-family:"Plex Mono",ui-monospace,monospace;font-weight:500;
  font-size:var(--step-meta);letter-spacing:var(--track-meta);text-transform:uppercase}
.card-foot .waiting.is-stale{color:var(--rust)}

.needs{margin:16px 0 4px;padding:12px 13px;background:var(--paper-dim);
  border:1px solid var(--line);border-left:4px solid var(--rust);border-radius:var(--radius)}
.needs-title{font-size:12px;text-transform:uppercase;letter-spacing:.08em;margin:0 0 2px;
  font-family:"Plex Mono",monospace;font-weight:500;color:var(--rust)}
.needs-count{display:inline-block;background:var(--rust);color:var(--white);
  border-radius:999px;padding:0 7px;margin-left:4px;font-size:11px}
.needs .card.row{background:var(--white)}

.counters{display:flex;gap:8px}
/* min-height is stated, not left to padding arithmetic. At 390px these cleared 44px
   by accident of the stacked number + label; the >=820px override below turns them
   into a single baseline row, which collapsed them to 41px. A counter here is also the
   filter control, so it is a thumb target on both breakpoints. */
.stat{flex:1;display:block;text-align:center;text-decoration:none;background:var(--white);
  border:1px solid var(--line);border-radius:8px;padding:9px 4px;color:var(--ink);
  min-height:44px;box-sizing:border-box}
.stat:hover{border-color:var(--clay)}
.stat.is-active{border-color:currentColor;border-width:2px;padding:8px 3px}
/* A count is a FIGURE, not a label with a number in it (renovation step 3). At 21px
   it sat one step above the body text and read as prose; the board's job is to be
   scanned, and a scan lands on figures. `tabular-nums` is the load-bearing half: the
   three counters sit in a column in the rail, and proportional digits make 4 / 11 /
   128 start at three different places, so the eye re-finds the column on every row.
   Tabular locks the advance width and the numbers line up as a ledger column does.

   Not the full display step, and that is measured rather than conceded: at 390px the
   three counters share the width three ways (~118px each), and a 46px numeral beside
   a 10px label in a 118px tile is a figure wearing the tile, not filling it. This
   step tops out at 38px, which is display's floor — the board's largest type is
   still its title. */
.stat-num{display:block;font-family:"Zilla Slab",Georgia,serif;font-weight:700;
  font-size:var(--step-figure);line-height:1;font-variant-numeric:tabular-nums;
  font-feature-settings:"tnum" 1}
.stat-label{display:block;font-size:10px;text-transform:uppercase;letter-spacing:.06em;
  color:var(--ink-soft);margin-top:2px}
.stat.s-open{color:var(--rust)} .stat.s-assigned{color:var(--amber-text)}
.stat.s-resolved{color:var(--green)}
.stat.s-open .stat-num,.stat.s-assigned .stat-num,.stat.s-resolved .stat-num{color:inherit}
.clear-filter{display:inline-block;margin-top:8px;font-size:12px;color:var(--ink-soft)}
.month-link{display:inline-flex;align-items:center;min-height:44px;margin-top:4px;
  font-size:12px;color:var(--green-dark);text-transform:uppercase;letter-spacing:.04em;
  text-decoration:none}
.month-link:hover{color:var(--ink)}
.board-heading{font-family:"Zilla Slab",Georgia,serif;font-size:17px;margin:16px 0 2px}

/* Desktop is a first-class layout here, not a scale-up: the board is the view that
   benefits most from width (master §7). Stats and filters left, list right.
   The wide shell is OPT-IN (.shell-wide) — the 2026-08-16 design pass found the
   global 1100px override inflating every single-column screen: a form stretched to
   1100px reads as a scale-up, which is the exact thing this block says desktop is
   not. Screens that recompose for width declare it; the rest keep the 680 measure. */
@media (min-width:820px){
  .shell-wide{max-width:1100px}
  .board-split{display:grid;grid-template-columns:240px 1fr;gap:24px;align-items:start}
  .board-rail{position:sticky;top:16px}
  .counters{flex-direction:column}
  .stat{display:flex;align-items:baseline;gap:8px;text-align:left;padding:11px 12px}
  .stat.is-active{padding:9px 11px}
  /* The rail is 240px and the counter is a baseline row there, so the figure has
     room to be one — it no longer shrinks below its phone size, which is what the
     old 19px override did. */
  .stat-num{font-size:var(--step-figure)}
  .stat-label{margin-top:0}
  .board-heading{margin-top:0}
  /* minmax(0,1fr), not 1fr: bare 1fr means minmax(auto,1fr), so a card's
     min-content width pins the track and two of them overflow the shell from
     ~1000px (walk 2026-08-31 — scrollWidth 1144 in a 1085 viewport). Zero
     floors the track so the pair always shares the container. */
  .needs{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:0 14px}
  .needs-title,.needs + .hint,.needs > .hint,.needs > .empty{grid-column:1 / -1}
}

/* --- Offline banner (master §4: a first-class state, never an error) -------
   Persistent and non-modal — it does not block reading what is already on screen,
   and it self-dismisses on recovery. Rust because it is a real constraint on what
   the resident can do, not a neutral notice. */
.offline-banner{position:sticky;top:0;z-index:20;background:var(--rust);color:var(--white);
  font-size:13px;line-height:1.4;text-align:center;padding:8px 14px}
.offline-banner[hidden]{display:none}
.is-offline .btn-primary,.is-offline .btn-secondary{opacity:.55}
/* A marker that survives on the item itself, for an action that failed after the
   user moved on (master §4's placement table). */
.send-failed{border-left-color:var(--rust)!important}
.send-failed-note{margin-top:8px;font-size:13px;color:var(--rust);font-weight:500}

/* Provider stopgap line — informational, not an error. Recessed rather than loud:
   it explains a gap, it is not something the provider must act on. */
.stopgap{background:var(--paper-dim);border:1px dashed var(--line);border-radius:var(--radius);
  padding:12px 14px;margin:0 0 14px;font-size:14px;color:var(--ink-soft)}
.contact{margin:8px 0 0;font-size:13px}
.contact a{color:var(--green-dark)}
/* Tap-to-call — the provider's payload, promoted from a 17px-tall inline text link
   (measured, 2026-08-16 design pass) to the most tappable thing on their card. 48px:
   the field-standard floor this pass adopts for provider-facing actions (§7, amended
   the same day) — a gloved thumb in a yard is the actual input device. Green because
   calling is the doing half of the secondary palette, and rust stays reserved. */
.btn-call{display:flex;align-items:center;justify-content:center;gap:8px;min-height:48px;
  margin-top:8px;background:var(--green);color:var(--white);border-radius:999px;
  font-weight:600;font-size:16px;text-decoration:none}
.btn-call:hover{background:var(--green-dark)}
.btn-call .mono{font-size:15px}
/* The quiet member of the button family: cancels and not-yets. Visible as a control
   (micro-states: a default state must read as pressable) without competing with the
   action it stands beside. */
.btn-quiet{background:var(--white);color:var(--ink);border:1px solid var(--clay)}
.btn-quiet:hover{background:var(--paper-dim)}

/* Skip link — visually hidden until focused, then unmissable. Present for keyboard
   users only, and it must never be display:none, which removes it from tab order and
   defeats the entire point. */
.skip-link{position:absolute;left:-9999px;top:0;z-index:100;background:var(--ink);
  color:var(--white);padding:12px 16px;min-height:44px;display:flex;align-items:center;border-radius:0 0 8px 0;font-size:14px}
.skip-link:focus{left:0}
/* The skip target must not draw a focus ring of its own — it is a scroll destination,
   not a control. */
main:focus{outline:none}

/* Bottom dock (master §3, §7) — built now, rendered from 6b. Fixed to the bottom with
   safe-area padding: that is where a thumb rests and where the home indicator sits.
   Active state is dot + colour + aria-current, never colour alone. */
.dock{position:fixed;left:0;right:0;bottom:0;z-index:30;display:flex;
  justify-content:space-around;align-items:center;background:var(--white);
  border-top:1px solid var(--line);
  padding:6px 6px calc(env(safe-area-inset-bottom, 8px) + 6px)}
.dock-item{display:flex;flex-direction:column;align-items:center;gap:3px;
  min-height:44px;min-width:56px;justify-content:center;text-decoration:none;
  color:var(--ink-soft);border-radius:10px;padding:4px 10px}
/* 12px, not 11: the dock is read at arm's length while walking (the manager in the
   corridor, the provider in a yard), and with no icons the label is the whole sign. */
.dock-label{font-size:12px;text-transform:uppercase;letter-spacing:.04em}
.dock-dot{width:4px;height:4px;border-radius:50%;background:currentColor;opacity:0}
.dock-item.is-active{color:var(--rust)}
.dock-item.is-active .dock-dot{opacity:1}
/* The dock's items, top placement (§7 amended 2026-08-16, extending the labels-only
   ruling): above 820px the bottom edge is not thumb territory and a fixed dock is
   furniture in the wrong room. The SAME dock_items render in the appbar — mono
   labels, no icons — with active carried by rust + underline: the dot is a stacked-
   dock affordance and the underline is its horizontal equivalent. Exactly one
   rendering is displayed per breakpoint; display:none keeps the other out of the
   accessibility tree and the tab order. */
/* Sign out joins the nav's mono-caps register (design pass, 2026-08-16) — it sat in
   six templates as an inline-styled one-off, lowercase beside an uppercase nav. */
.signout{display:inline-flex;align-items:center;min-height:44px;padding:0 4px;
  background:none;border:none;cursor:pointer;font-size:12px;
  text-transform:uppercase;letter-spacing:.04em;color:var(--ink-soft)}
.signout:hover{color:var(--ink)}

/* ONE navigation, in ONE place, at EVERY width (owner-ruled 2026-08-28: "the
   navigation must remain consistent and footer must always have nav").

   This block used to swap the dock for a horizontal top nav above 820px — §7's
   2026-08-16 amendment, reasoned as "the bottom edge is not thumb territory" at
   desktop. True about thumbs, and it cost more than it bought: navigation that
   MOVES when the window is resized is navigation a person has to find twice, and
   a wide screen's footer carried nothing at all. Found by looking at the bottom
   of the complaints surface, where the page simply ended.

   So the dock is now the only rendering and it is always present. The top nav is
   gone rather than kept alongside — two navs at one width is not "consistent",
   it is the same list in two places waiting to disagree. `.topnav` keeps no
   styles here at all; the markup went with it.

   NOTE FOR THE SIDEBAR SLICE (ADR-045 part 3): that ruling said the same items
   become a persistent left SIDEBAR at width. This ruling is later and narrower,
   and it wins where they meet — whatever the sidebar becomes, the footer keeps
   its nav, or a person loses the one place they have learned to look. */
@media (min-width:820px){
  /* The dock stays. Wider screens get it wider, not absent — the items spread
     across the available room instead of crowding the centre of a 1440px page. */
  .dock{justify-content:center;gap:clamp(8px,4vw,72px)}
}

/* The public actions (2026-08-17 night, the pricing rework's review): the landing
   pinned four actions under the thumb and every page they led to dropped them.
   These are the landing's .mdock and masthead actions bar, carried into base.html
   for every signed-out page — .pdock fixed at the thumb under 900px (the landing
   dock's own breakpoint, not the app dock's 820, so the two surfaces switch
   together), .pnav folded into the appbar above it. One PUBLIC_ACTIONS list feeds
   both renderings; display:none keeps the inactive one out of the tab order and
   the accessibility tree, so one "Actions" landmark is live per breakpoint. The
   last item is the door and reads filled, the landing's .mdock-app. The .shell's
   own 96px bottom padding is what keeps the fixed dock off the footer's © line. */
.pnav{display:none}
.pdock{position:fixed;left:0;right:0;bottom:0;z-index:30;display:flex;
  align-items:stretch;background:var(--white);border-top:1px solid var(--line);
  padding:0 0 env(safe-area-inset-bottom, 0px)}
.pdock a{flex:1;display:flex;align-items:center;justify-content:center;
  text-align:center;min-height:54px;padding:6px 2px;
  font-family:"Plex Mono",ui-monospace,monospace;font-size:10.5px;
  text-transform:uppercase;letter-spacing:.03em;line-height:1.3;
  color:var(--ink-soft);text-decoration:none}
.pdock a.is-active{color:var(--rust);font-weight:600}
.pdock a:last-child{background:var(--rust);color:var(--white)}
@media (min-width:900px){
  .pdock{display:none}
  .pnav{display:flex;gap:2px;margin-left:auto;align-items:center}
  .pnav a{display:flex;align-items:center;min-height:44px;padding:0 11px;
    font-family:"Plex Mono",ui-monospace,monospace;font-size:12px;
    text-transform:uppercase;letter-spacing:.04em;color:var(--ink-soft);
    text-decoration:none;border-bottom:2px solid transparent}
  .pnav a:hover{color:var(--rust-dark)}
  .pnav a.is-active{color:var(--rust);border-bottom-color:var(--rust)}
  .pnav a:last-child{color:var(--rust-dark);font-weight:600}
}

/* In-flight card treatment — the loading state for card-level actions (master §4).
   The progress ladder relabels a button, but assign is select-and-done and has no
   button to relabel: without this, changing the dropdown on a slow connection shows
   nothing at all until the row swaps, and the manager taps again. htmx puts
   .htmx-request on the element that issued the request, so target the enclosing card. */
/* The month view (ledger-visible pass): the owner's ten-second read. Numbers first —
   Zilla, big — labels mono beneath, the counters' own vocabulary at poster size. */
/* Wraps since the latency tiles (2026-08-18): three per row at 375px, one row of
   six at desk width — a fixed basis, so two tiles and six read as one family. */
.month-grid{display:flex;gap:8px;flex-wrap:wrap}
.month-stat{flex:1 1 100px;background:var(--white);border:1px solid var(--line);border-radius:8px;
  padding:20px 10px 14px;text-align:center}
.month-num{display:block;font-size:40px;font-weight:700;line-height:1}
/* The label sits under its number the way a ledger total sits over its base rule —
   the hairline is the tile's one piece of craft (design pass, 2026-08-16). */
.month-label{display:block;font-size:11px;text-transform:uppercase;letter-spacing:.06em;
  color:var(--ink-soft);margin:10px auto 0;padding-top:8px;
  border-top:1px solid var(--line);max-width:150px}

/* The trail (ledger-visible pass, 2026-08-16): the append-only spine rendered inside
   the card — recessed like the register page it is, one mono line per event. The
   toggle is a text control with a full 44px hit area; the note is the immutability
   sentence, quiet because it is a fact and not a feature. */
.trail-toggle-form{margin:10px 0 0}
.trail-toggle{display:inline-flex;align-items:center;min-height:44px;padding:0 8px 0 0;
  background:none;border:none;cursor:pointer;font-size:12px;color:var(--ink-soft);
  text-transform:uppercase;letter-spacing:.04em}
.trail-toggle:hover{color:var(--ink)}
.trail{margin-top:8px;padding:10px 12px;background:var(--paper-dim);
  border:1px dashed var(--line);border-radius:8px}
.trail-line{margin:0 0 4px;font-size:12px;color:var(--ink)}
.trail-when{color:var(--ink-soft)}
.trail-note{margin:8px 0 0;font-size:13px;font-style:italic;color:var(--green-dark)}
/* The silence-killer line on a resident's card — prose, not mono: it is a sentence
   about a person's situation, not a datum. */
.state-line{margin-top:4px}

/* The receipt moment (design pass, 2026-08-16): a just-filed ticket's stamp presses
   onto the card — the register motif enacted at the instant of creation, which is the
   product's promise ("filed once, logged, and someone owns it") made physical. Scoped
   to #ticket-list (the resident's own list): the board must never become a drumroll
   of eight stamps. Fires once per inserted card (htmx afterbegin inserts only the new
   node) and once for the newest card on a full load — a single beat, deliberately.
   Reduced motion: the global animation kill leaves the stamp simply present, which is
   the correct static end state. */
@keyframes nh-stamp-press{0%{transform:rotate(-6deg) scale(1.55);opacity:0}
  65%{transform:rotate(-6deg) scale(.96);opacity:1}100%{transform:rotate(-6deg) scale(1)}}
#ticket-list .card:first-child .stamp{animation:nh-stamp-press .28s ease-out}
/* The estate's birth certificate (ADR-017): the same press, once, on the setup
   receipt. One ceremony moment per act — the provider pool's Vouched stamps do
   NOT animate, deliberately (the ceremony there is the vouch screen itself). */
.setup-receipt .stamp{animation:nh-stamp-press .28s ease-out}

/* Progress as place (ADR-017 setup steps): mono, quiet, a location not a meter. */
.setup-place{font-size:12px;text-transform:uppercase;letter-spacing:.06em;
  color:var(--ink-soft);margin:0 0 10px}

/* --- Operator console v2 (ADR-019): the company, thinking ------------------
   Charts are inline SVG from svg_charts.py — §7 tokens via CSS variables, width
   from the stylesheet (viewBox scales), never a fixed pixel box. KPI deltas ride
   inside the month-stat tile; direction is text + arrow, never colour alone. */
.kpi-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:8px}
.kpi-delta{display:block;font-size:11px;letter-spacing:.04em;color:var(--ink-soft);
  margin-top:6px}
.kpi-up{color:var(--green)}
.kpi-down{color:var(--rust)}
.chart{display:block;width:100%;height:auto;background:var(--white);
  border:1px solid var(--line);border-radius:var(--radius);max-width:760px}
.chart-tall{max-width:760px}
.ch-tick{font-family:"Plex Mono",monospace;font-size:10px;fill:var(--ink-soft)}
.ch-val{font-family:"Zilla Slab",Georgia,serif;font-weight:700;font-size:15px;fill:var(--ink)}
.ch-val-s{font-family:"Plex Mono",monospace;font-size:11px;fill:var(--ink-soft)}
.ch-route{font-family:"Plex Mono",monospace;font-size:11px;fill:var(--ink)}
.chart-legend .lg{display:inline-block;width:10px;height:10px;border-radius:2px;
  vertical-align:-1px}
.lg-resident{background:var(--rust)} .lg-manager{background:var(--green)}
.lg-provider{background:var(--amber)} .lg-owner{background:var(--clay)}
/* Findings: sentences first, chrome second. An "act" finding carries the rust
   rule — same triple-encoding rule as status (text says why, colour agrees). */
.insight,.action-row{margin:0 0 6px;font-size:14.5px;max-width:68ch;
  border-left:3px solid var(--line);padding-left:12px}
.insight-act{border-left-color:var(--rust)}
.insight a,.action-row a{color:inherit;text-decoration-color:var(--clay);
  text-underline-offset:3px}
/* The projection chip (§4): fabricated history states itself on the screen that
   renders it — amber like the EXAMPLE band, because it is the same disclosure. */
.projection-chip{display:inline-block;background:var(--amber);color:var(--ink);
  font-size:11px;font-weight:500;text-transform:uppercase;letter-spacing:.08em;
  padding:4px 10px;border-radius:4px;margin:0 0 14px}

@keyframes nh-pulse{0%,100%{opacity:1}50%{opacity:.55}}
.card:has(.htmx-request),.card.htmx-request{animation:nh-pulse 1.2s ease-in-out infinite}
@media (prefers-reduced-motion:reduce){
  /* Static, not absent. Removing the animation must not remove the feedback. */
  .card:has(.htmx-request),.card.htmx-request{animation:none;opacity:.62}
}

/* --- The two attachment faces (0014, wide build ruling) -----------------------
   Work photos on a ticket, and the estate's inspection reports. Both are evidence,
   so both are quiet: the picture and the filename are the content, and the chrome
   around them earns nothing. */

/* The evidence strip. A row that scrolls sideways rather than a grid that reflows:
   at 390px two thumbs fit, and a third that wraps onto its own line reads as a
   second group of photos rather than the rest of the same one. */
.evidence {
  list-style: none;
  display: flex;
  gap: 8px;
  overflow-x: auto;
  margin: 10px 0 0;
  padding: 0;
}
/* 64px, not 44: the floor is what a thumb must be able to hit, and a photo you are
   meant to recognise before tapping has to be big enough to recognise. */
.evidence-thumb {
  display: block;
  min-height: 64px;
  width: 64px;
  height: 64px;
  border: 1px solid var(--line);
  border-radius: 8px;
  overflow: hidden;
  background: var(--paper-dim);
}
.evidence-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }

.photo-form { margin: 10px 0 0; }
.photo-form .hint { margin: 6px 0 0; }
/* §7's one form-control recipe, applied to the file picker: --clay border, 8px
   radius. The button the browser draws inside it cannot be styled across engines,
   so the box around it carries the design instead of fighting for the box inside. */
.photo-file, .report-file {
  display: block;
  width: 100%;
  min-height: 44px;
  padding: 10px;
  border: 1px solid var(--clay);
  border-radius: 8px;
  background: var(--white);
  font: inherit;
  font-size: 16px; /* under 16px iOS zooms the viewport on focus (ADR-009) */
}
/* Provider-facing: §7's 48px floor for outdoor, gloved, one-handed use — this is the
   control a provider presses standing in someone's yard with the job just finished. */
.photo-add { min-height: 48px; }

/* The folded photo control (design pass, 2026-08-27). `.quiet-details` supplies the
   recipe; this only restores §7's 48px outdoor floor for the summary, because for the
   assigned provider this fold IS the button — pressed one-handed, in a yard, gloved —
   and `.quiet-details` was written for a manager at a desk (44px).

   Declared AFTER `.quiet-details>summary` (further down this file) would be the tidy
   place for it, but this block belongs with the photo control it governs — so the
   floor is pinned with equal specificity plus one class to outrank the 44px rule
   regardless of source order. Measured, not assumed: the first version of this line
   sat here at equal specificity and lost, and the summary rendered 44px. */
.photo-fold.quiet-details>summary{min-height:48px}

.reports { margin: 8px 0 4px; }
.report-compose { margin: 0 0 10px; }
/* Capped to the reading measure, the §7 prose rule applied to a row that reads like
   prose: at 1280 the list ran the full 1064px column, which put a filename and its
   date a metre apart with nothing between them to carry the eye across. Caught in the
   2026-08-17 walk. The compose form above deliberately keeps the neighbouring notices
   panel's width — matching the screen it sits on beats matching my own preference. */
.report-list { list-style: none; padding: 0; margin: 0; max-width: 46rem; }
.report-add { min-height: 44px; }
.report-row { margin-bottom: 8px; }
/* The whole row is the target, so there is one thing to hit rather than a link
   floating in a card beside its own metadata. */
.report-link {
  display: flex;
  flex-wrap: wrap;
  gap: 2px 10px;
  align-items: baseline;
  justify-content: space-between;
  min-height: 44px;
  padding: 10px 14px;
  background: var(--paper-dim);
  border: 1px solid var(--line);
  border-radius: 8px;
  color: var(--ink);
  text-decoration: none;
}
.report-name { font-weight: 500; }
.report-meta { font-size: 12px; color: var(--ink-soft); }

/* --- Notices (ADR-019) --------------------------------------------------------
   Deliberately quieter than a ticket card: a notice is something you read once,
   not something you act on, so it gets no stamp, no accent border and no actions.
   The paper-dim ground separates it from the white cards below without competing
   with them for the eye. */
.notices { margin: 8px 0 4px; }
.notice-compose textarea { min-height: 76px; }
.notice {
  background: var(--paper-dim);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 12px 14px;
  margin-bottom: 8px;
}
.notice-body { margin: 0; white-space: pre-wrap; }
.notice-meta { margin: 6px 0 0; font-size: 12px; }

/* whats-new — the Desk strip (communication spec Phase 1). Furniture on the notices
   doctrine, not a card: no stamp, no accent border, no actions. Each row's badge
   colour mirrors the ticket card's stamp tokens (§7) so "Assigned" here and
   "Assigned" on the card read as the same fact, never a second palette. */
.whats-new { margin: 8px 0 4px; }
/* The mono eyebrow register (matches .needs-title's treatment on the board, and
   the .entry .mark gutter eyebrow from 960px): metadata, not a heading — small,
   uppercase, spaced, quieter than ink. Not .needs-title's rust: that colour
   is the board's "ignored, and that is the reason to open the app" alarm, and
   this strip is proactively-shown, unignored information, not a stale alert. */
.wn-title {
  font-family: "Plex Mono", ui-monospace, monospace;
  font-weight: 500;
  font-size: .75rem;
  text-transform: uppercase;
  letter-spacing: .1em;
  color: var(--ink-soft);
  margin: 0 0 8px;
}
.wn-list { list-style: none; margin: 0; padding: 0; }
.wn-item {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 8px 0;
  border-bottom: 1px solid var(--line);
}
.wn-item:last-child { border-bottom: none; }
/* Mono uppercase for metadata (§7's signature) — the label is a stamp, not prose. */
.wn-label {
  font-family: "Plex Mono", ui-monospace, monospace;
  font-size: 11px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: .08em;
  white-space: nowrap;
}
.wn-body { font-size: 14px; color: var(--ink); }
.wn-open .wn-label { color: var(--rust); }
.wn-assigned .wn-label { color: var(--amber-text); }
.wn-resolved .wn-label { color: var(--green); }
.wn-notice .wn-label { color: var(--ink-soft); }
.wn-quiet { font-size: 13px; color: var(--ink-soft); margin: 4px 0 8px; }

/* --- The month-end report (wide build ruling, 2026-08-16) ----------------------
   A document, not a screen: the print rendering is the real one, and the screen
   styling is that document previewed. Tables are mono-and-hairline — the ledger
   voice — never zebra-striped; a register earns trust by looking like one. */
.report-link{display:inline-flex;align-items:center;min-height:44px;
  font-size:13px;color:var(--green-dark);text-decoration:none}
.report-link:hover{color:var(--ink)}
/* The register runs eight columns and its min-content width beats a phone
   (467px against 390 — walk 2026-08-31). Wide content scrolls in its own
   container (master §4's offline-market discipline applied to layout); the
   PAGE never scrolls sideways, and the fixed dock stops stretching with it. */
.report{overflow-x:auto}
.report-table{width:100%;border-collapse:collapse;font-size:13px}
.report-table th{font-family:"IBM Plex Mono",monospace;font-weight:400;font-size:11px;
  text-transform:uppercase;letter-spacing:.06em;color:var(--ink-soft);
  text-align:left;padding:6px 8px 6px 0;border-bottom:1px solid var(--ink)}
.report-table td{padding:8px 8px 8px 0;border-bottom:1px solid var(--line);
  vertical-align:top}
.report-head{position:relative}
.print-btn{position:absolute;top:24px;right:0}
.report-to-date{color:var(--rust)}
.report-footer{margin-top:24px;padding-top:12px;border-top:1px solid var(--ink)}

@media print{
  /* Strip the app, keep the record. The EXAMPLE band is NOT hidden here — a demo
     report must carry its stamp onto paper (master §4). */
  .dock,.pdock,.pnav,.appbar,.signout,.print-btn{display:none !important}
  body{background:#fff}
  .shell{max-width:none;padding:0}
  .month-stat{border-color:#000}
  .report-table th{border-bottom-color:#000;color:#000}
  .report-table td{border-bottom-color:#999}
}

/* --- The estate switcher (wide build ruling, 2026-08-16) -----------------------
   A place-changer, not a widget: it reads as part of the board's frame. The
   select inherits the form styling's 44px floor; the label is the mono
   metadata voice. */
.estate-switcher{display:flex;align-items:center;gap:8px;margin:0 0 12px}
.estate-switcher select{max-width:280px;min-height:44px}
.switcher-label{font-size:11px;text-transform:uppercase;letter-spacing:.06em;
  color:var(--ink-soft)}
/* The being-built strip (ADR-024): content, never controls. Muted on purpose —
   it must read as an aside about the future, not as the product. The dashed
   border is the visual claim "not yet real", the same honesty species as the
   EXAMPLE band; nothing in it has a hover state because nothing in it is
   pressable. */
.coming { margin: 32px 0 16px; padding: 14px 16px 12px; border: 1px dashed var(--line); border-radius: 8px; background: transparent; }
.coming-chip { font-size: 11px; letter-spacing: 0.08em; color: var(--ink-soft); margin: 0 0 8px; }
.coming-item { margin: 0 0 8px; }
.coming-name { font-size: 14px; font-weight: 600; margin: 0; color: var(--ink-soft); }
.coming-line { margin: 2px 0 0; }
.coming-note { margin: 10px 0 0; font-size: 12px; }
/* The persona switcher (§7 public surface, 2026-08-17). Mono-caps because it is
   navigation; the current item is marked by weight and rust AND by aria-current,
   never by colour alone. Wraps as a block on a phone. */
.persona-nav { display: flex; flex-wrap: wrap; gap: 4px 14px; margin: 4px 0 0; font-size: 12px; letter-spacing: 0.04em; text-transform: uppercase; }
.persona-link { color: var(--ink-soft); text-decoration: none; padding: 6px 0; }
.persona-link:hover { color: var(--rust-dark); text-decoration: underline; text-underline-offset: 4px; }
.persona-link.is-current { color: var(--rust); font-weight: 600; }
/* --- The PMS surfaces (ADR-025) -----------------------------------------------
   The units list reuses the board row's density; a whole row is the link, so the
   touch target is the card rather than a word inside it. */
.unit-row{display:block;text-decoration:none;color:inherit}
.unit-row:hover{border-color:var(--ink-soft)}

/* Write controls fold away by default. This screen carries five forms, and a
   manager arrives to READ it far more often than to change it — open forms would
   make the record look like a data-entry screen instead of a record. <details>
   rather than JS: it is keyboard-operable and works with no script at all. */
.quiet-details,.danger-details{margin-top:12px}
.quiet-details>summary,.danger-details>summary{
  cursor:pointer;font-size:12px;text-transform:uppercase;letter-spacing:.06em;
  color:var(--green-dark);min-height:44px;display:flex;align-items:center}
.danger-details>summary{color:var(--rust)}
.quiet-details>summary:hover,.danger-details>summary:hover{color:var(--ink)}

/* A claim the landlord has not confirmed yet. Green, not rust: this is money the
   tenant says they have paid, and colouring it as a problem would make the
   manager's list read as debt when it is really an item of THEIR homework. */
.awaiting-flag{color:var(--green-dark)}

/* --- The marketing motion system (spec 2026-08-17 §3.2, owner ruling) ---------
   B (line-rise) and C (the word that rewrites itself) for the PUBLIC pages the
   app serves: the persona pitches and /pricing. Scoped by class, and the classes
   appear only in those templates — the app's own screens stay still (§1 bans
   engagement mechanics, and this system stops at the sign-in door). The global
   reduced-motion kill at the top of this file covers every rule below; each
   element's BASE style is its resting state, so animation:none leaves a complete
   page, not a paused one. All finite: each plays once and stops. */
.m-rise{animation:m-rise .5s ease both}
.m-d1{animation-delay:.08s}
.m-d2{animation-delay:.16s}
.m-d3{animation-delay:.24s}
@keyframes m-rise{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}

/* C: the audience word cycles the OTHER audiences and settles on this page's
   own — the switcher's promise, made typographic. The alternates are aria-hidden
   and the real word is the resting and accessible text, so a screen reader and a
   reduced-motion reader both get exactly the sentence the page means. inline-grid
   reserves its widest child's width; the persona nouns sit within a character of
   each other, which is what keeps the settled line from carrying a hole. */
.c-swap{display:inline-grid;color:var(--rust)}
.c-swap>span{grid-area:1/1;white-space:nowrap}
.c-word{animation:c-real 6.4s ease both}
.c-alt{opacity:0}
.c-alt:nth-child(2){animation:c-alt1 6.4s ease both}
.c-alt:nth-child(3){animation:c-alt2 6.4s ease both}
.c-alt:nth-child(4){animation:c-alt3 6.4s ease both}
@keyframes c-real{0%,22%{opacity:1}30%,86%{opacity:0}94%,100%{opacity:1}}
@keyframes c-alt1{0%,22%{opacity:0}30%,40%{opacity:1}48%,100%{opacity:0}}
@keyframes c-alt2{0%,48%{opacity:0}56%,64%{opacity:1}72%,100%{opacity:0}}
@keyframes c-alt3{0%,72%{opacity:0}80%,86%{opacity:1}94%,100%{opacity:0}}

/* The public pages' site footer (2026-08-17 night). Quiet by design: the mono
   register, soft ink, a rule above — the page's signature, not a destination. */
.site-foot{
  margin:36px 0 8px;padding:14px 0 0;border-top:1px solid var(--line);
  font-size:11.5px;letter-spacing:.05em;line-height:1.9;color:var(--ink-soft)}
.site-foot a{color:var(--ink-soft)}
.site-foot a:hover{color:var(--rust-dark)}

/* /pricing speaks by persona (2026-08-17 night rework). The finder index is the
   persona-nav's register stacked as lines — each an in-page anchor to a tier
   card. The only interactivity on the page is CSS: :target lifts the card an
   anchor landed on, so a visitor sent from a persona page arrives with their
   line already found; the public pages run no JavaScript, so this is the whole
   mechanism. The action line (.tier-act) is every card's last word — one of the
   three public actions, per the actions-only nav ruling. */
.tier-index{display:flex;flex-direction:column;margin:0 0 20px;font-size:12px;
  letter-spacing:.04em;text-transform:uppercase}
.tier-index a{color:var(--ink-soft);text-decoration:none;padding:6px 0}
.tier-index a:hover{color:var(--rust-dark);text-decoration:underline;
  text-underline-offset:4px}
.tier-card:target{border-color:var(--rust)}
.tier-act{margin:10px 0 0;font-size:12.5px}
.tier-act a{color:var(--rust-dark)}

/* owner digest */
/* The panel that leads the owner's home (communication spec Phase 3): the
   same card surface as .month-stat, the same wn-title eyebrow the feed
   already uses for a section head — the ten-second read gets a frame before
   the ledger tiles below it repeat the numbers in more detail. */
.digest-panel{margin:0 0 16px}
.digest-grid{display:flex;gap:8px;flex-wrap:wrap;margin:0}
.digest-grid > div{flex:1 1 100px;background:var(--white);border:1px solid var(--line);
  border-radius:8px;padding:10px 10px 8px}
.digest-grid dt{font-family:"Plex Mono",monospace;font-size:11px;text-transform:uppercase;
  letter-spacing:.06em;color:var(--ink-soft);margin:0}
.digest-grid dd{font-family:"Zilla Slab",Georgia,serif;font-weight:700;font-size:24px;
  line-height:1.2;margin:2px 0 0}
.digest-quiet{margin:8px 0 0;font-size:12.5px;color:var(--ink-soft)}

/* The PDF stand-in on a proof thumbnail (books-and-proof Part B). A bank's emailed
   confirmation has no picture to show, and an <img> pointed at a PDF renders as a
   broken image — which reads as "your evidence failed to upload" at exactly the
   moment a tenant most needs to believe it did not. Mono caps on the recessed
   surface: the metadata register, saying what the thing is. */
.proof-doc{display:flex;align-items:center;justify-content:center;width:100%;height:100%;
  background:var(--paper-dim);color:var(--ink-soft);font-size:var(--step-meta);
  letter-spacing:var(--track-meta)}

/* --- The complaints surface (ADR-045, the appflow direction) -------------------
   The first screen built to the new shell. Everything here is §7's existing
   material — the type ladder, the mono metadata register, --line hairlines, rust
   as the active accent — arranged the way the reference product arranges it.
   ADR-043 and ADR-044 both hold: not one new hex. */

.page-head{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;
  gap:12px;margin:var(--sp-6) 0 var(--sp-4)}
/* The action is a button-shaped anchor, so it needs the width reset §7's mobile
   pill would otherwise force on it. */
.page-action{width:auto;padding:0 var(--sp-6);min-height:44px}

/* Tabs. Real links carrying real URLs — a filtered list that cannot be sent to
   somebody is half a feature. The active one is marked three ways (weight, ink,
   and a rust underline) because §7 refuses colour as the only carrier. */
.tabs{display:flex;flex-wrap:wrap;gap:0 var(--sp-6);border-bottom:1px solid var(--line);
  margin-bottom:var(--sp-4)}
.tab{position:relative;display:inline-flex;align-items:center;min-height:44px;
  color:var(--ink-soft);text-decoration:none;font-size:15px;white-space:nowrap}
.tab:hover{color:var(--ink)}
.tab.is-on{color:var(--rust);font-weight:600}
.tab.is-on::after{content:"";position:absolute;left:0;right:0;bottom:-1px;height:2px;
  background:var(--rust)}

.filters{display:flex;flex-wrap:wrap;gap:var(--sp-2);align-items:center;
  margin-bottom:var(--sp-4)}
.filters input[type=search]{flex:2 1 240px;width:auto;min-height:44px}
.filters select{flex:1 1 170px;width:auto;min-height:44px}
.filters .filter-go{width:auto;flex:0 0 auto;min-height:44px;padding:0 var(--sp-6)}

/* The table scrolls inside its own box, never the page — §7's rule for wide
   content, and the reason a phone never gets a horizontal scrollbar. */
.table-wrap{overflow-x:auto}
.complaints-table{min-width:640px}
.complaints-table td{vertical-align:top}
.complaints-table .num{text-align:right;font-variant-numeric:tabular-nums;
  font-feature-settings:"tnum" 1}
.complaints-table .row-desc{color:var(--ink-soft)}
.complaint-thumb{width:56px;height:44px}
.complaint-thumb img{width:100%;height:100%;object-fit:cover;display:block}

/* Below 820px the table stops being a table: each row becomes a stacked card and
   every cell carries its own header from data-label. A five-column register on a
   390px screen is either a sideways scroll or a lie about what fits, and §7's
   floor says neither. */
@media (max-width:819.98px){
  .complaints-table{min-width:0}
  .complaints-table thead{position:absolute;width:1px;height:1px;overflow:hidden;
    clip:rect(0 0 0 0);white-space:nowrap}
  .complaints-table tr{display:block;background:var(--white);
    border:1px solid var(--line);border-radius:var(--radius);
    padding:var(--sp-3);margin-bottom:var(--sp-2)}
  .complaints-table td{display:flex;gap:var(--sp-3);justify-content:space-between;
    align-items:baseline;border:none;padding:3px 0}
  .complaints-table td::before{content:attr(data-label);flex:0 0 auto;
    font-family:"Plex Mono",ui-monospace,monospace;font-size:var(--step-meta);
    letter-spacing:var(--track-meta);text-transform:uppercase;color:var(--ink-soft)}
  .complaints-table .num{text-align:right}
}

/* --- The sidebar (ADR-045 part 3; owner-ruled 2026-08-29) ---------------------
   A SUPERSET of the dock, never a replacement. The dock is the first five of the
   same list and stays at every width — that ruling is not reopened here.

   1024px, not the 820px the board splits at. Taking 200px for a rail at 820
   would squeeze the content the rail exists to help somebody reach; the board's
   own two-column split already owns that breakpoint. Below 1024 the dock is the
   whole navigation, exactly as it is today, and `.with-rail` is an inert
   wrapper. */
.with-rail{display:block}
.sidebar{display:none}

@media (min-width:1024px){
  .with-rail{display:grid;grid-template-columns:200px minmax(0,1fr);
    gap:0 var(--sp-8);max-width:1340px;margin:0 auto;align-items:start}
  /* The shell keeps its own measure inside the content column rather than
     centring against the viewport — §7's reading measure is about the line
     length, not about the window. */
  .with-rail > .shell{margin:0}
  /* A rail-less screen must not fall into the rail's column. The wrapper is
     always emitted but _sidebar.html renders only with nav_items — the wizard,
     the onboarding acts, change-password and the PUBLIC join page have none,
     and the grid above was dropping their whole face into the 200px gutter
     (measured 164px wide at 1100px, the 2026-08-31 D/E walk, defect D-1).
     With no sidebar the shell is the wrapper's only child: span the grid and
     keep the shell's own centred measure. :only-child, not :has() — the fix
     must hold on the oldest Android WebView a resident holds. width:100% is
     load-bearing: auto margins switch a grid item off stretch, and without an
     explicit width the shell shrank to its content (455px, measured). */
  .with-rail > .shell:only-child{grid-column:1 / -1;width:100%;margin:0 auto}
  .sidebar{display:flex;flex-direction:column;gap:2px;position:sticky;top:18px;
    padding:var(--sp-6) 0 0 18px}
  .sidebar-item{display:flex;align-items:center;min-height:44px;padding:0 12px;
    font-family:"Plex Mono",ui-monospace,monospace;font-size:12px;
    text-transform:uppercase;letter-spacing:.04em;color:var(--ink-soft);
    text-decoration:none;border-radius:8px;
    /* The active marker is a left rule, not a fill: a filled block in a paper
       register reads as a button, and these are places rather than actions. */
    border-left:2px solid transparent}
  .sidebar-item:hover{color:var(--ink);background:var(--paper-dim)}
  .sidebar-item.is-active{color:var(--rust);font-weight:500;
    border-left-color:var(--rust);background:var(--white)}
}

/* What the slip's text says (2026-08-30). Rust only where the amount was NOT
   found — and the words carry the same claim, so this is emphasis on a sentence
   that already stands alone (§7: never colour alone). Not green on a match:
   a match is not an approval, and a green tick beside somebody else's money
   would invite the confirmation this product deliberately leaves to a person. */
.proof-read.is-unmatched{color:var(--rust)}

/* --- The dashboard (ADR-045; ADR-046 for the monogram) ----------------------
   No new hexes: ADR-043 and ADR-044 both ruled that §7's palette stands, so
   every colour below is an existing token. The one new SHAPE is .monogram, and
   it is a shape precisely because it is not a photograph — see ADR-046. */
.kpi-tile{display:block;text-decoration:none;color:inherit}
.kpi-tile:hover{border-color:var(--clay)}
.kpi-tile:focus-visible{outline:2px solid var(--rust);outline-offset:2px}

/* One column on a phone, three at width. 820px is the same fold the board and
   the complaints table use — one breakpoint for "this screen recomposes", not a
   third number for a reader to learn. */
.panel-grid{display:grid;grid-template-columns:1fr;gap:var(--sp-4);margin-top:var(--sp-6)}
@media (min-width:820px){.panel-grid{grid-template-columns:repeat(3,1fr)}}

.panel{background:var(--white);border:1px solid var(--line);border-radius:var(--radius);
  padding:var(--sp-4);min-width:0}
.panel-h{font-family:"Zilla Slab",Georgia,serif;font-weight:600;font-size:var(--step-card);
  margin:0 0 var(--sp-3)}
.panel-note{font-size:12.5px;color:var(--ink-soft);margin:0 0 var(--sp-3);line-height:1.45}
.panel-list{list-style:none;margin:0;padding:0}
.panel-row{display:flex;align-items:center;gap:var(--sp-3);padding:var(--sp-2) 0;
  border-bottom:1px solid var(--line);min-width:0}
.panel-row:last-child{border-bottom:0}

/* The monogram (ADR-046). A disc with two letters, standing exactly where the
   reference product puts a tenant photograph. It does the same scanning job —
   an anchor the eye returns to down a column of rows — and collects nothing to
   do it: the letters are computed at render time from the name already on
   record. aria-hidden in the markup, because the name is announced beside it. */
.monogram{flex:0 0 auto;width:34px;height:34px;border-radius:50%;
  display:inline-flex;align-items:center;justify-content:center;
  background:var(--paper-dim);color:var(--ink-soft);
  font-family:"IBM Plex Mono",ui-monospace,monospace;font-size:12px;font-weight:500;
  letter-spacing:.04em}

.panel-body{display:flex;flex-direction:column;min-width:0;flex:1 1 auto}
/* The NAME may be truncated: a pathologically long one would otherwise stretch
   the row, and the first words identify the person well enough to scan by. */
.panel-name{font-size:14px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* The META may NOT. It was `nowrap` with an ellipsis for one render, and at three
   columns on a 1280px screen every panel cut its own date off — "2 unpaid,
   oldest 20…", "Moved in 2…", "TCK-0001 · Unit 1 · 2026…". The date is the
   information: how long the debt has run, how long somebody has lived there with
   no terms. A tidy row that hides the number it exists to report is worse than a
   two-line row, so this wraps. */
.panel-meta{font-size:11px;color:var(--ink-soft);letter-spacing:var(--track-meta);
  line-height:1.5;overflow-wrap:anywhere}
.panel-num{flex:0 0 auto;font-size:13px;font-weight:500;white-space:nowrap}

/* Status as a WORD first (§7: never colour alone). The colours match the card
   stamp's so the two screens agree, but the word is what carries it. */
.panel-status{flex:0 0 auto;font-size:10.5px;text-transform:uppercase;
  letter-spacing:var(--track-meta);white-space:nowrap}
.panel-status.s-open{color:var(--rust)}
.panel-status.s-assigned{color:var(--amber-text)}
.panel-status.s-resolved{color:var(--green)}

.panel-more{display:inline-block;margin-top:var(--sp-2);font-size:12px;
  color:var(--rust-dark);text-decoration:none;border-bottom:1px solid var(--line)}
.panel-more:hover{border-bottom-color:var(--rust-dark)}
/* 44px touch target on the in-row "Set the terms" link (a11y pass, 2026-08-04):
   in a row it is the only tappable thing, and a 12px line is not a target. */
.panel-row .panel-more{margin-top:0;padding:12px 0;line-height:20px;border-bottom:0;
  text-decoration:underline}

/* ═══ The beauty pass (spec 2026-08-31) — the finish layer ══════════════════════
   Owner ruling: the market is the middle class and above, and a product that
   looks cheap will not be paid for. Everything below is CSS-only and rides the
   reduced-motion kill at the top of this file; the view-transition block carries
   its own kill because the global one matches elements, not pseudo-elements. */

/* Step 1 — depth on the surfaces that hold content. The dock floats the way a
   thumb bar should; the stat tiles and cards sit just proud of the paper. */
.card{box-shadow:var(--shadow-rest)}
.stat,.month-stat{box-shadow:var(--shadow-rest)}
.dock{box-shadow:0 -2px 14px rgba(32,36,43,.08)}

/* Step 2 — cross-document view transitions. Free bytes: browsers without the
   API get today's instant navigation, and nothing is fetched either way. */
@view-transition{navigation:auto}
::view-transition-old(root),::view-transition-new(root){animation-duration:.18s}
@media (prefers-reduced-motion:reduce){
  ::view-transition-group(*),::view-transition-image-pair(*),
  ::view-transition-old(*),::view-transition-new(*){animation:none!important}
}

/* Step 3 — evidence reads at evidence size. Ticket photos and proof slips share
   .evidence-thumb; nothing new loads, the pictures that already load get room.
   96px keeps two thumbs plus a peek in a 390px strip — the peek is what says
   "this row scrolls". */
.evidence-thumb{width:96px;height:96px;min-height:96px;box-shadow:var(--shadow-rest)}

/* Step 4 — finish. The press answers the finger even where transitions are
   killed (a moved resting state, not an animation); figures set like figures. */
.btn:active,.btn-call:active,.tap-target:active{transform:translateY(1px)}
.month-num,.report-table td,.report-table th{font-variant-numeric:tabular-nums}
