/* =============================================================================
   ASTOR-CORE.CSS  —  Quiet Luxury Design System  (App Interior)
   Manifesto: Architectural Digest × Monocle.
   Ink on Bone. 0.5px hairlines. 0px radius everywhere. No shadows.
   Oxblood reserved for risk signals only — see --c-oxblood in tokens.css.

   Tokens live in tokens.css — load that first.
   This file: reset, layout, and component definitions only.
   ============================================================================= */

/* Sub-layer splitting (reset / layout / components) will be applied when
   style.css is decomposed into competing layered files. For now the full
   file sits in @layer components so the layer order in tokens.css takes
   effect relative to future stylesheets loaded on the same page. */
@layer components {

/* ─────────────────────────────────────────────────────────────────────────────
   1. GLOBAL RESET & TYPOGRAPHY
   ───────────────────────────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
body {
  background: var(--c-bone); color: var(--c-ink); font-family: var(--font-sans);
  -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
  margin: 0; padding: 0; line-height: 1.5; display: flex; flex-direction: column; min-height: 100vh;
}
html { overflow-x: hidden; }
a { color: inherit; text-decoration: none; }
h1, h2, h3, h4, h5, h6 { margin: 0; font-weight: 600; line-height: 1.2; }
p { margin: 0; }
img { display: block; max-width: 100%; }
.site-main { flex: 1; display: flex; flex-direction: column; }
body.ql-theme *, body.ql-theme *::before, body.ql-theme *::after { box-sizing: border-box !important; }

/* ─────────────────────────────────────────────────────────────────────────────
   2. SITE NAV — MOVED to nav.css & 3. SITE FOOTER
   The nav chrome (.site-nav / .site-brand / .nav-dropdown* / .nav-avatar) used
   to be duplicated here as a stripped-down fork of the layout.css copy: it was
   missing --right, -divider, -section-label, -item--btn, -arrow, .is-active and
   .is-current, so the account menu opened centred (overflowing the right edge)
   with the view-as picker and Log out rendering as raw browser buttons on every
   tools page. One shared definition now lives in static/planning/nav.css, which
   base.html loads outside {% block base_styles %}. Do not re-add it here.
   ───────────────────────────────────────────────────────────────────────────── */
.site-footer { border-top: 1px solid var(--c-rule); background: transparent; }
.site-footer-inner { max-width: 1100px; margin: 0 auto; padding: 28px 18px 22px; display: flex; justify-content: space-between; align-items: flex-start; gap: 24px; flex-wrap: wrap; }
.site-footer-left { display: flex; flex-direction: column; gap: 5px; }
.site-footer-brand { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; margin-bottom: 2px; }
.site-footer-brand-name { font-size: 0.80rem; font-weight: 700; letter-spacing: 0.02em; color: var(--c-ink); }
.site-footer-brand-sep { color: var(--c-muted); font-size: 0.80rem; }
.site-footer-brand-tag { font-size: 0.68rem; font-weight: 700; letter-spacing: 0.20em; text-transform: uppercase; color: var(--c-muted); }
.site-footer-email { font-size: 0.78rem; color: var(--c-muted); text-decoration: none; }
.site-footer-email:hover { color: var(--c-ink); text-decoration: underline; }
.site-footer-right { display: flex; flex-direction: column; align-items: flex-end; gap: 8px; }
.site-footer-links { display: flex; align-items: center; gap: 10px; }
.site-footer-link { font-size: 0.68rem; font-weight: 700; letter-spacing: 0.18em; text-transform: uppercase; color: var(--c-muted); text-decoration: none; }
.site-footer-link:hover { color: var(--c-ink); }
.site-footer-link-sep { color: var(--c-pale); font-size: 0.78rem; }
.site-footer-copy { margin: 0; font-size: 0.70rem; color: var(--c-pale); letter-spacing: 0.03em; }
.site-footer-base { border-top: 1px solid var(--c-rule); }
.site-footer-base-inner { max-width: 1100px; margin: 0 auto; padding: 9px 18px 10px; font-size: 0.68rem; color: var(--c-pale); line-height: 1.6; }
@media (max-width: 600px) {
  .site-footer-inner { flex-direction: column; gap: 18px; padding: 22px 18px 18px; }
  .site-footer-right { align-items: flex-start; }
}

/* ─────────────────────────────────────────────────────────────────────────────
   4. BUTTON SYSTEM 
   ───────────────────────────────────────────────────────────────────────────── */
/* `font-family: inherit` IS LOAD-BEARING, not tidiness. A <button> does not
   inherit the body font — the UA supplies its own (Arial here) — so a button
   and a link carrying the SAME class rendered in different typefaces, with
   different line-heights, and therefore different heights. Measured on the CRM
   contact record, 2026-09-02: the <a> chips were Inter/16.5px/25px tall and the
   <button> beside them was Arial/normal/21px tall. Cain: *"the remove one is
   randomly different size?"* It was not random and it was not local — every
   surface mixing <a> and <button> in one action group had it. */
.btn, .action-btn, .submit-btn { display: inline-flex; align-items: center; justify-content: center; gap: 6px; padding: 6px 32px; font-family: inherit; font-size: 0.81rem; font-weight: 500; letter-spacing: 0.05em; line-height: 1; border-radius: 0; cursor: pointer; white-space: nowrap; text-decoration: none; transition: background 0.18s, border-color 0.18s, color 0.18s; }
.btn:focus-visible { outline: 2px solid var(--c-ink); outline-offset: 2px; }
.btn:disabled, .submit-btn:disabled { opacity: 0.38; cursor: not-allowed; }

/* THE GHOST BUTTON, AS ASTOR_SYSTEM §8.2 DEFINES IT — "transparent fill, Inky Black
   border and text". It was `border: var(--c-rule)` on `color: var(--c-muted)`: the
   FAINTEST hairline in the system carrying metadata grey, which is a disabled
   field's colouring, not a button's. Cain, 2026-08-28, on the facility-fit card: *"the
   button to add is basically invisible"*. It was not a one-page problem — this rule is
   the base for 427 plain `.action-btn` across 89 templates, and almost nothing overrode
   it, so every tools surface has been drawing its secondary actions in the disabled
   palette ([[feedback_fix_the_class_not_the_instance]]).

   HOVER FOLLOWS §8.2 TOO: "Hover drops opacity to 0.7. No fill on hover." The old hover
   painted a 4% ink wash AND swapped both border and text to ink — which is to say the
   button only became legible once you were already pointing at it. */
.btn--ghost, .action-btn { background: transparent; border: 1px solid var(--c-ink); color: var(--c-ink); }
.btn--ghost:hover, .action-btn:hover { background: transparent; border-color: var(--c-ink); color: var(--c-ink); opacity: 0.7; }

.btn--primary, .action-btn--primary, .submit-btn { background: var(--c-ink); color: var(--c-white); border: 1px solid var(--c-ink); }

/* ── SIZE: the row-density button ────────────────────────────────────────────
   `6px 32px` is right for a page-header action and far too wide for three
   buttons sharing a table or list row. `.action-btn--sm` was already being
   asked for in `lender/deal_interest_run.html` and defined NOWHERE, so that
   button has been silently full-size since it was written — a modifier that
   does nothing is the same class of defect as a comment asserting an
   invariant. This gives it a body. */
.action-btn--sm, .btn--sm { padding: 5px 14px; font-size: 12px; }

/* ── SIGNAL: the destructive action ──────────────────────────────────────────
   Oxblood is the only signal colour in the system (ASTOR §14), and this is the
   pattern already spelled out four times locally — dq-btn--danger,
   ops-card__btn--danger, dd-resend-btn--danger, crm-bulk-bar__btn--danger.
   Named once here so a fifth surface does not have to reinvent it. Border and
   text, never a fill: a filled red button reads as the PRIMARY action, and
   deleting a row never is. */
.action-btn--danger, .btn--danger { background: transparent; border-color: var(--c-oxblood); color: var(--c-oxblood); }
.action-btn--danger:hover, .btn--danger:hover { background: transparent; border-color: var(--c-oxblood); color: var(--c-oxblood); opacity: 0.7; }

/* ── WEIGHT: the filled secondary, which INVERTS on hover ────────────────────
   Cain, 2026-09-02, on the linked-deal rows: *"can we please also have like a
   hover css so its a little more obvious its a button please. i would actually
   prefer they were dark then lite of hover."*

   The ghost button's only hover was `opacity: 0.7` — it FADES on approach,
   which is the affordance backwards: the control gets quieter exactly when you
   reach for it. This one is filled at rest and empties on hover, so the change
   is unmissable and the direction is right.

   NOT a change to `.action-btn--primary`, and not a change to the ghost
   default. `--primary` is the page's one real call to action and ASTOR §8.2
   fixes its hover deliberately ("Never a colour shift"); a row full of primary
   buttons would say every action on the page is the main one. `--solid` is the
   dense row-action weight, and it composes with `--danger` rather than
   redefining it, so a surface wanting an OUTLINE danger button still has one. */
.action-btn--solid, .btn--solid { background: var(--c-ink); border-color: var(--c-ink); color: var(--c-white); }
.action-btn--solid:hover, .btn--solid:hover { background: transparent; border-color: var(--c-ink); color: var(--c-ink); opacity: 1; }
.action-btn--solid.action-btn--danger, .btn--solid.btn--danger { background: var(--c-oxblood); border-color: var(--c-oxblood); color: var(--c-white); }
.action-btn--solid.action-btn--danger:hover, .btn--solid.btn--danger:hover { background: transparent; border-color: var(--c-oxblood); color: var(--c-oxblood); opacity: 1; }
.btn--primary:hover, .action-btn--primary:hover, .submit-btn:hover { background: var(--c-ink); }

.btn-danger { background: var(--c-oxblood); color: var(--c-white); border: 1px solid var(--c-oxblood); }
.btn-danger:hover { background: var(--c-oxblood-dk); border-color: var(--c-oxblood-dk); }

/* ── Mobile touch targets (MOBILE_UX.md §3) — the lean desktop button (~25px
   tall) is a mis-tap magnet on a phone. Grow the hit area to ≥44px at ≤768px;
   the inline-flex centering keeps the visual chrome (border, font) unchanged. */
@media (max-width: 768px) {
  .btn, .action-btn, .submit-btn { min-height: 44px; }
}

/* ─────────────────────────────────────────────────────────────────────────────
   5. APP SHELL (Sidebar & Tools Canvas)
   ───────────────────────────────────────────────────────────────────────────── */
.tools-shell { display: flex; min-height: calc(100vh - var(--nav-h)); background: var(--c-bone); }
.tools-content { flex: 1; display: flex; flex-direction: column; background: var(--c-bone); min-width: 0; }

.sidebar { width: var(--sidebar-w); background: var(--c-ink); color: rgba(255,255,255,0.80); flex-shrink: 0; display: flex; flex-direction: column; position: sticky; top: var(--nav-h); height: calc(100vh - var(--nav-h)); overflow-y: auto; align-self: flex-start; border-right: none; }
.sidebar-inner { display: flex; flex-direction: column; min-height: 100%; padding-bottom: 16px; }
.sidebar-brand { display: flex; align-items: center; padding: 18px 16px 14px; border-bottom: 1px solid rgba(255,255,255,0.07); text-decoration: none; }
.sidebar-brand-name { font-size: 12px; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase; color: rgba(255,255,255,0.40); }
.sidebar-brand-mark { font-size: 14px; font-weight: 600; letter-spacing: -0.01em; color: var(--c-white); }
.sidebar-nav { flex: 1; padding: 8px 0; }
.sidebar-label { display: block; font-size: 9px; font-weight: 700; letter-spacing: 0.18em; text-transform: uppercase; color: rgba(255,255,255,0.28); margin: 16px 12px 4px; }
.sidebar-link { display: block; color: rgba(255,255,255,0.60); padding: 7px 12px; font-size: 13px; text-decoration: none; border-left: 2px solid transparent; border-radius: 0; }
.sidebar-link:hover { background: rgba(255,255,255,0.06); color: rgba(255,255,255,0.95); }
.sidebar-link.is-active { background: rgba(255,255,255,0.10); color: var(--c-white); font-weight: 600; border-left-color: rgba(255,255,255,0.60); padding-left: 10px; }
/* Count of items needing action on a nav destination (e.g. drawdowns due and
   not recorded). Amber on the dark sidebar — oxblood is the page-level accent
   but reads as near-black against this surface. Square, like everything else
   in the system. Floats right so it never pushes the label. */
.sidebar-badge { float: right; min-width: 17px; padding: 0 5px; margin-left: 8px; border-radius: 0;
                 background: var(--status-warn-lt, #fbbf24); color: var(--c-ink, #14100c);
                 font-size: 10px; font-weight: 700; line-height: 17px; text-align: center;
                 font-variant-numeric: tabular-nums; }
.sidebar-mobile-toggle { display: none; }

/* Sidebar toolbar — search input + quick-create button. Sits below brand. */
.sidebar-toolbar { display: flex; align-items: stretch; gap: 6px; padding: 10px 12px; border-bottom: 1px solid rgba(255,255,255,0.07); }
.sidebar-search { position: relative; flex: 1; display: flex; align-items: center; }
.sidebar-search-icon { position: absolute; left: 9px; top: 50%; transform: translateY(-50%); color: rgba(255,255,255,0.40); pointer-events: none; }
.sidebar-search-input { width: 100%; background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.10); color: var(--c-white); font-size: 12px; font-family: inherit; padding: 7px 28px 7px 28px; border-radius: 0; outline: none; transition: border-color 0.15s ease, background 0.15s ease; }
.sidebar-search-input::placeholder { color: rgba(255,255,255,0.40); }
.sidebar-search-input:focus { border-color: rgba(255,255,255,0.45); background: rgba(255,255,255,0.07); }
.sidebar-search-kbd { position: absolute; right: 7px; top: 50%; transform: translateY(-50%); font-family: var(--font-mono); font-size: 10px; font-weight: 600; color: rgba(255,255,255,0.45); border: 1px solid rgba(255,255,255,0.18); padding: 1px 5px; border-radius: 0; background: transparent; pointer-events: none; }

/* Quick-create button + dropdown panel. */
.sidebar-quick { position: relative; }
.sidebar-quick-trigger { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; background: var(--c-white); color: var(--c-ink); border: 1px solid var(--c-white); border-radius: 0; cursor: pointer; padding: 0; transition: opacity 0.15s ease; }
.sidebar-quick-trigger:hover { opacity: 0.82; }
.sidebar-quick-trigger[aria-expanded="true"] { opacity: 0.82; }
.sidebar-quick-panel { position: absolute; top: calc(100% + 6px); right: 0; min-width: 180px; background: var(--c-ink); border: 1px solid rgba(255,255,255,0.18); border-radius: 0; padding: 4px 0; z-index: 250; opacity: 0; pointer-events: none; transform: translateY(-4px); transition: opacity 0.15s ease, transform 0.15s ease; }
.sidebar-quick.is-open .sidebar-quick-panel { opacity: 1; pointer-events: auto; transform: translateY(0); animation: astor-rise 0.18s ease both; }
.sidebar-quick-item { display: block; padding: 8px 14px; font-size: 12px; color: rgba(255,255,255,0.78); text-decoration: none; white-space: nowrap; }
.sidebar-quick-item:hover { background: rgba(255,255,255,0.08); color: var(--c-white); }
.sidebar-quick-rule { height: 1px; background: rgba(255,255,255,0.10); margin: 4px 0; }

/* Collapsible nav groups — used for Intelligence, CRM, Settings, Internal. */
.sidebar-group { display: flex; flex-direction: column; }
.sidebar-group-toggle { display: flex; align-items: center; justify-content: space-between; width: 100%; background: transparent; border: 0; color: rgba(255,255,255,0.32); font-family: inherit; font-size: 9px; font-weight: 700; letter-spacing: 0.18em; text-transform: uppercase; padding: 0 12px; margin: 16px 0 4px; cursor: pointer; text-align: left; line-height: 1.4; }
.sidebar-group-toggle:hover { color: rgba(255,255,255,0.55); }
.sidebar-group-chevron { color: rgba(255,255,255,0.40); transition: transform 0.18s ease; }
.sidebar-group[data-collapsed="true"] .sidebar-group-chevron { transform: rotate(-90deg); }
.sidebar-group-items { display: flex; flex-direction: column; }
.sidebar-group[data-collapsed="true"] .sidebar-group-items { display: none; }
.sidebar-group--utility .sidebar-group-toggle { color: rgba(255,255,255,0.22); }
.sidebar-group--utility { border-top: 1px solid rgba(255,255,255,0.06); margin-top: 12px; padding-top: 4px; }

/* ─────────────────────────────────────────────────────────────────────────────
   6. UNIVERSAL COMPONENTS (.ql-system)
   Replaces all bloat: .ai-*, .cmp-*, .ul-*, .bpl-*, .mc-*, .dash-*
   ───────────────────────────────────────────────────────────────────────────── */

/* Page Wrappers */
.ql-page { flex: 1; display: flex; flex-direction: column; width: 100%; max-width: 1200px; margin: 0 auto; padding: 32px 40px 64px; background: transparent; }
.ql-page--narrow { max-width: 860px; }

/* ── Wide content scrolls; the page does not widen (ASTOR §4.1) ──────────────
   The companion to deleting `ql-page--wide`. A table too wide for the canvas goes
   inside one of these, so the page keeps the single product width and the TABLE
   takes the overflow.

   In `app.css` rather than cashflow.css deliberately: `cf-scroll-x` needs a
   stylesheet most tools pages do not load, and a scroll container that silently does
   nothing is worse than none — the table just overflows and nobody sees a failure.
   Measured 2026-09-02: 43 tables of six columns or more, up to TWELVE, were relying
   on the page not being narrower than they are. */
.ql-scroll-x { overflow-x: auto; }
/* `.ql-page--wide` was DELETED, 2026-09-01, at Cain's instruction: "why is this page
   different dimensions to the whole website, I have tried so hard to make this a rule
   why do you completely ignore it ... delete the class it shouldn't be anywhere."
   It was a 1500px override on a 1200px canvas, so any page carrying it started ~135px
   left of every other page in the product. PR 966 fixed one instance of exactly this
   ("the deal header was the only indented page header in Astor") and it came back on a
   new page within days, because the rule was only ever a sentence.
   Wide content scrolls inside its own container (ASTOR §4.1); the page does not widen.
   `scripts/page_width_audit.py` now fails the build if a non-standard container
   reappears. Do not reintroduce this — add the scroll container instead. */
.ql-page--dossier { display: grid; grid-template-columns: 1fr 380px; gap: 40px; align-items: start; }

/* Universal Headers */
.ql-header { margin-bottom: 24px; padding-bottom: 24px; border-bottom: 1px solid var(--c-rule); display: flex; flex-direction: column; gap: 6px; }
.ql-header--no-border { border-bottom: none; padding-bottom: 0; }

/* Sub-nav strip — the in-page journey (ASTOR §8.6). Global tools primitive;
   pair the page's .ql-header with .ql-header--no-border so this strip's
   underline is the sole demarcation (§3 forbids stacked hairlines).
   Incubated in the cashflow tool, graduated here when the CRM email
   workspace adopted it — one strip, not a fork. */
.cf-tabs { display: flex; gap: 0; border-bottom: 1px solid var(--c-rule); margin: 0 0 var(--space-5); overflow-x: auto; overflow-y: hidden; scrollbar-width: thin; }
/* The BUTTON form is normalised here, not only in console.css. The two copies of this
   rule had diverged: console.css carried `border: 0; background: none; cursor: pointer;
   font: inherit` and this one did not, so a `<button class="cf-tabs__link">` rendered
   with the browser's default chrome on every page that loads app.css — the exact defect
   Audit I exists for, one component along. A tab strip is navigation, and §8.6 is
   explicit: hairline-underlined, ink-on-bone, NO BOXES. */
.cf-tabs__link { padding: var(--space-2) var(--space-4); font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--c-muted); text-decoration: none; font-weight: var(--weight-semibold); border: 0; border-bottom: 1px solid transparent; margin-bottom: -1px; white-space: nowrap; background: none; cursor: pointer; font-family: inherit; transition: color 0.12s; }
.cf-tabs__link:hover { color: var(--c-ink); }
.cf-tabs__link.is-active { color: var(--c-ink); border-bottom-color: var(--c-ink); }
/* A tab that still needs filling in once says so, quietly, until it is done.
   Oxblood because it is the same "your attention is needed" the rest of the
   system uses — not a warning, and never on a tab that is merely optional. */
.cf-tabs__flag { display: inline-block; margin-left: 6px; padding: 1px 5px;
                 font-size: 9px; letter-spacing: 0.06em; font-weight: 600;
                 color: var(--c-white); background: var(--c-oxblood);
                 vertical-align: 1px; }
.cf-tabs__meta { margin-left: auto; padding: var(--space-2) 0; font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.06em; color: var(--c-pale); white-space: nowrap; }
/* Mobile touch target (MOBILE_UX.md §3) — ≥44px; the strip scrolls, no wrap. */
@media (max-width: 768px) { .cf-tabs__link { min-height: 44px; display: flex; align-items: center; } }
.ql-header__top { display: flex; justify-content: space-between; align-items: flex-end; gap: 16px; flex-wrap: wrap; }
.ql-header__eyebrow { font-size: 10px; font-weight: 600; letter-spacing: 0.18em; text-transform: uppercase; color: var(--c-muted); margin-bottom: 4px; }
.ql-header__title { font-size: clamp(1.5rem, 2.4vw, 2.25rem); font-weight: 300; color: var(--c-ink); letter-spacing: -0.03em; line-height: 1.1; margin: 0; }
.ql-header__sub { font-size: 13px; color: var(--c-muted); max-width: 600px; line-height: 1.5; margin: 0; }
.ql-header__actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
/* PAGE ACTIONS ON THE EYEBROW LINE — one place, the same point on every page.
   Cain, 2026-09-18, on the four lender tools pages: the buttons "are all in different
   places and dont really have a bespoke css sytling or place they just float about".
   Measured: `.ql-header__actions` sits in `.ql-header__top` BESIDE a wrapping lede, so a
   long lede pushed them underneath it and a short one left them beside the title — x=917
   to 1280 at 1440/1920 and a different height on every page at 1280.
   The eyebrow line does not move: it is the first line of every page. So `.ql-header__bar`
   holds the eyebrow on the left and `.page-actions` on the right, and a page's Monday pull
   is its LAST action, so the pull sits in the same corner wherever there is one.
   Each action is ONE or TWO words with a square-grid glyph (`lender/_action_glyph.html`,
   §16.3); what it does in full lives in its hover title and, for a pull, on the blackout.
   Cain on the first cut, 2026-09-18: *"can they please look cooler and not have so much
   text. they are too wordy"*. The Monday pull is the PRIMARY (ink-filled) action.
   Pinned by lender/tests_page_actions.py. */
.ql-header__bar { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: var(--space-2) var(--space-4); min-height: 32px; margin-bottom: var(--space-2); }
.ql-header__bar > .ql-header__eyebrow { margin-bottom: 0; }
.page-actions { display: flex; align-items: center; justify-content: flex-end; flex-wrap: wrap; gap: var(--space-2); margin-left: auto; }
.page-actions > form { display: contents; }
.page-action { display: inline-flex; align-items: center; gap: 6px; height: 28px; padding: 0 10px; border: 1px solid transparent; border-radius: 0; background: transparent; color: var(--c-ink); font-family: inherit; font-size: 11px; font-weight: var(--weight-semibold); letter-spacing: 0.08em; line-height: 1; text-transform: uppercase; text-decoration: none; white-space: nowrap; cursor: pointer; transition: color 0.12s, background 0.12s, border-color 0.12s; }
.page-action__glyph { flex: none; width: 12px; height: 12px; }
.page-action:focus-visible { outline: 1px solid var(--c-ink); outline-offset: 2px; }
.page-action--primary { background: var(--c-ink); border-color: var(--c-ink); color: var(--c-white); }
.page-action--primary:hover { background: transparent; color: var(--c-ink); }
/* QUIET — Cain's pick, 2026-09-18, of two looks mocked on the real pages (the other was a
   hairline toolbar). The eyebrow's mono voice: secondary actions are bare muted words that
   ink up on hover; only the pull carries a fill. */
.page-actions--quiet { gap: var(--space-1); font-family: var(--font-mono); }
.page-actions--quiet .page-action:not(.page-action--primary) { color: var(--c-muted); }
.page-actions--quiet .page-action:not(.page-action--primary):hover { color: var(--c-ink); border-bottom-color: var(--c-ink); }
/* The hairline before a tools-page eyebrow. It was defined in lender.css and modal.css
   only, so /lender/completions/ — which loads neither — printed its eyebrow without it. */
.ff-eyebrow-rule { display: inline-block; width: 28px; height: 1px; background: var(--c-rule); vertical-align: middle; margin-right: 14px; }
@media (max-width: 768px) {
  .page-actions { width: 100%; margin-left: 0; justify-content: flex-start; }
  .page-action { height: auto; min-height: 44px; flex: 1 1 auto; justify-content: center; }
}
/* A printed page is read, not operated — `.ql-header__actions` was hidden in print on the
   lender.css pages, and its replacement keeps that on every page. */
@media print { .page-actions { display: none !important; } }

