/* ==========================================================================
   PropMan — the design system's stylesheet.

   PropMan's screens are rendered on the server (DEC-0057), which checks and computes everything, and
   every screen works with script blocked. So a component here is markup plus a class, and every preview
   is the markup a Razor component emits. Where a component uses script, it is only to enhance: its page
   draws the state without script first, and its rules here say which parts the script adds (the brand
   book's Script section).

   The bundle.js beside this file is not the app's script: it runs only on design surfaces, where it
   lets a canvas draw an icon by name. The app draws the same glyphs from a server-side component.

   Every colour comes from a token. Both themes are designed, neither derived from the other, and
   print is a third designed palette rather than a filter over light.
   ========================================================================== */

/* --- The area accent ------------------------------------------------------ */

/* EACH MAJOR AREA HAS ITS OWN ACCENT so a reader knows where they are without reading the
   heading. A screen root carries data-area and these four locals resolve to that area's set;
   everything downstream reads the locals, so no rule names an area.

   WHAT IT COLOURS: the screen title's rule, the area's navigation rows, its breadcrumb, and the
   eyebrow above a title. WHAT IT NEVER COLOURS: the button that commits. The main action is
   identical on every screen, which is a decision — a reader who has learned where the dark
   button is should not have to re-learn it per area.

   The grouping is locked; 2b confirmed it. See the Areas page.

   A SCREEN UNDER NO AREA — Today, search, the error pages — takes area-none, a warm stone near neutral. It
   used to fall back to the accent, whose hue was also Records', so Today's lit row read as a Records row. */
.pm {
    --area: var(--area-none);
    --area-text: var(--area-none-text);
    --area-tint: var(--area-none-tint);
    --area-hairline: var(--area-none-hairline);
}

[data-area="work"] {
    --area: var(--area-work); --area-text: var(--area-work-text);
    --area-tint: var(--area-work-tint); --area-hairline: var(--area-work-hairline);
}
[data-area="money"] {
    --area: var(--area-money); --area-text: var(--area-money-text);
    --area-tint: var(--area-money-tint); --area-hairline: var(--area-money-hairline);
}
[data-area="records"] {
    --area: var(--area-records); --area-text: var(--area-records-text);
    --area-tint: var(--area-records-tint); --area-hairline: var(--area-records-hairline);
}
[data-area="compliance"] {
    --area: var(--area-compliance); --area-text: var(--area-compliance-text);
    --area-tint: var(--area-compliance-tint); --area-hairline: var(--area-compliance-hairline);
}
[data-area="admin"] {
    --area: var(--area-admin); --area-text: var(--area-admin-text);
    --area-tint: var(--area-admin-tint); --area-hairline: var(--area-admin-hairline);
}

/* --- Base ---------------------------------------------------------------- */

.pm {
    font-family: var(--font-ui);
    font-size: var(--fs-body);
    line-height: 1.55;
    color: var(--text);
    /* A CHECKED RADIO TAKES THE MAIN ACTION'S INK, not the platform's, which rendered red on one machine and
       will differ on the next. color-scheme is set with the theme in tokens.css, so an unchecked radio and a
       date box's glyph are drawn for the theme they sit in rather than as a white disc on dark. */
    accent-color: var(--solid);
}

.pm * { box-sizing: border-box; }

.pm :focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* THE RAMP STEPS BY SIZE, NEVER BY WEIGHT ALONE: page title 24, panel heading 16, section heading 14, body 14,
   secondary 13, caption 12, column head 11. A heading on the page — a panel's, a notice's, a modal's — is an
   h2 at the panel step. A heading INSIDE a panel is a section, and is never larger than the panel heading
   above it: an h3, or an h2 in a panel's body, at the section step. */
.pm h2 { font-size: var(--fs-panel-head); font-weight: 600; margin: 0 0 var(--space-8); }
.pm h3 { font-size: var(--fs-section); font-weight: 600; margin: var(--space-16) 0 0; }
.panel-body h2, .aside-body h2, .confirm-box h2 { font-size: var(--fs-section); }

.pm code {
    font-family: var(--font-mono);
    font-size: var(--fs-caption);
    color: var(--text-muted);
}

.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

/* --- Icons --------------------------------------------------------------- */

/* INLINE SVG, NEVER AN ICON FONT. A scale whose meaning is carried by a font is a scale that
   loses its meaning when the font does not load — and an icon font's glyph is read out as a
   character by some screen readers. Every icon is drawn on a 16px grid at one stroke width,
   inherits the ink beside it through currentColor, and is aria-hidden with words beside it. */
.icon {
    width: var(--icon-size);
    height: var(--icon-size);
    flex: none;
    display: inline-block;
    vertical-align: -0.18em;
    stroke: currentColor;
    fill: none;
    stroke-width: var(--icon-stroke);
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* A mark's icon is drawn at the mark's own ink rather than the row's, which is what makes it
   the third encoding rather than a decoration beside the second. */
.icon-mark { stroke: var(--mark-ink, currentColor); }

/* An icon whose substance is a fill rather than a stroke — the one filled state of a set. */
.icon-solid { fill: currentColor; stroke: none; }

/* A GLYPH MOUNTED BY NAME arrives inside its mount — [component-from-global-scope="PropMan.Icon"] on a
   design surface — so a rule that reaches a glyph as a CHILD would pass over it and reach only a drawn
   one. Every such rule here names the mount beside .icon, so a named glyph is reached exactly as a drawn
   one is; a rule that reaches a glyph as a DESCENDANT reaches both already. The mount itself sits on the
   line as a drawn glyph does and never shrinks. The app draws its glyphs inline and has no mount. */
[component-from-global-scope="PropMan.Icon"] { flex: none; vertical-align: -0.18em; }

/* --- The screen title ----------------------------------------------------- */

/* THE TITLE IS ALWAYS DRAWN and sits ABOVE the record read, never inside it. Twelve routes lose
   their h1 today because the heading sits inside content that only renders when the read came
   back Ok, so a refused or missing record arrives as a notice floating in an empty content area.
   The contract this replaces it with is written out in changes.md. */
.title-band {
    display: flex;
    align-items: baseline;
    gap: var(--space-12);
    flex-wrap: wrap;
    padding-left: var(--space-12);
    border-left: 3px solid var(--area);
    /* ONE SPACE BETWEEN THE TITLE AND WHAT FOLLOWS IT, whatever that is — a lede, a stepper, a notice, a panel.
       It ran from 8px to 32px between screens. */
    margin: 0 0 var(--space-24);
}

.pm h1 {
    font-size: var(--fs-screen-title);
    font-weight: 600;
    letter-spacing: -0.015em;
    margin: 0;
}

/* The record's own name, after the screen's noun — "Deposit — 14 Elm Row". Set in the same size
   and a lighter weight, because the screen is what the reader chose and the record is what they
   chose it for. */
.title-record { font-weight: 400; color: var(--text-2); }

/* The area, above the title, in the area's own ink. It is the half of "where am I" that survives
   a reader who has turned the navigation off in their head. */
.eyebrow {
    display: block;
    width: 100%;
    font-size: var(--fs-col-head);
    letter-spacing: .06em;
    text-transform: uppercase;
    font-weight: 600;
    color: var(--area-text);
    margin: 0 0 var(--space-4);
}

/* --- Panels -------------------------------------------------------------- */

.panel {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-card);
    box-shadow: var(--shadow-card);
    margin: 0 0 var(--space-32);
    overflow: hidden;
    display: flex;
    flex-direction: column;
}

.panel-head {
    padding: var(--space-16) var(--space-20);
    border-bottom: 1px solid var(--border-subtle);
    display: flex;
    align-items: center;
    gap: var(--space-12);
}

.panel-head h2 { font-size: var(--fs-panel-head); font-weight: 600; margin: 0; }
.panel-body { padding: var(--space-20); overflow-x: auto; flex: 1 1 auto; }

/* A panel's way onward, under its body: one quiet anchor, a navigation, so it trails its chevron. */
.panel-foot { padding: var(--space-8) var(--space-12); border-top: 1px solid var(--border-subtle); }

/* The line that says what the figure above it IS. A figure with no caption is a number the reader has
   to name for themselves, and a dashboard panel's heading is a question rather than a label. */
.figure-caption { margin: var(--space-4) 0 var(--space-16); color: var(--text-muted); font-size: var(--fs-secondary); }

/* PANELS SIDE BY SIDE are one height, and each one's foot sits on the same line: the panel is a column whose
   body takes the spare height. As many columns as fit, each at least 18rem, so two panels share the width
   rather than leaving a third column empty.

   THE MINIMUM GIVES WAY TO THE ROW'S OWN WIDTH. A grid track never shrinks below its minimum, and 18rem is
   eighteen times the reader's own root size — so a reader who has set a large browser font asks for a track
   wider than the row it sits in, and the page scrolls sideways. min() caps the minimum at the row's width,
   which stacks the panels instead; one column of panels is the narrow answer this already gives a phone. */
.panel-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(18rem, 100%), 1fr)); gap: var(--space-24); align-items: stretch; margin: 0 0 var(--space-32); }
.panel-row > .panel { margin: 0; }

/* --- Type helpers -------------------------------------------------------- */

.note, .filter-note, .counts {
    color: var(--text-muted);
    font-size: var(--fs-secondary);
    margin: var(--space-12) 0;
    max-width: var(--measure-prose);
}

.lede {
    margin: 0 0 var(--space-20);
    font-size: var(--fs-secondary);
    color: var(--text-muted);
    max-width: var(--measure-lede);
}

.basis, .as-of {
    display: block;
    color: var(--text-muted);
    font-size: var(--fs-caption);
}

/* A LINK IS DRAWN IN THE ACCENT INK. Zero specificity, so any component that styles its own links —
   a navigation row, a breadcrumb, a button-styled anchor, a chip — wins without having to out-shout it. */
:where(.pm a) { color: var(--accent); }
:where(.pm a:hover) { color: var(--accent-hover); }

/* A LINK INSIDE RUNNING TEXT KEEPS A PERMANENT UNDERLINE. In a sentence the underline is the only cue
   that does not depend on colour. Everything that ACTS is a button and carries no underline; this is
   the other half of that rule.

   RUNNING TEXT IS NAMED BY WHAT IT IS, NOT BY THE ELEMENT IT SITS IN. This rule used to reach every link
   in any li — and a list is also how the sidebar, the breadcrumb and a results list are built, so each of
   those had to override it. Now a list item is running text only inside a list that is prose: an absent
   list, a read-back, an error summary, or a list marked .prose. Zero specificity, for the reason above. */
:where(.pm p a, .pm .prose li a, .pm .absent li a, .pm .entered li a, .pm .error-summary li a,
       .note a, .lede a, .basis a, .notice-detail a, .notice-note a, .empty-note a, .confirm-what a,
       .confirm-warning a, .banner a, .modal a, .toggle-state a, .field-error-more a) {
    text-decoration: underline;
    text-underline-offset: 2px;
}

