/* Leasii base styles. Colours, type, spacing and radii come from tokens.css. Raw px in type, spacing and radii are
   allowed only where CssTokenTests lists a reason; the few literal colours (QR code, sign-in panel, print) say why. */

/* Stylesheet map (R13 2h, R14). One component, one defining block: change a component where it is defined, do not
   override it further down. Search for the banner to find a section.
     Base ................ type scale, links, focus, utilities (top of the file)
     Shell ............... app shell, sidebar, tab bar, tablet rail, top bar, content padding
     Components .......... .card, buttons, badges, alerts, stat tiles, empty states, forms, filter bar, tables, tabs, dl
     Round 14 components . .hero, .property-hero, .facts, .tile, .row, .is-new, .chip, .steps, .toast, .skeleton, .banner-actionable
     Navigation .......... .tabs and .tabs-more, breadcrumbs, row menus
     Page header ......... .page-header and its aside (search "==== Page header ====")
     Pages ............... "==== Round N: ..." sections, each for one area (owner, tenant, trade, staff): only layout and
                           exceptions that belong to that page, never a new look for a shared component
   New shared components go in a "Components" section above the pages, with a usage comment; a page section may place a
   component but not restyle it. Stylelint (no-duplicate-selectors) and CssTokenTests keep the rule honest. */

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--surface);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--text-base);
  line-height: var(--leading-base);
}

/* Figures line up in columns. Only on numbers: Inter's tabular hyphen would put gaps in prose ("Self - managed"). */
.num, .table td.num, .stat-value, .ledger-amount, .staff-queue-count, .nav-count, .bell-count, input[type="number"], .input-prefix .input, time, .tabular { font-variant-numeric: tabular-nums; }

h1, h2, h3, p { margin: 0; }
/* R14 (2): 32/38 on phones and tablets (up to 959px), 40/44 from 960px (below). The page title is the loudest thing on the page. */
h1 { font-family: var(--font-display); font-weight: 700; font-size: var(--text-display-sm); line-height: var(--leading-display-sm); letter-spacing: var(--display-tracking); }
h2 { font-family: var(--font-display); font-weight: 600; font-size: var(--text-xl); line-height: var(--leading-xl); }
h3 { font-size: var(--text-lg); line-height: var(--leading-lg); font-weight: 600; }

a { color: var(--brand); }
a:hover { color: var(--brand-strong); }

:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }

/* B8: the hidden attribute always hides, even on elements whose class sets a display (.btn, .form-grid, …). */
[hidden] { display: none !important; }
.skip-link { position: absolute; left: -9999px; top: var(--space-2); }
.skip-link:focus { left: var(--space-2); z-index: var(--z-overlay); padding: var(--space-2) var(--space-4); background: var(--surface-raised); border-radius: var(--radius-md); }

.muted { color: var(--ink-muted); }
.caption { font-size: var(--text-md); line-height: var(--leading-md); color: var(--ink-muted); }
/* R14 (2): timestamps, references and counts only; every other secondary line is a .caption (14/20). */
.meta { font-size: var(--text-sm); line-height: var(--leading-sm); color: var(--ink-muted); }
.label { font-size: var(--text-md); line-height: var(--leading-md); font-weight: 600; }
.stack { display: flex; flex-direction: column; gap: var(--space-4); }
.stack-lg { display: flex; flex-direction: column; gap: var(--space-6); }
.stack-2xs { display: flex; flex-direction: column; gap: var(--space-0-5); }
.stack-xs { display: flex; flex-direction: column; gap: var(--space-1); }
.stack-sm { display: flex; flex-direction: column; gap: var(--space-2); }

/* App shell: mobile first. Top bar and bottom tab bar on phones; sidebar from 960px. */

.app { min-height: 100vh; display: flex; flex-direction: column; }

.topbar {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-2);
  padding: var(--space-4) var(--space-4) var(--space-2);
}

.sidebar { display: none; }

.content { flex: 1 1 auto; min-width: 0; padding: var(--space-2) var(--space-4) var(--space-8); }
.content-inner { width: 100%; }

.tabbar {
  position: sticky; bottom: 0;
  /* However many tabs the person has (2 for a trade, 3 for an applicant, up to 5), they share the width evenly. */
  display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr);
  /* Room for the home indicator only on phones that have one (PO-39; needs viewport-fit=cover in _Head). */
  padding: var(--space-2) var(--space-1) calc(var(--space-2) + env(safe-area-inset-bottom));
  background: var(--surface-raised); border-top: 1px solid var(--line);
}

.tab {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--space-0-5);
  min-height: 48px; color: var(--ink-muted); text-decoration: none; font-size: var(--text-sm); line-height: 16px;
  font-weight: 600; position: relative; padding-inline: var(--space-0-5); text-align: center; overflow-wrap: break-word;
  hyphens: auto;
}
.tab[aria-current="page"], .tab.tab-current { color: var(--brand); }
.tab[aria-disabled="true"] { opacity: .55; }
button.tab { border: 0; background: none; font-family: inherit; cursor: pointer; }
.more-menu { max-height: calc(100vh - 2 * var(--space-8)); overflow-y: auto; }
/* Phones: the More and account menus open as bottom sheets, next to the tab bar that opened them. */
@media (max-width: 599px) {
  .modal.more-menu { width: 100%; max-width: 100%; margin: auto 0 0; border-radius: var(--radius-lg) var(--radius-lg) 0 0; border-bottom: 0; padding-bottom: calc(var(--space-6) + env(safe-area-inset-bottom)); }
}

.nav { display: flex; flex-direction: column; gap: var(--space-1); }
.nav-item {
  display: flex; align-items: center; gap: var(--space-3);
  min-height: 44px; padding: 0 var(--space-3); border-radius: var(--radius-md);
  color: var(--ink); text-decoration: none; font-size: var(--text-md); line-height: var(--leading-md); font-weight: 600;
}
.nav-item:hover { background: var(--surface); color: var(--ink); }
.nav-item[aria-current="page"] { background: var(--brand-soft); color: var(--brand-strong); }
.nav-item[aria-disabled="true"] { color: var(--ink-muted); }
.nav-item .soon { margin-left: auto; font-size: var(--text-sm); font-weight: 400; }

@media (min-width: 960px) {
  /* The whole app, sidebar included, is at most --page-max (1440px) wide and centred; the content fills the rest. */
  .app { flex-direction: row; width: 100%; max-width: var(--page-max); margin-inline: auto; }
  /* Staff tools and pages marked wide use more of a large screen (up to --page-max-wide, sidebar included). */
  .app.app-wide { max-width: var(--page-max-wide); }
  .topbar, .tabbar { display: none; }
  .sidebar {
    display: flex; flex-direction: column; gap: var(--space-4);
    position: sticky; top: 0; height: 100vh; flex: 0 0 248px;
    /* A long menu (staff see every Admin section) scrolls inside the panel instead of spilling past it. */
    overflow-y: auto; overscroll-behavior: contain;
    padding: var(--space-6) var(--space-4);
    background: var(--surface-raised); border-right: 1px solid var(--line);
  }
  .sidebar .brand { display: block; padding: var(--space-2) 0 var(--space-4); }
  /* Owner's decision (15 Nov 2026): on desktop the logo fills the sidebar's full width (it keeps its proportions, so about
     78px tall in the 248px sidebar). Every bar keeps the 28px mark; 60px stays only on the sign-in panel. */
  .sidebar .brand img { display: block; width: 100%; height: auto; }
  .content { padding: var(--space-8) var(--space-10) var(--space-12); }
  .card p { max-width: 46em; }
  h1 { font-size: var(--text-3xl); line-height: var(--leading-3xl); }
}

/* R14 (3.15): tablets (600-959px, the rail from 700px) get tablet spacing around the page, not the phone's: 24px sides. */
@media (min-width: 600px) and (max-width: 959px) {
  .content { padding: var(--space-6) var(--space-6) var(--space-10); }
}

/* Components */

.card {
  padding: var(--space-4);
  background: var(--surface-raised); border: 1px solid var(--line); border-radius: var(--radius-lg);
}
/* R14 (3.8): tablets use the desktop's 20px card padding. */
@media (min-width: 600px) { .card { padding: var(--space-5); } }

/* Button weight: one primary per page, for the single next step on that page (record the payment, book the job).
   Secondary for the alternatives. Text buttons (.btn-text) for low-risk extras. Danger (.btn-danger) only inside a
   confirmation, never as the button that opens it: a destructive action sits behind a quiet button and the confirm
   dialog colours its own confirm button (data-confirm-danger). A form launcher is a <details class="fold-action">,
   with .fold-primary when it's the page's next step. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
  min-height: 44px; padding: 0 var(--space-4); border-radius: var(--radius-md);
  font: var(--weight-semibold) var(--text-md)/var(--leading-md) var(--font-sans); text-decoration: none; cursor: pointer;
}
.btn-primary { border: 0; background: var(--brand); color: var(--on-brand); }
.btn-primary:hover { background: var(--brand-strong); color: var(--on-brand); }
.btn-secondary { border: 1px solid var(--line-strong); background: var(--surface-raised); color: var(--ink); }
.btn-secondary:hover { border-color: var(--brand); color: var(--ink); }
/* Danger: the confirm button for things that can't be undone (serve a notice, reverse a payment, delete). */
.btn-danger { border: 0; background: var(--critical); color: var(--surface-raised); }
.btn-danger:hover { background: color-mix(in srgb, var(--critical) 85%, var(--ink)); color: var(--surface-raised); }
.btn:disabled, .btn[aria-disabled="true"] { opacity: .6; cursor: not-allowed; }
.btn-lg { min-height: 52px; padding: 0 var(--space-6); font-size: var(--text-base); }
.btn-critical-outline { border: 1px solid var(--critical); background: var(--surface-raised); color: var(--critical); }
.btn-critical-outline:hover { background: var(--critical-soft); color: var(--critical); }
.btn-key { border: 0; background: var(--key); color: var(--on-key); }
.btn-key:hover { background: color-mix(in srgb, var(--key) 88%, var(--on-key)); color: var(--on-key); }

.icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; border: 1px solid var(--line); border-radius: var(--radius-md);
  background: var(--surface-raised); color: var(--ink);
}

/* nowrap: a badge is a short label; it never breaks over two lines or stretches to the height of a button beside it. */
.badge { display: inline-block; padding: var(--space-0-5) var(--space-2); border-radius: var(--radius-sm); font-size: var(--text-sm); line-height: var(--leading-sm); font-weight: 600; white-space: nowrap; }
.badge-key { background: var(--key); color: var(--on-key); }
.badge-soft { background: var(--brand-soft); color: var(--brand-strong); }
/* Status tones (StatusBadge tag helper): good = done, warn = needs someone, critical = urgent or failed,
   info = in progress, muted = closed or not applicable. badge-key is for promotions only, never status. */
.badge-good { background: var(--good-soft); color: var(--good); }
.badge-warn { background: var(--warning-soft); color: var(--warning); }
.badge-critical { background: var(--critical-soft); color: var(--critical); }
.badge-info { background: var(--info-soft); color: var(--info); }
.badge-muted { background: var(--neutral-soft); color: var(--ink-muted); }
:is(.stack, .stack-sm, .stack-xs, .stack-2xs) > .badge { align-self: flex-start; }
/* A button in a stack keeps its natural width: a full-width secondary button reads as the page's main action.
   Use .btn-block for a deliberate full-width button, or .btn-block-phone for thumb-width on phones only. */
:is(.stack, .stack-lg, .stack-sm, .stack-xs, .stack-2xs) > .btn,
:is(.stack, .stack-lg, .stack-sm, .stack-xs, .stack-2xs) > form:has(> .btn:only-child) { align-self: flex-start; }
:is(.stack, .stack-lg, .stack-sm, .stack-xs, .stack-2xs) > .btn.btn-block { align-self: stretch; }
@media (max-width: 599px) {
  .btn-block-phone, :is(.stack, .stack-lg, .stack-sm, .stack-xs) > .btn.btn-block-phone { align-self: stretch; width: 100%; }
}

.banner { display: flex; gap: var(--space-3); padding: var(--space-4); border-radius: var(--radius-md); background: var(--brand-soft); color: var(--ink); }

.stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: var(--space-4); }
.stat { display: flex; flex-direction: column; gap: var(--space-1); }
.stat-value { font-family: var(--font-display); font-weight: 700; font-size: var(--text-2xl); line-height: var(--leading-2xl); }
/* R14 (3.4): a tile's tone is a dot before its label, not a coloured border; only the page's alarm tile also colours its value
   (.stat-critical). The dot is drawn on the first label by itself, or with an explicit <span class="stat-dot"> where a view wants it. */
.stat-dot, .stat:is(.stat-critical, .stat-warn):not(:has(.stat-dot)) > .label:first-child::before {
  content: ""; display: inline-block; width: 8px; height: 8px; margin-right: var(--space-1-5); border-radius: 50%; background: currentColor; vertical-align: middle;
}
.stat-critical > .label:first-child::before, .stat-critical .stat-dot { color: var(--critical); }
.stat-warn > .label:first-child::before, .stat-warn .stat-dot { color: var(--warning); }

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

/* Empty states (_EmptyState partial): what an empty list means, and the next step if there is one. R14 (3.10): a calm
   illustration (.empty-art, an inline SVG from Navigation/Illustrations) or the icon tile, a title that says what it
   means, a body that says what will appear and how, then the verb. In a card of its own it is centred from 600px. */
.empty { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-4); padding: var(--space-4) 0; }
.empty.card, .card > .empty:only-child { padding: var(--space-8) var(--space-5); }
.empty-art { width: 96px; height: 96px; color: var(--brand); flex: none; }
.empty-art .art-fill { fill: var(--brand-soft); stroke: none; }
.empty-art .art-line { fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.empty-art .art-accent { fill: var(--key); stroke: none; }
.empty-icon { display: grid; place-items: center; width: 56px; height: 56px; border-radius: var(--radius-lg); background: var(--brand-soft); color: var(--brand-strong); }
.empty-icon > svg { width: 24px; height: 24px; }
.empty-title { font-size: var(--text-lg); line-height: var(--leading-lg); font-weight: var(--weight-semibold); }
.empty-body { max-width: 34em; font-size: var(--text-base); line-height: var(--leading-base); color: var(--ink-muted); }
/* A centred confirmation for a page with one message and one next step (e.g. staff signed in again). */
.status-panel { display: flex; flex-direction: column; align-items: center; gap: var(--space-4); max-width: 480px; margin: var(--space-12) auto 0; padding: var(--space-8) var(--space-6); text-align: center; }
.status-panel-title { margin: 0; font-size: var(--text-2xl); line-height: var(--leading-2xl); }
.status-panel-body { margin: 0; color: var(--ink-muted); }
.status-panel-actions { justify-content: center; }
@media (min-width: 600px) {
  .empty-art { width: 120px; height: 120px; }
  .empty.card, .card > .empty:only-child { align-items: center; text-align: center; }
}

/* ==== Round 14 components (every recipe is shown in the style guide, /admin/style-guide) ==== */

/* Hero (_Hero partial): the page's one thing, above the fold. One per page; it holds the page's only .btn-primary. The
   kicker says why now, the value says what, the text says the next step in one sentence. Tone is an edge, never a frame. */
.hero { display: grid; gap: var(--space-4); padding: var(--space-5); border-radius: var(--radius-lg); background: var(--brand-tint); border: 1px solid transparent; }
.hero-body { display: grid; gap: var(--space-1); min-width: 0; align-content: start; }
.hero-kicker { font-size: var(--text-md); line-height: var(--leading-md); font-weight: var(--weight-semibold); color: var(--brand-strong); }
.hero-value { margin: 0; font-family: var(--font-display); font-weight: var(--weight-bold); font-size: var(--text-2xl); line-height: var(--leading-2xl); letter-spacing: var(--display-tracking); color: var(--ink); }
.hero-text { max-width: 38em; font-size: var(--text-base); line-height: var(--leading-base); color: var(--ink); }
.hero-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.hero-art { width: 96px; height: 96px; color: var(--brand); }
.hero-critical { background: var(--critical-soft); box-shadow: inset 4px 0 0 var(--critical); }
.hero-critical .hero-kicker { color: var(--critical); }
.hero-warn { background: var(--warning-soft); box-shadow: inset 4px 0 0 var(--warning); }
.hero-warn .hero-kicker { color: var(--warning); }
.hero-good { background: var(--good-soft); }
.hero-good .hero-kicker { color: var(--good); }
@media (max-width: 599px) { .hero-actions .btn { flex: 1 1 auto; } }
@media (min-width: 600px) {
  .hero { grid-template-columns: minmax(0, 1fr) auto; align-items: center; padding: var(--space-6); border-radius: var(--radius-xl); }
  .hero:has(> .hero-art) { grid-template-columns: auto minmax(0, 1fr) auto; }
  .hero-value { font-size: var(--text-3xl); line-height: var(--leading-3xl); }
}

/* Property hero (the property page's photo band, .property-hero): the band, then the address, the facts and the actions. */
.property-hero { overflow: hidden; border-radius: var(--radius-lg); border: 1px solid var(--line); background: var(--surface-raised); }
.property-hero-band { display: grid; place-items: center; aspect-ratio: 3 / 1; background: var(--brand-soft); border-bottom: 1px solid var(--line); color: var(--brand-strong); font-size: var(--text-3xl); line-height: var(--leading-3xl); font-weight: var(--weight-semibold); }
.property-hero-band > img { width: 100%; height: 100%; object-fit: cover; }
.property-hero-body { display: grid; gap: var(--space-3); padding: var(--space-4); }
/* Facts: <dl class="facts"><div><dt class="fact-label">Rent</dt><dd class="fact-value">$650 a week</dd></div></dl>. */
.facts { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-3) var(--space-4); margin: 0; }
.facts > div { min-width: 0; }
.fact-label { font-size: var(--text-md); line-height: var(--leading-md); color: var(--ink-muted); }
.fact-value { margin: 0; font-size: var(--text-base); line-height: var(--leading-base); font-weight: var(--weight-semibold); overflow-wrap: anywhere; }
@media (min-width: 600px) {
  .property-hero-band { aspect-ratio: 21 / 9; max-height: 220px; }
  .facts { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .property-hero-body { padding: var(--space-5); }
}
@media (min-width: 960px) {
  .property-hero-body { grid-template-columns: minmax(0, 1fr) auto; align-items: start; }
  .property-hero-body .facts { grid-column: 1 / -1; }
}

/* Icon tile and avatar tile (.tile): one size (--tile) for rows, empty states and menus. Tone follows the row's state. */
.tile { display: grid; place-items: center; flex: none; width: var(--tile); height: var(--tile); border-radius: var(--radius-md); background: var(--brand-soft); color: var(--brand-strong); }
.tile > svg { width: 20px; height: 20px; }
.tile-warn { background: var(--warning-soft); color: var(--warning); }
.tile-critical { background: var(--critical-soft); color: var(--critical); }
.tile-info { background: var(--info-soft); color: var(--info); }
.tile-muted { background: var(--neutral-soft); color: var(--ink-muted); }
.tile-good { background: var(--good-soft); color: var(--good); }
.tile-lg { width: 56px; height: 56px; border-radius: var(--radius-lg); }
.tile-lg > svg { width: 28px; height: 28px; }

/* Row (_Row partial, <ul class="rows"> of <li class="row">): a list of things you open. The whole row is one link (the
   title's ::after covers it); buttons and menus inside stay clickable above it. In a card the rows run to the card's edge. */
.rows { display: flex; flex-direction: column; margin: 0; padding: 0; list-style: none; }
.row {
  position: relative; display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: var(--space-3);
  min-height: 64px; padding-block: var(--space-3); border-top: 1px solid var(--line);
}
.row:first-child { border-top: 0; }
.row:not(:has(> .row-lead)) { grid-template-columns: minmax(0, 1fr) auto; }
.row-lead { width: var(--tile); height: var(--tile); }
.row-main { display: flex; flex-direction: column; gap: var(--space-0-5); min-width: 0; }
.row-main > .row-title { font-size: var(--text-base); line-height: var(--leading-base); font-weight: var(--weight-semibold); color: var(--ink); text-decoration: none; overflow-wrap: anywhere; }
.row-main > .row-title::after { content: ""; position: absolute; inset: 0; }
.row-meta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-1) var(--space-2); font-size: var(--text-md); line-height: var(--leading-md); color: var(--ink-muted); }
.row-meta > span:not(.row-badge-phone) ~ span:not(.row-badge-phone)::before { content: "\00b7"; margin-right: var(--space-2); }
.row-end { display: flex; align-items: center; gap: var(--space-2); justify-self: end; }
.row-end .meta { white-space: nowrap; }
.row .row-go { flex: none; width: 20px; height: 20px; color: var(--ink-muted); }
.row-message { align-items: start; }
.row-message .row-lead { width: var(--tile); height: var(--tile); }
.row-message:hover { background: transparent; }
.row-name { color: var(--ink); font-weight: var(--weight-semibold); }
.row:hover, .row:has(.row-title:focus-visible) { background: var(--surface); }
.row:has(.row-title:focus-visible) { outline: 2px solid var(--brand); outline-offset: -2px; }
.row .row-title:focus-visible { outline: 0; }
.row :is(.btn, .row-menu, form) { position: relative; z-index: 1; }
.row-badge-phone { display: none; }
/* Rows run to the card's edge, also in the owner home's "Needs you" and "Coming up" groups (and the fold that holds the rest). */
.card > .rows, .card > .attention > .rows, .card > .attention > .fold > .rows { margin-inline: calc(-1 * var(--space-4)); }
.card > .rows > .row, .card > .attention > .rows > .row, .card > .attention > .fold > .rows > .row { padding-inline: var(--space-4); }
@media (max-width: 599px) {
  /* The badge moves under the title as the first meta item, so the end holds only the chevron. */
  .row-end > .badge { display: none; }
  .row-badge-phone { display: inline-flex; }
}
@media (min-width: 960px) {
  .card > .rows, .card > .attention > .rows, .card > .attention > .fold > .rows { margin-inline: calc(-1 * var(--space-5)); }
  .card > .rows > .row, .card > .attention > .rows > .row, .card > .attention > .fold > .rows > .row { padding-inline: var(--space-5); }
}
@media (hover: hover) and (pointer: fine) {
  .row .row-go { opacity: 0; }
  .row:hover .row-go, .row:focus-within .row-go { opacity: 1; }
}
@media (prefers-reduced-motion: no-preference) {
  .row { transition: background var(--duration-fast) var(--ease-standard); }
  .row .row-go { transition: opacity var(--duration-fast) var(--ease-standard); }
}

/* "This just changed": the record just created or saved (class is-new, set by the page) fades its wash out once;
   with reduced motion the wash is static. site.js scrolls it into view once. */
.is-new { background: var(--highlight); }
@media (prefers-reduced-motion: no-preference) {
  .is-new { background: none; animation: highlight 1.2s var(--ease-out) 1; }
  @keyframes highlight { from { background: var(--highlight); } }
}

/* Chips (label.chip around a checkbox): a multi-choice feature as a toggle with its own tick. A group is .chip-group. */
.chip-group { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.chip {
  position: relative; display: inline-flex; align-items: center; gap: var(--space-2); min-height: 44px; padding: 0 var(--space-4);
  border: 1px solid var(--line-strong); border-radius: var(--radius-pill); background: var(--surface-raised);
  font: var(--weight-semibold) var(--text-md)/var(--leading-md) var(--font-sans); cursor: pointer;
}
.chip input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; }
.chip:hover { border-color: var(--brand); }
.chip:has(input:checked) { background: var(--brand-soft); border-color: var(--brand); color: var(--brand-strong); }
.chip:has(input:checked)::before { content: "\2713"; }
.chip:has(input:focus-visible) { outline: 2px solid var(--brand); outline-offset: 2px; }
.chip:has(input:disabled) { opacity: .6; cursor: not-allowed; }
@media (min-width: 960px) { .chip { min-height: 36px; } }
@media (prefers-reduced-motion: no-preference) {
  .chip { transition: background var(--duration-fast) var(--ease-standard), border-color var(--duration-fast) var(--ease-standard); }
}

/* Step flow (_Steps partial and site.js): one form, its sections as steps. Without script every section shows and the
   form posts as before. .steps is the header, section.step the pages, .step-actions Back and Continue. */
.steps { display: flex; gap: var(--space-2); list-style: none; margin: 0; padding: 0; overflow-x: auto; counter-reset: step; scrollbar-width: none; }
.steps::-webkit-scrollbar { display: none; }
.steps li {
  position: relative; display: flex; align-items: center; gap: var(--space-2); min-height: 44px; padding: 0 var(--space-2); flex: none;
  color: var(--ink-muted); font: var(--weight-semibold) var(--text-md)/var(--leading-md) var(--font-sans); white-space: nowrap;
}
.steps li::before {
  counter-increment: step; content: counter(step); display: grid; place-items: center; width: 28px; height: 28px; border-radius: 50%;
  border: 1.5px solid var(--line-strong); font-size: var(--text-sm); line-height: var(--leading-sm);
}
.steps li[aria-current="step"] { color: var(--ink); }
.steps li[aria-current="step"]::before { background: var(--brand); border-color: var(--brand); color: var(--on-brand); }
.steps li.is-done { color: var(--ink); }
.steps li.is-done::before { content: "\2713"; background: var(--brand-soft); border-color: var(--brand-soft); color: var(--brand-strong); }
.steps li + li { padding-left: var(--space-6); }
.steps li + li::after { content: ""; position: absolute; left: 0; top: 50%; width: var(--space-4); height: 1px; background: var(--line); }
.is-stepped .step:not([aria-current="step"]) { display: none; }
.step-title { margin: 0; font-family: var(--font-display); font-size: var(--text-xl); line-height: var(--leading-xl); font-weight: 600; }
.step-title:focus, #main:focus { outline: 0; }
.step-actions { justify-content: space-between; }
.step-actions > :last-child { margin-left: auto; }
.steps li:not(.is-done) .step-done { display: none; }
.review { display: flex; flex-direction: column; gap: var(--space-4); }
/* The Review step's plain-English summary is filled in by step-review.js, so it only shows with script; without it every step shows. */
[data-review-summary] { display: none; }
.js [data-review-summary] { display: grid; gap: var(--space-3); }
[data-review-summary] > p { margin: 0; }
[data-review-summary] [hidden] { display: none; }
@media (min-width: 960px) { form[data-steps] > .step { max-width: 720px; } }

/* Toasts (_Toast partial, site.js): "saved", "sent", "recorded" after a save. Fixed at the bottom (above the phone tab
   bar), announced politely, gone after six seconds (longer while hovered or focused). Errors stay as alerts on the page. */
.toast-region {
  position: fixed; left: 0; right: 0; bottom: calc(var(--space-4) + env(safe-area-inset-bottom)); z-index: var(--z-overlay);
  display: flex; flex-direction: column; align-items: center; gap: var(--space-2); padding: 0 var(--space-4); pointer-events: none;
}
.toast {
  pointer-events: auto; display: flex; align-items: center; gap: var(--space-3); width: min(100%, 420px); min-height: 52px; padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md); background: var(--surface-raised); border: 1px solid var(--line); box-shadow: var(--shadow-overlay);
  color: var(--ink); font-size: var(--text-md); line-height: var(--leading-md);
}
.toast > svg { flex: none; width: 20px; height: 20px; color: var(--good); }
.toast-text { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.toast-action { flex: none; }
.toast-close { display: none; flex: none; width: 36px; height: 36px; border: 0; background: none; }
.js .toast-close { display: inline-flex; }
@media (max-width: 959px) { .app:has(> .tabbar) .toast-region { bottom: calc(65px + var(--space-3) + env(safe-area-inset-bottom)); } }
@media (min-width: 700px) and (max-width: 959px) { .app:has(> .tabbar) .toast-region { bottom: calc(var(--space-4) + env(safe-area-inset-bottom)); } }
@media (min-width: 960px) {
  /* Beside the sidebar, bottom left of the content, measured from the centred app's edge. */
  .toast-region { align-items: flex-start; left: calc(max(0px, (100vw - var(--page-max)) / 2) + 248px + var(--space-6)); right: auto; padding: 0; }
  .app-wide .toast-region { left: calc(max(0px, (100vw - var(--page-max-wide)) / 2) + 248px + var(--space-6)); }
}
@media (prefers-reduced-motion: no-preference) {
  .toast { animation: toast-in var(--duration-base) var(--ease-out); transition: opacity var(--duration-slow) var(--ease-out), transform var(--duration-slow) var(--ease-out); }
  .toast.is-leaving { opacity: 0; transform: translateY(8px); }
  @keyframes toast-in { from { opacity: 0; transform: translateY(8px); } }
}

/* Skeletons: where content loads by script (address lookup results, "Reading your lease", the staff detail pane). The
   container sets aria-busy="true" while it loads. A flat fill with reduced motion. */
.skeleton { display: block; height: 1em; border-radius: var(--radius-sm); background: var(--neutral-soft); }
.skeleton-row { display: grid; grid-template-columns: var(--tile) minmax(0, 1fr); gap: var(--space-3); padding: var(--space-3) 0; }
.skeleton-row > .skeleton:first-child { height: var(--tile); }
@media (prefers-reduced-motion: no-preference) {
  .skeleton { background: linear-gradient(90deg, var(--neutral-soft) 0 40%, var(--surface) 50%, var(--neutral-soft) 60% 100%); background-size: 200% 100%; animation: shimmer 1.2s linear infinite; }
  @keyframes shimmer { to { background-position: -200% 0; } }
}

/* A banner with its action at the end (the two-step nudge when the setup checklist is hidden). One line on a desktop. */
.banner-actionable { align-items: center; }
.banner-actionable .btn { margin-left: auto; flex: none; }
@media (max-width: 599px) { .banner-actionable { flex-wrap: wrap; } }


/* Forms */

.field { display: flex; flex-direction: column; gap: var(--space-1-5); margin: 0; padding: 0; border: 0; min-width: 0; }
/* Not choice cards inside a fieldset.field: they keep their own padding and regular-weight captions (UX-B4). */
.field label:not(.choice), .field legend { font-size: var(--text-md); line-height: var(--leading-md); font-weight: 600; padding: 0; }
.field-aside { align-self: flex-end; }
.input {
  width: 100%; min-height: 44px; padding: var(--space-2) var(--space-3);
  border: 1px solid var(--line-strong); border-radius: var(--radius-md);
  background: var(--surface-raised); color: var(--ink); font: var(--weight-regular) var(--text-base)/var(--leading-base) var(--font-sans);
}
.input:focus { outline: 2px solid var(--brand); outline-offset: 0; border-color: var(--brand); }
/* Selects: our own chevron, drawn in the text colour, with room so it doesn't touch the border. */
select.input {
  appearance: none; padding-right: var(--space-10);
  background-image: linear-gradient(45deg, transparent 50%, var(--ink-muted) 50%), linear-gradient(135deg, var(--ink-muted) 50%, transparent 50%);
  background-position: right 19px center, right 14px center; background-size: 5px 5px, 5px 5px; background-repeat: no-repeat;
}
select.input[multiple] { padding-right: var(--space-3); background-image: none; }
select.input.input-sm { padding-right: 34px; background-position: right 16px center, right 11px center; }
/* File pickers: a real button and the chosen file's name, the same in every browser. */
input[type="file"].input { padding: var(--space-1-5); font-size: var(--text-md); line-height: 30px; color: var(--ink-muted); }
input[type="file"].input::file-selector-button {
  min-height: 32px; margin-right: var(--space-3); padding: 0 var(--space-3);
  border: 1px solid var(--line-strong); border-radius: var(--radius-sm); background: var(--surface); color: var(--ink);
  font: var(--weight-semibold) var(--text-md)/var(--leading-md) var(--font-sans); cursor: pointer;
}
input[type="file"].input:hover::file-selector-button { border-color: var(--brand); }
input[type="date"].input, input[type="time"].input, input[type="datetime-local"].input { min-width: 0; }
/* color-scheme (tokens.css) already draws the picker icon light in dark mode, so no invert filter here (UX-B2). */
input.input::-webkit-calendar-picker-indicator { cursor: pointer; opacity: .7; }
/* R11-J3: one invalid state wherever a field is wrong: rejected by jQuery validation, by site.js's own checks
   (data-client-invalid) or by the server (aria-invalid on hand-written fields). Focused, it keeps the critical border with
   the brand focus ring drawn just outside it, so focus never turns a wrong field green. */
.input:is(.input-validation-error, [aria-invalid="true"], [data-client-invalid]) { border-color: var(--critical); }
.input:is(.input-validation-error, [aria-invalid="true"], [data-client-invalid]):focus { border-color: var(--critical); outline-offset: 2px; }
.input-sm { width: auto; min-height: 40px; padding: var(--space-2); font-size: var(--text-md); line-height: var(--leading-md); }
.input-sm.input-fill { width: 100%; }
.field-error, .field-validation-error { color: var(--critical); font-size: var(--text-sm); line-height: var(--leading-sm); }
.field-validation-valid { display: none; }
fieldset { margin: 0; padding: 0; border: 0; min-width: 0; }
legend { padding: 0; margin-bottom: var(--space-2); }

.check { display: flex; align-items: flex-start; gap: var(--space-2); font-size: var(--text-md); line-height: var(--leading-md); }
.check input, .choice input { width: 24px; height: 24px; margin: 1px 0 0; flex: none; accent-color: var(--brand); }
/* R9-C31: a check box alone in a table cell, with a 44px target. */
.check.compare-pick { min-width: 44px; min-height: 44px; align-items: center; justify-content: center; cursor: pointer; }
.check.compare-pick input { margin: 0; }

.choice-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: var(--space-2); }
.choice {
  display: flex; align-items: flex-start; gap: var(--space-3); padding: var(--space-3);
  border: 1px solid var(--line); border-radius: var(--radius-md); background: var(--surface-raised); cursor: pointer;
}
.choice:has(input:checked) { border-color: var(--brand); background: var(--brand-soft); }
.choice-disabled { opacity: .55; cursor: not-allowed; }