/* Universal Grids */
.ql-grid { display: grid; gap: 16px; margin-bottom: 24px; }
.ql-grid--2 { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
.ql-grid--3 { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
.ql-grid--4 { grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); }

/* Stat Groups (Dashboards & Hero Metrics) */
.ql-stat-group { display: flex; flex-wrap: wrap; border: 1px solid var(--c-rule); background: var(--c-white); margin-bottom: 24px; border-radius: 0; overflow: hidden; }
.ql-stat-cell { flex: 1; min-width: 120px; padding: 16px 20px; border-right: 1px solid var(--c-rule); display: flex; flex-direction: column; }
.ql-stat-cell:last-child { border-right: none; }
.ql-stat-cell__val { font-size: 28px; font-weight: 300; color: var(--c-ink); letter-spacing: -0.04em; font-family: var(--font-mono); line-height: 1; }
.ql-stat-cell__lbl { font-size: 10px; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase; color: var(--c-muted); margin-top: 6px; white-space: nowrap; }

/* Standard Cards */
.ql-card { background: var(--c-white); border: 1px solid var(--c-rule); border-radius: 0; display: flex; flex-direction: column; }
.ql-card:hover.ql-card--interactive { border-color: var(--c-ink); cursor: pointer; }
.ql-card__head { display: flex; justify-content: space-between; align-items: center; padding: 12px 16px; border-bottom: 1px solid var(--c-rule); background: var(--c-bone); }
.ql-card__title { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.10em; color: var(--c-ink); margin: 0; }

/* ── First-run setup card ────────────────────────────────────────────────────
   The three things a newly-provisioned lender has not been told about. It
   REMOVES ITSELF once they are done, so there is no dismiss control and no
   "onboarded" state to store — the card's absence IS the done state. Styled as
   an ordinary card rather than a banner: it is information they can act on, not
   an interruption. */
.fr-card__count { margin-left: auto; font-size: 10px; text-transform: uppercase;
                  letter-spacing: 0.07em; color: var(--c-muted); font-weight: 600; }
.fr-lead { font-size: 13px; line-height: 1.6; color: var(--c-muted);
           margin: 0 0 14px; max-width: 66ch; }
.fr-item { display: flex; align-items: baseline; gap: 12px; padding: 13px 0;
           border-top: 1px solid var(--c-rule); flex-wrap: wrap; }
.fr-item__mark { flex: 0 0 16px; font-size: 13px; color: var(--c-stone); }
.fr-item__body { flex: 1 1 260px; display: flex; flex-direction: column; gap: 3px; }
.fr-item__body b { font-size: 14px; font-weight: 600; color: var(--c-ink); }
.fr-item__why { font-size: 12px; line-height: 1.55; color: var(--c-muted); }
.fr-item__state { flex: 0 0 auto; font-size: 9px; text-transform: uppercase;
                  letter-spacing: 0.07em; font-weight: 600; padding: 3px 7px;
                  color: var(--c-white); background: var(--c-oxblood); }
.fr-item__cta { flex: 0 0 auto; font-size: 12px; font-weight: 600;
                color: var(--c-ink); text-decoration: none;
                border-bottom: 1px solid var(--c-ink); }
.fr-item__cta:hover { color: var(--c-oxblood); border-bottom-color: var(--c-oxblood); }
.ql-card__body { padding: 16px; flex: 1; }

/* Lists & Data Rows */
.ql-list { display: flex; flex-direction: column; border: 1px solid var(--c-rule); background: var(--c-white); border-radius: 0; }
.ql-row { display: flex; align-items: center; justify-content: space-between; padding: 12px 16px; border-bottom: 1px solid var(--c-rule); gap: 12px; }
.ql-row:last-child { border-bottom: none; }
.ql-row:hover.ql-row--interactive { background: var(--c-bone); cursor: pointer; }
.ql-row__content { flex: 1; min-width: 0; }
.ql-row__title { font-size: 13px; font-weight: 600; color: var(--c-ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin: 0; }
.ql-row__sub { font-size: 11px; color: var(--c-muted); margin-top: 2px; }
.ql-row__aside { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
.ql-row__chevron { color: var(--c-pale); font-size: 16px; line-height: 1; }

/* Section heading with an optional trailing action link (dashboard sub-sections) */
.ql-section-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 0 0 12px; flex-wrap: wrap; }
.ql-section-head__title { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.10em; color: var(--c-ink); margin: 0; }
.ql-section-head__link { font-size: 12px; color: var(--c-muted); text-decoration: none; }
.ql-section-head__link:hover { color: var(--c-ink); }

/* Inline status banner (plan badge / notice) — a one-line ql-card variant */
.ql-banner { flex-direction: row; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; padding: 14px 16px; }
.ql-banner__text { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.ql-banner__note { font-size: 13px; color: var(--c-muted); margin: 0; }

/* Tags, Badges, Avatars */
.ql-tag { display: inline-flex; align-items: center; font-size: 10px; font-weight: 600; padding: 2px 8px; text-transform: uppercase; letter-spacing: 0.05em; border: 1px solid var(--c-rule); background: transparent; color: var(--c-muted); border-radius: 0; white-space: nowrap; }
.ql-tag--ink { border-color: var(--c-ink); color: var(--c-ink); }
.ql-tag--oxblood { border-color: var(--c-oxblood); color: var(--c-oxblood); }
.ql-tag--bone { background: var(--c-bone); border-color: var(--c-stone); color: var(--c-ink); }
.ql-avatar { width: 32px; height: 32px; border-radius: 0; background: var(--c-bone); border: 1px solid var(--c-rule); display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 700; color: var(--c-ink); flex-shrink: 0; }

/* Search & Filters Bar */
.ql-controls { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin-bottom: 16px; }
.ql-search { flex: 1; min-width: 200px; max-width: 360px; position: relative; }
.ql-search input { width: 100%; padding: 8px 12px; border: 1px solid var(--c-rule); border-radius: 0; font-size: 13px; background: var(--c-white); color: var(--c-ink); outline: none; }
.ql-search input:focus { border-color: var(--c-ink); }
.ql-filter-select { padding: 8px 12px; border: 1px solid var(--c-rule); border-radius: 0; font-size: 13px; background: var(--c-white); color: var(--c-ink); cursor: pointer; outline: none; }

/* Progress Bars */
.ql-progress { display: flex; align-items: center; gap: 12px; width: 100%; }
.ql-progress__track { flex: 1; height: 4px; background: var(--c-rule); border-radius: 0; overflow: hidden; }
.ql-progress__fill { height: 100%; background: var(--c-ink); border-radius: 0; transition: width 0.4s; }
.ql-progress__fill--danger { background: var(--c-oxblood); }

/* Empty States */
.ql-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; padding: 64px 24px; background: var(--c-bone); border: 1px dashed var(--c-rule); color: var(--c-muted); border-radius: 0; }
.ql-empty__icon { font-size: 32px; margin-bottom: 12px; opacity: 0.5; }
.ql-empty__title { font-size: 14px; font-weight: 600; color: var(--c-ink); margin: 0 0 6px; }
.ql-empty__sub { font-size: 13px; max-width: 400px; margin: 0 auto; line-height: 1.5; }

/* Helpers */
.ql-val--ink { color: var(--c-ink); }
.ql-val--muted { color: var(--c-muted); }
.ql-val--oxblood { color: var(--c-oxblood); }
.font-mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }


/* ─────────────────────────────────────────────────────────────────────────────
   7. FORM & TABLE PRIMITIVES
   ───────────────────────────────────────────────────────────────────────────── */
/* These are BASE styles — the default look of a form field before any component
   has an opinion. They are wrapped in :where() so they carry ZERO specificity.
   Without it, `input[type="number"]` is (0,1,1) and silently OUTRANKS any
   component rule written as a single class (0,1,0) — the component's
   width/padding/font are discarded and it renders as a default full-width field.
   That is invisible in review and only shows up in computed styles; it shipped
   once (the memo Scheme inputs) and left ~70 dead declarations across the app.
   :where() keeps these as true fallbacks: they still apply when nothing else
   sets the property, and lose to any component rule that does.
   Audit G (scripts/css_specificity_audit.py) enforces this — do not unwrap. */
:where(.field-input, input[type="text"], input[type="email"], input[type="url"], input[type="number"], input[type="date"], input[type="tel"], select, textarea) {
  display: block; width: 100%; border: 1px solid var(--c-rule); border-radius: 0; padding: 12px;
  font-size: 14px; font-family: var(--font-sans); background: var(--c-white); color: var(--c-ink);
  outline: none; transition: border-color 0.15s; appearance: none;
}
:where(.field-input, input[type="text"], input[type="email"], input[type="url"], input[type="number"], input[type="date"], input[type="tel"], select, textarea):focus { border-color: var(--c-ink); background: var(--c-bone); }
/* NOT :where()-wrapped, deliberately: `label` is (0,0,1), which any component
   class (0,1,0) already outranks — it was never part of the specificity trap, so
   neutralising it would only widen the blast radius for no benefit. */
label, .field-label { display: block; font-size: 11px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--c-muted); margin-bottom: 6px; }

.ql-table { width: 100%; border-collapse: collapse; text-align: left; background: var(--c-white); border: 1px solid var(--c-rule); }
.ql-table th { font-size: 10px; font-weight: 400; text-transform: uppercase; letter-spacing: 0.05em; color: var(--c-text-muted); padding: 12px 16px; border-bottom: 1px solid var(--c-rule); background: var(--c-bone); }
.ql-table td { padding: 14px 16px; border-bottom: 1px solid var(--c-rule); font-size: 12px; color: var(--c-ink); }
.ql-table tbody tr:hover td { background: var(--c-bone); }


/* ─────────────────────────────────────────────────────────────────────────────
   8. SPECIALIZED COMPONENTS (The 20%)
   ───────────────────────────────────────────────────────────────────────────── */

/* ── 8.1 CRM Kanban Board ── */
.crm-kanban { display: flex; gap: 0; overflow-x: auto; padding-bottom: 16px; flex: 1; }
.crm-kanban-col { border-right: 1px solid var(--c-rule); min-width: 260px; padding: 0; display: flex; flex-direction: column; }
.crm-kanban-col:last-child { border-right: none; }
.crm-kanban-col__head { background: var(--c-white); border-bottom: 1px solid var(--c-rule); padding: 14px 16px 12px; display: flex; align-items: center; justify-content: space-between; }
.crm-kanban-col__title { color: var(--c-ink); font-size: 11px; font-weight: 600; letter-spacing: 0.10em; text-transform: uppercase; }
.crm-kanban-col__body { background: var(--c-bone); padding: 12px; display: flex; flex-direction: column; gap: 8px; flex: 1; }
.crm-kanban-card { background: var(--c-white); border: 1px solid var(--c-rule); border-left: 2px solid var(--c-ink); padding: 14px 14px 12px; text-decoration: none; display: block; border-radius: 0; transition: border-color 0.12s; }
.crm-kanban-card:hover { border-color: var(--c-ink); }
.crm-kanban-card__name { color: var(--c-ink); font-size: 13px; font-weight: 500; }
.crm-kanban-card__org { color: var(--c-muted); font-size: 11px; }

/* ── 8.2 Sticky Data Tables (.crm-table & .ul-table logic) ── */
.table-wrap-sticky { max-height: 75vh; overflow-y: auto; overflow-x: auto; border: 1px solid var(--c-rule); background: var(--c-white); }
.table-wrap-sticky table { width: 100%; border-collapse: collapse; table-layout: fixed; }
.table-wrap-sticky thead th { position: sticky; top: 0; z-index: 2; background: var(--c-bone); border-bottom: 1px solid var(--c-rule); }
/* Prevent text overflow in dense tables */
.table-wrap-sticky td.col-truncate { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; max-width: 0; }

/* ── 8.2a Lender pipeline ledger (#lp-table) ──
   table-layout:fixed split 8 columns evenly, so a long deal name/address
   crushed the "Deal" column and wrapped rows to 3–4 lines — uneven, messy.
   Give each column a purpose-built width, top-align every cell to one baseline,
   and let the wrapper scroll (not crush) below a sensible min width. */
#lp-table { min-width: 900px; }
#lp-table th { vertical-align: middle; }
#lp-table td { vertical-align: top; }
#lp-table th:nth-child(1), #lp-table td:nth-child(1) { width: 24%; }   /* Deal      */
#lp-table th:nth-child(2), #lp-table td:nth-child(2) { width: 15%; }   /* Status    */
#lp-table th:nth-child(3), #lp-table td:nth-child(3) { width: 8%;  }   /* Ref       */
#lp-table th:nth-child(4), #lp-table td:nth-child(4) { width: 10%; }   /* Facility  */
#lp-table th:nth-child(5), #lp-table td:nth-child(5) { width: 10%; }   /* GDV       */
#lp-table th:nth-child(6), #lp-table td:nth-child(6) { width: 11%; }   /* Rate/Term */
#lp-table th:nth-child(7), #lp-table td:nth-child(7) { width: 10%; }   /* Updated   */
#lp-table th:nth-child(8), #lp-table td:nth-child(8) { width: 12%; }   /* Actions   */
/* The status tag must never bleed into Ref if a label is long. */
#lp-table td:nth-child(2) .ql-tag { max-width: 100%; }

/* Phone: the ledger becomes Pattern F record rows (tables.css). The desktop
   column plan has to be switched OFF for that, and an id selector outranks any
   class rule — `#lp-table.tbl--rows td` is (1,1,1), which beats the (1,0,2)
   nth-child widths above because a class outweighs a pseudo-class. */
@media (max-width: 768px) {
  #lp-table.tbl--rows { min-width: 0; }
  #lp-table.tbl--rows td { width: auto; vertical-align: baseline; }
  /* The ledger's own two identity lines, sized for a phone. */
  #lp-table.tbl--rows .ledger-subject { font-size: 14px; font-weight: 500; }
  #lp-table.tbl--rows .ledger-sub { margin-top: 0; }
}

/* The nested-vertical-scroll fix, app-wide — see the rationale on .tbl-wrap in
   tables.css. This wrapper backs the deal ledger, the CRM tables and
   admin_user_list, so one rule retires every double-scroller at once. */
@media (max-width: 768px) {
  .table-wrap-sticky { max-height: none; overflow-y: hidden; }
}

/* ── 8.3 Maps & Legends ── */
.ql-map-panel { height: 480px; border: 1px solid var(--c-rule); border-radius: 0; background: var(--c-bone); overflow: hidden; position: relative; margin-bottom: 24px; }
.map-legend-overlay { position: absolute; bottom: 12px; right: 12px; background: rgba(244,244,241,0.96); border: 1px solid var(--c-rule); padding: 8px 12px; display: flex; flex-direction: column; gap: 6px; z-index: 999; }
.map-legend-row { display: flex; align-items: center; gap: 6px; font-size: 10px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--c-muted); }
.map-legend-dot { width: 8px; height: 8px; border-radius: 0; }

/* ── 8.4 Drawers & Modals (Tasks, Filters, Confirm) ── */
.ql-drawer-overlay { display: none; position: fixed; inset: 0; background: rgba(26,26,26,0.4); z-index: 200; backdrop-filter: blur(2px); }
.ql-drawer-overlay.is-open { display: block; }
.ql-drawer { position: fixed; top: 0; right: 0; width: 440px; max-width: 100vw; height: 100vh; background: var(--c-white); border-left: 1px solid var(--c-rule); z-index: 201; display: flex; flex-direction: column; transform: translateX(100%); transition: transform .25s cubic-bezier(.4,0,.2,1); }
.ql-drawer.is-open { transform: translateX(0); }
.ql-drawer__head { display: flex; align-items: center; justify-content: space-between; padding: 20px 24px; border-bottom: 1px solid var(--c-rule); background: var(--c-bone); }
.ql-drawer__body { padding: 24px; flex: 1; overflow-y: auto; background: var(--c-white); }

.site-confirm-bd { display: none; position: fixed; inset: 0; background: rgba(26, 26, 26, 0.55); z-index: 99000; align-items: center; justify-content: center; }
.site-confirm-bd.is-open { display: flex; }
.site-confirm-card { background: var(--c-white); border-radius: 0; border: 1px solid var(--c-rule); padding: 28px 28px 22px; max-width: 380px; width: 90%; animation: astor-rise 0.2s ease both; }
.site-confirm-btns { display: flex; gap: 10px; justify-content: flex-end; margin-top: 22px; }
/* The icon/title/msg/button rules live only in home.css (public bundle); the
   tools bundle (this file) needs them too or the modal renders unstyled on
   deal/CRM pages. Astor §8.1 icon well · §8.2 buttons. Oxblood marks the
   destructive default; the primary variant (data-confirm-variant="primary")
   is ink-on-bone for benign actions like sending an email. */
.site-confirm-icon { width: 40px; height: 40px; display: flex; align-items: center; justify-content: center; background: var(--c-stone); color: var(--c-oxblood); margin-bottom: 16px; }
.site-confirm-icon svg { display: block; }
.site-confirm-title { font-size: 15px; font-weight: 600; color: var(--c-ink); letter-spacing: -0.01em; margin-bottom: 6px; }
.site-confirm-msg { font-size: 13px; color: var(--c-muted); line-height: 1.55; }
.site-confirm-cancel { padding: 7px 18px; border: 1px solid var(--c-ink); background: transparent; color: var(--c-ink); font-size: 13px; font-weight: 500; letter-spacing: 0.02em; cursor: pointer; border-radius: 0; }
.site-confirm-cancel:hover { opacity: 0.7; }
.site-confirm-ok { padding: 7px 22px; border: 1px solid var(--c-ink); background: var(--c-ink); color: var(--c-white); font-size: 13px; font-weight: 500; letter-spacing: 0.02em; cursor: pointer; border-radius: 0; }
.site-confirm-ok:hover { opacity: 0.85; }
.site-confirm--primary .site-confirm-icon { color: var(--c-ink); background: var(--c-bone); }

/* ── The SEND panel: a second shape for the same confirm ─────────────────────
   Cain, 2026-08-27, after issuing an AIP: *"Ideally I want a proper
   confirmation popup which shows a viewable copy of what is about to be sent
   and who to (editable on the who to)."*

   The confirm above sets its body with `textContent` — plain text, 380px, no
   markup — so the whole confirmation for an outbound document was a sentence,
   and the operator took both the CONTENT and the RECIPIENT on trust. Fine
   while "send" meant "everyone already on the deal"; not fine once the same
   press can INVITE somebody.

   NOT A SECOND MODAL SYSTEM. The backdrop, the rise, the Cancel/OK pair and
   the Escape + backdrop-click handlers are the ones already in base.html. All
   that changes is that the card may be wide and may hold markup. */
.site-confirm-card--issue { max-width: 940px; padding: 0; text-align: left; }

.issue-head { display: flex; align-items: baseline; gap: 12px;
  padding: 18px 22px 14px; border-bottom: 1px solid var(--c-rule); }
.issue-head__title { font-size: 15px; font-weight: 600; color: var(--c-ink);
  letter-spacing: -0.01em; }
.issue-head__deal { font-size: 12px; color: var(--c-muted); margin-left: auto; }

.issue-body { display: grid; grid-template-columns: 1fr 320px; }
@media (max-width: 800px) { .issue-body { grid-template-columns: 1fr; } }

.issue-doc { border-right: 1px solid var(--c-rule); background: var(--c-stone);
  display: flex; flex-direction: column; }
.issue-doc iframe { width: 100%; height: 460px; border: 0; display: block;
  background: var(--c-stone); }
.issue-doc__foot { padding: 8px 14px; border-top: 1px solid var(--c-rule);
  font-size: 11px; color: var(--c-muted); display: flex; gap: 10px;
  align-items: baseline; background: var(--c-white); }
.issue-doc__foot a { color: var(--c-ink); }

.issue-who { padding: 16px 18px; display: flex; flex-direction: column; gap: 14px;
  max-height: 492px; overflow-y: auto; }
.issue-who__lbl { font-size: 10px; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.1em; color: var(--c-muted); }
.issue-rcpt { display: flex; align-items: flex-start; gap: 9px; padding: 8px 0;
  border-bottom: 1px solid var(--c-rule); cursor: pointer; }
.issue-rcpt input[type=checkbox] { width: auto; margin: 2px 0 0; flex: 0 0 auto; }
.issue-rcpt__who { font-size: 13px; color: var(--c-ink); line-height: 1.35; }
.issue-rcpt__meta { display: block; font-size: 11px; color: var(--c-muted); }
.issue-add { display: flex; flex-direction: column; gap: 6px; }
.issue-add label { font-size: 11px; color: var(--c-muted); }
.issue-add input[type=email] { width: 100%; padding: 8px 10px; border-radius: 0;
  border: 1px solid var(--c-rule); background: var(--c-white); font-size: 13px;
  color: var(--c-ink); font-family: var(--font-sans); }
.issue-note { font-size: 11px; color: var(--c-muted); line-height: 1.5; margin: 0; }
.issue-note strong { color: var(--c-ink); font-weight: 600; }
.issue-foot { display: flex; gap: 10px; justify-content: flex-end;
  padding: 14px 22px; border-top: 1px solid var(--c-rule); background: var(--c-bone); }

/* ── 8.5 Deal Stepper & Forms ── */
.dd-stepper { background: var(--c-white); border: 1px solid var(--c-rule); padding: 16px 20px; margin-bottom: 24px; }
/* Head: eyebrow title left, the status control right — one baseline row. */
.dd-stepper__head { display: flex; align-items: center; justify-content: space-between; gap: 12px 16px; flex-wrap: wrap; }
.dd-stepper__title { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.1em; color: var(--c-muted); }
.dd-stepper__form { display: flex; align-items: center; gap: 8px; margin: 0; }
.dd-stepper__select { font-family: var(--font-sans); font-size: 12px; font-weight: 500; padding: 6px 28px 6px 10px; border: 1px solid var(--c-rule); background-color: var(--c-white); color: var(--c-ink); border-radius: 0; outline: none; cursor: pointer;
  -webkit-appearance: none; appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 24 24' fill='none' stroke='%237A7A74' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 9px center; }
.dd-stepper__select:focus { border-color: var(--c-ink); }
.dd-stepper__submit { font-family: var(--font-sans); font-size: 12px; font-weight: 600; padding: 6px 14px; border: 1px solid var(--c-ink); background: var(--c-ink); color: var(--c-white); border-radius: 0; cursor: pointer; transition: background 100ms ease; }
.dd-stepper__submit:hover { background: var(--c-black); }
.dd-steps { display: flex; gap: 0; overflow-x: auto; margin-top: 16px; }
/* `min-width: 0`, not 84px (2026-09-19, lane P2). Measured on the deal page: at 84px six steps
   need 504px, so at 1280 the 478px box hid Completed behind a sideways scroll, and at 390 only
   4 of 6 showed and the PAGE scrolled. Every step now shares the line and its label wraps. */
.dd-step { flex: 1; min-width: 0; text-align: center; position: relative; padding: 0 2px; }
.dd-step::before { content: ''; position: absolute; top: 12px; left: 50%; right: -50%; height: 1px; background: var(--c-rule); z-index: 0; }
.dd-step:last-child::before { display: none; }
.dd-step__dot { width: 24px; height: 24px; border: 1px solid var(--c-rule); background: var(--c-white); display: flex; align-items: center; justify-content: center; font-size: 11px; font-weight: 700; margin: 0 auto 6px; position: relative; z-index: 1; color: var(--c-muted); border-radius: 0; }
.dd-step.is-current .dd-step__dot, .dd-step.is-done .dd-step__dot { background: var(--c-ink); border-color: var(--c-ink); color: var(--c-white); }
.dd-step.is-done::before { background: var(--c-ink); }
.dd-step__lbl { font-size: 10px; font-weight: 500; color: var(--c-muted); line-height: 1.3; }
.dd-step.is-current .dd-step__lbl, .dd-step.is-done .dd-step__lbl { color: var(--c-ink); font-weight: 700; }
/* Workout — redeemed short. An exception off-ramp, not a forward step: shown
   only when the deal is in it, and given the loss treatment (oxblood). */
.dd-step--workout .dd-step__dot { background: var(--c-oxblood); border-color: var(--c-oxblood); color: var(--c-white); }
.dd-step--workout .dd-step__lbl { color: var(--c-oxblood); font-weight: 700; }
.dd-step--workout::before { background: var(--c-oxblood); }
/* An off-ramp — Rejected, Requires more work (Cain approved the drawing, 2026-09-19). Not a
   step forward: nothing is ticked, and the stage is the terminal marker in the loss colour,
   as a workout is. Used by deal_detail.html and guest/deal_detail.html since PR 1411. */
.dd-step--offramp .dd-step__dot { background: var(--c-oxblood); border-color: var(--c-oxblood); color: var(--c-white); }
.dd-step--offramp .dd-step__lbl { color: var(--c-oxblood); font-weight: 700; }
/* A phone: every step keeps its box on the one line, and only the step the deal is AT keeps its
   label — the current step, a workout, an off-ramp. Seven labels do not fit 390px without
   breaking mid-word, and the one that matters is the one the deal is at. */
@media (max-width: 600px) {
  .dd-step:not(.is-current) .dd-step__lbl { display: none; }
  .dd-step.is-current { flex: 2; }
}

/* ── 8.6 Toasts ── */
.site-toasts { position: fixed; bottom: 24px; right: 24px; z-index: 9000; display: flex; flex-direction: column-reverse; gap: 10px; pointer-events: none; max-width: 360px; }
.site-toast { display: flex; align-items: flex-start; gap: 12px; padding: 13px 16px; border-radius: 0; background: var(--c-ink); color: var(--c-white); font-size: 13px; font-weight: 500; pointer-events: all; border-left: 3px solid var(--c-rule); }
.site-toast--error { border-left-color: var(--c-oxblood); }

/* ── 8.7 Form Layout Utilities ── */
.form-section { padding-top: 24px; margin-top: 24px; border-top: 1px solid var(--c-rule); }
.form-section:first-child { border-top: none; padding-top: 0; margin-top: 0; }
.form-section__title { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.12em; color: var(--c-muted); margin-bottom: 16px; }

/* Name hero (org/contact form main field) */
.name-hero { margin-bottom: 24px; padding-bottom: 24px; border-bottom: 1px solid var(--c-rule); }
.name-hero__label { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.12em; color: var(--c-muted); margin-bottom: 8px; }
.name-hero__hint { font-size: 11px; color: var(--c-pale); margin-top: 6px; line-height: 1.5; }
.name-hero__legal-row { margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--c-rule); }
.name-hero__legal-label { font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.10em; color: var(--c-muted); margin-bottom: 6px; display: flex; align-items: center; gap: 5px; }
.name-hint { display: flex; align-items: flex-start; gap: 5px; font-size: 11px; color: var(--c-pale); margin-top: 5px; line-height: 1.4; }

/* Companies House Lookup */
.ch-lookup-wrap { background: var(--c-bone); border: 1px solid var(--c-rule); padding: 16px 18px; margin-bottom: 24px; }
.ch-lookup-title { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.12em; color: var(--c-muted); margin-bottom: 12px; display: flex; align-items: center; gap: 6px; }
.ch-lookup-row { display: flex; gap: 8px; align-items: flex-end; }
.ch-lookup-input { flex: 1; max-width: 400px; padding: 8px 12px; border: 1px solid var(--c-rule); border-radius: 0; font-size: 13px; background: var(--c-white); color: var(--c-ink); outline: none; }
.ch-lookup-input:focus { border-color: var(--c-ink); background: var(--c-bone); }
.ch-lookup-btn { padding: 8px 14px; border: 1px solid var(--c-rule); border-radius: 0; font-size: 12px; font-weight: 600; background: var(--c-white); color: var(--c-muted); cursor: pointer; white-space: nowrap; }
.ch-lookup-btn:hover { border-color: var(--c-ink); color: var(--c-ink); background: var(--c-bone); }
.ch-linked-badge { display: none; align-items: center; gap: 8px; background: var(--c-white); border: 1px solid var(--c-ink); padding: 8px 12px; font-size: 12px; font-weight: 600; color: var(--c-ink); margin-bottom: 10px; }
.ch-linked-badge.is-visible { display: flex; }
.ch-linked-badge__remove { background: none; border: none; font-size: 16px; color: var(--c-muted); cursor: pointer; line-height: 1; padding: 0 2px; }
.ch-results { border: 1px solid var(--c-rule); background: var(--c-white); margin-top: 8px; max-height: 340px; overflow-y: auto; }
.ch-result-item { display: block; padding: 10px 14px; cursor: pointer; border-bottom: 1px solid var(--c-rule); }
.ch-result-item:last-child { border-bottom: none; }
.ch-result-item:hover { background: var(--c-bone); }
.ch-detail-row { display: none; margin-top: 12px; }
.ch-detail-row.is-visible { display: block; }
.ch-detail-table { width: 100%; border-collapse: collapse; font-size: 12px; }
.ch-detail-table td { padding: 5px 10px; border-bottom: 1px solid var(--c-rule); }
.ch-detail-table td:first-child { font-weight: 600; color: var(--c-muted); width: 38%; white-space: nowrap; }