/* --- Money and dates ----------------------------------------------------- */

.money {
    font-family: var(--font-figures);
    font-size: var(--fs-secondary);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.stamp {
    font-family: var(--font-figures);
    font-size: var(--fs-secondary);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* A COUNT IN A COLUMN — days late, a number of properties — set as a figure is: the mono face at column
   size, so a column of counts aligns, in a cell that carries figure. A count inside a sentence is words
   and takes none of this. */
.count {
    font-family: var(--font-figures);
    font-size: var(--fs-secondary);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* A DATE OR AN AMOUNT INSIDE A SENTENCE TAKES THE INTERFACE FACE, WITH PROPORTIONAL FIGURES.

   The face: the mono face is a true monospace, so every glyph — digit, comma, full stop — takes
   one advance. Down a column that is the point; in a sentence it turns "£1,500.00" into four
   numbers. That is a property of any monospace, not of one face, and it holds for the current mono
   face exactly as it held for the last (measured: comma and full stop are each one digit wide).

   The figures: in a sentence there is no column to align, so tabular figures buy nothing there
   either — and in the interface face they now cost something. Its digits are proportional by
   default and vary widely, so tabular spacing pads a narrow digit to the widest one's width: a "1"
   gains a little over a fifth of an em. The previous interface face's digits were nearly even,
   which is why this used to be inherited from the column rule above without anyone noticing. It is
   stated here now, so the prose form and the column form differ on purpose. nowrap is still
   inherited.

   li IS THE MARK OF A LINE OF WORDS HERE, and a scale line is the one list item that is not one. Its rule
   sits with the scale, below. */
p .stamp, .note .stamp, .lede .stamp, .empty-note .stamp, caption .stamp, li .stamp, .eyebrow .stamp,
.confirm-what .stamp, .field-error .stamp, .entered .stamp, .facts dd .stamp, .title-record .stamp, .notice h2 .stamp,
p .money, .note .money, .lede .money, .empty-note .money, caption .money, li .money, .eyebrow .money,
.confirm-what .money, .field-error .money, .entered .money, .facts dd .money, .title-record .money, .notice h2 .money {
    font-family: var(--font-figures);
    font-size: inherit;
    font-variant-numeric: proportional-nums;
}

/* THE LEAD FIGURE ON A SCREEN, above the 14px boundary and therefore in the interface face. That
   face has proportional punctuation — its comma and full stop are about a third the width of a
   digit, so the gaps close — and it carries tabular figures as a feature, which this rule switches
   on: under it every digit takes one width. Its tabular digit is exactly as wide as the mono face's
   cell, so a figure crossing the boundary keeps its digit width. One home, declared once — three
   screen files each wrote this rule against a selector of their own. */
.money-display {
    font-family: var(--font-figures);
    font-size: var(--fs-display);
    font-weight: 500;
    letter-spacing: -0.015em;
    font-variant-numeric: tabular-nums;
    color: var(--text);
}

/* WHEN A READING WAS TAKEN. Drawn only where the reader genuinely needs the reading time — a
   dashboard figure somebody is about to act on, a reconciliation. NOT under a register's row
   count, where it is the screen explaining where a figure came from.

   PROPORTIONAL FIGURES, like every count and clock set in a line of words. Tabular figures were
   chosen here so the stamp would keep its width as the minute changed — but no figure in this app
   changes in place: every screen is drawn whole, once, per request. So the width they held steady
   was never seen to move, while the gap they open round a "1" in this face is. */
.published {
    color: var(--text-faint);
    font-size: var(--fs-caption);
    font-variant-numeric: proportional-nums;
    white-space: nowrap;
}

.ref {
    background: var(--surface-inset);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-badge);
    padding: 1px 5px;
    white-space: nowrap;
}

/* A record's NAME, which the list routes will publish to a caller holding the read grant, with
   the short handle behind it. The name is the normal case; see the Picker page. */
.record-name { font-weight: 500; }
.record-handle { color: var(--text-muted); font-size: var(--fs-caption); }

/* --- Buttons: three tiers -------------------------------------------------- */

/* TIER 2 is the base, because it is the commonest. One primary per screen; a quiet tier for the
   things inside a row.

   A BUTTON-STYLED <a> IS STILL AN <a>. Where the thing NAVIGATES it stays an anchor wearing this
   styling — making it a real <button> breaks middle-click, open-in-new-tab and the keyboard, and
   that is an accessibility regression dressed as a visual change. Only a write is a <button>. */
.pm button, .pm .button {
    font: inherit;
    font-family: var(--font-ui);
    font-size: var(--fs-secondary);
    height: var(--control-height);
    padding: 0 var(--space-16);
    border-radius: var(--radius-control);
    border: 1px solid var(--border-control);
    background: var(--surface);
    color: var(--text-2);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: var(--space-8);
    text-decoration: none;
    white-space: nowrap;
}

.pm button:hover, .pm .button:hover { background: var(--surface-inset); color: var(--text); }

/* Tier 1 — the main action. Near-black on light, white on dark, and the SAME on every screen:
   it deliberately does not take the area accent. */
.pm button.primary, .pm .button.primary {
    background: var(--solid);
    border-color: var(--solid);
    color: var(--on-solid);
    font-weight: 500;
}

.pm button.primary:hover, .pm .button.primary:hover { background: var(--solid); color: var(--on-solid); opacity: .9; }

/* Tier 3 — quiet. For the things inside a table row, where a bordered button per row would draw
   a grid of boxes over the figures. It is shorter, so a row is no taller than its text needs. */
.pm button.quiet, .pm .button.quiet {
    height: var(--control-height-quiet);
    padding: 0 var(--space-8);
    border-color: transparent;
    background: transparent;
    color: var(--text-2);
    border-radius: var(--radius-badge);
    font-weight: 500;
}

.pm button.quiet:hover, .pm .button.quiet:hover {
    background: var(--surface-inset);
    border-color: var(--border-control);
    color: var(--text);
}

/* AN ICON-ONLY BUTTON is square, and its name is words a screen reader reads (visually-hidden). Used only where
   the words sit beside it already — the sidebar's search, under its own label. */
.pm button.icon-only, .pm .button.icon-only { width: var(--control-height); padding: 0; justify-content: center; }

/* WHAT KEEPS THREE QUIET BUTTONS IN ONE ROW FROM READING AS THREE OF THE SAME THING. A row's
   actions divide into ones that GO somewhere and ones that DO something, and the two carry
   different icons and sit on different sides: navigations trail a chevron, acts lead with the
   icon of the act. A row whose three links became three identical quiet buttons has lost the
   distinction; this is what carries it. */
.button.quiet-go .icon, .button.quiet-go > [component-from-global-scope="PropMan.Icon"] { order: 2; }
.row-actions { display: flex; align-items: center; gap: var(--space-4); justify-content: flex-end; }

/* THE WAY OUT, a link immediately to the right of the committing button and never drawn as one. On a single
   form it is Cancel, or what the reader can do instead; on a step of a multi-step form it is the step back,
   named for where it goes, and on the first step it leaves the form. One place on every form. */
.confirm-cancel, .cancel {
    margin-left: var(--space-16);
    font-size: var(--fs-secondary);
    color: var(--accent);
    text-decoration: underline;
    text-underline-offset: 2px;
}

.write-actions {
    display: flex;
    align-items: center;
    gap: var(--space-16);
    margin: var(--space-24) 0 0;
    padding-top: var(--space-16);
    border-top: 1px solid var(--border-hair);
}

.write-actions .confirm-cancel, .act-bar .confirm-cancel { margin-left: 0; }

/* What cannot be reached is named in words and carries no control. This app draws no disabled
   button anywhere, and the rule is load-bearing rather than stylistic: a control that does not
   work is a promise the software cannot keep. */
.absent {
    color: var(--text-muted);
    font-size: var(--fs-secondary);
    max-width: var(--measure-prose);
    padding-left: var(--space-20);
}

.absent li { margin-bottom: var(--space-6); }

/* --- Fields -------------------------------------------------------------- */

.pm input[type="text"], .pm input[type="password"], .pm input[type="date"],
.pm input[type="number"], .pm select, .pm textarea {
    font: inherit;
    font-family: var(--font-ui);
    font-size: var(--fs-body);
    height: var(--input-height);
    padding: 0 var(--space-12);
    width: 100%;
    border-radius: var(--radius-control);
    border: 1px solid var(--border-control);
    background: var(--surface-subtle);
    color: var(--text);
}

/* A MULTI-LINE BOX, for an answer in the reader's own words — a reason given for a change. It opens
   with room for a few lines and grows downward only, so it never widens past its field. */
.pm textarea {
    display: block;
    height: auto;
    min-height: calc(var(--input-height) * 2.5);
    padding: var(--space-8) var(--space-12);
    line-height: 1.5;
    resize: vertical;
}

.pm input:focus-visible, .pm select:focus-visible, .pm textarea:focus-visible {
    border-color: var(--accent);
    background: var(--surface);
}

.field-label {
    display: block;
    font-size: var(--fs-secondary);
    font-weight: 500;
    color: var(--text-2);
    margin-bottom: var(--space-6);
}

.field { max-width: 22rem; }

/* THE RULE IS READ BEFORE THE BOX IS TYPED INTO, so the hint sits between the label and the box, and a refusal
   sits under the box: label, hint, box, refusal. A hint under the box was read after typing, and under a
   refusal it was read last of all. */
.field .basis { margin: 0 0 var(--space-6); max-width: 46ch; }

/* FIELDS SIDE BY SIDE SIT ON FIXED TRACKS, two to a row, so a refusal that wraps under one box never pushes its
   neighbour sideways. A field that needs the row takes field-wide. One track on a narrow screen. */
.field-set {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 22rem));
    align-items: start;
    gap: var(--space-16) var(--space-24);
    margin: var(--space-16) 0 0;
}

.field-set > .field-wide { grid-column: 1 / -1; }
@media (max-width: 40rem) { .field-set { grid-template-columns: minmax(0, 1fr); } }

/* A CONSEQUENCE IS NOT A HINT. What cannot be undone, or runs out on a day, is set in the body's ink with a
   glyph hanging beside it, before the thing it is about: between a label and its box, or before the button
   that commits. The warning glyph for what cannot be undone; the clock, in the amber the stale draft uses,
   for a day that is close. A routine hint stays quiet, so quiet can be trusted. */
.consequence {
    position: relative;
    margin: 0 0 var(--space-8);
    padding-left: calc(var(--icon-size) + var(--space-8));
    max-width: 60ch;
    font-size: var(--fs-secondary);
    color: var(--text);
}

.consequence > .icon:first-child,
.consequence > [component-from-global-scope="PropMan.Icon"]:first-child {
    position: absolute;
    left: 0;
    top: 1px;
    top: calc((1lh - var(--icon-size)) / 2);
    color: var(--warning-text);
}

.consequence-due { color: var(--warning-text); }

