/* ============================================================
   SHC application layer, on top of the UI kit in css/ui/.

   The kit supplies the responsive shell, tokens, components and
   utilities. This file supplies two things the kit does not:

   1. The badge and helper vocabulary the application already
      speaks (`b-ok`, `stat-row`, `card-pad`…). Renaming several
      hundred call sites across nine modules would be churn with
      real regression risk and no user-visible gain, so the names
      are mapped onto the kit's tokens instead.
   2. Screens that exist only here — the client portal, public
      verification, the map, the Copilot and the paginator.

   Everything below uses the kit's tokens, so a change of theme or
   brand still flows through in one place.
   ============================================================ */

/* ---------- Badge variants (kit uses badge-*, app uses b-*) ----------
   Aliased to the kit's own declarations rather than re-tinted, so a theme
   change reaches both spellings. */
.badge.b-ok      { background:var(--ok-bg);      color:var(--ok-tx); }
.badge.b-warn    { background:var(--warn-bg);    color:var(--warn-tx); }
.badge.b-crit    { background:var(--crit-bg);    color:var(--crit-tx); }
.badge.b-info    { background:var(--info-bg);    color:var(--info-tx); }
.badge.b-serious { background:var(--serious-bg); color:var(--serious-tx); }
.badge.b-neutral { background:var(--surface-sunken); color:var(--text-muted); border:1px solid var(--line-soft); }
.badge.b-gold    { background:var(--accent-100); color:var(--accent-600); }

.badge.b-ok .dot      { background:var(--ok); }
.badge.b-warn .dot    { background:var(--warn); }
.badge.b-crit .dot    { background:var(--crit); }
.badge.b-info .dot    { background:var(--info); }
.badge.b-serious .dot { background:var(--serious); }
.badge.b-neutral .dot { background:var(--text-subtle); }
.badge.b-gold .dot    { background:var(--accent-500); }

/* Legacy aliases used by charts and flags before the kit landed. */
:root {
  --shc-600: var(--brand-600);
  /* The tinted-panel trio. Only design.css defined these, and the app and
     guest layouts do not load it, so every `var(--shc-50)` panel outside the
     paginator rendered with no background and a currentColor border. Aliased
     to the themed brand ramp so they follow light/dark like everything else. */
  --shc-50:  var(--brand-50);
  --shc-100: var(--brand-100);
  --shc-700: var(--text-brand);
  --shc-800: var(--brand-950);
  --shc-900: var(--brand-950);
  --ink-900: var(--text);
  --ink-700: var(--text);
  --ink-500: var(--text-muted);
  --ink-400: var(--text-muted);
  --ink-300: var(--line-strong);
  --s1: var(--series-1);
  --s2: var(--series-2);
  --s3: var(--series-3);
}

/* ---------- Layout helpers the app uses ---------- */
.stat-row {
  display:grid; gap:var(--space-4, 14px);
  /* auto-fit, not a fixed four: the tiles are permission-gated, so a Records
     Officer sees two and a Branch Manager four. A fixed track count left the
     row half empty for every role that cannot see everything. */
  grid-template-columns:repeat(auto-fit, minmax(230px, 1fr));
}

/* Panels that should fill the row whatever the role can see. */
.panel-grid { display:grid; gap:var(--space-4, 16px); grid-template-columns:repeat(auto-fit, minmax(340px, 1fr)); }
.panel-grid > * { min-width:0; }
.card-pad { padding:var(--space-4, 15px) var(--space-5, 18px); }
.chart-wrap { padding:var(--space-4, 15px) var(--space-5, 18px); }
.split-70 { display:grid; grid-template-columns:minmax(0,7fr) minmax(0,3fr); gap:var(--space-4, 16px); }
.copilot-grid { display:grid; grid-template-columns:minmax(0,1fr) 320px; gap:var(--space-4, 16px); }

@media (max-width:900px) {
  .split-70, .copilot-grid { grid-template-columns:minmax(0,1fr); }
}

/* ---------- Copilot observations on a handset ----------
   The empty state fitted on a phone, but the working state did not: a 280px
   minimum copy block competed with Review, while tabs, status and judgement
   actions wrapped into unrelated fragments. Keep every observation readable
   in one rail and make each action an explicit touch row. */
.copilot-observation-copy,
.copilot-observation-detail,
.copilot-observation-citations,
.copilot-observation-citations > div { min-width:0; }

.copilot-observation-copy,
.copilot-observation-citations > div { overflow-wrap:anywhere; }