/* CH mini lookup (inline inside contact form org picker) */
.ch-mini-row { display: flex; gap: 8px; }
.ch-mini-input { flex: 1; padding: 7px 10px; border: 1px solid var(--c-rule); border-radius: 0; font-size: 12px; background: var(--c-white); color: var(--c-ink); outline: none; }
.ch-mini-input:focus { border-color: var(--c-ink); }
.ch-mini-btn { padding: 7px 12px; border: 1px solid var(--c-rule); border-radius: 0; font-size: 11px; font-weight: 600; background: var(--c-white); color: var(--c-muted); cursor: pointer; white-space: nowrap; }
.ch-mini-btn:hover { border-color: var(--c-ink); color: var(--c-ink); }
.ch-mini-results { display: none; border: 1px solid var(--c-rule); background: var(--c-white); margin-top: 6px; max-height: 220px; overflow-y: auto; }
.ch-mini-item { padding: 8px 12px; font-size: 12px; cursor: pointer; border-bottom: 1px solid var(--c-rule); display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.ch-mini-item:last-child { border-bottom: none; }
.ch-mini-item:hover { background: var(--c-bone); }
.legal-name-row { display: none; align-items: center; gap: 8px; font-size: 12px; color: var(--c-muted); padding: 8px 12px; border-left: 2px solid var(--c-ink); background: var(--c-bone); margin: 8px 0; }
.legal-name-row.is-visible { display: flex; }

/* ── 8.7 Ledger Table Utilities ── */
.ledger-subject { font-size: 14px; font-weight: 500; color: var(--c-ink); line-height: 1.3; }
.ledger-sub { font-size: 11px; color: var(--c-muted); margin-top: 2px; }
.table-btn { display: inline-flex; align-items: center; font-size: 11px; font-weight: 600; letter-spacing: 0.04em; padding: 3px 10px; border: 1px solid var(--c-rule); color: var(--c-muted); background: transparent; cursor: pointer; text-decoration: none; border-radius: 0; white-space: nowrap; }
.table-btn:hover { border-color: var(--c-ink); color: var(--c-ink); background: var(--c-bone); }
.table-btn--muted { opacity: 0.55; }
.action-btn.is-active, .btn.is-active { background: var(--c-ink); color: var(--c-white); border-color: var(--c-ink); }
.ql-avatar--sm { width: 28px; height: 28px; font-size: 11px; }
.ql-avatar--md { width: 44px; height: 44px; font-size: 16px; }
.ql-avatar--lg { width: 64px; height: 64px; font-size: 24px; }

/* ── 8.8 Deal Pipeline (Lender) ── */
.lp-toggle { display: inline-flex; border: 1px solid var(--c-rule); }
.lp-toggle__btn { padding: 6px 14px; font-size: 11px; font-weight: 600; color: var(--c-muted); text-decoration: none; border-right: 1px solid var(--c-rule); white-space: nowrap; }
.lp-toggle__btn:last-child { border-right: none; }
.lp-toggle__btn.is-active { background: var(--c-ink); color: var(--c-white); }

/* Header overflow menu — collects the low-frequency utilities (imports, exports,
   facility-fit) behind one discreet control so the action strip reads as
   view-toggle · primary CTA. Native <details>, mirrors the top-nav dropdown. */
.lp-menu { position: relative; }
.lp-menu__trigger { list-style: none; cursor: pointer; }
.lp-menu__trigger::-webkit-details-marker { display: none; }
.lp-menu__chev { transition: transform 0.15s ease; margin-left: 2px; }
.lp-menu[open] .lp-menu__chev { transform: rotate(180deg); }
.lp-menu__panel { position: absolute; top: calc(100% + 6px); right: 0; min-width: 208px;
  background: rgba(244, 244, 241, 0.98); border: 1px solid var(--c-rule); border-radius: 0;
  box-shadow: 0 8px 28px rgba(26, 26, 26, 0.10); padding: 6px; z-index: 1000;
  display: flex; flex-direction: column; }
.lp-menu__label { padding: 8px 12px 4px; font-size: 9px; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.10em; color: var(--c-pale); }
.lp-menu__item { display: block; padding: 8px 12px; font-size: 0.78rem; color: var(--c-muted);
  text-decoration: none; white-space: nowrap; }
.lp-menu__item:hover { background: rgba(26, 26, 26, 0.06); color: var(--c-ink); }
.lp-menu__divider { height: 1px; background: var(--c-rule); margin: 6px 4px; }
.lp-pipeline { display: flex; overflow-x: auto; background: var(--c-white); border-bottom: 1px solid var(--c-rule); }
/* min-width is `max-content`, NOT a fixed px floor. An explicit px min-width
   REPLACES flexbox's automatic min-content floor, so the stages compressed to
   90px while `.lp-pipeline__label` is `white-space:nowrap` — measured at 375px,
   "Credit Approved" needed 108px inside a 67px box and painted 41px into its
   neighbour. It also made the strip narrower than its content, so the
   `overflow-x:auto` above was dead code and never scrolled. */
.lp-pipeline__stage { flex: 1; min-width: max-content; padding: 14px 16px; border-right: 1px solid var(--c-rule); cursor: pointer; display: flex; flex-direction: column; gap: 3px; text-decoration: none; color: inherit; transition: background 0.15s ease; }
.lp-pipeline__stage:last-child { border-right: none; }
.lp-pipeline__stage:hover { background: var(--c-bone); }
.lp-pipeline__stage.is-empty { opacity: 0.4; }
.lp-pipeline__dot { width: 7px; height: 7px; background: var(--c-ink); }
/* Hollow "start" dot for the first stage, muted for the terminal one. */
.lp-pipeline__dot--deal_received { background: var(--c-rule); border: 1px solid var(--c-muted); }
.lp-pipeline__dot--completed { background: var(--c-muted); }
.lp-pipeline__dot--workout { background: var(--c-oxblood); }
.lp-pipeline__count { font-size: 22px; font-weight: 300; font-family: var(--font-mono); letter-spacing: -0.04em; color: var(--c-ink); }
.lp-pipeline__label { font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.10em; color: var(--c-muted); white-space: nowrap; }
.lp-section-header { display: flex; align-items: center; justify-content: space-between; padding: 12px 16px; border-bottom: 1px solid var(--c-rule); background: var(--c-bone); flex-wrap: wrap; gap: 8px; }
.lp-section-title { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.10em; color: var(--c-ink); }
.lp-section-count { font-size: 11px; color: var(--c-muted); }
.lp-filter-btns { display: flex; gap: 6px; flex-wrap: wrap; }
.lp-filter-btn { padding: 3px 10px; font-size: 11px; border: 1px solid var(--c-rule); background: transparent; color: var(--c-muted); cursor: pointer; border-radius: 0; white-space: nowrap; }
.lp-filter-btn.active { background: var(--c-ink); color: var(--c-white); border-color: var(--c-ink); }
.lp-search-wrap { display: flex; align-items: center; gap: 8px; padding: 10px 16px; border-bottom: 1px solid var(--c-rule); }
.lp-search-icon { color: var(--c-muted); flex-shrink: 0; }
.lp-search { flex: 1; border: none; background: transparent; font-size: 13px; color: var(--c-ink); outline: none; }
.lp-search::placeholder { color: var(--c-pale); }
/* Stage strip + filter buttons are now server-side links (§4.2). */
.lp-filter-btn { text-decoration: none; display: inline-block; }
.lp-filter-btn:hover { border-color: var(--c-ink); color: var(--c-ink); }
.lp-filter-btn.active:hover { color: var(--c-white); }
/* Active stage: white lift + an ink underline bar (tab-like), no inline hack. */
.lp-pipeline__stage.is-active { background: var(--c-white); box-shadow: inset 0 -3px 0 var(--c-ink); opacity: 1; }
.lp-pipeline__stage.is-active .lp-pipeline__label { color: var(--c-ink); }

/* Table toolbar — one row: filtered count + reset on the left, search on the
   right. Replaces the old caption row + separate full-width search. */
.lp-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 10px 16px; border-bottom: 1px solid var(--c-rule); background: var(--c-bone); flex-wrap: wrap; }
.lp-toolbar__meta { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.lp-toolbar__search { display: flex; align-items: center; gap: 8px; flex: 1; min-width: 200px; max-width: 340px;
  margin-left: auto; background: var(--c-white); border: 1px solid var(--c-rule); padding: 6px 12px; }
.lp-clear { font-size: 11px; letter-spacing: 0.02em; color: var(--c-muted); text-decoration: none;
  border: 1px solid var(--c-rule); padding: 2px 9px; white-space: nowrap; }
.lp-clear:hover { border-color: var(--c-ink); color: var(--c-ink); background: var(--c-white); }
.lp-pager { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--c-rule); flex-wrap: wrap; }
.lp-pager__meta, .lp-pager__pos { font-size: 11px; color: var(--c-muted); letter-spacing: 0.04em; }
.lp-pager__nav { display: flex; align-items: center; gap: 10px; }
.action-btn.is-disabled { opacity: 0.4; pointer-events: none; }

/* ── 8.9 Borough Planning Accordion ── */
.bpl-disclaimer { display: flex; gap: 10px; align-items: flex-start; background: var(--c-bone); border: 1px solid var(--c-rule); border-left: 3px solid var(--c-ink); padding: 12px 16px; margin-bottom: 16px; font-size: 12px; color: var(--c-muted); line-height: 1.6; }
.bpl-disclaimer a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
.bpl-accordion { border: 1px solid var(--c-rule); background: var(--c-white); }
.bpl-accordion-row { border-bottom: 1px solid var(--c-rule); }
.bpl-accordion-row:last-child { border-bottom: none; }
.bpl-accordion-head { display: flex; align-items: center; gap: 10px; padding: 14px 16px; cursor: pointer; user-select: none; }
.bpl-accordion-head:hover { background: var(--c-bone); }
.bpl-accordion-name { font-size: 13px; font-weight: 600; color: var(--c-ink); flex: 1; min-width: 0; }
.bpl-accordion-body { display: none; padding: 20px; border-top: 1px solid var(--c-rule); background: var(--c-bone); }
.bpl-accordion-row.is-open .bpl-accordion-body { display: block; }
.bpl-accordion-row.is-open .bpl-chevron { transform: rotate(90deg); }
.bpl-chevron { color: var(--c-pale); font-size: 11px; transition: transform 0.15s; flex-shrink: 0; display: inline-block; }
.bpl-pill { display: inline-flex; align-items: center; font-size: 9px; font-weight: 700; padding: 2px 7px; text-transform: uppercase; letter-spacing: 0.06em; white-space: nowrap; border-radius: 0; }
.bpl-pill--a4 { border: 1px solid var(--c-oxblood); color: var(--c-oxblood); }
.bpl-pill--cons { border: 1px solid var(--c-ink); color: var(--c-ink); }
.bpl-pill--none, .bpl-pill--nodata { border: 1px solid var(--c-rule); color: var(--c-pale); }
.bpl-section { margin-bottom: 16px; background: var(--c-white); border: 1px solid var(--c-rule); padding: 16px; }
.bpl-section:last-child { margin-bottom: 0; }
.bpl-section-label { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.10em; color: var(--c-muted); margin-bottom: 10px; }
.bpl-source-badge { font-size: 9px; background: var(--c-stone); padding: 1px 6px; color: var(--c-muted); font-weight: 400; text-transform: none; letter-spacing: 0; margin-left: 6px; }
.bpl-source-badge--ai { background: rgba(96,0,0,0.06); color: var(--c-oxblood); }
.bpl-impact-summary { padding: 10px 14px; border-left: 2px solid var(--c-rule); margin-bottom: 12px; font-size: 12px; color: var(--c-muted); }
.bpl-impact-summary--high { border-left-color: var(--c-oxblood); color: var(--c-oxblood); }
.bpl-impact-summary--medium { border-left-color: var(--c-ink); color: var(--c-ink); }
.bpl-impact-icon { margin-right: 4px; }
.bpl-group { margin-bottom: 12px; }
.bpl-group-label { font-size: 11px; font-weight: 700; color: var(--c-ink); margin-bottom: 6px; }
.bpl-group-count { font-weight: 400; color: var(--c-muted); }
.bpl-a4-list { display: flex; flex-direction: column; gap: 2px; }
.bpl-a4-detail { border: 1px solid var(--c-rule); }
.bpl-a4-detail summary { list-style: none; }
.bpl-a4-detail summary::-webkit-details-marker { display: none; }
.bpl-a4-item { display: flex; align-items: center; gap: 8px; padding: 8px 12px; font-size: 12px; color: var(--c-ink); cursor: pointer; }
.bpl-a4-dot { width: 5px; height: 5px; background: var(--c-oxblood); flex-shrink: 0; }
.bpl-a4-name { flex: 1; }
.bpl-a4-since { font-size: 10px; color: var(--c-muted); white-space: nowrap; }
.bpl-hmo-badge { background: rgba(96,0,0,0.08); color: var(--c-oxblood); font-size: 9px; padding: 1px 5px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; }
.bpl-a4-cards { padding: 8px 12px 12px 26px; display: flex; flex-direction: column; gap: 6px; }
.bpl-a4-card { padding: 8px 12px; border-left: 2px solid var(--c-rule); }
.bpl-a4-card--high { border-left-color: var(--c-oxblood); }
.bpl-a4-card--medium { border-left-color: var(--c-ink); }
.bpl-a4-card-label { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: var(--c-muted); margin-bottom: 3px; }
.bpl-a4-card-text { font-size: 12px; color: var(--c-ink); }
.bpl-a4-card-who { font-size: 10px; color: var(--c-muted); margin-top: 3px; }
.bpl-a4-doc { padding: 4px 12px 8px; font-size: 11px; }
.bpl-a4-doc a { color: var(--c-muted); text-decoration: underline; text-underline-offset: 2px; }
.bpl-pro-link { display: inline-flex; align-items: center; gap: 6px; font-size: 11px; font-weight: 600; color: var(--c-muted); border: 1px solid var(--c-rule); padding: 5px 12px; margin-top: 12px; text-decoration: none; }
.bpl-pro-link:hover { border-color: var(--c-ink); color: var(--c-ink); }
.bpl-pro-badge { font-size: 10px; }
.bpl-cons-text { font-size: 12px; color: var(--c-muted); line-height: 1.7; }
.bpl-no-data { font-size: 12px; color: var(--c-pale); font-style: italic; }
.bpl-freshness { font-size: 10px; color: var(--c-pale); margin-top: 12px; border-top: 1px solid var(--c-rule); padding-top: 10px; }
/* A borough whose data has STOPPED ARRIVING states it in the card's normal
   ink, not in the pale used for fine print — a reader skimming the card must
   not skip the one line saying the rest of it is out of date. Deliberately
   NOT an alarm colour: staleness is a fact about our records, and the
   data-hedging rule keeps absence muted rather than loud (§11.5.2). */
.bpl-freshness--stale { color: var(--c-muted); }
/* Same rule on the borough page's header strip: a stopped feed is stated in
   the strip's own ink rather than dimmed away. Not an alarm colour. */
.bi-currency--stale { color: var(--c-ink); font-weight: 500; }

/* ── 8.10 Planning Watch List ── */
.aa-lookup-row { display: flex; gap: 8px; align-items: flex-end; }
.aa-result-bar { font-size: 13px; padding: 10px 14px; border: 1px solid var(--c-rule); background: var(--c-bone); }
.al-tabs { display: flex; align-items: center; border-bottom: 1px solid var(--c-rule); }
.al-tab { padding: 10px 16px; font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.08em; color: var(--c-muted); background: transparent; border: none; border-bottom: 2px solid transparent; cursor: pointer; border-radius: 0; }
.al-tab:hover { color: var(--c-ink); }
.al-tab.is-active { color: var(--c-ink); border-bottom-color: var(--c-ink); }
.al-tabs__count { margin-left: auto; padding-right: 16px; font-size: 12px; color: var(--c-muted); }
.watch-table-scroll { overflow-x: auto; }
.wl-address { font-size: 11px; color: var(--c-muted); margin-top: 2px; }
.wl-actions { display: flex; gap: 6px; align-items: center; }
.wl-actions-cell { text-align: right; white-space: nowrap; }

/* Sortable column headers — caret appears on hover, points on active sort */
.ql-table th.js-sort { cursor: pointer; user-select: none; white-space: nowrap; }
.ql-table th.js-sort:hover { color: var(--c-ink); }
.sort-caret { display: inline-block; width: 0; height: 0; margin-left: 4px; vertical-align: middle;
  border-left: 4px solid transparent; border-right: 4px solid transparent; border-bottom: 5px solid var(--c-pale); opacity: 0; transition: opacity 0.12s; }
.ql-table th.js-sort:hover .sort-caret { opacity: 0.6; }
.ql-table th[aria-sort] .sort-caret { opacity: 1; border-bottom-color: var(--c-ink); }
.ql-table th[aria-sort="descending"] .sort-caret { border-bottom: none; border-top: 5px solid var(--c-ink); }
.ql-table th.ql-num, .ql-table td.ql-num { text-align: right; }
.ql-cell-empty { color: var(--c-pale); }

/* Clickable data rows (whole row navigates; buttons/links still work) */
.ql-table--rows tbody tr[data-href] { cursor: pointer; }
.ql-table--rows tbody tr[data-href]:hover td { background: var(--c-bone); }

/* Stop → undo: dim in place, then collapse out */
.ql-table tbody tr.row-stopping td { opacity: 0.45; transition: opacity 0.18s; }
.ql-table tbody tr.row-fade-out td { opacity: 0; transition: opacity 0.2s; }

.ql-sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.coverage-modal { position: fixed; inset: 0; z-index: 8000; display: flex; align-items: flex-start; justify-content: center; padding-top: 40px; }
.coverage-modal-backdrop { position: absolute; inset: 0; background: rgba(26,26,26,0.5); }
.coverage-modal-content { background: var(--c-white); border: 1px solid var(--c-rule); max-width: 760px; width: calc(100% - 32px); max-height: calc(100vh - 80px); display: flex; flex-direction: column; position: relative; z-index: 1; }
.coverage-modal-header { display: flex; align-items: center; justify-content: space-between; padding: 16px 20px; border-bottom: 1px solid var(--c-rule); background: var(--c-bone); }
.coverage-modal-header h3 { font-size: 13px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.10em; margin: 0; }
.coverage-modal-close { background: none; border: 1px solid var(--c-rule); cursor: pointer; width: 28px; height: 28px; display: flex; align-items: center; justify-content: center; color: var(--c-muted); border-radius: 0; }
.coverage-modal-body { padding: 20px; overflow-y: auto; flex: 1; }
.coverage-intro { font-size: 13px; color: var(--c-muted); margin-bottom: 20px; line-height: 1.5; }
.coverage-section-label { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.12em; color: var(--c-muted); margin: 16px 0 8px; display: flex; align-items: center; gap: 8px; }
.coverage-section-dot { width: 8px; height: 8px; }
.coverage-section-dot--auto { background: var(--c-ink); }
.coverage-section-dot--manual { background: var(--c-pale); }
.coverage-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 8px; }
.coverage-card { border: 1px solid var(--c-rule); padding: 10px 12px; }
.coverage-card--manual { border-style: dashed; opacity: 0.65; }
.coverage-card-header { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.coverage-card-header h4 { font-size: 12px; font-weight: 600; margin: 0; }
.coverage-badge { font-size: 10px; color: var(--c-ink); font-weight: 600; display: inline-flex; align-items: center; }
.coverage-portal-link { font-size: 10px; color: var(--c-muted); white-space: nowrap; }
.coverage-portal-link:hover { color: var(--c-ink); }
.coverage-postcodes { margin-top: 8px; display: flex; flex-wrap: wrap; gap: 4px; }
.postcode-chip { font-size: 10px; font-family: var(--font-mono); background: var(--c-bone); padding: 2px 6px; color: var(--c-muted); }
.coverage-footer { margin-top: 20px; padding-top: 16px; border-top: 1px solid var(--c-rule); }
.coverage-footer p { font-size: 12px; color: var(--c-muted); margin-bottom: 10px; }
.coverage-request-btn { display: inline-flex; align-items: center; font-size: 11px; font-weight: 600; padding: 6px 14px; border: 1px solid var(--c-ink); color: var(--c-ink); text-decoration: none; }
.coverage-request-btn:hover { background: var(--c-ink); color: var(--c-white); }
.undo-toast { display: none; position: fixed; bottom: 24px; left: 50%; transform: translateX(-50%); background: var(--c-ink); color: var(--c-white); padding: 12px 20px; z-index: 9500; min-width: 280px; align-items: center; gap: 16px; font-size: 13px; }
.undo-toast.is-visible { display: flex; }
.undo-msg { flex: 1; }
.undo-actions { display: flex; align-items: center; gap: 10px; flex-shrink: 0; }
.undo-timer { font-size: 11px; color: rgba(255,255,255,0.5); font-family: var(--font-mono); }
.undo-btn { background: none; border: 1px solid rgba(255,255,255,0.4); color: var(--c-white); font-size: 11px; font-weight: 600; padding: 3px 10px; cursor: pointer; border-radius: 0; }
.undo-btn:hover { background: rgba(255,255,255,0.1); }

/* ─────────────────────────────────────────────────────────────────────────────
   9. MOBILE & GLOBAL UI Sweeps
   ───────────────────────────────────────────────────────────────────────────── */

/* Strip all remaining rounded corners to enforce the Manifesto */
.site-toast, .ql-drawer, .site-confirm-card, .table-wrap-sticky, .ql-map-panel, .map-legend-dot { border-radius: 0; }

/* Mobile Bottom Sheet */
.mobile-sheet-backdrop { display: none; position: fixed; inset: 0; background: rgba(0, 0, 0, 0.3); z-index: 996; }
.mobile-sheet-backdrop.is-open { display: block; }
/* Bottom sheets — ASTOR §8.3 mandates astor-rise for bottom sheets. The 100%
   transform-slide previously used here is not a sanctioned animation. */
.mobile-sheet { position: fixed; left: 0; right: 0; bottom: 0; z-index: 997; background: var(--c-bone); border-top: 1px solid var(--c-rule); padding-bottom: calc(68px + env(safe-area-inset-bottom)); max-height: 72vh; overflow-y: auto; display: none; }
.mobile-sheet.is-open { display: block; animation: astor-rise 0.22s ease-out both; }
.mobile-sheet-handle { width: 36px; height: 4px; background: var(--c-rule); margin: 12px auto 8px; border-radius: 0; }
.mobile-sheet-section { padding: 12px 20px; border-bottom: 1px solid var(--c-rule); }
.mobile-sheet-section:last-child { border-bottom: none; }
.mobile-sheet-label { font-size: 0.65rem; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase; color: var(--c-muted); margin-bottom: 8px; }
.mobile-sheet-link { display: block; padding: 10px 0; font-size: 0.9rem; font-weight: 500; color: var(--c-ink); text-decoration: none; border-bottom: 1px solid var(--c-rule); }
.mobile-sheet-link:last-child { border-bottom: none; }
.mobile-sheet-link.is-active { font-weight: 700; }
.mobile-sheet-link:hover { text-decoration: none; opacity: 0.75; }
.mobile-sheet-btn { background: none; border: none; font-family: inherit; font-size: 0.9rem; font-weight: 500; color: var(--c-oxblood); padding: 10px 0; cursor: pointer; }

/* Mobile Tab Bar */
.mobile-tab-bar { display: none; }

@media (max-width: 768px) {
  .sidebar { position: fixed; top: 0; left: 0; height: 100vh; z-index: 200; transform: translateX(-100%); transition: transform 0.22s ease; width: var(--sidebar-w); }
  .sidebar.is-open { transform: translateX(0); }
  .sidebar-mobile-toggle { display: none !important; } /* bottom tab bar handles mobile nav */
  .ql-page { padding: 24px 16px 48px; }
  .ql-page--dossier { grid-template-columns: 1fr; gap: 24px; }

  .mobile-tab-bar { display: flex; position: fixed; bottom: 0; left: 0; right: 0; z-index: 998; background: var(--c-bone); border-top: 1px solid var(--c-rule); padding-bottom: env(safe-area-inset-bottom); }
  body { padding-bottom: calc(var(--mobile-tab-h, 58px) + env(safe-area-inset-bottom)); }
  .nav-hamburger, .site-nav-links, .sidebar-overlay { display: none !important; }

  /* Side drawer (filters, queue) — full-screen on phones, with slide-up
     feel rather than 440px panel that overflowed the viewport. */
  .ql-drawer { width: 100%; max-width: 100vw; border-left: none; }
  .ql-drawer__head, .ql-drawer__body { padding-left: 16px; padding-right: 16px; }

  /* Toasts must clear the bottom tab bar + iOS home indicator and may not
     overflow the viewport. Mirrors the home.css fix for marketing pages. */
  .site-toasts {
    left: 12px;
    right: 12px;
    bottom: calc(var(--mobile-tab-h, 58px) + 12px + env(safe-area-inset-bottom));
    max-width: none;
  }
  .undo-toast {
    left: 12px;
    right: 12px;
    transform: none;
    bottom: calc(var(--mobile-tab-h, 58px) + 12px + env(safe-area-inset-bottom));
    min-width: 0;
  }

  /* Coverage modal — desktop reserves 40px top + 80px bottom; on phones
     that wastes nearly half the viewport. Trim padding and let the modal
     use the available height. */
  .coverage-modal { padding-top: max(16px, env(safe-area-inset-top)); }
  .coverage-modal-content { width: calc(100% - 16px); max-height: calc(100vh - 32px - env(safe-area-inset-top) - env(safe-area-inset-bottom)); }

  /* iOS zoom prevention — component-scoped inputs in app.css would otherwise
     override the global 16px rule from layout.css. */
  .ch-lookup-input,
  .ch-mini-input,
  .ql-search input,
  .ql-filter-select,
  .ql-tag-select,
  .lp-search,
  .cmp-picker__select,
  .bi-a4-search,
  .wd-input { font-size: 16px; }
}

.mobile-tab-item { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; padding: 9px 4px 8px; text-decoration: none; color: var(--c-muted); font-size: 0.6rem; letter-spacing: 0.06em; font-weight: 500; text-transform: uppercase; background: none; border: none; min-height: 54px; cursor: pointer; }
.mobile-tab-item.is-active { color: var(--c-ink); }
.mobile-tab-icon { flex-shrink: 0; }

/* ─────────────────────────────────────────────────────────────────────────────
   8.11 Area Intelligence  (London-wide cross-borough hook page)
   Layout: hero → upsell (free only) → method strip → KPI strip →
   featured cards (opps/risks/movers) → map → bar chart → grade heat map →
   filterable table → CTA strip.
   ───────────────────────────────────────────────────────────────────────────── */

/* Hero — Appraisal-Dossier architecture (ASTOR §5). Asymmetric 1fr 380px
   grid: left column carries eyebrow mark → title → sub → hero metric →
   meta strip; right column is the Forensic Card with the headline insight.
   On tablet/mobile collapses to single-column with the Forensic Card below
   the metric, never above (the hero metric is the page's anchor). */
.ai-hero { display: grid; grid-template-columns: 1fr 380px; gap: 48px; align-items: start; padding: 8px 0 32px; margin: 0 0 24px; border-bottom: 1px solid var(--c-rule); }
.ai-hero__main { min-width: 0; }
.ai-hero__title { font-size: clamp(2rem, 3.4vw, 3rem); font-weight: 300; letter-spacing: -0.03em; line-height: 1.05; color: var(--c-ink); margin: 16px 0 12px; }
.ai-hero__sub { font-size: 14px; line-height: 1.65; color: var(--c-muted); max-width: 56ch; margin: 0 0 28px; }
.ai-hero__stat { padding: 8px 0 0; }
.ai-hero__meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding-top: 18px; margin-top: 18px; border-top: 1px solid var(--c-rule); font-size: 11px; color: var(--c-muted); font-family: var(--font-mono); letter-spacing: 0.04em; }
.ai-hero__meta-sep { color: var(--c-pale); }
.ai-hero__live { display: inline-flex; align-items: center; gap: 8px; color: var(--c-ink); font-weight: 600; }
.ai-hero__live--inactive { color: var(--c-muted); font-weight: 500; }