/* A FIELD THE SERVER REFUSED. The border is not the whole message — the words below it are — but
   a healthy-looking field beside an error is its own small lie. Phase 3 has settled that the API
   will NAME the field it refused, so the right box is marked rather than guessed at from the
   message text. */
.pm input[aria-invalid="true"], .pm select[aria-invalid="true"], .pm textarea[aria-invalid="true"] { border-color: var(--danger); }

/* THE SENTENCE IS ONE RUN OF TEXT, AND THE MARK HANGS IN THE MARGIN BESIDE IT. A flex row would make
   every piece of the sentence its own column — the words, an amount, the words after it — so an
   amount such as the deposit cap would stand apart from the sentence it belongs to. The mark is
   centred on the first line, and a line that wraps starts under the words, not under the mark. */
.field-error {
    position: relative;
    margin: var(--space-6) 0 0;
    padding-left: calc(15px + var(--space-8));
    font-size: var(--fs-secondary);
    color: var(--danger-text);
    max-width: 46ch;
}

.field-error .mark {
    position: absolute;
    left: 0;
    top: 2px;
    top: calc((1lh - 15px) / 2);
    width: 15px;
    height: 15px;
    border-radius: 4px;
    background: var(--danger);
    color: var(--on-danger);
    display: grid;
    place-items: center;
}

.field-error .mark .icon { width: 11px; height: 11px; stroke-width: 2.25px; vertical-align: 0; }

/* A REFUSAL TOO LONG TO SIT BESIDE THE BOX. Phase 3 has settled that the deposit-cap refusal
   carries its full statutory consequence uncompressed, which is longer than any message on
   screen today. So the field carries a short line and the whole text goes in the summary at the
   top, or in a disclosure under the field where the reader is already looking. */
.field-error-more {
    margin: var(--space-4) 0 0;
    font-size: var(--fs-caption);
}

.field-error-more > summary { cursor: pointer; color: var(--danger-text); }
.field-error-more > summary, .more > summary { list-style: none; display: inline-flex; align-items: center; gap: var(--space-4); }
.field-error-more > summary::-webkit-details-marker, .more > summary::-webkit-details-marker { display: none; }
.field-error-more > div { margin-top: var(--space-4); color: var(--text-2); max-width: 46ch; }

/* A DISCLOSURE THAT IS NOT AN ERROR — what an act means, how a figure is worked out — set in the ordinary
   ink. field-error-more is for the rest of a refusal and takes the danger ink; nothing that is not a
   refusal borrows it. */
.more { margin: var(--space-4) 0 0; font-size: var(--fs-caption); }
.more > summary { cursor: pointer; color: var(--text-2); }
.more > summary:hover { color: var(--text); }
.more > div { margin-top: var(--space-4); color: var(--text-2); max-width: 46ch; }

/* A money box, and the unit is drawn INSIDE it rather than left to the label. */
.money-field {
    display: flex;
    align-items: stretch;
    max-width: 14rem;
    border: 1px solid var(--border-control);
    border-radius: var(--radius-control);
    background: var(--surface-subtle);
    overflow: hidden;
}

.money-field .unit {
    display: inline-flex;
    align-items: center;
    padding: 0 var(--space-8) 0 var(--space-12);
    font-family: var(--font-figures);
    font-size: var(--fs-body);
    color: var(--text-muted);
    background: var(--surface-inset);
    border-right: 1px solid var(--border-mark);
}

.pm .money-field input {
    border: none;
    border-radius: 0;
    background: transparent;
    text-align: right;
    padding-right: var(--space-12);
    font-family: var(--font-figures);
    font-variant-numeric: tabular-nums;
}

.money-field:focus-within { border-color: var(--accent); background: var(--surface); }
.money-field:focus-within .unit { background: var(--surface-subtle); }
.pm .money-field input:focus-visible { outline: none; background: transparent; }
.money-field[data-invalid="true"] { border-color: var(--danger); }

/* A date box. The native control stays the source of truth; the styled calendar below is an
   enhancement over it. */
.date-field { max-width: 12rem; position: relative; }

/* --- A decision rather than a value -------------------------------------- */

.choice { display: flex; flex-direction: column; gap: var(--space-2); min-width: 16rem; }

.choice-option {
    display: flex;
    align-items: center;
    gap: var(--space-8);
    font-size: var(--fs-secondary);
    padding: var(--space-4) 0;
}

.choice-option input[type="radio"] { margin: 0; width: auto; height: auto; }

/* --- The toggle ---------------------------------------------------------- */

/* BUTTONS AND LINKS, NOT A SWITCH. Nothing client-side can carry a flipped switch to the server, so
   each option is the control for what pressing it does, and the one in force is marked.

   TWO SHAPES, chosen by what a press does. Where every press IS the change — the theme — each option
   is a submit button and aria-pressed marks the one in force: pressing sets exactly the state the
   button names, so "pressed" is true before the press and on the page that comes back. Where a press
   ASKS first — a compliance control, where every change asks for a reason — aria-pressed would
   promise a state the press does not set. There the option in force is not a control at all: it is words,
   marked with aria-current="true", the current one of a set, as the chosen tab is. Every other
   option is a link that opens the question: asking records nothing. */
.toggle { display: inline-flex; gap: var(--space-2); }

.pm .toggle button, .pm .toggle .button {
    height: var(--segment-height);
    padding: 0 var(--space-12);
    border-radius: var(--radius-chip);
    background: var(--surface);
    border-color: var(--border-control);
    color: var(--text-2);
}

.pm .toggle .toggle-current {
    display: inline-flex;
    align-items: center;
    height: var(--segment-height);
    padding: 0 var(--space-12);
    border: 1px solid var(--text);
    border-radius: var(--radius-chip);
    font-size: var(--fs-secondary);
    white-space: nowrap;
}

.toggle-state { margin: var(--space-6) 0 0; font-size: var(--fs-caption); color: var(--text-muted); }

/* WHILE A QUESTION IS OPEN, THE TOGGLE ABOVE IT IS WORDS. A lone option left standing looked like a button,
   and in the old fill it read as a second main action beside the real one. So no option is drawn: the
   setting in force is said, and the question below is the only thing that can post. */
.toggle-now { margin: 0; font-size: var(--fs-body); color: var(--text); }

/* --- One selection ------------------------------------------------------------------------------------- */

/* THE CHOSEN ONE OF A SET IS OUTLINED IN THE INK, AND solid FILLS ONLY THE MAIN ACTION. The option in force,
   the chosen tab, the marked segment of a segmented mark, the chosen calendar day and the step the reader is
   on each used solid, so a chosen segment beside the primary read as a second primary. Now each is a 2px
   outline in text on the surface, its words in text at weight 600. The forced-colours block below underlines
   each as well. */
.pm .toggle button[aria-pressed="true"], .pm .toggle .toggle-current,
.tabs .current, .kindmark .seg.on,
.pm .calendar-day.chosen, .pm .calendar-day[aria-current="date"],
.steps .step-here .pip-num {
    background: var(--surface);
    border-color: var(--text);
    box-shadow: inset 0 0 0 1px var(--text);
    color: var(--text);
    font-weight: 600;
}

.pm .toggle button[aria-pressed="true"]:hover { background: var(--surface-inset); color: var(--text); }

/* --- Search and the picker ------------------------------------------------ */

.search-box {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: var(--space-8);
    margin: 0 0 var(--space-16);
}

.search-field { flex: 1 1 10rem; min-width: 0; }
.search-field .field-label { margin-bottom: var(--space-4); }

.picker { max-width: 34rem; }

.picker-results {
    list-style: none;
    margin: var(--space-12) 0 0;
    padding: 0;
    border: 1px solid var(--border-mark);
    border-radius: var(--radius-inset);
    overflow: hidden;
}

.picker-results li { border-bottom: 1px solid var(--border-hair); background: var(--surface); }
.picker-results li:last-child { border-bottom: none; }
/* A candidate the reader ticked before finding again, kept at the head of the new answer — the server keeps it
   there, so a new term never takes a choice off the page. A firmer rule sets it apart from what the term found. */
.picker-results li.picker-kept { border-bottom: 1px solid var(--border-strong); }

.picker-results label {
    display: flex;
    align-items: baseline;
    gap: var(--space-12);
    padding: var(--space-12) var(--space-16);
    font-size: var(--fs-secondary);
    cursor: pointer;
}

.picker-results label:hover { background: var(--surface-inset); }
.picker-results input[type="radio"] { margin: 0; width: auto; height: auto; }
.picker-qualifier { color: var(--text-muted); margin-left: auto; }

/* --- The landlord row: a register narrowed to one landlord, found by name ------------------------- */

/* THE ROW IS THE SERVER'S, IN EVERY STATE. A register that narrows to one landlord gives the landlord a row
   of its own in its filter bar, drawn with the page: the Find form, or — with a landlord in force — the chip
   in the form's place. Script adds nothing to the layout, and choosing a landlord moves nothing but the rows.
   Its height is held at a control's, so the register sits at one depth whichever the row holds.
   Every landlord's address ends in a fragment naming this row, and so does the chip's own link, so after a
   landlord is chosen or taken off, a keyboard's next stop is what the row holds, and then the rows.
   THE SCROLL MARGIN IS THE WINDOW'S HEIGHT LESS 12rem. Arriving at the row moves the page only when the row
   would otherwise sit in the window's last 12rem, and then only far enough to bring it 12rem above the foot,
   with the box and the register's first rows under it. Anywhere higher, the page stays where it opened, title
   and all, however many notices stand above the filters: a fixed margin was a guess at the row's depth, which
   a notice makes wrong. The small viewport height is the one a phone's toolbars leave. */
.filter-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-8) var(--space-12); min-height: var(--control-height); margin: 0 0 var(--space-12); scroll-margin-top: max(0px, calc(100vh - 12rem)); scroll-margin-top: max(0px, calc(100svh - 12rem)); }

/* WITHOUT SCRIPT IT IS A FORM THAT WORKS: a GET to the register's own address carrying every filter in force,
   answered by the server with the matching landlords as links (find-answer, inside the form, so it prints
   away with it). A GET writes nothing, so it needs no stand-in form. WITH SCRIPT THE SAME BOX IS UPGRADED IN
   PLACE — the script adds the combobox roles and the popup — so nothing moves when the script arrives.
   THE SCRIPT FILTERS NOTHING: each pause in the typing asks the server, which asks PropMan under the caller's
   own grant and answers with the options. THE SCRIPT NARROWS NOTHING EITHER: each option is the register's
   own address with the landlord added, the address a row's landlord link is built from, so choosing one is
   following a link and the server narrows the list. */
.combobox { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-8); flex: 1 1 auto; min-width: 0; margin: 0; }
.combobox > .field-label { margin: 0; }
/* ONE LABEL COLUMN IN A FILTER BAR. The bar stacks rows, the band tabs above the Landlord row, and each row's
   first control starts on one edge. So this label is held to the bar's label column: at least 4rem, then 16px
   to the box, the gap the Tabs row leaves after its own label. The filter bar holds its other labels to the
   same 4rem. A label longer than the column pushes out only its own row. */