/* align-items: center, so a badge or a line of text beside a 44px button keeps its own height (PO-12). */
.actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.row-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-1) var(--space-4); }
/* Filters and ranges (_FilterBar partial): one joined control, the chosen one marked with aria-current. */
.filter-bar { display: inline-flex; flex-wrap: wrap; align-self: flex-start; max-width: 100%; border: 1px solid var(--line-strong); border-radius: var(--radius-md); background: var(--surface-raised); overflow: hidden; }
.filter-bar a { display: inline-flex; align-items: center; gap: var(--space-2); min-height: 44px; padding: 0 var(--space-4); color: var(--ink); font-size: var(--text-md); line-height: var(--leading-md); font-weight: var(--weight-semibold); text-decoration: none; }
.filter-bar a + a { border-left: 1px solid var(--line); }
.filter-bar a:hover { background: var(--surface); color: var(--ink); }
.filter-bar a[aria-current="true"], .filter-bar a[aria-current="page"] { background: var(--brand-soft); color: var(--brand-strong); }
.filter-bar .filter-count { color: var(--ink-muted); font-weight: var(--weight-regular); font-variant-numeric: tabular-nums; }
.filter-bar a[aria-current] .filter-count { color: inherit; }
@media (min-width: 960px) { .filter-bar a { min-height: 36px; } }
.btn-block { width: 100%; }
/* Text buttons: a quieter action next to a primary one, still a 44px target. */
.btn-text { border: 0; background: none; color: var(--brand); padding: 0 var(--space-2); }
.btn-text:hover { color: var(--brand-strong); text-decoration: underline; }
.btn-text-danger { border: 0; background: none; color: var(--critical); padding: 0 var(--space-2); }
.btn-text-danger:hover { text-decoration: underline; }
/* A row of actions starting with a text button lines up with the text above it, not indented. */
.actions > .btn-text:first-child, .actions > form:first-child > .btn-text, .actions > form:first-child > .btn-text-danger { padding-inline-start: 0; }
.inline-form { display: inline-flex; }
.btn-link { border: 0; padding: 0; background: none; color: var(--brand); font: inherit; font-weight: 600; cursor: pointer; }
.btn-link:hover { text-decoration: underline; }
.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* Alerts (PO-15): info = something to know (including why a page isn't open yet), success = it worked, warn = needs
   attention soon, error = it didn't work. Each tone has its own soft background and an icon in the tone's colour, so
   they're told apart without relying on colour. The icon is a mask on ::before (--alert-icon), so views add no markup. */
.alert { position: relative; padding: var(--space-3) var(--space-4); border-radius: var(--radius-md); font-size: var(--text-md); line-height: var(--leading-md); border: 1px solid transparent; color: var(--ink); }
.content-inner > .alert { margin-bottom: var(--space-4); }
.alert ul { margin: 0; padding-left: var(--space-5); }
.alert:is(.alert-info, .alert-success, .alert-warn, .alert-error):not(:has(> svg, > .spinner, > .identity-spinner)) { padding-inline-start: calc(var(--space-4) + var(--space-5) + var(--space-3)); }
.alert:is(.alert-info, .alert-success, .alert-warn, .alert-error):not(:has(> svg, > .spinner, > .identity-spinner))::before {
  content: ""; position: absolute; left: var(--space-4); top: var(--space-3); width: 20px; height: 20px;
  background: var(--alert-tone); -webkit-mask: var(--alert-icon) center / contain no-repeat; mask: var(--alert-icon) center / contain no-repeat;
}
.alert-info {
  --alert-tone: var(--info); background: var(--info-soft); border-color: color-mix(in srgb, var(--info) 30%, transparent);
  --alert-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpath d='M12 16v-4M12 8h.01'/%3E%3C/svg%3E");
}
.alert-success {
  --alert-tone: var(--good); background: var(--good-soft); border-color: color-mix(in srgb, var(--good) 30%, transparent);
  --alert-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpath d='m8 12 3 3 5-6'/%3E%3C/svg%3E");
}
.alert-warn {
  --alert-tone: var(--warning); background: var(--warning-soft); border-color: color-mix(in srgb, var(--warning) 35%, transparent);
  --alert-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0z'/%3E%3Cpath d='M12 9v4M12 17h.01'/%3E%3C/svg%3E");
}
.alert-error {
  --alert-tone: var(--critical); background: var(--critical-soft); border-color: color-mix(in srgb, var(--critical) 35%, transparent); color: var(--critical);
  --alert-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpath d='m15 9-6 6M9 9l6 6'/%3E%3C/svg%3E");
}
.alert-dev { background: color-mix(in srgb, var(--key) 22%, var(--surface-raised)); border-color: var(--key); color: var(--ink); overflow-wrap: anywhere; }
.validation-summary-valid { display: none; }

/* Lists and page headers */

.page-header {
  display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--space-4); align-items: flex-start;
  row-gap: var(--space-3);
}
/* .narrow no longer narrows the page: every page uses the same width (--page-max, sidebar included). Running text keeps a
   readable line length, and single form fields keep a usable width, so wide screens don't stretch them. */
.narrow { max-width: none; }
.narrow > p, .narrow > .stack-xs > p, .narrow > .caption { max-width: 46em; }
@media (min-width: 960px) {
  :is(form.stack, form.stack-lg, form.card, .card > form, .narrow) > .field,
  :is(form.stack, form.stack-lg, form.card, .card > form, .narrow) > fieldset.field { max-width: 720px; }
}
.list { list-style: none; margin: 0; padding: 0; }
.list-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3) var(--space-4); padding: var(--space-3) 0; border-top: 1px solid var(--line); }
.list-row:first-child { border-top: 0; padding-top: 0; }
.list-main { display: flex; flex-direction: column; min-width: 0; flex: 1 1 220px; }
.list-main .caption { overflow-wrap: anywhere; }
.list-main .badge { margin-left: var(--space-1-5); }
.list-tags { display: flex; gap: var(--space-1); }
.list-role, .list-count { color: var(--ink-muted); font-size: var(--text-md); }
.avatar {
  display: grid; place-items: center; flex: none; width: 40px; height: 40px; border-radius: 50%;
  background: var(--brand-soft); color: var(--brand-strong); font-weight: 600;
}

/* App shell additions */

.topbar-actions { display: flex; gap: var(--space-2); }
.topbar-actions form { display: contents; }
.sidebar-footer {
  display: flex; align-items: center; margin-top: auto; padding-top: var(--space-4); border-top: 1px solid var(--line);
  gap: var(--space-1);
}
.sidebar-footer .avatar { width: 32px; height: 32px; font-size: var(--text-md); }
.sidebar-user {
  flex: 1; font-size: var(--text-md); line-height: var(--leading-md); color: var(--ink);
  font-weight: var(--weight-semibold); display: flex; flex-direction: column; min-width: 0; overflow-wrap: normal;
}

/* Sign-in pages: a brand panel beside the form on desktop, the form alone on phones.
   The panel keeps the light theme's deep green in dark mode too. */

.auth { min-height: 100vh; display: grid; }
.auth-aside { display: none; }
.auth-main { display: flex; flex-direction: column; min-height: 100vh; padding: var(--space-5) var(--space-4); }
.auth-top { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); }
.auth-mobile-logo img { display: block; height: 28px; width: auto; }
.auth-switch { margin-left: auto; font-size: var(--text-md); color: var(--ink-muted); }
.auth-switch a { font-weight: 600; }
.auth-panel { width: 100%; max-width: 400px; margin: auto; padding: var(--space-10) 0; display: flex; flex-direction: column; gap: var(--space-6); }
@media (min-width: 600px) {
  .auth-panel {
    max-width: 420px; margin: auto; padding: 36px var(--space-8);
    background: var(--surface-raised); border: 1px solid var(--line); border-radius: var(--radius-lg); box-shadow: var(--shadow-raised);
  }
}
.auth-head { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-2); }
.auth-head h1, .auth-panel h1 { font-weight: var(--display-weight); font-size: var(--text-display-sm); line-height: var(--leading-display-sm); letter-spacing: var(--display-tracking); }
.auth-form { display: flex; flex-direction: column; gap: var(--space-4); }
.auth-alt { color: var(--ink-muted); font-size: var(--text-md); }
.auth-alt a { font-weight: 600; }
.auth-icon { display: grid; place-items: center; width: 48px; height: 48px; margin-bottom: var(--space-2); border-radius: var(--radius-lg); background: var(--brand-soft); color: var(--brand-strong); }
.auth-foot { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-1-5) var(--space-4); font-size: var(--text-sm); color: var(--ink-muted); }
.auth-foot a { color: var(--ink-muted); }

.input-group { position: relative; display: flex; }
.input-group .input { padding-right: 72px; }
.input-addon {
  position: absolute; top: 4px; right: 4px; bottom: 4px; padding: 0 var(--space-3); border: 0; border-radius: var(--radius-sm);
  background: transparent; color: var(--brand); font: var(--weight-semibold) var(--text-md)/var(--leading-md) var(--font-sans); cursor: pointer;
}
.input-addon:hover { background: var(--brand-soft); }

.strength { display: flex; align-items: center; gap: var(--space-1-5); font-size: var(--text-sm); color: var(--ink-muted); }
.strength i { width: 36px; height: 4px; border-radius: var(--radius-xs); background: var(--line); }
.strength span { margin-left: var(--space-1-5); }
.strength[data-level="1"] i:nth-child(-n+1) { background: var(--critical); }
.strength[data-level="2"] i:nth-child(-n+2) { background: var(--key); }
.strength[data-level="3"] i { background: var(--good); }
.strength[data-level="1"] span { color: var(--critical); }

@media (min-width: 960px) {
  .auth { grid-template-columns: 3fr minmax(420px, 2fr); }
  .auth-main { padding: 28px var(--space-12); }
  .auth-mobile-logo { visibility: hidden; }
  .auth-aside {
    position: sticky; top: 0; height: 100vh; overflow: hidden;
    display: flex; flex-direction: column; justify-content: space-between; gap: var(--space-8);
    padding: var(--space-10) var(--space-12); color: var(--on-brand-deep);
    background:
      radial-gradient(circle at 85% 15%, rgba(242,180,65,.22), transparent 40%),
      radial-gradient(circle at 10% 90%, rgba(79,195,161,.25), transparent 45%),
      var(--brand-deep);
  }
  .auth-logo { display: block; width: 168px; height: auto; }
  .auth-aside-body { display: flex; flex-direction: column; gap: var(--space-4); }
  .auth-tagline { font-family: var(--font-display); font-weight: var(--display-weight); font-size: var(--text-3xl); line-height: var(--leading-3xl); letter-spacing: var(--display-tracking); max-width: 440px; }
  /* R11-L3: secondary text is a colour, not opacity, so the contrast that's measured is the contrast that's drawn. */
  .auth-aside-lede { font-size: var(--text-lg); line-height: var(--leading-lg); color: var(--on-brand-deep-muted); max-width: 25.5em; }
  .auth-cards { display: flex; flex-direction: column; gap: var(--space-3); margin-top: var(--space-6); max-width: 380px; }
  .auth-card {
    display: flex; align-items: flex-start; gap: var(--space-3); padding: 14px var(--space-4); border-radius: var(--radius-lg);
    background: var(--overlay-on-dark); border: 1px solid var(--overlay-line-on-dark); backdrop-filter: blur(6px);
  }
  .auth-card-offset { margin-left: var(--space-8); }
  .auth-card div { display: flex; flex-direction: column; }
  .auth-card strong { font-size: var(--text-base); }
  /* R11-L3: full white. At 80% opacity this small text measured 3.9:1 where a card sits on the panel's lighter glow. */
  .auth-card span:not(.auth-card-dot) { font-size: var(--text-sm); }
  .auth-card-dot { flex: none; width: 10px; height: 10px; margin-top: var(--space-1-5); border-radius: 50%; }
  .auth-dot-good { background: var(--good-on-deep); } .auth-dot-key { background: var(--key); } .auth-dot-soft { background: var(--soft-on-deep); }
  .auth-points { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-5); margin: 0; padding: 0; list-style: none; font-size: var(--text-md); }
  .auth-points li::before { content: "✓"; margin-right: var(--space-2); color: var(--key); font-weight: 700; }
}

/* Admin tables and forms */

/* R9-F17: positioned, so a visually hidden span in a cell (absolutely positioned) is clipped by the scroller instead of
   widening the page. */
.table-wrap { position: relative; overflow-x: auto; max-width: 100%; min-width: 0; }
/* Tables that become one card per row on phones: each cell after the first shows its column name (data-label). */
@media (max-width: 599px) {
  .table-cards thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
  .table-cards, .table-cards tbody, .table-cards tr, .table-cards td { display: block; width: 100%; }
  .table-cards tr { padding: var(--space-3) 0; border-bottom: 1px solid var(--line); }
  .table-cards tr:last-child { border-bottom: 0; }
  .table.table-cards td { padding: var(--space-0-5) 0; border-bottom: 0; overflow-wrap: anywhere; }
  .table-cards td[data-label]::before { content: attr(data-label) ": "; color: var(--ink-muted); }
  .table-cards .badge { margin: var(--space-1) var(--space-1-5) 0 0; }
  .table-cards td.num { text-align: left; }
  .table-cards .table-cards-title { font-weight: 600; }
}
.table { width: 100%; border-collapse: collapse; font-size: var(--text-md); line-height: var(--leading-md); }
.table th { padding: var(--space-2) var(--space-3); text-align: left; font-weight: 600; color: var(--ink-muted); border-bottom: 1px solid var(--line); white-space: nowrap; }
.table td { padding: var(--space-3); border-bottom: 1px solid var(--line); vertical-align: middle; }
.table tr:last-child td { border-bottom: 0; }
.table .num { text-align: right; white-space: nowrap; }
/* R14 (3.9): the primary column of a table reads at body size. */
.table td.td-main { font-size: var(--text-base); line-height: var(--leading-base); }
.table .col-end { text-align: right; }
.table .badge { margin-left: var(--space-1-5); }
/* R14 (3.11): one column on phones (never two 170px inputs), two from 600px, three with .form-grid-3 from 960px. A row that
   spans the grid is .field-wide. */
.form-grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-4); }
.form-grid > .field-wide { grid-column: 1 / -1; }
@media (min-width: 600px) { .form-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 960px) { .form-grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
/* Label and value pairs: <dl class="rule-list"><div><dt>…</dt><dd>…</dd></div></dl>, stacked on phones. */
textarea.input { resize: vertical; }
.input-prefix { position: relative; }
.input-prefix span { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); color: var(--ink-muted); }
.input-prefix .input { padding-left: 26px; }
.nav-admin { padding-top: var(--space-4); border-top: 1px solid var(--line); }
.nav-heading { padding: 0 var(--space-3); font-size: var(--text-xs); font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-muted); }

.switch input { appearance: none; position: relative; width: 44px; height: 26px; margin: 0; border-radius: var(--radius-pill); background: var(--line-strong); cursor: pointer; transition: background var(--duration-fast) var(--ease-standard); }
.switch input::after { content: ""; position: absolute; top: 3px; left: 3px; width: 20px; height: 20px; border-radius: 50%; background: var(--surface-raised); transition: transform var(--duration-fast) var(--ease-standard); }
.switch input:checked { background: var(--brand); }
.switch input:checked::after { transform: translateX(18px); }
.switch input:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }

.feature-switch { display: flex; flex-direction: column; gap: var(--space-4); padding-bottom: var(--space-4); border-bottom: 1px solid var(--line); }
.feature-switch-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-4); }
.feature-switch-head .switch { flex: none; }

/* Two-step verification */
.method-choice { color: inherit; text-decoration: none; }
.method-choice:hover { border-color: var(--brand); }
.setup-steps { display: flex; flex-direction: column; gap: var(--space-6); margin: 0; padding-left: var(--space-5); }
.setup-steps > li { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-2); }
.qr-code { padding: 10px; border: 1px solid var(--line); border-radius: var(--radius-md); background: #ffffff; } /* QR codes need a white background in dark mode too */
.code-inline { font-family: var(--font-mono); font-size: var(--text-md); letter-spacing: .04em; overflow-wrap: anywhere; }
.input-code { max-width: 220px; font-family: var(--font-mono); font-size: 20px; letter-spacing: .2em; }
.recovery-codes { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: var(--space-2) var(--space-4); margin: 0; padding: 0; list-style: none; }

/* Dialogs (<dialog class="modal">) */
.modal { width: min(480px, calc(100vw - 2 * var(--space-4))); max-height: calc(100vh - 2 * var(--space-4)); overscroll-behavior: contain; padding: var(--space-6); border: 1px solid var(--line); border-radius: var(--radius-lg); background: var(--surface-raised); color: var(--ink); box-shadow: var(--shadow-raised); }
.modal::backdrop { background: var(--scrim); }
/* Owner's decision (15 Nov 2026): a dialog's buttons sit on the right. Source order is kept, so focus order still matches. */
.modal .actions { justify-content: flex-end; }
/* R11-J4: a form in a dialog keeps its buttons in view: the last row of actions sticks to the dialog's foot while the form
   scrolls under it (bottom is the dialog's padding, which sticky positioning keeps clear). Swipes past the end don't
   scroll the page behind, and the page doesn't scroll while a dialog is open. */
html:has(dialog.modal:modal) { overflow: hidden; }
.modal form:has(.field) > .actions:last-of-type {
  position: sticky; bottom: calc(-1 * var(--space-6)); z-index: 1; margin-inline: calc(-1 * var(--space-6));
  padding: var(--space-3) var(--space-6); border-top: 1px solid var(--line); background: var(--surface-raised);
}
/* Phones: a form dialog fills the screen, like the More menu's sheet, so the form has the whole height. */
@media (max-width: 599px) {
  .modal:not(.more-menu, .account-menu):has(form .field) {
    width: 100%; max-width: 100%; height: 100%; max-height: 100%; margin: 0; border: 0; border-radius: 0;
    padding-bottom: calc(var(--space-6) + env(safe-area-inset-bottom));
  }
}
.modal-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-4); }
button.method-choice { width: 100%; font: inherit; color: inherit; text-align: left; }
.method-choice .caption, .modal p { overflow-wrap: anywhere; }

/* Settings */

.tabs { display: flex; gap: var(--space-1); overflow-x: auto; border-bottom: 1px solid var(--line); }
.tabs a { padding: var(--space-3) var(--space-4); border-bottom: 2px solid transparent; color: var(--ink-muted); font-weight: 600; font-size: var(--text-md); text-decoration: none; white-space: nowrap; }
.tabs a:hover { color: var(--ink); }
/* R14 (3.15): 36px tabs from 960px, like the filter bar; 44px on touch widths. */
@media (min-width: 960px) { .tabs a { padding-block: var(--space-2); } }
/* More (.tabs-more): a section with more than five tabs keeps five and puts the rest in a menu. Markup: a <details
   class="tabs-more"> as the last child of the nav, with <summary>More</summary> and <div class="tabs-more-menu"> holding
   the links; the same links are also written inline with class "tabs-extra", which show from 1200px (and the menu hides).
   site.js positions the menu under the summary (the tabs scroll sideways, so it is fixed), closes it on Escape, an outside
   click or a scroll, and returns focus to the summary. */
.tabs .tabs-extra { display: none; }
.tabs-more { --tabs-more-top: auto; --tabs-more-left: auto; flex: none; }
.tabs-more > summary {
  display: flex; align-items: center; gap: var(--space-1); padding: var(--space-3) var(--space-4); border-bottom: 2px solid transparent;
  color: var(--ink-muted); font-weight: var(--weight-semibold); font-size: var(--text-md); line-height: var(--leading-md); cursor: pointer; list-style: none; white-space: nowrap;
}
.tabs-more > summary::-webkit-details-marker { display: none; }
.tabs-more > summary::after {
  content: ""; width: 7px; height: 7px; margin-top: -3px; border-right: 2px solid currentColor; border-bottom: 2px solid currentColor; transform: rotate(45deg);
}
.tabs-more > summary:hover, .tabs-more[open] > summary { color: var(--ink); }
.tabs-more:has([aria-current="page"]) > summary { color: var(--brand-strong); border-bottom-color: var(--brand); }
.tabs-more-menu {
  position: fixed; top: var(--tabs-more-top); left: var(--tabs-more-left); z-index: var(--z-header); display: flex; flex-direction: column;
  min-width: 12rem; padding: var(--space-1); border: 1px solid var(--line); border-radius: var(--radius-md); background: var(--surface-raised); box-shadow: var(--shadow-overlay);
}
.tabs .tabs-more-menu a { display: flex; align-items: center; min-height: 44px; padding: 0 var(--space-3); border-bottom: 0; border-radius: var(--radius-sm); }
.tabs .tabs-more-menu a:hover { background: var(--surface); }
.tabs .tabs-more-menu a[aria-current="page"] { background: var(--brand-soft); }
@media (min-width: 960px) { .tabs-more > summary { padding-block: var(--space-2); } }
@media (min-width: 1200px) {
  .tabs .tabs-extra { display: block; }
  .tabs .tabs-more { display: none; }
}
.tabs a[aria-current="page"] { color: var(--brand-strong); border-bottom-color: var(--brand); }
.sidebar-account {
  display: flex; align-items: center; flex: 1; min-width: 0; border-radius: var(--radius-md); background: none;
  color: var(--ink); font: inherit; text-align: left; cursor: pointer; gap: var(--space-2); min-height: 48px;
  padding: var(--space-1-5); border: 1px solid transparent;
  transition: background var(--duration-fast) var(--ease-standard), border-color var(--duration-fast) var(--ease-standard);
}
.sidebar-account:hover { background: var(--surface); }
.sidebar-account svg { flex: none; color: var(--ink-muted); }
.owner-switcher { display: flex; flex-direction: column; gap: var(--space-1-5); padding: 0 var(--space-3); }
.owner-switcher .nav-heading { padding: 0; }
.owner-switcher select { width: 100%; }

/* Notifications */

.nav-secondary { padding-top: var(--space-2); }
.nav-count { margin-left: auto; min-width: 22px; padding: 1px 7px; border-radius: var(--radius-pill); background: var(--brand); color: var(--on-brand); font-size: var(--text-xs); line-height: 18px; text-align: center; }
.bell { position: relative; }
/* Unread counts are brand-coloured everywhere (routine updates aren't alarms). */
.bell-count { position: absolute; top: 2px; right: 2px; min-width: 18px; padding: 0 var(--space-1); border-radius: var(--radius-pill); background: var(--brand); color: var(--on-brand); box-shadow: 0 0 0 2px var(--surface-raised); font-size: var(--text-xs); line-height: 18px; font-weight: var(--weight-semibold); text-align: center; }
.notification-list .is-unread .label { font-weight: 700; }
.dot { flex: none; width: 10px; height: 10px; border-radius: 50%; background: transparent; align-self: flex-start; margin-top: 7px; }
.dot-brand { background: var(--brand); }
.dot-critical { background: var(--critical); }

/* Inbox: emails shown as their own text, line breaks kept */
.email-text { margin: 0; white-space: pre-line; overflow-wrap: anywhere; line-height: 1.6; }
.attachment-list { margin: 0; padding: 0; list-style: none; }
.email-more summary { cursor: pointer; }
.email-more .email-text { margin-top: var(--space-2); }

/* Messages: tenancy conversations, never edited once sent */
.thread { display: flex; flex-direction: column; gap: var(--space-3); margin: 0; padding: 0; list-style: none; }
.message { display: flex; flex-direction: column; gap: var(--space-1); max-width: 85%; align-self: flex-start; padding: var(--space-3) var(--space-4); border: 1px solid var(--line); border-radius: var(--radius-md); background: var(--surface); }
.message-mine { align-self: flex-end; border-color: transparent; background: var(--brand-soft); }
/* A conversation (_MessageThread): avatar, name and time above a bubble; your own messages sit on the right, tinted. */
.chat { display: flex; align-items: flex-start; gap: var(--space-2); max-width: 85%; align-self: flex-start; }
.chat-mine { flex-direction: row-reverse; align-self: flex-end; }
.chat-avatar { width: 32px; height: 32px; font-size: var(--text-md); }
.chat-body { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; }
.chat-bubble { padding: var(--space-3) var(--space-4); border: 1px solid var(--line); border-radius: var(--radius-md); background: var(--surface); }
.chat-mine .chat-bubble { border-color: transparent; background: var(--brand-soft); }
.chat-meta { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-0-5) var(--space-2); }
.chat-mine .chat-meta { justify-content: flex-end; }
.chat-body > .rows { border: 1px solid var(--line); border-radius: var(--radius-md); background: var(--surface); overflow: hidden; }
.chat-body > .rows > .row { padding-inline: var(--space-3); }
.tabs .dot { display: inline-block; width: 8px; height: 8px; margin: 0 0 1px var(--space-1-5); vertical-align: middle; }

/* Properties */

.property-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: var(--space-4); }
.property-card {
  display: flex; flex-direction: column; gap: var(--space-3); padding: 0; overflow: hidden; color: var(--ink);
  text-decoration: none; position: relative;
}
.property-card:hover { color: var(--ink); border-color: var(--line); }
.property-photo {
  position: relative; display: grid; place-items: center; overflow: hidden; border-bottom: 1px solid var(--line);
  aspect-ratio: 3 / 1; background: var(--brand-soft);
}
/* Out of the flow, so the photo's own height can't override the box's aspect ratio (PO-13: the phone 3:1 crop now holds). */
.property-photo img { position: absolute; inset: 0; display: block; width: 100%; height: 100%; object-fit: cover; }
/* Phones: a shorter photo so two properties and their badges fit on a screen (live review M7). */
@media (max-width: 599px) { .property-photo { aspect-ratio: 3 / 1; } }
.property-body { padding: 0 var(--space-4) var(--space-4); }
/* Property hero (page-specific parts; the band, body and facts are shared above): the one action and the row menu sit together, and a fact that needs attention reads in its tone. */
.property-hero-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.property-hero-actions .btn { flex: 1 1 auto; }
@media (min-width: 600px) { .property-hero-actions .btn { flex: none; } }
.property-hero h1 { overflow-wrap: anywhere; }
.fact-critical { color: var(--critical); }
.fact-warn { color: var(--warning); }
/* Overview: the set-up card's progress and three photo thumbnails. */
.setup-progress { display: flex; align-items: center; gap: var(--space-3); }
.setup-progress .progress { flex: 1 1 auto; height: 6px; }
.photo-strip-overview { grid-template-columns: repeat(3, minmax(0, 120px)); }
.photo-strip-overview img { aspect-ratio: 3 / 2; }
/* R9-D7: tracks may shrink below their content (a long trade name in a select), so the page never scrolls sideways. */
.overview-grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-4); }
.overview-grid .input { max-width: 100%; text-overflow: ellipsis; }
@media (min-width: 960px) { .overview-grid { grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); } }
.checklist { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.checklist li { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-1) var(--space-2); padding-left: 28px; position: relative; }
.checklist li::before { content: ""; position: absolute; left: 0; top: 3px; width: 18px; height: 18px; border-radius: 50%; border: 2px solid var(--line-strong); }
.checklist li.done::before { border-color: var(--good); background: var(--good); }
/* Onboarding checklist (OnboardingChecklist view component): six steps with a progress bar */
.onboarding-property + .onboarding-property { padding-top: var(--space-4); border-top: 1px solid var(--line); }
.onboarding-progress { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-4); }
.progress { flex: 1 1 200px; height: 8px; border-radius: var(--radius-sm); background: var(--surface); border: 1px solid var(--line-strong); overflow: hidden; }
.progress-fill { display: block; height: 100%; width: 0; background: var(--brand); }
.progress-fill[data-done="1"] { width: calc(100% / 6); }
.progress-fill[data-done="2"] { width: calc(200% / 6); }
.progress-fill[data-done="3"] { width: 50%; }
.progress-fill[data-done="4"] { width: calc(400% / 6); }
.progress-fill[data-done="5"] { width: calc(500% / 6); }
.progress-fill[data-done="6"] { width: 100%; }
.onboarding-steps { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-4); }
.onboarding-step { display: flex; align-items: flex-start; gap: var(--space-2); }
.onboarding-step-body { flex: 1; min-width: 0; }
.onboarding-step-body > .btn { align-self: flex-start; margin-top: var(--space-1); }
.onboarding-marker { flex: none; width: 18px; height: 18px; margin-top: var(--space-0-5); border-radius: 50%; border: 2px solid var(--line-strong); }
.onboarding-step.is-done .onboarding-marker { border-color: var(--good); background: var(--good); }
.onboarding-step.is-progress .onboarding-marker { border-color: var(--brand); background: linear-gradient(90deg, var(--brand) 50%, transparent 50%); }
.photo-strip { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: var(--space-2); }
.photo-strip img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; border-radius: var(--radius-md); }
.photo-strip img.floor-plan { object-fit: contain; background: var(--surface-raised); border: 1px solid var(--line); }
.photo-strip-sm { grid-template-columns: repeat(auto-fill, minmax(88px, 1fr)); }