/* Live Stitch dot — ASTOR §8.3. 5px Oxblood square, 0px radius, ql-stitch
   keyframe (sanctioned in tokens.css §12). Driven by `is_data_live` in the
   view (any insight has been generated). */
.ai-stitch-dot { width: 5px; height: 5px; background: var(--c-oxblood); display: inline-block; flex-shrink: 0; animation: ql-stitch 2.8s ease-in-out infinite; }
.ai-hero__live--inactive .ai-stitch-dot { animation: none; background: var(--c-pale); }

/* Eyebrow Mark — ASTOR §8.4. 28px hairline + 14px gap + 10px label.
   Uses the accent-rule shade from the system (rgba(26,26,26,0.20)) — this
   is the only place in the system that rule appears. */
.ql-eyebrow-mark { display: inline-flex; align-items: center; gap: 14px; }
.ql-eyebrow-mark__rule { width: 28px; height: 1px; background: rgba(26, 26, 26, 0.20); display: block; }
.ql-eyebrow-mark__label { font-size: 10px; font-weight: 500; text-transform: uppercase; letter-spacing: 0.18em; color: var(--c-muted); }

/* Forensic Card — extends .ql-forensic-card (defined earlier) with the
   hero-specific layout: lede paragraph, row stack, CTA, signature. */
.ai-hero__forensic { padding: 24px 26px; align-self: stretch; display: flex; flex-direction: column; }
.ai-forensic__lede { font-size: 14px; font-weight: 400; line-height: 1.55; color: var(--c-white); margin: 6px 0 16px; }
.ai-forensic__lede strong { color: var(--c-white); font-weight: 700; font-family: var(--font-mono); font-variant-numeric: tabular-nums; letter-spacing: -0.02em; }
.ai-forensic__rows { display: flex; flex-direction: column; margin-bottom: 16px; }
.ai-forensic__cta { display: inline-flex; align-items: center; justify-content: center; gap: 6px; padding: 10px 14px; font-size: 12px; font-weight: 600; letter-spacing: 0.04em; background: var(--c-white); color: var(--c-ink); text-decoration: none; margin-top: auto; transition: opacity 100ms ease; }
.ai-forensic__cta:hover { opacity: 0.85; }
.ai-hero__forensic--empty .ai-forensic__lede { color: rgba(255,255,255,0.78); font-weight: 300; }

@media (max-width: 980px) {
  .ai-hero { grid-template-columns: 1fr; gap: 32px; }
  .ai-hero__forensic { max-width: 480px; }
}

/* Empty state — shown when no councils have analysis data yet. Replaces
   the cluster of empty featured-cards / map / chart sections that would
   otherwise leave the page with an 80% void between KPIs and table. */
.ai-empty-state { padding: 36px 32px; margin: 0 0 24px; border: 1px solid var(--c-rule); background: var(--c-white); }
.ai-empty-state__title { font-size: 16px; font-weight: 600; color: var(--c-ink); margin: 0 0 6px; }
.ai-empty-state__sub { font-size: 13px; color: var(--c-muted); line-height: 1.6; margin: 0 0 22px; max-width: 60ch; }
.ai-empty-state__steps { display: flex; gap: 24px; flex-wrap: wrap; }
.ai-empty-state__step { display: flex; align-items: center; gap: 10px; font-size: 12px; color: var(--c-muted); }
.ai-empty-state__step-num { display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; background: var(--c-bone); border: 1px solid var(--c-rule); font-family: var(--font-mono); font-size: 11px; font-weight: 600; color: var(--c-ink); }

/* Pro banner — primary conversion surface for free users, sits directly
   under the hero. SaaS pattern: eyebrow + title + sub + 4 bulleted features
   + dominant CTA + trust line. Two-column desktop layout (text vs CTA),
   collapses to single-column on mobile so the CTA is full-width. */
.ai-pro-banner { display: grid; grid-template-columns: 1fr 280px; gap: 32px; padding: 28px 32px; margin: 0 0 28px; background: var(--c-ink); color: var(--c-white); border: 1px solid var(--c-ink); }
.ai-pro-banner__main { min-width: 0; }
.ai-pro-banner__eyebrow { font-size: 10px; font-weight: 700; letter-spacing: 0.18em; text-transform: uppercase; color: rgba(255,255,255,0.55); margin-bottom: 10px; }
.ai-pro-banner__title { font-size: clamp(1.25rem, 2vw, 1.75rem); font-weight: 300; letter-spacing: -0.02em; color: var(--c-white); line-height: 1.15; margin: 0 0 10px; }
.ai-pro-banner__sub { font-size: 13px; line-height: 1.55; color: rgba(255,255,255,0.78); margin: 0 0 14px; max-width: 540px; }
.ai-pro-banner__bullets { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: 1fr 1fr; gap: 6px 28px; }
.ai-pro-banner__bullets li { font-size: 12px; color: rgba(255,255,255,0.78); line-height: 1.55; padding-left: 16px; position: relative; }
.ai-pro-banner__bullets li::before { content: ""; position: absolute; left: 0; top: 8px; width: 6px; height: 1px; background: rgba(255,255,255,0.65); }
.ai-pro-banner__bullets li strong { color: var(--c-white); font-weight: 600; }
.ai-pro-banner__action { display: flex; flex-direction: column; align-items: stretch; justify-content: center; gap: 12px; border-left: 1px solid rgba(255,255,255,0.18); padding-left: 32px; }
.ai-pro-banner__cta { display: inline-flex; align-items: center; justify-content: center; padding: 14px 22px; font-size: 13px; font-weight: 600; letter-spacing: 0.04em; background: var(--c-white); color: var(--c-ink); border: 1px solid var(--c-white); text-decoration: none; white-space: nowrap; transition: background 100ms ease; }
.ai-pro-banner__cta:hover { background: var(--c-bone); }
.ai-pro-banner__trust { margin: 0; font-size: 11px; color: rgba(255,255,255,0.55); line-height: 1.5; text-align: center; }
@media (max-width: 820px) {
  .ai-pro-banner { grid-template-columns: 1fr; padding: 22px 22px; gap: 20px; }
  .ai-pro-banner__action { border-left: none; border-top: 1px solid rgba(255,255,255,0.18); padding-left: 0; padding-top: 18px; }
  .ai-pro-banner__bullets { grid-template-columns: 1fr; }
}

/* Methodology strip — the answer to "what am I looking at?".
   Auto-fitting grid: as many columns as fit at minmax(220px,1fr), so it
   renders as 5 → 4 → 3 → 2 → 1 cleanly across viewport widths without
   needing manual breakpoints. Vertical rules removed (they break ugly when
   the grid wraps); the ink-on-bone palette gives enough separation alone. */
.ai-method { border: 1px solid var(--c-rule); background: var(--c-white); padding: 16px 18px 18px; margin: 0 0 20px; }
.ai-method__title { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.12em; color: var(--c-muted); margin-bottom: 14px; }
.ai-method__items { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 14px 28px; }
.ai-method__item { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.ai-method__key { font-size: 11px; font-weight: 700; color: var(--c-ink); text-transform: uppercase; letter-spacing: 0.06em; }
.ai-method__val { font-size: 12px; color: var(--c-muted); line-height: 1.55; }
.ai-sig-mini { display: inline-block; font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; padding: 1px 5px; border: 1px solid var(--c-rule); }
.ai-sig-mini--opp { border-color: var(--c-ink); color: var(--c-ink); background: var(--c-bone); }
.ai-sig-mini--risk { border-color: var(--c-oxblood); color: var(--c-oxblood); }

/* "/33" denominator inside the headline KPI strip */
.ai-stat-of { font-size: 13px; color: var(--c-muted); font-weight: 400; margin-left: 2px; }

/* Featured cards — top opportunities, risks, movers. Three-up grid that
   collapses to single column on mobile. Each card has an eyebrow, ranked
   list of three boroughs, and a single trailing metric per row. */
.ai-feature-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; margin: 0 0 24px; }
@media (max-width: 880px) { .ai-feature-grid { grid-template-columns: 1fr; } }
.ai-feature { border: 1px solid var(--c-rule); background: var(--c-white); display: flex; flex-direction: column; }
.ai-feature__head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; padding: 12px 14px; border-bottom: 1px solid var(--c-rule); }
.ai-feature__eyebrow { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.1em; color: var(--c-ink); }
.ai-feature__eyebrow--opp::before { content: ""; display: inline-block; width: 6px; height: 6px; background: var(--c-ink); margin-right: 8px; vertical-align: middle; }
.ai-feature__eyebrow--risk::before { content: ""; display: inline-block; width: 6px; height: 6px; background: var(--c-oxblood); margin-right: 8px; vertical-align: middle; }
.ai-feature__eyebrow--risk { color: var(--c-oxblood); }
.ai-feature__hint { font-size: 10px; color: var(--c-muted); }
.ai-feature__list { list-style: none; margin: 0; padding: 0; }
.ai-feature__row { display: grid; grid-template-columns: 22px 1fr auto; gap: 10px; align-items: center; padding: 12px 14px; border-bottom: 1px solid var(--c-rule); }
.ai-feature__row:last-child { border-bottom: none; }
.ai-feature__rank { font-family: var(--font-mono); font-size: 11px; color: var(--c-muted); }
.ai-feature__main { min-width: 0; }
.ai-feature__name { font-size: 13px; font-weight: 600; color: var(--c-ink); text-decoration: none; line-height: 1.25; display: block; }
a.ai-feature__name:hover { text-decoration: underline; }
.ai-feature__line { font-size: 11px; color: var(--c-muted); margin-top: 3px; line-height: 1.45; overflow: hidden; }
.ai-feature__pill { display: inline-block; font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; padding: 1px 5px; border: 1px solid var(--c-oxblood); color: var(--c-oxblood); margin-right: 6px; }
.ai-feature__metric { display: flex; flex-direction: column; align-items: flex-end; flex-shrink: 0; }
.ai-feature__val { font-family: var(--font-mono); font-size: 18px; font-weight: 600; letter-spacing: -0.02em; color: var(--c-ink); line-height: 1; }
.ai-feature__val--up { color: var(--c-ink); }
.ai-feature__val--down { color: var(--c-oxblood); }
.ai-feature__suffix { font-size: 11px; color: var(--c-muted); margin-left: 1px; }
.ai-feature__lbl { font-size: 9px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--c-muted); margin-top: 3px; }
.ai-feature__empty { padding: 22px 14px; font-size: 12px; color: var(--c-muted); text-align: center; }

/* Sort/filter controls */
.ai-controls { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; padding: 0 0 14px; }
.ai-controls__label { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.1em; color: var(--c-muted); white-space: nowrap; }
.ai-controls__sep { width: 1px; height: 18px; background: var(--c-rule); margin: 0 4px; }
.ai-btn { display: inline-flex; align-items: center; padding: 4px 11px; font-size: 11px; font-weight: 500; border: 1px solid var(--c-rule); background: transparent; color: var(--c-muted); cursor: pointer; text-decoration: none; white-space: nowrap; border-radius: 0; }
.ai-btn:hover { border-color: var(--c-ink); color: var(--c-ink); }
.ai-btn--active { background: var(--c-ink); color: var(--c-white); border-color: var(--c-ink); }

/* Ranked table — re-skinned via .bi-type-table with these tweaks */
.ai-rank-table tbody tr { transition: background-color 0.10s; }
.ai-rank-table tbody tr:hover { background: var(--c-bone); }
.ai-rank-table .right { text-align: right; }
.ai-row--hidden { display: none; }
.ai-rank { font-family: var(--font-mono); font-size: 11px; color: var(--c-muted); }
.ai-borough-name { font-weight: 600; font-size: 13px; color: var(--c-ink); text-decoration: none; }
a.ai-borough-name:hover { text-decoration: underline; }
.ai-no-data { font-size: 10px; color: var(--c-muted); margin-top: 2px; }
.ai-score { display: inline-block; font-family: var(--font-mono); font-size: 13px; font-weight: 600; padding: 2px 8px; background: var(--c-bone); color: var(--c-ink); min-width: 36px; text-align: center; }
.ai-score--high { background: var(--c-ink); color: var(--c-white); }
.ai-score--mid { background: var(--c-charcoal); color: var(--c-white); }
.ai-score--low { background: var(--c-bone); color: var(--c-muted); }
.ai-grade { display: inline-block; font-family: var(--font-mono); font-size: 12px; font-weight: 700; padding: 2px 7px; min-width: 28px; text-align: center; }
.ai-grade.grade--a, .ai-grade.grade--A { background: var(--c-ink); color: var(--c-white); }
.ai-grade.grade--b, .ai-grade.grade--B { background: var(--c-charcoal); color: var(--c-white); }
.ai-grade.grade--c, .ai-grade.grade--C { background: var(--c-stone); color: var(--c-ink); border: 1px solid var(--c-rule); }
.ai-grade.grade--d, .ai-grade.grade--D, .ai-grade.grade--f, .ai-grade.grade--F { background: var(--c-oxblood); color: var(--c-white); }
.ai-grade.grade--none { background: var(--c-bone); color: var(--c-muted); border: 1px solid var(--c-rule); }
.ai-bar-wrap { display: flex; align-items: center; gap: 6px; min-width: 130px; }
.ai-bar-bg { flex: 1; height: 5px; background: var(--c-rule); min-width: 60px; }
.ai-bar-fill { height: 100%; background: var(--c-ink); }
.ai-bar-fill--low { background: var(--c-oxblood); }
.ai-bar-fill--mid { background: var(--c-muted); }
.ai-bar-pct { font-family: var(--font-mono); font-size: 11px; color: var(--c-ink); white-space: nowrap; }
.ai-bar-na { color: var(--c-muted); font-size: 12px; }
.ai-trend { display: inline-block; font-family: var(--font-mono); font-size: 11px; font-weight: 600; white-space: nowrap; }
.ai-trend--up { color: var(--c-ink); }
.ai-trend--down { color: var(--c-oxblood); }
.ai-trend--flat { color: var(--c-muted); }
.ai-speed { display: inline-block; font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; padding: 2px 7px; border: 1px solid var(--c-rule); color: var(--c-muted); }
.ai-speed--fast { border-color: var(--c-ink); color: var(--c-ink); }
.ai-speed--slow { border-color: var(--c-oxblood); color: var(--c-oxblood); }
.ai-weeks { font-family: var(--font-mono); font-size: 12px; color: var(--c-ink); }
.ai-signals { display: flex; flex-wrap: wrap; gap: 4px; max-width: 220px; }
.ai-sig { font-size: 9px; padding: 2px 6px; border: 1px solid var(--c-rule); color: var(--c-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 140px; }
.ai-sig--opp { border-color: var(--c-ink); color: var(--c-ink); background: var(--c-bone); }
.ai-sig--risk { border-color: var(--c-oxblood); color: var(--c-oxblood); }
.ai-view-btn { font-size: 11px; font-weight: 600; color: var(--c-ink); text-decoration: none; white-space: nowrap; }
.ai-view-btn:hover { text-decoration: underline; }
.ai-view-btn--locked { color: var(--c-muted); }
.ai-view-btn--locked:hover { color: var(--c-ink); }
.ai-empty { padding: 48px 24px; text-align: center; border: 1px solid var(--c-rule); background: var(--c-white); }
.ai-empty__title { font-size: 15px; font-weight: 600; margin-bottom: 6px; }
.ai-empty__sub { font-size: 13px; color: var(--c-muted); }

/* Region tabs — primary segmentation control above the table. Style
   matches `.ai-btn` but with a count chip + bolder weight. */
.ai-region-tabs { display: flex; flex-wrap: wrap; gap: 0; border: 1px solid var(--c-rule); margin: 0 0 14px; background: var(--c-white); }
.ai-region-tab { display: inline-flex; align-items: center; gap: 8px; padding: 10px 18px; font-size: 12px; font-weight: 600; letter-spacing: 0.02em; color: var(--c-muted); text-decoration: none; border-right: 1px solid var(--c-rule); white-space: nowrap; transition: background 100ms ease; }
.ai-region-tab:last-child { border-right: none; }
.ai-region-tab:hover { background: var(--c-bone); color: var(--c-ink); }
.ai-region-tab--active { background: var(--c-ink); color: var(--c-white); }
.ai-region-tab--active:hover { background: var(--c-ink); color: var(--c-white); }
.ai-region-tab__count { font-family: var(--font-mono); font-size: 10px; font-weight: 600; opacity: 0.75; }
.ai-region-tab--active .ai-region-tab__count { opacity: 0.85; }
@media (max-width: 600px) {
  .ai-region-tab { flex: 1 1 50%; justify-content: center; padding: 9px 12px; }
  .ai-region-tab:nth-child(2n) { border-right: none; }
  .ai-region-tab { border-bottom: 1px solid var(--c-rule); }
}

/* Region pill in the table — small, neutral, monochrome */
.ai-region-pill { display: inline-block; font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.06em; padding: 2px 7px; border: 1px solid var(--c-rule); color: var(--c-muted); background: var(--c-bone); white-space: nowrap; }

/* Median sold price cell */
.ai-sold { font-family: var(--font-mono); font-size: 12px; font-weight: 600; color: var(--c-ink); white-space: nowrap; }

/* ── Interactive toolbar (search + view toggle) ── */
.ai-toolbar { display: flex; align-items: center; gap: 12px; padding: 0 0 14px; flex-wrap: wrap; }
.ai-toolbar__search { display: flex; align-items: center; gap: 8px; flex: 1; min-width: 200px; max-width: 320px; padding: 7px 11px; border: 1px solid var(--c-rule); background: var(--c-white); color: var(--c-muted); }
.ai-toolbar__search:focus-within { border-color: var(--c-ink); color: var(--c-ink); }
.ai-toolbar__search input { flex: 1; border: none; outline: none; background: transparent; font-size: 13px; color: var(--c-ink); }
.ai-toolbar__count { font-size: 12px; color: var(--c-muted); white-space: nowrap; }
.ai-toolbar__toggle { display: flex; margin-left: auto; border: 1px solid var(--c-rule); }
.ai-toggle-btn { padding: 7px 16px; font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.06em; color: var(--c-muted); background: var(--c-white); border: none; border-right: 1px solid var(--c-rule); cursor: pointer; }
.ai-toggle-btn:last-child { border-right: none; }
.ai-toggle-btn.is-active { background: var(--c-ink); color: var(--c-white); }

/* ── Sortable header caret ── */
.ai-rank-table th.ai-js-sort { cursor: pointer; user-select: none; white-space: nowrap; }
.ai-rank-table th.ai-js-sort:hover { color: var(--c-ink); }
.ai-caret { display: inline-block; width: 0; height: 0; margin-left: 3px; vertical-align: middle; border-left: 4px solid transparent; border-right: 4px solid transparent; border-bottom: 5px solid var(--c-pale); opacity: 0; transition: opacity 0.12s; }
.ai-rank-table th.ai-js-sort:hover .ai-caret { opacity: 0.5; }
.ai-rank-table th[aria-sort] .ai-caret { opacity: 1; border-bottom-color: var(--c-ink); }
.ai-rank-table th[aria-sort="descending"] .ai-caret { border-bottom: none; border-top: 5px solid var(--c-ink); }

/* ── Opportunity quadrant (scatter) ── */
.ai-quad { display: grid; grid-template-columns: 22px 1fr; grid-template-rows: 1fr 22px; gap: 6px; }
.ai-quad__yaxis { grid-column: 1; grid-row: 1; writing-mode: vertical-rl; transform: rotate(180deg); text-align: center; font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.1em; color: var(--c-muted); align-self: center; }
.ai-quad__xaxis { grid-column: 2; grid-row: 2; text-align: center; font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.1em; color: var(--c-muted); }
.ai-quad__plot { grid-column: 2; grid-row: 1; position: relative; height: 460px; border: 1px solid var(--c-rule); background: var(--c-white); overflow: hidden; }
.ai-quad__gridline { position: absolute; background: var(--c-rule); }
.ai-quad__gridline--v { left: 50%; top: 0; bottom: 0; width: 1px; }
.ai-quad__gridline--h { top: 50%; left: 0; right: 0; height: 1px; }
.ai-quad__corner { position: absolute; font-size: 10px; color: var(--c-pale); text-transform: uppercase; letter-spacing: 0.05em; pointer-events: none; }
.ai-quad__corner--tl { top: 8px; left: 8px; }
.ai-quad__corner--tr { top: 8px; right: 8px; color: var(--c-ink); font-weight: 600; }
.ai-quad__corner--bl { bottom: 8px; left: 8px; }
.ai-quad__corner--br { bottom: 8px; right: 8px; }
.ai-quad__dot { position: absolute; transform: translate(-50%, -50%); border-radius: 50%; border: 1px solid rgba(255,255,255,0.7); padding: 0; cursor: pointer; opacity: 0.82; transition: opacity 0.1s, box-shadow 0.1s; }
.ai-quad__dot:hover, .ai-quad__dot:focus { opacity: 1; box-shadow: 0 0 0 3px rgba(26,26,26,0.12); outline: none; z-index: 2; }
.ai-quad__dot.grade--a, .ai-quad__dot.grade--A { background: var(--c-ink); }
.ai-quad__dot.grade--b, .ai-quad__dot.grade--B { background: var(--c-charcoal); }
.ai-quad__dot.grade--c, .ai-quad__dot.grade--C { background: var(--c-stone); }
.ai-quad__dot.grade--d, .ai-quad__dot.grade--D, .ai-quad__dot.grade--f, .ai-quad__dot.grade--F { background: var(--c-oxblood); }
.ai-quad__dot.grade--none { background: var(--c-pale); }
.ai-quad__legend { display: flex; gap: 16px; flex-wrap: wrap; margin-top: 10px; font-size: 11px; color: var(--c-muted); }
.ai-quad__tooltip { position: absolute; transform: translate(-50%, calc(-100% - 10px)); background: var(--c-ink); color: var(--c-white); padding: 8px 10px; font-size: 11px; line-height: 1.5; white-space: nowrap; pointer-events: none; z-index: 5; }
.ai-quad__tip-name { font-weight: 700; margin-bottom: 2px; }
.ai-quad__tip-row { opacity: 0.85; }
.ai-quad__tip-row b { color: var(--c-white); font-weight: 600; }

/* Watches strip — personalisation surface for logged-in users with active
   PlanningWatch rows. Sits between hero and Pro banner. Up to 6 cards in a
   responsive grid; falls back to a "view all" link when over 6. The strip
   is the single biggest answer to "every user starts blank". */
.ai-watches { padding: 22px 24px; margin: 0 0 24px; background: var(--c-white); border: 1px solid var(--c-rule); }
.ai-watches__head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; flex-wrap: wrap; padding-bottom: 14px; border-bottom: 1px solid var(--c-rule); margin-bottom: 14px; }
.ai-watches__eyebrow { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.12em; color: var(--c-ink); }
.ai-watches__hint { font-size: 11px; color: var(--c-muted); }
.ai-watches__grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 12px; }
.ai-watches__more { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--c-rule); text-align: right; }
.ai-watches__more a { font-size: 11px; font-weight: 600; color: var(--c-ink); text-decoration: none; }
.ai-watches__more a:hover { text-decoration: underline; }

.ai-watch { display: flex; flex-direction: column; gap: 8px; padding: 12px 14px; border: 1px solid var(--c-rule); background: var(--c-bone); text-decoration: none; color: inherit; transition: background 100ms ease, border-color 100ms ease; }
a.ai-watch:hover { background: var(--c-white); border-color: var(--c-ink); }
.ai-watch--locked { position: relative; }
.ai-watch__head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.ai-watch__name { font-size: 13px; font-weight: 600; color: var(--c-ink); line-height: 1.25; }
.ai-watch__metrics { display: flex; gap: 16px; flex-wrap: wrap; padding: 4px 0; }
.ai-watch__metric { display: inline-flex; flex-direction: column; gap: 2px; }
.ai-watch__metric--up .ai-watch__num { color: var(--c-ink); }
.ai-watch__metric--down .ai-watch__num { color: var(--c-oxblood); }
.ai-watch__num { font-family: var(--font-mono); font-size: 14px; font-weight: 600; color: var(--c-ink); letter-spacing: -0.02em; }
.ai-watch__lbl { font-size: 9px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--c-muted); }
.ai-watch__foot { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; padding-top: 6px; border-top: 1px solid var(--c-rule); }
.ai-watch__count { font-size: 10px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--c-muted); font-family: var(--font-mono); }
.ai-watch__pending { font-size: 10px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--c-pale); }

/* Starter-view chips — pre-built deep-link "stories" above the table.
   Each chip carries an intent (label) + a sub-line and a live count. The
   user can click a chip instead of assembling region+grade+sort filters
   themselves. Active chip mirrors the URL state. */
.ai-starter-strip { display: flex; align-items: flex-start; gap: 16px; padding: 0 0 18px; margin: 0 0 6px; }
.ai-starter-strip__label { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.12em; color: var(--c-muted); padding-top: 14px; flex-shrink: 0; min-width: 84px; }
.ai-starter-strip__chips { display: flex; flex-wrap: wrap; gap: 8px; flex: 1; }