.filter-row .combobox > .field-label { min-width: 4rem; margin-right: var(--space-8); }
/* The box grows to 16rem and gives way to the column below it, so the label, the box and Find reflow to
   two lines on a phone rather than run past the screen's edge. */
.combobox-field { position: relative; flex: 1 1 10rem; max-width: 16rem; min-width: 0; }
.combobox-field > .icon, .combobox-field > x-import .icon { position: absolute; left: var(--space-12); top: 50%; transform: translateY(-50%); color: var(--text-muted); pointer-events: none; }
.pm .combobox-field input[type="text"] { padding-left: calc(var(--space-12) + var(--icon-size) + var(--space-8)); }

/* The server's answer to Find, without script: what matched, said in words, then the landlords as links —
   each the register's own address with that landlord added. A list of results, not prose, so its links are
   styled here: the name in the body's ink, the handle behind it, the chevron because each goes somewhere. */
.find-answer { flex-basis: 100%; }
.find-answer > .filter-note { margin: var(--space-4) 0 var(--space-8); }
.find-results { max-width: 28rem; list-style: none; margin: 0; padding: 0; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-inset); overflow: hidden; }
.find-results li + li { border-top: 1px solid var(--border-hair); }
.find-results a { display: flex; align-items: baseline; gap: var(--space-12); padding: var(--space-8) var(--space-12); color: var(--text); font-size: var(--fs-secondary); text-decoration: none; }
.find-results a:hover { background: var(--surface-inset); color: var(--text); }
.find-results a .record-name { font-weight: 600; }
.find-results a .record-handle { margin-left: auto; }
.find-results a > .icon, .find-results a > x-import { align-self: center; color: var(--text-muted); }

/* With script, the answer opens under the box and over what follows, so the register does not move while a
   reader types. It never scrolls the page. */
.combobox-list {
    position: absolute;
    left: 0;
    top: calc(100% + var(--space-6));
    z-index: 4;
    min-width: 100%;
    width: max-content;
    max-width: 28rem;
    padding: var(--space-4);
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-card);
    box-shadow: var(--shadow-raised);
}

.combobox-list [role="listbox"] { margin: 0; padding: 0; list-style: none; }

.combobox-option {
    display: flex;
    align-items: baseline;
    gap: var(--space-12);
    padding: var(--space-8) var(--space-12);
    border-radius: var(--radius-chip);
    font-size: var(--fs-secondary);
    color: var(--text);
    cursor: pointer;
}

.combobox-option:hover { background: var(--surface-inset); }
/* The option the arrows are on takes the one selection: a 2px outline in text, its words at 600. */
.combobox-option[aria-selected="true"] { background: var(--surface); box-shadow: inset 0 0 0 2px var(--text); }
.combobox-option[aria-selected="true"] .record-name,
.combobox-option[aria-selected="true"] > code.ref:first-child { font-weight: 600; }
.combobox-option .record-handle { margin-left: auto; }
.combobox-option .record-handle + .picker-qualifier { margin-left: 0; }

/* What the options cannot say — more matched than are listed, nothing matched, the landlords could not be
   looked up — is words in the popup's foot, never an empty panel. It sits beside the listbox, never in it, so
   the arrows never land on it. It is not a live region: the popup is redrawn with each answer, and a region
   drawn along with its words is often not announced. A listener hears each answer once, from the page's one
   status region, which the server draws empty outside the popup (the brand book's Script section). */
.combobox-answer { margin: 0; padding: var(--space-8) var(--space-12); font-size: var(--fs-secondary); color: var(--text-muted); max-width: 24rem; }

/* ON A PHONE the label takes its own line, and the popup is exactly the box's width, the box being held to
   the column, so neither runs past the screen's edge. The row keeps the label's line in every state, the chip
   on the lower line where the box was, so here too the register sits at one depth whichever the row holds. */
@media (max-width: 40rem) {
    .filter-row { min-height: calc(var(--fs-secondary) * 1.55 + var(--space-8) + var(--control-height)); align-content: flex-end; }
    .filter-row > .filter-chip { margin-bottom: calc((var(--control-height) - var(--segment-height)) / 2); }
    .combobox > .field-label { flex-basis: 100%; }
    .combobox-list { width: 100%; max-width: none; }
}

/* --- Tabs ---------------------------------------------------------------- */

.tabs {
    display: flex;
    align-items: center;
    gap: var(--space-6);
    flex-wrap: wrap;
    margin: 0 0 var(--space-12);
}

.tabs a, .tabs .current {
    display: inline-flex;
    align-items: center;
    gap: var(--space-6);
    height: var(--segment-height);
    padding: 0 var(--space-12);
    border-radius: var(--radius-chip);
    font-size: var(--fs-secondary);
    text-decoration: none;
}

.tabs a { border: 1px solid var(--border-control); background: var(--surface); color: var(--text-2); }
.tabs a:hover { background: var(--surface-inset); color: var(--text); }

/* THE CHOSEN TAB IS NOT A LINK AT ALL — a link to where you already are is a control that does
   nothing. aria-current is what says which one it is in speech; its outline is the one selection above. */
.tabs .current { border: 1px solid var(--text); }

/* A FILTER IN FORCE THAT IS NOT A TAB — one landlord, say — sits in the filter bar as a chip, and pressing it
   takes the filter off. It is a link to the same address without that argument, so no script. */
.filter-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--space-6);
    height: var(--segment-height);
    padding: 0 var(--space-8) 0 var(--space-12);
    border: 1px solid var(--text);
    border-radius: var(--radius-chip);
    background: var(--surface);
    color: var(--text);
    font-size: var(--fs-secondary);
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
}

.filter-chip:hover { background: var(--surface-inset); }
.filter-chip .icon { color: var(--text-muted); }

/* A count is part of the tab's label, so it is set as words: proportional figures. See .published. */
.tab-count { margin-left: var(--space-4); opacity: .75; font-variant-numeric: proportional-nums; }

/* --- The stepper --------------------------------------------------------- */

.steps {
    list-style: none;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-8) var(--space-16);
    margin: 0 0 var(--space-20);
    padding: 0;
    font-size: var(--fs-secondary);
}

.steps li { display: inline-flex; align-items: center; gap: var(--space-8); color: var(--text-muted); }
.steps .step-ahead { color: var(--text-muted); }

.steps .pip-num {
    flex: 0 0 auto;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    display: inline-grid;
    place-items: center;
    font-size: var(--fs-caption);
    font-variant-numeric: tabular-nums;
    border: 1px solid var(--border-mark);
    background: var(--surface);
    color: var(--text-muted);
}

/* A STEP DONE carries a tick in place of its number, so a past step is told from one ahead by shape, not by a
   shade of grey. The step the reader is on is outlined, the one selection. */
.steps .step-done { color: var(--text-2); }
.steps .step-done .pip-num { background: var(--surface-inset); border-color: var(--border-mark); color: var(--text-2); }
.steps .step-done .pip-num .icon, .steps .step-done .pip-num [component-from-global-scope="PropMan.Icon"] { width: 12px; height: 12px; stroke-width: 2px; vertical-align: 0; }
.steps .step-here { color: var(--text); font-weight: 600; }

/* WHAT IS KEPT, said where the reader can see it. The stepper now holds a draft, so a reader who
   leaves mid-way has something to come back to — and a control that goes back is drawn on EVERY
   step rather than appearing and disappearing between them, because a control that moves is one
   a reader stops trusting. */
.steps-bar {
    display: flex;
    align-items: center;
    gap: var(--space-16);
    flex-wrap: wrap;
    margin: 0 0 var(--space-20);
}

.steps-bar .steps { margin: 0; }
/* The step back: one link, in one place — the way out beside each step's committing button. */
.steps-draft { margin-left: auto; font-size: var(--fs-caption); color: var(--text-muted); display: inline-flex; align-items: center; gap: var(--space-6); }
/* A draft kept long enough to be doubted: the same line in the warning ink, with the clock rather than the
   page. What it says is the screen's. */
.steps-draft-stale { color: var(--warning-text); }

/* --- The block that commits ---------------------------------------------- */

.confirm-box {
    margin: var(--space-20) 0 0;
    padding: var(--space-16) var(--space-20);
    border: 1.5px solid var(--warning-hairline);
    border-radius: var(--radius-inset);
    background: var(--warning-tint);
}

.confirm-what { margin: 0 0 var(--space-12); max-width: var(--measure-prose); }

/* EMPHASIS IS NOT DIMMER THAN WHAT IT EMPHASISES. A bold line inside quieter ink — "The legal duty does not
   switch off." in a note — is set in the body's ink, so in dark it does not read fainter than the text above. */
:where(.note, .notice-detail, .notice-note, .confirm-warning, .toggle-state, .lede) b { color: var(--text); }
.confirm-warning { margin: 0 0 var(--space-16); max-width: var(--measure-prose); font-size: var(--fs-secondary); color: var(--text-2); }

/* THE PARTS STACKED ABOVE THE BUTTON KEEP ONE GAP, set here once rather than by each screen: a field, a choice,
   a disclosure, a consequence line, a read-back, a refusal summary. The button comes last, its way out beside
   it. */
.confirm-box > .field, .confirm-box > .choice, .confirm-box > .more, .confirm-box > .consequence,
.confirm-box > .facts, .confirm-box > .error-summary { margin: 0 0 var(--space-16); }

/* A RECORD'S FACTS, OR WHAT THE READER ENTERED, READ BACK: a label and its value, read down, in two columns.
   One pattern wherever a thing is read back — a record's own facts, a check step, a confirm block — so a
   reader who has learnt where the value sits finds it there every time. facts-stacked puts each value under
   its label, for a narrow column. */
.facts { display: grid; grid-template-columns: minmax(8rem, 15rem) minmax(0, 1fr); gap: var(--space-8) var(--space-16); margin: 0; font-size: var(--fs-secondary); max-width: var(--measure-lede); }
.facts dt { color: var(--text-muted); }
.facts dd { margin: 0; color: var(--text); }
.facts-stacked { grid-template-columns: minmax(0, 1fr); gap: var(--space-2) 0; }
.facts-stacked dd { margin-bottom: var(--space-8); }

/* RETIRED: the bulleted read-back. Every read-back is facts now; the rule stays so a canvas that re-syncs
   before it redraws is not left unstyled. Remove it once no board draws .entered. */
.entered { font-size: var(--fs-secondary); max-width: var(--measure-prose); padding-left: var(--space-20); margin: var(--space-8) 0 0; }
.entered li { margin-bottom: var(--space-4); }

/* THE SCRIM COVERS THE SCREEN; that is its whole job. Fixed to the viewport, above everything the shell
   draws — the caller's menu included — with the modal centred in it. No script: it is drawn first in the
   document when the session has ended, so it is read first, and it is fixed so where it is written does
   not decide where it is painted. A preview contains it in a frame of its own. */