/* Inspection room: one card per item with one-tap Yes / No choices; cards sit side by side on wide screens. */
.room-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-4); }
.room-save-status { margin: 0; }
.room-items { display: grid; gap: var(--space-4); list-style: none; margin: 0; padding: 0; }
@media (min-width: 960px) { .room-items { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1440px) { .room-items { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.room-item { display: flex; flex-direction: column; gap: var(--space-4); }
.room-item-name { margin: 0; font-size: var(--text-lg); line-height: var(--leading-lg); }
.room-item-ratings { display: flex; flex-direction: column; gap: var(--space-2); }
.room-item-actions, .room-add-item { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--space-2) var(--space-4); }
.room-item-actions .field, .room-add-item .field { flex: 1 1 220px; }
/* R8-C9: the one segmented control (InspectionHtml.Segmented): the fieldset draws no box of its own, only the options do. */
.segmented { margin: 0; padding: 0; border: 0; min-width: 0; }
.segmented-legend { float: left; width: 104px; margin: 0; padding-top: var(--space-3); font-size: var(--text-md); line-height: var(--leading-md); font-weight: 600; }
.segmented-options {
  position: relative; display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); overflow: hidden;
  border: 1px solid var(--line-strong); border-radius: var(--radius-md);
}
.segmented-input { position: absolute; width: 1px; height: 1px; margin: 0; opacity: 0; }
.segmented-option {
  display: flex; align-items: center; justify-content: center; gap: var(--space-1); min-height: 44px;
  background: var(--surface-raised); color: var(--ink);
  font: var(--weight-semibold) var(--text-md)/var(--leading-md) var(--font-sans); cursor: pointer;
  padding: 0 var(--space-1); font-weight: var(--weight-regular);
}
.segmented-option + .segmented-input + .segmented-option { border-left: 1px solid var(--line-strong); }
.segmented-option:hover { background: var(--brand-soft); }
.segmented-input:checked + .segmented-yes { background: var(--brand); color: var(--on-brand); }
.segmented-input:checked + .segmented-no { background: var(--critical); color: var(--surface-raised); }
/* The tick means the choice doesn't rely on colour alone (WCAG 1.4.1). Its space is always kept, so choosing never resizes an option. */
.segmented-option::before { content: "\2713"; visibility: hidden; }
.segmented-input:checked + .segmented-option::before { visibility: visible; }
.segmented-input:focus-visible + .segmented-option { outline: 2px solid var(--ink); outline-offset: -4px; }
.segmented:disabled .segmented-option { cursor: default; }
.segmented:disabled .segmented-input:not(:checked) + .segmented-option { color: var(--ink-muted); }
/* A locked (shared or signed off) report: muted fills and no hover, so it doesn't look like it will respond. */
.segmented:disabled .segmented-options { border-style: dashed; }
.segmented:disabled .segmented-option, .segmented:disabled .segmented-option:hover { background: var(--surface); }
.segmented:disabled .segmented-input:checked + .segmented-yes { background: var(--brand-soft); color: var(--ink); }
.segmented:disabled .segmented-input:checked + .segmented-no { background: color-mix(in srgb, var(--critical) 14%, var(--surface)); color: var(--ink); }
.input:disabled { background: var(--surface); color: var(--ink); border-style: dashed; cursor: default; }
.room-locked { display: flex; gap: var(--space-2); align-items: flex-start; }
.room-locked svg { flex: none; margin-top: var(--space-0-5); }
.room-pager { display: flex; flex-wrap: wrap; gap: var(--space-2); justify-content: space-between; }
.room-pager-next { margin-left: auto; }
.link-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.link-list li { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.link-list a { overflow-wrap: anywhere; }
.photo-grid { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: var(--space-4); }
.photo-tile { display: flex; flex-direction: column; gap: var(--space-3); padding: var(--space-3); }
.photo-tile img, .home-card-photo { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; border-radius: var(--radius-md); }
.inline-edit { display: flex; gap: var(--space-2); }
.inline-edit .input { flex: 1; }
/* Address type-ahead (address-lookup.js); shown only once the script runs */
.address-checked { margin-left: var(--space-2); vertical-align: middle; }
.address-suggestions { max-height: 300px; overflow-y: auto; margin: 0; padding: var(--space-1) 0; list-style: none; border: 1px solid var(--line); border-radius: var(--radius-md); background: var(--surface-raised); box-shadow: var(--shadow-raised); }
.address-suggestions li { padding: var(--space-3); cursor: pointer; font-size: var(--text-base); line-height: var(--leading-base); }
.address-suggestions li:hover, .address-suggestions li[aria-selected="true"] { background: var(--brand-soft); color: var(--brand-strong); }
.room summary { display: flex; justify-content: space-between; align-items: baseline; cursor: pointer; list-style: none; }
.room summary::-webkit-details-marker { display: none; }
.room[open] summary { margin-bottom: var(--space-4); }
.choice-list-compact { grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); }
.choice-list-compact .choice { min-height: 44px; padding: var(--space-2) var(--space-3); align-items: center; }
/* Two choices side by side even on phones, e.g. I agree / I disagree on the signing page's comment step. */
.choice-list-pair { grid-template-columns: 1fr 1fr; }
.choice-list-pair .choice { min-height: 44px; padding: var(--space-2) var(--space-3); align-items: center; }
.plain-list { margin: 0; padding-left: var(--space-5); }

/* Compliance, rules and tasks */

.item-row { display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: var(--space-2) var(--space-4); }
.item-row > :first-child { flex: 1 1 280px; min-width: 0; }
.fold[open] > summary { margin-bottom: var(--space-4); }
/* .fold is for showing more information only. To open a form, use .fold-action: the summary looks and behaves like a
   button (add .fold-primary when it's the page's main action). */
.fold-action > summary {
  display: inline-flex; align-items: center; gap: var(--space-2); min-height: 44px; padding: 0 var(--space-4);
  border: 1px solid var(--line-strong); border-radius: var(--radius-md); background: var(--surface-raised); color: var(--ink);
  font: var(--weight-semibold) var(--text-md)/var(--leading-md) var(--font-sans); list-style: none; cursor: pointer;
}
.fold-action > summary::-webkit-details-marker { display: none; }
.fold-action > summary::before { content: "+"; font-size: var(--text-lg); line-height: 1; }
.fold-action[open] > summary::before { content: "\2212"; }
.fold-action > summary:hover { border-color: var(--brand); }
.fold-action.fold-primary > summary { border-color: transparent; background: var(--brand); color: var(--on-brand); }
.fold-action.fold-primary > summary:hover { background: var(--brand-strong); }
.fold-action[open] > summary { margin-bottom: var(--space-4); }
.rule-value { white-space: pre-line; }
.prose { max-width: 44.5em; line-height: 1.6; }
.prose h2, .prose h3 { margin-top: var(--space-6); }
.prose ul, .prose ol { padding-left: 1.4em; }
.prose code { font-size: 0.9em; background: var(--surface); padding: 1px var(--space-1); border-radius: var(--radius-sm); }
.task-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.task-list > li { padding: var(--space-4) 0; border-top: 1px solid var(--line); }
.task-list > li:first-child { border-top: 0; padding-top: 0; }
.mono { font-family: var(--font-mono); font-size: var(--text-sm); }
/* R10-J3: a monospace field (BSB, account number, codes, keys) keeps its field's size: 16px for .input, as iOS Safari
   zooms the page when a field under 16px is focused. Compact fields follow .input-sm (16px on touch screens too). */
:is(input, textarea, select).mono { font-size: var(--text-base); line-height: var(--leading-base); }
:is(input, textarea, select).input-sm.mono { font-size: var(--text-md); line-height: var(--leading-md); }
@media (pointer: coarse) { :is(input, textarea, select).input-sm.mono { font-size: var(--text-base); line-height: var(--leading-base); } }
/* Long unbroken values (hashes, ids, addresses) wrap inside their box instead of widening the page. */
.wrap-anywhere { overflow-wrap: anywhere; word-break: break-word; }
/* Raw settings and other preformatted text: keep the line breaks, wrap long lines, never scroll the page sideways. */
.pre-wrap { margin: 0; max-width: 100%; white-space: pre-wrap; overflow-wrap: anywhere; }

/* Legal holds (RUL-004) */

.badge-group { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.badge-link { text-decoration: none; display: inline-flex; align-items: center; min-height: 24px; }
.badge-link:hover { text-decoration: underline; }

/* Trade ratings (TRD-002) */

.stars { color: var(--warning); letter-spacing: 1px; white-space: nowrap; }
.stars-empty { color: var(--line-strong); }
.rating-line { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-2); }
.star-input { display: inline-flex; flex-direction: row-reverse; justify-content: flex-end; gap: var(--space-1); }
.star-input input { position: absolute; opacity: 0; width: 1px; height: 1px; }
/* R10-D6: each star is a 44px touch target (WCAG 2.2 SC 2.5.8 needs 24px); the hover fill only where hover is real, so it doesn't stick on touch. */
.star-input label { cursor: pointer; font-size: var(--text-2xl); line-height: 1; color: var(--line-strong); display: inline-grid; place-items: center; min-inline-size: 44px; min-block-size: 44px; }
.star-input input:checked ~ label { color: var(--warning); }
@media (hover: hover) { .star-input label:hover, .star-input label:hover ~ label { color: var(--warning); } }
.star-input input:focus-visible + label { outline: 2px solid var(--brand); outline-offset: 2px; border-radius: var(--radius-sm); }

/* Message templates (COM-008 to COM-010) */

.template-body { min-height: 280px; line-height: 22px; }
.template-source { margin: 0; padding: var(--space-4); background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-md); font-family: var(--font-mono); font-size: var(--text-sm); line-height: 20px; white-space: pre-wrap; overflow-wrap: anywhere; }
.template-preview { display: block; width: 100%; min-height: 520px; border: 1px solid var(--line); border-radius: var(--radius-md); background: var(--surface); }
.template-placeholders { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-2); }
.template-locked { margin: 0; padding-left: var(--space-5); }
.template-published { display: block; }

/* Electronic signing: the signer's pages, /verify and the owner's envelope pages */

.signing-shell { min-height: 100vh; display: flex; flex-direction: column; gap: var(--space-6); padding: var(--space-4); }
.signing-top, .signing-main { width: 100%; max-width: var(--page-max); margin: 0 auto; }
.signing-top { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); }
.signing-top img { display: block; height: 28px; width: auto; }
.signing-main { flex: 1; display: flex; flex-direction: column; gap: var(--space-4); }
.signing-main [hidden] { display: none; }
/* Renter pages (apply) reuse the signing shell with their own small header. */
.renter-tagline { margin-right: auto; }
.renter-nav a { display: inline-flex; align-items: center; min-height: 44px; font-weight: 600; }
.signing-steps { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-2); counter-reset: signing-step; }
.signing-steps li { counter-increment: signing-step; padding-top: var(--space-2); border-top: 3px solid var(--line); font-size: var(--text-sm); line-height: var(--leading-sm); color: var(--ink-muted); }
.signing-steps li::before { content: counter(signing-step) ". "; font-weight: 600; }
.signing-steps li.current { border-top-color: var(--brand); color: var(--ink); font-weight: 600; }
.signing-steps li.done { border-top-color: var(--good); }
/* Signing document viewer (pdf-viewer.js): PDF.js draws each page on a canvas; page boxes are sized by the script as you zoom. */
.pdf-viewer { display: flex; flex-direction: column; gap: var(--space-2); }
.pdf-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.pdf-zoom { min-width: 44px; padding-left: var(--space-2); padding-right: var(--space-2); }
.pdf-zoom-level { min-width: 48px; text-align: center; font-variant-numeric: tabular-nums; }
.pdf-page-count { margin-left: auto; }
.pdf-pages { height: 70vh; min-height: 420px; overflow: auto; -webkit-overflow-scrolling: touch; overscroll-behavior: contain; padding: var(--space-2); border: 1px solid var(--line); border-radius: var(--radius-md); background: var(--surface); }
.pdf-pages:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.pdf-page { margin: 0 auto var(--space-2); background: var(--surface-raised); box-shadow: var(--shadow-raised); position: relative; }
.pdf-page canvas { display: block; width: 100%; height: 100%; }
.pdf-fallback { margin: 0; padding: var(--space-4); border: 1px dashed var(--line-strong); border-radius: var(--radius-md); color: var(--ink-muted); }
.signature-pad { display: block; width: 100%; height: 180px; touch-action: none; cursor: crosshair; color: var(--ink); background: var(--surface); border: 1px dashed var(--line-strong); border-radius: var(--radius-md); }
/* Typed signatures preview in the script font they're stamped in on the signed copy (Dancing Script, SIL OFL 1.1). */
@font-face { font-family: "Leasii Signature"; src: url("/lib/fonts/dancing-script/DancingScript.ttf") format("truetype"); font-weight: 400 700; font-display: swap; }
.signature-typed { margin: 0; padding: var(--space-2) var(--space-4); min-height: 56px; font-family: "Leasii Signature", var(--font-display); font-size: 34px; line-height: 44px; overflow-wrap: anywhere; border-bottom: 1px solid var(--line-strong); }
.hash { overflow-wrap: anywhere; word-break: break-all; }
.signer-list > li { display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: var(--space-2) var(--space-4); }
.event-trail { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); font-size: var(--text-md); line-height: var(--leading-md); }
.event-trail li { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-0-5); padding-left: var(--space-4); border-left: 2px solid var(--line); }
@media (min-width: 600px) { .event-trail li { grid-template-columns: 190px minmax(0, 1fr); gap: var(--space-4); } }
.verify-result { border-left: 4px solid var(--good); }
.verify-result.verify-missing { border-left-color: var(--critical); }

/* Proof of ownership queue (ONB-003): submissions past the 24-hour target */

/* Identity checks (ONB-001): the return page shows the message for the check's current state */

[data-identity-state] .identity-state { display: none; }
[data-identity-state="Pending"] .identity-state[data-state="Pending"],
[data-identity-state="Verified"] .identity-state[data-state="Verified"],
[data-identity-state="InReview"] .identity-state[data-state="InReview"],
[data-identity-state="NameMismatch"] .identity-state[data-state="NameMismatch"],
[data-identity-state="Failed"] .identity-state[data-state="Failed"],
[data-identity-state="Expired"] .identity-state[data-state="Expired"] { display: flex; }

/* Staff legal holds: owners, their properties and tenancies to choose from, indented by level. */
.hold-targets, .hold-targets ul { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-2); }
.hold-targets ul { padding-left: var(--space-6); }

/* Reading an uploaded lease (INT-011): values to check against the lease, and ones that weren't found */
.field-source { overflow-wrap: anywhere; }
.field-read-check .input { border-color: var(--warning); box-shadow: 0 0 0 1px color-mix(in srgb, var(--warning) 45%, transparent); }
.field-read-check .field-source { color: color-mix(in srgb, var(--warning) 70%, var(--ink)); font-weight: 600; }
.field-read-missing .input { border-style: dashed; border-color: var(--line-strong); }

/* Legal documents: the update banner, the Markdown editor and comparing a draft with the version in force */
.legal-update { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); flex-wrap: wrap; margin-bottom: var(--space-4); }
.legal-body { min-height: 420px; line-height: 22px; }
.legal-changes summary { cursor: pointer; font-weight: 600; }
.legal-publish { border-top: 1px solid var(--line); padding-top: var(--space-4); }
.legal-diff { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-1); }
.legal-diff li { display: flex; gap: var(--space-2); padding: var(--space-1) var(--space-2); border-radius: var(--radius-sm); line-height: 20px; overflow-wrap: anywhere; }
.legal-diff-mark { flex: none; width: 1ch; font-family: var(--font-mono); }
.legal-diff-same { color: var(--ink-muted); }
.legal-diff-added { background: color-mix(in srgb, var(--good) 12%, var(--surface-raised)); }
.legal-diff-added ins { text-decoration: none; }
.legal-diff-removed { background: color-mix(in srgb, var(--critical) 10%, var(--surface-raised)); }

/* Phone top bar: logo with whose records you're working on, then at most three 44px buttons; the account menu */
.topbar-brand { flex: 1 1 auto; display: flex; flex-direction: column; gap: var(--space-0-5); min-width: 0; }
.topbar-brand > a { display: inline-flex; align-self: flex-start; }
.topbar-owner { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.account-btn { padding: 0; border-color: transparent; background: none; border-radius: 50%; cursor: pointer; }
.account-btn .avatar { width: 36px; height: 36px; font-size: var(--text-md); }
.account-summary { display: flex; align-items: center; min-width: 0; gap: var(--space-3); }
/* One line, cut with an ellipsis (the full address is in its title), never broken mid-word (UX-B19). */
.account-summary > .stack-2xs { min-width: 0; }
.account-summary .caption { display: block; overflow: visible; text-overflow: clip; white-space: normal; overflow-wrap: break-word; }
.account-signout { padding-top: var(--space-2); border-top: 1px solid var(--line); }
.more-menu .owner-switcher { padding: 0; }
.support-banner { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-2) var(--space-4); }
.auth-logo-link { display: inline-flex; }

/* Forms while they send (site.js): buttons are disabled and say what's happening. */
form[aria-busy="true"] :is(button, input[type="submit"]):disabled { cursor: progress; }

/* Linked cards (e.g. the dashboard's summary) */
a.card { color: var(--ink); text-decoration: none; }
a.card:hover, a.card:focus-visible, .card.card-link:hover { border-color: var(--brand); color: var(--ink); box-shadow: var(--shadow-hairline); }
@media (prefers-reduced-motion: no-preference) {
  a.card:active, .card.card-link:active { transform: translateY(1px); }
}
.stat-critical .stat-value { color: var(--critical); }

/* Dashboard: what needs you now, then what's coming up, in one card */
.attention + .attention { padding-top: var(--space-6); border-top: 1px solid var(--line); }
.attention-amount, .attention-amount a { font-weight: 700; color: var(--critical); }
/* A collapsed onboarding checklist: one line with a disclosure, so urgent items stay near the top on phones. */
.checklist-fold > summary .label { display: inline; }

/* Accessibility (WCAG 2.2 AA) */

/* 2.4.11: keep focused fields and links clear of the sticky marketing header and the phone tab bar when the browser scrolls to them. */
html:has(.mk-header) { scroll-padding-top: 84px; }
@media (max-width: 959px) { html:has(.tabbar) { scroll-padding-bottom: 96px; } }

/* 2.5.8: standalone small links (caption links like "Forgot password?" and "Receipt", links in label and value lists and
   table cells) are at least 24px tall. :where() keeps this from overriding any other display rule on those links. */
:where(a.caption, .ledger-row a, .details dd > a, .rule-list dd > a, td > a):not(:where(.btn, .row-title, [hidden])) { display: inline-block; padding-block: 3px; }

/* 2.3.3: people who ask for less motion get none. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; scroll-behavior: auto !important; }
}

/* ---- Non-owner screens: staff home, trade jobs, tenant rent, inspections, audit (live review fixes) ---- */

/* Tabs that scroll sideways fade at the edge that has more (site.js sets data-more), so hidden tabs aren't missed. */
:is(.tabs, .filter-bar)[data-more="end"] { mask-image: linear-gradient(to right, #000 calc(100% - 40px), transparent); }
:is(.tabs, .filter-bar)[data-more="start"] { mask-image: linear-gradient(to left, #000 calc(100% - 40px), transparent); }
:is(.tabs, .filter-bar)[data-more="both"] { mask-image: linear-gradient(to right, transparent, #000 40px, #000 calc(100% - 40px), transparent); }
/* R13-J1: on phones a filter bar scrolls sideways in one row, with the same edge fade as tabs, instead of wrapping. */
@media (max-width: 599px) {
  .filter-bar { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; }
  .filter-bar::-webkit-scrollbar { display: none; }
  .filter-bar a { flex: none; white-space: nowrap; }
}

/* Admin menu groups */
.nav-group { display: flex; flex-direction: column; gap: var(--space-0-5); }
.nav-group + .nav-group { margin-top: var(--space-2); }
.nav-subheading { padding: var(--space-1) var(--space-3) 0; font-size: var(--text-xs); line-height: var(--leading-xs); color: var(--ink-muted); }

/* Staff home: one card per queue with what's waiting */

/* A trade's jobs */
.job-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.job-card { display: flex; color: inherit; text-decoration: none; align-items: center; gap: var(--space-3); }
.job-card:hover { border-color: var(--brand); }
.job-card .label { overflow-wrap: anywhere; }

/* Copy buttons for the tenant's bank details and reference */
.copy-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.copy-value { overflow-wrap: anywhere; user-select: all; }
.copy-btn { min-height: 44px; }
.copy-btn.is-copied { border-color: var(--good); }
.copy-status { min-height: 20px; margin: 0; }

/* The tenant's rent record: one line per entry, the amount in words ("Paid $680") */
.ledger-list { list-style: none; margin: 0; padding: 0; }
.ledger-row {
  display: flex; align-items: flex-start; gap: var(--space-4); padding: var(--space-2) 0;
  border-bottom: 1px solid var(--line); justify-content: flex-start;
}
.ledger-row:last-child { border-bottom: 0; }
.ledger-row > .stack-xs { min-width: 0; overflow-wrap: anywhere; }
.ledger-amount { flex: none; font-weight: 600; white-space: nowrap; }
.ledger-amount-paid { color: var(--good); }

/* Rows that are whole links (at least 44px tall) */
.row-link { min-height: 44px; color: inherit; text-decoration: none; border-radius: var(--radius-md); }
.row-link:hover .label { color: var(--brand-strong); text-decoration: underline; }
.row-link-static { min-height: 44px; }

/* The tenant's item cards on a shared inspection report */
.tenant-item { padding: var(--space-4); border: 1px solid var(--line); border-radius: var(--radius-md); background: var(--surface-raised); }

/* Staff audit log: details wrap, and the raw record is behind "Details" */
.audit-record { overflow-wrap: anywhere; min-width: 0; }
/* "Details" reads like a .fold summary: brand text with + and −, not the browser's triangle. */
.audit-raw { margin: var(--space-1) 0 0; padding: var(--space-2); max-width: 100%; background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-sm);
  font-family: var(--font-mono); font-size: var(--text-xs); line-height: 18px; white-space: pre-wrap; overflow-wrap: anywhere; }

/* Error and No access pages: in the app layout or the website's, so they set their own width and spacing. */
/* CON-B4: in the app they follow the page flow, left-aligned at full width like every app page; on the website they sit
   in the centred reading column (.mk .error-page in marketing.css). */
.error-page { width: 100%; box-sizing: border-box; }
.error-page > p { max-width: 46em; }
.error-page h1 { overflow-wrap: anywhere; }
.error-page .code-inline { font-size: var(--text-sm); }
.error-help { padding-top: var(--space-4); border-top: 1px solid var(--line); }

/* Sections below are kept apart so each area's styles stay together. */

/* ==== Owner rent and actions (PO-01, PO-04, PO-07, PO-08) ==== */

/* A form launcher that's also a card: just the button while closed, the card around the form once open. */
.fold-action.card:not([open]) { padding: 0; border: 0; background: none; }

/* Owner's rent ledger: one list for every screen, grouped by month with a sticky month heading. Stacked rows on a phone
   (description and amount, then date and balance, then paid-to and More); columns once the ledger is wide enough (R01). */
.ledger { padding-top: 0; container: ledger / inline-size; }
.ledger-columns { display: none; }
.ledger-month-heading {
  position: sticky; top: 0; z-index: 1; margin: 0; padding: var(--space-3) 0 var(--space-2);
  background: var(--surface-raised); border-bottom: 1px solid var(--line);
  font: var(--weight-semibold) var(--text-md)/var(--leading-md) var(--font-sans);
}
.ledger-line {
  display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: var(--space-1) var(--space-4); align-items: start;
  grid-template-areas: "desc amount" "date balance" "paidto more";
  padding-block: var(--space-3);
}
.ledger-line > .ledger-desc { grid-area: desc; min-width: 0; overflow-wrap: anywhere; }
.ledger-line > .ledger-amount { grid-area: amount; text-align: right; }
/* UX-A21: rent due and paid in their own columns. On a phone a line has one or the other, in the amount spot. Paid is in
   the good tone at regular weight, with no minus sign. */
.ledger-line > :is(.ledger-due, .ledger-paid) { grid-area: amount; text-align: right; white-space: nowrap; font-size: var(--text-base); line-height: var(--leading-base); font-weight: var(--weight-semibold); }
.ledger-line > :is(.ledger-due, .ledger-paid):empty { display: none; }
.ledger-paid { color: var(--good); }
.ledger-line > .ledger-date { grid-area: date; }
.ledger-line > .ledger-balance { grid-area: balance; text-align: right; }
.ledger-line > .ledger-paid-to { grid-area: paidto; }
.ledger-line > .row-menu { grid-area: more; justify-self: end; }
.ledger-date, .ledger-balance, .ledger-paid-to { font-size: var(--text-sm); line-height: var(--leading-sm); color: var(--ink-muted); white-space: nowrap; }
.ledger-earlier { padding-top: var(--space-4); }
.ledger-earlier[open] > summary { margin-bottom: 0; }
/* R14 (4.4): every line starts with a small tile (receipt, rent due, credit, charge); the description reads at 16/24, the amounts right-aligned. */
.ledger-desc { display: flex; align-items: flex-start; gap: var(--space-3); }
.ledger-tile { width: 32px; height: 32px; border-radius: var(--radius-md); }
.ledger-tile > svg { width: 16px; height: 16px; }
.ledger-text { min-width: 0; font-size: var(--text-base); line-height: var(--leading-base); }
.ledger-text > .caption { font-size: var(--text-md); line-height: var(--leading-md); }
.rent-steps-link { margin-top: calc(-1 * var(--space-3)); }

/* R14 (4.4): reminders, payment plan and usage charges folded into one card, closed until opened (or until one of their forms was sent back). */
.rent-settings { padding: 0; }
.rent-settings-summary {
  position: relative; display: flex; flex-direction: column; gap: var(--space-0-5); min-height: 44px; padding: var(--space-4) var(--space-8) var(--space-4) var(--space-4);
  list-style: none; cursor: pointer; border-radius: var(--radius-lg);
}
.rent-settings-summary::-webkit-details-marker { display: none; }
.rent-settings-summary::after {
  content: ""; position: absolute; right: var(--space-5); top: 50%; width: 8px; height: 8px; margin-top: -6px;
  border-right: 2px solid var(--ink-muted); border-bottom: 2px solid var(--ink-muted); transform: rotate(45deg);
}
.rent-settings[open] > .rent-settings-summary::after { margin-top: -2px; transform: rotate(-135deg); }
.rent-settings-summary:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }
.rent-settings-title { font-size: var(--text-lg); line-height: var(--leading-lg); font-weight: var(--weight-semibold); }
.rent-settings > .rent-tools { display: grid; grid-template-columns: minmax(0, 1fr); border-top: 1px solid var(--line); }
.rent-settings .rent-tools > .card { padding: var(--space-4); border: 0; border-radius: 0; box-shadow: none; background: none; }
.rent-settings .rent-tools > .card + .card { border-top: 1px solid var(--line); }
@media (min-width: 960px) {
  .rent-settings > .rent-tools { grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: start; }
  .rent-settings .rent-tools > .card { padding: var(--space-5); }
  .rent-settings .rent-tools > .card + .card { border-top: 0; border-left: 1px solid var(--line); }
  .rent-settings .rent-tools > #reminders { grid-column: 1 / -1; border-bottom: 1px solid var(--line); }
  .rent-settings .rent-tools > #reminders + .card { border-left: 0; }
}

/* The per-line "…" menu (_RowMenu): receipts and reversing, out of the way. Reverse still asks first. */

/* R01 (round 4): columns depend on the ledger's own width, not the viewport, because the 248px sidebar takes a big share
   of a small laptop. Below 48rem the ledger stays stacked as on a phone; from 48rem the columns show with paid-to tucked
   under the description; from 60rem paid-to gets its own column. The description never drops below 14rem, so it can't
   wrap one letter per line. Content widths with the sidebar: 1024px → about 654px (stacked), 1180px → about 810px
   (columns, no paid-to column, description ≈ 270px), 1440px → about 1070px (all columns, description ≈ 350px). */
@container ledger (min-width: 48rem) {
  .ledger-columns, .ledger-line {
    display: grid; grid-template-columns: 7.5rem minmax(14rem, 1fr) 6.5rem 6.5rem 7rem 2.5rem; gap: 0 var(--space-3); align-items: baseline;
    grid-template-areas: "date desc due paid balance more" ". paidto . . . .";
  }
  .ledger-columns > .ledger-col-paid-to { display: none; }
  .ledger-line > .ledger-due { grid-area: due; }
  .ledger-line > .ledger-paid { grid-area: paid; }
  .ledger-columns { padding: var(--space-3) 0 var(--space-2); font-size: var(--text-sm); line-height: var(--leading-sm); font-weight: var(--weight-semibold); color: var(--ink-muted); }
  .ledger-columns > .num { text-align: right; }
  .ledger-line { padding-block: var(--space-2); }
  .ledger-line .row-menu-button { width: 36px; height: 36px; }
  .ledger-date, .ledger-balance { font-size: var(--text-md); line-height: var(--leading-md); color: var(--ink); }
  /* The column headings name these, so their inline labels are for screen readers only. */
  .ledger-balance .ledger-label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
}
@container ledger (min-width: 60rem) {
  .ledger-columns, .ledger-line {
    grid-template-columns: 7.5rem minmax(14rem, 1fr) 6.5rem 6.5rem 7rem 7.5rem 2.5rem; gap: var(--space-4);
    grid-template-areas: "date desc due paid balance paidto more";
  }
  .ledger-columns > .ledger-col-paid-to { display: block; }
  .ledger-paid-to { font-size: var(--text-md); line-height: var(--leading-md); color: var(--ink); }
  .ledger-paid-to .ledger-label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
}

/* Compliance: one compact row per check. Status, then the name and dates, then its one action. */
.compliance-list { list-style: none; margin: 0; padding-block: 0; }
.compliance-row {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: var(--space-3) var(--space-4); align-items: start;
  padding-block: var(--space-4); border-top: 1px solid var(--line);
}
.compliance-row:first-child { border-top: 0; }
.compliance-status { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-1); }
.compliance-name { font: var(--weight-semibold) var(--text-base)/var(--leading-base) var(--font-sans); overflow-wrap: anywhere; }
.compliance-more[open] > summary { margin-bottom: var(--space-3); }
/* Once open, the record form takes the whole row. */
.compliance-row > .compliance-record[open] { grid-column: 1 / -1; }
@media (max-width: 599px) {
  .compliance-row { grid-template-columns: minmax(0, 1fr); }
  .compliance-status { flex-direction: row; flex-wrap: wrap; }
}

/* ==== Owner navigation and layout (PO-05, PO-06, PO-10) ==== */

/* R13-J1: a section's pages (_SubNav) are underline tabs (.tabs), the same as a property's tabs and the rules library:
   tabs change the page, a filter bar (.filter-bar) changes the list. */

/* Readable line length for page introductions (PO-06). */

/* Wide pages (ViewData["Wide"] = true in the view): table-heavy screens may use the full 1440px. */
.content-inner-wide { max-width: none; }

/* Main column and aside, side by side from 1200px (e.g. the dashboard: what needs doing, then the numbers and setup). */
.layout-split { display: grid; gap: var(--space-6); align-items: start; }
@media (min-width: 1200px) { .layout-split { grid-template-columns: minmax(0, 2fr) minmax(280px, 1fr); } }

/* Settings (_SettingsLayout): a grouped side menu from 1280px; below that a "‹ Settings" breadcrumb back to the /settings
   list. The header is the shared _PageHeader with "Settings" as its eyebrow at every width (CON-A1). R9-B24: from 960px the
   app's 248px sidebar is shown too, so the menu waits for 1280px; on an iPad in landscape (1080px) two side menus left
   Billing's plans table 466px and scrolling sideways. */
.layout-settings { display: grid; gap: var(--space-6); align-items: start; }
.settings-nav { display: none; }
.settings-back { align-self: flex-start; }
.settings-nav ul { display: flex; flex-direction: column; gap: var(--space-1); margin: 0; padding: 0; list-style: none; }
.settings-nav-group + .settings-nav-group { margin-top: var(--space-6); }
.settings-nav-heading { margin: 0 0 var(--space-2); padding-inline: var(--space-3); color: var(--ink-muted); font-size: var(--text-xs); line-height: var(--leading-xs); font-weight: var(--weight-semibold); text-transform: uppercase; letter-spacing: 0.04em; }
.settings-nav a {
  display: flex; align-items: center; min-height: 40px; padding: var(--space-2) var(--space-3); border-radius: var(--radius-md);
  color: var(--ink); font-size: var(--text-md); line-height: var(--leading-md); text-decoration: none;
}
.settings-nav a:hover { background: var(--surface); }
.settings-nav a[aria-current="page"] { background: var(--brand-soft); color: var(--brand-strong); font-weight: var(--weight-semibold); }
@media (min-width: 1200px) {
  .layout-settings { grid-template-columns: 220px minmax(0, 1fr); }
  .settings-nav { display: block; position: sticky; top: var(--space-8); }
  .settings-back { display: none; }
}

/* The /settings list: one 56px row per page, with a chevron. */
.settings-list { margin: 0; padding: 0; list-style: none; overflow: hidden; }
.settings-list li + li { border-top: 1px solid var(--line); }
.settings-list a {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); min-height: 56px; padding-inline: var(--space-4); padding-block: var(--space-3);
  color: var(--ink); font-size: var(--text-base); line-height: var(--leading-base); text-decoration: none;
}
.settings-list a:hover { background: var(--surface); }
.settings-list svg { flex: none; color: var(--ink-muted); }

/* ==== Staff screens (RO-07, RO-08, RO-09) ==== */

/* Toolbars: a row of filters above a list, or a short form on one line (search, a select, a count; a decision and its
   notes). Fields sit side by side and wrap on phones; buttons match .input-sm. */
.toolbar { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--space-2) var(--space-3); }
.toolbar > .btn { min-height: 40px; }
.toolbar > input[type="search"] { flex: 1 1 220px; max-width: 360px; }
.toolbar-count { margin-left: auto; align-self: center; }

/* Data tables for staff (RO-07). .table-compact: one-line rows and a header that stays in view while scrolling.
   .table-hover: the row under the pointer is shaded. a.row-title: the record's name, and the whole row opens it. */
.table-compact th { padding: var(--space-2) var(--space-3); position: sticky; top: 0; z-index: var(--z-sticky);
  background: var(--surface-raised); border-bottom: 0; box-shadow: inset 0 -1px 0 var(--line); }
.table-compact td { padding: calc(var(--space-1) * 1.5) var(--space-3); }
/* .table-wrap scrolls sideways, which would pin the header to the wrapper instead of the page. Lists of records fit
   the width from tablet up, so there they don't scroll and the header sticks; on phones they're cards with no header. */
@media (min-width: 600px) { .table-wrap:has(> .table-compact.table-hover) { overflow: visible; } }
.table-hover tbody tr:hover { background: var(--surface); }
.table tr[hidden] { display: none; }
.table a.row-title { color: var(--ink); text-decoration: none; font-weight: var(--weight-semibold); }
.table tr:hover a.row-title, .table a.row-title:focus-visible { color: var(--brand-strong); text-decoration: underline; }
.table tr:has(a.row-title) { position: relative; }
.table a.row-title::after { content: ""; position: absolute; inset: 0; }
/* Other links and controls in the row stay clickable above the row's link. */
.table tr:has(a.row-title) :is(a:not(.row-title), button, input, select, textarea, label, summary) { position: relative; z-index: 1; }
@media (max-width: 599px) { .table-cards td.col-end { text-align: left; } }

/* Audit log (RO-08): one filter row, folded behind "Filter" on phones (R11-I2: by CSS, then audit-log.js); times on one line. */
.audit-id { flex: 1 1 280px; max-width: 420px; }
@media (min-width: 600px) {
  .audit-filter-fold > summary { display: none; }
  .audit-when { white-space: nowrap; }
}

/* Trust and safety queue (RO-09): subject, status and age on one line. */
.queue-head { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-1) var(--space-2); }
.queue-head > h3 { min-width: 0; }

/* ==== Tenant and trade (RO-10, RO-11) ==== */

/* Status banner tones: one sentence saying where something is up to. */
.banner > p { margin: 0; }
.banner-good { background: var(--good-soft); border-left: 4px solid var(--good); }
.banner-warn { background: var(--warning-soft); border-left: 4px solid var(--warning); }
.banner-info { background: var(--info-soft); border-left: 4px solid var(--info); }

/* Action bar: the page's next step. On phones and tablets it sticks to the bottom of the screen (above the tab bar in
   the app), so it's in thumb reach on a long page; on a computer it's an ordinary row of buttons. */
.action-bar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
@media (max-width: 959px) {
  .action-bar {
    position: sticky; bottom: 0; z-index: var(--z-sticky);
    margin-inline: calc(-1 * var(--space-4));
    padding: var(--space-3) var(--space-4) calc(var(--space-3) + env(safe-area-inset-bottom));
    background: var(--surface-raised); border-top: 1px solid var(--line);
  }
  .action-bar > .btn-primary { flex: 1 1 auto; }
  /* In the app the tab bar is at the bottom (8px + 48px tab + 24px padding + 1px border), so the bar sits on top of it. */
  .app .action-bar { bottom: calc(var(--space-2) + 48px + var(--space-6) + 1px); padding-bottom: var(--space-3); }
  /* 2.4.11: fields the browser scrolls to stay clear of the bar (and the tab bar under it). */
  html:has(.app .action-bar) { scroll-padding-bottom: 168px; }
  html:has(.signing-shell .action-bar) { scroll-padding-bottom: 96px; }
}

/* RO-10: the tenant's inspection, with progress across rooms and a tick on each finished room. */
.room-progress { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-4); }
/* A native <progress> (value and max in the markup, so no inline widths), drawn like the .progress bar. */
progress.progress { -webkit-appearance: none; appearance: none; display: block; min-width: 0; color: var(--brand); }
progress.progress::-webkit-progress-bar { background: var(--surface); }
progress.progress::-webkit-progress-value { background: var(--brand); }
progress.progress::-moz-progress-bar { background: var(--brand); }
.tabs a.is-done::after { content: "\2713"; content: "\2713" / ""; margin-left: var(--space-1); color: var(--good); }