.ai-chip { display: inline-flex; align-items: center; gap: 14px; padding: 10px 14px; border: 1px solid var(--c-rule); background: var(--c-white); text-decoration: none; transition: border-color 100ms ease, background 100ms ease; min-width: 0; }
.ai-chip:hover { border-color: var(--c-ink); }
.ai-chip--active { background: var(--c-ink); border-color: var(--c-ink); }
.ai-chip--active .ai-chip__title { color: var(--c-white); }
.ai-chip--active .ai-chip__sub { color: rgba(255,255,255,0.65); }
.ai-chip--active .ai-chip__count { color: var(--c-white); }
.ai-chip--empty { opacity: 0.55; }
.ai-chip--reset { background: transparent; }
.ai-chip__main { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.ai-chip__title { font-size: 12px; font-weight: 600; color: var(--c-ink); white-space: nowrap; }
.ai-chip__sub { font-size: 10px; color: var(--c-muted); white-space: nowrap; }
.ai-chip__count { font-family: var(--font-mono); font-size: 12px; font-weight: 700; color: var(--c-ink); letter-spacing: -0.02em; flex-shrink: 0; padding-left: 4px; border-left: 1px solid var(--c-rule); padding-left: 12px; }
.ai-chip--active .ai-chip__count { border-left-color: rgba(255,255,255,0.18); }

@media (max-width: 700px) {
  .ai-starter-strip { flex-direction: column; gap: 8px; }
  .ai-starter-strip__label { padding-top: 0; }
}

/* "Showing N of M" indicator inside ai-controls when filters narrow the table */
.ai-controls__count { font-size: 11px; color: var(--c-muted); font-family: var(--font-mono); margin-left: auto; }


/* Tiny "region" tag inside featured cards */
.ai-feature__region { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: var(--c-ink); }

/* Footer close-the-loop CTA — slim, centred, less prominent than the
   top banner. Pure conversion fallback for users who scrolled past. */
.ai-footer-cta { margin-top: 32px; padding: 28px 24px; border: 1px solid var(--c-rule); background: var(--c-white); text-align: center; }
.ai-footer-cta__title { font-size: 18px; font-weight: 300; letter-spacing: -0.02em; color: var(--c-ink); margin: 0 0 6px; }
.ai-footer-cta__sub { font-size: 13px; color: var(--c-muted); line-height: 1.5; margin: 0 0 16px; max-width: 480px; margin-left: auto; margin-right: auto; }
.ai-footer-cta__btn { display: inline-flex; align-items: center; padding: 11px 22px; font-size: 12px; font-weight: 600; letter-spacing: 0.04em; background: var(--c-ink); color: var(--c-white); border: 1px solid var(--c-ink); text-decoration: none; }
.ai-footer-cta__btn:hover { background: var(--c-charcoal); }

/* Upgrade variant of the bottom CTA strip — emphasises the conversion ask
   without leaving the .bi-cta-strip dark-bordered container. (Retained for
   pages that still use the bi-cta--upgrade modifier.) */
.bi-cta--upgrade { background: var(--c-ink); color: var(--c-white); }
.bi-cta--upgrade .bi-cta__title,
.bi-cta--upgrade .bi-cta__sub,
.bi-cta--upgrade .bi-cta__icon,
.bi-cta--upgrade .bi-cta__arrow { color: var(--c-white); }
.bi-cta--upgrade .bi-cta__sub { color: rgba(255,255,255,0.75); }
.bi-cta--upgrade:hover { background: var(--c-charcoal); }

/* ─────────────────────────────────────────────────────────────────────────────
   8.12 Borough Compare
   ───────────────────────────────────────────────────────────────────────────── */
.cmp-grid { display: grid; gap: 16px; align-items: start; }
/* min-width: 0 on tracks so long content inside cards can't blow out
   the grid and force horizontal viewport overflow. */
.cmp-grid > * { min-width: 0; }
.cmp-grid--1 { grid-template-columns: 1fr; }
.cmp-grid--2 { grid-template-columns: repeat(2, 1fr); }
.cmp-grid--3 { grid-template-columns: repeat(3, 1fr); }
@media (max-width: 1024px) {
  .cmp-grid--3 { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 720px) {
  .cmp-grid--2, .cmp-grid--3 { grid-template-columns: 1fr; }
}

/* Compare list — used for sections (e.g. Article 4 directions, Best use
   types) where items are long strings that don't fit the chip pattern.
   FT data-ledger style: hairline-separated rows that wrap naturally. */
.cmp-list { display: flex; flex-direction: column; }
.cmp-list-item { font-size: 12px; color: var(--c-ink); line-height: 1.5; padding: 8px 0; border-bottom: 1px solid var(--ql-rule, rgba(26,26,26,0.12)); word-break: break-word; }
.cmp-list-item:last-child { border-bottom: none; padding-bottom: 0; }
.cmp-list-item:first-child { padding-top: 0; }
.cmp-list-item--muted { color: var(--c-muted); font-size: 11px; }
.cmp-list-item__meta { display: block; font-size: 10px; color: var(--c-muted); margin-top: 2px; font-family: var(--font-mono); letter-spacing: 0.02em; }

/* Allow chip text to wrap when content is long. Pair with .ql-tag. */
.ql-tag--wrap { white-space: normal; word-break: break-word; max-width: 100%; line-height: 1.35; padding: 4px 8px; }
.cmp-picker__title { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.1em; color: var(--c-muted); margin-bottom: 4px; }
.cmp-picker__row { display: flex; gap: 12px; align-items: flex-end; flex-wrap: wrap; }
.cmp-picker__field { display: flex; flex-direction: column; gap: 4px; flex: 1; min-width: 160px; }
.cmp-picker__label { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: var(--c-muted); }
.cmp-picker__select { width: 100%; padding: 8px 10px; border: 1px solid var(--c-rule); background: var(--c-white); color: var(--c-ink); font-size: 13px; border-radius: 0; outline: none; }
.cmp-picker__select:focus { border-color: var(--c-ink); background: var(--c-bone); }
.cmp-btn { display: inline-flex; align-items: center; padding: 8px 20px; font-size: 12px; font-weight: 600; background: var(--c-ink); color: var(--c-white); border: 1px solid var(--c-ink); cursor: pointer; text-decoration: none; border-radius: 0; white-space: nowrap; }
.cmp-btn:hover { background: var(--c-ink); }
.cmp-btn--ghost { background: transparent; color: var(--c-muted); border-color: var(--c-rule); }
.cmp-btn--ghost:hover { border-color: var(--c-ink); color: var(--c-ink); background: transparent; }
.ql-card__name { display: block; font-size: 15px; font-weight: 700; color: var(--c-ink); text-decoration: none; line-height: 1.2; }
.ql-card__name:hover { text-decoration: underline; }
.ql-card__sub { display: block; font-size: 11px; color: var(--c-muted); font-weight: 400; margin-top: 3px; }
.ql-card__nodata { padding: 20px 16px; font-size: 13px; color: var(--c-muted); }
.ql-card--link { text-decoration: none; transition: border-color 0.12s; }
.ql-card--link:hover { border-color: var(--c-ink); }
.ql-card--link .ql-card__name { font-size: 15px; font-weight: 700; color: var(--c-ink); margin-bottom: 6px; display: block; }
.ql-card--link .ql-card__desc { font-size: 13px; color: var(--c-muted); line-height: 1.5; margin-bottom: 12px; display: block; }
.ql-card--link .ql-card__cta { font-size: 12px; font-weight: 600; color: var(--c-ink); }
.cmp-grade { font-family: var(--font-mono); font-size: 22px; font-weight: 700; padding: 4px 10px; flex-shrink: 0; }
.cmp-section { padding: 12px 16px; border-bottom: 1px solid var(--c-rule); }
.cmp-section:last-child { border-bottom: none; }
.cmp-section__title { font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.12em; color: var(--c-muted); margin-bottom: 6px; }
.cmp-headline { font-size: 13px; color: var(--c-ink); line-height: 1.5; margin: 0; }
.cmp-metrics { display: flex; flex-direction: column; gap: 8px; }
.cmp-metric { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.cmp-metric__lbl { font-size: 11px; color: var(--c-muted); }
.cmp-metric__val { font-family: var(--font-mono); font-size: 12px; font-weight: 600; color: var(--c-ink); }
.cmp-metric__na { color: var(--c-muted); }
.cmp-bar-wrap { display: flex; align-items: center; gap: 6px; }
.cmp-bar-bg { flex: 1; height: 4px; background: var(--c-rule); min-width: 60px; }
.cmp-bar-fill { height: 100%; background: var(--c-ink); }
.cmp-bar--high { background: var(--c-ink); }
.cmp-bar--mid { background: var(--c-muted); }
.cmp-bar--low { background: var(--c-oxblood); }
.cmp-bar-pct { font-family: var(--font-mono); font-size: 11px; color: var(--c-ink); white-space: nowrap; }
.cmp-speed { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; padding: 2px 6px; border: 1px solid var(--c-rule); color: var(--c-muted); }
.cmp-speed--fast { border-color: var(--c-ink); color: var(--c-ink); }
.cmp-speed--slow { border-color: var(--c-oxblood); color: var(--c-oxblood); }
.cmp-trend--up { color: var(--c-ink); font-weight: 600; font-size: 12px; }
.cmp-trend--down { color: var(--c-oxblood); font-weight: 600; font-size: 12px; }
.cmp-trend--flat { color: var(--c-muted); font-size: 12px; }
.cmp-stance { display: inline-block; font-size: 11px; font-weight: 600; padding: 3px 10px; border: 1px solid var(--c-rule); color: var(--c-muted); }
.cmp-stance--supportive { border-color: var(--c-ink); color: var(--c-ink); background: var(--c-bone); }
.cmp-stance--restrictive { border-color: var(--c-oxblood); color: var(--c-oxblood); }
.cmp-hmo { font-size: 12px; color: var(--c-ink); line-height: 1.5; margin: 0; }
@media (max-width: 860px) {
  .cmp-grid--2, .cmp-grid--3 { grid-template-columns: 1fr; }
}

/* ─────────────────────────────────────────────────────────────────────────────
   8.13 Borough Intelligence (bi-*)
   ───────────────────────────────────────────────────────────────────────────── */
.ql-hero-metric { font-size: 72px; font-weight: 200; letter-spacing: -0.05em; color: var(--c-ink); line-height: 1; margin: 0; font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.ql-hero-metric--label { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.12em; color: var(--c-muted); margin: 6px 0 0; }
.ql-dossier-row { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; padding: 10px 0; border-bottom: 1px solid var(--c-rule); }
.ql-dossier-row:last-child { border-bottom: none; }
.ql-dossier-row__label { font-size: 11px; font-weight: 600; color: var(--c-muted); text-transform: uppercase; letter-spacing: 0.05em; flex-shrink: 0; }
.ql-dossier-row__value { font-size: 13px; font-weight: 500; color: var(--c-ink); text-align: right; }

/* Forensic card — ASTOR Intelligence Unit dark panel */
.ql-forensic-card { background: var(--c-ink); color: var(--c-white); padding: 24px; }
.ql-forensic-card__label { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.12em; color: var(--c-pale); margin-bottom: 4px; }
.ql-forensic-card__title { font-size: 18px; font-weight: 700; color: var(--c-white); margin-bottom: 16px; line-height: 1.3; }
.forensic-row { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; padding: 8px 0; border-bottom: 1px solid rgba(255,255,255,0.08); }
.forensic-row__label { font-size: 11px; color: var(--c-pale); font-weight: 500; flex-shrink: 0; }
.forensic-row__value { font-size: 13px; font-weight: 600; color: var(--c-white); text-align: right; }
.forensic-rule { border: none; border-top: 1px solid rgba(255,255,255,0.1); margin: 12px 0; }
.forensic-text { font-size: 13px; color: var(--c-pale); line-height: 1.6; margin: 8px 0; }
.forensic-meta { font-size: 10px; color: var(--c-pale); text-transform: uppercase; letter-spacing: 0.08em; margin-top: 16px; font-family: var(--font-mono); opacity: 0.6; }

/* Analyst signature — ASTOR §5. The "running foot" of a dossier page.
   Monospace, pale stone, uppercase, hairline rule above to separate it
   from the report body. Centred to read like a print byline. */
.ql-signature {
  margin: 64px 0 16px;
  padding-top: 20px;
  border-top: 1px solid var(--c-rule);
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--c-pale);
  text-align: center;
  line-height: 1.7;
}

.wd-back-btn { display: inline-flex; align-items: center; gap: 6px; padding: 6px 14px; font-size: 12px; font-weight: 500; border: 1px solid var(--c-rule); background: transparent; color: var(--c-muted); cursor: pointer; text-decoration: none; border-radius: 0; white-space: nowrap; }
.wd-back-btn:hover { border-color: var(--c-ink); color: var(--c-ink); }

.bi-section { padding: 24px 0; border-bottom: 1px solid var(--c-rule); }
.bi-section:last-child { border-bottom: none; }
.bi-section-title { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.12em; color: var(--c-muted); margin-bottom: 14px; }

/* MapLibre Article 4 popup — built dynamically by JS (borough_intelligence.html, map.html) */
.bi-popup-title { font-size: 12px; font-weight: 600; line-height: 1.3; }
.bi-popup-meta  { font-size: 10px; color: var(--c-muted); margin-top: 2px; }
.bi-popup-doc   { margin-top: 6px; font-size: 11px; }
.bi-popup-doc-link { color: var(--c-ink); }
.bi-popup-hmo   { background: var(--c-oxblood); color: var(--c-white); padding: 1px 5px; font-size: 10px; }
.bi-confidence { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; padding: 2px 7px; border: 1px solid var(--c-rule); color: var(--c-muted); }
.bi-confidence--high { border-color: var(--c-ink); color: var(--c-ink); }
.bi-confidence--low { border-color: var(--c-oxblood); color: var(--c-oxblood); }
.bi-no-data { padding: 20px 0; font-size: 13px; color: var(--c-muted); }
.bi-no-data-title { font-size: 16px; font-weight: 600; color: var(--c-ink); margin-bottom: 8px; }

/* Council profile */
.bi-profile-grid { display: grid; grid-template-columns: 1fr; gap: 0; border: 1px solid var(--c-rule); }
.bi-profile-row { display: grid; grid-template-columns: 180px 1fr; gap: 16px; align-items: start; padding: 10px 14px; border-bottom: 1px solid var(--c-rule); }
.bi-profile-row:last-child { border-bottom: none; }
.bi-profile-key { font-size: 11px; font-weight: 600; color: var(--c-muted); }
.bi-profile-val { font-size: 13px; color: var(--c-ink); font-weight: 500; line-height: 1.5; }
.bi-speed-fast { color: var(--c-ink); font-weight: 700; }
.bi-speed-slow { color: var(--c-oxblood); font-weight: 700; }
.bi-speed-medium { color: var(--c-muted); font-weight: 700; }

/* Investment chips */
.bi-chip-list { display: flex; flex-wrap: wrap; gap: 6px; }
.bi-chip { font-size: 11px; font-weight: 600; padding: 3px 10px; border: 1px solid var(--c-rule); color: var(--c-muted); }
.bi-chip--green { border-color: var(--c-ink); color: var(--c-ink); background: var(--c-bone); }
.bi-chip--red { border-color: var(--c-oxblood); color: var(--c-oxblood); }
.bi-opp-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
@media (max-width: 640px) { .bi-opp-grid { grid-template-columns: 1fr; } }

/* Approval/refusal two-col */
.bi-two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; }
@media (max-width: 700px) { .bi-two-col { grid-template-columns: 1fr; } }

/* Timing */
.bi-timing-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 12px; }
.bi-timing-card { border: 1px solid var(--c-rule); padding: 12px 14px; background: var(--c-white); }
.bi-timing-label { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: var(--c-muted); margin-bottom: 4px; }
.bi-timing-val { font-size: 13px; color: var(--c-ink); line-height: 1.4; }

/* Trend / analyst */
.bi-trend { font-size: 13px; color: var(--c-ink); line-height: 1.6; }
.bi-analyst-notes { font-size: 13px; color: var(--c-muted); line-height: 1.6; font-style: italic; }

/* Application type table */
.bi-type-table { width: 100%; border-collapse: collapse; font-size: 12px; }
.bi-type-table th { padding: 8px 10px; background: var(--c-bone); border-bottom: 1px solid var(--c-rule); font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: var(--c-muted); white-space: nowrap; text-align: left; }
.bi-type-table th.right, .bi-type-table td.right { text-align: right; }
.bi-type-table tbody tr { border-bottom: 1px solid var(--c-rule); }
.bi-type-table td { padding: 8px 10px; vertical-align: middle; }
.bi-type-risks-inline { font-size: 10px; color: var(--c-muted); margin-top: 2px; }
.bi-type-risk { font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; padding: 2px 6px; border: 1px solid var(--c-rule); color: var(--c-muted); }
.bi-type-risk--high { border-color: var(--c-oxblood); color: var(--c-oxblood); }
.bi-type-risk--medium { border-color: var(--c-muted); color: var(--c-muted); }
.bi-type-risk--low { border-color: var(--c-ink); color: var(--c-ink); }
.bi-type-notes { font-size: 11px; color: var(--c-muted); line-height: 1.4; max-width: 220px; }

/* ── Borough dossier — Wave 5 (MOBILE_CSS_ROLLOUT §4). ──────────────────────
   Everything below was an INLINE style in borough_intelligence.html. Inline
   styles cannot be reached by a media query (§1.3), which is why a three-column
   prose grid and a non-wrapping header row survived every previous wave. The
   rules live here so ≤768px can actually change them. */

/* The dark "Astor analyst's read" card: three prose columns that must not
   stay three columns at 375px (~90px each). */
.bi-dossier__cols {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 22px;
  font-size: 13.5px;
  line-height: 1.65;
  color: rgba(255, 255, 255, 0.78);
}
.bi-dossier__label {
  display: block;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.35);
  margin: 0 0 5px;
}
.bi-dossier__text { margin: 0; color: rgba(255, 255, 255, 0.78); }

/* HMO viability / commercial-conversion outlook pair. */
.bi-outlook-pair {
  margin-top: 1rem;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.75rem;
}

/* Article 4 section header — heading plus the "alert me" toggle. */
.bi-a4d-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 8px;
}
.bi-a4d-head__title { margin: 0; }
.bi-a4d-head__src {
  font-size: 0.7rem;
  font-weight: 400;
  color: var(--status-ok-text-dkr);
  background: var(--status-ok-bg-mid);
  padding: 1px 5px;
  vertical-align: middle;
  margin-left: 0.3rem;
}
.bi-a4d-head__form { margin: 0; }
.bi-a4d-alert {
  background: var(--c-white);
  color: var(--c-ink);
  border: 1px solid var(--c-ink);
  padding: 6px 12px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  cursor: pointer;
  white-space: nowrap;
}
.bi-a4d-alert.is-on { background: var(--c-ink); color: var(--c-white); }

/* Recent-applications / sold-prices cells. The address truncates to one
   ellipsised line on desktop; the phone card lets it wrap instead. */
.bi-app-addr-cell { max-width: 340px; }
.bi-app-addr {
  font-size: 13px;
  color: var(--c-ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.bi-app-addr--sub { font-size: 11px; color: var(--c-muted); }
/* On mobile the address IS the link to the council portal, so hiding the
   "Portal ↗" column takes no action with it. */
.bi-app-addr--link { color: var(--c-ink); text-decoration: none; }
.bi-app-addr--link:hover { text-decoration: underline; }
/* The received date rides along in the reference line on the phone only —
   desktop already has its own Received column and must not show it twice. */
.bi-app-ref__when { display: none; color: var(--c-muted); }
.bi-app-date { font-family: var(--font-mono); font-size: 12px; color: var(--c-muted); }
.bi-app-portal { font-size: 12px; color: var(--c-ink); text-decoration: none; }
.bi-sale-price { font-weight: 700; font-family: var(--font-mono); }
.bi-sale-num { font-family: var(--font-mono); font-size: 12px; color: var(--c-muted); }

@media (max-width: 768px) {
  /* One prose column, not three. */
  .bi-dossier__cols { grid-template-columns: 1fr; gap: 18px; }
  .bi-outlook-pair  { grid-template-columns: 1fr; }

  /* Let the heading and the alert button stack rather than force the page
     363px wide inside a 343px column. */
  .bi-a4d-head { flex-wrap: wrap; gap: 10px; }
  .bi-a4d-alert { min-height: 44px; }   /* it is a tap target (MOBILE_UX §3) */

  /* Pattern C fit-ups. The card gives each value a full row, so the desktop
     truncation and width caps are actively harmful here. */
  .bi-type-table.tbl--cards .bi-app-addr-cell { max-width: none; }
  .bi-type-table.tbl--cards .bi-app-addr {
    white-space: normal;
    overflow: visible;
    overflow-wrap: anywhere;
    text-overflow: clip;
  }
  .bi-type-table.tbl--cards .bi-type-notes { max-width: none; }
  /* Prose values (an address, a paragraph of investor notes) get the label
     ABOVE and the text left-aligned full-width. The default label-left /
     value-right row is right for a number; a wrapped sentence ragged-left
     against its label is not. */
  .bi-type-table.tbl--cards .bi-app-addr-cell,
  .bi-type-table.tbl--cards .bi-type-notes-cell {
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-1);
    text-align: left;
  }
  /* .right is a desktop column alignment; in a card the label is on the left
     and the value on the right already, so the class must not fight the flex. */
  .bi-type-table.tbl--cards td.right { text-align: right; }
  .bi-type-table.tbl--cards td.tbl-card-head.right { text-align: left; }
  /* The card header carries the row's subject at readable size. */
  .bi-type-table.tbl--cards .bi-sale-price { font-size: 15px; }

  /* Recent applications is Pattern F, not C — the width cap and the desktop
     ellipsis belong to the desktop column plan, not to a record row. */
  .bi-type-table.tbl--rows .bi-app-addr-cell { max-width: none; }
  .bi-type-table.tbl--rows .bi-app-addr { font-size: 13.5px; }
  .bi-type-table.tbl--rows .bi-app-ref { font-size: 11px; color: var(--c-muted); }
  .bi-app-ref__when { display: inline; }
}

/* "Your context" strip — always-on CRM signal, even at zero */
.bi-context-strip { margin-top: 20px; padding: 12px 16px; border: 1px solid var(--c-rule); background: var(--c-bone); display: flex; align-items: center; gap: 24px; flex-wrap: wrap; }
.bi-context-strip__label { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.10em; color: var(--c-muted); }
.bi-context-strip__items { display: flex; gap: 24px; flex-wrap: wrap; align-items: baseline; }
.bi-context-item { display: inline-flex; align-items: baseline; gap: 6px; text-decoration: none; color: var(--c-ink); }
a.bi-context-item:hover .bi-context-item__num { color: var(--c-oxblood); }
.bi-context-item__num { font-size: 18px; font-weight: 700; font-family: var(--font-mono); color: var(--c-ink); line-height: 1; }
.bi-context-item__label { font-size: 11px; color: var(--c-muted); }
@media (max-width: 600px) { .bi-context-strip { gap: 12px; padding: 10px 12px; } .bi-context-strip__items { gap: 16px; } }

/* CTA strip — buttons that read as buttons */
.bi-cta-strip { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 0; border: 1px solid var(--c-ink); margin-top: 12px; background: var(--c-white); }
.bi-cta { display: flex; align-items: center; gap: 12px; padding: 16px 18px; border-right: 1px solid var(--c-rule); text-decoration: none; color: var(--c-ink); transition: background 100ms ease, color 100ms ease; position: relative; }
.bi-cta:last-child, .bi-cta-form:last-child .bi-cta { border-right: none; }
.bi-cta:hover { background: var(--c-ink); color: var(--c-white); }
.bi-cta:hover .bi-cta__title { color: var(--c-white); }
.bi-cta:hover .bi-cta__sub { color: rgba(255,255,255,0.75); }
.bi-cta:hover .bi-cta__icon, .bi-cta:hover .bi-cta__arrow { color: var(--c-white); }
.bi-cta__icon { flex-shrink: 0; color: var(--c-ink); display: flex; align-items: center; justify-content: center; width: 24px; height: 24px; }
.bi-cta__body { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; }
.bi-cta__title { font-size: 13px; font-weight: 700; color: var(--c-ink); }
.bi-cta__sub { font-size: 11px; color: var(--c-muted); line-height: 1.4; }
.bi-cta__arrow { flex-shrink: 0; font-size: 16px; color: var(--c-muted); transition: transform 100ms ease; }
.bi-cta:hover .bi-cta__arrow { transform: translateX(2px); }

.bi-cta-form { margin: 0; display: contents; }
.bi-cta--toggle { background: transparent; border: none; border-right: 1px solid var(--c-rule); cursor: pointer; text-align: left; font-family: inherit; }
.bi-cta--toggle.is-active { background: var(--c-ink); color: var(--c-white); }
.bi-cta--toggle.is-active .bi-cta__title { color: var(--c-white); }
.bi-cta--toggle.is-active .bi-cta__sub { color: rgba(255,255,255,0.75); }
.bi-cta--toggle.is-active .bi-cta__icon, .bi-cta--toggle.is-active .bi-cta__arrow { color: var(--c-white); }

@media (max-width: 700px) { .bi-cta { border-right: none; border-bottom: 1px solid var(--c-rule); } .bi-cta-strip > *:last-child .bi-cta, .bi-cta-strip > .bi-cta:last-child { border-bottom: none; } }

/* Investment grade legend (under the hero letter) */
.bi-grade-key { font-size: 10px; color: var(--c-muted); margin: 10px 0 0; line-height: 1.5; max-width: 240px; }

/* Top case officers */
.bi-officer-list { display: flex; flex-direction: column; border: 1px solid var(--c-rule); }
.bi-officer-row { display: flex; justify-content: space-between; align-items: baseline; padding: 10px 14px; border-bottom: 1px solid var(--c-rule); }
.bi-officer-row:last-child { border-bottom: none; }
.bi-officer-name { font-size: 13px; font-weight: 500; color: var(--c-ink); }
.bi-officer-count { font-size: 12px; color: var(--c-muted); font-family: var(--font-mono); }
.bi-officer-count strong { color: var(--c-ink); font-weight: 700; }

/* Inline borough map */
.bi-map { width: 100%; height: 320px; border: 1px solid var(--c-rule); background: var(--c-bone); }
.bi-map .maplibregl-ctrl-attrib { font-size: 9px; }
.bi-map-footer { display: flex; justify-content: flex-end; padding-top: 6px; }
.bi-map-link { font-size: 11px; color: var(--c-muted); text-decoration: none; }
.bi-map-link:hover { color: var(--c-ink); }
.bi-map-note { margin-top: 10px; padding: 10px 14px; border: 1px solid var(--c-rule); border-left: 3px solid var(--c-muted); background: var(--c-bone); font-size: 12px; color: var(--c-muted); line-height: 1.55; }
.bi-map-note strong { color: var(--c-ink); font-weight: 600; }
@media (max-width: 640px) { .bi-map { height: 240px; } }

/* Recent activity (staff timeline) */
.bi-activity { display: flex; flex-direction: column; border: 1px solid var(--c-rule); }
.bi-activity-row { padding: 10px 14px; border-bottom: 1px solid var(--c-rule); border-left: 3px solid var(--c-rule); }
.bi-activity-row:last-child { border-bottom: none; }
.bi-activity-row--warning { border-left-color: var(--c-muted); }
.bi-activity-row--error { border-left-color: var(--c-oxblood); }
.bi-activity-meta { display: flex; gap: 10px; align-items: center; font-size: 10px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--c-muted); margin-bottom: 4px; }
.bi-activity-time { font-family: var(--font-mono); }
.bi-activity-type { font-weight: 700; }
.bi-activity-title { font-size: 13px; color: var(--c-ink); font-weight: 500; }
.bi-activity-body { font-size: 11px; color: var(--c-muted); margin-top: 2px; line-height: 1.5; }

/* Article 4 breakdown */
.bi-a4-breakdown { display: flex; flex-direction: column; gap: 8px; margin-bottom: 16px; }
.bi-a4-restrict { padding: 10px 14px; border-left: 3px solid var(--c-rule); background: var(--c-bone); }
.bi-a4-restrict--high { border-left-color: var(--c-oxblood); }
.bi-a4-restrict--medium { border-left-color: var(--c-muted); }
.bi-a4-restrict--low { border-left-color: var(--c-ink); }
.bi-a4-restrict-type { font-size: 11px; font-weight: 700; color: var(--c-ink); margin-bottom: 3px; }
.bi-a4-restrict-detail { font-size: 12px; color: var(--c-muted); line-height: 1.5; margin-bottom: 6px; }
.bi-a4-restrict-meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.bi-a4-scope-pill { font-size: 9px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.06em; padding: 2px 6px; border: 1px solid var(--c-rule); color: var(--c-muted); }
.bi-a4-scope-pill--borough { border-color: var(--c-ink); color: var(--c-ink); }
.bi-a4-scope-pill--area { border-color: var(--c-muted); color: var(--c-muted); }
.bi-a4-who { font-size: 10px; color: var(--c-muted); }
.bi-a4-impact-summary { display: flex; gap: 10px; align-items: flex-start; padding: 12px 14px; border-left: 3px solid var(--c-rule); background: var(--c-bone); margin-bottom: 16px; font-size: 13px; color: var(--c-ink); }
.bi-a4-impact-summary--high { border-left-color: var(--c-oxblood); }
.bi-a4-impact-summary--medium { border-left-color: var(--c-muted); }
.bi-a4-impact-icon { font-size: 14px; flex-shrink: 0; }
.bi-a4-group { margin-bottom: 20px; }
.bi-a4-group-label { font-size: 11px; font-weight: 700; color: var(--c-ink); text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 8px; }
.bi-a4-group-count { font-size: 10px; color: var(--c-muted); font-weight: 400; text-transform: none; letter-spacing: 0; }
.bi-a4-scope-note { font-size: 11px; color: var(--c-muted); margin-bottom: 8px; line-height: 1.5; }
.bi-a4-list { display: flex; flex-direction: column; gap: 0; border: 1px solid var(--c-rule); }
.bi-a4-detail { border-bottom: 1px solid var(--c-rule); }
.bi-a4-detail:last-child { border-bottom: none; }
.bi-a4-detail summary { display: flex; align-items: center; gap: 8px; padding: 10px 14px; cursor: pointer; list-style: none; font-size: 13px; }
.bi-a4-detail summary::-webkit-details-marker { display: none; }
.bi-a4-item-dot { width: 6px; height: 6px; background: var(--c-muted); flex-shrink: 0; }
.bi-a4-detail[open] .bi-a4-item-dot { background: var(--c-ink); }
.bi-a4-item-since { font-size: 10px; color: var(--c-muted); margin-left: auto; white-space: nowrap; }
.bi-a4-hmo-badge { font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; padding: 1px 5px; border: 1px solid var(--c-oxblood); color: var(--c-oxblood); margin-left: 4px; }
.bi-a4-cards { display: flex; flex-direction: column; gap: 0; padding: 0 14px 10px 28px; }
.bi-a4-card { padding: 8px 12px; margin-bottom: 6px; border: 1px solid var(--c-rule); background: var(--c-white); }
.bi-a4-card--high { border-left: 3px solid var(--c-oxblood); }
.bi-a4-card--medium { border-left: 3px solid var(--c-muted); }
.bi-a4-card--low { border-left: 3px solid var(--c-ink); }
.bi-a4-card-label { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; color: var(--c-muted); margin-bottom: 3px; }
.bi-a4-card-text { font-size: 12px; color: var(--c-ink); line-height: 1.5; }
.bi-a4-card-who { font-size: 10px; color: var(--c-muted); margin-top: 4px; }
.bi-a4-doc { padding: 6px 14px 10px 28px; font-size: 11px; }
.bi-a4-doc a { color: var(--c-ink); }
.bi-a4-no-data { font-size: 12px; color: var(--c-muted); padding: 12px 0; }