.modal-scrim {
    position: fixed;
    inset: 0;
    z-index: 10;
    display: grid;
    place-items: center;
    padding: var(--page-padding);
    background: var(--scrim);
}

.modal {
    max-width: 460px;
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-card);
    box-shadow: var(--shadow-raised);
    padding: var(--space-24);
}

.modal h2 { margin: 0 0 var(--space-12); font-size: var(--fs-panel-head); }
.modal p { margin: 0 0 var(--space-12); font-size: var(--fs-secondary); color: var(--text-2); }
.modal p:last-child { margin-bottom: 0; }

/* --- Notices ------------------------------------------------------------- */

/* ONE SHAPE FOR EVERY ANSWER THAT IS NOT DATA, tinted by WHAT THE READER HAS TO DO ABOUT IT
   rather than by which HTTP status arrived. Four tones and no more:

     danger  — it did not happen, and something has to change before it will
     warning — it happened; check it
     fault   — it did not happen, or nobody can tell, and only time or another person will change it
     done    — it happened as asked

   A NOTICE SITS UNDER THE TITLE, ON ITS LEFT EDGE — a failure or a refusal that is the screen's whole answer
   included. Its left rule and hanging glyph already anchor it there; centred, it floated away from the title
   it answers. */
.notice {
    border: 1px solid var(--border);
    border-left-width: 3px;
    border-radius: var(--radius-inset);
    padding: var(--space-16) var(--space-20);
    margin: 0 0 var(--space-24);
    background: var(--surface);
    max-width: var(--measure-prose);
}

/* The heading is one run of text and its icon hangs in the margin, for the reason the field error's
   mark does: a flex row would tear an amount or a link in the heading away from its words. The error
   summary is a notice, so this is its heading too. */
.notice h2 {
    position: relative;
    margin: 0 0 var(--space-6);
    padding-left: calc(var(--icon-size) + var(--space-8));
    font-size: var(--fs-panel-head);
    font-weight: 600;
}

.notice h2 > .icon:first-child,
.notice h2 > [component-from-global-scope="PropMan.Icon"]:first-child {
    position: absolute;
    left: 0;
    top: 2px;
    top: calc((1lh - var(--icon-size)) / 2);
}

.notice-detail { margin: 0; color: var(--text-2); font-size: var(--fs-secondary); }
.notice-note { margin: var(--space-8) 0 0; color: var(--text-muted); font-size: var(--fs-secondary); }

.notice-refused, .notice-notfound, .notice-turnedaway, .notice-badaddress {
    border-color: var(--danger-hairline);
    border-left-color: var(--danger);
    background: var(--danger-tint);
}

.notice-refused h2, .notice-notfound h2, .notice-turnedaway h2, .notice-badaddress h2 { color: var(--danger-text); }

/* It happened, and is worth a second look. The ONLY tone on this ramp. */
.notice-warn {
    border-color: var(--warning-hairline);
    border-left-color: var(--warning);
    background: var(--warning-tint);
}

.notice-warn h2 { color: var(--warning-text); }

/* Nobody can tell, or we could not ask — including the three screens that now ask on arrival and
   can say PropMan is unreachable BEFORE anyone types a sort code. */
.notice-unsent, .notice-broke, .notice-unauthenticated, .notice-unreachable {
    border-color: var(--fault-hairline);
    border-left-color: var(--fault);
    background: var(--fault-tint);
}

.notice-unsent h2, .notice-broke h2, .notice-unauthenticated h2, .notice-unreachable h2 { color: var(--fault-text); }

/* It happened as asked. Its own green, never an area's hue: an outcome is not tinted by where the reader is,
   and in the accent it read as Records' chrome. */
.notice-ok {
    border-color: var(--done-hairline);
    border-left-color: var(--done);
    background: var(--done-tint);
}

.notice-ok h2 { color: var(--done-text); }

/* THE ONE WAY ON, inside the notice: a base-tier button, or a primary where it is the screen's only act —
   Try again (the same address, no script), Open Brand House, Back to Statements. Every failure ends with one.
   Where a retry could make a record twice, the notice's heading is the instruction and this is the safe step. */
.notice-actions { display: flex; align-items: center; gap: var(--space-16); flex-wrap: wrap; margin: var(--space-16) 0 0; }

/* THE SUMMARY AT THE TOP OF A REFUSED FORM. Each entry links to the field it names, so a reader
   who lands at the top of the document after a POST has one press to reach the box they must
   change. It is also where a refusal too long for the field's own line is printed in full. */
.error-summary ol { margin: var(--space-12) 0 0; padding-left: var(--space-20); font-size: var(--fs-secondary); }
.error-summary li { margin-bottom: var(--space-6); }
.error-summary a { color: var(--danger-text); }

/* THE ACCESS BANNER sits with the read it is about — beside the part of the page that was refused — not above
   everything. A page with several reads may lose one and keep the rest. */
.banner {
    border: 1px solid var(--warning-hairline);
    background: var(--warning-tint);
    border-radius: var(--radius-inset);
    padding: var(--space-16);
    margin: 0 0 var(--space-20);
    display: flex;
    gap: var(--space-12);
    align-items: flex-start;
}

/* CAPPED LIKE ANY OTHER PROSE HERE: the banner spans the read it is about, which on a wide screen is the
   whole panel, and its sentence would run that whole width. The words are capped, not the box. */
.banner p { margin: 0; font-size: var(--fs-secondary); color: var(--text-2); max-width: var(--measure-prose); }
.banner .icon { margin-top: 2px; color: var(--warning-text); }

/* A BASE BUTTON ON A TINTED GROUND TAKES A STRONGER EDGE. Its ground is surface, and against a tint that is
   barely a shade, so the 1px edge carries the separation on its own — and border-control is measured against
   surface, where it has margin, not against a tint, where it has almost none. The token's own note carries the
   figures. Every notice drawn here has a tone, and so a tint (the Banner page lists them); the primary keeps
   solid's edge, because its fill is the cue. The quiet tier is left out because its edge is a hover state: a
   quiet button on a tint would want this ink under the pointer, and the rule to add then is its :hover. */
:where(.notice, .banner, .confirm-box) :where(button, .button):not(.primary):not(.quiet) {
    border-color: var(--border-on-tint);
}

/* --- Empty ---------------------------------------------------------------- */

/* AN EMPTY ANSWER IS A MOMENT YOU LOOK AT, NOT ONE YOU READ DOWN, so its words are centred: the heading,
   the line under it and any one link in that line. The block itself keeps its place and its width —
   the whole-screen panel still spans the container from the screen's left edge — and the title above
   it stays at that edge. The line is held to the prose measure, so a long one centres as a short column
   rather than a wide one. */
.empty, .empty-screen { text-align: center; }
.empty { padding: var(--space-16) 0 var(--space-4); }
.empty-heading { margin: 0 0 var(--space-6); font-weight: 600; color: var(--text); }
.empty-note { margin: 0 auto; max-width: var(--measure-prose); font-size: var(--fs-secondary); color: var(--text-muted); }

/* A picker's empty answer is not a moment of its own: it is part of a form the reader is filling in,
   under the search they just typed, so it stays at the form's left edge. */
.picker .empty { text-align: start; }
.picker .empty-note { margin-inline: 0; }

.empty-screen {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-card);
    box-shadow: var(--shadow-card);
    padding: var(--space-32) var(--space-24);
    margin: var(--space-4) 0 var(--space-8);
}

.empty-screen .empty-heading { font-size: var(--fs-panel-head); }

/* --- Standalone moments, and the signed-out card ------------------------- */

/* A FAILURE THAT IS THE SCREEN'S WHOLE ANSWER — an error page, a refused read, PropMan out of reach — sits under
   the title, on its left edge, like every notice. It was centred in the container, and the eye had to travel
   from the title to a box that looked detached from it. The owner's ruling, 2026-09-18. What stays centred:
   the sign-in card, and an empty answer's words inside its panel. */
.moment { display: flex; flex-direction: column; align-items: flex-start; margin: 0; }
.moment > .notice { width: 100%; }
.moment > .empty-screen { width: 100%; }

/* THE SIGNED-OUT PAGE: one card, centred across the window, with no navigation. The card's brand, heading and
   the line under the form centre, a moment's words; the form itself is read down, so its label, box and
   refusal stay left. The card's top sits at a fixed height rather than in the window's middle, so a refusal
   that makes it taller grows it downward and the key's box does not jump. */
.auth-page { min-height: 100vh; display: grid; justify-items: center; align-content: start; padding: max(var(--page-padding), 12vh) var(--page-padding) var(--page-padding); }
.sign-in-card { width: min(26rem, 100%); background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-card); box-shadow: var(--shadow-card); padding: var(--space-32); display: flex; flex-direction: column; gap: var(--space-20); }
.sign-in-card .brand { justify-content: center; padding: 0; gap: 14px; font-size: 28px; }
.sign-in-card .brand-mark { width: 28px; height: 28px; }
.sign-in-card h1 { text-align: center; }
.sign-in-card > .note { margin: 0; text-align: center; }
.sign-in-card .notice { margin: 0; }
.sign-in-card .field { max-width: none; }
.sign-in-form { display: flex; flex-direction: column; gap: var(--space-16); margin: 0; }
.sign-in-form > button.primary { width: 100%; justify-content: center; }
.sign-in-foot { display: flex; justify-content: center; border-top: 1px solid var(--border-subtle); padding-top: var(--space-16); }

/* --- Tables --------------------------------------------------------------- */

.pm table { border-collapse: collapse; width: 100%; margin: var(--space-4) 0 var(--space-8); font-size: var(--fs-body); }
/* A caption sits a hair in from the table's clipped edge, so its first glyph's overhang is not shaved off. */
.pm caption { text-align: left; color: var(--text-muted); font-size: var(--fs-secondary); padding: 0 0 var(--space-12) var(--space-2); }

/* THE TINTED HEADER BAND, THE HAIRLINE UNDER EACH ROW, THE HEAVIER RULE ABOVE THE TOTALS. Kept
   exactly as they were — the owner looked at the alternatives and kept this. */
.pm thead th {
    font-size: var(--fs-col-head);
    font-weight: 600;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--text-muted);
    background: var(--surface-subtle);
    border-bottom: 1px solid var(--border-subtle);
    white-space: nowrap;
}

.pm th, .pm td {
    text-align: left;
    padding: var(--row-height-comfortable) var(--space-16);
    border-bottom: 1px solid var(--border-hair);
    vertical-align: top;
}

.pm tbody tr:last-child > * { border-bottom: none; }

/* A REGISTER'S COLUMNS HOLD THEIR PLACE between the states of one screen. A register declares a width, in a
   colgroup, for every column whose content has a fixed shape — a mark, a figure, a count, a date — and leaves
   the one column holding a name without a width. All the spare width goes to that column, so a filter, a
   caller or a refusal never moves a column. The layout stays automatic, on purpose: where the container is
   narrower than the declared widths, the columns shrink to their content rather than squeeze the named column
   to nothing, which a fixed layout does. Classes, not style attributes: the app's policy refuses inline
   styles. */