/* RO-11: the trade's job page. Call and Maps side by side, the next step full width under them. */
.action-bar-trade { display: grid; grid-template-columns: minmax(0, 1fr); }
.action-bar-tools { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: var(--space-2); }
/* On a computer, the address has "Open in Maps" and the contact card shows the number. */
@media (min-width: 960px) { .action-bar-trade { display: none; } }
/* tel: links only help on a phone: computers show the number as text. */
.phone-text { display: none; }
@media (min-width: 960px) {
  .phone-call { display: none; }
  .phone-text { display: inline; }
}
.invoice-row { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-2); }

/* ==== Round 2: components and states ==== */

/* PO-11, RO-38: every control answers hover, press and focus. Colour changes ease in; a pressed button moves down 1px
   (not for people who ask for less motion). Disabled controls don't react to hover. */
.btn, .icon-btn, .fold-action > summary, .filter-bar a, .nav-item, .tabs a, .settings-nav a, .settings-list a, .input, .choice, a.card {
  transition: background-color var(--duration-fast) var(--ease-standard), border-color var(--duration-fast) var(--ease-standard),
    color var(--duration-fast) var(--ease-standard), box-shadow var(--duration-fast) var(--ease-standard);
}
.btn-secondary:hover, .fold-action:not(.fold-primary) > summary:hover { background: var(--surface); }
.icon-btn:hover { border-color: var(--line-strong); background: var(--surface); color: var(--ink); }
.btn-primary:active, .fold-action.fold-primary > summary:active { background: color-mix(in srgb, var(--brand-strong) 85%, var(--ink)); }
.btn-secondary:active, .fold-action:not(.fold-primary) > summary:active, .icon-btn:active { border-color: var(--brand); background: var(--brand-soft); }
.btn-danger:active { background: color-mix(in srgb, var(--critical) 75%, var(--ink)); }
.btn-critical-outline:active { background: color-mix(in srgb, var(--critical) 20%, var(--surface-raised)); }
.btn-key:active { background: color-mix(in srgb, var(--key) 78%, var(--on-key)); }
.btn-text:active, .btn-text-danger:active { background: var(--surface); }
@media (prefers-reduced-motion: no-preference) {
  :is(.btn, .icon-btn, .fold-action > summary):active:not(:disabled, [aria-disabled="true"]) { transform: translateY(1px); }
}
.btn-primary:is(:disabled, [aria-disabled="true"]):hover { background: var(--brand); }
.btn-secondary:is(:disabled, [aria-disabled="true"]):hover { border-color: var(--line-strong); background: var(--surface-raised); }
.btn-danger:is(:disabled, [aria-disabled="true"]):hover { background: var(--critical); }
.btn-text:is(:disabled, [aria-disabled="true"]):hover { color: var(--brand); text-decoration: none; }
.input:hover:not(:focus, :disabled, .input-validation-error, [aria-invalid="true"], [data-client-invalid]) { border-color: var(--ink-muted); }
/* The button that sent a form shows a spinner beside "Saving…" (site.js sets aria-busy on it). */
.btn[aria-busy="true"] { opacity: .85; }
.btn[aria-busy="true"]::before {
  content: ""; flex: none; width: 16px; height: 16px; border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%;
  animation: spin .8s linear infinite;
}

/* PO-38, PO-34, RO-22: one spinner (16px), for a short wait inside a line of text or a panel that is
   filling in (reading a lease, fetching suggestions). .identity-spinner and .reading-spinner are old names for
   .spinner. People who ask for less motion get a still ring. */
.spinner, .identity-spinner, .reading-spinner {
  display: inline-block; flex: none; width: 16px; height: 16px; margin-right: var(--space-2); vertical-align: -3px;
  border: 2px solid var(--line-strong); border-top-color: var(--brand); border-radius: 50%; animation: spin .9s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .spinner, .identity-spinner, .reading-spinner, .btn[aria-busy="true"]::before { animation: none; } }

/* PO-16: card titles. Page title (h1), section heading (h2) and card title are three steps: in a list of cards, use
   <h2 class="card-title"> (or h3) so the cards don't all shout at section size. */
.card-title { font-family: var(--font-sans); font-size: var(--text-lg); line-height: var(--leading-lg); font-weight: var(--weight-semibold); }

/* PO-19: one label and value list. <dl class="dl"> with <dt>/<dd> pairs, either flat or each pair in a <div>. Phones:
   the label sits over its value. From 600px: a label column as wide as the longest label (up to 16rem), then values.
   .details, .rule-list and .read-extras are older names for the same thing. */
:is(.dl, .details, .rule-list, .read-extras) { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-0-5); margin: 0; }
:is(.dl, .details, .rule-list, .read-extras) > div { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-0-5); }
:is(.dl, .details, .rule-list, .read-extras) dt { color: var(--ink-muted); font-size: var(--text-md); line-height: var(--leading-md); }
:is(.dl, .details, .rule-list, .read-extras) dd { margin: 0; overflow-wrap: anywhere; }
:is(.dl, .details, .rule-list, .read-extras) > dd + dt, :is(.dl, .details, .rule-list, .read-extras) > div + div { margin-top: var(--space-2); }
@media (min-width: 600px) {
  :is(.dl, .details, .rule-list, .read-extras) { grid-template-columns: fit-content(16rem) minmax(0, 1fr); gap: var(--space-2) var(--space-4); }
  :is(.dl, .details, .rule-list, .read-extras) > div { grid-column: 1 / -1; grid-template-columns: fit-content(16rem) minmax(0, 1fr); grid-template-columns: subgrid; gap: var(--space-4); }
  :is(.dl, .details, .rule-list, .read-extras) > dd + dt, :is(.dl, .details, .rule-list, .read-extras) > div + div { margin-top: 0; }
}

/* PO-28, RO-27: one disclosure. A chevron that turns down when open, at least a 24px target (44px for .fold on phones).
   .fold and .audit-details are brand-coloured "show more" links; .disclosure is the plain version. Form launchers keep
   .fold-action (a button with + and −). */
:is(.disclosure, .fold, .checklist-fold, .audit-details, .legal-changes, .email-more) > summary {
  --summary-pad: var(--space-0-5);
  position: relative; min-height: 24px; padding-block: var(--summary-pad); padding-inline-start: calc(var(--space-4) + var(--space-1-5));
  list-style: none; cursor: pointer;
}
:is(.disclosure, .fold, .checklist-fold, .audit-details, .legal-changes, .email-more) > summary::-webkit-details-marker { display: none; }
:is(.disclosure, .fold, .checklist-fold, .audit-details, .legal-changes, .email-more) > summary::before {
  content: ""; position: absolute; left: 0; top: calc(var(--summary-pad) + 2px); top: calc(var(--summary-pad) + (1lh - 16px) / 2); width: 16px; height: 16px;
  background: currentColor; transition: transform var(--duration-fast) var(--ease-standard);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m9 6 6 6-6 6'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m9 6 6 6-6 6'/%3E%3C/svg%3E") center / contain no-repeat;
}
:is(.disclosure, .fold, .checklist-fold, .audit-details, .legal-changes, .email-more)[open] > summary::before { transform: rotate(90deg); }
:is(.fold, .audit-details) > summary { color: var(--brand); font-size: var(--text-md); line-height: var(--leading-md); font-weight: var(--weight-semibold); }
:is(.fold, .audit-details) > summary:hover { color: var(--brand-strong); }
@media (max-width: 599px) { .fold > summary { --summary-pad: var(--space-3); } }

/* PO-46, RO-27: target sizes. A back link ("← Properties", the first link in a page's header stack) only covers its
   words, and is 44px tall on phones and tablets. Standalone small links are at least 24px tall. Tabs are 44px on phones. */
:is(.stack, .stack-lg, .stack-sm, .stack-xs, .stack-2xs) > a.caption:first-child, a.back-link { align-self: flex-start; }
@media (max-width: 959px) {
  :is(.stack, .stack-lg, .stack-sm, .stack-xs, .stack-2xs) > a.caption:first-child, a.back-link { display: inline-flex; align-items: center; min-height: 44px; padding-block: 0; }
}
@media (max-width: 599px) { .tabs a { display: inline-flex; align-items: center; min-height: 44px; } }

/* PO-25, RO-26: see .bell-count. PO-45: notifications are an inbox. The whole row opens the notification; the title is
   ink, underlined on hover and focus; the time sits on the right from 600px; "I'm on it" stays its own button. */
.notification-list .list-row {
  position: relative; display: grid; grid-template-columns: auto minmax(0, 1fr); grid-template-areas: "dot main" ". time" ". action";
  align-items: start; gap: var(--space-1) var(--space-3); padding: var(--space-3) var(--space-2);
}
.notification-list .list-row:first-child { padding-top: var(--space-3); }
.notification-list .dot { grid-area: dot; margin-top: var(--space-1-5); }
.notification-list .list-main { grid-area: main; }
.notification-time { grid-area: time; }
.notification-action { grid-area: action; position: relative; z-index: 1; }
.notification-link { color: var(--ink); text-decoration: none; }
.notification-link::after { content: ""; position: absolute; inset: 0; }
.notification-link:hover, .notification-link:focus-visible { color: var(--ink); text-decoration: underline; }
.notification-list .list-row:has(.notification-link):hover { background: var(--surface); }
@media (min-width: 600px) {
  .notification-list .list-row { grid-template-columns: auto minmax(0, 1fr) auto auto; grid-template-areas: "dot main time action"; }
  .notification-time { text-align: right; white-space: nowrap; }
}

/* RO-14: staff tools use a smaller page title, so the first screen shows the work, not the heading. Keyed to .app-wide, which
   _Layout sets for Admin pages and for every page a staff account opens (/help, /settings and /notifications included), so
   a staff user never switches between two sizes (CON-C1). The title size itself is under "Consistency: staff". */
@media (min-width: 960px) {
  .app-wide .content { padding-top: var(--space-6); }
}

/* PO-37: printing (ledgers for tribunals and accountants, reports, notices, activity). Just the page's content, black
   on white in the light palette whatever the screen theme, no navigation or buttons, rows kept whole across pages.
   Literal colours on purpose: paper is always white. .no-print hides anything else that's only for the screen. */
@media print {
  :root, :root[data-theme], :root:not([data-theme="light"]) {
    color-scheme: light;
    --surface: #ffffff; --surface-raised: #ffffff; --line: #cccccc; --line-strong: #888888; --ink: #000000; --ink-muted: #444444;
    --brand: #0e6b57; --brand-strong: #0a5244; --brand-soft: #dcefe8; --on-brand: #ffffff;
    --good: #1d7343; --warning: #8f5400; --critical: #b42318; --info: #1f5ea8;
    --good-soft: #e4eee8; --warning-soft: #efe7db; --critical-soft: #f8e9e8; --info-soft: #e9eff6; --neutral-soft: #edefee;
  }
  .sidebar, .topbar, .tabbar, .skip-link, .action-bar, .actions, .row-actions, .filter-bar, .toolbar, .tabs, .settings-nav,
  .btn, .icon-btn, .fold-action, .row-menu, .fold:not([open]), .modal, .no-print { display: none !important; }
  body { background: #ffffff; color: #000000; font-size: 11pt; line-height: 1.4; }
  .app { display: block; min-height: 0; }
  .content { padding: 0; }
  .content-inner, .content-inner-wide, .narrow { max-width: none; }
  .card { border: 0; padding: 0; box-shadow: none; }
  h1, h2, h3, .ledger-month-heading { break-after: avoid; }
  .list-row, .ledger-line, .ledger-row, .compliance-row, .table tr, .alert, .event-trail li { break-inside: avoid; }
  .ledger-month-heading, .table-compact th { position: static; }
  .table-wrap { overflow: visible; }
  .table th, .table td { padding: 4pt 6pt; }
  thead { display: table-header-group; }
  a { color: inherit; }
  .content a[href^="http"]::after { content: " (" attr(href) ")"; font-size: 9pt; color: #444444; }
}

/* ==== Round 2: app shell, auth and dark mode ==== */

/* Notifications and Help follow the main menu with a thin divider, not a gap; the account button stays at the bottom. */
.sidebar > .nav-secondary { margin-top: calc(var(--space-3) * -1); padding-top: var(--space-1); border-top: 1px solid var(--line); }
/* Staff without an owner role: their queues come straight after Home, without an "Admin" heading or divider (RO-25). */
.nav-admin.nav-admin-only { padding-top: 0; border-top: 0; }

/* Logos: one image per theme, following the same switches as tokens.css, so an appearance chosen in the account menu
   wins over the device's setting (RO-37). Only the hidden one is forced, so each logo keeps its own layout rules. */
@media not all and (prefers-color-scheme: dark) { :root:not([data-theme="dark"]) .logo-dark { display: none !important; } }
@media (prefers-color-scheme: dark) {
  :root[data-theme="light"] .logo-dark { display: none !important; }
  :root:not([data-theme="light"]) .logo-light { display: none !important; }
}
:root[data-theme="dark"] .logo-light { display: none !important; }
.topbar-brand > a img { display: block; height: 28px; width: auto; }

/* Read-only access, said calmly where whose records you're working on is shown (PO-42). */
.view-only { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-1); margin: 0; padding: 0 var(--space-3); }
.view-only-badge { align-self: flex-start; }

/* Account menu: a bottom sheet on phones; on desktop a panel just above the sidebar's account button (PO-26). */
.theme-picker { display: flex; flex-direction: column; gap: var(--space-2); }
.theme-picker legend { margin-bottom: var(--space-2); padding: 0; }
.theme-options { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-1); padding: var(--space-1); border: 1px solid var(--line); border-radius: var(--radius-md); background: var(--surface); }
.theme-option { position: relative; display: flex; align-items: center; justify-content: center; min-height: 40px; border-radius: var(--radius-sm); color: var(--ink-muted); font-size: var(--text-md); font-weight: var(--weight-semibold); cursor: pointer; }
.theme-option input { position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0; }
.theme-option:has(input:checked) { background: var(--surface-raised); color: var(--ink); box-shadow: var(--shadow-raised); }
.theme-option:has(input:focus-visible) { outline: 2px solid var(--brand); outline-offset: 2px; }
@media (min-width: 960px) {
  .modal.account-menu { width: 300px; margin: auto auto var(--space-16) var(--space-4); padding: var(--space-5); box-shadow: var(--shadow-overlay); }
  .modal.account-menu::backdrop { background: transparent; }
  /* The sidebar already has the owner switcher. */
  .account-menu .owner-switcher { display: none; }
}

/* Auth: on phones the sign-in page's "New to Leasii?" link is under the form, so the top bar doesn't repeat it (PO-48). */
@media (max-width: 599px) { .auth-switch-wide { display: none; } }

/* ==== Round 2: owner property pages ==== */

/* PO-17: breadcrumbs (_Breadcrumbs) instead of stacked "← X" links. On a phone only the parent shows, as a 44px "‹ Tenancy". */
.breadcrumbs { align-self: flex-start; max-width: 100%; }
.breadcrumbs ol { display: flex; flex-wrap: wrap; align-items: center; margin: 0; padding: 0; list-style: none; font-size: var(--text-sm); line-height: var(--leading-sm); }
.breadcrumbs li { display: inline-flex; align-items: center; min-width: 0; }
.breadcrumbs li + li::before { content: "/"; content: "/" / ""; margin: 0 var(--space-2); color: var(--ink-muted); }
.breadcrumbs a { display: inline-flex; align-items: center; min-height: 24px; color: var(--ink-muted); text-decoration: underline; text-underline-offset: 2px; overflow-wrap: anywhere; }
.breadcrumbs a:hover { color: var(--ink); }
/* R9-E20: touch targets are at least 44px wide as well as tall (the iPad "Repairs" crumb was 43px). */
@media (pointer: coarse) { .breadcrumbs a { min-width: 44px; min-height: 44px; justify-content: center; } }
@media (max-width: 599px) {
  .breadcrumbs li:not(:last-child) { display: none; }
  .breadcrumbs li + li::before { content: none; }
  .breadcrumbs a { min-height: 44px; font-size: var(--text-md); line-height: var(--leading-md); font-weight: var(--weight-semibold); color: var(--brand-strong); text-decoration: none; }
  .breadcrumbs a::before { content: "\2039"; content: "\2039" / ""; margin-right: var(--space-1-5); font-size: var(--text-lg); }
}

/* PO-18: a lighter property header. The street is the h1 with the suburb under it; pages below the tabs (rent ledger,
   notices) put their own title in the h1 and the street above it, and drop the tab strip. Tabs stick to the top on phones. */
.property-header { display: flex; flex-direction: column; gap: var(--space-1); }
.property-header > .page-header { align-items: flex-start; }
@media (max-width: 959px) {
  .property-tabs { position: sticky; top: 0; z-index: var(--z-sticky); background-color: var(--surface); }
}

/* PO-21: a row's "…" menu (_RowMenu) for secondary and risky actions, so a list doesn't repeat red buttons. */
.row-menu { position: relative; display: inline-block; }
.row-menu > summary { list-style: none; cursor: pointer; }
.row-menu > summary::-webkit-details-marker { display: none; }
.row-menu[open] > summary { border-color: var(--brand); background: var(--brand-soft); }
.row-menu-panel {
  position: absolute; right: 0; top: calc(100% + var(--space-1)); z-index: var(--z-overlay); min-width: 200px;
  margin: 0; padding: var(--space-1); list-style: none; background: var(--surface-raised);
  border: 1px solid var(--line); border-radius: var(--radius-md); box-shadow: var(--shadow-overlay);
  max-width: calc(100vw - 2 * var(--space-4));
}
.row-menu-panel form { margin: 0; }
.row-menu-item {
  display: flex; align-items: center; width: 100%; min-height: 44px; padding: 0 var(--space-3); border: 0; border-radius: var(--radius-sm);
  background: none; color: var(--ink); font: var(--weight-regular) var(--text-md)/var(--leading-md) var(--font-sans);
  text-align: left; text-decoration: none; white-space: nowrap; cursor: pointer;
}
.row-menu-item:hover, .row-menu-item:focus-visible { background: var(--surface); color: var(--ink); }
.row-menu-danger, .row-menu-danger:hover { color: var(--critical); }
.row-menu-danger:hover { background: var(--critical-soft); }

/* PO-23: a locked inspection room reads as a record: one row per item with a badge per check, its comment and photos.
   A shared room shows where you are ("Room 2 of 10"); on phones the room pager sits above the tab bar. */
.room-summary { margin: 0; padding: 0; list-style: none; }
.room-summary-row { display: flex; flex-direction: column; gap: var(--space-2); padding-block: var(--space-4); border-top: 1px solid var(--line); }
.room-summary-row:first-child { padding-top: 0; border-top: 0; }
.room-summary-row:last-child { padding-bottom: 0; }
.room-summary-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: var(--space-2) var(--space-4); }
.room-summary-name { margin: 0; font-size: var(--text-base); line-height: var(--leading-base); }
.room-summary-checks { display: flex; flex-wrap: wrap; gap: var(--space-1-5); margin: 0; padding: 0; list-style: none; }
.room-summary-note { overflow-wrap: anywhere; }
.room-summary .photo-strip-sm { max-width: 480px; }
@media (max-width: 959px) {
  .app .room-pager-sticky {
    position: sticky; bottom: calc(var(--space-2) + 48px + var(--space-6) + 1px); z-index: var(--z-sticky);
    padding-block: var(--space-3); background: var(--surface); border-top: 1px solid var(--line);
  }
}

/* PO-13: a property card with no photo shows a calm house outline (and "Add photos" for people who can add them). */

/* PO-24: forms to the GOV.AU pattern. Examples go in hint text under the label (not placeholders), a fieldset's legend is a
   step bigger than its fields' labels, and short answers get short inputs (.input-w-xs/-sm/-md, RO-29). */
.legend-lg { font-size: var(--text-lg); line-height: var(--leading-lg); font-weight: var(--weight-semibold); }
.field-hint { margin: 0; font-size: var(--text-md); line-height: var(--leading-md); color: var(--ink-muted); }

/* ==== Round 2: owner overview pages ==== */

/* PO-27: a dashboard tile with nothing to show still has a value ("None", muted) and its caption says what to do. */
.stat-value.muted { color: var(--ink-muted); }
.stat-action { color: var(--brand); font-weight: var(--weight-semibold); }
a.stat:hover .stat-action { text-decoration: underline; }

/* PO-43: a tag is a kind, not a state (e.g. "Legal deadline"): neutral, outlined, with an icon. States use badges. */
.tag {
  display: inline-flex; align-items: center; gap: var(--space-1); padding: 0 var(--space-1-5); vertical-align: baseline;
  border: 1px solid var(--line-strong); border-radius: var(--radius-sm); color: var(--ink-muted);
  font-size: var(--text-sm); line-height: var(--leading-sm); font-weight: var(--weight-semibold); white-space: nowrap;
}
.tag > svg { flex: none; }

/* PO-29: a task's actions line up with the text above, even when they wrap; the space between them comes from the gap,
   not the text buttons' own padding. Snooze and Dismiss are in the row's menu. */
.task-list .actions { align-items: center; gap: var(--space-2) var(--space-4); }
.task-list .actions > form > :is(.btn-text, .btn-text-danger) { padding-inline: 0; }

/* PO-41: Money's rent table. On phones each tenancy is a card with its status beside the address. */
.money-rent .badge-group .badge { margin: 0; }
@media (max-width: 599px) {
  .money-rent tbody tr { display: grid; grid-template-columns: minmax(0, 1fr) auto; column-gap: var(--space-3); }
  .money-rent tbody td { grid-column: 1 / -1; }
  .money-rent tbody td.table-cards-title { grid-column: 1; grid-row: 1; }
  .money-rent tbody td.table-cards-status { grid-column: 2; grid-row: 1; }
  .money-rent .badge-group { justify-content: flex-end; }
}

/* PO-22: Settings › Activity, grouped by day. Time on the left, what happened beside it; a burst is one line. */
.activity-day { font-family: var(--font-sans); font-size: var(--text-md); line-height: var(--leading-md); font-weight: var(--weight-semibold); color: var(--ink-muted); }
.activity-list > li + li { border-top: 1px solid var(--line); }
.activity-row { display: grid; grid-template-columns: 4.75rem minmax(0, 1fr); gap: var(--space-3); padding-block: var(--space-3); }
.activity-list > .activity-row:first-child { padding-top: 0; }
.activity-list > .activity-row:last-child { padding-bottom: 0; }
.activity-time { color: var(--ink-muted); font-size: var(--text-md); line-height: var(--leading-md); white-space: nowrap; }
.activity-main { min-width: 0; overflow-wrap: anywhere; }
.activity-run > .list { margin-top: var(--space-2); }
.activity-run .activity-row { padding-block: var(--space-2); }
.activity-run .activity-row + .activity-row { border-top: 1px solid var(--line); }

/* PO-47: the staff style guide (/admin/style-guide). Only the page's own scaffolding; the examples use the real classes. */
.sg-contents { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-4); margin: 0; padding: 0; list-style: none; font-size: var(--text-md); line-height: var(--leading-md); }
.sg-scale { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-4); }
.sg-text-xs { font-size: var(--text-xs); } .sg-text-sm { font-size: var(--text-sm); } .sg-text-md { font-size: var(--text-md); }
.sg-text-base { font-size: var(--text-base); } .sg-text-lg { font-size: var(--text-lg); } .sg-text-xl { font-size: var(--text-xl); }
.sg-text-2xl { font-size: var(--text-2xl); } .sg-text-3xl { font-size: var(--text-3xl); }
.sg-swatches { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: var(--space-3); margin: 0; padding: 0; list-style: none; }
.sg-swatch { display: flex; align-items: center; gap: var(--space-2); min-width: 0; }
.sg-chip { flex: none; width: 32px; height: 32px; border: 1px solid var(--line); border-radius: var(--radius-sm); }
.sg-ink { background: var(--ink); } .sg-ink-muted { background: var(--ink-muted); } .sg-surface { background: var(--surface); }
.sg-surface-raised { background: var(--surface-raised); } .sg-line { background: var(--line); } .sg-line-strong { background: var(--line-strong); }
.sg-brand { background: var(--brand); } .sg-brand-strong { background: var(--brand-strong); } .sg-brand-soft { background: var(--brand-soft); }
.sg-key { background: var(--key); } .sg-good { background: var(--good); } .sg-good-soft { background: var(--good-soft); }
.sg-warning { background: var(--warning); } .sg-warning-soft { background: var(--warning-soft); } .sg-critical { background: var(--critical); }
.sg-critical-soft { background: var(--critical-soft); } .sg-info { background: var(--info); } .sg-info-soft { background: var(--info-soft); }
.sg-neutral-soft { background: var(--neutral-soft); }

/* ==== Round 2: tenants, applicants and trades ==== */

/* RO-29: inputs sized to what goes in them (GOV.AU forms), never wider than their column. */
.input-w-xs { max-width: calc(8ch + var(--space-10)); }
.input-w-sm { max-width: calc(12ch + var(--space-10)); }
.input-w-md { max-width: calc(24ch + var(--space-10)); }

/* RO-13: how to pay. Bank details at body size and on the label's line; the status sits beside "Copy all" so it
   reserves no empty line; the anti-scam warning is an info alert with a shield. */
.pay-details dt:has(+ dd.copy-row) { align-self: center; }
.copy-value.mono { font-size: var(--text-base); letter-spacing: .02em; }
.copy-all { align-items: center; }
.copy-all .copy-status { min-height: 0; }
.pay-warning { display: flex; align-items: flex-start; gap: var(--space-3); }
.pay-warning-icon { flex: none; margin-top: var(--space-0-5); color: var(--brand-strong); }

/* RO-33: the tenant's summary strip. Rows on a phone, three tiles side by side from tablet up. */
.tenant-summary { display: grid; gap: var(--space-2); }
.tenant-summary-tile {
  display: flex; align-items: center; gap: var(--space-3); padding: var(--space-3) var(--space-4); position: relative; color: inherit;
  text-decoration: none; padding-right: calc(var(--space-4) + var(--space-6));
  transition: border-color var(--duration-fast) var(--ease-standard);
}
/* R14 4.7: three cards (repairs, inspections, requests), each a tile, a label with its caption, and a chevron. Rent is the hero above. */
@media (min-width: 600px) { .tenant-summary { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-3); } }
@media (min-width: 960px) { .overview-grid { align-items: start; } }

/* RO-30: the applicant's empty My applications page. */
.applying-title { font-size: var(--text-lg); line-height: var(--leading-lg); }

/* RO-31: a trade's jobs, grouped by what needs doing; each card led by its time, with a chevron. */
.job-group-title { display: flex; align-items: center; gap: var(--space-2); font-size: var(--text-lg); line-height: var(--leading-lg); }
.job-group-count { padding: 0 var(--space-2); border-radius: var(--radius-pill); background: var(--neutral-soft); font-family: var(--font-sans); font-size: var(--text-sm); line-height: var(--leading-sm); font-weight: 600; }
.job-card-body { flex: 1 1 auto; min-width: 0; }
.job-card-title { font-size: var(--text-base); line-height: var(--leading-base); overflow-wrap: anywhere; }
.job-card-chevron { flex: none; color: var(--ink-muted); }
.job-card:hover .job-card-chevron { color: var(--brand); }

/* RO-40: the tenant's message composer, under the conversation. */
.composer { padding-top: var(--space-4); border-top: 1px solid var(--line); }
.composer-bar { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: var(--space-3); }
.composer-attach { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; flex: 1 1 240px; }
.composer-attach-label { display: inline-flex; align-items: center; gap: var(--space-1-5); font-weight: 600; }
.composer-attach-label svg { flex: none; color: var(--ink-muted); }

/* ==== Round 2: staff pages ==== */

.staff-sections { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: var(--space-5) var(--space-6); }

/* RO-24: ownership check. Facts and decision on the left, the latest document beside them (sticky) on desktop. */
.ownership-evidence-image { display: block; width: 100%; height: auto; border: 1px solid var(--line); border-radius: var(--radius-md); }
@media (min-width: 1200px) {
  .ownership-review { align-items: start; }
  .ownership-evidence { position: sticky; top: var(--space-6); }
  .ownership-evidence .pdf-pages { height: 60vh; min-height: 320px; }
  .ownership-evidence-image { max-height: 70vh; object-fit: contain; }
}

/* RO-32: switches say On or Off in words (not position and colour alone), and the save bar shows unsaved changes (save-bar.js). */
.switch:has(.switch-state) { display: inline-flex; align-items: center; gap: var(--space-2); }
.switch-state { min-width: 3ch; font-size: var(--text-sm); line-height: var(--leading-sm); font-weight: 600; color: var(--ink-muted); }
.switch-state::before { content: "Off"; }
.switch input:checked ~ .switch-state { color: var(--ink); }
.switch input:checked ~ .switch-state::before { content: "On"; }
.save-bar { justify-content: flex-end; }
.save-bar-status { margin-right: auto; }
.save-bar.is-dirty .save-bar-status { color: var(--warning); font-weight: 600; }
@media (min-width: 960px) {
  .save-bar.is-dirty {
    position: sticky; bottom: 0; z-index: var(--z-sticky);
    margin: 0 calc(-1 * var(--space-5)) calc(-1 * var(--space-5)); padding: var(--space-3) var(--space-5);
    background: var(--surface-raised); border-top: 1px solid var(--line); border-radius: 0 0 var(--radius-lg) var(--radius-lg);
  }
}

/* RO-34: the forms matrix. Badges stay on one line and columns keep a usable width (the table scrolls on phones). */

/* ==== Round 3: App shell, navigation and global CSS ==== */

/* UX-B3: a list that is also a card (<ul class="card list">, <ul class="card task-list">) keeps the card's inner padding;
   .list and .task-list only reset a bare list. Notification lists stay flush: their rows carry their own padding and hover. */
.card:is(.list, .task-list) { padding: var(--space-4); }
@media (min-width: 960px) { .card:is(.list, .task-list) { padding: var(--space-5); } }
.card.list > .list-row:last-child, .card.task-list > li:last-child { padding-bottom: 0; }
.card.notification-list { padding: 0; }
.card.notification-list > .list-row:last-child { padding-bottom: var(--space-3); }

/* UX-B4: a choice card's caption is ordinary text, even inside a fieldset.field. */
.choice .caption { font-weight: var(--weight-regular); }

/* UX-B6, UX-A14: the owner menu's groups (Daily, Rentals, Tools) under the same small headings as the Admin menu. */
.nav-main { gap: 0; }

/* UX-C12: a count on a phone tab (and on More) when something behind it needs doing, like the sidebar's .nav-count. */
.tab-count {
  position: absolute; top: 2px; left: calc(50% + var(--space-1-5)); min-width: 18px; padding: 0 var(--space-1);
  border-radius: var(--radius-pill); background: var(--brand); color: var(--on-brand); box-shadow: 0 0 0 2px var(--surface-raised);
  font-size: var(--text-xs); line-height: 18px; font-weight: var(--weight-semibold); text-align: center;
}

/* UX-C25: the Admin menu's groups fold (Queues and the group you're in stay open), with tighter rows on a computer, so the
   whole menu fits one screen. */
.nav-fold { display: block; }
.nav-fold > * + * { margin-top: var(--space-0-5); }
.nav-fold > summary { display: flex; align-items: center; justify-content: space-between; min-height: 28px; list-style: none; cursor: pointer; border-radius: var(--radius-sm); }
.nav-fold > summary::-webkit-details-marker { display: none; }
.nav-fold > summary::after {
  content: ""; width: 6px; height: 6px; margin-right: var(--space-1); border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
  transform: rotate(-45deg); transition: transform var(--duration-fast) var(--ease-standard);
}
.nav-fold[open] > summary::after { transform: rotate(45deg); }
.nav-fold > summary:hover { color: var(--ink); }
.nav-fold > summary:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
@media (min-width: 960px) {
  .sidebar .nav-admin .nav-item { min-height: 36px; }
}

/* UX-B26: tablets (700-959px) get an icon rail instead of the phone tab bar. The top bar keeps notifications, help and the
   account; the rail shows each section's icon with its name as a tooltip (and to screen readers). */