/* Commercial banner */
.bi-a4-comm-banner { display: flex; gap: 12px; padding: 12px 14px; background: var(--c-bone); border: 1px solid var(--c-rule); margin-bottom: 8px; }
.bi-a4-comm-icon { font-size: 18px; flex-shrink: 0; }
.bi-a4-comm-body { flex: 1; }
.bi-a4-comm-headline { font-size: 12px; font-weight: 700; color: var(--c-ink); margin-bottom: 4px; }
.bi-a4-comm-note { font-size: 11px; color: var(--c-muted); line-height: 1.5; }
.bi-a4-comm-toggle { display: inline-flex; align-items: center; margin-top: 8px; font-size: 11px; font-weight: 600; color: var(--c-muted); background: none; border: none; cursor: pointer; padding: 0; text-decoration: underline; }

/* Chip grid */
.bi-a4-chip-grid { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.bi-a4-chip-grid.is-collapsed { max-height: 80px; overflow: hidden; }
.bi-a4-chip-item { display: inline-flex; align-items: center; gap: 4px; font-size: 11px; padding: 3px 8px; border: 1px solid var(--c-rule); color: var(--c-muted); text-decoration: none; }
.bi-a4-chip-item--comm { color: var(--c-muted); border-color: var(--c-rule); }
.bi-a4-chip-item:hover { border-color: var(--c-ink); color: var(--c-ink); }
.bi-a4-chip-count { font-size: 9px; color: var(--c-muted); }
.bi-a4-chip-ext { font-size: 9px; color: var(--c-muted); }
.bi-a4-search { width: 100%; padding: 6px 10px; border: 1px solid var(--c-rule); font-size: 12px; background: var(--c-white); color: var(--c-ink); border-radius: 0; outline: none; }
.bi-a4-search:focus { border-color: var(--c-ink); }
.bi-a4-show-more-chips { display: block; margin-top: 8px; font-size: 11px; font-weight: 600; color: var(--c-muted); background: none; border: none; cursor: pointer; padding: 0; text-decoration: underline; }

/* ── HMO Requirements section ─────────────────────────────────────────── */
.bi-hmo-intro { font-size: 13px; color: var(--c-muted); line-height: 1.6; max-width: 760px; margin: 0 0 18px; }
.bi-hmo-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0; border: 1px solid var(--c-rule); }
.bi-hmo-pillar { padding: 18px 20px; border-right: 1px solid var(--c-rule); }
.bi-hmo-pillar:last-child { border-right: none; }
.bi-hmo-pillar-eyebrow { font-family: var(--font-mono); font-size: 9px; font-weight: 600; letter-spacing: 0.14em; text-transform: uppercase; color: var(--c-pale); margin-bottom: 14px; }

.bi-hmo-status { font-size: 12px; font-weight: 700; padding: 8px 12px; border-left: 3px solid var(--c-rule); margin-bottom: 10px; line-height: 1.4; }
.bi-hmo-status--req { border-left-color: var(--c-oxblood); color: var(--c-ink); }
.bi-hmo-status--pd  { border-left-color: var(--c-muted); color: var(--c-muted); }

.bi-hmo-text { font-size: 12px; color: var(--c-muted); line-height: 1.55; margin: 0 0 8px; }
.bi-hmo-link { font-size: 11px; font-weight: 600; color: var(--c-ink); text-decoration: underline; }

.bi-hmo-block { padding-bottom: 12px; margin-bottom: 12px; border-bottom: 1px solid var(--c-rule); }
.bi-hmo-block:last-child { border-bottom: none; margin-bottom: 0; padding-bottom: 0; }
.bi-hmo-block-label { font-size: 11px; font-weight: 700; color: var(--c-ink); margin-bottom: 4px; }
.bi-hmo-block-lead { font-size: 12px; font-weight: 600; color: var(--c-ink); margin-bottom: 4px; }
.bi-hmo-tag { font-size: 9px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.06em; padding: 1px 5px; border: 1px solid var(--c-rule); color: var(--c-muted); margin-left: 4px; }

.bi-hmo-scheme { padding: 10px 0 12px; border-top: 1px solid var(--c-rule); }
.bi-hmo-scheme-type { font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: var(--c-muted); margin-bottom: 3px; }
.bi-hmo-scheme-title { font-size: 12px; font-weight: 700; color: var(--c-ink); line-height: 1.4; margin-bottom: 3px; }
.bi-hmo-scheme-applies { font-size: 11px; color: var(--c-ink); margin-bottom: 4px; }
.bi-hmo-scheme-meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 4px 0 6px; }
.bi-hmo-dates { font-family: var(--font-mono); font-size: 10px; color: var(--c-muted); font-variant-numeric: tabular-nums; }

.bi-hmo-hedge { font-size: 11px; color: var(--c-muted); line-height: 1.55; padding: 10px 12px; border-left: 3px solid var(--c-rule); background: var(--c-bone); margin-top: 10px; }
.bi-hmo-hedge a { color: var(--c-ink); }

.bi-hmo-rooms { width: 100%; border-collapse: collapse; }
.bi-hmo-rooms td { padding: 8px 0; border-bottom: 1px solid var(--c-rule); vertical-align: top; }
.bi-hmo-rooms tr:last-child td { border-bottom: none; }
.bi-hmo-rooms-occ { font-size: 11px; color: var(--c-ink); line-height: 1.4; padding-right: 12px; }
.bi-hmo-rooms-area { font-family: var(--font-mono); font-size: 13px; font-weight: 500; color: var(--c-ink); font-variant-numeric: tabular-nums; white-space: nowrap; text-align: right; }

.bi-hmo-foot { font-size: 10px; color: var(--c-pale); line-height: 1.6; margin: 16px 0 0; }
.bi-hmo-foot a { color: var(--c-muted); text-decoration: underline; }

@media (max-width: 1024px) {
  .bi-hmo-grid { grid-template-columns: 1fr; }
  .bi-hmo-pillar { border-right: none; border-bottom: 1px solid var(--c-rule); }
  .bi-hmo-pillar:last-child { border-bottom: none; }
}

/* ── 8.16 Queue page (wd-*) ── */
.wd-body { display: grid; grid-template-columns: 260px 1fr; gap: 20px; align-items: start; margin-top: 20px; }
@media (max-width: 920px) { .wd-body { grid-template-columns: 1fr; } }
.wd-aside { display: flex; flex-direction: column; gap: 12px; position: sticky; top: 24px; align-self: start; }
@media (max-width: 920px) { .wd-aside { position: static; } }
.wd-panel { background: var(--c-white); border: 1px solid var(--c-rule); padding: 16px; }
.wd-panel--flat { background: var(--c-bone); border-color: var(--c-rule); }
.wd-panel-label { font-size: 10px; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase; color: var(--c-muted); margin-bottom: 10px; }
.wd-panel-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }
.wd-panel-addr { font-size: 14px; font-weight: 700; color: var(--c-ink); line-height: 1.4; margin-bottom: 10px; }
.wd-panel-meta { display: flex; flex-direction: column; gap: 6px; }
.wd-panel-empty { font-size: 13px; color: var(--c-muted); font-style: italic; }
.wd-kv { display: flex; align-items: baseline; gap: 8px; }
.wd-kv-k { font-size: 10px; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; color: var(--c-muted); white-space: nowrap; flex-shrink: 0; }
.wd-kv-v { font-size: 12px; color: var(--c-ink); }
.wd-quickviews { display: flex; flex-direction: column; gap: 2px; }
.wd-qv { display: flex; align-items: center; justify-content: space-between; padding: 7px 10px; font-size: 12px; font-weight: 500; color: var(--c-ink); text-decoration: none; border: 1px solid transparent; }
.wd-qv:hover { background: var(--c-white); border-color: var(--c-rule); text-decoration: none; }
.wd-qv--active { background: var(--c-ink); color: var(--c-white); border-color: var(--c-ink); }
.wd-qv-count { display: inline-flex; align-items: center; justify-content: center; min-width: 20px; height: 18px; padding: 0 5px; background: var(--status-warn-dk); color: var(--c-white); font-size: 10px; font-weight: 700; }
.wd-qv--active .wd-qv-count { background: rgba(255,255,255,0.25); }
.wd-main { min-width: 0; }
.wd-filtergroup { padding: 10px 0; border-bottom: 1px solid var(--c-rule); }
.wd-filtergroup:last-child { border-bottom: none; padding-bottom: 0; }
.wd-filterlabel { font-size: 10px; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase; color: var(--c-muted); margin-bottom: 8px; }
.wd-input { width: 100%; padding: 8px 10px; font-size: 13px; border: 1px solid var(--c-rule); background: var(--c-bone); color: var(--c-ink); outline: none; border-radius: 0; }
.wd-input:focus { border-color: var(--c-ink); background: var(--c-white); }
.wd-clear-btn { font-size: 11px; font-weight: 500; color: var(--c-muted); background: none; border: none; cursor: pointer; padding: 0; text-transform: uppercase; letter-spacing: 0.06em; }
.wd-run-btn { display: inline-flex; align-items: center; justify-content: center; padding: 8px 14px; font-size: 12px; font-weight: 600; border: 1px solid var(--c-ink); background: var(--c-ink); color: var(--c-white); cursor: pointer; border-radius: 0; }
.wd-section-sub { font-size: 12px; color: var(--c-muted); }
.wd-risk-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.wd-risk-item { display: flex; align-items: flex-start; gap: 10px; padding-bottom: 10px; border-bottom: 1px solid var(--c-rule); }
.wd-risk-item:last-child { border-bottom: none; padding-bottom: 0; }
.wd-risk-score { font-size: 18px; font-weight: 800; letter-spacing: -0.03em; min-width: 38px; text-align: right; font-variant-numeric: tabular-nums; }
.wd-risk-score--high { color: var(--c-oxblood); }
.wd-risk-score--mid { color: var(--status-warn-dk); }
.wd-risk-score--low { color: var(--status-ok-bright); }
.wd-risk-body { flex: 1; min-width: 0; }
.wd-risk-title { font-size: 12px; font-weight: 600; color: var(--c-ink); line-height: 1.35; display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; margin-bottom: 5px; }
.wd-risk-sub { display: flex; align-items: center; gap: 8px; }
.wd-ext-link { font-size: 11px; color: var(--c-muted); text-decoration: none; }
.wd-ext-link:hover { color: var(--c-ink); }
.wd-results-head { display: flex; align-items: flex-end; justify-content: space-between; margin-bottom: 10px; }

/* ── 8.17 Portfolio Intelligence (pi-*) ──
   Monochrome ink → muted → oxblood per ASTOR §2. Oxblood is the only
   signal colour, reserved for true risk (high score, refusal). Mids are
   neutral muted; lows / "good" states get plain ink (or muted) — never
   green/yellow traffic lights. */
.pi-bcard { background: var(--c-white); border: 1px solid var(--c-rule); display: flex; flex-direction: column; }
.pi-bcard-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; padding: 12px 14px; border-bottom: 1px solid var(--c-rule); }
.pi-bcard-title { font-size: 14px; font-weight: 700; color: var(--c-ink); }
.pi-bcard-sub { font-size: 11px; color: var(--c-muted); margin-top: 2px; }
.pi-sep { color: var(--c-pale); margin: 0 6px; }

.pi-grade { display: inline-block; font-family: var(--font-mono); font-size: 20px; font-weight: 700; color: var(--c-ink); letter-spacing: -0.02em; min-width: 32px; text-align: right; padding: 0 4px; }
.pi-grade--a { color: var(--c-ink); }
.pi-grade--b { color: var(--c-charcoal); }
.pi-grade--c { color: var(--c-muted); }
.pi-grade--d, .pi-grade--f { color: var(--c-oxblood); }
.pi-grade--none { font-size: 14px; color: var(--c-pale); font-weight: 400; }

.pi-bcard-metrics { display: grid; grid-template-columns: repeat(4, 1fr); gap: 0; border-bottom: 1px solid var(--c-rule); }
.pi-metric { padding: 10px 14px; border-right: 1px solid var(--c-rule); text-align: center; }
.pi-metric:last-child { border-right: none; }
.pi-metric-val { font-family: var(--font-mono); font-size: 18px; font-weight: 600; color: var(--c-ink); letter-spacing: -0.02em; }
.pi-metric-val--bad { color: var(--c-oxblood); }
.pi-metric-val--muted { color: var(--c-pale); font-weight: 400; }
.pi-metric-label { font-size: 10px; color: var(--c-muted); text-transform: uppercase; letter-spacing: 0.06em; margin-top: 2px; }

.pi-riskbar { display: flex; height: 5px; margin: 10px 14px 6px; }
.pi-riskbar-seg { height: 100%; }
.pi-riskbar-seg--high { background: var(--c-oxblood); }
.pi-riskbar-seg--mid { background: var(--c-charcoal); }
.pi-riskbar-seg--low { background: var(--c-muted); }
.pi-riskbar-seg--none { background: var(--c-rule); }

.pi-risk-legend { display: flex; gap: 10px; flex-wrap: wrap; padding: 0 14px 10px; font-size: 11px; color: var(--c-muted); }
.pi-risk-chip { display: flex; align-items: center; gap: 4px; }
.pi-risk-chip-dot { width: 8px; height: 8px; flex-shrink: 0; }
.pi-risk-chip-dot--high { background: var(--c-oxblood); }
.pi-risk-chip-dot--mid { background: var(--c-charcoal); }
.pi-risk-chip-dot--low { background: var(--c-muted); }
.pi-risk-chip-dot--none { background: var(--c-rule); }

.pi-bcard-intel { padding: 10px 14px; border-top: 1px solid var(--c-rule); font-size: 12px; color: var(--c-ink); }
.pi-intel-headline { color: var(--c-muted); margin: 0 0 6px; font-size: 12px; line-height: 1.5; }
.pi-intel-link { font-size: 11px; font-weight: 600; color: var(--c-ink); text-decoration: none; }
.pi-intel-link:hover { text-decoration: underline; }

.pi-bcard-toggle { display: flex; align-items: center; justify-content: space-between; padding: 8px 14px; border-top: 1px solid var(--c-rule); font-size: 11px; font-weight: 600; color: var(--c-muted); cursor: pointer; user-select: none; }
.pi-bcard-toggle:hover { color: var(--c-ink); }
.pi-toggle-arrow { font-size: 9px; transition: transform 120ms ease; }
.pi-bcard-toggle.is-open .pi-toggle-arrow { transform: rotate(180deg); }
.pi-prop-list { display: none; }
.pi-prop-list.is-open { display: block; }
.pi-prop-row { display: flex; align-items: center; gap: 10px; padding: 10px 14px; border-top: 1px solid var(--c-rule); }
.pi-score-pill { font-family: var(--font-mono); font-size: 12px; font-weight: 700; width: 32px; text-align: center; padding: 2px 0; flex-shrink: 0; letter-spacing: -0.02em; }
.pi-score-pill--high { color: var(--c-oxblood); }
.pi-score-pill--mid { color: var(--c-charcoal); }
.pi-score-pill--low { color: var(--c-muted); }
.pi-score-pill--none { color: var(--c-pale); }
.pi-needs-dot { width: 6px; height: 6px; background: var(--c-oxblood); flex-shrink: 0; }
.pi-prop-address { flex: 1; min-width: 0; }
.pi-prop-name { font-size: 12px; font-weight: 500; color: var(--c-ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pi-prop-meta { font-size: 11px; color: var(--c-muted); }
.pi-prop-new { color: var(--c-ink); font-weight: 600; }
.pi-prop-actions { display: flex; gap: 6px; flex-shrink: 0; }
.pi-prop-btn { display: inline-flex; align-items: center; padding: 4px 10px; font-size: 11px; font-weight: 600; border: 1px solid var(--c-rule); color: var(--c-muted); text-decoration: none; border-radius: 0; transition: background 100ms ease, color 100ms ease, border-color 100ms ease; }
.pi-prop-btn--view:hover { background: var(--c-ink); color: var(--c-white); border-color: var(--c-ink); }
.pi-prop-btn--review { border-color: var(--c-ink); color: var(--c-ink); }
.pi-prop-btn--review:hover { background: var(--c-ink); color: var(--c-white); }

/* Triage feed — "this week in your portfolio". A horizontal-rule list of
   the most recent material events (new matches, decisions). Each row links
   straight to the watch detail page. The thing the user came here to see. */
.pi-triage { background: var(--c-white); border: 1px solid var(--c-rule); margin: 0 0 24px; }
.pi-triage__head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; padding: 14px 18px; border-bottom: 1px solid var(--c-rule); flex-wrap: wrap; }
.pi-triage__eyebrow { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.12em; color: var(--c-ink); }
.pi-triage__hint { font-size: 11px; color: var(--c-muted); font-family: var(--font-mono); }
.pi-triage__list { list-style: none; margin: 0; padding: 0; }
.pi-triage__row { display: grid; grid-template-columns: 80px 1fr auto; gap: 16px; align-items: center; padding: 12px 18px; border-bottom: 1px solid var(--c-rule); }
.pi-triage__row:last-child { border-bottom: none; }
.pi-triage__type { font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; padding: 3px 8px; border: 1px solid var(--c-rule); color: var(--c-muted); white-space: nowrap; text-align: center; }
.pi-triage__type--decision { border-color: var(--c-oxblood); color: var(--c-oxblood); }
.pi-triage__type--new_match { border-color: var(--c-ink); color: var(--c-ink); background: var(--c-bone); }
.pi-triage__main { display: flex; flex-direction: column; gap: 2px; min-width: 0; text-decoration: none; }
.pi-triage__label { font-size: 13px; font-weight: 600; color: var(--c-ink); line-height: 1.3; }
.pi-triage__main:hover .pi-triage__label { text-decoration: underline; }
.pi-triage__sub { font-size: 11px; color: var(--c-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pi-triage__sep { color: var(--c-pale); margin: 0 4px; }
.pi-triage__when { font-family: var(--font-mono); font-size: 11px; color: var(--c-muted); white-space: nowrap; }
@media (max-width: 600px) {
  .pi-triage__row { grid-template-columns: 70px 1fr; }
  .pi-triage__when { grid-column: 2; padding-top: 4px; }
}

/* Empty state — replaces the old ql-empty block, matches Architectural-
   Digest aesthetic of the rest of the page. */
.pi-empty { padding: 48px 32px; border: 1px solid var(--c-rule); background: var(--c-white); text-align: center; }
.pi-empty__title { font-size: 18px; font-weight: 300; letter-spacing: -0.02em; color: var(--c-ink); margin: 0 0 8px; }
.pi-empty__sub { font-size: 13px; color: var(--c-muted); line-height: 1.6; max-width: 480px; margin: 0 auto 18px; }
.pi-empty__cta { display: inline-flex; align-items: center; padding: 11px 22px; font-size: 12px; font-weight: 600; letter-spacing: 0.04em; background: var(--c-ink); color: var(--c-white); border: 1px solid var(--c-ink); text-decoration: none; }
.pi-empty__cta:hover { background: var(--c-charcoal); }

/* ─────────────────────────────────────────────
   8.18  wd-btn  Watch / Queue button variants
   ───────────────────────────────────────────── */
.wd-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 6px 12px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  border: 1px solid var(--c-rule);
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
  background: var(--c-white);
  color: var(--c-ink);
  border-radius: 0;
}
.wd-btn:hover { text-decoration: none; }
.wd-btn--primary { background: var(--c-ink); color: var(--c-white); border-color: var(--c-ink); }
.wd-btn--primary:hover { background: var(--c-ink); }
.wd-btn--ghost { background: var(--c-white); color: var(--c-muted); }
.wd-btn--ghost:hover { background: var(--c-bone); color: var(--c-ink); border-color: var(--c-muted); }
.wd-btn--sm { padding: 4px 10px; font-size: 10px; }

/* ─────────────────────────────────────────────
   8.19  wd-status-dot  Watch active indicator
   ───────────────────────────────────────────── */
.wd-status-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  flex-shrink: 0;
}
.wd-status-dot--on  { background: var(--status-ok-bright); }
.wd-status-dot--off { background: var(--c-muted); }

/* ─────────────────────────────────────────────
   8.20  aq-*  Action queue bulk bar
   ───────────────────────────────────────────── */
.aq-bulk {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 14px;
  background: var(--c-bone);
  border: 1px solid var(--c-rule);
  margin-bottom: 2px;
  flex-wrap: wrap;
}
.aq-bulk-left {
  display: flex;
  align-items: center;
  gap: 12px;
}
.aq-select-all {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--c-ink);
  cursor: pointer;
  user-select: none;
}
.aq-bulk-actions {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}
@media (max-width: 640px) {
  .aq-bulk { flex-direction: column; align-items: flex-start; }
  .aq-bulk-actions .wd-btn { font-size: 10px; padding: 4px 8px; }
}

/* ─────────────────────────────────────────────
   8.21  wd-tag  Relevance / match tags
   ───────────────────────────────────────────── */
.wd-tag { display: inline-flex; align-items: center; padding: 2px 8px; font-size: 10px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; border: 1px solid var(--c-rule); background: var(--c-bone); color: var(--c-muted); white-space: nowrap; }
.wd-tag--exact   { background: var(--status-ok-bg-mid);  color: var(--status-ok-text-dkr); border-color: transparent; }
.wd-tag--likely  { background: var(--tier-pro-bg-mid);   color: var(--pck-grade-b-text);   border-color: transparent; }
.wd-tag--postcode { background: var(--c-bone); color: var(--c-muted); }

/* ─────────────────────────────────────────────
   8.22  wd-empty  Empty / zero-state
   ───────────────────────────────────────────── */
.wd-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; padding: 64px 24px; }
.wd-empty-icon  { color: var(--c-pale); margin-bottom: 16px; }
.wd-empty-title { font-size: 15px; font-weight: 700; color: var(--c-ink); margin: 0 0 6px; }
.wd-empty-sub   { font-size: 13px; color: var(--c-muted); max-width: 340px; line-height: 1.55; margin: 0 0 20px; }

/* ─────────────────────────────────────────────
   8.23  wd-pagination  Pagination bar (queue)
   ───────────────────────────────────────────── */
.wd-pagination { display: flex; align-items: center; justify-content: center; gap: 8px; padding: 20px 0; flex-wrap: wrap; }

/* ─────────────────────────────────────────────
   8.24  aq-item  Action queue item cards
   ───────────────────────────────────────────── */
.aq-list { display: flex; flex-direction: column; }

.aq-item {
  display: grid;
  grid-template-columns: 22px 1fr auto;
  gap: 0 16px;
  padding: 16px;
  background: var(--c-white);
  border: 1px solid var(--c-rule);
  border-left: 3px solid var(--c-pale);
  margin-top: -1px;
  align-items: start;
  transition: background .1s;
}
.aq-item:first-child { margin-top: 0; }
.aq-item:hover { background: var(--c-bone); }
.aq-item--high { border-left-color: var(--c-oxblood); }
.aq-item--mid  { border-left-color: var(--status-warn-dk); }

.aq-item-check { padding-top: 2px; }
.aq-item-check input[type="checkbox"] { cursor: pointer; }

.aq-item-body  { min-width: 0; }

.aq-item-top  { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; margin-bottom: 4px; }
.aq-item-addr { font-size: 14px; font-weight: 700; color: var(--c-ink); line-height: 1.35; flex: 1; min-width: 0; }
.aq-item-title { font-size: 12px; color: var(--c-muted); line-height: 1.5; margin: 0 0 8px; }

/* Triage chips */
.aq-triage-row  { display: flex; align-items: center; flex-wrap: wrap; gap: 4px; margin-bottom: 8px; }
.aq-triage-chip {
  display: inline-flex; align-items: center;
  padding: 2px 7px; font-size: 10px; font-weight: 700;
  letter-spacing: .04em; text-transform: uppercase;
  border: 1px solid var(--c-rule); color: var(--c-muted);
  background: var(--c-bone); white-space: nowrap;
}
.aq-triage-chip--type     { background: var(--tier-pro-bg-mid);     color: var(--pck-grade-b-text);    border-color: transparent; }
.aq-triage-chip--hmo      { background: var(--status-warn-bg-alt);  color: var(--status-warn-text);    border-color: transparent; }
.aq-triage-chip--a4       { background: var(--pck-grade-d-bg);      color: var(--status-err-text-alt); border-color: transparent; }
.aq-triage-chip--high     { background: var(--pck-grade-d-bg);      color: var(--status-err-text-alt); border-color: transparent; }
.aq-triage-chip--mid      { background: var(--status-warn-bg-alt);  color: var(--status-warn-text);    border-color: transparent; }
.aq-triage-oneline { font-size: 11px; color: var(--c-muted); font-style: italic; }

/* Meta band */
.aq-item-meta { display: flex; align-items: center; flex-wrap: wrap; gap: 4px 6px; margin-top: 10px; }
.aq-meta-item { font-size: 11px; color: var(--c-muted); }
.aq-meta-sep  { font-size: 11px; color: var(--c-rule); }

/* Risk score badge */
.aq-risk-score {
  display: inline-flex; align-items: baseline; gap: 1px;
  font-size: 22px; font-weight: 800; line-height: 1;
  font-family: var(--font-mono, monospace); font-variant-numeric: tabular-nums;
  color: var(--c-pale); flex-shrink: 0;
}
.aq-risk-score.aq-risk--high { color: var(--c-oxblood); }
.aq-risk-score.aq-risk--mid  { color: var(--status-warn-dk); }
.aq-risk-score.aq-risk--low  { color: var(--c-muted); }
.aq-risk-denom { font-size: 11px; font-weight: 500; color: var(--c-pale); margin-left: 1px; }