col.w-4 { width: 4rem; } col.w-6 { width: 6rem; } col.w-7 { width: 7rem; } col.w-8 { width: 8rem; }
col.w-9 { width: 9rem; } col.w-10 { width: 10rem; } col.w-12 { width: 12rem; } col.w-14 { width: 14rem; }
col.w-16 { width: 16rem; } col.w-20 { width: 20rem; }

/* THE READER CHOOSES THE DENSITY and it is remembered. Compact is roughly the density the app
   ships today; comfortable is the new default. The mechanism is ThemeChoice's exactly: a form
   post writes a cookie, the absence of the cookie IS the default, and the server puts the
   attribute on the root element while the document is written. See the Table page. */
[data-density="compact"] th, [data-density="compact"] td {
    padding: var(--row-height-compact) var(--space-12);
}

[data-density="compact"] .row-actions .button,
[data-density="compact"] .row-actions button { height: 22px; }

.register, .figures {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-card);
    box-shadow: var(--shadow-card);
    overflow: hidden;
}

.figures { max-width: 34rem; }

/* 2px, and the WIDTH is the point rather than the weight: under border-collapse two 1px borders
   on a shared edge are resolved by rules that hand it to the row above, so a 1px totals rule is
   simply not painted. */
.figures tr.rule > * { border-top: 2px solid var(--border-strong); }
.figures tr.payout > * { font-weight: 600; color: var(--text); }

/* A ROW WHERE TWO FIGURES READ ACROSS DO NOT AGREE — printed against paid — is marked in both stacks: the
   danger tint, and a rule down its label's edge. The words say so too, hidden beside the label. */
.figures tr.differs > * { background: var(--danger-tint); }
.figures tr.differs > th:first-child { box-shadow: inset 3px 0 0 var(--danger); }
.figures th { font-weight: 400; color: var(--text-2); }

/* Right-aligned in any column, which is the half of the money rule a CELL owns. */
.pm td.figure, .pm th.figure { text-align: right; }

/* How big the register is. NO PUBLISH STAMP HERE: a register printing when it was read is the
   screen explaining where a figure came from, which the brand book forbids. The count is a sentence —
   "6 supplier bills." — so its figures are proportional. See .published. */
.tally {
    margin: var(--space-12) 0 var(--space-16);
    color: var(--text-muted);
    font-size: var(--fs-secondary);
    font-variant-numeric: proportional-nums;
    line-height: 1.5;
}

/* Forward-only keyset paging: a cursor names a position, not an offset, so there is no page
   number to draw and no "previous". Both are anchors wearing button styling. */
.pager { display: flex; gap: var(--space-8); margin: var(--space-16) 0 0; }

/* --- Chips and marks ------------------------------------------------------ */

/* LOUD ONLY WHEN IT NEEDS ATTENTION. On a register of 200 rows the eye should go to the
   exceptions, so a state the reader may have to act on is FILLED and an ordinary one is
   OUTLINED. The words are the same either way; the fill is what ranks them. */
.tag {
    display: inline-flex;
    align-items: center;
    gap: var(--space-4);
    padding: 1px var(--space-8);
    border: 1px solid var(--border-mark);
    border-radius: var(--radius-badge);
    font-size: var(--fs-caption);
    color: var(--text-muted);
    background: var(--surface-subtle);
    text-decoration: none;
    white-space: nowrap;
}

.tag .icon { width: 12px; height: 12px; }

/* Outlined, on the amber ramp: an ordinary state that is still worth a glance. */
.tag-warn { color: var(--warning-text); border-color: var(--warning-hairline); background: var(--warning-tint); }

/* Filled — the exception a reader may have to act on. */
.tag-loud { background: var(--warning); border-color: var(--warning); color: var(--on-warning); font-weight: 500; }
.tag-loud-danger { background: var(--danger); border-color: var(--danger); color: var(--on-danger); font-weight: 500; }

/* The section 6.2 arrears band: an ORDINAL scale, encoded by a pip count, a widening rule and a
   colour. THE GEOMETRY IS NOT NEGOTIABLE: an icon cannot carry rank, so the count and the width are the
   order. THE SCALE'S GLYPH, pound, IS DRAWN ONCE PER SCALE — on a dashboard panel, on each group's header,
   at the head of a column or a legend — and never on every row: it named the same thing on every line and
   ranked nothing. A row carries the rule and the pips. */
.band-mark { display: inline-flex; align-items: center; gap: var(--space-8); --mark-ink: var(--band-ink, var(--text)); }
.band-pips { display: inline-flex; align-items: center; gap: 2.5px; }
.pip { width: 6px; height: 12px; border-radius: 1px; background: var(--band-pip-off); }
.pip-on { background: var(--band-ink, var(--text)); }
.pip-over { margin-left: 2px; }
.band-rule { width: var(--band-rule-w, 7px); height: 22px; border-radius: 2px; background: var(--band-ink, var(--text)); }

.band-0 { --band-ink: var(--band-0); --band-rule-w: 3px; }
.band-1 { --band-ink: var(--band-1); --band-rule-w: 3px; }
.band-2 { --band-ink: var(--band-2); --band-rule-w: 4px; }
.band-3 { --band-ink: var(--band-3); --band-rule-w: 5px; }
.band-4 { --band-ink: var(--band-4); --band-rule-w: 6px; }
.band-5 { --band-ink: var(--band-5); --band-rule-w: 7px; }

.band-mono.band-0 { --band-ink: var(--band-mono-0); }
.band-mono.band-1 { --band-ink: var(--band-mono-1); }
.band-mono.band-2 { --band-ink: var(--band-mono-2); }
.band-mono.band-3 { --band-ink: var(--band-mono-3); }
.band-mono.band-4 { --band-ink: var(--band-mono-4); }
.band-mono.band-5 { --band-ink: var(--band-mono-5); }

.band-unknown .band-pips, .band-unknown .band-rule, .band-unknown .icon { display: none; }
.band-name { color: var(--text-2); }
.band-tenancies { color: var(--text-muted); font-size: var(--fs-caption); }

/* The section 6.3 compliance scale: the shape, the shape's SIZE, the colour — and now a ranked
   icon. Three steps is few enough that an icon set CAN be ordered by convention, which is why
   this scale gains ranked icons and the six-step band scale does not. */
.comp-mark { display: inline-flex; align-items: center; gap: var(--space-8); vertical-align: middle; --mark-ink: var(--comp-ink, var(--text)); }
.comp-shape { display: inline-block; flex: none; width: var(--comp-size, 9px); height: var(--comp-size, 9px); background: var(--comp-ink, var(--text)); }
.comp-0 { --comp-ink: var(--comp-held); --comp-size: 6px; }
.comp-1 { --comp-ink: var(--warning); --comp-size: 8px; }
.comp-2 { --comp-ink: var(--danger); --comp-size: 9px; }
.comp-0 .comp-shape { clip-path: circle(50%); }
.comp-1 .comp-shape { clip-path: polygon(50% 0%, 100% 100%, 0% 100%); }
.comp-2 .comp-shape { transform: rotate(45deg); }
.comp-unknown .comp-shape, .comp-unknown .icon { display: none; }
.comp-name { color: var(--text-2); }

/* A SCALE: marks one to a line, worst first where the scale is ordinal — a dashboard panel's bands, a
   legend. ONE GRID FOR EVERY LINE — mark, label, figure — so the labels start at one edge and the figures
   end at one edge, however narrow the panel. Each line is a subgrid of the list, which keeps it a list item
   to a screen reader. A second fact about a line (a band's tenancy count) sits under its label, in
   scale-label. A figure or a count that belongs to the line sits in the last column, in at-end. */
.scale { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: auto minmax(0, 1fr) auto; column-gap: var(--space-12); row-gap: var(--space-12); font-size: var(--fs-secondary); }
.scale li { display: grid; grid-column: 1 / -1; grid-template-columns: subgrid; align-items: center; }
.scale .at-end { grid-column: -2 / -1; text-align: right; }

/* A FIGURE AT A SCALE LINE'S END IS READ AGAINST THE FIGURES ABOVE AND BELOW IT — a column — so it keeps the
   column's form, which the prose rule above takes away from a figure inside any li. That rule uses li as the
   mark of a line of words, and a scale line is the one list item that is not one: its figures end on one edge
   and have to line up. A count already keeps the column's form, since the prose rule never named it. */
.scale .money.at-end, .scale .stamp.at-end {
    font-family: var(--font-figures);
    font-size: var(--fs-secondary);
    font-variant-numeric: tabular-nums;
}
.scale-label { display: flex; flex-direction: column; min-width: 0; line-height: 1.35; }

/* A statement line's state: FOUR GLYPHS, each its own shape — a page on the document (doc-filled), an eye
   struck through (eye-off), a bin (bin), a padlock (lock) — in a column headed State. The four box shapes that
   carried it before are gone: the filled square for a line on the document read as a ticked checkbox in
   light and an empty one in dark, which invited a click. The glyph is reinforcement; which money column a
   line's figure sits in is what tells hidden from struck out. */
.line-mark { display: inline-flex; align-items: center; color: var(--text-2); }
.line-mark-removed, .line-mark-locked { color: var(--text-muted); }

/* A property's shape: three-way and ALWAYS three-way, with one of them marked. */
.kindmark { display: inline-flex; border: 1px solid var(--border-mark); border-radius: var(--radius-chip); overflow: hidden; vertical-align: middle; }
.kindmark .seg { display: inline-flex; align-items: center; gap: var(--space-4); padding: var(--space-4) var(--space-8); font-size: var(--fs-caption); color: var(--text-faint); background: var(--surface); border-right: 1px solid var(--border-subtle); }
.kindmark .seg:last-child { border-right: none; }
/* The marked segment takes the one selection, above. */

/* A nominal legend: which rows on this block share an owner. Its four shapes ARE its icons and
   it gains no new one — a glyph added beside them would be a fifth thing to tell apart. */
.owner-chip { display: inline-block; width: 9px; height: 9px; vertical-align: middle; margin-right: var(--space-6); background: var(--text-faint); }
.owner-0 { background: var(--accent); border-radius: 2px; }
.owner-1 { background: var(--owner-green); border-radius: 50%; }
.owner-2 { background: var(--warning); transform: rotate(45deg); }
.owner-3 { background: var(--text-2); border-radius: 2px / 50%; }
.owner-4 { background: var(--text-faint); border-radius: 1px; width: 4px; height: 12px; }

/* --- The calendar --------------------------------------------------------- */

/* Script, as an enhancement only (the brand book's Script section). The native date input remains the
   source of truth and works with the script absent; this panel replaces the browser's own picker when it
   runs. */
.calendar {
    width: 268px;
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-card);
    box-shadow: var(--shadow-raised);
    padding: var(--space-12);
}

.calendar-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-8);
    margin-bottom: var(--space-8);
}