@media (min-width: 700px) and (max-width: 959px) {
  .app { display: grid; grid-template-columns: 72px minmax(0, 1fr); grid-template-rows: auto 1fr; align-items: start; }
  .app > .sidebar {
    display: flex; flex-direction: column; gap: var(--space-3); grid-column: 1; grid-row: 1 / span 2;
    position: sticky; top: 0; height: 100vh; overflow-y: auto; overscroll-behavior: contain;
    padding: var(--space-4) var(--space-2); background: var(--surface-raised); border-right: 1px solid var(--line);
  }
  .app > .topbar { grid-column: 2; grid-row: 1; }
  .app > .content { grid-column: 2; grid-row: 2; }
  .app > .tabbar { display: none; }
  .sidebar .brand, .sidebar .owner-switcher, .sidebar .view-only, .sidebar .sidebar-footer, .sidebar > .nav-secondary,
  .sidebar .nav-heading, .nav-main .nav-subheading { display: none; }
  .sidebar .nav-item { position: relative; justify-content: center; padding: 0; font-size: 0; }
  .sidebar .nav-label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
  .sidebar .nav-count { position: absolute; top: 0; right: 0; margin: 0; padding: 0 var(--space-1); min-width: 18px; }
  .sidebar .nav-group + .nav-group, .sidebar .nav-admin { margin-top: 0; padding-top: var(--space-2); border-top: 1px solid var(--line); }
  .sidebar .nav-fold > summary { justify-content: center; padding: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .sidebar .nav-fold > summary::after { display: none; }
  /* No tab bar under the page: the action bar and room pager sit at the bottom of the screen. */
  .app .action-bar { bottom: 0; padding-bottom: calc(var(--space-3) + env(safe-area-inset-bottom)); }
  .app .room-pager-sticky { bottom: 0; }
  html:has(.tabbar) { scroll-padding-bottom: 0; }
  html:has(.app .action-bar) { scroll-padding-bottom: 96px; }
}

/* ==== Round 3: Public site ==== */

/* ==== Round 3: Owner rent, repairs and tenancy ==== */

/* UX-A3: quick amounts above the payment amount (shown by rent-payment.js), and the live "Pays rent to…" line. */
.amount-chips { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.amount-chip {
  min-height: 36px; padding: 0 var(--space-3); border: 1px solid var(--line-strong); border-radius: var(--radius-pill);
  background: var(--surface-raised); color: var(--ink); font: var(--weight-semibold) var(--text-sm)/var(--leading-sm) var(--font-sans); cursor: pointer;
}
.amount-chip:hover { border-color: var(--brand); background: var(--brand-soft); }
.amount-chip[aria-pressed="true"] { border-color: var(--brand); background: var(--brand-soft); color: var(--brand-strong); }
.amount-chip[aria-pressed="true"]::before { content: "\2713\00a0"; }
.amount-chip:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }

/* UX-A4: "Add a credit or charge" is a text button at the ledger's right edge; its form opens below it. */
.ledger-adjust { display: flex; flex-direction: column; align-items: flex-end; gap: var(--space-3); }
.ledger-adjust > summary {
  list-style: none; cursor: pointer; padding: var(--space-1) 0; color: var(--brand);
  font: var(--weight-semibold) var(--text-md)/var(--leading-md) var(--font-sans);
}
.ledger-adjust > summary::-webkit-details-marker { display: none; }
.ledger-adjust > summary::before { content: "+\00a0"; }
.ledger-adjust[open] > summary::before { content: "\2212\00a0"; }
.ledger-adjust > summary:hover { text-decoration: underline; }
.ledger-adjust > form { align-self: stretch; width: 100%; min-width: 0; }

/* UX-A4: the formal-steps card keeps its button secondary and says it's urgent with a critical edge. */
.arrears-step-urgent { border-left: 4px solid var(--critical); }

/* UX-A5: arrears reminders as a timeline of steps, each with its day ("7 days late") and what it says. */
.step-timeline { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-4); }
.step-timeline-step {
  position: relative; display: flex; flex-direction: column; gap: var(--space-1); padding-left: var(--space-6);
}
.step-timeline-step::before {
  content: ""; position: absolute; left: 0; top: var(--space-1); width: 12px; height: 12px; border-radius: 50%;
  background: var(--surface-raised); border: 2px solid var(--brand);
}
.step-timeline-step:not(:last-child)::after {
  content: ""; position: absolute; left: 5px; top: var(--space-5); bottom: calc(-1 * var(--space-4)); border-left: 2px solid var(--line);
}
.step-timeline-day { font-weight: var(--weight-semibold); }
@media (min-width: 600px) {
  .step-timeline { grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); grid-auto-flow: column; }
  .step-timeline-step { padding-left: 0; padding-top: var(--space-6); }
  .step-timeline-step::before { top: 0; }
  .step-timeline-step:not(:last-child)::after { left: var(--space-4); right: calc(-1 * var(--space-4)); top: 5px; bottom: auto; border-left: 0; border-top: 2px solid var(--line); }
}
/* A unit after a number field ("days late"), like .input-prefix before one. */
.input-suffix { display: flex; align-items: center; gap: var(--space-2); }
.input-suffix .input { width: calc(6ch + var(--space-8)); }
.input-suffix span { color: var(--ink-muted); white-space: nowrap; }

/* UX-A8: the tenancy's documents, the lease and other key papers first with a file icon. */
.doc-list { list-style: none; margin: 0; padding: 0; }
.doc-row { display: flex; align-items: flex-start; gap: var(--space-3); padding: var(--space-2) 0; border-top: 1px solid var(--line); }
.doc-row:first-child { border-top: 0; padding-top: 0; }
.doc-row:not(.doc-row-pinned) { padding-left: calc(var(--space-5) + var(--space-3)); }
.doc-row > svg { flex: none; width: var(--space-5); height: var(--space-5); margin-top: var(--space-0-5); color: var(--brand); }
.doc-row-pinned a { font-weight: var(--weight-semibold); }

/* R14 (4.3): the people on a tenancy are rows; each person's notice consent line and their forms sit under the row, level with its text. */
.rows > .person-more { display: flex; flex-direction: column; gap: var(--space-2); padding: 0 0 var(--space-3) calc(var(--tile) + var(--space-3)); }
.card > .rows > .person-more { padding-right: var(--space-4); padding-left: calc(var(--space-4) + var(--tile) + var(--space-3)); }
.rows > .person-more > .caption { margin: 0; }
@media (min-width: 960px) { .card > .rows > .person-more { padding-right: var(--space-5); padding-left: calc(var(--space-5) + var(--tile) + var(--space-3)); } }

/* UX-A2: the repair's stepper (Reported, Acknowledged, Booked, Fixed), done steps ticked and the current one in brand. */
.stepper { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--space-1); counter-reset: step; }
.stepper-step {
  counter-increment: step; display: flex; flex-direction: column; gap: var(--space-1); padding-top: var(--space-2);
  border-top: 4px solid var(--line); color: var(--ink-muted); font-size: var(--text-sm); line-height: var(--leading-sm);
  min-width: 0; overflow-wrap: normal;
}
.stepper-step::before { content: counter(step); font-weight: var(--weight-semibold); }
.stepper-step.is-done { border-top-color: var(--brand); color: var(--ink); }
.stepper-step.is-done::before { content: "\2713"; color: var(--brand); }
.stepper-step.is-current { border-top-color: var(--brand); color: var(--ink); font-weight: var(--weight-semibold); }
.stepper-step.is-current::before { color: var(--brand); }
@media (min-width: 600px) { .stepper-step { font-size: var(--text-md); line-height: var(--leading-md); } }

/* ==== Round 3: Owner property, compliance and inspections ==== */
/* UX-A7: property cards. The address is the link and covers the card; the to-do badge is its own link above it.
   A short 3:1 photo (or monogram) on wider screens, a 72px square thumbnail beside the text on phones. */
.property-card:has(.property-card-link:hover) { border-color: var(--brand); }
.property-card:has(.property-card-link:focus-visible) { outline: 2px solid var(--brand); outline-offset: 2px; }
.property-card-link { color: var(--ink); text-decoration: none; overflow-wrap: anywhere; }
.property-card-link:hover { color: var(--ink); text-decoration: underline; }
.property-card-link:focus-visible { outline: none; }
.property-card-link::after { content: ""; position: absolute; inset: 0; border-radius: inherit; }
.property-card-todo { position: relative; z-index: 1; display: inline-flex; text-decoration: none; align-items: center; min-height: var(--space-6); }
.property-card-todo:hover .badge { text-decoration: underline; }
.property-card .list-tags { flex-wrap: wrap; }
.property-card .list-tags .badge { white-space: normal; }
.property-monogram { font-size: var(--text-3xl); line-height: var(--leading-3xl); font-weight: var(--weight-semibold); color: var(--brand-strong); }
@media (max-width: 599px) {
  .property-card { flex-direction: row; align-items: flex-start; gap: var(--space-3); padding: var(--space-3); }
  .property-photo { flex: none; width: 72px; aspect-ratio: 1; border: 0; border-radius: var(--radius-md); }
  .property-monogram { font-size: var(--text-xl); line-height: var(--leading-xl); }
  .property-body { flex: 1 1 auto; min-width: 0; padding: 0; }
}

/* UX-A9: compliance. A fixed status column, so every check's name starts at the same place whatever its badge says. */
@media (min-width: 600px) { .compliance-row { grid-template-columns: 9.5rem minmax(0, 1fr) auto; } }
.compliance-not-needed[open] > summary { margin-bottom: 0; }

/* UX-A10: jump to a room. A list on phones; a row of chips on wider screens. */
.room-jump { display: flex; flex-direction: column; gap: var(--space-2); }
.room-jump-select { display: flex; flex-direction: column; gap: var(--space-1); }
.room-jump-row { display: flex; gap: var(--space-2); }
.room-jump-row .input { flex: 1 1 auto; min-width: 0; }
.room-jump-select [hidden] { display: none; }
.room-chips { display: none; flex-wrap: wrap; gap: var(--space-1-5); margin: 0; padding: 0; list-style: none; }
.room-chip {
  display: inline-flex; align-items: center; min-height: 36px; padding: var(--space-1) var(--space-3); border: 1px solid var(--line);
  border-radius: var(--radius-pill); background: var(--surface-raised); color: var(--ink); font-size: var(--text-sm); line-height: var(--leading-sm); text-decoration: none;
}
.room-chip:hover { border-color: var(--brand); color: var(--ink); }
.room-chip[aria-current="page"] { border-color: var(--brand); background: var(--brand-soft); color: var(--brand-strong); font-weight: var(--weight-semibold); }
@media (min-width: 600px) {
  .room-jump-select { display: none; }
  .room-chips { display: flex; }
}
.room-summary-compact .room-summary-row { padding-block: var(--space-2); }
.room-summary-compact .room-summary-row:first-child { padding-top: 0; }

/* UX-A11: a finished report leads with the report and its findings; rooms shrink to tiles. */
.inspection-findings { display: flex; flex-direction: column; margin: 0; padding: 0; list-style: none; }
.inspection-finding { display: flex; flex-direction: column; gap: var(--space-1-5); padding-block: var(--space-3); border-top: 1px solid var(--line); }
.inspection-finding:first-child { padding-top: 0; border-top: 0; }
.inspection-finding:last-child { padding-bottom: 0; }
.inspection-finding-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: var(--space-1) var(--space-3); }
.inspection-finding-head .list-tags { flex-wrap: wrap; }
.inspection-finding .caption { margin: 0; overflow-wrap: anywhere; }
.inspection-finding .photo-strip-sm { max-width: 480px; }
.inspection-room-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: var(--space-2); margin: 0; padding: 0; list-style: none; }
.inspection-room-tile {
  display: flex; flex-direction: column; gap: var(--space-0-5); height: 100%; min-height: 44px; padding: var(--space-2) var(--space-3);
  border: 1px solid var(--line); border-radius: var(--radius-md); color: var(--ink); text-decoration: none;
}
.inspection-room-tile:hover { border-color: var(--brand); color: var(--ink); }
.inspection-room-tile .label { color: var(--brand); }

/* UX-A15: Add a property on a phone. Address fields wait for the finder; rooms in one row; features as wrapping chips (R14 3.11). */
.address-manual-open { align-self: flex-start; }
.property-counts { display: grid; grid-template-columns: repeat(3, minmax(0, 8rem)); gap: var(--space-3); }
.property-strata[open] > summary { margin-bottom: var(--space-3); }
@media (max-width: 599px) {
  .property-counts { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-2); }
}

/* UX-A20, R14 (4.2): property overview. Tenancy first, set-up before the details while something is open; from 960px the cards sit two across in that order. */
.overview-details > .alert { margin: 0; }

/* ==== Round 3: Owner dashboard, money, billing and settings ==== */

/* UX-A12: dashboard placement. Desktop (1200px+): what needs doing beside the numbers and setup. Tablets: what needs doing
   first. Phones: the numbers first, as a strip of compact tiles that scrolls sideways. */
.dashboard-split { display: grid; gap: var(--space-6); align-items: start; }
.dashboard-split > * { min-width: 0; }
@media (min-width: 1200px) {
  .dashboard-split { grid-template-columns: minmax(0, 2fr) minmax(280px, 1fr); grid-template-areas: "main stats" "main setup"; grid-template-rows: auto 1fr; }
  .dashboard-main { grid-area: main; }
  .dashboard-stats { grid-area: stats; }
  .dashboard-setup { grid-area: setup; }
}
/* R14 (3.4): on tablets and narrow desktops the four tiles sit above the list as a grid, not below it. */
@media (min-width: 600px) and (max-width: 1199px) {
  .dashboard-split { grid-template-areas: "stats" "main" "setup"; }
  .dashboard-main { grid-area: main; }
  .dashboard-stats { grid-area: stats; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); }
  .dashboard-setup { grid-area: setup; }
}
@media (max-width: 599px) {
  .dashboard-split { gap: var(--space-4); }
  .dashboard-stats { order: -1; display: flex; gap: var(--space-2); overflow-x: auto; scroll-snap-type: x proximity; margin-inline: calc(-1 * var(--space-4)); padding: 0 var(--space-4) var(--space-1); scrollbar-width: none; }
  .dashboard-stats::-webkit-scrollbar { display: none; }
  .dashboard-stats > .stat { flex: 0 0 160px; scroll-snap-align: start; padding: var(--space-3); gap: var(--space-0-5); }
  .dashboard-stats .stat > .caption { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .dashboard-stats .stat-value { font-size: var(--text-xl); line-height: var(--leading-xl); }
  .dashboard-stats .stat-of { display: block; }
  .dashboard-stats .stat-progress { display: none; }
}

/* UX-A13: received against expected. UX-A6: a routine inspection that's due and not booked. */
.stat-of { font-size: var(--text-md); line-height: var(--leading-md); font-weight: var(--weight-semibold); color: var(--ink-muted); font-family: inherit; }
.stat-progress { appearance: none; width: 100%; height: 6px; border: 0; border-radius: var(--radius-pill); background: var(--neutral-soft); overflow: hidden; }
.stat-progress::-webkit-progress-bar { background: var(--neutral-soft); border-radius: var(--radius-pill); }
.stat-progress::-webkit-progress-value { background: var(--good); border-radius: var(--radius-pill); }
.stat-progress::-moz-progress-bar { background: var(--good); border-radius: var(--radius-pill); }
.stat-warn-text { color: var(--warning); font-weight: var(--weight-semibold); }

/* UX-A22: "Change account" is an edit (a pencil), not an add; the account name carries a lock. */
.fold-action > summary.fold-action-edit::before {
  content: ""; width: 16px; height: 16px; background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 20h9 M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4z'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 20h9 M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4z'/%3E%3C/svg%3E") center / contain no-repeat;
}
.account-name { display: inline-flex; align-items: center; gap: var(--space-1-5); }
.account-name svg { flex: none; color: var(--ink-muted); }

/* UX-B8: a switch that can't be changed looks it; locked notification types say "Always on" with a lock instead. */
.switch input:disabled { opacity: .5; cursor: not-allowed; }
.always-on { display: inline-flex; align-items: center; gap: var(--space-1); font-size: var(--text-sm); line-height: var(--leading-sm); color: var(--ink-muted); white-space: nowrap; }
.table-group-row th { padding-top: var(--space-4); font-size: var(--text-sm); line-height: var(--leading-sm); color: var(--ink-muted); text-align: left; font-weight: var(--weight-semibold); }

/* UX-B10: the one page lede: body size, muted, at most 65 characters wide (never a caption). */
.page-lede { margin: 0; max-width: 41.5em; font-size: var(--text-base); line-height: var(--leading-base); }

/* UX-B13: the billing summary leads with the next charge. */
.billing-summary .stat-value { margin: 0; }
.billing-plans td { vertical-align: top; }

/* UX-B18: Activity filters and the day's time zone. */
.activity-filter { align-items: end; }
.activity-automatic { justify-content: flex-end; }
.activity-zone { font-weight: var(--weight-regular); color: var(--ink-muted); }

/* UX-B23: each Settings entry says what it's for. */
.settings-list-text { display: flex; flex-direction: column; gap: var(--space-0-5); min-width: 0; }
.settings-list-label { display: inline-flex; align-items: center; gap: var(--space-2); }

/* UX-B24: closing the account sits apart, in its own card. */
.danger-zone { border-color: var(--critical); }

/* UX-B25: an editable role is one tappable row with a chevron. */
.role-row-editable { padding-block: 0; }
.role-row-link { display: flex; align-items: center; gap: var(--space-3); width: 100%; min-height: 56px; padding-block: var(--space-3); color: var(--ink); text-decoration: none; }
.role-row-link:hover .label { text-decoration: underline; }
.role-row-link > svg { flex: none; color: var(--ink-muted); }
.list-row.role-row-editable:first-child .role-row-link { padding-top: 0; }

/* ==== Round 3: Owner notices, changes, rules, notifications and help ==== */

/* UX-B15, UX-C11: notifications. One meaning per colour: the icon tile (tinted by type) says what it's about, a brand
   bar and a light brand tint say unread, and "Urgent" is a badge. Time has its own fixed column so it lines up; the
   chevron says the row opens; "I'm on it" sits under the text so it never pushes the time across. */
.notification-group { font-size: var(--text-sm); line-height: var(--leading-sm); font-weight: var(--weight-semibold); color: var(--ink-muted); }
.notification-feed { padding: 0; overflow: hidden; }
.notification-feed .list-row {
  grid-template-columns: var(--space-8) minmax(0, 1fr) auto; grid-template-areas: "icon main chevron" "icon time chevron" ". action .";
  align-items: start; gap: var(--space-0-5) var(--space-3); padding: var(--space-3) var(--space-4);
}
.notification-feed .list-row:first-child { padding-top: var(--space-3); }
.notification-feed .is-unread { background: color-mix(in srgb, var(--brand-soft) 45%, var(--surface-raised)); box-shadow: inset 3px 0 0 var(--brand); }
.notification-feed .label { font-weight: var(--weight-regular); }
.notification-feed .is-unread .label { font-weight: var(--weight-semibold); }
.notification-feed .list-row:has(.notification-link):hover { background: var(--surface); }
.notification-icon {
  grid-area: icon; display: grid; place-items: center; width: var(--space-8); height: var(--space-8);
  border-radius: var(--radius-md); background: var(--neutral-soft); color: var(--ink-muted);
}
.notif-kind-money { background: var(--good-soft); color: var(--good); }
.notif-kind-repair { background: var(--warning-soft); color: var(--warning); }
.notif-kind-notice { background: var(--info-soft); color: var(--info); }
.notif-kind-ownership { background: var(--brand-soft); color: var(--brand-strong); }
.notif-kind-person, .notif-kind-message { background: var(--neutral-soft); color: var(--ink-muted); }
.notification-title { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-0-5) var(--space-2); }
.notification-title .badge { margin-left: 0; }
.notification-feed .notification-time { grid-area: time; text-align: left; font-variant-numeric: tabular-nums; }
.notification-chevron { grid-area: chevron; align-self: center; color: var(--ink-muted); }
.notification-feed .notification-action { grid-area: action; margin-top: var(--space-2); }
@media (min-width: 600px) {
  .notification-feed .list-row {
    grid-template-columns: var(--space-8) minmax(0, 1fr) 5.5rem var(--space-4); grid-template-areas: "icon main time chevron" ". action . .";
  }
  .notification-feed .notification-time { text-align: right; white-space: nowrap; padding-top: var(--space-0-5); }
}

/* UX-B16: Inbox sections share one heading and lede rhythm; their empty states are the shared component. */

/* UX-B14: State rules. Jump links stay in view; each rule is a disclosure with its name and a one-line summary. */
.rule-state-switch { min-width: 12rem; }
.rule-jump {
  position: sticky; top: 0; z-index: var(--z-sticky); display: flex; gap: var(--space-2); overflow-x: auto;
  margin-inline: calc(-1 * var(--space-1)); padding: var(--space-2) var(--space-1); background: var(--surface); scrollbar-width: none;
}
.rule-jump::-webkit-scrollbar { display: none; }
.rule-jump a {
  flex: none; display: inline-flex; align-items: center; min-height: 36px; padding: 0 var(--space-3);
  border: 1px solid var(--line-strong); border-radius: var(--radius-pill); background: var(--surface-raised);
  color: var(--ink); font-size: var(--text-sm); line-height: var(--leading-sm); font-weight: var(--weight-semibold); text-decoration: none;
}
.rule-jump a:hover, .rule-jump a:focus-visible { border-color: var(--brand); color: var(--brand-strong); }
.rule-section, #coming-up, #dates, #forms, #holidays { scroll-margin-top: var(--space-16); }
.rule-items { list-style: none; margin: 0; padding: 0; }
.rule-items > li + li { border-top: 1px solid var(--line); }
.rule-items > li { scroll-margin-top: var(--space-16); }
.rule-item > summary {
  position: relative; display: flex; flex-direction: column; gap: var(--space-0-5); padding: var(--space-3) var(--space-8) var(--space-3) 0;
  list-style: none; cursor: pointer;
}
.rule-item > summary::-webkit-details-marker { display: none; }
.rule-item > summary::after {
  content: ""; position: absolute; right: var(--space-1); top: calc(var(--space-3) + var(--space-1)); width: 16px; height: 16px; background: var(--ink-muted);
  transition: transform var(--duration-fast) var(--ease-standard);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E") center / contain no-repeat;
}
.rule-item[open] > summary::after { transform: rotate(180deg); }
.rule-item > summary:hover .rule-name { color: var(--brand-strong); }
.rule-name { font-size: var(--text-lg); line-height: var(--leading-lg); font-weight: var(--weight-semibold); color: var(--ink); }
.rule-summary { font-size: var(--text-md); line-height: var(--leading-md); color: var(--ink-muted); }
.rule-item[open] .rule-summary { display: none; }
.rule-body { padding-bottom: var(--space-4); }
.rule-body .rule-value { max-width: 44.5em; margin: 0; }
.rule-body .caption { margin: 0; }

/* UX-B17: help search on one line with an icon button; who it's written for as pills under it. */
.help-search { display: flex; gap: var(--space-2); }
.help-search .input { flex: 1 1 auto; min-width: 0; }

/* UX-B20: the notice chooser. A suggested card first, then even cards in groups, each with a small type icon. */
.notice-group-title { font-size: var(--text-md); line-height: var(--leading-md); font-weight: var(--weight-semibold); color: var(--ink-muted); }
.notice-choices { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 1fr)); align-items: stretch; gap: var(--space-3); }
.notice-choice { display: flex; align-items: flex-start; gap: var(--space-3); height: 100%; }
.notice-choice-icon {
  flex: none; display: grid; place-items: center; width: var(--space-8); height: var(--space-8);
  border-radius: var(--radius-md); background: var(--neutral-soft); color: var(--ink-muted);
}
.notice-choice-suggested { border-color: var(--brand); background: color-mix(in srgb, var(--brand-soft) 45%, var(--surface-raised)); }
.notice-choice-suggested .notice-choice-icon { background: var(--brand-soft); color: var(--brand-strong); }
.notice-choice-reason { font-size: var(--text-md); line-height: var(--leading-md); font-weight: var(--weight-semibold); color: var(--brand-strong); }

/* UX-B21: the two common ways a tenancy ends are choice cards; the rare ones sit in the quieter disclosure below. */
.ending-paths, .change-options { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr)); align-items: stretch; gap: var(--space-3); }
.ending-path, .change-option { display: flex; flex-direction: column; gap: var(--space-1); height: 100%; }

/* UX-B22: Changes and endings options are choice cards with their consequence; forms hold the post buttons. */
.change-options > form { display: flex; }
button.change-option { width: 100%; font: inherit; color: var(--ink); text-align: left; cursor: pointer; }
button.change-option:hover, button.change-option:focus-visible { border-color: var(--brand); }

/* ==== Round 3: Tenants, applicants, trades and uploads ==== */

/* UX-C10, UX-A16: the shared upload (_FileDrop, file-drop.js). A dashed zone (or, compact, a button) around a real file
   input. Without script the native control shows inside the zone; with it the input is hidden but still focusable, and
   chosen files show as thumbnails with a remove button each. */
.file-drop { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; }
.file-drop-zone {
  position: relative; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--space-2);
  min-height: 120px; padding: var(--space-5) var(--space-4); border: 2px dashed var(--line-strong); border-radius: var(--radius-lg);
  background: var(--surface); text-align: center; cursor: pointer;
  transition: border-color var(--duration-fast) var(--ease-standard), background-color var(--duration-fast) var(--ease-standard);
}
.file-drop-zone:hover, .file-drop-zone.is-dragging { border-color: var(--brand); background: var(--brand-soft); }
.file-drop-zone:focus-within { outline: 2px solid var(--brand); outline-offset: 2px; }
.file-drop-input { max-width: 100%; font: inherit; font-size: var(--text-sm); color: var(--ink-muted); }
.file-drop.is-enhanced .file-drop-input { position: absolute; inset: 0; width: 1px; height: 1px; opacity: 0; overflow: hidden; }
.file-drop-prompt { display: none; flex-direction: column; align-items: center; gap: var(--space-1); }
.file-drop.is-enhanced .file-drop-prompt { display: flex; }
.file-drop-icon { display: grid; place-items: center; width: 40px; height: 40px; border-radius: 50%; background: var(--brand-soft); color: var(--brand-strong); }
.file-drop-action { font-weight: var(--weight-semibold); color: var(--brand-strong); }
.file-drop-list { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: var(--space-2); }
.file-drop-item { position: relative; display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; }
.file-drop-thumb { width: 100%; aspect-ratio: 1; object-fit: cover; border-radius: var(--radius-md); border: 1px solid var(--line); background: var(--surface-raised); }
.file-drop-doc { display: grid; place-items: center; font-size: var(--text-sm); line-height: var(--leading-sm); font-weight: var(--weight-semibold); color: var(--ink-muted); }
.file-drop-name { font-size: var(--text-xs); line-height: var(--leading-xs); color: var(--ink-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.file-drop-remove { position: absolute; top: var(--space-1); right: var(--space-1); width: 32px; height: 32px; border-radius: 50%; background: var(--surface-raised); box-shadow: var(--shadow-raised); }
.file-drop-count:empty { display: none; }
/* Compact (the message composer): a button-sized zone with the icon beside the words. */
.file-drop-compact .file-drop-zone { flex-direction: row; min-height: 44px; padding: var(--space-1) var(--space-3); border-width: 1px; border-style: solid; border-radius: var(--radius-md); background: var(--surface-raised); }
.file-drop-compact .file-drop-prompt { flex-direction: row; gap: var(--space-2); }
.file-drop-compact .file-drop-icon { width: auto; height: auto; background: none; color: var(--ink-muted); }
.file-drop-compact .file-drop-action { color: var(--ink); }
.file-drop-compact .file-drop-list { grid-template-columns: repeat(auto-fill, minmax(72px, 1fr)); }

/* UX-C2: a trade's booked visit while jobs are paused is read only, so it doesn't look tappable. */
.job-card-paused:hover { border-color: var(--line); }

/* UX-C3 and R14 4.7: the tenant home leads with one hero (rent, an inspection to sign, or the lease check); the copy button sits in it. */
.hero-actions .copy-status { min-height: 0; }
/* UX-C4: the facts being confirmed, inside the lease check. */
.lease-check-facts { padding: var(--space-3) var(--space-4); border-radius: var(--radius-md); background: var(--surface-raised); }

/* UX-C5: the summary tiles are links: a chevron, and the border changes on hover and press. */
.tenant-summary-tile:hover { border-color: var(--brand); }
.tenant-summary-tile:active { border-color: var(--brand-strong); background: var(--brand-soft); }
.tenant-summary-chevron { position: absolute; top: 50%; right: var(--space-3); transform: translateY(-50%); color: var(--ink-muted); }
.tenant-summary-tile:hover .tenant-summary-chevron { color: var(--brand); }

/* UX-C13: the rent record shows the balance after each line; a payment is marked by its icon. */
.ledger-icon { flex: none; display: grid; place-items: center; width: 28px; height: 28px; margin-top: var(--space-0-5); border-radius: 50%; background: var(--neutral-soft); color: var(--ink-muted); }
.ledger-icon-paid { background: var(--good-soft); color: var(--good); }
.ledger-row > .ledger-row-main { flex: 1 1 auto; }
.ledger-figures { flex: none; display: flex; flex-direction: column; align-items: flex-end; gap: var(--space-0-5); text-align: right; }
.ledger-running { white-space: nowrap; }
.ledger-running-owing { color: var(--critical); }

/* UX-C6: the tenant's inspection check. One compact row per item: name and the landlord's three marks, then Agree / Disagree. */
.tenant-lines { list-style: none; margin: 0; padding: 0; }
.tenant-check-list { max-width: 880px; list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--line); }
.tenant-check { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: start; gap: var(--space-2) var(--space-3); padding: var(--space-3) 0; border-bottom: 1px solid var(--line); }
.tenant-check.is-missing { border-left: 3px solid var(--critical); padding-left: var(--space-3); }
.tenant-check-main { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; }
.tenant-check-name { font-weight: var(--weight-semibold); overflow-wrap: anywhere; }
.tenant-check-marks { display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-3); }
.tenant-mark { display: inline-flex; align-items: center; gap: var(--space-1); font-size: var(--text-sm); line-height: var(--leading-sm); color: var(--ink-muted); }
.tenant-mark.is-yes svg { color: var(--good); }
.tenant-mark.is-no { color: var(--critical); }
.tenant-check-photos { display: flex; flex-wrap: wrap; gap: var(--space-1); }
.tenant-check-photos img { width: 56px; height: 56px; object-fit: cover; border-radius: var(--radius-sm); border: 1px solid var(--line); }
.tenant-check-error, .tenant-check-comment { grid-column: 1 / -1; }
/* The comment opens only for a disagreement (or when there's already one). */
.tenant-check-comment { display: none; flex-direction: column; }
.tenant-check:has(input[value="false"]:checked) .tenant-check-comment, .tenant-check.has-comment .tenant-check-comment { display: flex; }
.action-bar-status { flex: 1 1 100%; }
@media (min-width: 600px) { .action-bar-status { flex: 1 1 auto; } }

/* UX-C7: a signed-off report as a summary: disagreements, then one line per room that opens to its items. */
.inspection-disagreement { flex-direction: column; align-items: flex-start; gap: var(--space-0-5); }
.inspection-room-line { display: block; }
.inspection-room-line > .fold > summary .caption { font-weight: var(--weight-regular); }
.inspection-item-line { justify-content: space-between; }

/* UX-C8: a repair's timeline. A dot per step on a line; done steps filled, the current one ringed. */
.repair-timeline { list-style: none; margin: 0; padding: 0; }
.repair-step { position: relative; display: flex; gap: var(--space-3); padding-bottom: var(--space-4); }
.repair-step:last-child { padding-bottom: 0; }
.repair-step::before { content: ""; position: absolute; top: var(--space-5); bottom: 0; left: 7px; width: 2px; background: var(--line); }
.repair-step:last-child::before { display: none; }
.repair-step-dot { flex: none; width: 16px; height: 16px; margin-top: var(--space-0-5); border: 2px solid var(--line-strong); border-radius: 50%; background: var(--surface-raised); }
.repair-step-done .repair-step-dot { border-color: var(--good); background: var(--good); }
.repair-step-current .repair-step-dot { border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-soft); }
.repair-step-upcoming .label { color: var(--ink-muted); font-weight: var(--weight-regular); }
/* The request list: each row opens its page, with a chevron. */
.repair-row { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-3) 0; border-top: 1px solid var(--line); color: inherit; text-decoration: none; }
.tenant-lines > li:first-child > .repair-row { border-top: 0; padding-top: 0; }
.repair-row-body { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.repair-row-chevron { flex: none; color: var(--ink-muted); }
.repair-row:hover .label { color: var(--brand-strong); text-decoration: underline; }
.repair-row:hover .repair-row-chevron { color: var(--brand); }

/* UX-C9: emergency help as a slim critical strip; the call button has the number on its own line. */
.emergency-call { flex-direction: column; align-items: center; gap: 0; min-height: 56px; text-align: center; }
.emergency-call-number { font-variant-numeric: tabular-nums; font-size: var(--text-lg); line-height: var(--leading-lg); }

/* UX-C14: the facts above the notice form. */
.notice-facts dd strong { font-weight: var(--weight-semibold); }

/* UX-C15: the lease's copy status takes no room until something is copied. */
#lease-copy-status:empty { display: none; }

/* UX-C16: the renter profile checklist: a tick or an empty circle, and a link for each thing still to do. */
.profile-checklist { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-1-5); }
.profile-check { display: flex; align-items: center; gap: var(--space-2); }
.profile-check-icon { flex: none; color: var(--ink-muted); }
.profile-check.is-done { color: var(--ink-muted); }
.profile-check.is-done .profile-check-icon { color: var(--good); }
/* UX-C17: each section says whether it's done; anchors land below the sticky header. */
.profile-section { scroll-margin-top: var(--space-16); }
.profile-section-done { display: inline-flex; align-items: center; gap: var(--space-1); font-size: var(--text-sm); line-height: var(--leading-sm); color: var(--good); font-weight: var(--weight-semibold); }

/* ==== Round 3: Staff tools ==== */

/* UX-C18 / C26: queue pills (Open / Answered, Waiting / Decided) sit above the list like Support's. */

/* UX-C19: the trust queue. One dense row per flagged record (address · kinds · owner · count · age), opening a review
   panel with the flags, the case links and explicit decisions. Secondary links are in the row's "…" menu. */
.trust-queue { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.trust-row {
  display: grid; align-items: start; gap: var(--space-2); padding: var(--space-2) 0; border-top: 1px solid var(--line);
  grid-template-columns: auto minmax(0, 1fr) auto;
}
.trust-row:first-child { border-top: 0; }
.trust-row > .row-menu { margin-top: var(--space-1); }
.trust-case-summary { display: grid; flex-wrap: wrap; align-items: center; gap: var(--space-1) var(--space-3); min-height: 52px; cursor: pointer; list-style: none; grid-template-columns: var(--row-icon) minmax(0, 1fr) auto; padding: var(--space-1) 0; border-radius: var(--radius-md); }
.trust-case-summary::-webkit-details-marker { display: none; }
.trust-case-main { flex: 1 1 260px; min-width: 0; display: flex; flex-direction: column; }
.trust-case-badges { flex: none; }
.trust-case-open { flex: none; pointer-events: none; }
.trust-case-summary:hover .trust-case-open { border-color: var(--brand); }
.trust-case[open] .trust-case-open { background: none; border-color: var(--brand); border: 0; color: var(--brand); }
.trust-case-panel { margin-top: var(--space-2); padding: var(--space-4); background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-md); }
.trust-case-flags { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-3); }
.trust-case-flags > li + li { padding-top: var(--space-3); border-top: 1px solid var(--line); }
.trust-decision .actions { flex-wrap: wrap; }
@media (max-width: 599px) {
  .trust-case-panel { padding: var(--space-3); }
  .trust-decision .actions > .btn { flex: 1 1 auto; }
}