/* Reasons list */
.aq-reasons    { margin: 10px 0 0; padding-left: 18px; list-style: disc; }
.aq-reasons li { font-size: 12px; color: var(--c-muted); line-height: 1.55; margin-bottom: 2px; }
.aq-notice     { font-size: 12px; color: var(--c-muted); font-style: italic; margin-top: 10px; }

/* Actions column */
.aq-item-actions { display: flex; flex-direction: column; gap: 6px; flex-shrink: 0; min-width: 108px; }

@media (max-width: 640px) {
  .aq-item { grid-template-columns: 22px 1fr; }
  .aq-item-actions { grid-column: 1 / -1; flex-direction: row; flex-wrap: wrap; padding-top: 10px; border-top: 1px solid var(--c-rule); margin-top: 4px; }
}

/* ─────────────────────────────────────────────
   8.25  Pagination  (ql-page-*)
   ───────────────────────────────────────────── */
.ql-pagination { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 14px 0 0; flex-wrap: wrap; }
.ql-page-info { font-size: 12px; color: var(--c-muted); }
.ql-page-links { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.ql-page-btn { display: inline-flex; align-items: center; padding: 4px 10px; font-size: 11px; font-weight: 600; border: 1px solid var(--c-rule); color: var(--c-muted); text-decoration: none; border-radius: 0; cursor: pointer; background: var(--c-white); }
.ql-page-btn:hover { border-color: var(--c-ink); color: var(--c-ink); }
.ql-page-btn--current { background: var(--c-ink); color: var(--c-white); border-color: var(--c-ink); cursor: default; }
.ql-page-ellipsis { padding: 4px 6px; color: var(--c-pale); font-size: 11px; }

/* ─────────────────────────────────────────────
   8.22  Expandable log rows  (ql-row extensions)
   ───────────────────────────────────────────── */
.ql-row--error   { border-left: 3px solid var(--c-oxblood); }
.ql-row--warning { border-left: 3px solid var(--status-warn-dk); }
.ql-row--error .ql-row-header:hover,
.ql-row--warning .ql-row-header:hover { background: var(--c-bone); }
.ql-row-header { display: flex; align-items: center; gap: 10px; padding: 12px 16px; cursor: pointer; user-select: none; }
.ql-row-chevron { width: 16px; height: 16px; color: var(--c-pale); flex-shrink: 0; transition: transform 0.15s; overflow: visible; }
.ql-row.is-open > .ql-row-header .ql-row-chevron { transform: rotate(90deg); }
.ql-row-meta { flex: 1; min-width: 0; }
.ql-row-title { font-size: 13px; font-weight: 600; color: var(--c-ink); margin: 0 0 2px; }
.ql-row-sub   { font-size: 11px; color: var(--c-muted); margin: 0; }
.ql-row-badges { display: flex; gap: 6px; align-items: center; flex-shrink: 0; flex-wrap: wrap; }
.ql-row-body { display: none; border-top: 1px solid var(--c-rule); padding: 14px 16px; }
.ql-row.is-open > .ql-row-body { display: block; }
.ql-row-summary { font-size: 12px; color: var(--c-muted); margin-bottom: 12px; line-height: 1.6; white-space: pre-wrap; }

/* Nested expandable sub-rows (borough breakdown within a run) */
.ql-subrow-list { display: flex; flex-direction: column; border: 1px solid var(--c-rule); background: var(--c-white); margin-top: 4px; }
.ql-subrow { border-bottom: 1px solid var(--c-rule); }
.ql-subrow:last-child { border-bottom: none; }
.ql-subrow--error   { border-left: 2px solid var(--c-oxblood); }
.ql-subrow--warning { border-left: 2px solid var(--status-warn-dk); }
.ql-subrow-header { display: flex; align-items: center; gap: 8px; padding: 8px 12px; cursor: pointer; user-select: none; font-size: 12px; }
.ql-subrow-header:hover { background: var(--c-bone); }
.ql-subrow-chevron { width: 14px; height: 14px; color: var(--c-pale); flex-shrink: 0; transition: transform 0.15s; overflow: visible; }
.ql-subrow.is-open > .ql-subrow-header .ql-subrow-chevron { transform: rotate(90deg); }
.ql-subrow-name  { flex: 1; font-weight: 500; color: var(--c-ink); }
.ql-subrow-stats { font-size: 10px; color: var(--c-muted); white-space: nowrap; }
.ql-subrow-body { display: none; padding: 10px 14px; border-top: 1px solid var(--c-rule); background: var(--c-bone); }
.ql-subrow.is-open > .ql-subrow-body { display: block; }
.ql-log-pre { font-family: var(--font-mono); font-size: 11px; line-height: 1.7; margin: 0; color: var(--c-ink); white-space: pre-wrap; word-break: break-all; }
.ql-log-key { color: var(--c-muted); font-weight: 600; }
.ql-log-val { color: var(--status-ok-bright); }
.ql-log-num { color: var(--c-ink); }

/* ─────────────────────────────────────────────
   8.23  Status indicators  (status-dot)
   ───────────────────────────────────────────── */
.status-dot { display: inline-block; width: 8px; height: 8px; background: var(--c-pale); flex-shrink: 0; margin-right: 6px; vertical-align: middle; }
.status-dot--on  { background: var(--status-ok-bright); }
.status-dot--off { background: var(--c-pale); }

/* ─────────────────────────────────────────────
   8.24  Settings preference toggle  (pref-toggle)
   ───────────────────────────────────────────── */
.pref-list { display: flex; flex-direction: column; }
.pref-item { display: flex; align-items: center; gap: 14px; padding: 14px 0; border-bottom: 1px solid var(--c-rule); }
.pref-item:last-child { border-bottom: none; }
.pref-item:first-child { padding-top: 0; }
.pref-toggle { position: relative; display: inline-block; width: 40px; height: 22px; flex-shrink: 0; }
.pref-toggle input[type="checkbox"] { opacity: 0; width: 0; height: 0; position: absolute; }
.toggle-track { position: absolute; inset: 0; background: var(--c-rule); cursor: pointer; transition: background 0.18s; }
.toggle-track::after { content: ''; position: absolute; width: 16px; height: 16px; top: 3px; left: 3px; background: var(--c-white); transition: transform 0.18s; }
.pref-toggle input:checked + .toggle-track { background: var(--c-ink); }
.pref-toggle input:checked + .toggle-track::after { transform: translateX(18px); }
.pref-label { flex: 1; min-width: 0; }
.pref-name { font-size: 13px; font-weight: 600; color: var(--c-ink); margin: 0 0 2px; }
.pref-hint { font-size: 12px; color: var(--c-muted); margin: 0; line-height: 1.5; }

/* ─────────────────────────────────────────────
   8.25  Inline tier-change select  (ql-tag-select)
   ───────────────────────────────────────────── */
.ql-tag-form   { margin: 0; }
.ql-tag-select { font-size: 11px; border: 1px solid var(--c-rule); padding: 3px 6px; background: var(--c-white); color: var(--c-muted); cursor: pointer; border-radius: 0; outline: none; font-family: var(--font-sans); }
.ql-tag-select:focus { border-color: var(--c-ink); color: var(--c-ink); }


/* ── Watchlist table + Toast + Add Alert (from style.css) ── */
.watch-table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font-size: 0.92rem;
}

.watch-table thead { background: rgba(255, 255, 255, 0.30); }

.watch-table th,
.watch-table td {
  padding: 12px 10px;
  border-bottom: 1px solid var(--c-hairline);
  text-align: left;
  vertical-align: middle;
}

.watch-table thead th {
  font-size: 0.70rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--c-pale);
  font-weight: 650;
  border-bottom: 1px solid rgba(0,0,0,0.14);
}

.watch-table tbody tr { transition: background 120ms ease; }
.watch-table tbody tr:hover { background: rgba(0,0,0,0.03); }

.watch-table td.watch-details-col { position: relative; }
.watch-table td.watch-details-col::after {
  content: "›";
  position: absolute;
  right: 10px;
  top: 50%;
  transform: translateY(-50%);
  opacity: 0;
  transition: opacity 120ms ease;
  color: rgba(0,0,0,0.35);
}
.watch-table tbody tr:hover td.watch-details-col::after { opacity: 1; }

/* Status pill (single source of truth) */
.status-pill {
  display: inline-flex;
  align-items: center;
  padding: 5px 10px;
  border-radius: 0;
  border: 1px solid rgba(0,0,0,0.10);
  background: rgba(0,0,0,0.02);
  font-size: 12px;
  font-weight: 650;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}
/* In the watch-list table the status reads as an Astor hairline tag — same
   weight/scale/spacing as .ql-tag. The shared base above is left intact for
   the lender deal-memo hero, which layers its own status-* colour modifiers.
   Scoped to .ql-table so the colour modifiers below win the cascade. */
.ql-table .status-pill {
  padding: 2px 8px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.05em;
  border-color: var(--c-rule);
  background: transparent;
}
.ql-table .status-active { border-color: var(--c-ink); color: var(--c-ink); }
.ql-table .status-inactive { border-color: var(--c-rule); color: var(--c-muted); }

#watch-map {
  border: 1px solid var(--c-hairline);
  background: rgba(255, 255, 255, 0.30);
}

/* Address link polish */
.watch-link {
  display: inline-block;
  font-weight: 650;
  text-decoration: none;
}
.watch-link:hover { text-decoration: underline; }

.watch-postcode {
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.6px;
  white-space: nowrap;
}


/* =========================================================
   TOAST + UNDO TOAST
   ========================================================= */
.toast {
  position: fixed;
  right: 18px;
  bottom: 18px;
  max-width: 420px;
  padding: 12px 14px;
  font-size: 0.86rem;
  line-height: 1.4;
  opacity: 0;
  transform: translateY(10px);
  pointer-events: none;
  transition: opacity 0.15s ease, transform 0.15s ease;
  background: rgba(14, 20, 27, 0.95);
  color: rgba(255, 255, 255, 0.94);
  z-index: 9999;
}
.toast--show { opacity: 1; transform: translateY(0); }

.undo-toast {
  position: fixed;
  right: 18px;
  bottom: 18px;
  z-index: 9999;
  max-width: 420px;
  padding: 12px 14px;
  background: rgba(14, 20, 27, 0.95);
  color: rgba(255, 255, 255, 0.92);
  display: none;
  gap: 12px;
  align-items: center;
  border: 1px solid rgba(255, 255, 255, 0.10);
}
.undo-toast.show { display: flex; }

.undo-toast .undo-msg { font-size: 0.86rem; line-height: 1.35; }

.undo-toast .undo-actions {
  display: flex;
  gap: 10px;
  align-items: center;
  margin-left: auto;
}

.undo-toast .undo-btn {
  border: 1px solid rgba(255, 255, 255, 0.18);
  background: rgba(255, 255, 255, 0.10);
  color: var(--c-white);
  padding: 6px 12px;
  cursor: pointer;
  font-size: 0.70rem;
  font-weight: 650;
  letter-spacing: 0.22em;
  text-transform: uppercase;
}
.undo-toast .undo-btn:hover { background: rgba(255, 255, 255, 0.16); }

.undo-toast .undo-timer {
  font-variant-numeric: tabular-nums;
  font-size: 0.72rem;
  color: rgba(255, 255, 255, 0.70);
}


/* =========================================================
   Add alert UI + helpers
   ========================================================= */
.add-alert-wrap { margin-top: 10px; }

.add-alert-row {
  display: grid;
  grid-template-columns: 260px 1fr 160px;
  gap: 12px;
  align-items: end;
  margin-top: 10px;
}

.field label { display: block; margin-bottom: 6px; }

/* merged postcode control */
.postcode-merge {
  display: grid;
  grid-template-columns: 160px 1fr;
  border: 1px solid rgba(0, 0, 0, 0.12);
  overflow: hidden;
  background: var(--c-white);
}
.postcode-merge select,
.postcode-merge input {
  border: 0;
  width: 100%;
  padding: 10px 12px;
  outline: none;
  background: transparent;
}
.postcode-merge .postcode-left {
  border-right: 1px solid rgba(0, 0, 0, 0.10);
  background: rgba(0, 0, 0, 0.02);
}

.add-alert-btn {
  height: 42px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 100%;
}

.header-postcode-preview {
  margin-top: 6px;
  font-size: 12px;
  opacity: 0.75;
  display: none;
}

.postcode-merge select:disabled {
  opacity: 0.55;
  color: rgba(0,0,0,0.55);
  cursor: not-allowed;
}
select option[disabled] { color: rgba(0,0,0,0.55); }

.form-feedback {
  margin-top: 10px;
  padding: 10px 12px;
  font-size: 13px;
  line-height: 1.35;
}
.form-feedback--error {
  background: rgba(220, 38, 38, 0.08);
  border: 1px solid rgba(220, 38, 38, 0.18);
}
.form-feedback--success {
  background: rgba(16, 185, 129, 0.10);
  border: 1px solid rgba(16, 185, 129, 0.18);
}

/* secondary destructive action */
.btn-stop {
  appearance: none;
  background: transparent;
  border: 1px solid rgba(0, 0, 0, 0.18);
  color: rgba(0, 0, 0, 0.75);
  padding: 7px 14px;
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease, transform 0.05s ease;
}
.btn-stop:hover {
  background: rgba(0, 0, 0, 0.04);
  border-color: rgba(0, 0, 0, 0.35);
  color: rgba(0, 0, 0, 0.9);
}
.btn-stop:active { transform: translateY(1px); }
.btn-stop:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.12);
}
.watch-table tr .btn-stop { opacity: 0.65; }
.watch-table tr:hover .btn-stop { opacity: 1; }

/* =========================================================
   Responsive
   ========================================================= */
@media (max-width: 980px) {
  .pc-grid { grid-template-columns: 1fr; }
  .pc-side { position: static; }
  .eld-tools-list { grid-template-columns: 1fr; }
  .eld-tool-row { min-height: 260px; }
}

@media (max-width: 860px) {
  .eld-hero-bottom { grid-template-columns: 1fr; }
  .eld-hero-sub { justify-self: start; max-width: 70ch; }
  .eld-hero-title { line-height: 0.96; }
}

@media (max-width: 900px) {
  .add-alert-row { grid-template-columns: 1fr; }
  .add-alert .primary-btn { width: 100%; }
}

@media (max-width: 640px) {
  .pc-search-row { grid-template-columns: 1fr; }
  .btn, .primary-btn { width: 100%; }
  .watchlist-top { flex-direction: column; align-items: flex-start; }

  .eld-tools-wrap { padding: 54px 18px 70px; }
  .eld-tools-head {
    flex-direction: column;
    align-items: flex-start;
    gap: 10px;
    padding-bottom: 18px;
  }
  .eld-tools-sub { padding: 9px 11px; }
}

/* ─────────────────────────────────────────────────────────────────────────────
   PLANNING MAP  —  map-* components
   Full-viewport map shell with slide-in detail panel and overlay controls.
   Extends: .tools-shell / .tools-content layout, .ql-card, .btn, .ql-tag, .ql-empty
   ───────────────────────────────────────────────────────────────────────────── */

/* Map fills tools-content completely — no scroll, no padding */
.map-page .tools-content { overflow: hidden; }

.map-wrap {
  position: relative;
  flex: 1;
  display: flex;
  overflow: hidden;
}

#map { flex: 1; }

/* ── Top-left control stack ──
   Search · AI search · Watch-area · result chip live in one flex column so
   they flow and never collide. (They were previously each absolutely
   positioned by a hard-coded top: offset — any height change, e.g. the search
   error showing or a wide TV aspect, made them overlap.) max-width keeps the
   stack from crowding the map on narrow viewports. */
.map-controls {
  position: absolute;
  top: var(--space-4);
  left: var(--space-4);
  z-index: 10;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  width: 340px;
  max-width: calc(100% - (2 * var(--space-4)));
}

/* ── Search overlay ── */
.map-search {
  position: relative;   /* anchors .map-search__error */
  display: flex;
  gap: var(--space-2);
}

.map-search input {
  flex: 1;
  padding: 9px 12px;
  border: 1px solid var(--c-rule);
  font-size: var(--text-sm);
  background: var(--c-white);
  color: var(--c-ink);
  outline: none;
  box-shadow: none;
  border-radius: 0;
}

.map-search input:focus { border-color: var(--c-ink); }

/* Ask-the-map AI field. Lifted out of an inline style on #mapAIInput so a media
   query can reach it at all — see the note in map.html. The map page is not
   under .ql-page, and mobile.css's guard is :where()-zero-specificity, so the
   16px bump is stated here explicitly. */
.map-ai-input {
  flex: 1;
  min-width: 0;
  border: 0;
  padding: 8px 10px;
  font-size: 12px;
  background: transparent;
  outline: none;
  color: var(--c-ink);
}
@media (max-width: 768px) {
  .map-ai-input { font-size: 16px; }
  /* MapLibre's own controls stay 29px outside presentation mode (§3). */
  .map-wrap .maplibregl-ctrl button { width: 44px; height: 44px; }
}

.map-search__error {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  background: var(--status-err-bg);
  border: 1px solid var(--status-err-border);
  color: var(--status-err-dk);
  padding: var(--space-2) var(--space-3);
  font-size: var(--text-xs);
  display: none;
}

/* ── Detail panel ── */
.map-panel {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 360px;
  background: var(--c-white);
  border-left: 1px solid var(--c-rule);
  display: flex;
  flex-direction: column;
  transform: translateX(100%);
  transition: transform var(--transition-base);
  z-index: 20;
  overflow: hidden;
}

.map-panel.is-open { transform: translateX(0); }

.map-panel__head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: var(--space-2);
  padding: var(--space-4);
  border-bottom: 1px solid var(--c-rule);
  background: var(--c-bone);
}

.map-panel__address {
  font-size: var(--text-base);
  font-weight: var(--weight-semibold);
  color: var(--c-ink);
  margin: 0 0 2px;
  line-height: var(--leading-snug);
}

.map-panel__borough {
  font-size: var(--text-xs);
  color: var(--c-muted);
  text-transform: capitalize;
  letter-spacing: 0.03em;
  margin: 0;
}

.map-panel__close {
  background: none;
  border: none;
  cursor: pointer;
  color: var(--c-muted);
  padding: 2px;
  flex-shrink: 0;
  line-height: 1;
}

.map-panel__close:hover { color: var(--c-ink); }

.map-panel__body {
  padding: var(--space-4);
  overflow-y: auto;
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.map-panel__proposal {
  font-size: var(--text-sm);
  color: var(--c-muted);
  line-height: var(--leading-normal);
  margin: 0;
}

.map-panel__meta {
  display: grid;
  grid-template-columns: 1fr 1fr;
  border: 1px solid var(--c-rule);
}

.map-meta-item {
  padding: var(--space-3) var(--space-3);
  border-right: 1px solid var(--c-rule);
}

.map-meta-item:last-child { border-right: none; }

.map-meta-item__lbl {
  font-size: var(--text-2xs);
  font-weight: var(--weight-semibold);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  color: var(--c-muted);
  margin-bottom: 3px;
}

.map-meta-item__val {
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  color: var(--c-ink);
  font-family: var(--font-mono);
}

.map-panel__actions {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-top: auto;
  padding-top: var(--space-2);
}

/* Full-width panel buttons — extend .btn */
.map-panel__actions .btn {
  width: 100%;
  padding: 10px var(--space-4);
  justify-content: center;
}

/* Status tag variants for planning decisions — extend .ql-tag */
.ql-tag--approved {
  background: var(--status-ok-bg-deep);
  border-color: var(--status-ok-border);
  color: var(--status-ok-text-dk);
}

.ql-tag--refused {
  background: var(--status-err-bg);
  border-color: var(--status-err-border);
  color: var(--status-err-text-alt);
}

.ql-tag--pending {
  background: var(--status-warn-bg-deep);
  border-color: var(--status-warn-border);
  color: var(--status-warn-text-alt);
}

/* Borough panel A4 list — last item no divider */
#bpA4List > div:last-child { border-bottom: none; }

/* ── Layer controls ── */
.map-layers {
  position: absolute;
  bottom: var(--space-8);
  left: var(--space-4);
  z-index: 10;
  background: var(--c-white);
  border: 1px solid var(--c-rule);
  padding: var(--space-3) var(--space-4);
}

.map-layers__title {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  width: 100%;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  font-size: var(--text-2xs);
  font-weight: var(--weight-semibold);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  color: var(--c-muted);
  margin-bottom: var(--space-2);
}

.map-layers__title:hover { color: var(--c-ink); }

.map-layers__chev {
  flex-shrink: 0;
  transition: transform var(--transition-base);
}

/* Collapsed: hide the body, fold the chevron up, drop the title's gap so the
   panel shrinks to a single "Layers" row that reclaims the map. */
.map-layers.is-collapsed .map-layers__body { display: none; }
.map-layers.is-collapsed .map-layers__title { margin-bottom: 0; }
.map-layers.is-collapsed .map-layers__chev { transform: rotate(180deg); }

/* Cap the open legend so a tall list never runs off a short viewport. */
.map-layers__body {
  max-height: min(52vh, 420px);
  overflow-y: auto;
}

/* Collapsible sub-groups ("Your Deals", "Constraints"). Same visual weight as
   the old static .map-layers__sub headings they replace — a heading that is now
   a control, not new chrome. Collapsed by default: the legend was 17 rows tall
   and ate the lower-left quarter of the map. */
.map-layers__group-toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  width: 100%;
  background: none;
  border: 0;
  padding: 0;
  margin: var(--space-2) 0 var(--space-1);
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: 9px;
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  color: var(--c-pale);
  text-align: left;
}

.map-layers__group-toggle:hover { color: var(--c-muted); }

.map-layers__group-toggle .map-layers__chev {
  transform: rotate(180deg);   /* collapsed: chevron points up */
}

.map-layers__group-toggle[aria-expanded="true"] .map-layers__chev {
  transform: rotate(0deg);
}

.map-layers label {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-sm);
  color: var(--c-ink);
  cursor: pointer;
  margin-bottom: var(--space-1);
}

.map-layers label:last-child { margin-bottom: 0; }

/* Legend zoom cue. A layer that is switched on but below its zoom threshold is
   not "off" and not "broken" — it is dormant, and the legend has to say which.
   "(zoom 17+)" could not: a zoom level is not a unit anyone reads off a scale
   bar in metres. */
.map-layers__cue {
  margin-left: auto;
  flex-shrink: 0;
  font-family: var(--font-mono);
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  color: var(--c-muted);
  border-bottom: 1px dotted currentColor;
  cursor: zoom-in;
}

.map-layers__cue:hover { color: var(--c-ink); }

/* Dim the row itself so the state reads at a glance, without hiding the
   checkbox — the user's choice to have the layer on is still theirs. */
.map-layers label.is-dormant { color: var(--c-muted); }


/* ── Zoom-out hint pill ── */
.map-zoom-hint {
  display: none;
  position: absolute;
  bottom: calc(var(--space-8) + 120px);
  left: 50%;
  transform: translateX(-50%);
  background: rgba(26,26,26,0.82);
  color: var(--c-white);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  padding: var(--space-1) var(--space-3);
  border-radius: 0;
  pointer-events: none;
  white-space: nowrap;
  backdrop-filter: blur(4px);
  z-index: 5;
}

/* ── Pin-loading hint (ASTOR §14 canonical pattern) ── */
.map-load-hint {
  position: absolute;
  top: var(--space-3);
  left: 50%;
  transform: translateX(-50%);
  display: flex; align-items: center; gap: 8px;
  background: rgba(26,26,26,0.82);
  color: var(--c-white);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  padding: var(--space-1) var(--space-3);
  border-radius: 0;
  pointer-events: none;
  white-space: nowrap;
  backdrop-filter: blur(4px);
  z-index: 5;
}
.map-load-hint[hidden] { display: none; }
.map-load-hint__dot {
  width: 5px; height: 5px;
  background: var(--c-oxblood);
  flex-shrink: 0;
  animation: ql-stitch 2.8s ease-in-out infinite;
}

/* ── Display toggle (presentation mode switch) ──
   Pinned bottom-right, below the detail panel (z 10 < panel z 20) so an open
   panel covers it. Lets anyone force big-screen mode on/off; the choice is
   sticky and overrides the auto heuristic. */
.map-display-toggle {
  position: absolute;
  bottom: var(--space-8);
  right: var(--space-4);
  z-index: 10;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  background: var(--c-white);
  border: 1px solid var(--c-rule);
  padding: 8px 12px;
  font-size: var(--text-2xs);
  font-weight: var(--weight-semibold);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  color: var(--c-muted);
  cursor: pointer;
}
.map-display-toggle:hover { color: var(--c-ink); border-color: var(--c-ink); }
.map-display-toggle svg { flex-shrink: 0; }
.map-wrap.is-presentation .map-display-toggle { color: var(--c-ink); border-color: var(--c-ink); }

/* ── Presentation / big-screen mode ──
   Auto-enabled on large DPR-1 displays (TVs, projectors, tab-casting) and
   forceable via the Display toggle. Class-driven so it scales the chrome at
   ANY resolution when on — viewport width alone can't tell a TV from a desk
   monitor, so we don't drive this off a media query. Map stays legible from
   across a room: larger type, wider panels, bigger tap/― targets. */
.map-wrap.is-presentation .map-controls { width: 460px; gap: var(--space-3); }
.map-wrap.is-presentation .map-search input,
.map-wrap.is-presentation #mapAIInput { font-size: 18px; padding: 14px 16px; }
.map-wrap.is-presentation .map-search .btn { font-size: 16px; padding: 12px 22px; }
.map-wrap.is-presentation #mapAIBtn { font-size: 14px; padding: 0 20px; }
.map-wrap.is-presentation #mapWatchAreaBtn { font-size: 14px; padding: 14px 18px; }
.map-wrap.is-presentation #mapAIChip { font-size: 16px; padding: 18px 20px; }

.map-wrap.is-presentation .map-layers { padding: var(--space-4) var(--space-5); }
.map-wrap.is-presentation .map-layers__title { font-size: var(--text-sm); }
.map-wrap.is-presentation .map-layers__body { max-height: 72vh; }
.map-wrap.is-presentation .map-layers label {
  font-size: 16px;
  gap: var(--space-3);
  margin-bottom: var(--space-2);
}
.map-wrap.is-presentation .map-swatch { width: 16px; height: 16px; }
.map-wrap.is-presentation .map-glyph { width: 20px; height: 20px; }

.map-wrap.is-presentation .map-panel { width: 480px; }
.map-wrap.is-presentation .map-panel__address { font-size: var(--text-lg); }
.map-wrap.is-presentation .map-panel__proposal,
.map-wrap.is-presentation .map-meta-item__val { font-size: 16px; }
.map-wrap.is-presentation .map-panel__actions .btn { font-size: 15px; padding: 14px var(--space-4); }

/* MapLibre's own zoom/compass controls are tiny — scale them too. */
.map-wrap.is-presentation .maplibregl-ctrl button { width: 38px; height: 38px; }

/* The iOS-zoom guard that used to live here (scoped to .ql-page/.dash-page) is
   superseded by static/planning/mobile.css, which fixes the cascade defect at
   the root by using the @layer utilities slot — see MOBILE_CSS_ROLLOUT §1.2/§3.
   Deliberately NOT kept as a belt-and-braces copy: two sources of one truth is
   how the original bug bred. */
@media (max-width: 768px) {
  /* Site-settings feature switches are a 40x22 track — the real tap target is
     the 22px label, well under the 44px floor (§3), and these toggle live
     features. Grow the hit area with a centred pseudo-element so the switch
     keeps its exact look. .pref-toggle::after is unused (the knob is drawn by
     .toggle-track::after). */
  .pref-toggle::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 48px;
    height: 44px;
  }
}