@media (max-width:640px) {
  .copilot-observation-page-actions,
  .copilot-observation-filters {
    display:grid !important;
    grid-template-columns:minmax(0, 1fr);
    gap:8px !important;
  }

  .copilot-observation-page-actions > .btn,
  .copilot-observation-tab,
  .copilot-observation-status {
    width:100%;
    max-width:none !important;
    min-height:44px;
    justify-content:center;
  }

  .copilot-observation-filters > .ui-select {
    width:100%;
    max-width:none;
  }

  .copilot-observation-filters > .ui-select .ui-select-control {
    min-height:44px;
    align-items:center;
  }

  .copilot-observation-filter-spacer { display:none; }

  .copilot-observation-summary {
    display:grid !important;
    grid-template-columns:minmax(0, 1fr);
  }

  .copilot-observation-copy { min-width:0 !important; }

  .copilot-observation-review {
    width:100%;
    min-height:44px;
    justify-content:center;
  }

  .copilot-observation-heading > b { flex:1 1 220px; }

  .copilot-observation-heading .badge {
    max-width:100%;
    white-space:normal;
  }

  .copilot-observation-decision-meta {
    display:flex;
    flex-direction:column;
    align-items:flex-start;
    gap:5px;
  }

  .copilot-observation-decision-badge {
    max-width:100%;
    height:auto;
    min-height:24px;
    white-space:normal;
    line-height:1.35;
  }

  .copilot-observation-decision-actions {
    align-items:stretch !important;
    flex-direction:column;
  }

  .copilot-observation-decision-actions > .btn {
    width:100%;
    min-height:44px;
    justify-content:center;
  }
}

/* ---------- Chrome bits ---------- */
.branch-pill {
  display:inline-flex; align-items:center; gap:6px; padding:5px 11px;
  border:1px solid var(--line); border-radius:var(--radius-pill, 99px);
  background:var(--surface); font-size:12px; font-weight:600; color:var(--text);
}
.branch-pill .dot { width:7px; height:7px; border-radius:50%; background:var(--ok); }

.icon-btn {
  position:relative; display:inline-flex; align-items:center; justify-content:center;
  width:34px; height:34px; border:1px solid var(--line); border-radius:var(--radius-md, 8px);
  background:var(--surface); color:var(--text-muted); cursor:pointer; text-decoration:none;
}
.icon-btn:hover { background:var(--surface-hover); color:var(--text); }
.icon-btn .bubble {
  position:absolute; top:-5px; inset-inline-end:-5px; min-width:16px; height:16px; padding:0 4px;
  border-radius:99px; background:var(--crit); color:#fff; font-size:10px; font-weight:700;
  display:flex; align-items:center; justify-content:center;
}

/* ---------- Key/value grid ---------- */
.kv-grid { display:grid; grid-template-columns:1fr 1fr; gap:10px 26px; }
.kv-grid.kv-1 { grid-template-columns:1fr; gap:8px; }
.kv-grid .k { font-size:10.5px; font-weight:600; color:var(--text-muted); letter-spacing:.05em; text-transform:uppercase; }
.kv-grid .v { font-size:13px; font-weight:600; color:var(--text); margin-top:2px; }
.kv-grid.kv-1 > div { display:flex; justify-content:space-between; align-items:baseline; gap:12px; }
.kv-grid.kv-1 .k { text-transform:none; font-size:12.5px; letter-spacing:0; }
@media (max-width:640px) { .kv-grid { grid-template-columns:minmax(0,1fr); } }