.calendar-month { font-size: var(--fs-secondary); font-weight: 600; }

.calendar-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }

.calendar-grid .dow {
    font-size: var(--fs-col-head);
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--text-muted);
    font-weight: 600;
    text-align: center;
    padding-bottom: var(--space-4);
}

.pm .calendar-day {
    height: 32px;
    width: 100%;
    padding: 0;
    border: 1px solid transparent;
    border-radius: var(--radius-chip);
    background: transparent;
    color: var(--text);
    font-family: var(--font-figures);
    font-size: var(--fs-secondary);
    font-variant-numeric: tabular-nums;
    justify-content: center;
}

/* ONLY A DAY THAT CAN BE CHOSEN TAKES A HOVER. The :not() is load-bearing, not tidiness: this rule and the
   unavailable rule below each name two classes, and this one adds :hover on top, so unqualified it outranked
   the unavailable day's own transparent ground and edge. An unavailable day then lit up under the pointer
   exactly as a choosable one does — a control's answer from something that is not a control. */
.pm .calendar-day:not(.unavailable):hover { background: var(--surface-inset); border-color: var(--border-control); }
.calendar-day.other-month { color: var(--text-faint); }

/* TODAY is ringed, not filled: filling it would compete with the day that is chosen. */
.pm .calendar-day.today { border-color: var(--area); font-weight: 600; }

/* The chosen day takes the one selection, above: outlined in the ink. Today keeps its thinner ring in the area's
   ink, so the two are told apart by weight and colour of ring, and the chosen day by its words' weight too.

   IT SAYS SO WITH aria-current="date", which is also what the hook above selects on. A day is a <button>, and
   a button does not support aria-selected: the attribute announces nothing there, which is the same reason
   aria-expanded came off the date box. aria-current is what the lit navigation row and the current tab use. */

/* A DAY THAT CANNOT BE CHOSEN IS NOT A DISABLED CONTROL. A grid cannot leave a cell out without
   breaking the week, so the day is still NAMED — and it is drawn as plain text with no border,
   no hover and no focus stop, which is this system's "absent" one step down: the day is there,
   the control is not. */
.calendar-day.unavailable {
    color: var(--text-faint);
    background: transparent;
    border-color: transparent;
    cursor: default;
    display: grid;
    place-items: center;
    height: 32px;
    font-family: var(--font-figures);
    font-size: var(--fs-secondary);
}

.calendar-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-8);
    margin-top: var(--space-8);
    padding-top: var(--space-8);
    border-top: 1px solid var(--border-hair);
}

/* --- The collapsed aside --------------------------------------------------- */

details.aside {
    border: 1px solid var(--border);
    border-radius: var(--radius-card);
    background: var(--surface);
    box-shadow: var(--shadow-card);
    margin: var(--space-20) 0;
    font-size: var(--fs-body);
}

/* EVERY DISCLOSURE SHOWS THAT IT OPENS, AND ALL OF THEM LOOK ALIKE. The summary ends with the disclose glyph,
   a chevron, which turns over when it is open — an aside, a more, the rest of a refusal and the name menu.
   The aside's summary is a flex row, which takes the browser's own triangle away, so without the glyph it was
   a white card with nothing saying it could be pressed. */
details.aside > summary { padding: var(--space-16) var(--space-20); cursor: pointer; font-weight: 600; color: var(--text-2); display: flex; align-items: center; gap: var(--space-8); list-style: none; }
details.aside > summary::-webkit-details-marker { display: none; }
details.aside > summary > h2 { display: inline; margin: 0; font-size: inherit; font-weight: inherit; color: inherit; }
details.aside > summary:hover { color: var(--text); }
details.aside[open] > summary { border-bottom: 1px solid var(--border-subtle); }
details.aside > summary > .icon:last-child, details.aside > summary > [component-from-global-scope="PropMan.Icon"]:last-child { margin-left: auto; color: var(--text-muted); }
details[open] > summary > .icon:last-child, details[open] > summary > [component-from-global-scope="PropMan.Icon"]:last-child { transform: rotate(180deg); }
.more > summary > .icon:last-child, .field-error-more > summary > .icon:last-child { width: 14px; height: 14px; }
.aside-body { padding: var(--space-16) var(--space-20) var(--space-20); }

/* --- Navigation ------------------------------------------------------------ */

/* FOLDED IN FROM 2b'S CANVAS, where the owner approved it. The declarations are 2b's, unchanged, with
   one exception, .nav-count, stated where it occurs, and one addition: in forced colours the lit row
   carries an underline, because its tinted ground is taken away there. What IS changed is the selectors: 2b had to prefix several
   of them with .pm to out-shout the old list-link rule, and with that rule fixed at source the prefixes
   are gone — the same rules now win at the specificity they were written for. */

/* The shell: the sidebar beside the content column. */
.shell { display: flex; height: 100%; }
.content { flex: 1; display: flex; flex-direction: column; min-width: 0; }

/* THE PAGE'S CONTAINER: the screen, the <main> under the top bar. It is at most content-max wide plus
   its own gutters, and it is centred in the column once the column is wider than that. Below that width
   it fills the column, so the gutters are unchanged and a phone never reaches it. NOTHING INSIDE IS
   CENTRED: every screen keeps one left edge, and a register takes the container's full width. The top
   bar is not in it and still spans the column. The gutters are page-padding, which is what both the app
   and the canvases give the screen; this rule sets no padding of its own. */
.content > main {
    width: 100%;
    max-width: calc(var(--content-max) + 2 * var(--page-padding));
    margin-inline: auto;
}

/* The sidebar. Search, Today, then the areas with a row per section — and nothing else. */
.side { width: var(--sidebar-width); flex: 0 0 var(--sidebar-width); background: var(--surface); border-right: 1px solid var(--border); display: flex; flex-direction: column; min-height: 0; }
/* THE SWING MARK AND THE WORD "Semvo", both in the brand's colour (DEC-0350 ruling 5): the mark an inline SVG filled
   with currentColor, its box the word's size and half that between them. Brand blue is for these two alone; the main
   action stays the solid colour. */
.brand { display: flex; align-items: center; gap: 10px; padding: 18px 18px 16px; font-weight: 700; font-size: 20px; line-height: 1; letter-spacing: -0.01em; color: var(--brand); text-decoration: none; }
.brand-mark { width: 20px; height: 20px; flex: none; }
.side-search { padding: 0 var(--space-16); margin: 0 0 var(--space-8); flex-wrap: nowrap; }
.side-search .search-field { flex: 1 1 0; }
.side-search button { flex: none; }
.nav { padding: var(--space-4) var(--space-12) var(--space-12); display: flex; flex-direction: column; gap: var(--space-12); }
/* A LIST DRAWN WITHOUT MARKERS CARRIES role="list" IN ITS MARKUP. Safari takes list-style: none as a
   reason to stop announcing a list, and "list, five items" is how a screen reader knows how long an
   area is. The same holds for the trail, the stepper and the picker's results. */
.nav-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0; }

/* An area's heading, with a short bar in the area's own ink. It is a real heading — an <h2> — so a
   screen reader can move from area to area by heading, and the list under it takes its name from it.
   The .pm prefix is what lets it out-rank the base rule for an h2. */
.pm .nav-head { display: flex; align-items: center; gap: var(--space-8); margin: 0 0 var(--space-4); padding: 0 var(--space-8); font-size: var(--fs-col-head); letter-spacing: .06em; text-transform: uppercase; font-weight: 600; color: var(--text-muted); }
.nav-head::before { content: ""; width: 3px; height: 12px; border-radius: 2px; background: var(--area); }

/* One section. The row lit is the section the screen sits in, read from its place on the route map,
   and it carries aria-current as well as the class: which row is lit is information. */
.nav-row { display: flex; align-items: center; gap: var(--space-8); min-height: 30px; padding: 0 var(--space-8); border-radius: var(--radius-chip); font-size: var(--fs-secondary); color: var(--text-2); text-decoration: none; }
.nav-row .icon { color: var(--text-muted); }
.nav-row:hover { background: var(--surface-inset); color: var(--text); }
.nav-row.here { background: var(--area-tint); color: var(--area-text); font-weight: 500; }
.nav-row.here .icon { color: var(--area-text); }

/* THE SIZE OF THE REGISTER THE READER IS ON, at the end of the lit row. The app draws this today and
   2b's canvas styled a class for it without drawing one, so it is carried here to keep the function.
   THE ONE EXCEPTION to "2b's declarations, unchanged": 2b's stylesheet set it in the mono face. It is a
   count beside a label, which this system sets as words — the interface face, proportional figures —
   and it keeps the app's dimming, which the forced-colours block below takes away again. */
.nav-count { margin-left: auto; font-size: var(--fs-caption); font-weight: 400; font-variant-numeric: proportional-nums; opacity: .75; }

/* With nothing open, the sentence the app already has stands where the rows were. */
.none-open { margin: var(--space-4) var(--space-8); font-size: var(--fs-secondary); color: var(--text-muted); max-width: 22ch; }

.side-foot { margin-top: auto; border-top: 1px solid var(--border); padding: var(--space-12) var(--space-16); display: flex; flex-direction: column; gap: var(--space-8); }
.side-foot form { margin: 0; }
.side-foot form > button:only-child { width: 100%; justify-content: center; }
.about-link { font-size: var(--fs-secondary); color: var(--text-2); text-decoration: underline; text-underline-offset: 2px; }

/* Who is signed in. */
.caller { display: flex; flex-direction: column; gap: var(--space-2); font-size: var(--fs-secondary); }
.caller-name { color: var(--text); }
.caller-email, .caller-firm { color: var(--text-muted); }

/* The top bar: the trail on the left, the caller's menu on the right, both on the edges of the page's
   container. Its insets follow the container's: page-padding until the column is wider than content-max
   and its gutters, then half of what is spare. So the trail sits over the title it describes at every
   width, and the bar itself still spans the column. */
.topbar { height: var(--topbar-height); flex: 0 0 var(--topbar-height); background: var(--surface); border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: var(--space-24); padding: 0 max(var(--page-padding), calc((100% - var(--content-max)) / 2)); box-sizing: border-box; }

/* RETIRED: the caller block, Sign out and the theme drawn inline in the bar. Nothing here draws them any
   more; the rules stay so a canvas that re-syncs before it redraws its top bar is not left unstyled.
   Remove them once no board draws .who. */
.who { margin-left: auto; display: flex; align-items: center; gap: var(--space-12); }
.who form { margin: 0; }
.who .caller { flex-direction: row; align-items: baseline; gap: var(--space-8); }

/* WHO YOU ARE, AS A MENU. The caller's name opens a disclosure — details and summary, the mechanism the
   phone's section menu uses, so no script — holding the email, the firm, the theme, the row density and
   Sign out. Theme and density are preferences changed rarely, so they sit one press away rather than on
   every screen.

   IT IS A DISCLOSURE, NOT AN ARIA MENU. An ARIA menu promises arrow keys, focus moved into it on open and
   Escape to close, all of which need script. What is here is what a keyboard and a screen reader expect of
   a disclosure: the name is a button that says whether it is expanded, and the controls inside are ordinary
   ones in tab order after it. Every control in it posts, and the page comes back with it closed. */