/* UX-C23: staff home as a work list. Empty queues step back (muted number, no border); one past its target is red. */

/* UX-C21: rules library. Rows grouped by state under a heading that sticks below the table header; "To confirm" is a
   small dot and text, so the filled badges (Draft, In review) stand out. */
.rules-group th { text-align: left; background: var(--surface); font-weight: var(--weight-semibold); }
.rules-group th .caption { margin-left: var(--space-2); font-weight: var(--weight-regular); }
@media (min-width: 600px) { .rules-table .rules-group th { top: calc(var(--space-8) + var(--space-0-5)); } }
@media (max-width: 599px) {
  .rules-table .rules-group { padding: var(--space-2) 0 var(--space-1); border-bottom: 0; }
  .rules-table .rules-group th { display: block; position: static; padding: var(--space-1) var(--space-2); box-shadow: none; border-radius: var(--radius-sm); }
}

/* UX-C22: a rule version for reviewers. Badges on one line with the dates, text at a readable measure, cited sections as
   chips, parameters as a labelled list (lists in two columns), and the raw JSON folded away. */
.rule-version-head { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3); }
.rule-text { max-width: 44.5em; }
.tag-list { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--space-1-5); }
.tag-link { color: var(--brand-strong); border-color: var(--brand); text-decoration: none; }
.tag-link:hover { background: var(--brand-soft); text-decoration: underline; }
.rule-parameters { display: grid; grid-template-columns: minmax(0, 14rem) minmax(0, 1fr); gap: var(--space-2) var(--space-4); max-width: 44.5em; margin: 0; }
.rule-parameters dt { color: var(--ink-muted); font-weight: var(--weight-semibold); }
.rule-parameters dd { margin: 0; }
.rule-parameter-items { columns: 2 12rem; column-gap: var(--space-6); margin: 0; padding-left: var(--space-5); }
.rule-parameter-items > li { break-inside: avoid; }
@media (max-width: 599px) { .rule-parameters { grid-template-columns: 1fr; gap: var(--space-1); } .rule-parameters dd + dt { margin-top: var(--space-2); } }

/* UX-C20: the audit log. Filters on two rows; each event is one line (time · who · what · record) with what changed
   behind its own fold; times in tabular figures. */
.audit-who { flex: 1 1 200px; max-width: 280px; }
.audit-toggles { align-items: center; }
.audit-toggles .check { align-items: center; }
.audit-change > summary { display: flex; align-items: baseline; gap: var(--space-2); min-width: 0; cursor: pointer; list-style: none; }
.audit-change > summary::-webkit-details-marker { display: none; }
.audit-change > summary::after { content: "+"; flex: none; margin-left: auto; color: var(--brand); font-weight: var(--weight-semibold); }
.audit-change[open] > summary::after { content: "−"; }
.audit-change-action { flex: none; }
.audit-change-line { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.audit-change[open] .audit-change-line { visibility: hidden; }
.audit-change-body { padding: var(--space-1) 0 var(--space-1); }
@media (min-width: 600px) {
  .audit-table .audit-what { width: 100%; max-width: 0; }
  .audit-table .audit-who-cell, .audit-table .audit-record { white-space: nowrap; }
}

/* UX-C26: ownership check. The Match table compares what the owner says with the document, line by line. */
.ownership-match tbody th { position: static; box-shadow: none; background: transparent; font-weight: var(--weight-semibold); white-space: nowrap; vertical-align: top; }
.ownership-match td { vertical-align: top; }
.ownership-match .match-check { white-space: nowrap; }
.match-note { display: block; }
.match-mark { font-weight: var(--weight-bold); }
.match-mark.is-match { color: var(--good); }
.match-mark.is-mismatch { color: var(--critical); }

/* UX-C27: metric tiles. The change on the previous period has an arrow and good or bad colour; a 24px trend line shows
   the selected range. */
.sparkline { display: block; width: 100%; height: 24px; overflow: visible; }
.sparkline polyline { fill: none; stroke: var(--brand); stroke-width: 1.5; stroke-linejoin: round; stroke-linecap: round; vector-effect: non-scaling-stroke; }
.sparkline.is-flat polyline { stroke: var(--line-strong); }
.metric-delta { display: inline-flex; flex-wrap: wrap; gap: var(--space-1); font-weight: var(--weight-semibold); }
.metric-delta-good { color: var(--good); }
.metric-delta-critical { color: var(--critical); }
.metric-delta-neutral { color: var(--ink-muted); }
.metric-cost { width: fit-content; text-decoration: underline dotted; text-underline-offset: 2px; cursor: help; }

/* On screens wider than the app, the centred app gets a left edge to match the sidebar's right one. */
@media (min-width: 1441px) { .sidebar { border-left: 1px solid var(--line); } .app { border-right: 1px solid var(--line); } }
/* A wide page fills screens up to 1920px, so it only gets those edges beyond that. */
@media (min-width: 1441px) and (max-width: 1920px) { .app-wide .sidebar { border-left: 0; } .app.app-wide { border-right: 0; } }

/* ==== Round 4: owner money ==== */

/* R01: the rent page's forms (credit or charge, payment plan, usage charges) wrap to one column instead of pushing past a
   narrow content column; a form inside a flex details stretches to the column, never wider. */
.rent-page .form-grid { grid-template-columns: repeat(auto-fit, minmax(min(220px, 100%), 1fr)); }
.rent-page :is(.form-grid, form, .field) { min-width: 0; }
.rent-page .form-grid :is(.input, select) { max-width: 100%; }

/* R04: jump links to the arrears tools under the rent figures, and the tools themselves: reminders across the top, payment
   plan and usage charges side by side when there's room. */
.rent-tools-nav { display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-4); }
.rent-tools { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(22rem, 100%), 1fr)); gap: var(--space-4); align-items: start; }
.rent-tools > #reminders { grid-column: 1 / -1; }

/* R11: on phones the summary tiles are a 2 x 2 grid (every tile in view, none cut off) instead of a sideways strip. */
@media (max-width: 599px) {
  .dashboard-stats { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); overflow: visible; margin-inline: 0; padding: 0; scroll-snap-type: none; }
  .dashboard-stats > .stat { min-width: 0; }
}

/* ==== Round 4: owner setup ==== */

/* R02: the two ways to start a tenancy on a vacant property, side by side when there's room. */
.tenancy-start-choices { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(18rem, 100%), 1fr)); gap: var(--space-3); }
.tenancy-start-choices .choice { align-items: flex-start; }
/* The new-lease path's steps as one short line under the intro. */
.setup-steps-inline { display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-4); margin: 0; padding: 0; list-style: none; }
.setup-steps-inline [aria-current="step"] { color: var(--ink); }

/* R18: owner details on desktop: street and line 2 side by side, then suburb wider than state and postcode. */
@media (min-width: 600px) { .address-row { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr) minmax(0, 1fr); } }

/* R12: a member's role changes through an explicit Change role fold, which takes the row's full width when open. */
.team-role-change[open] { flex-basis: 100%; order: 10; }
.team-role-change[open] > summary { margin-bottom: var(--space-2); }

/* ==== Round 4: tenant ==== */
/* R34: tenant forms keep every field, drop zone and fold to the same width as the text fields on a wide screen. */
@media (min-width: 960px) {
  .tenant-form > :is(.form-grid, .file-drop, details, fieldset, .check),
  .tenant-form .fold .field { max-width: 720px; }
}
.tenant-access { margin: 0; padding: 0; border: 0; min-width: 0; }
.tenant-form-pair { grid-template-columns: repeat(auto-fit, minmax(min(180px, 100%), 1fr)); }
/* R33: on a wide screen the Agree / Disagree control sits next to the item, not 900px away; the list is capped. */
/* R25: on a routine report "Comment" opens an item's comment and photos; without script they all show. */
.tenant-check-toggle { min-height: 44px; }
/* R9-I7: theme.js marks <html class="js"> in <head>, so with script the boxes are closed from the first paint (no collapse when tenant-inspection.js runs). */
:root:not(.js-comment-toggle):not(.js) .tenant-check.is-optional .tenant-check-comment { display: flex; }
.tenant-check-comment .file-drop { max-width: 480px; }
.repair-expect { padding-top: var(--space-3); border-top: 1px solid var(--line); }

/* ==== Round 4: applicant ==== */
/* R40: renter profile. Progress first on phones; on desktop the sections on the left and the progress beside them, in view.
   About you and Income share one card, divided by a rule, with its Save at the end of the card. */
.profile-subsection { border-top: 1px solid var(--line); padding-top: var(--space-6); }
.profile-save-bar { border-top: 1px solid var(--line); padding-top: var(--space-4); }
.profile-edit-row { flex: 1 1 100%; }
@media (min-width: 1200px) {
  .renter-profile > .renter-profile-main { grid-column: 1; grid-row: 1; }
  .renter-profile > .renter-profile-aside { grid-column: 2; grid-row: 1; position: sticky; top: var(--space-6); }
}
/* R44: the neutral "nothing here yet" card keeps a readable width instead of running across the page. */
.my-applications-empty { max-width: 46em; }

/* ==== Round 4: trade ==== */
/* R14 4.8: the job's five-step stepper (Sent, Booked, Done, Invoiced, Paid) reuses the repair stepper's look. */
.stepper-5 { grid-template-columns: repeat(5, minmax(0, 1fr)); }
/* R51: the job and quote pages, details on the left and the next step on the right on a computer. */
.job-layout { display: grid; gap: var(--space-6); align-items: start; }
@media (min-width: 1024px) { .job-layout { grid-template-columns: minmax(0, 3fr) minmax(320px, 2fr); } }
/* R49: invoice lines, description beside amount. */
.invoice-lines { display: grid; gap: var(--space-2); }
.invoice-line { display: grid; gap: var(--space-2); }
@media (min-width: 600px) { .invoice-line { grid-template-columns: minmax(0, 2fr) minmax(8rem, 1fr); } }
/* R51: the trade's jobs. Cards on a phone; a table with the picked job beside it on a computer. */
.job-totals { display: flex; flex-wrap: wrap; gap: var(--space-6); margin: 0; }
.job-totals dt { color: var(--ink-muted); font-size: var(--text-md); line-height: var(--leading-md); }
.job-totals dd { margin: 0; font-size: var(--text-lg); line-height: var(--leading-lg); font-weight: var(--weight-semibold); }
.trade-jobs-desk { display: none; }
/* R9-J16: the table and its detail pane from 1200px; iPads (landscape too) keep the cards, which open a job on its page. */
@media (min-width: 1200px) {
  .trade-jobs-cards { display: none; }
  .trade-jobs-desk { display: grid; gap: var(--space-6); align-items: start; grid-template-columns: minmax(0, 2fr) minmax(300px, 1fr); }
  .job-detail { position: sticky; top: var(--space-4); }
}
.trade-jobs-desk tr.is-selected td { background: var(--neutral-soft); }
.trade-jobs-desk tr.is-selected td:first-child { box-shadow: inset 3px 0 0 var(--brand); }
/* R50: a trade's licence or insurance line on the owner's list, with its expiry flag beside it. */
.credential-line { display: inline-flex; flex-wrap: wrap; align-items: center; gap: var(--space-1-5); }

/* ==== Round 4: staff ops ==== */
/* Queue list + detail side by side from 1024px, so iPads in landscape keep the list (R9-F13; support, privacy: queue-split.js; trust: trust-queue.js). */
.queue-split, .trust-split { display: grid; gap: var(--space-4); align-items: start; }
@media (min-width: 1024px) {
  .queue-split.is-split { grid-template-columns: minmax(300px, 2fr) minmax(0, 3fr); }
  .trust-split.is-split { grid-template-columns: minmax(300px, 2fr) minmax(0, 3fr); }
  .queue-split.is-split .queue-inline-form { display: none; }
  .queue-pane, .trust-pane { position: sticky; top: var(--space-4); max-height: calc(100vh - var(--space-8)); overflow: auto; }
  .queue-pane .stack-lg { gap: var(--space-4); }
  .queue-pane .card { border: 0; padding: 0; box-shadow: none; background: transparent; }
  .queue-split.is-split .queue-rows .item-row, .queue-split.is-split [data-queue-row] { cursor: pointer; }
  .trust-split.is-split .trust-case[open] > .trust-case-panel { display: none; }
  .trust-pane .trust-case-panel { margin-top: 0; padding: 0; border: 0; background: transparent; }
}
[data-queue-row].is-selected, .trust-row.is-selected { background: var(--brand-soft); border-radius: var(--radius-md); }
.queue-rows > li.is-selected { padding-left: var(--space-2); padding-right: var(--space-2); }
.queue-keys { margin-bottom: var(--space-2); }
.queue-keys kbd, .trust-pane-nav kbd { font-family: var(--font-mono); font-size: var(--text-sm); padding: 0 var(--space-1); border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--surface); }
@media (max-width: 1023px) { .queue-keys { display: none; } }
.trust-pane-nav { margin-bottom: var(--space-3); padding-bottom: var(--space-3); border-bottom: 1px solid var(--line); }
.trust-case-title { margin: 0; }
.trust-case:not([open]) .trust-case-title, .trust-case[open] > .trust-case-panel .trust-case-title { display: none; }
.trust-pane .trust-case-title { display: none; }
/* Trust queue rows: a tick box for bulk dismiss, the case, its menu; the flag reason on the row (two lines at most). */
.trust-pick { display: flex; align-items: center; min-height: 44px; padding-right: var(--space-1); }
.trust-case-reason { display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.trust-assign { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-2); padding: var(--space-2) 0; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }
/* Support request: the note switch tints the reply box so it's clear the customer won't see it. */
.support-reply.is-note textarea { background: var(--warning-soft); border-color: var(--warning); }
.support-note + .support-note { padding-top: var(--space-3); border-top: 1px solid var(--line); }
.support-email-log td .caption { display: block; }
/* Security incidents: when Leasii became aware, when it was logged, the assess-by date; an append-only timeline. */
.incident-clock { grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); margin: var(--space-3) 0; }
.incident-timeline { list-style: none; margin: 0; padding: 0 0 0 var(--space-3); border-left: 2px solid var(--line); display: flex; flex-direction: column; gap: var(--space-3); }
.check-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: var(--space-1) var(--space-4); }
/* Ownership check on a phone or a tablet (R9-F14: up to 1279px, iPad landscape too): one column with the document first, at
   full width so it can be read, then the claim. Each column may shrink, so the Match table scrolls in its own box rather
   than the page, and on a phone its labels wrap so the tick boxes stay on screen. */
.ownership-review > * { min-width: 0; }
@media (max-width: 599px) {
  .ownership-match tbody th, .ownership-match .match-check { white-space: normal; }
}
@media (max-width: 1199px) {
  .ownership-review { grid-template-columns: minmax(0, 1fr); }
  .ownership-review > .ownership-evidence { order: -1; }
  .ownership-evidence .pdf-pages { height: 55vh; min-height: 280px; }
  .ownership-evidence-image { max-height: 60vh; object-fit: contain; }
}

/* ==== Round 4: staff admin ==== */
/* R69/R70: rules library. Jump links to each state; the table header sticks to the page on desktop (no scrolling box
   around it); on a phone each state folds under its heading (wwwroot/js/rules-groups.js). */
.rules-jump { display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-3); font-size: var(--text-sm); line-height: var(--leading-sm); }
@media (min-width: 600px) { .rules-table-wrap { overflow: visible; } }
.rules-assign { flex-wrap: wrap; }
.rules-select { width: var(--space-8); }
.rules-group-toggle { all: unset; cursor: pointer; display: inline-flex; align-items: center; gap: var(--space-2); min-height: 44px; }
.rules-group-toggle:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.rules-group-toggle::after { content: "▾"; color: var(--ink-muted); }
tbody.is-folded .rules-group-toggle::after { content: "▸"; }
/* R11-I2: the tbody is folded in the markup, so on a phone with script the rows are hidden from the first paint (rules-groups.js
   only adds the toggles and opens a state); wider screens, and phones without script, show every row. */
@media (max-width: 599px) {
  .js tbody.is-folded > tr:not([data-table-group]) { display: none; }
  .js .rules-table tr[data-table-group] > th { min-height: calc(44px + 2 * var(--space-1)); }
}
@media (min-width: 600px) { .rules-group-toggle { pointer-events: none; min-height: 0; } .rules-group-toggle::after { content: none; } }

/* R66: what's missing on official forms, state by state. */
.forms-gaps { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-2); }
.forms-gap-state { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3); padding: var(--space-2) 0; border-top: 1px solid var(--line); }
.forms-gap-state:first-child { border-top: 0; }

/* R68: bond rules and guides, one card per state. */
.bond-states { display: grid; gap: var(--space-4); grid-template-columns: repeat(auto-fill, minmax(min(100%, 22rem), 1fr)); align-items: start; }

/* R67: each state's go-live checklist next to its Open switch. */
.state-readiness-grid { display: grid; gap: var(--space-4); grid-template-columns: repeat(auto-fill, minmax(min(100%, 24rem), 1fr)); align-items: start; }
.state-readiness-head { display: flex; justify-content: space-between; align-items: flex-start; gap: var(--space-3); }
.readiness-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-2); }
.readiness-item { display: flex; gap: var(--space-2); align-items: flex-start; }
.readiness-mark { flex: none; width: var(--space-5); text-align: center; font-weight: var(--weight-semibold); }
.readiness-item.is-done .readiness-mark { color: var(--good); }
.readiness-item.is-blocker .readiness-mark { color: var(--critical); }
.readiness-item.is-open .readiness-mark { color: var(--warning); }
.state-readiness-site { display: grid; gap: var(--space-2); padding-top: var(--space-2); border-top: 1px solid var(--line); }
.switch-row { display: flex; justify-content: space-between; align-items: center; gap: var(--space-3); }

/* R71, R79, R80, R83: set-up alerts on the staff home and the pages that fix them. */

/* R85: the style guide's ledger example sits in a ledger container, so it stacks at 1024 like the real ledger. */
.sg-ledger { min-width: 0; }

/* ==== Round 4: follow-ups ==== */

/* ==== Round 5: owner ==== */
/* Owner details: the company and trust fields hide for a person or joint owners (owner-details.js). */
/* Rent page, unpaid rent steps: where the timeframes come from, on its own line under the reminders sentence. */
.rule-source { padding-top: var(--space-2); border-top: 1px solid var(--line); }
/* New tenancy: the checks the lease needs before it starts. */
.pre-lease-checks { margin: 0; padding: 0; list-style: none; display: grid; gap: var(--space-2); }
.pre-lease-checks > li { display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-2); align-items: baseline; }
/* Upcoming legal change (e.g. QLD's 2027 smoke alarm upgrade): information, not a check, so an amber edge and no status colour fill. */
.upcoming-change { border-left: 4px solid var(--warning); }
.upcoming-change-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-2); }
.upcoming-change-head > .label { margin: 0; }
.upcoming-change-due { margin: 0; }
.upcoming-change-list { margin: 0; padding-left: var(--space-5); display: grid; gap: var(--space-1); }
/* A date from a rule that isn't verified yet: a quiet note beside it (_RuleCheck), never a warning. */
.rule-check { color: var(--ink-muted); }
.compliance-main .rule-check, .pre-lease-checks .rule-check { display: block; }

/* ==== Round 5: tenant ==== */
/* Moving out: the researched draft notice rule, shown as a guide only and clearly marked as not yet confirmed. */
/* Repair: the landlord's latest update above the timeline, so the page and the notification say the same thing. */
.repair-latest { padding: var(--space-3) var(--space-4); border-left: 3px solid var(--info); border-radius: var(--radius-sm); background: var(--info-soft); }
/* Landlord details: labels above values so a long email or address has the card's full width; emails break only at @ and dots. */
.landlord-details, .landlord-details > div { grid-template-columns: minmax(0, 1fr); gap: var(--space-0-5); }
.landlord-details > dd + dt { margin-top: var(--space-2); }
.landlord-details dd { overflow-wrap: break-word; }
.landlord-missing { margin: 0; padding-left: var(--space-5); display: grid; gap: var(--space-1); }
/* Phones: while a text field has focus (the keyboard is open), the tab bar hides and the sticky action bar drops back into
   the page, so neither covers the field being typed in or the hint under it. */
@media (max-width: 959px) {
  .app:has(.content :is(textarea, select, input:not([type="checkbox"], [type="radio"], [type="submit"], [type="button"], [type="file"], [type="hidden"])):focus) > .tabbar { display: none; }
  .app:has(.content :is(textarea, select, input:not([type="checkbox"], [type="radio"], [type="submit"], [type="button"], [type="file"], [type="hidden"])):focus) .action-bar { position: static; margin-inline: 0; padding-inline: 0; border-top: 0; background: none; }
}

/* ==== Round 5: applicant and trade ==== */

/* ==== Round 5: staff ops ==== */
/* Audit log on phones: 25 entries at a time (audit-log.js hides the rest until "Show 25 more"). */
.audit-table tr[hidden] { display: none; }
.audit-more { justify-self: start; display: none; }
/* R11-I2: drawn this way from the first paint, not after audit-log.js runs: the filter of an unfiltered log is folded,
   rows 26 on are hidden and the "Show 25 more" button shows. Without script every row and the filter show. */
@media (max-width: 599px) {
  .js .audit-filter-fold[data-fold-phone="closed"] > :not(summary) { display: none; }
  .js .audit-table.is-paged tbody tr:nth-child(n+26) { display: none; }
  .js .audit-more:not([hidden]) { display: inline-flex; }
}
/* Audit entries for Leasii's own settings: what each changed from and to. */
.audit-values { margin: 0; }
/* Incident drafts for the OAIC and the people affected: plain text to copy, kept readable. */
.breach-draft { font-family: var(--font-mono); font-size: var(--text-sm); line-height: var(--leading-md); white-space: pre-wrap; }
/* System health: label and value side by side on wider screens. */
@media (min-width: 600px) {
  .health-dl > div { grid-template-columns: minmax(12rem, 18rem) minmax(0, 1fr); align-items: baseline; }
}

/* ==== Round 5: compliance and admin ==== */
/* States: an open state's blockers stay visible; the website settings fold on phones (phone-fold.js). */
.state-blockers { color: var(--critical); }
.state-readiness-site-fold > summary { font-size: var(--text-sm); }
/* R11-I2: folds marked data-phone-closed (states, bond rules, forms) are drawn closed on a phone from the first paint;
   phone-fold.js then closes the details element itself and marks it data-phone-ready. Without script they stay open. */
@media (max-width: 599px) { .js details[data-phone-closed]:not([data-phone-ready]) > :not(summary) { display: none; } }
/* Bond page and rules library: bulk send to legal review. */
.bond-send { flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3); }
.bond-send > .label { flex: 1 1 16rem; }
/* Rule parameters: a list of objects (phrase and why) as a small table, not raw JSON. */
.rule-parameter-table { font-size: var(--text-sm); }
.rule-parameter-table td { vertical-align: top; overflow-wrap: normal; }
/* Form mapping editor: inputs fill their cells. */
.mapping-editor td .input { width: 100%; min-width: 8rem; }
.mapping-editor td:last-child { width: 1%; }
/* Billing: ledger totals and backups as label–value rows on wider screens. */
@media (min-width: 600px) {
  :is(.billing-totals, .backup-lines) { grid-template-columns: minmax(0, 12rem) minmax(0, 1fr); column-gap: var(--space-4); }
  :is(.billing-totals, .backup-lines) > dd + dt { margin-top: 0; }
}
.backup-checklist { margin: 0; padding-left: var(--space-5); }
/* Pricing: the plans and offers tables fit at 1024 (status badges wrap; the name also opens the plan). */
.pricing-plans .badge { white-space: normal; }
.pricing-plans td.col-end { width: 1%; white-space: nowrap; }
@media (min-width: 600px) and (max-width: 1199px) { .pricing-plans th:first-child, .pricing-plans td:first-child { display: none; } }

/* ==== Accessibility: shared ==== */

/* A8, B6, C7: placeholders meet 4.5:1 in both themes (the browser's grey is 3.54:1 on the dark field). Instructions belong
   in hint text, not placeholders (PO-24); a placeholder is only ever an example. */
::placeholder { color: var(--ink-placeholder); opacity: 1; }

/* A2, B2, C4 (2.4.11 Focus Not Obscured): a field or link the browser scrolls to stays clear of whatever sticks to the
   top or bottom of the screen. Sticky headers inside the content use scroll-margin on the controls under them, so they
   add to the page's own scroll padding. */
@media (max-width: 959px) {
  html:has(.property-tabs) { scroll-padding-top: var(--space-16); }
}
html:has(.rule-jump) { scroll-padding-top: calc(var(--space-16) + var(--space-2)); }
.ledger :is(a, button, summary, input, select, textarea) { scroll-margin-top: var(--space-12); }
@media (min-width: 600px) {
  .table-compact tbody :is(a, button, summary, input, select, textarea, label) { scroll-margin-top: var(--space-12); }
  /* The rules library pins its state row under the column header. */
  .rules-table tbody :is(a, button, summary, input, select, textarea, label) { scroll-margin-top: calc(var(--space-8) * 2 + var(--space-4)); }
}
/* The save bar sticks to the bottom on a computer once something has changed. */
@media (min-width: 960px) {
  html:has(.save-bar.is-dirty) { scroll-padding-bottom: var(--space-24); }
}
/* B4: on phones the renter profile's Save sticks only once something has changed, not over the fields from the start. */
@media (max-width: 959px) {
  .profile-save-bar.action-bar:not(.is-dirty) { position: static; }
}

/* B10: tab labels keep a gap between them at 320px ("My rental" and "Messages" no longer run together); a long label
   wraps onto a second line rather than touching its neighbour. */
@media (max-width: 379px) {
  .tab { font-size: var(--text-xs); line-height: var(--leading-xs); }
}

/* A4, C8: the error summary (Accessibility/ErrorSummaryTagHelper.cs). It takes focus when the page loads, so its focus
   ring is clear; each problem is a link to its field. */
.error-summary { display: flex; flex-direction: column; gap: var(--space-2); }
.error-summary:focus { outline: 3px solid var(--critical); outline-offset: 2px; }
.error-summary-title { margin: 0; font-size: var(--text-base); line-height: var(--leading-base); font-weight: var(--weight-semibold); color: inherit; }
.error-summary-list { display: grid; gap: var(--space-1); }
.error-summary-list a { color: inherit; font-weight: var(--weight-semibold); text-decoration: underline; }
.error-summary-list a:hover { text-decoration-thickness: 2px; }

/* A5: "(optional)" after a label (OptionalLabelTagHelper): fields are required unless marked optional. */
.field-optional { font-weight: var(--weight-regular); color: var(--ink-muted); }
/* "All fields are required unless marked optional", said once at the top of a form. */
.form-required-note { margin: 0; }

/* B4 (1.4.10, 1.4.4): on a short screen (a phone on its side, or 200-400% zoom) nothing sticks: bars that stay on screen
   would leave room for only a few lines of the page. Scroll padding for them goes too. */
@media (max-height: 500px) {
  .action-bar, .save-bar.is-dirty, .app > .tabbar, .tabbar, .property-tabs, .rule-jump, .ledger-month-heading,
  .table-compact th, .rules-table .rules-group th, .app .room-pager-sticky, .settings-nav { position: static; }
  /* Overrides each page's own sticky offsets above, whatever their specificity. */
  html { scroll-padding-block: 0 !important; }
  .ledger :is(a, button, summary, input, select, textarea),
  .table-compact tbody :is(a, button, summary, input, select, textarea, label),
  .rules-table tbody :is(a, button, summary, input, select, textarea, label) { scroll-margin-top: 0; }
}

/* ==== Accessibility: owner ==== */
/* A11Y-A1: the "Paid on" date now has a visible label; the Paid button lines up with the field, not the label. */
.utility-paid-form { align-items: end; }
/* A11Y-A3: Reports' month field applies with a Show button beside it, not on change. */
.report-month { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
/* A11Y-A1: bank statement triage. On phones each control shows its own small label; from 1024px the rows line up under
   one set of column headings and the per-control labels go (the lists stay named by the heading, aria-labelledby). */
.import-head { display: none; }
.import-row { display: flex; flex-direction: column; gap: var(--space-3); }
.import-cell { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; }
.import-cell.actions { flex-direction: row; }
.import-cell-label { font-size: var(--text-sm); line-height: var(--leading-sm); font-weight: var(--weight-semibold); color: var(--ink-muted); }
.import-cell > .input, .import-rent .actions .input { width: 100%; max-width: 100%; }
.import-rent .actions { flex-wrap: nowrap; }
@media (min-width: 1024px) {
  .import-head, .import-row { display: grid; grid-template-columns: minmax(0, 2fr) repeat(3, minmax(0, 1.25fr)) minmax(0, 1.5fr); gap: var(--space-3); align-items: start; }
  .import-head { font-size: var(--text-sm); line-height: var(--leading-sm); font-weight: var(--weight-semibold); color: var(--ink-muted); padding-bottom: var(--space-2); border-bottom: 1px solid var(--line); }
  .import-cell-label { display: none; }
  .import-tx { grid-column: 1; }
  .import-rent { grid-column: 2; }
  .import-property { grid-column: 3; }
  .import-category { grid-column: 4; }
  .import-assign { grid-column: 5; }
}

/* ==== Accessibility: renters ==== */
/* A11Y-B3: the tenant inspection's "unsaved changes" and "saving this room first" messages take no space until they say
   something, but stay in the accessibility tree so the role="status" announcement is reliable. */
[data-room-unsaved]:empty, [data-finish-status]:empty { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
[data-room-unsaved]:not(:empty) { color: var(--ink); font-weight: var(--weight-semibold); }

/* ==== Accessibility: staff ==== */
/* A11Y-C1: the staff sign-in time-limit dialog (staff-session.js) and the note under a text box whose unsent text came back. */
.staff-session-dialog [hidden] { display: none; }
.staff-draft-note { margin-top: var(--space-1); }
/* A11Y-C2: the queue shortcuts hint with its on/off switch (wide screens only, like the shortcuts). */
@media (min-width: 1024px) { .queue-keys { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-1) var(--space-4); } }
.queue-keys > p { margin: 0; }
.queue-keys-switch[hidden], .queue-keys > p[hidden] { display: none; }
/* A11Y-C5: the trust pane is named by its "Case: …" heading; the case's own h3 would repeat it. */
.trust-pane-title { margin: 0 0 var(--space-3); }
/* A11Y-C6: the first of the next 25 audit entries takes focus. */
.audit-table tr[tabindex="-1"]:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }
/* A11Y-C9: PDF.js text layer over each page's picture (pdf-viewer.js): transparent text in the same place as the drawn
   text, for screen readers, find in page and copying. Adapted from PDF.js's own viewer styles; --total-scale-factor
   follows the zoom and --min-font-size is measured by PDF.js. */
.pdf-text-layer {
  --total-scale-factor: 1; --min-font-size: 1; --scale-round-x: 1px; --scale-round-y: 1px;
  --min-font-size-inv: calc(1 / var(--min-font-size));
  --text-scale-factor: calc(var(--total-scale-factor) * var(--min-font-size));
  position: absolute; inset: 0; overflow: clip; line-height: 1; text-align: initial; text-size-adjust: none; -webkit-text-size-adjust: none;
  forced-color-adjust: none; transform-origin: 0 0; caret-color: CanvasText; z-index: 0;
}
.pdf-text-layer :is(span, br) { position: absolute; color: transparent; white-space: pre; cursor: text; transform-origin: 0% 0%; }
.pdf-text-layer span { --font-height: 0; --scale-x: 1; --rotate: 0deg; font-size: calc(var(--text-scale-factor) * var(--font-height)); transform: rotate(var(--rotate)) scaleX(var(--scale-x)) scale(var(--min-font-size-inv)); z-index: 1; }
.pdf-text-layer span.markedContent { top: 0; height: 0; }
.pdf-text-layer ::selection { background: color-mix(in srgb, var(--brand) 30%, transparent); }
/* A11Y-C11: the style guide's heading samples are paragraphs styled as headings, so the page keeps one h1. */
.sg-h1 { font-family: var(--font-display); font-weight: 700; font-size: var(--text-display-sm); line-height: var(--leading-display-sm); }
.sg-h2 { font-family: var(--font-display); font-weight: 600; font-size: var(--text-xl); line-height: var(--leading-xl); }
.sg-h3 { font-size: var(--text-lg); line-height: var(--leading-lg); font-weight: 600; }

/* ==== Consistency: shared ==== */
/* Badges and actions beside a page title (_PageHeader): one row, top right on desktop, under the title on phones. */
.page-header-aside {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); flex: 0 1 auto; min-width: 0;
  max-width: 100%; align-self: flex-start;
}

/* ==== Consistency: staff ==== */
/* CON-C1: one staff title size, 28px at every width, keyed to .app-wide (Admin pages and every page a staff account opens, set by _Layout). Phones match
   customers (28); from 960px customers get 40 and staff stay at 28, a clear step above the 22px h2. */
.app-wide h1 { font-size: var(--text-2xl); line-height: var(--leading-2xl); }

/* ==== Consistency: owner-a ==== */
/* CON-A2: a long street as the eyebrow wraps rather than pushing the page sideways. */
.page-header .label { overflow-wrap: anywhere; }
/* CON-A7: a card's heading row, with or without a button, keeps the title on the same line as its neighbours' titles. */
.card-header { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-2) var(--space-4); min-height: 44px; }
/* CON-A16: a compliance check's badges sit under its name (as on every list row), so the row is name, then its action. */
.compliance-main .compliance-status { flex-direction: row; flex-wrap: wrap; align-items: center; }
@media (min-width: 600px) { .compliance-row { grid-template-columns: minmax(0, 1fr) auto; } }