/* ════════════════════════════════════════════════════════════════════════════
   PICK-ONE CONTROLS — .seg and .pick
   ════════════════════════════════════════════════════════════════════════════
   Cain, 2026-08-17: "make it a memory that I hate dropdowns or inputs boxes
   that span the whole page for zero reason." A <select> hides its options until
   clicked and costs a click to read; on a short, known set it is strictly worse
   than showing them. The comment already written at .ff-scope says it best —
   "a dropdown can only ever show the one you already picked."

   The system had five radio-card variants before these (.fund-type-opt,
   .lm-vis__opt, .dr-settle__opt, .ff-scope__opt, .dm-check__opt) and not one is
   a general, form-native, no-JS "pick one of 2-6": each is domain-named,
   depends on JS for its selected state, or is button-backed rather than
   form-backed. These follow .fund-type-opt's own :has(input:checked) idiom.

     .seg   — a segmented control, one radio per option, 2-6 options.
     .pick  — the same choice when each option needs a line of explanation.

   RESTATED ONCE, DELIBERATELY: static/planning/auth.css carries a `.pc-form`
   -scoped copy of .seg for the PUBLIC deal-intake form, which is white-label
   and does not load this file. Change one, change both — the comment there
   says the same thing from the other side.

   THE RESETS ARE LOAD-BEARING, not tidiness. The bare `label` rule above styles
   every <label> as an uppercase eyebrow — 0.06em tracking, weight 600, 6px
   bottom margin — and these options ARE labels. A class outranks an element
   selector only for the properties it DECLARES, so anything left unsaid still
   applies, and text-transform/font-weight then INHERIT into the child spans.
   Without them, .pick__why rendered its explanatory sentence in full caps. */
.seg { display: inline-flex; flex-wrap: wrap; width: fit-content;
       border: 1px solid var(--c-rule); background: var(--c-white); }
.seg__opt { position: relative; padding: 7px 14px; cursor: pointer;
            font-size: 12px; line-height: 1.2; color: var(--c-muted);
            border-right: 1px solid var(--c-rule); white-space: nowrap;
            display: inline-flex; align-items: center; min-height: 34px;
            text-transform: none; letter-spacing: normal; font-weight: 500;
            margin-bottom: 0; transition: background .12s, color .12s; }
.seg__opt:last-child { border-right: none; }
.seg__opt input { position: absolute; opacity: 0; pointer-events: none; }
.seg__opt:hover { background: var(--c-bone); color: var(--c-ink); }
/* THE SELECTED STATE HAS TWO SPELLINGS, AND BOTH ARE REQUIRED. `:has(input:checked)`
   covers the radio form of this control. It CANNOT cover the form where the
   options are links — a tablist that switches view by GET, which has no radio to
   check — and Settings → Lending shipped exactly that: correct `is-active` and
   `aria-selected` in the markup, and no rule in any stylesheet to draw them.
   Every tab rendered identically, so the one you were on was invisible. Cain,
   2026-08-23: "the tab doesn't show which one you are on, eg LTV or LTGDV so the
   user has to click through to figure it out." Fixed on the COMPONENT, not on
   that page, so the next link-shaped .seg is not the same bug again. */
.seg__opt:has(input:checked),
.seg__opt.is-active { background: var(--c-ink); color: var(--c-white); }
/* A link has no radio to focus, so give the anchor form its own ring. */
a.seg__opt { text-decoration: none; }
a.seg__opt:focus-visible { outline: 1px solid var(--c-ink); outline-offset: -2px; }
.seg__opt:has(input:focus-visible) { outline: 1px solid var(--c-ink);
                                     outline-offset: -2px; }
/* A terminal state is not the next rung of a ladder, so it is set apart from
   the progressive ones rather than continuing the run. */
.seg--split .seg__opt--term { border-left: 1px solid var(--c-ink); }
.seg__opt--term:has(input:checked),
.seg__opt--term.is-active { background: var(--c-oxblood); }

/* .chipset — pick one of SEVEN OR MORE, where the labels explain themselves.
   Added 2026-08-21. .seg is a single bordered box with internal dividers, and
   it is documented for 2-6 options for a reason: at ten it wraps, and a wrapped
   segmented control leaves an orphan on a second row with a gap beside it where
   the box keeps going. Cain saw exactly that on the public intake form the day
   it shipped — "sloppily implemented ... the usage could be significantly more
   efficient".

   So: no outer box. Each option is its own hairline chip and the row wraps like
   text, which is what a tap-chip actually is. Same radio-backed, no-JS idiom as
   .seg and .pick, same :has(input:checked) selected state.

   Which of the three to reach for:
     .seg      2-6 options, short labels, one row      — a segmented control
     .pick     any count where each option needs a LINE of explanation
     .chipset  7+ options whose labels need no explanation
   `Retail` explains itself; `Release of Equity` does not. That is the test. */
.chipset { display: flex; flex-wrap: wrap; gap: 6px; }
.chipset__opt { position: relative; display: inline-flex; align-items: center;
                min-height: 34px; padding: 7px 13px; cursor: pointer;
                font-size: 12px; line-height: 1.2; color: var(--c-muted);
                border: 1px solid var(--c-rule); background: var(--c-white);
                text-transform: none; letter-spacing: normal; font-weight: 500;
                margin-bottom: 0; transition: background .12s, color .12s,
                border-color .12s; }
.chipset__opt input { position: absolute; opacity: 0; pointer-events: none; }
.chipset__opt:hover { border-color: var(--c-ink); color: var(--c-ink); }
.chipset__opt:has(input:checked) { background: var(--c-ink); color: var(--c-white);
                                   border-color: var(--c-ink); }
.chipset__opt:has(input:focus-visible) { outline: 1px solid var(--c-ink);
                                         outline-offset: 2px; }
/* A nested family (the commercial classes) reveals only once its parent is
   picked — ten chips at rest becomes six, and the four specialisms appear when
   they can mean something. Indented under a rule so the nesting is visible. */
.chipset--child { margin-top: 8px; padding-left: 14px;
                  border-left: 1px solid var(--c-rule); }

.pick { display: grid; gap: 0; border: 1px solid var(--c-rule);
        background: var(--c-white); }
.pick--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.pick--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
/* Stacked when each option needs more than a line: a columns grid would set the
   longest description's height for every option. */
.pick--stack { grid-template-columns: 1fr; }
.pick--stack .pick__opt { border-right: none;
                          border-bottom: 1px solid var(--c-rule); }
.pick--stack .pick__opt:last-child { border-bottom: none; }
.pick__opt { position: relative; display: flex; flex-direction: column; gap: 5px;
             padding: 16px 18px; cursor: pointer;
             text-transform: none; letter-spacing: normal; margin-bottom: 0;
             border-right: 1px solid var(--c-rule); }
.pick__opt:last-child { border-right: none; }
.pick__opt input { position: absolute; opacity: 0; pointer-events: none; }
.pick__opt:hover { background: var(--c-bone); }
.pick__name { font-size: 13px; font-weight: 600; color: var(--c-ink); }
.pick__why { font-size: 11px; font-weight: 400; color: var(--c-muted);
             line-height: 1.45; }
.pick__opt:has(input:checked) { background: var(--c-ink); }
.pick__opt:has(input:checked) .pick__name { color: var(--c-white); }
.pick__opt:has(input:checked) .pick__why { color: rgba(255,255,255,0.65); }

/* A nested family of `.pick` cards — the commercial specialisms under
   "Commercial". Restated in app.css and auth.css exactly as the rest of the
   pick-one primitives are: the PUBLIC white-label intake form loads auth.css
   and never app.css, and a copy is the price of that split.

   The left rule and indent are what say "this refines the answer above" —
   necessary because parent and children share one radio group, so picking a
   child unchecks the parent card and the geometry is the only thing left
   holding them together. The lead-in line carries the same message in words. */
.pick-child { margin-top: 10px; padding-left: 14px;
  border-left: 1px solid var(--c-rule); }

/* `hidden` MUST ACTUALLY HIDE. The reveal in intake_form.html sets
   `box.hidden = true` on a family that is not in play. `[hidden]` is a
   USER-AGENT rule (`display: none`), so ANY author `display` declaration
   outranks it — `.chipset { display: flex }` did exactly that, and the
   commercial specialisms sat on screen permanently no matter what the script
   decided. Two independent faults produced one symptom: the reveal could not
   find its parent, AND hiding did not hide.

   Author-level and specific enough to beat the component rule. Do not remove it
   because "hidden already does that" — here it did not. */
.pick-child[hidden],
.chipset--child[hidden] { display: none; }
.pick__lead { display: block; font-size: 10px; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.06em; color: var(--c-muted);
  margin-bottom: 6px; }
.pick__opt:has(input:focus-visible) { outline: 1px solid var(--c-ink);
                                      outline-offset: -2px; }
@media (max-width: 768px) {
  .pick--2, .pick--3 { grid-template-columns: 1fr; }
  .pick__opt { border-right: none; border-bottom: 1px solid var(--c-rule); }
  .pick__opt:last-child { border-bottom: none; }
}

} /* end @layer components */

/* ════════════════════════════════════════════════════════════════════════════
   .btn-astor — the Astor primary/ghost button.

   Canonically defined in home.css, but the tools shell (base_tools.html)
   overrides base_styles to load ONLY tokens.css + app.css — home.css never
   loads here, so every .btn-astor inside a tool page (investor portal, borough
   intelligence, etc.) rendered as an unstyled browser button. Ported verbatim
   from home.css so those buttons match the marketing surface. Unlayered +
   !important (as in the original) so it resolves identically in both shells;
   --ql-ink / --ql-font come from tokens.css, present in both.
   ════════════════════════════════════════════════════════════════════════════ */
.btn-astor {
  display: inline-flex !important;
  align-items: center !important;
  gap: 10px;
  padding: 6px 32px !important;
  background: var(--ql-ink) !important;
  color: var(--c-white) !important;
  font-family: var(--ql-font);
  font-size: 13px !important;
  font-weight: 500 !important;
  letter-spacing: 0.05em !important;
  text-transform: none !important;
  text-decoration: none !important;
  border: 1px solid var(--ql-ink) !important;
  border-radius: 0px !important;
  height: auto !important;
  line-height: normal !important;
  cursor: pointer;
  white-space: nowrap;
  box-shadow: none !important;
  transition: opacity 0.2s ease, box-shadow 0.2s ease;
}
.btn-astor:hover {
  background: var(--ql-ink) !important;
  border-color: var(--ql-ink) !important;
  color: var(--c-white) !important;
  opacity: 0.8;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.35) !important;
  text-decoration: none !important;
}
.btn-astor--ghost {
  background: transparent !important;
  color: var(--ql-ink) !important;
  border: 1px solid var(--c-ink) !important;
  opacity: 1;
}
.btn-astor--ghost:hover {
  background: transparent !important;
  border-color: var(--ql-ink) !important;
  color: var(--ql-ink) !important;
  opacity: 0.7;
  box-shadow: none !important;
}
/* ── Busy-lock status line (ASTOR §14) ────────────────────────────────────────
   Shown by static/planning/busy.js while a slow submit is in flight: the
   ql-stitch dot (.ai-stitch-dot, above) plus a present-tense sentence naming the
   work. The spinner reuses astor-spin — the only sanctioned rotation. */
.astor-busy { display: flex; align-items: center; gap: var(--space-2);
  margin-top: var(--space-3); font-size: 11px; color: var(--c-muted); line-height: 1.5; }
.astor-busy[hidden] { display: none; }
.astor-busy__spin { animation: astor-spin 0.8s linear infinite; flex-shrink: 0;
  vertical-align: -2px; margin-right: 4px; }
[data-busy-running] button:disabled, button:disabled .astor-busy__spin { cursor: progress; }

@media (prefers-reduced-motion: reduce) {
  .astor-busy__spin { animation: none; }
}

/* ── The grouped loan board (dg-*) ───────────────────────────────────────────
   Groups are DERIVED from loan_type x loan_state and ordered per COMPANY. The
   whole book reads as a handful of collapsed lines carrying counts and money, so
   the page never needs the sideways scroll the Monday board needs — Cain,
   2026-08-28: the scroll "being massive sideways not vertical" is the thing to
   avoid. Everything here is tokens; no new colours enter the system.          */
.dg-board { display: flex; flex-direction: column; gap: 14px; }
.dg { border: 1px solid var(--c-rule); background: var(--c-white); }
.dg__head { padding: 14px 16px; border-left: 3px solid var(--c-rule); }
.dg--live > .dg__head { border-left-color: var(--c-ink); }
.dg--redeemed > .dg__head { border-left-color: var(--c-pale); }
.dg--unset > .dg__head { border-left-color: var(--c-oxblood); }
.dg__grip { color: var(--c-pale); cursor: grab; font-size: 13px; line-height: 1; user-select: none; flex-shrink: 0; }
.dg__grip:active { cursor: grabbing; }
.dg__toggle { display: flex; align-items: center; gap: 9px; background: none; border: 0; padding: 0; font: inherit; color: inherit; cursor: pointer; text-align: left; }
.dg__toggle:focus-visible { outline: 2px solid var(--c-ink); outline-offset: 3px; }
.dg__chev { color: var(--c-muted); font-size: 11px; transition: transform .15s ease; }
.dg.is-open .dg__chev { transform: rotate(90deg); }
.dg__name { font-size: 15px; font-weight: 600; color: var(--c-ink); }
/* The metrics are COLUMNS down the board, so their widths are the GRID's (below),
   not their own: a box sized by its own figure puts each column's right edge
   wherever that figure ends. The widest figure this book produces, a nine-digit
   total, measures 124px at 18px mono — the money tracks are 128. */
.dg__metric { display: flex; flex-direction: column; align-items: flex-end; gap: 1px; min-width: 0; }
.dg__metric b { font-size: 18px; font-weight: 300; letter-spacing: -.03em; color: var(--c-ink); font-variant-numeric: tabular-nums; }
.dg__metric i { font-size: 10px; font-style: normal; text-transform: uppercase; letter-spacing: .10em; color: var(--c-muted); }
.dg__body { border-top: 1px solid var(--c-rule); }
.dg:not(.is-open) .dg__body { display: none; }
.dg__more { padding: 11px 16px; font-size: 12px; color: var(--c-muted); border-top: 1px solid var(--c-rule); }
/* The totals row: the filter's own aggregate, above the groups and outside
   .dg-board, so it takes the board's gap as its own bottom margin. */
.dg--total { margin-bottom: 14px; }
.dg--total > .dg__head { border-left-color: var(--c-ink); }
/* The deal table's columns. `.table-wrap-sticky table` is `table-layout: fixed`,
   which with no widths gave all eight columns an equal eighth — the address, the
   longest thing on the row, wrapped to four lines beside a Rate column mostly
   empty. Every width is restated here and they sum to 100: a fixed table gives an
   undeclared column ZERO width, not a share.
   SIZED FOR A 1280px LAPTOP, measured: the table there is ~937px, and at the first
   widths (dates 9.5%, 16px gutters) "05 Nov 2026" and "not recorded" overflowed
   their 89px cells and painted over the next column. 1440 and 1920 were clean,
   which is why the first render passed. */
.dg-table .dg-col--deal  { width: 25%; }
.dg-table .dg-col--ubo   { width: 13%; }
.dg-table .dg-col--date  { width: 10.5%; }
.dg-table .dg-col--money { width: 10.5%; }
.dg-table .dg-col--pct   { width: 7%; }
.dg-table .dg-col--rate  { width: 13%; }
/* Inner gutters 10px, outer 16px — desktop only: at <=768px `.tbl--rows` turns the
   row into a grid with its own zero padding, and an unlayered rule here would beat
   it. */
/* And a floor. Below ~1150px wide the percentages leave a date 79px and it painted
   over Maturity (measured at 1100 and 1024). Under 900px the table scrolls inside
   `.table-wrap-sticky`, which is `overflow-x: auto` — a short sideways scroll in a
   narrow window, never text on top of text. */
@media (min-width: 769px) {
  .dg-table { min-width: 900px; }
  .dg-table th, .dg-table td { padding-left: 10px; padding-right: 10px; }
  .dg-table th:first-child, .dg-table td:first-child { padding-left: 16px; }
  .dg-table th:last-child, .dg-table td:last-child { padding-right: 16px; }
}
/* Figures are one line each; a date or a sum broken over two lines stops being
   scannable down its column. Heads may wrap (a clipped head still looks whole). */
.dg-table td.ql-num, .dg-table td.dg-date { white-space: nowrap; }
/* A date the row does not have, said in words and set back a step. Words may wrap
   where a date may not: in a narrow window "not / recorded" on two lines is still
   read correctly, while an overflowing phrase paints over the Maturity column. */
.dg-table .dg-unset { color: var(--c-pale); white-space: normal; }
.dg.is-dragging { opacity: .45; }
.dg.is-over { border-color: var(--c-ink); }
@media (prefers-reduced-motion: reduce) { .dg__chev { transition: none; } }

/* ── The deal board, 2026-09-11: sorting, redeemed, the master row, six figures ──
   Approved as docs/mockups/board-and-favourites.html (round 2), measured there at
   1920/1440/1366/1280/1100/1024/390 by measure_board_and_favourites_mockup.py. */
/* (4) THE HEAD BECOMES A GRID. Six figures are six COLUMNS down the board, and a
   flex row sizes each box by its own figure, so a column's right edge would land
   wherever its figure ended. Every head — the master row and every group — shares
   one track list, so the columns agree to the pixel all the way down. */
.dg__head { display: grid;
  grid-template-columns: 14px minmax(0, 1fr) 44px 128px 128px 76px 76px 124px;
  column-gap: 10px; align-items: start; }
.dg__head > .dg__grip, .dg__head > .dg__toggle { align-self: center; }
.dg__metric small { font-size: 10px; color: var(--c-pale); letter-spacing: 0; white-space: nowrap; }
.dg__metric .dg__nil { color: var(--c-pale); }
.dg__grip--none { cursor: default; }
.dg__toggle { min-width: 0; }
.dg__name { min-width: 0; }
.dg__hint { display: block; font-size: 11px; font-weight: 400; color: var(--c-muted); margin-top: 2px; }
/* Below the 1200 rung the name takes its own line and the six figures sit under
   it, still in the same six tracks — so the columns still line up. */
@media (max-width: 1200px) {
  .dg__head { grid-template-columns: 14px minmax(0, 1fr) 40px 124px 124px 72px 72px 116px; row-gap: 10px; }
  .dg__head > .dg__toggle { grid-column: 2 / -1; }
  .dg__metric--n   { grid-column: 3; } .dg__metric--fac { grid-column: 4; }
  .dg__metric--gdv { grid-column: 5; }
  .dg__metric--ltv { grid-column: 6; } .dg__metric--ltgdv { grid-column: 7; }
  .dg__metric--mat { grid-column: 8; }
}
/* A phone: no drag (the grip is a mouse affordance), the name across the top and
   the figures three to a row beneath it. */
@media (max-width: 768px) {
  .dg__head { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .dg__head > .dg__grip { display: none; }
  .dg__head > .dg__toggle { grid-column: 1 / -1; }
  .dg__head .dg__metric { grid-column: auto; align-items: flex-start; }
  .dg__metric--mat:empty { display: none; }
}
@media (max-width: 540px) {
  .dg__head { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* (2) REDEEMED READS AS THE CLOSED BOOK. Three signals, all tokens:
   the head sits on the SUNKEN surface (--c-stone, §2 — "micro-areas"), the
   group's status glyph is HOLLOW where a live group's is SOLID (§16.2's fill
   axis: solid ink = live, hollow stone outline = redeemed / closed), and the
   name drops to regular weight. Text stays ink: muted-on-stone is 3.9:1 and
   fails AA, and a distinction bought with illegibility is not one. */
.dg__state { display: inline-block; width: 9px; height: 9px; flex-shrink: 0;
  background: var(--c-ink); border: 1px solid var(--c-ink); }
.dg--redeemed .dg__state { background: transparent; border-color: var(--c-muted); }
.dg--unset .dg__state { background: transparent; border-color: var(--c-oxblood); }

/* THE MASTER ROW IS THE TOP HEADER, not another group. Cain, 2026-09-11: it
   "needs to be more top headery". It is the total, and §4.1 sanctions exactly
   this for a total: an ink fill with white text, finalising the arithmetic
   beneath it. Labels and counts take §5's white-at-opacity tiers, never grey. */
.dg--total { border-color: var(--c-ink); }
.dg--total > .dg__head { background: var(--c-ink); border-left-color: var(--c-ink); }
.dg--total .dg__name, .dg--total .dg__metric b { color: var(--c-white); }
.dg--total .dg__metric i, .dg--total .dg__hint, .dg--total .dg__chev { color: rgba(255,255,255,0.60); }
.dg--total .dg__metric small { color: rgba(255,255,255,0.45); }
.dg--total .dg__toggle:focus-visible { outline-color: var(--c-white); }
.dg-foot { font-size: 11px; line-height: 1.5; color: var(--c-muted); margin: 12px 16px 16px; max-width: 88ch; }
.dg--redeemed > .dg__head { background: var(--c-stone); }
.dg--redeemed .dg__name { font-weight: 400; }
.dg--redeemed .dg__metric b { font-weight: 300; }

/* (1) EVERY HEADER SORTS. The house caret (.sort-caret, app.css) — after a
   left-aligned label, BEFORE a right-aligned one, and hung into the cell's own
   padding either way so a label ends where its column's figures end and the
   caret costs the column no width. Heads may wrap; a clipped head still looks whole. */
.dg-table th.js-sort { white-space: normal; }
.dg-table th.js-sort:focus-visible { outline: 2px solid var(--c-ink); outline-offset: -2px; }
.dg-table th .sort-caret { margin: 0 -12px 0 4px; }
.dg-table th.ql-num .sort-caret { margin: 0 4px 0 -12px; }

/* (3) THE MASTER ROW. Open, it pins to the top of the window and the one table
   fills what is left beneath it — its own header pinned inside (the house
   .table-wrap-sticky), and each group's subheading pinned under that as you pass
   through it. The table scrolls inside its frame, so the narrow-window sideways
   scroll still works; `overscroll-behavior` stops the frame handing the scroll to
   the page, which would carry the master row away. */
.dg--total.is-open > .dg__head { position: sticky; top: var(--nav-h); z-index: 5; }
/* Opening scrolls the master row to the top — UNDER the 57px top bar, not behind it.
   Scrolled to the very top, its natural place sat above the bar's edge, so the
   pinned row came down over the column headers. Found by drive_deal_board.py on
   the real page; the mockup had no top bar to collide with. */
.dg--total { scroll-margin-top: var(--nav-h); }
.dg--total.is-open .table-wrap-sticky { max-height: calc(100vh - var(--nav-h) - var(--dg-master-h, 88px));
  overscroll-behavior: contain; border: 0; }
.dg-root.is-master .dg-board { display: none; }
.dg-board .dg-subbody { display: none; }
/* A subheading is a BAND: the sunken stone surface, a 3px bar in the group's own
   colour down its left edge (the same bar the board's group heads carry), and the
   group's square. Cain, 2026-09-11: "slightly more colouring … to make those
   subheaders more obvious". Ink master → stone bands → white rows: three tiers. */
.dg-subhead th { position: sticky; top: var(--dg-thead-h, 37px); z-index: 1;
  background: var(--c-stone); box-shadow: inset 3px 0 0 var(--c-ink);
  border-top: 1px solid var(--c-rule); border-bottom: 1px solid var(--c-rule);
  text-transform: none; letter-spacing: 0; padding-top: 12px; padding-bottom: 12px; }
.dg-subhead th > * { vertical-align: middle; }
.dg-subhead__name { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.10em;
  color: var(--c-ink); margin-left: 8px; }
.dg-subhead__meta { font-size: 11px; color: var(--c-muted); margin-left: 12px; }
.dg-subbody--redeemed .dg-subhead th { box-shadow: inset 3px 0 0 var(--c-pale); }
.dg-subbody--redeemed .dg-subhead__name { font-weight: 500; }
.dg-subbody--unset .dg-subhead th { box-shadow: inset 3px 0 0 var(--c-oxblood); }
.dg-subbody--redeemed .dg__state { background: transparent; border-color: var(--c-muted); }
.dg-subbody--unset .dg__state { background: transparent; border-color: var(--c-oxblood); }
@media (max-width: 768px) {
  .dg-subbody { display: block; }
  .tbl--rows tr.dg-subhead { display: block; min-height: 0; padding: 10px 12px; background: var(--c-stone);
    box-shadow: inset 3px 0 0 var(--c-ink); }
  .tbl--rows tr.dg-subhead th { display: block; position: static; padding: 0; border: 0; background: transparent; }
  .dg-subbody--redeemed tr.dg-subhead { box-shadow: inset 3px 0 0 var(--c-pale); }
}


/* ── Sidebar favourites, 2026-09-11 — approved as docs/mockups/board-and-favourites.html ── */
/* A.2 FAVOURITES. The sidebar's own vocabulary: .sidebar-label for the heading,
   .sidebar-link for each page, and the same white-at-opacity tiers every other
   sidebar control uses. A favourite never takes .is-active — §15.4 allows ONE
   lit item, and that belongs to the section you are in (Lending), not a shortcut. */
.sidebar-fav { display: flex; align-items: stretch; }
.sidebar-fav__link { flex: 1; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.sidebar-fav__remove { flex-shrink: 0; background: none; border: 0; padding: 0 12px; font: inherit;
  font-size: 14px; line-height: 1; color: rgba(255,255,255,0.28); cursor: pointer; }
.sidebar-fav__remove:hover, .sidebar-fav__remove:focus-visible { color: rgba(255,255,255,0.95);
  background: rgba(255,255,255,0.06); outline: none; }
/* The add control and the "3 of 3" count share the heading row — no line of their own,
   so a page that is not pinned has the same sidebar height as one that is. */
.sidebar-fav-head { display: flex; align-items: baseline; justify-content: space-between; }
.sidebar-fav-head .sidebar-label { margin-right: 8px; }
.sidebar-fav-slot { margin-right: 12px; }
.sidebar-fav-add { background: none; border: 0; font: inherit; font-size: 11px; line-height: 1.4;
  color: rgba(255,255,255,0.45); padding: 0; cursor: pointer; white-space: nowrap; }
.sidebar-fav-add:hover, .sidebar-fav-add:focus-visible { color: rgba(255,255,255,0.95); outline: none;
  text-decoration: underline; }
.sidebar-fav-count { font-size: 11px; line-height: 1.4; color: rgba(255,255,255,0.28); white-space: nowrap; }
.sidebar-fav-note { font-size: 11px; line-height: 1.45; color: rgba(255,255,255,0.28); margin: 2px 12px 4px; }
@media (max-width: 768px) {
  .sidebar-fav__remove { min-width: 44px; min-height: 44px; }
  .sidebar-fav-head { align-items: center; }
  .sidebar-fav-add { min-height: 44px; }
}
/* A save that failed says so, in the sidebar's own tier — never oxblood on ink,
   which measures 1.6:1 (§14.6). */
.sidebar-fav-note--problem { color: rgba(255,255,255,0.75); }

/* The plan badge at the foot of the sidebar had no rule in any stylesheet, so it sat
   hard against the edge. It takes the sidebar's own 12px gutter; nothing else about
   it changes. (Found drawing the favourites mockup, 2026-09-11.) */
.sidebar-plan { padding: 12px; }