.who-menu { position: relative; margin-left: auto; }
.topbar .who-menu { margin-right: calc(-1 * var(--space-8)); }
.who-menu > summary { list-style: none; display: inline-flex; align-items: center; gap: var(--space-6); height: var(--control-height); padding: 0 var(--space-8); border-radius: var(--radius-control); font-size: var(--fs-secondary); font-weight: 500; color: var(--text); cursor: pointer; white-space: nowrap; }
.who-menu > summary::-webkit-details-marker { display: none; }
.who-menu > summary:hover { background: var(--surface-inset); }
.who-menu > summary .icon { color: var(--text-muted); }
.who-menu[open] > summary .icon { transform: rotate(180deg); }
.who-panel { position: absolute; right: var(--space-8); top: calc(100% + var(--space-6)); z-index: 3; width: max-content; min-width: 16rem; max-width: calc(100vw - 2 * var(--space-16)); background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-card); box-shadow: var(--shadow-raised); padding: var(--space-16); display: flex; flex-direction: column; gap: var(--space-16); }
.who-panel form { margin: 0; }
.who-panel .field-label { margin-bottom: var(--space-6); }
.who-sign-out { border-top: 1px solid var(--border-subtle); padding-top: var(--space-16); }
.who-sign-out button { width: 100%; justify-content: center; }

/* On a phone the same menu sits at the foot of the sections panel, where 2b put the caller, so it opens
   in place rather than over the page. Its summary is a 44px touch target like every row there. */
.ph-panel .who-menu { margin-left: 0; }
.ph-panel .who-menu > summary { height: var(--touch-height); padding: 0; font-size: var(--fs-body); }
/* On a phone every control in the menu is a touch target, 44px high. */
.ph-panel .toggle button, .ph-panel .who-sign-out button, .ph-panel .side-search button, .ph-panel .side-search input { height: var(--touch-height); }
.ph-panel .who-panel { position: static; width: auto; min-width: 0; max-width: none; border: 0; border-radius: 0; box-shadow: none; padding: var(--space-8) 0 0; }

/* The trail. Each place above this screen is a link, the section in its area's ink. A section this
   caller cannot open is words, not a link — the trail names it without offering it. */
.crumbs ol { list-style: none; margin: 0; padding: 0; display: flex; align-items: center; gap: var(--space-8); font-size: var(--fs-secondary); color: var(--text-muted); }
.crumbs a { color: var(--text-muted); text-decoration: none; }
.crumbs a:hover { text-decoration: underline; }
.crumbs .crumb-section a, .crumbs .crumb-section span { color: var(--area-text); font-weight: 500; }
.crumbs .crumb-closed span { color: var(--text-muted); }
.crumbs .sep { color: var(--text-faint); }

/* On a phone: the brand, and the sections behind a details/summary disclosure — no script. The trail
   becomes one back link. */
.ph-top { position: relative; display: flex; align-items: center; justify-content: space-between; height: 60px; padding: 0 var(--space-12) 0 0; background: var(--surface); border-bottom: 1px solid var(--border); box-sizing: border-box; }
.ph-menu { position: static; }
.ph-menu > summary { list-style: none; display: inline-flex; align-items: center; gap: var(--space-8); height: var(--touch-height); padding: 0 var(--space-12); border: 1px solid var(--border-control); border-radius: var(--radius-control); background: var(--surface); font-size: var(--fs-secondary); color: var(--text-2); cursor: pointer; }
.ph-menu > summary::-webkit-details-marker { display: none; }
.ph-menu[open] .when-closed, .ph-menu:not([open]) .when-open { display: none; }
.ph-menu[open] > summary .icon { transform: rotate(180deg); }
.ph-panel { position: absolute; left: 0; right: 0; top: 100%; z-index: 2; background: var(--surface); border-bottom: 1px solid var(--border); box-shadow: var(--shadow-raised); padding-top: var(--space-12); }
/* THE PANEL IS AS WIDE AS THE WINDOW AND ITS CONTENTS ARE NOT. This shape holds up to 900px, not only on a
   phone, and at that width a row running the whole way to the edge with its count a hand's width from its
   name reads as a stretched bar rather than a list. The contents keep a column a little wider than a phone,
   so nothing on a phone moves. */
.ph-panel > * { max-width: 26rem; }
.ph-panel .side-search { padding: 0 var(--space-16); }
.ph-panel .side-foot { margin-top: var(--space-8); padding-bottom: var(--space-16); }
.ph-panel .who { margin: 0; flex-wrap: wrap; }
.ph-panel .who .caller { flex-direction: column; flex-basis: 100%; }
.ph-row { min-height: var(--touch-height); font-size: var(--fs-body); }
/* THREE BANDS, NOT TWO. The narrow shape runs up to the layout switch, which the brand book's width section
   states once, as a minimum: the sidebar shell is what answers at and above it, and the narrow shell is what
   answers below — so a consumer writing the pair writes the minimum, never a maximum that would let both
   shells answer on the line itself. Inside the narrow shape a phone's 16px gutter is right only as far as a
   phone: above that the screen is wide enough for the page padding the sidebar shell gives it, so a register
   does not sit 16px from the window's edge at 899 and 28px at 900. */
.ph-screen { padding: var(--space-16); }
@media (min-width: 40rem) { .ph-screen { padding: var(--page-padding); } }
.ph-back { margin: 0 0 var(--space-12); font-size: var(--fs-secondary); }
.ph-back a { display: inline-flex; align-items: center; gap: var(--space-4); min-height: var(--touch-height); color: var(--text-2); text-decoration: none; font-weight: 500; }

/* --- Print ------------------------------------------------------------------ */

/* The app's own print rules, carried into the system. The palette half lives in the compiled tokens,
   which force the print values whatever is on screen; this is the component half.

   THE CHROME IS NOT PART OF A DOCUMENT: the navigation, the top bar, the phone header, the pager and
   every form go. A confirm block sits inside a form on every screen that draws one, so it goes too
   without being named.

   A PIP, A RULE AND A COMPLIANCE SHAPE ARE THE ENCODING, and each is a filled box — so a browser that
   drops backgrounds when printing would print the scale away. The ink is asked for by name, and a pip
   also carries a border in its own ink, because asking is a request a browser may refuse. On paper the
   arrears ramp is drawn from the neutral tokens, so the order reads off the ink as well as the form. */
@media print {
    .side, .topbar, .ph-top, .pager, .skip, form { display: none; }
    /* A fixed element prints on every page, and a session that ended is not part of a document. */
    .modal-scrim { display: none; }
    .panel, .register, .figures { border-radius: 0; box-shadow: none; }
    tr { break-inside: avoid; }

    /* Paper sets its own width, and a document is not centred on it: the screen's container lets go. */
    .content > main { max-width: none; margin-inline: 0; }

    /* The row of tabs goes; the one in force stays, as words, so a filtered table is not printed as
       though it were the whole thing. */
    .tabs a { display: none; }

    .band-0 { --band-ink: var(--band-mono-0); }
    .band-1 { --band-ink: var(--band-mono-1); }
    .band-2 { --band-ink: var(--band-mono-2); }
    .band-3 { --band-ink: var(--band-mono-3); }
    .band-4 { --band-ink: var(--band-mono-4); }
    .band-5 { --band-ink: var(--band-mono-5); }

    .pip, .band-rule, .comp-shape { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
    .pip { border: 1px solid var(--band-pip-off); }
    /* The landlord row keeps no height of its own on paper: its form goes with the others, and a chip stays. */
    .filter-row { min-height: 0; margin: 0; }
    .pip-on { border-color: var(--band-ink, var(--text)); }
}

/* --- Forced colours -------------------------------------------------------- */

/* WHAT THIS MODE DOES, AS A BROWSER DOES IT. A reader in a high-contrast theme has every colour the
   author names replaced by a system one — EXCEPT a system colour named outright, which the browser
   keeps. The compiled tokens lean on that: in this mode each token is itself a system colour, so a
   fill named through a token SURVIVES. A filled pip stays CanvasText, and the count reads. The rules
   below restate the marks' grounds by name all the same.

   THE SAME RULE IS A TRAP FOR A LABEL ON A FILL. The browser also paints a Canvas backplate behind
   every run of text. A label whose ink resolves to Canvas or ButtonFace — the on- inks — sits on
   that backplate in the backplate's own colour, and vanishes, while the fill it was meant to stand
   out from stays. So a fill is never removed here by the mode; where it carries a label it is
   removed on purpose, below.

   forced-color-adjust is deliberately not used — the point is to keep working WITH the substitution
   rather than to opt out of it.

   THE ICONS NEED NOTHING HERE. They are strokes in currentColor, not text, so no backplate is painted
   behind them; what needs restating is everything whose substance is a BACKGROUND. */
@media (forced-colors: active) {
    .pip { background: Canvas; border: 1px solid CanvasText; }
    .pip-on { background: CanvasText; }
    .band-rule { background: CanvasText; }
    .comp-shape { background: CanvasText; }
    .combobox-option[aria-selected="true"] { text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: 3px; }
    .tab-count, .nav-count { opacity: 1; }
    /* A SELECTION IS UNDERLINED HERE AS WELL. The lit row's tint resolves to Canvas, and the mode draws every
       border in one ink and drops box-shadow, so an outlined selection keeps only a 1px edge like its
       neighbours'. An underline carries it: it moves nothing, and it does not fight the focus outline. A
       differing statement row keeps its words, which are hidden beside its label. */
    .figures tr.differs > th:first-child { border-left: 3px solid CanvasText; }
    .nav-row.here, .toggle button[aria-pressed="true"], .toggle .toggle-current, .tabs .current,
    .calendar-day.chosen, .calendar-day[aria-current="date"], .kindmark .seg.on, .filter-chip {
        text-decoration: underline;
        text-decoration-thickness: 2px;
        text-underline-offset: 3px;
    }
    /* EVERY LABEL SET ON A SOLID FILL IS DRAWN OUTLINED: ButtonText on ButtonFace, with a 2px border.
       The set is chosen by one test — a rule that sets words in an on- ink over a fill — and this list is
       every rule in this stylesheet that does: the primary button and the loud chips. The selections were on
       it until they became outlines; an outline survives this mode as it is, and the underline above still
       marks each. The field error's mark also sets an on- ink, but for a glyph drawn as a stroke, so it keeps
       its fill. A new rule that sets words in an on- ink joins this list. What the fill said is carried
       another way: the heavier border for a loud chip's rank, and a primary button's own words for what it
       does. */
    .pm button.primary, .pm .button.primary, .pm button.primary:hover, .pm .button.primary:hover,
    .tag-loud, .tag-loud-danger {
        background: ButtonFace;
        color: ButtonText;
        border: 2px solid ButtonText;
    }
}