/* ---------- Pagination (one paginator; see defect D-9) ---------- */
.pager { display:flex; align-items:center; justify-content:space-between; gap:14px; flex-wrap:wrap; font-size:12.5px; padding:12px 16px; }
.pager-summary { color:var(--text-muted); }
.pager-summary b { color:var(--text); font-weight:600; }
.pager-links {
  display:flex; align-items:center; gap:4px; list-style:none; margin:0; padding:0;
  /* Wraps: a 20-page table renders 20 links, and on a 390px screen the row was
     430–507px wide — the paginator introduced for D-9 became the last source of
     horizontal overflow on two screens. */
  flex-wrap:wrap; justify-content:center;
}
.pager-link {
  display:inline-flex; align-items:center; justify-content:center;
  min-width:32px; height:32px; padding:0 8px;
  border:1px solid var(--line); border-radius:var(--radius-md, 8px);
  background:var(--surface); color:var(--text); text-decoration:none;
  font-size:12.5px; font-variant-numeric:tabular-nums;
}
.pager-link:hover { border-color:var(--brand-600); color:var(--brand-600); }
.pager-link.is-current { background:var(--brand-600); border-color:var(--brand-600); color:#fff; font-weight:600; }
.pager-link.is-disabled { color:var(--text-subtle, #9ca3af); background:var(--surface-sunken); cursor:default; }
.pager-link.is-gap { border:0; background:none; color:var(--text-muted); min-width:18px; }
.pager-link:focus-visible { outline:var(--focus-ring); outline-offset:2px; }
@media (max-width:640px) { .pager { justify-content:center; } .pager-summary { width:100%; text-align:center; } }

/* ---------- Contextual help prose ---------- */
.prose { max-width:68ch; font-size:13.5px; line-height:1.65; color:var(--text); }
.prose h2 { font-size:15px; margin:22px 0 8px; }
.prose h2:first-child { margin-top:0; }
.prose h3 { font-size:13.5px; margin:18px 0 6px; }
.prose p { margin:0 0 10px; }
/* The kit resets `list-style:none` globally, which is right for nav and
   wrong for prose — a help topic numbers its controls to match the numbered
   badges on the figure beside it, so the markers have to come back. */
.prose ul, .prose ol { margin:0 0 12px; padding-inline-start:22px; }
.prose ul { list-style:disc; }
.prose ol { list-style:decimal; }
.prose ul ul { list-style:circle; }
.prose li { margin-bottom:5px; }
.prose li::marker { color:var(--text-subtle); }
.prose code { font:12px var(--font-mono); background:var(--surface-sunken); padding:1px 5px; border-radius:4px; }
.prose table { border-collapse:collapse; margin:0 0 14px; width:100%; font-size:12.5px; }
.prose th, .prose td { border:1px solid var(--line); padding:6px 9px; text-align:start; vertical-align:top; }
.prose th { background:var(--surface-sunken); font-weight:600; }
.prose blockquote { margin:0 0 12px; padding:8px 12px; border-inline-start:3px solid var(--accent-500, #c9a227); background:var(--surface-sunken); }
/* --brand-600 is a fixed navy in both themes; --text-brand is the
   theme-aware one, so a prose link stays readable in dark mode. */
.prose a { color:var(--text-brand); }

/* ---------- Client portal & public verification ---------- */
.portal-wrap { max-width:var(--container-max, 1100px); margin:0 auto; padding:0 var(--space-4, 16px) 40px; }
.portal-bar { border-bottom:1px solid var(--line); background:var(--surface); }
.portal-bar .portal-wrap { display:flex; align-items:center; gap:14px; padding-block:12px; flex-wrap:wrap; }
.portal-nav { display:flex; gap:4px; flex-wrap:wrap; }
.portal-nav a {
  padding:7px 12px; border-radius:var(--radius-md, 8px); font-size:13px; font-weight:600;
  color:var(--text-muted); text-decoration:none;
}
.portal-nav a:hover { background:var(--surface-hover); color:var(--text); }
.portal-nav a[aria-current="page"] { background:var(--brand-50); color:var(--brand-600); }
.portal-foot { border-top:1px solid var(--line); padding:18px 0; color:var(--text-muted); font-size:11.5px; }

.pub { min-height:100vh; display:flex; flex-direction:column; background:var(--page); }
.pub-head { border-bottom:1px solid var(--line); background:var(--surface); padding:16px 0; }
.pub-body { flex:1; width:100%; max-width:620px; margin:0 auto; padding:28px 16px; }
.pub-foot { border-top:1px solid var(--line); padding:16px 0; text-align:center; color:var(--text-muted); font-size:11.5px; }

.verify-shield { display:flex; align-items:center; justify-content:center; border-radius:50%; margin:0 auto; }
.shield-ok { background:var(--ok); }
.shield-bad { background:var(--crit); }

/* ---------- Map ---------- */
.map { width:100%; height:auto; background:var(--surface-sunken); border-radius:var(--radius-md, 8px); }
.map-plot { cursor:pointer; transition:opacity var(--duration-fast, .12s) var(--ease-out); }
.map-plot:hover { opacity:.75; }
.map-label { font-size:7px; fill:var(--text-muted); pointer-events:none; }

/* ---------- Tables that must not push the page sideways ---------- */
.table-scroll { overflow-x:auto; -webkit-overflow-scrolling:touch; }
@media (max-width:760px) {
  table.data { display:block; overflow-x:auto; white-space:nowrap; }
  .page-head { flex-direction:column; align-items:flex-start; gap:10px; }
  .page-head .actions { width:100%; flex-wrap:wrap; }
}

/* ---------- Topbar on a phone ----------
   The kit's topbar only reduces its padding below 900px, which is right for a
   tablet and wrong for a 390px handset: the search box holds its width and the
   action cluster pushes the page 155px sideways. Everything here earns its
   place at that width or gets out of the way — the branch is still named in
   the sidebar footer, so the pill is the first to go. */
@media (max-width:640px) {
  .topbar { gap:8px; padding-inline:var(--space-3, 10px); }
  .topbar .searchbox { flex:1 1 auto; min-width:0; }
  .topbar .searchbox kbd { display:none; }
  .topbar-actions { gap:6px; }
  .topbar-actions .branch-pill { display:none; }
}
@media (max-width:400px) {
  /* Below this, the search box becomes an icon-sized affordance so the
     sign-out control stays reachable rather than pushed off-screen. */
  .topbar .searchbox input { width:0; min-width:0; padding:0; }
}

/* Desktop tables must remain actual tables: `display:block` leaves the outer
   element full width but lets its header and rows collapse to content width.
   Narrow screens retain the responsive scroll rule above; unusually wide
   desktop reports opt into `.table-scroll` around their table. */
@media (min-width:761px) {
  table.data {
    display:table; width:100%; max-width:100%;
  }
}
table.data:focus-visible { outline:var(--focus-ring); outline-offset:-2px; }

@media (max-width:760px) {
  /* A card header with a title, a hint and a "more" link runs out of room
     long before the table does. */
  .card-head { flex-wrap:wrap; row-gap:4px; }
  .card-head .more { margin-inline-start:0; }
}

/* A two-up list that can actually reach one column.
   `1fr` is `minmax(auto, 1fr)`, so a track never shrinks below its content —
   a 97px status badge beside its label kept the SIGA section list 18px wider
   than a 390px handset. `minmax(0, 1fr)` lets it shrink; below 640 it stacks. */
.auto-2 { display:grid; grid-template-columns:repeat(2, minmax(0, 1fr)); gap:8px 22px; }
.auto-2 > * { min-width:0; }
@media (max-width:640px) { .auto-2 { grid-template-columns:minmax(0, 1fr); } }

/* ---------- `.err` collision ----------
   The kit uses `.err` for the error MESSAGE inside `.field` and keeps it
   `display:none` until there is one. The application uses `err` as a modifier
   ON the control itself (`class="input @error('x') err @enderror"`), so every
   input that failed validation was set to display:none — the field vanished
   instead of turning red, and the user could not correct the thing they had
   just got wrong. Restore the control, and colour it. */
.field input.err,
.field select.err,
.field textarea.err,
.field .input.err {
  display:revert;
  border-color:var(--crit);
}
.field input.err:focus-visible,
.field select.err:focus-visible,
.field textarea.err:focus-visible { outline:var(--focus-ring-danger, var(--focus-ring)); }

/* The other half of the same collision: the application also renders the
   MESSAGE as `<div class="err">`, and only renders it when there is an error
   (`@error(...)`). The kit hides that element until its own JS reveals it, so
   69 validation messages across 15 screens were rendered and invisible — the
   form simply refused to submit with no stated reason. Presence is the signal
   here, so show it. */
.field > .err,
.field .err:not(input):not(select):not(textarea) {
  display:block;
  font-size:var(--text-xs, 11.5px);
  color:var(--crit-tx, #a02525);
  font-weight:600;
  margin-top:4px;
}

/* QC review: scan on the left, extracted metadata on the right (FR-M3-5).
   `1.2fr 1fr` cannot compress below its content, so the pair stayed 38px wider
   than a handset; on a phone the two panels stack instead. */
.qc-split { display:grid; grid-template-columns:minmax(0, 1.2fr) minmax(0, 1fr); gap:16px; align-items:start; }
.qc-split > * { min-width:0; }
@media (max-width:900px) { .qc-split { grid-template-columns:minmax(0, 1fr); } }

/* ---------- Document viewer ----------
   On a phone the previous inline three-column grid still allocated space to
   thumbnails and the integrity rail. The preview could collapse to 29px wide
   without horizontal overflow, so it evaded the old responsive check. Keep
   the document first, turn thumbnails into a horizontal strip, then show the
   integrity/linking rail below it. */
@media (max-width:760px) {
  .document-viewer-layout { grid-template-columns:minmax(0, 1fr) !important; gap:12px !important; }
  .document-viewer-layout > * { min-width:0; }
  .document-preview { order:1; min-height:380px !important; }
  .document-preview iframe { height:min(72vh, 520px) !important; }
  .document-thumbnails { order:2; flex-direction:row !important; max-height:none !important; overflow-x:auto !important; overflow-y:hidden !important; padding-bottom:2px; }
  .document-thumbnails > div { flex:0 0 84px; }
  .document-detail-rail { order:3; }
}

/* ---------- Record detail pages ----------
   Estate, parcel, party and case pages used a fixed 2fr/1fr inline grid.
   At handset width it kept the right-hand rail beside the record, leaving the
   actual record several hundred pixels off-screen. These pages are read in
   order, so stack their contextual rail after the primary record. */
@media (max-width:760px) {
  .record-detail-layout { grid-template-columns:minmax(0, 1fr) !important; gap:12px !important; }
  .record-detail-layout > * { min-width:0; }
  .allocation-detail-layout { grid-template-columns:minmax(0, 1fr) !important; gap:12px !important; }
  .allocation-detail-layout > * { min-width:0; }
  .record-create-plot { grid-template-columns:minmax(0, 1fr) !important; }
  .interest-clearance-summary { flex-wrap:wrap; }
  .interest-charge-fields,
  .interest-charge-actions { grid-template-columns:minmax(0, 1fr) !important; }
  .interest-inline-action { flex-direction:column; align-items:stretch !important; }
  .interest-inline-action > * { min-width:0; width:100%; }
}


/* ---------- Modal backdrop ----------
   The kit's `.modal-root` positions and centres the dialog but paints no
   scrim of its own — its demo shell supplies one. Ours renders the modal
   straight into the page, so the content behind it stayed fully lit and the
   dialog read as a floating card rather than a modal. Reported by A3 against
   the shared component, so one rule fixes it for every agent's modal. */
.modal-root {
  background:var(--surface-overlay, rgba(15, 23, 42, .45));
  -webkit-backdrop-filter:blur(1.5px);
  backdrop-filter:blur(1.5px);
}
@media (prefers-reduced-motion: reduce) {
  .modal-root { -webkit-backdrop-filter:none; backdrop-filter:none; }
  .modal { transition:none; }
}

/* ---------- Filter rows ----------
   A <select> is sized by its longest option and will not shrink below that
   intrinsic width, so a row of filters overflows a narrow viewport however
   the flex container is configured — long audit action names ("prerequisite
   _satisfied") pushed /audit-log 73px past a 390px screen. `min-width:0`
   lets them compress; below 640 each filter takes the full width instead of
   fighting for a share of it. */
.filters { display:flex; gap:8px; flex-wrap:wrap; margin-bottom:14px; }
.filters > * { min-width:0; }
.input, select.input, .filters .input { min-width:0; }

/* A page action must not touch the filter/search block that follows it. The
   estate register exposed the zero-gap case, but page headers are shared by
   every module, so the spacing belongs to the shared application layer. */
.page-head { margin-bottom:14px; }

@media (max-width:640px) {
  .filters { display:grid; grid-template-columns:minmax(0,1fr); gap:8px; }
  .filters > .input,
  .filters > .btn { max-width:none !important; width:100%; }
}

/* ---------- Readable records on handsets ----------
   Sideways scrolling keeps a wide table inside its card, but it also leaves
   important status and action columns invisible until the user discovers the
   scroll gesture. ui.utils.js labels simple table rows from their real column
   headings; this presentation exposes the complete record as a compact card.
   Tables with complex multi-row headings deliberately retain native scroll. */
@media (max-width:760px) {
  .page-head .actions > .btn {
    flex:1 1 180px;
    min-height:38px;
    justify-content:center;
  }

  table.mobile-card-table {
    display:block;
    width:100%;
    overflow:visible;
    white-space:normal;
  }

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

  .mobile-card-table tbody { display:block; }

  .mobile-card-table tbody tr {
    display:grid;
    grid-template-columns:repeat(2, minmax(0, 1fr));
    column-gap:14px;
    padding:14px;
    border-bottom:1px solid var(--line-soft);
  }

  .mobile-card-table tbody tr:last-child { border-bottom:0; }

  .mobile-card-table tbody td {
    display:block;
    min-width:0;
    padding:7px 0;
    border:0;
    text-align:start;
    overflow-wrap:anywhere;
  }

  .mobile-card-table tbody td.num { text-align:start; }

  .mobile-card-table tbody td::before {
    content:attr(data-mobile-label);
    display:block;
    margin-bottom:3px;
    color:var(--text-muted);
    font-size:10px;
    font-weight:700;
    letter-spacing:.06em;
    text-transform:uppercase;
  }

  .mobile-card-table tbody td[data-mobile-span="full"],
  .mobile-card-table tbody td[colspan] { grid-column:1 / -1; }

  .mobile-card-table tbody td[data-mobile-span="full"]:first-child { padding-top:0; }
  .mobile-card-table tbody td[data-mobile-span="full"]:first-child::before,
  .mobile-card-table tbody td[data-mobile-label="Actions"]::before { display:none; }
  .mobile-card-table tbody td[data-mobile-label="Actions"] { padding-bottom:0; }
  .mobile-card-table tbody td.data-mobile-empty { display:none; }

  .mobile-card-table tbody td[data-mobile-label="Actions"] > .more,
  .mobile-card-table tbody td[data-mobile-label="Actions"] > .btn,
  .mobile-card-table tbody td[data-mobile-label="Actions"] > a {
    display:flex;
    width:100%;
    min-height:38px;
    align-items:center;
    justify-content:center;
    border:1px solid var(--line);
    border-radius:var(--radius-sm, 8px);
    background:var(--surface-sunken);
  }

  .mobile-card-table tbody td[data-mobile-label="Actions"] a,
  .mobile-card-table tbody td[data-mobile-label="Actions"] button {
    display:inline-flex;
    min-height:38px;
    align-items:center;
    justify-content:center;
    padding-inline:10px;
  }

  .search-results-grid,
  .mobile-split-grid { grid-template-columns:minmax(0, 1fr) !important; }

  .billing-cycle-fields,
  .payment-plan-fields { grid-template-columns:repeat(2, minmax(0, 1fr)) !important; }

  .report-summary-grid {
    grid-template-columns:repeat(auto-fit, minmax(min(100%, 180px), 1fr)) !important;
  }
}

@media (max-width:520px) {
  .billing-cycle-fields,
  .payment-plan-fields,
  .mobile-form-grid,
  .report-summary-grid { grid-template-columns:minmax(0, 1fr) !important; }

  .mobile-form-grid > [style*="grid-column"] { grid-column:1 !important; }
}

/* ---------- Party register on a handset ----------
   Party identity values are long by design. Keep the search affordances and
   every identity field at a readable single-column width instead of asking a
   user to parse two compressed metadata tracks. */
@media (max-width:640px) {
  .party-registry-filters {
    display:grid;
    grid-template-columns:minmax(0, 1fr);
    gap:8px;
  }

  .party-registry-filters > .input,
  .party-registry-filters > .ui-select {
    width:100%;
    max-width:none !important;
  }
}

@media (max-width:430px) {
  .party-registry-table tbody tr { grid-template-columns:minmax(0, 1fr); }
  .party-registry-table tbody td { grid-column:1 !important; }
}

/* ---------- Estate map controls on touch screens ----------
   The SVG already scales without distorting its declared geometry. The UI
   around it previously wrapped by label length, leaving three tiny selects
   beside arbitrarily wide layer chips. Use a predictable control grid so the
   map remains filterable with one hand at every supported handset width. */
@media (max-width:760px) {
  .map-page-actions {
    display:grid !important;
    grid-template-columns:repeat(2, minmax(0, 1fr));
    gap:8px;
  }

  .map-page-actions > .btn {
    width:100%;
    min-height:44px !important;
    justify-content:center;
  }

  .map-page-actions > .btn:last-child:nth-child(odd) { grid-column:1 / -1; }

  .map-filter-grid {
    display:grid;
    grid-template-columns:repeat(2, minmax(0, 1fr));
    gap:8px;
    align-items:stretch;
  }

  .map-filter-grid > .ui-select,
  .map-filter-grid > .chip,
  .map-filter-grid > .btn {
    width:100%;
    max-width:none !important;
    min-height:44px !important;
    justify-content:center;
  }

  .map-filter-grid > .ui-select .ui-select-control { min-height:44px; }
  .map-status-legend { display:grid; grid-template-columns:repeat(3, minmax(0, 1fr)); }
  .map-status-legend .li { min-width:0; align-items:flex-start; }
  .estate-map-canvas { display:block; max-width:100%; touch-action:manipulation; }
  .map-import-result { flex-wrap:wrap; }
  .map-import-result > .btn { width:100%; justify-content:center; }
}

@media (max-width:520px) {
  .map-page-actions,
  .map-filter-grid { grid-template-columns:minmax(0, 1fr); }
  .map-page-actions > .btn:last-child:nth-child(odd) { grid-column:1; }
  .map-status-legend { grid-template-columns:repeat(2, minmax(0, 1fr)); }
  .map-selected-actions { flex-direction:column; }
  .map-selected-actions > .btn { width:100%; justify-content:center; }
}

/* ---------- Counter verification on a handset ----------
   The recent-failure strip used a wrapping flex row with a 240px minimum text
   block. It technically fit at 390px, but left its fraud status as a short,
   detached fragment on the next row. Preserve the icon/copy relationship and
   give status/actions a deliberate full-width row. */
.counter-recent-failure { margin-top:14px; }

@media (max-width:640px) {
  .counter-verification-head .actions {
    display:grid;
    grid-template-columns:minmax(0, 1fr);
  }

  .counter-verification-head .actions > .btn,
  .counter-verification-head .actions > .badge {
    width:100%;
    min-height:40px;
    justify-content:center;
  }

  .counter-reference-row,
  .counter-identity-row,
  .counter-result-actions { flex-direction:column; }

  .counter-reference-row > .btn,
  .counter-identity-row > .btn,
  .counter-result-actions > .btn {
    width:100%;
    min-height:44px;
    margin-top:0 !important;
    justify-content:center;
  }

  .counter-identity-row > .field { width:100%; }

  .counter-recent-failure-layout {
    display:grid !important;
    grid-template-columns:44px minmax(0, 1fr);
    gap:12px !important;
    align-items:start !important;
  }

  .counter-recent-failure-copy {
    min-width:0 !important;
    overflow-wrap:anywhere;
  }

  .counter-recent-failure-status,
  .counter-recent-failure-action {
    grid-column:1 / -1;
    width:100%;
    min-height:40px;
    justify-content:center;
  }
}

@media (max-width:360px) {
  .counter-recent-failure-layout { grid-template-columns:minmax(0, 1fr); }
  .counter-recent-failure-icon { margin-inline:auto; }
  .counter-recent-failure-copy { text-align:center; }
  .counter-recent-failure-status,
  .counter-recent-failure-action { grid-column:1; }
}

/* ---------- Estate register on a handset ----------
   Scrolling a seven-column estate table technically avoids page overflow, but
   it hides the land status and the only Open action outside the viewport.
   An estate is a compact, read-first record, so below 640px its row becomes a
   labelled card instead. The table semantics remain for assistive technology. */
.estate-registry-head { margin-bottom:14px; }

@media (max-width:760px) {
  .estate-registry-head .actions .btn {
    width:100%;
    min-height:38px;
    justify-content:center;
  }

  .estate-registry-filters {
    display:grid;
    grid-template-columns:repeat(2, minmax(0, 1fr));
    gap:8px;
  }

  .estate-registry-filters > input:first-child,
  .estate-registry-filters > .chip { grid-column:1 / -1; }

  .estate-registry-filters > .input,
  .estate-registry-filters > .ui-select,
  .estate-registry-filters > .chip {
    width:100%;
    max-width:none !important;
  }

  .estate-registry-filters > .chip { justify-content:center; min-height:36px; }

  table.estate-registry-table {
    display:block;
    overflow:visible;
    white-space:normal;
  }

  .estate-registry-table thead {
    position:absolute;
    width:1px;
    height:1px;
    margin:-1px;
    padding:0;
    overflow:hidden;
    clip:rect(0 0 0 0);
    white-space:nowrap;
    border:0;
  }

  .estate-registry-table tbody {
    display:block;
  }

  .estate-registry-table tbody tr {
    display:grid;
    grid-template-columns:repeat(2, minmax(0, 1fr));
    column-gap:14px;
    padding:14px;
    border-bottom:1px solid var(--line-soft);
  }

  .estate-registry-table tbody tr:last-child { border-bottom:0; }

  .estate-registry-table tbody td {
    display:block;
    min-width:0;
    padding:8px 0;
    border:0;
    text-align:start;
  }

  .estate-registry-table tbody td.num { text-align:start; }

  .estate-registry-table tbody td::before {
    content:attr(data-label);
    display:block;
    margin-bottom:3px;
    color:var(--text-muted);
    font-size:10px;
    font-weight:700;
    letter-spacing:.06em;
    text-transform:uppercase;
  }

  .estate-registry-table tbody td:first-child,
  .estate-registry-table tbody td:nth-child(2),
  .estate-registry-table tbody td:nth-child(5),
  .estate-registry-table tbody td:nth-child(6),
  .estate-registry-table tbody td:nth-child(7) { grid-column:1 / -1; }

  .estate-registry-table tbody td:first-child { padding-top:0; }
  .estate-registry-table tbody td:first-child::before,
  .estate-registry-table tbody td:last-child::before { display:none; }
  .estate-registry-table tbody td:last-child { padding-bottom:0; }
  .estate-registry-table tbody td:last-child .more {
    display:flex;
    width:100%;
    min-height:38px;
    align-items:center;
    justify-content:center;
    border:1px solid var(--line);
    border-radius:var(--radius-sm, 8px);
    background:var(--surface-sunken);
  }

  .estate-create-dialog { max-width:calc(100vw - 24px) !important; padding:20px 16px !important; }
  .estate-create-form { grid-template-columns:minmax(0, 1fr) !important; }
  .estate-create-form > [style*="grid-column"] { grid-column:1 !important; }
  .estate-create-actions { flex-direction:column-reverse; }
  .estate-create-actions > .btn { width:100%; }
}

@media (max-width:520px) {
  .estate-registry-filters { grid-template-columns:minmax(0, 1fr); }
  .estate-registry-filters > input:first-child,
  .estate-registry-filters > .chip { grid-column:1; }
}

/* ---------- Contrast corrections (AX-1) ----------
   Measured by axe-core over 67 screens. These override the UI kit's own
   tokens; the kit is the user's design system, so the divergence is recorded
   here rather than hidden — the replacements are colours the kit already
   ships in its ramp, so the palette keeps its character.

   Fixed in tokens.css: --text-muted 4.46:1 → 7.56:1, --text-subtle 2.53:1 →
   5.38:1. The three below cannot be fixed by a token swap alone. */

/* The gold accent on its own tint carries real labels. The first correction
   reached 4.26:1; this shade clears the 4.5:1 AA threshold with headroom. */
.badge.b-gold { color:#72580f; }

:root[data-theme="dark"] .badge.b-gold {
  background:rgba(201,162,39,.16);
  color:var(--accent-300);
}

/* Dark mode, two visible bugs A7 caught that light mode hides entirely. */
:root[data-theme="dark"] .avatar,
:root:not([data-theme="light"]) .avatar {
  /* brand ink on a dark panel measured 1.28:1 — the initials were unreadable */
  color:var(--text);
  background:var(--brand-600);
}
:root[data-theme="dark"] mark,
:root:not([data-theme="light"]) mark {
  /* the search highlight kept its light background under dark text: 1.03:1 */
  background:var(--accent-700);
  color:#fff;
}

/* A link inside a paragraph must not rely on colour alone (WCAG 1.4.1). The
   kit removes underlines globally, which is right for nav and wrong here. */
.prose a,
.cell-sub a,
.muted a,
p a { text-decoration:underline; text-underline-offset:2px; }

/* A handful of pre-kit screens still carry the old status palette inline.
   In dark mode those light-theme literals fall below AA; map only those exact
   legacy values onto the semantic dark tokens until the screens are migrated. */
:root[data-theme="dark"] [style*="color:#a12c2c"],
:root[data-theme="dark"] [style*="color:#a02525"] { color:var(--crit-tx) !important; }
:root[data-theme="dark"] [style*="background:#f7fcf8"],
:root[data-theme="dark"] [style*="background:#f4fbf5"] { background:var(--ok-bg) !important; }
:root[data-theme="dark"] [style*="background:#fdf7f7"],
:root[data-theme="dark"] [style*="background:#fdf1f1"] { background:var(--crit-bg) !important; }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .badge.b-gold {
    background:rgba(201,162,39,.16);
    color:var(--accent-300);
  }
  :root:not([data-theme]) [style*="color:#a12c2c"],
  :root:not([data-theme]) [style*="color:#a02525"] { color:var(--crit-tx) !important; }
  :root:not([data-theme]) [style*="background:#f7fcf8"],
  :root:not([data-theme]) [style*="background:#f4fbf5"] { background:var(--ok-bg) !important; }
  :root:not([data-theme]) [style*="background:#fdf7f7"],
  :root:not([data-theme]) [style*="background:#fdf1f1"] { background:var(--crit-bg) !important; }
}