/* ==== Consistency: shell ==== */
/* CON-A15/B12: one style for a label over a group of links or rows: 12px semibold muted, sentence case, no tracking. The
   sidebar's groups, the account and admin menus, the settings side menu, the /settings list and the Notifications day
   groups all use it. New markup can use .group-label. */
.group-label, .nav-heading, .nav-subheading, .settings-nav-heading, .notification-group {
  font-size: var(--text-xs); line-height: var(--leading-xs); font-weight: var(--weight-semibold);
  letter-spacing: normal; text-transform: none; color: var(--ink-muted);
}
/* STANDARDS (Layout): the one reading width, for a form's body or running text only, never for a whole page. */
.measure { max-width: 46em; }

/* ==== Consistency: renters ==== */
/* CON-B11: the one neutral alert, for a guide or an estimate that isn't a warning (Moving out's "As a guide"): the alert's
   shape and padding on a neutral surface with a solid border, no icon. Dashed borders are for drop zones only. */
.alert-neutral { background: var(--neutral-soft); border-color: var(--line-strong); }
.alert-neutral .tag { margin-left: var(--space-1); }
/* CON-C4: one look for the staff queues (Support, Privacy requests, Trust and safety): the selected row is tinted with a
   brand edge, the list is one card of divided rows, and the pane's header holds Previous, Next and the full page link. */
:is([data-queue-row], .trust-row).is-selected { background: var(--brand-soft); border-radius: var(--radius-md); box-shadow: inset 3px 0 0 var(--brand); }
.queue-rows > li.is-selected, .trust-row.is-selected { padding-left: var(--space-3); padding-right: var(--space-2); }
.queue-row-rich > .queue-inline-form { flex: 1 1 100%; }
.queue-pane-nav {
  position: sticky; top: 0; z-index: 2; display: flex;
  flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-2) var(--space-3);
  margin: 0 calc(-1 * var(--space-5)) var(--space-4); padding: var(--space-3) var(--space-5);
  border-bottom: 1px solid var(--line); background: var(--surface-raised);
}
@media (min-width: 1024px) { .queue-pane .breadcrumbs { display: none; } }
/* CON-C5: one control height per row. Inside a filter toolbar every input, select and button is 40px; everywhere else 44. */
.toolbar :is(.btn, .input) { min-height: 40px; }
.toolbar .input { padding-top: var(--space-1-5); padding-bottom: var(--space-1-5); font-size: var(--text-md); line-height: var(--leading-md); }
/* CON-C13: a form's submit in a grid of fields keeps its natural width and lines up with the inputs' bottom edge. */
.field-action { display: flex; align-items: flex-end; }
.field-action > .btn { width: auto; }
/* CON-C9: a highlight that isn't a state ("Most popular") is a tag in the brand's soft tone, never the amber key colour. */
.tag-highlight { border-color: var(--brand); background: var(--brand-soft); color: var(--brand-strong); }
/* CON-C14: a rule's parameter table keeps each phrase whole; the longer explanation column wraps instead. */
.rule-parameter-table td:first-child { min-width: 16ch; }
/* A11Y leftover (2.4.11): on /admin/rules the column header and the state row stick to the top. Focus moved by Tab or
   Shift+Tab onto a row already on screen doesn't use scroll-margin, so the page itself keeps that band clear. */
@media (min-width: 600px) and (min-height: 501px) {
  html:has(.rules-table) { scroll-padding-top: calc(var(--space-12) + var(--space-10)); }
}
/* CON-C10: the style guide shows both page-title sizes: customer pages step up to 40px from 960px, staff pages stay 28. */
@media (min-width: 960px) { .sg-h1 { font-size: var(--text-3xl); line-height: var(--leading-3xl); } }
.sg-h1-staff { font-family: var(--font-display); font-weight: 700; font-size: var(--text-2xl); line-height: var(--leading-2xl); }
.sg-rules { margin: 0; padding-left: var(--space-5); display: flex; flex-direction: column; gap: var(--space-1); }

/* ==== Staff home ==== */
/* Staff alerts: the title carries the tone, the text stays readable ink; a × hides one until it changes or tomorrow. */
.admin-alerts { display: flex; flex-direction: column; gap: var(--space-3); }
.admin-alerts:empty { display: none; }
.admin-alert { display: flex; align-items: flex-start; gap: var(--space-3); margin: 0; }
.admin-alert.alert-error { color: var(--ink); }
.admin-alert-body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: var(--space-1); }
.admin-alert-body p { margin: 0; }
.admin-alert-title { font-weight: var(--weight-semibold); color: var(--alert-tone); }
.admin-alert-list { margin: 0; padding-left: var(--space-5); color: var(--ink-muted); }
.admin-alert-action { font-weight: var(--weight-semibold); }
.admin-alert.banner-actionable { align-items: center; flex-wrap: wrap; }
.admin-alert.banner-actionable .admin-alert-body { flex-basis: 16rem; }
.admin-alert.banner-actionable .admin-alert-action { margin-left: auto; }
.admin-alert-close { flex: none; margin: calc(-1 * var(--space-2)) calc(-1 * var(--space-2)) 0 0; }
.admin-alert-close .icon-btn { border-color: transparent; background: transparent; color: var(--ink-muted); }
.admin-alert-close .icon-btn:hover { border-color: color-mix(in srgb, var(--alert-tone) 35%, transparent); background: var(--surface-raised); color: var(--ink); }
.hidden-alerts { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-1) var(--space-2); margin: 0; }
.hidden-alerts .btn-link { min-height: 24px; }

/* Queue cards: one height on every row, the same anatomy whether full or empty — name, count, what's counted, then a
   footer for the oldest item and any target badge. The left edge carries the state: red past target, amber waiting. */
.staff-queues { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 13.5rem), 1fr)); grid-auto-rows: 1fr; gap: var(--space-4); }
.staff-queues > li { display: flex; min-width: 0; }
.staff-queue {
  --queue-tone: var(--line); position: relative; flex: 1; display: flex; flex-direction: column; gap: var(--space-1); min-width: 0;
  padding: var(--space-4) var(--space-4) var(--space-4) var(--space-5); border: 1px solid var(--line); border-radius: var(--radius-lg);
  background: var(--surface-raised); color: inherit; text-decoration: none; overflow: hidden;
  transition: border-color var(--duration-fast) var(--ease-standard), box-shadow var(--duration-fast) var(--ease-standard);
}
.staff-queue::before { content: ""; position: absolute; inset: 0 auto 0 0; width: 4px; background: var(--queue-tone); }
.staff-queue:hover { border-color: var(--brand); box-shadow: var(--shadow-raised); }
.staff-queue.has-items { --queue-tone: var(--warning); }
.staff-queue.is-overdue { --queue-tone: var(--critical); }
.staff-queue.is-empty { --queue-tone: transparent; background: var(--surface); }
.staff-queue-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-2); }
.staff-queue-label { font-weight: var(--weight-semibold); font-size: var(--text-md); line-height: var(--leading-md); }
.staff-queue-go { flex: none; margin-top: var(--space-0-5); color: var(--ink-muted); transition: transform var(--duration-fast) var(--ease-standard); }
.staff-queue:hover .staff-queue-go { color: var(--brand); transform: translateX(2px); }
.staff-queue-count { margin-top: var(--space-1); font-family: var(--font-display); font-size: var(--text-2xl); line-height: var(--leading-2xl); font-weight: var(--weight-bold); }
.staff-queue.has-items .staff-queue-count { color: var(--warning); }
.staff-queue.is-overdue .staff-queue-count { color: var(--critical); }
.staff-queue.is-empty .staff-queue-count { color: var(--ink-muted); }
.staff-queue-unit { font-size: var(--text-sm); line-height: var(--leading-sm); color: var(--ink-muted); }
.staff-queue-foot { margin-top: auto; padding-top: var(--space-3); display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.staff-queue-meta { display: inline-flex; align-items: center; gap: var(--space-1); font-size: var(--text-sm); line-height: var(--leading-sm); color: var(--ink-muted); white-space: nowrap; }
.staff-queue.is-empty .staff-queue-meta { color: var(--good); }
@media (max-width: 599px) {
  .staff-queues { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-3); }
  .staff-queue { padding: var(--space-3) var(--space-3) var(--space-3) var(--space-4); }
  .staff-queue-count { font-size: var(--text-xl); line-height: var(--leading-xl); }
  .staff-queue-meta { white-space: normal; }
}

/* Needs attention and recently handled: one row per item, the whole row opens it (the title link stretches over it). */
.staff-work { display: grid; gap: var(--space-4); align-items: stretch; }
@media (min-width: 960px) { .staff-work { grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); } }
.staff-panel { display: flex; flex-direction: column; gap: var(--space-3); }
.staff-panel-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: var(--space-1) var(--space-3); }
.staff-work-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.staff-work-row {
  --work-icon: var(--tile); position: relative; display: flex; align-items: center; gap: var(--space-3); min-height: 56px; padding: var(--space-2);
  border: 1px solid var(--line); border-radius: var(--radius-md);
}
.staff-work-row:hover { background: var(--surface); }
.staff-work-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: var(--space-0-5); }
.staff-work-link { color: var(--ink); font-weight: var(--weight-semibold); text-decoration: none; }
.staff-work-link::after { content: ""; position: absolute; inset: 0; border-radius: var(--radius-md); }
.staff-work-link:hover { text-decoration: underline; }
.staff-work-link:focus-visible { outline: none; }
.staff-work-row:has(.staff-work-link:focus-visible) { outline: 2px solid var(--brand); outline-offset: 2px; }
.staff-work-row > .badge { flex: none; }
.staff-work-go { flex: none; color: var(--ink-muted); }
.staff-work-row:hover .staff-work-go { color: var(--brand); }
@media (max-width: 599px) {
  .staff-work-row { flex-wrap: wrap; }
  .staff-work-main { flex-basis: calc(100% - var(--work-icon) - var(--space-3)); }
  .staff-work-row > .badge { margin-left: calc(var(--work-icon) + var(--space-3)); }
  .staff-work-go { display: none; }
}

/* A card folded away as an accordion (Backups and recovery): title, a one-line status and a badge show while closed. */
.card-fold { padding: 0; }
.card-fold > summary {
  display: flex; align-items: center; gap: var(--space-3); min-height: 56px; padding: var(--space-4) var(--space-5);
  list-style: none; cursor: pointer; border-radius: var(--radius-lg);
}
.card-fold > summary::-webkit-details-marker { display: none; }
.card-fold > summary::after {
  content: ""; flex: none; width: 18px; height: 18px; background: var(--ink-muted); transition: transform var(--duration-fast) var(--ease-standard);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E") center / contain no-repeat;
}
.card-fold[open] > summary::after { transform: rotate(180deg); }
.card-fold > summary:hover { background: var(--surface); }
.card-fold > summary:hover::after { background: var(--brand); }
.card-fold-title { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: var(--space-0-5); }
.card-fold[open] > summary { border-bottom: 1px solid var(--line); border-radius: var(--radius-lg) var(--radius-lg) 0 0; }
.card-fold-body { padding: var(--space-4) var(--space-5) var(--space-5); }
.card-fold-body > .caption { margin: 0; }
@media (max-width: 599px) {
  .card-fold > summary { flex-wrap: wrap; padding: var(--space-3) var(--space-4); }
  .card-fold-body { padding: var(--space-3) var(--space-4) var(--space-4); }
}

/* ==== Page header ==== */
/* One header for every page (_PageHeader, _AdminHeader, _PropertyHeader): the section eyebrow in brand green, the title,
   one sentence of lede, then "How this works" folded for the detail. Actions sit level with the title block. */
/* Top-aligned, so opening "How this works" grows the text downwards and the actions stay where they are. */
.page-header > .stack-xs { flex: 1 1 20rem; min-width: 0; }
/* R13-J2: from 600px the badges and actions stay level with the title on every page: the title block yields first, the
   aside wraps inside itself (right-aligned, at most 60% of the row) before anything drops under the title. Phones keep
   the stack, left-aligned. Headers that only pair a card title with a state keep wrapping as before. */
@media (min-width: 600px) {
  .page-header:has(> .page-header-aside) { flex-wrap: nowrap; }
  .page-header > .page-header-aside { max-width: 60%; justify-content: flex-end; }
  .page-header-aside > .actions { justify-content: flex-end; }
}
.page-header h1 { margin: 0; }
.page-eyebrow { color: var(--brand-strong); font-weight: var(--weight-semibold); letter-spacing: 0.01em; }
.page-header .page-lede { color: var(--ink-muted); }
/* R8-B5: may shrink to the column, so its own wrapping kicks in instead of pushing the page sideways on phones. */
/* With an eyebrow above the title, the actions line up with the title itself. */
.page-header:has(> .stack-xs > .page-eyebrow) > .page-header-aside { margin-top: calc(var(--leading-md) + var(--space-1)); }
.page-more { max-width: 41.5em; }
.page-more > summary { font-size: var(--text-sm); line-height: var(--leading-sm); }
.page-more[open] > summary { margin-bottom: var(--space-2); }
.page-more-body {
  display: flex; flex-direction: column; gap: var(--space-2); padding: var(--space-1) 0 var(--space-1) var(--space-4);
  border-left: 2px solid var(--brand-soft); color: var(--ink-muted); font-size: var(--text-sm); line-height: var(--leading-sm);
}
.page-more-body p, .page-more-body ul { margin: 0; }
.page-more-body ul { padding-left: var(--space-5); }
/* R14 (3.1): the record's one state as a pill after the title (<div class="page-title-row"><h1>Alex and Sam</h1><span class="badge page-state">Current</span></div>),
   so the aside holds actions only. The compact header (section sub-pages: rent, notices, lease) steps the title down to 28px
   on desktop and sits directly on its tabs. */
.page-title-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-1) var(--space-2); }
.page-state { font-size: var(--text-sm); }
.page-header-compact + .tabs, .page-header-compact + .tabs-scroll { margin-top: var(--space-3); }
@media (min-width: 960px) { .page-header-compact h1 { font-size: var(--text-2xl); line-height: var(--leading-2xl); } }

/* ==== Sidebar ==== */
/* Group headings in the menu (Queues, Commercial, Content and rules, System) read at body size, not as fine print. */
.sidebar .nav-subheading, .more-menu .nav-subheading { font-size: var(--text-md); line-height: var(--leading-md); font-weight: var(--weight-semibold); color: var(--ink-muted); }
.sidebar .nav-fold > summary { min-height: 36px; }
.sidebar .nav-fold > summary:hover { color: var(--ink); }

/* ==== Account menu ==== */
/* The account button at the foot of the sidebar: avatar, name, and a chevron pointing up: the menu opens above (with the email). */
.sidebar-account:hover, body:has(#account-menu[open]) .sidebar-account { background: var(--surface); border-color: var(--line); }
.sidebar-account .avatar { width: 36px; height: 36px; font-size: var(--text-md); }
.sidebar-user-name {
  overflow-wrap: anywhere; font-size: var(--text-md); line-height: var(--leading-md);
  font-weight: var(--weight-semibold); color: var(--ink);
}

/* The menu: who you are, your pages, appearance and sign out, as one tidy list with dividers between the groups. */
.account-menu > .stack { gap: var(--space-3); }
.account-menu .modal-head { align-items: center; padding-bottom: var(--space-3); border-bottom: 1px solid var(--line); }
.account-summary .avatar { width: 40px; height: 40px; font-size: var(--text-lg); }
.account-summary h2.label { font-size: var(--text-md); line-height: var(--leading-md); }
.account-nav { gap: var(--space-0-5); padding-bottom: var(--space-3); border-bottom: 1px solid var(--line); }
.account-menu .nav-item { min-height: 40px; padding: 0 var(--space-2); font-weight: var(--weight-semibold); }
.account-menu .nav-item:hover { background: var(--surface); }
.account-menu .nav-item svg { color: var(--ink-muted); }
.account-menu .theme-picker legend { font-size: var(--text-xs); line-height: var(--leading-xs); font-weight: var(--weight-semibold); color: var(--ink-muted); }
.account-menu .theme-option { min-height: 32px; font-size: var(--text-sm); }
.account-signout-btn { width: 100%; border: 0; background: none; font: inherit; font-size: var(--text-md); font-weight: var(--weight-semibold); color: var(--critical); text-align: left; cursor: pointer; }
.account-signout-btn svg { color: currentColor; }
.account-signout-btn:hover { background: var(--critical-soft); }
@media (min-width: 960px) {
  /* A popover anchored above the account button, as wide as the sidebar's content, closed with Escape or a click outside. */
  .modal.account-menu {
    width: 232px; margin: auto auto calc(var(--space-16) + var(--space-4)) var(--space-2); padding: var(--space-3);
    border-radius: var(--radius-lg); box-shadow: var(--shadow-overlay);
  }
  .account-close { display: none; }
}
/* The menu header shows the whole email too, wrapped. */

/* ==== Staff queues ==== */
/* Support, Privacy requests, Trust and safety and Ownership checks share one look (CON-C4), after the staff home: a
   segmented filter bar with counts, then the list as a card of rows where the whole row opens the item (a stretched title
   link, or the case's summary on Trust): a tinted icon, the title, a muted meta line, badges and a chevron. The left edge
   carries the state (amber due soon, red past target); the selected row is brand-soft with a brand edge. On wide screens
   the selected item sits in its own card beside the list under a sticky header: Previous and Next on the left, a pop-out
   to the item's own page on the right. Keyboard shortcuts are off until turned on; the keys hint shows only while on. */
.staff-queue-page .page-header-aside form { margin: 0; }

/* R13-J1: the queue filters are the one shared filter bar (.filter-bar), the same for staff and customers. */

/* The list card's head: its title and count, the shortcuts switch on the right (wide screens only), and the keys hint
   below it only while shortcuts are on. */
.queue-list { display: flex; flex-direction: column; gap: var(--space-3); min-width: 0; }
.queue-list-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-1) var(--space-4); min-height: 44px; }
.queue-list-heading { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-1) var(--space-2); min-width: 0; }
.queue-list-heading > .card-title { margin: 0; }
.queue-list-heading > .caption {
  display: inline-flex; align-items: center; padding: 0 var(--space-2); border-radius: var(--radius-pill); background: var(--neutral-soft);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.queue-list-head .queue-keys { margin: 0 0 0 auto; align-items: center; }
.queue-list-head .queue-keys-switch { display: inline-flex; align-items: center; min-height: 44px; margin: 0; color: var(--ink-muted); }
.queue-keys-hint { margin: 0; padding: var(--space-2) var(--space-3); border-radius: var(--radius-md); background: var(--surface); }
.queue-keys-hint[hidden] { display: none; }
.queue-keys-hint kbd { font-family: var(--font-mono); font-size: var(--text-sm); padding: 0 var(--space-1); border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--surface-raised); }
@media (max-width: 1023px) { .queue-keys-hint { display: none; } }

/* Rows. --row-edge is the coloured left edge (an inset shadow, so the row keeps its shape and nothing shifts). */
.queue-rows, .trust-queue { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.queue-rows > .queue-row, .trust-queue > .trust-row {
  --row-edge: transparent; --row-icon: 36px; position: relative; display: grid; align-items: center; gap: var(--space-2) var(--space-3);
  min-height: 64px; padding: var(--space-3); border: 1px solid var(--line); border-radius: var(--radius-md);
  box-shadow: inset 3px 0 0 var(--row-edge); transition: background-color var(--duration-fast) var(--ease-standard);
}
.queue-rows > .queue-row { grid-template-columns: var(--row-icon) minmax(0, 1fr) auto; }
.queue-rows > .queue-row:hover, .trust-queue > .trust-row:hover { background: var(--surface); }
:is(.queue-rows > .queue-row, .trust-queue > .trust-row)[data-tone="Warn"] { --row-edge: var(--warning); }
:is(.queue-rows > .queue-row, .trust-queue > .trust-row)[data-tone="Critical"] { --row-edge: var(--critical); }
.queue-rows > .queue-row.is-selected, .trust-queue > .trust-row.is-selected {
  --row-edge: var(--brand); padding: var(--space-3); background: var(--brand-soft); border-color: color-mix(in srgb, var(--brand) 35%, transparent); box-shadow: inset 3px 0 0 var(--row-edge);
}
.queue-row-icon {
  flex: none; display: inline-flex; align-items: center; justify-content: center; width: var(--row-icon); height: var(--row-icon);
  border-radius: var(--radius-pill); background: var(--neutral-soft); color: var(--ink-muted);
}
[data-tone="Warn"] .queue-row-icon { background: var(--warning-soft); color: var(--warning); }
[data-tone="Critical"] .queue-row-icon { background: var(--critical-soft); color: var(--critical); }
.is-selected .queue-row-icon { background: var(--surface-raised); color: var(--brand-strong); }
.queue-row-main { min-width: 0; display: flex; flex-direction: column; gap: var(--space-0-5); }
.queue-row-link { color: var(--ink); font-weight: var(--weight-semibold); text-decoration: none; }
.queue-row-link::after { content: ""; position: absolute; inset: 0; border-radius: var(--radius-md); }
.queue-row-link:hover { text-decoration: underline; }
.queue-row-link:focus-visible { outline: none; }
.queue-row:has(.queue-row-link:focus-visible) { outline: 2px solid var(--brand); outline-offset: 2px; }
.queue-row-excerpt { color: var(--ink); display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.queue-row-badges { margin-top: var(--space-1); gap: var(--space-1-5); }
.queue-row-go { flex: none; color: var(--ink-muted); transition: transform var(--duration-fast) var(--ease-standard); }
.queue-row:hover .queue-row-go, .trust-case-summary:hover .queue-row-go { color: var(--brand); transform: translateX(2px); }
.queue-split.is-split .queue-rows > .queue-row { cursor: pointer; }
/* Privacy's quick update form sits under its row on narrower screens, above the row's stretched link. */
.queue-rows > .queue-row > .queue-inline-form { grid-column: 2 / -1; position: relative; z-index: 1; padding-top: var(--space-3); border-top: 1px solid var(--line); }

/* Trust rows: the bulk tick box, the case (its summary laid out like a row), its menu. */
.trust-queue > .trust-row { grid-template-columns: auto minmax(0, 1fr) auto; align-items: start; column-gap: var(--space-2); padding: var(--space-2); }
.trust-queue > .trust-row.is-selected { padding: var(--space-2); }
.trust-row > .trust-pick { min-height: 52px; padding-left: var(--space-1); }
.trust-case-summary > .queue-row-icon { grid-row: 1 / span 2; align-self: start; }
.trust-case-summary > .trust-case-main { grid-column: 2; gap: var(--space-0-5); }
.trust-case-summary > .trust-case-main > .label { font-weight: var(--weight-semibold); }
.trust-case-summary > .trust-case-badges { grid-column: 2; gap: var(--space-1-5); }
.trust-case-summary > .trust-case-open { grid-column: 3; grid-row: 1 / span 2; }
@media (max-width: 1023px) { .trust-case[open] .trust-case-open { transform: rotate(90deg); } }
.trust-case-summary:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.trust-bulk { align-self: flex-start; }

/* The selected item's card: a sticky header, sections divided by rules instead of cards inside a card. */
@media (min-width: 1024px) {
  .queue-pane:has(> .queue-pane-nav), .trust-pane { padding-top: 0; }
  .queue-pane [data-queue-detail] > .card { padding-top: var(--space-4); border-top: 1px solid var(--line); border-radius: 0; }
  .queue-pane [data-queue-detail] > .fold-action { align-self: flex-start; }
}
.queue-pane-steps { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.queue-pane-where { margin-left: var(--space-1); font-variant-numeric: tabular-nums; }
.queue-pane-where:empty { display: none; }
/* "Open on its own page": an icon button with its name as a tooltip on hover and keyboard focus. */
.queue-pane-pop { position: relative; flex: none; color: var(--ink-muted); }
.queue-pane-pop:hover, .queue-pane-pop:focus-visible { color: var(--brand-strong); border-color: var(--brand); }
.queue-pane-tip {
  position: absolute; top: calc(100% + var(--space-1-5)); right: 0; z-index: 3; padding: var(--space-1) var(--space-2); border-radius: var(--radius-sm);
  background: var(--ink); color: var(--surface-raised); font-size: var(--text-sm); line-height: var(--leading-sm); font-weight: var(--weight-semibold);
  white-space: nowrap; pointer-events: none; opacity: 0; visibility: hidden; transform: translateY(-2px);
  transition: opacity var(--duration-fast) var(--ease-standard), transform var(--duration-fast) var(--ease-standard), visibility var(--duration-fast);
}
.queue-pane-pop:hover .queue-pane-tip, .queue-pane-pop:focus-visible .queue-pane-tip { opacity: 1; visibility: visible; transform: none; }

/* A support request: the conversation as a thread (Leasii's replies tinted), then the reply box as one composer. */
.support-conversation { display: flex; flex-direction: column; gap: var(--space-4); }
.support-thread { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-3); }
.support-msg {
  --avatar: 32px; display: flex; flex-direction: column; gap: var(--space-2); padding: var(--space-3) var(--space-4);
  border: 1px solid var(--line); border-radius: var(--radius-lg); background: var(--surface-raised);
}
.support-msg.is-staff { border-color: color-mix(in srgb, var(--brand) 25%, var(--line)); background: var(--brand-soft); }
.support-msg-head { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-1) var(--space-2); }
.support-msg-avatar {
  flex: none; display: inline-flex; align-items: center; justify-content: center; width: var(--avatar); height: var(--avatar);
  border-radius: var(--radius-pill); background: var(--neutral-soft); color: var(--ink-muted); font-size: var(--text-sm); font-weight: var(--weight-semibold);
}
.support-msg.is-staff .support-msg-avatar { background: var(--brand); color: var(--on-brand); }
.support-msg-time { margin-left: auto; }
.support-msg-body { margin: 0; padding-left: calc(var(--avatar) + var(--space-2)); overflow-wrap: anywhere; }
.support-reply { padding: var(--space-4); border: 1px solid var(--line-strong); border-radius: var(--radius-lg); background: var(--surface); }
.support-reply .toolbar > select { flex: 1 1 11rem; min-width: 0; }
.queue-pane .support-reply .toolbar > .btn { flex: none; }

/* An ownership check in the pane: one column, the match table first and the latest document right under it (so the
   two are compared together), then the owner, identity, documents and the decision; sections divided by rules. */
@media (min-width: 1024px) {
  .queue-pane .ownership-review { grid-template-columns: minmax(0, 1fr); gap: var(--space-4); }
  .queue-pane .ownership-review > .stack-lg { display: contents; }
  .queue-pane .ownership-review > .stack-lg > * { order: 2; }
  .queue-pane .ownership-review > .stack-lg > :first-child { order: 0; }
  .queue-pane .ownership-review > .ownership-evidence { order: 1; position: static; }
  .queue-pane .ownership-review .card { padding-top: var(--space-4); border-top: 1px solid var(--line); border-radius: 0; }
  .queue-pane .ownership-review > .stack-lg > :first-child.card { padding-top: 0; border-top: 0; }
  .queue-pane .ownership-evidence .pdf-pages { height: 55vh; }
}

/* Side by side at 1024px and up (R9-F13: iPad landscape too), both columns share one height: the list card and the selected item's card stretch to
   the taller of the two (empty space under the last row is fine). The pane is no longer its own scroll box, so its
   header sticks to the top of the window while the item scrolls, and a newly shown item's heading clears that header. */
@media (min-width: 1024px) {
  .queue-split.is-split, .trust-split.is-split { align-items: stretch; }
  .queue-split.is-split > .queue-pane, .trust-split.is-split > .trust-pane { position: static; max-height: none; overflow: visible; }
  .queue-pane-nav { border-radius: var(--radius-lg) var(--radius-lg) 0 0; }
  .queue-pane h2[tabindex="-1"], .trust-pane-title { scroll-margin-top: var(--space-24); }
  .staff-equal-split { align-items: stretch; }
  .staff-equal-split > * { display: flex; flex-direction: column; }
  .staff-equal-split > * > :last-child { flex: 1 1 auto; }
}

/* Decision, notes and reply boxes use the whole width of their card, in the queues and on the full detail pages (the
   site-wide 720px cap on a lone form field is for settings-style forms). Labels and hints line up with the box. */
@media (min-width: 960px) {
  :is(.staff-queue-page, [data-queue-detail]) :is(form.stack, form.stack-lg, form.card, .card > form) > :is(.field, fieldset.field) { max-width: none; }
}
:is(.staff-queue-page, [data-queue-detail]) form textarea.input { width: 100%; }

/* Internal notes on a support request fold away (a card-fold) with their count in the summary. In the pane the fold
   lines up with the other sections instead of carrying a card's inset. */
.support-notes .support-note + .support-note { padding-top: var(--space-3); border-top: 1px solid var(--line); }
@media (min-width: 1024px) {
  .queue-pane [data-queue-detail] > .card-fold { padding-top: 0; }
  .queue-pane [data-queue-detail] > .card-fold > summary { padding-inline: 0; border-radius: 0; }
  .queue-pane [data-queue-detail] > .card-fold > summary:hover { background: transparent; }
  .queue-pane [data-queue-detail] > .card-fold > .card-fold-body { padding-inline: 0; }
}

/* Accounts a customer has let you open, above the support list. */
.queue-grants { list-style: none; margin: 0; padding: 0; }
.queue-grant { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-2) var(--space-4); padding: var(--space-3) 0; border-top: 1px solid var(--line); }
.queue-grant:first-child { border-top: 0; padding-top: 0; }
.queue-grant-main { flex: 1 1 16rem; min-width: 0; display: flex; flex-direction: column; gap: var(--space-0-5); }
.queue-grant form { margin: 0; }

@media (max-width: 599px) {
  .queue-rows > .queue-row, .trust-queue > .trust-row { --row-icon: 32px; gap: var(--space-2); padding: var(--space-3) var(--space-2) var(--space-3) var(--space-3); }
  .queue-rows > .queue-row { grid-template-columns: var(--row-icon) minmax(0, 1fr); }
  .queue-rows > .queue-row > .queue-row-go { display: none; }
  .queue-rows > .queue-row > .queue-inline-form { grid-column: 1 / -1; }
  .trust-case-summary { grid-template-columns: minmax(0, 1fr); column-gap: var(--space-2); }
  .trust-case-summary > .trust-case-open { display: none; }
  .trust-case-summary > .queue-row-icon { display: none; }
  .trust-case-summary > :is(.trust-case-main, .trust-case-badges) { grid-column: 1; }
  .support-msg { padding: var(--space-3); }
  .support-msg-body { padding-left: 0; }
  .support-msg-time { margin-left: 0; flex-basis: 100%; }
  .support-reply { padding: var(--space-3); }
}

/* ==== Tiles: one height ==== */
/* Every grid of tiles or cards across the site: tiles in a row are the same height, and from 600px every row of a grid
   is as tall as its tallest tile, so a grid reads as one even block (on phones, single-column lists keep their natural
   heights). Content that closes a tile — a caption, badges, a progress bar, actions — sits at its foot. */
:is(.stat-grid, .property-grid, .notice-choices, .ending-paths, .change-options, .tenancy-start-choices, .tenant-summary,
    .choice-list, .photo-grid, .inspection-room-grid, .sg-swatches, .bond-states, .state-readiness-grid, .rent-tools) {
  align-items: stretch;
}
@media (min-width: 600px) {
  :is(.stat-grid, .property-grid, .notice-choices, .ending-paths, .change-options, .tenancy-start-choices, .tenant-summary,
      .choice-list, .photo-grid, .inspection-room-grid, .sg-swatches) { grid-auto-rows: 1fr; }
}
/* A tile wrapped in a list item or form fills the wrapper. */
:is(.stat-grid, .property-grid, .notice-choices, .ending-paths, .change-options, .tenancy-start-choices, .tenant-summary,
    .photo-grid, .inspection-room-grid, .sg-swatches) > :is(li, form) { display: flex; flex-direction: column; min-width: 0; }
:is(.stat-grid, .property-grid, .notice-choices, .ending-paths, .change-options, .tenancy-start-choices, .tenant-summary,
    .photo-grid, .inspection-room-grid, .sg-swatches) > :is(li, form) > :is(.card, a, button, article, figure, label) { flex: 1 1 auto; }
:is(.stat-grid, .property-grid, .bond-states, .state-readiness-grid, .rent-tools) > .card { height: 100%; }
.stat > :is(.caption, .stat-progress, .stat-of):last-child { margin-top: auto; }
/* R13-J6: a tenant with several rentals sees a card per tenancy: the same 3:1 band as a property card, the address and
   its state, rent and paid-to (or when it ended and the bond), then the next thing to do. Full-width band on phones. */
.tenancy-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: var(--space-4); }
.tenancy-card { display: flex; flex-direction: column; padding: 0; overflow: hidden; }
.tenancy-card .property-body { flex: 1 1 auto; padding: var(--space-4); }
.tenancy-card .stat-value { font-size: var(--text-xl); line-height: var(--leading-xl); }
.tenancy-card .stat-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.tenancy-card .item-row > :first-child { flex: 1 1 auto; }
@media (max-width: 599px) {
  .tenancy-card .property-photo { width: auto; aspect-ratio: 3 / 1; border-radius: 0; border-bottom: 1px solid var(--line); }
  .tenancy-card .property-monogram { font-size: var(--text-3xl); line-height: var(--leading-3xl); }
}
/* R13-J5: every card's body sits right under its band; only the badge row sinks, so badges line up across the grid. */
.property-card .property-body { flex: 1 1 auto; display: flex; flex-direction: column; }
.property-card .property-body > .list-tags { margin-top: auto; padding-top: var(--space-2); }

/* ==== Review fixes: shared ==== */
/* R8-B5: page-header actions (_PageHeader, _PropertyHeader, _AdminHeader) wrap under the title on phones, full width,
   rather than running off the right edge. Pickers and inputs in the aside shrink to fit too. */
.page-header-aside > *, .page-header-aside > .actions > form { min-width: 0; max-width: 100%; }
.page-header-aside :is(.input, select) { min-width: 0; max-width: 100%; }
@media (max-width: 599px) {
  .page-header > .page-header-aside { flex: 1 1 100%; margin-top: 0; }
  .page-header-aside > .actions { flex: 1 1 100%; }
  .page-header-aside > .actions > .btn { flex: 1 1 auto; }
  /* A picker in the header (e.g. Money > Reports' month) takes its own row; its field shares the row with its button. */
  .page-header-aside > .actions > form { flex: 1 1 100%; }
  .page-header-aside > .actions > form > .input { flex: 1 1 8rem; width: auto; }
}

/* ==== Review fixes: batch 1 a ==== */
/* R8-A8: a property's plan step. The plans are radio cards (.choice-list); "Pay monthly or yearly" only shows for a plan
   with a monthly or yearly fee (no JavaScript needed). */
.plan-form:has(input[data-fee="false"]:checked) .plan-interval { display: none; }
.plan-choice-price { font-weight: 600; font-size: var(--text-md); line-height: var(--leading-md); }
.plan-choices .choice { height: 100%; }

/* R8-A6: the dashboard's setup list, one compact row per property with its next step; the six steps open on click. */
.onboarding-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-3); }
.onboarding-row { display: flex; flex-direction: column; gap: var(--space-2); }
.onboarding-row + .onboarding-row { padding-top: var(--space-3); border-top: 1px solid var(--line); }
.onboarding-row-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-2) var(--space-4); }
.onboarding-row-text { flex: 1 1 240px; min-width: 0; }
.onboarding-row .progress { flex: 0 1 160px; }
.onboarding-row-steps[open] > summary { margin-bottom: var(--space-3); }
.onboarding-more > .onboarding-list { padding-top: var(--space-1); }

/* ==== Review fixes: batch 2 b ==== */
/* R8-A14: the marketing header keeps five links between 900px and 1200px; "How it works" and "FAQ" join them on wide
   screens (they're always in the phone menu and the footer). */
.mk-nav a.mk-nav-wide { display: none; }
@media (min-width: 1200px) { .mk-nav a.mk-nav-wide { display: inline; } }
/* R8-A13: the homes for rent filters stack on phones and sit in one row from 960px, the button at its natural width. */
.homes-filters { display: grid; grid-template-columns: 1fr; gap: var(--space-3); align-items: end; }
@media (min-width: 960px) { .homes-filters { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr) minmax(0, 1fr) auto; } }
.homes-filters .field { margin: 0; }
/* R8-A3: words to try after a help search finds nothing. */
.help-suggestions { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-2) var(--space-4); list-style: none; margin: 0; padding: 0 0 var(--space-4); }
/* R8-A17: what's wrong with the owner's wording, checked as they type. */
.template-live-problems ul { margin: var(--space-1) 0 0; padding-left: var(--space-5); }

/* ==== Review fixes: batch 3 b ==== */
/* R8-B12: a rent increase's old and new rent, the change and the estimate, in a quiet panel under the amount. */
.rent-change { padding: var(--space-3) var(--space-4); border-radius: var(--radius-md); background: var(--surface); border: 1px solid var(--line); }
.rent-change p { margin: 0; }
.rent-confirm p { margin: 0; }

/* ==== Review fixes: batch 3 c ==== */
/* R8-C11: Listings' "New listing" picker has a visible label; the button lines up with the select, and on phones the
   pair takes the full width under the title instead of running off the right edge. */
.listing-new { align-items: flex-end; flex-wrap: wrap; }
.listing-new > .listing-new-field { flex: 1 1 16rem; min-width: 0; max-width: 100%; margin: 0; }
.listing-new-field > .input { width: 100%; min-width: 0; text-overflow: ellipsis; }
@media (min-width: 600px) { .listing-new > .listing-new-field { flex: 0 1 15rem; width: 15rem; } }

/* ==== Review fixes: batch 4 c ==== */
/* R8-D4: forms inside an error alert (the tenant's "I've paid" and payment-plan folds in the overdue banner) keep normal
   ink, so labels don't read as validation errors; red stays for the banner's heading and amount. */
.alert-error form { color: var(--ink); }
/* R8-D7: the examples of urgent repairs under "How urgent". */
.repair-urgent-list { margin: 0; padding-left: var(--space-5); }
.repair-urgent-note p { margin: 0; }

/* ==== Review fixes: batch 4 b ==== */
/* R8-C9: one segmented control for inspections (Yes / No / N/A) and the tenant's Agree / Disagree. Options share the row
   equally, unchosen options are regular weight and the chosen one is a solid fill, a tick and bold (A11Y-B1). */
.segmented-input:checked + .segmented-option { font-weight: var(--weight-semibold); }
.segmented-input:checked + .segmented-na { background: var(--ink-muted); color: var(--surface-raised); }
.segmented-input:checked:focus-visible + .segmented-option { outline-color: var(--surface-raised); }
.segmented:disabled .segmented-input:checked + .segmented-na { background: var(--neutral-soft); color: var(--ink); }
.tenant-check .segmented-option { padding: 0 var(--space-3); }
/* R8-D12: an invoice's status and its Download link sit together at the end of the row. */
.invoice-row-end { display: inline-flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3); }

/* ==== Review fixes: batch 5 b ==== */
/* R8-G6: on the tablet icon rail, Admin group headings ("Content and rules", "Commercial") don't fit, so they're a divider
   (the line above each group) and stay for screen readers as the group's name. Every group is open on the rail so all
   its icons show (site.js opens them; ::details-content shows them without the script where supported). */
@media (min-width: 700px) and (max-width: 959px) {
  .sidebar .nav-fold > summary.nav-subheading {
    position: absolute; width: 1px; height: 1px; min-height: 0; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; pointer-events: none;
  }
  .sidebar .nav-fold > summary + * { margin-top: 0; }
}
@media (min-width: 700px) and (max-width: 959px) {
  .sidebar .nav-fold::details-content { content-visibility: visible; }
}

/* ==== Review fixes: batch 6 renters ==== */
/* R8-D14: in the message composer the message box fills the card, and the attach button keeps its natural width with
   its limit ("Up to 5 photos or PDFs.") beside it rather than stretching across the card. */
form.composer > .field { max-width: none; }
.composer-attach .file-drop-compact { flex-direction: row; flex-wrap: wrap; align-items: center; gap: var(--space-1) var(--space-3); }
.composer-attach .file-drop-compact > .file-drop-zone { order: 0; flex: 0 0 auto; }
.composer-attach .file-drop-compact > .caption:not(.file-drop-count) { order: 1; }
.composer-attach .file-drop-compact > :is(.file-drop-list, .file-drop-count) { order: 2; flex-basis: 100%; }

/* ==== Review fixes: batch 6 public ==== */
/* R8-A15: a fresh invitation link to copy. */
.team-share-link { flex: 1 1 16rem; min-width: 0; }
/* R8-A19: a property with no photo gets a slim band with its monogram, not half the card. */
/* R13-J5: a property without a photo has the same 3:1 band (monogram on brand-soft) as one with a photo. */
/* R8-D18: answering a report room by room on a phone, the tab bar stays at the foot of the page instead of floating, so
   only the room's Save bar sits over the form (the menu is still there at the end of the page). */
@media (max-width: 959px) {
  .app:has(.room-form[data-tenant-room] .action-bar) > .tabbar { position: static; }
  .app:has(.room-form[data-tenant-room] .action-bar) .room-form .action-bar { bottom: 0; padding-bottom: calc(var(--space-3) + env(safe-area-inset-bottom)); }
  html:has(.room-form[data-tenant-room] .action-bar) { scroll-padding-bottom: 96px; }
}
/* R8-D16: the trade's jobs table on a laptop: When as two short lines that don't wrap, and a narrower job panel until the
   screen is wide, so job names have room. */
.trade-job-when { display: flex; flex-direction: column; gap: var(--space-0-5); }
.trade-job-day { white-space: nowrap; }
/* R9-J16: the "When" detail ("Tomorrow at 9:00 am, in 2 days") wraps, so the column doesn't push the table past its card. */
.trade-job-when > .caption { min-width: 9rem; }
@media (min-width: 1200px) and (max-width: 1439px) {
  .trade-jobs-desk { grid-template-columns: minmax(0, 1fr) minmax(260px, 300px); }
}

/* ==== Review fixes: batch 6 owner-core ==== */
/* R8-B17: the tablet icon rail shows each section's short name under its icon, as the phone tab bar does, so nobody has to
   guess between similar icons (Tasks and Inspections, Listings and Repairs). The rail is a little wider to fit them. */
@media (min-width: 700px) and (max-width: 959px) {
  .app { grid-template-columns: 88px minmax(0, 1fr); }
  .sidebar .nav-item {
    flex-direction: column; gap: var(--space-0-5); min-height: 56px; padding: var(--space-1-5) var(--space-0-5);
    font-size: var(--text-xs); line-height: var(--leading-xs); text-align: center;
  }
  .sidebar .nav-label {
    position: static; width: auto; height: auto; max-width: 100%; overflow: visible; clip-path: none;
    white-space: normal; overflow-wrap: break-word; hyphens: auto;
  }
}

/* ==== Review fixes: batch 6 staff-2 ==== */
/* R8-G18: on phones the internal notes heading wraps beside its chevron, which stays on the heading's first line. */
@media (max-width: 599px) {
  .support-notes > summary { flex-wrap: nowrap; align-items: flex-start; }
  .support-notes > summary::after { align-self: flex-start; margin-top: var(--space-0-5); }
}

/* ==== Review fixes: batch 6 letting-renter ==== */
/* R8-F12: each home card opens its ad from anywhere on the card (the headline link stretches over it), with Save on top. */
.home-card { position: relative; }
.home-card-link { color: var(--ink); font-weight: 600; text-decoration: none; }
.home-card-link::after { content: ""; position: absolute; inset: 0; border-radius: inherit; }
.home-card-link:hover { text-decoration: underline; }
.home-card:has(.home-card-link:focus-visible) { outline: 2px solid var(--brand); outline-offset: 2px; }
.home-card-link:focus-visible { outline: none; }
.home-card-save { position: relative; z-index: 1; align-self: flex-start; }
.home-card-save .btn-text { padding-inline: 0; min-height: 44px; }
/* R8-F12: the search alert line under the filters, and the ad's tools (save, share, map). */
.homes-alert-form { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-4); }
.homes-alert-form > .caption { margin: 0; }
.home-ad-tools { align-items: center; }
/* R8-D19: a referee or document keeps its "…" menu at the right of its row on phones too. */
.item-row:has(> :first-child + .row-menu:last-child) { flex-wrap: nowrap; }
.item-row:has(> :first-child + .row-menu:last-child) > :first-child { flex: 1 1 auto; }
/* R8-D19: one progress layout for "How your profile's going" and "Get ready to apply": the count, then a full-width bar. */
.profile-progress { flex-direction: column; align-items: stretch; gap: var(--space-2); }
.profile-progress > progress.progress { flex: 0 0 auto; width: 100%; }
/* R8-D19: the profile's three section actions look alike: a rule above, the primary button at natural width on the right. */
.profile-form-actions { justify-content: flex-end; border-top: 1px solid var(--line); padding-top: var(--space-4); }
.profile-form-actions > .btn-primary { flex: 0 0 auto; }
@media (max-width: 959px) {
  .profile-save-bar.action-bar:not(.is-dirty) { margin-inline: 0; padding: var(--space-4) 0 0; background: none; }
}
/* R8-G16: the All forms table fits its card at tablet widths: long cells wrap, and anything still too wide scrolls inside the card. */
@media (min-width: 600px) {
  .forms-list-fold > .table-wrap:has(> .table) { overflow-x: auto; }
  .forms-list-fold .table td { overflow-wrap: anywhere; }
}

/* ==== Review fixes: batch 6 owner-ops ==== */
/* R8-C15: the repair stepper never breaks a word mid-way ("Acknowledg / ed"): labels wrap only between words (hyphenated
   if a word truly can't fit), and on narrow phones the four steps sit two by two. */
.stepper-label { hyphens: auto; }
@media (max-width: 599px) { .stepper { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: var(--space-3); } }
/* R8-C16: on a repair, the cards side by side are the same height (the left column's last card grows to match). */
@media (min-width: 960px) {
  .overview-grid.repair-overview { align-items: stretch; }
  .repair-overview > .stack-lg > .card:last-child { flex: 1 1 auto; }
}
/* R8-C16: a list card's heading has room above the first row, and the last row has no extra space under it. */
.card:not(.stack, .stack-sm, .stack-lg, .stack-xs) > .card-title + :is(.task-list, .list) { margin-top: var(--space-3); }
.card > .task-list > li:last-child { padding-bottom: 0; }
/* R8-C17: Open repairs' filters and property picker share a row, wrapping on phones. */
.repair-filters { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); }
.repair-filter-property { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); min-width: 0; max-width: 100%; }
.repair-filter-property > .input { width: auto; min-width: 0; max-width: 100%; }
/* R14 4.5-4.6: the repair page's cards sit in two columns from 600px (was 960), and the problem reads at 16/24. */
@media (min-width: 600px) { .overview-grid.repair-overview { grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); align-items: stretch; } }
.repair-problem { font-size: var(--text-base); line-height: var(--leading-base); max-width: 44.5em; }
/* R14 3.5: inbox rows keep to one line of facts (the subject, then the start of the message). */
.rows-inbox .row-meta { flex-wrap: nowrap; overflow: hidden; }
.rows-inbox .row-meta > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rows-inbox .row-meta > span:first-of-type { flex: none; max-width: 60%; }
/* R14 4.9: a renter-profile document or referee being edited sits in the list where its row was. */
.rows-edit { list-style: none; padding: var(--space-3) var(--space-4); border-top: 1px solid var(--line); }
.rows-edit:first-child { border-top: 0; }
@media (min-width: 960px) { .rows-edit { padding-inline: var(--space-5); } }
/* R8-C21: screenshots and files under a support message. */
.support-msg-files { list-style: none; margin: 0; padding-left: calc(var(--avatar) + var(--space-2)); display: flex; flex-direction: column; gap: var(--space-1); }
@media (max-width: 599px) { .support-msg-files { padding-left: 0; } }

/* ==== Review fixes: batch 6 staff-1 ==== */
/* R8-E10: a folded Admin menu group shows the total waiting inside it; the bubble goes once the group is open (each
   section then shows its own). */
.nav-fold > summary .nav-group-count { margin-left: auto; margin-right: var(--space-2); }
.nav-fold[open] > summary .nav-group-count { display: none; }
/* R8-E17: the staff role picker and its Change button sit side by side in the table row. */
.toolbar.toolbar-inline { flex-wrap: nowrap; align-items: center; }
/* R8-E8: a partner's history, newest last, with a rule between entries. */
.partner-history > li + li { padding-top: var(--space-2); border-top: 1px solid var(--line); }

/* ==== Review fixes: batch 6 letting-owner ==== */
/* R8-F6: "Before it goes live" is a to-do list: one bullet each, the fix linked after the reason. */
.listing-blockers { margin: 0; padding-left: var(--space-5); display: flex; flex-direction: column; gap: var(--space-2); font-size: var(--text-md); line-height: var(--leading-md); }
.listing-blockers a { white-space: nowrap; }
/* R8-F6: a Publish button that can't be pressed yet reads as unavailable but stays legible in both themes. */
.btn-primary.listing-publish-blocked:disabled,
.btn-primary.listing-publish-blocked:disabled:hover { opacity: 1; background: var(--neutral-soft); color: var(--ink-muted); border: 1px solid var(--line-strong); }
/* R8-F6: the rent estimate and viewings cards each keep their own height (no tall empty block under the shorter one). */
.overview-grid.listing-grid { align-items: start; }
/* R8-C13: the ad's photos on the listing and its edit page; the cover is first. */
.listing-photos { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: var(--space-3); }
.listing-photo { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-1); min-width: 0; }
.listing-photo img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; border-radius: var(--radius-md); background: var(--surface); }
.listing-photo img.floor-plan { object-fit: contain; border: 1px solid var(--line); }
/* R8-F8: the applications filter bar and sort share a row, wrapping on phones. */
.application-tools { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-3); }
.application-tools .toolbar { align-items: center; }
.application-compare .application-note { display: block; }
.application-compare td:first-child { width: 1%; }
.application-side-by-side th[scope="row"] { color: var(--ink-muted); font-weight: var(--weight-semibold); white-space: nowrap; vertical-align: top; }
.application-side-by-side td { vertical-align: top; min-width: 10rem; }
/* R8-F10: each past address in the rental history is its own block, with a rule between them. */
.rental-history-entry { border: 0; margin: 0; padding: 0; }
.rental-history-entry + .rental-history-entry { padding-top: var(--space-4); border-top: 1px solid var(--line); }

/* ==== Sidebar account (no popup) ==== */
/* Desktop sidebar: Appearance sits with the other settings-type items, then the person's name (to their profile) and a
   Sign out button that asks first. The tablet rail and phones keep the account sheet from the top bar. */
.sidebar-theme { display: flex; flex-direction: column; gap: var(--space-1-5); margin: 0; padding: var(--space-3) 0 0; border: 0; border-top: 1px solid var(--line); }
.sidebar-theme legend { float: left; width: 100%; margin-bottom: var(--space-1-5); padding: 0 var(--space-3); }
.sidebar-theme .theme-option { min-height: 32px; font-size: var(--text-sm); }
a.sidebar-account { color: inherit; text-decoration: none; }
a.sidebar-account[aria-current="page"] { background: var(--brand-soft); border-color: transparent; }
.sidebar-signout { flex: none; margin: 0; }
.sidebar-signout .icon-btn { border-color: transparent; background: transparent; color: var(--ink-muted); }
.sidebar-signout .icon-btn:hover { border-color: var(--line); background: var(--surface); color: var(--critical); }
@media (min-width: 700px) and (max-width: 959px) { .sidebar .sidebar-theme { display: none; } }

/* ==== List spacing ==== */
/* Bulleted and numbered lists get room between items across the site: running text (help guides, legal pages, the
   FAQ, "How this works") a little more, compact lists (alerts, checklists, captions) a little less. Component lists
   with their own layout (menus, rows, cards: anything with a class or role="list") keep theirs. */
:where(ul, ol):where(:not([class]):not([role])) > li + li { margin-top: var(--space-1-5); }
:where(.prose, .legal-doc, .legal-body, .page-more-body, .mk-faq, .faq, .help-article) :where(ul, ol) > li + li { margin-top: var(--space-2); }
:where(.plain-list, .admin-alert-list, .backup-checklist, .alert ul, .alert ol, .evidence-list) > li + li { margin-top: var(--space-1-5); }
:where(.prose, .legal-doc) :where(li) > :where(ul, ol) { margin-top: var(--space-1-5); }
/* Menus and other horizontal lists lay their items out themselves: no extra space above each item. */
:where(nav) :where(ul, ol) > li + li { margin-top: 0; }

/* ==== Round 9: forms, touch, layout and the style guide (R9-G12, R9-J4, R9-J9, R9-J10, R9-J15, R9-J17, R9-J22) ==== */
/* R9-J4: on touch screens compact fields are full size too: 16px text (iOS Safari zooms a field under 16px when it's
   focused) and 44px tall, like .input and .btn. The toolbar buttons beside them match. */
@media (pointer: coarse) {
  .input-sm { min-height: 44px; font-size: var(--text-base); line-height: var(--leading-base); }
  .toolbar > .btn { min-height: 44px; }
}
/* R9-J17: tap targets at least 44px tall on touch screens: help-centre guide links, the rent tools' jump links, badge
   links in a property's header, the theme options and a field's Show button. */
@media (pointer: coarse) {
  .task-list > li > a.label, .rent-tools-nav a, .badge-link { display: inline-flex; align-items: center; min-height: 44px; }
  .theme-option, .account-menu .theme-option, .sidebar-theme .theme-option { min-height: 44px; }
  .input-addon { top: 0; bottom: 0; }
}
/* R10-G9: the property card's "checks to do" badge link is a 24px target at least (WCAG 2.5.8), 44px on touch. */
/* R9-G13 (round 10): the rest of the small tap targets are 44px tall on touch screens: the auth pages' header and footer
   links, logo, "Forgot password?" and sign-up check box; the rules jump tabs; folded "How this works" and "What changed"
   summaries; legal contents links; Settings › Privacy links; and the property card badge. */
@media (pointer: coarse) {
  .auth-foot a, .auth-alt a, .auth-switch a, .field-aside, .auth-logo-link, .auth-mobile-logo, .touch-link,
  :where(.legal-doc) nav[aria-label="Contents"] a, .property-card-todo { display: inline-flex; align-items: center; min-height: 44px; }
  .rule-jump a { min-height: 44px; }
  .page-more > summary, .legal-changes > summary { display: flex; align-items: center; min-height: 44px; }
  .auth-form .check { min-height: 44px; align-items: center; }
  .auth-form .check input { width: 24px; height: 24px; margin: 0; }
}
/* R9-G13 (round 11): the app's logo links (sidebar, phone top bar, renter and signing headers, /verify) and every
   breadcrumb link (help, settings, legal versions) are 44px tall on touch screens too. */
@media (pointer: coarse) {
  .brand, .topbar-brand > a, .signing-top > a, .breadcrumbs a { display: inline-flex; align-items: center; min-height: 44px; }
}
/* R9-G12: the terms row on sign-up is a 24px target at least (WCAG 2.5.8). */
.auth-form .check { min-height: var(--space-6); }
/* R9-J15: cards side by side on the tenancy and property overviews end level, with their actions at the foot. */
@media (min-width: 960px) {
  .overview-grid.overview-even { align-items: stretch; }
  .overview-even > .card > :is(.actions, form):last-child { margin-top: auto; }
}
/* R9-J22: touch screens have no hover, so the queue pane's "Open on its own page" shows its name beside the icon. */
@media (hover: none) {
  .queue-pane-pop { width: auto; gap: var(--space-1-5); padding: 0 var(--space-3); }
  .queue-pane-tip { position: static; padding: 0; background: none; color: inherit; opacity: 1; visibility: visible; transform: none; }
}
/* R9-J9: the "New" menu in a page header opens under its button like a row menu. R10-J1: below 960px the header's
   actions sit at the left of the screen, so the menu opens from the button's left edge (a right-anchored panel ran off
   the left of phones and under the iPad rail). row-menu.js then flips any menu that would still open off either edge. */
.new-menu > summary { list-style: none; }
@media (max-width: 959px) { .new-menu .row-menu-panel { left: 0; right: auto; } }
.row-menu-panel.row-menu-panel-start { left: 0; right: auto; }
.row-menu-panel.row-menu-panel-end { left: auto; right: 0; }
/* R9-J10: the style guide's spacing, radius and shadow samples. */
.sg-space { display: block; flex: none; height: var(--space-4); background: var(--brand-soft); border: 1px solid var(--brand); }
.sg-space-1 { width: var(--space-1); } .sg-space-2 { width: var(--space-2); } .sg-space-3 { width: var(--space-3); } .sg-space-4 { width: var(--space-4); }
.sg-space-6 { width: var(--space-6); } .sg-space-8 { width: var(--space-8); } .sg-space-12 { width: var(--space-12); } .sg-space-16 { width: var(--space-16); }
.sg-tile { flex: none; width: 48px; height: 32px; background: var(--surface-raised); border: 1px solid var(--line-strong); }
.sg-radius-xs { border-radius: var(--radius-xs); } .sg-radius-sm { border-radius: var(--radius-sm); } .sg-radius-md { border-radius: var(--radius-md); }
.sg-radius-lg { border-radius: var(--radius-lg); } .sg-radius-xl { border-radius: var(--radius-xl); } .sg-radius-pill { border-radius: var(--radius-pill); }
.sg-shadow-raised { border-color: var(--line); box-shadow: var(--shadow-raised); } .sg-shadow-overlay { border-color: var(--line); box-shadow: var(--shadow-overlay); }
/* R9-J10: the style guide's light and dark panels side by side (tokens.css gives .sg-preview-light/-dark each theme's tokens). */
.sg-theme-pair { display: grid; gap: var(--space-4); }
@media (min-width: 960px) { .sg-theme-pair { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.sg-theme-panel { padding: var(--space-4); border: 1px solid var(--line); border-radius: var(--radius-md); background: var(--surface); color: var(--ink); }
/* R9-J14: the sign-in pages' heading isn't bigger than the website's on phones (28/34 there, 32/38 from 600px). */
@media (max-width: 599px) { .auth-head h1, .auth-panel h1 { font-size: var(--text-2xl); line-height: var(--leading-2xl); } }

/* ==== Round 10: layout on tablets, tables, touch (R9-J8, R9-J16, R9-J17, R10-J1, R10-J3) ==== */
/* R9-J8: the property's eight tabs fit an iPad in portrait (690px) with a little less padding each; phones scroll them. */
@media (max-width: 1199px) { .property-tabs a { padding-inline: var(--space-3); } }
/* R9-J16: wide tables become one card per row on tablets too, not only on phones: .table-cards-md below 960px (iPad
   portrait), .table-cards-lg below 1200px (iPad landscape). Same anatomy as .table-cards: a title cell, then each cell
   with its column name (data-label). */
@media (max-width: 959px) {
  .table-cards-md thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
  .table-cards-md, .table-cards-md tbody, .table-cards-md tr, .table-cards-md :is(td, th) { display: block; width: 100%; }
  .table-cards-md tr { padding: var(--space-3) 0; border-bottom: 1px solid var(--line); }
  .table-cards-md tr:last-child { border-bottom: 0; }
  .table.table-cards-md :is(td, th) { padding: var(--space-0-5) 0; border-bottom: 0; overflow-wrap: anywhere; text-align: left; }
  .table-cards-md td[data-label]::before { content: attr(data-label) ": "; color: var(--ink-muted); }
  .table-cards-md .table-cards-title { font-weight: var(--weight-semibold); }
}
@media (max-width: 1199px) {
  .table-cards-lg thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
  .table-cards-lg, .table-cards-lg tbody, .table-cards-lg tr, .table-cards-lg :is(td, th) { display: block; width: 100%; }
  .table-cards-lg tr { padding: var(--space-3) 0; border-bottom: 1px solid var(--line); }
  .table-cards-lg tr:last-child { border-bottom: 0; }
  .table.table-cards-lg :is(td, th) { padding: var(--space-0-5) 0; border-bottom: 0; overflow-wrap: anywhere; text-align: left; white-space: normal; }
  .table-cards-lg td[data-label]::before { content: attr(data-label) ": "; color: var(--ink-muted); }
  .table-cards-lg .table-cards-title { font-weight: var(--weight-semibold); }
  /* A row's inline form (the staff role picker) wraps under its label instead of widening the card. */
  .table-cards-lg .toolbar.toolbar-inline { flex-wrap: wrap; }
}
@media (max-width: 599px) { .table-cards .toolbar.toolbar-inline { flex-wrap: wrap; } }
/* R9-J16: the forms coverage matrix (16 columns) is one block per state at every width: the state, then its forms in a
   grid that fills the width, each with the form's name above it. Nothing scrolls sideways. */
.forms-matrix thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
.forms-matrix, .forms-matrix tbody { display: block; width: 100%; }
.forms-matrix tr { display: grid; grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr)); gap: var(--space-3) var(--space-4); padding: var(--space-4) 0; border-bottom: 1px solid var(--line); }
.forms-matrix tr:last-child { border-bottom: 0; }
.table.forms-matrix td { display: block; min-width: 0; padding: 0; border-bottom: 0; overflow-wrap: anywhere; }
.table.forms-matrix td:first-child { grid-column: 1 / -1; font-weight: var(--weight-semibold); }
.forms-matrix td[data-label]::before { content: attr(data-label); display: block; color: var(--ink-muted); font-size: var(--text-sm); line-height: var(--leading-sm); }
.forms-matrix .badge { margin-left: 0; white-space: nowrap; }
/* R9-J17: the rest of the touch targets, 44px tall on touch screens (pointer: coarse): every disclosure summary ("How this
   works", "How you get notices"), the ledger's "Add a credit or charge", the payment amount chips, the account menu's
   items, staff nav items and group summaries,
   and a row's title link (a.label). Links inside a sentence are exempt (WCAG 2.5.8) and keep their line height. */
@media (pointer: coarse) {
  :is(.disclosure, .fold, .checklist-fold, .audit-details, .legal-changes, .email-more) > summary { --summary-pad: var(--space-3); min-height: 44px; }
  .ledger-adjust > summary { display: inline-flex; align-items: center; min-height: 44px; padding-block: 0; }
  .amount-chip { min-height: 44px; }
  .account-menu .nav-item, .sidebar .nav-fold > summary, .sidebar .nav-admin .nav-item { min-height: 44px; }
  a.label:not(.notification-link) { display: inline-flex; align-items: center; min-height: 44px; }
}
/* R9-J14: /verify is a website page in the signing frame; its title takes the website's display weight, like the others. */
.verify-page .page-header h1 { font-family: var(--font-display); font-weight: var(--display-weight); letter-spacing: var(--display-tracking); }
/* R10-J2: a tenancy's conversations as a wrapping list of compact links (one column on phones), not a sideways tab row. */
.conversation-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr)); gap: var(--space-2); margin: 0; padding: 0; list-style: none; }
.conversation-list a {
  display: flex; align-items: center; gap: var(--space-2); min-height: 44px; padding: 0 var(--space-3); border: 1px solid var(--line);
  border-radius: var(--radius-md); background: var(--surface-raised); color: var(--ink); font-size: var(--text-md); line-height: var(--leading-md); text-decoration: none;
}
.conversation-list a:hover { border-color: var(--brand); }
.conversation-list a[aria-current="page"] { border-color: var(--brand); background: var(--brand-soft); color: var(--brand-strong); font-weight: var(--weight-semibold); }
.conversation-list-title { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.conversation-list .dot { align-self: center; margin-top: 0; }

/* ==== Round 10: touch targets on the trade and partner pages (R9-E20) ==== */
/* On touch screens (phones, and iPads in either orientation): a checkbox's whole label is a 44px row, a "more" fold's
   summary is 44px tall, tabs and filters stay 44px on a wide iPad, and small links in captions and hints get a 44px row. */
@media (pointer: coarse) {
  .check, .choice { min-height: 44px; align-items: center; }
  .check input, .choice input { margin-top: 0; }
  .fold > summary { display: flex; align-items: center; gap: var(--space-2); min-height: 44px; list-style: none; cursor: pointer; }
  .fold > summary::-webkit-details-marker { display: none; }
  .fold > summary::before { content: "\25B8"; flex: none; }
  .fold[open] > summary::before { content: "\25BE"; }
  .filter-bar a { min-height: 44px; }
  /* R12-J2 and R12-G1: list links (official forms, document lists, legal versions, support attachments, the profile
     checklist) and staff controls (audit change summaries, the rules state jump) are 44px rows too. */
  .plain-list a, .version-list a, .support-msg-files a, .profile-checklist a, .rules-jump a { display: inline-flex; align-items: center; min-height: 44px; }
  .audit-change > summary { min-height: 44px; align-items: center; }
  .credential-line a, .field-hint a, .caption > a, a.caption, .tap-link { display: inline-flex; align-items: center; min-height: 44px; }
  /* R11-J7: the last small targets: an icon-size caption link ("PDF") is 44px wide too, a room's summary, the topbar logo,
     and a phone "Needs you" row's second action, kept clear of the row's own link. */
  .caption > a, a.caption, .tap-link { min-width: 44px; justify-content: center; }
  details.room > summary { align-items: center; min-height: 44px; }
  .topbar-brand > a { align-items: center; min-height: 44px; }
}

/* ==== Review 11: money (agent b) ==== */
/* R10-B9: the loan fields on Add an expense show only when Borrowing expenses is chosen (no JavaScript needed). */
.expense-form .expense-borrowing { display: none; border: 0; padding: 0; margin: 0; }
.expense-form:has(#expense-category option[value="BorrowingExpenses"]:checked) .expense-borrowing { display: flex; }

/* ==== Round 11: the last trade and partner touch targets (R9-E20) ==== */
/* On touch screens: each licence register link is its own 44px row, and the breadcrumb's links are 44px tall on iPads too
   (phones already get the 44px "‹ Parent" link). */
@media (pointer: coarse) {
  .register-links a, .breadcrumbs a { display: inline-flex; align-items: center; min-height: 44px; }
}

/* ==== Round 13: trades touch targets (R13-E3 c, d) ==== */
/* On touch screens: the compliance "View evidence" links and staff's "Check on ABN Lookup" link are 44px tall. */
@media (pointer: coarse) {
  .evidence-link, .abn-lookup-link { display: inline-flex; align-items: center; min-height: 44px; }
}

/* ==== Round 13: design consistency (R13-J9) ==== */
/* A long form in a dialog gets room for two columns from 960px: its form grids (minmax 220px) then pair their fields,
   so "Add an expense" fits a laptop screen with its sticky footer. Phones keep the full-width sheet. */
@media (min-width: 960px) {
  .modal:has(.form-grid) { width: min(640px, calc(100vw - 2 * var(--space-4))); }
}
/* A card's sub-sections step down from its title: h3 is 16/24 semibold, leaving 18/24 to .card-title. */
.card h3:not(.card-title) { font-size: var(--text-base); line-height: var(--leading-base); font-weight: var(--weight-semibold); }
/* Touch screens: toolbar filters (audit, rules) and a property's set-up checklist links are 44px targets too. */
@media (pointer: coarse) {
  .toolbar :is(.btn, .input) { min-height: 44px; }
  .toolbar .input { font-size: var(--text-base); line-height: var(--leading-base); }
  .checklist a { display: inline-flex; align-items: center; min-height: 44px; }
  .checklist li:has(> a)::before { top: 13px; }
}
