/* =============================================================================
   APP / SCREEN STYLES — token-only.
   -----------------------------------------------------------------------------
   Migrated from the former inline <style> in index.html. Every color, space,
   radius, font-size, shadow and border width references a SEMANTIC token from
   semantic.css — there are no raw hex/px/rem literals here (lint-enforced).
   Sizing/positioning (icon width/height, layout max-widths) and @media
   breakpoints remain in px: CSS custom properties cannot be used in media
   conditions, and intrinsic element dimensions are out of the token scales.
   New UI should prefer the shared base components in components.css.
   ========================================================================== */

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

    /* `clip`, NOT `hidden`. Both stop sideways scrolling from a wide table, but
       `overflow-x: hidden` makes overflow-y compute to `auto`, which turns <body> into
       a scroll container — and a scroll container that never scrolls silently disables
       EVERY `position: sticky` inside it (the sticky nav and the workflow's advance
       footer both just scrolled away). `clip` clips without creating one. */
    html, body { overflow-x: clip; }

    body {
      font-family: var(--font-sans);
      font-weight: var(--weight-light);
      line-height: 1.6;
      background: var(--color-canvas);
      color: var(--color-text-primary);
      min-height: 100vh;
      width: 100%;
      display: flex;
      flex-direction: column;
      align-items: center;
      padding: 0 var(--space-24) var(--space-64);
    }

    /* Baskerville display for all headings + titles — big and quiet, weight 400. */
    h1, h2, h3, h4 { font-family: var(--font-serif); font-weight: var(--weight-regular); }

    /* Crimson is the single accent — tint native form controls to match. */
    input, select, textarea { accent-color: var(--color-accent); }

    /* ── Nav — sticky editorial bar (Paper bg, Mist bottom-border) ── */
    nav {
      position: sticky;
      top: 0;
      z-index: 50;
      width: 100%;
      max-width: 1080px;
      display: flex;
      align-items: center;
      flex-wrap: wrap;
      gap: var(--space-12) var(--space-28);
      min-height: 72px;
      margin-bottom: var(--space-28);
      background: var(--color-canvas);
      border-bottom: var(--border-thin) solid var(--color-border);
    }
    /* The nav is STICKY at every width, including phones.
       History: it used to go position:static below 640px, because the nav then wrapped to
       three rows and pinning it would have cost a sixth of the screen. That rationale is
       stale — once the bar was consolidated to Portfolio · New · Admin + the avatar it
       became ONE row: measured at 53px, 6.8% of a 780px viewport, at every width from
       320px up. Leaving it scrolling cost far more than it saved: a link-level navigation
       audit found four journeys on a phone that landed the manager on a long screen (the
       Leases screen runs ~6,100px) with NO way back anywhere in the viewport. See
       "navigation you add ALWAYS carries a way back" in CLAUDE.md.
       The workflow's advance bar is pinned at the BOTTOM, so the two don't compete. */

    /* …EXCEPT in the installed PWA, where there is NO BROWSER BACK BUTTON. Letting the
       nav scroll away there leaves a manager genuinely stranded: scroll down a roster and
       the only route to any other screen is gone. (Reported from the phone: "set up a
       project … in the PWA I cannot go back.")
       Keyed off an ATTRIBUTE, not `@media (display-mode: standalone)`: iOS reports
       standalone through `navigator.standalone` on older versions and through display-mode
       on newer, so the app resolves both once and stamps `data-standalone` (same pattern as
       `data-ui`). It is also the only version of this that can be tested — display-mode
       cannot be emulated in headless Chromium, and a rule we cannot test is a rule that
       silently rots. */
    @media (max-width: 640px) {
      html[data-standalone] nav {
        position: sticky;
        top: 0;
        min-height: 0;
        margin-bottom: var(--space-16);
        padding-top: var(--safe-top);
        background: var(--color-canvas);
      }
    }

    /* The PWA runs edge-to-edge under the status bar / Dynamic Island (viewport-fit=cover),
       so anything pinned to an edge must inset itself or the system UI sits on top of it —
       which is what clipped "PORTFOLIO · + NEW" out of reach. env() resolves to 0 in a
       browser tab, so these are inert outside standalone. */
    body { padding-left: var(--safe-left); padding-right: var(--safe-right); }
    .wf-foot { padding-bottom: calc(var(--space-12) + var(--safe-bottom)); }

    /* The TrueUp mark — an equals sign (two equal-length, square-cornered bars).
       Inlined as SVG so the crimson bar can animate. Sized to sit level with the
       wordmark. Slate top bar, crimson bottom bar (tokens only — never recolor). */
    .tu-mark {
      display: block;
      height: 22px;
      width: auto;
      flex-shrink: 0;
      overflow: visible;
    }
    .tu-bar-slate   { fill: var(--color-brand-slate); }
    .tu-bar-crimson { fill: var(--color-brand-crimson); }

    /* "Settle to even": the crimson bar starts short and slides out to match the
       slate bar's length — trued up. GPU-friendly transform only, and only when
       the viewer hasn't asked for reduced motion. */
    @media (prefers-reduced-motion: no-preference) {
      .tu-bar-crimson {
        transform-box: fill-box;
        transform-origin: left center;
      }
      .tu-mark.tu-settle .tu-bar-crimson {
        animation: tu-settle-even 640ms cubic-bezier(0.22, 0.68, 0.2, 1) both;
      }
    }
    @keyframes tu-settle-even {
      0%   { transform: scaleX(0.4); }
      60%  { transform: scaleX(1.04); }
      100% { transform: scaleX(1); }
    }

    /* Header: the Stiles corporate (75-Years) mark, top-left, links home.
       margin-right:auto pushes the nav links / user to the right. The TrueUp
       lockup now headlines the Home hero (.hero-brand), not the nav. */
    .nav-home {
      display: flex;
      align-items: center;
      cursor: pointer;
      background: none;
      border: none;
      padding: 0;
      margin-right: auto;
    }
    .nav-stiles {
      height: 26px;
      width: auto;
    }

    nav .nav-links {
      display: flex;
      gap: var(--space-6);
      flex-wrap: wrap;
    }

    /* Account cluster (Admin + Sign out), pinned to the right of the nav. On mobile
       it rides on the logo row (see the media query) so the primary tabs get a
       full-width row of their own. */
    nav .nav-account {
      display: flex;
      align-items: center;
      gap: var(--space-12);
    }

    /* Redesign-only nav tabs (e.g. Portfolio) — hidden in the classic nav; the
       new UI reveals them (styles/theme-new.css). Keeps the classic nav unchanged. */
    nav .nav-tab--new { display: none; }
    /* Universal header search — new-UI-only surface, hidden in the classic nav. */
    nav .nav-search { display: none; }
    /* "Meet Cam" footer link — new-UI-only; hidden in the classic footer (revealed
       under data-ui="new" in theme-new.css). Covers its separator too. */
    .footer-cam { display: none; }
    /* Portfolio "Across all properties" rescue links — new-UI-only (they exist
       because the new nav folds those tabs; the classic nav still has them). */
    .pf-tools { display: none; }
    /* Phone-only "show desk controls" hint — hidden by default; revealed only in the
       new UI at a narrow viewport where the upload cards are withheld (theme-new.css).
       Classic never hides those cards, so it never needs the hint. */
    .desk-hint { display: none; }

    nav .nav-tab {
      background: none;
      border: none;
      border-bottom: var(--border-thick) solid transparent;
      font-family: var(--font-sans);
      font-size: var(--text-13);
      font-weight: var(--weight-semibold);
      text-transform: uppercase;
      letter-spacing: var(--tracking-8);
      color: var(--color-text-muted);
      cursor: pointer;
      padding: var(--space-8) var(--space-12);
      transition: color 0.15s, border-color 0.15s;
    }

    nav .nav-tab:hover { color: var(--color-text-primary); }
    nav .nav-tab.active { color: var(--color-accent); border-bottom-color: var(--color-accent); }

    /* CAM Rec tab + its saved-project resume menu. The wrap anchors the absolutely
       positioned menu, which REVEALS ON HOVER (or keyboard focus) of the whole tab —
       no tiny target to hit. The caret is a decorative affordance only. The menu holds
       a single "Resume a saved project" item that opens the dedicated Resume screen;
       the growing project list lives there (searchable), not crammed into this menu. */
    nav .nav-tab-wrap { position: relative; display: flex; align-items: center; }
    nav .nav-caret {
      display: none;                 /* shown only once saved projects exist (.has-resume) */
      color: var(--color-text-muted);
      font-size: var(--text-11);
      margin-left: var(--space-4);
      pointer-events: none;          /* decorative — hover the tab, not this glyph */
    }
    nav .nav-tab-wrap.has-resume .nav-caret { display: inline; }
    nav .nav-dropdown {
      display: none;
      position: absolute;
      top: 100%;
      left: 0;
      z-index: 60;
      min-width: 15rem;
      margin-top: var(--space-4);
      padding: var(--space-6);
      background: var(--color-surface);
      border: var(--border-thin) solid var(--color-border);
      border-radius: var(--radius-lg);
      box-shadow: var(--shadow-md);
    }
    /* Reveal on hover / focus-within, but only when there are saved projects. A small
       invisible bridge (the margin-top gap) is covered by the wrap's padding so the
       pointer can travel from tab to menu without the menu collapsing. */
    nav .nav-tab-wrap.has-resume:hover .nav-dropdown,
    nav .nav-tab-wrap.has-resume:focus-within .nav-dropdown { display: block; }
    nav .nav-dropdown-item {
      display: block;
      width: 100%;
      text-align: left;
      background: none;
      border: none;
      cursor: pointer;
      white-space: nowrap;
      font-size: var(--text-14);
      font-weight: var(--weight-medium);
      color: var(--color-text-primary);
      padding: var(--space-8) var(--space-12);
      border-radius: var(--radius-md);
      transition: background 0.15s, color 0.15s;
    }
    nav .nav-dropdown-item:hover { background: var(--color-surface-sunken); color: var(--color-accent); }

    /* Resume screen: an alphabetical, searchable list of saved projects. */
    .resume-screen { max-width: 40rem; }
    .resume-search { width: 100%; margin-bottom: var(--space-16); }
    .resume-screen-list { display: flex; flex-direction: column; gap: var(--space-8); }
    .resume-empty { color: var(--color-text-muted); font-size: var(--text-14); padding: var(--space-16) 0; }

    /* Signed-in user + Sign out, pushed to the right of the nav. */
    nav .nav-user {
      display: flex;
      align-items: center;
      gap: var(--space-12);
      padding-left: var(--space-12);
      border-left: var(--border-thin) solid var(--color-border);
    }
    /* ── Profile (avatar + tap-toggled menu) ────────────────────────────────
       Unscoped deliberately: there is ONE profile markup, so classic renders it
       properly too for as long as it exists (docs/classic-ui-removal.md).
       The avatar is SQUARE because --p-radius-circle / --p-radius-pill are both 0
       brand-wide — the same squared look every dot and chip in the app has.
       Tap-toggled, never :hover — a hover-only menu is unreachable on a phone. */
    nav .nav-user { position: relative; }
    /* `nav .nav-user { display: flex }` above BEATS the UA's [hidden]{display:none},
       so signed out (or before showNavUser runs) the cluster rendered anyway — as an
       empty avatar box and a divider with nothing in it. Same trap as .nav-profile;
       missed on the PARENT. Any element toggled by the hidden attribute needs this. */
    nav .nav-user[hidden] { display: none; }
    nav .nav-avatar {
      width: var(--space-32);
      height: var(--space-32);
      border-radius: var(--radius-circle);
      border: var(--border-thin) solid var(--color-border);
      background: var(--color-surface-muted);
      position: relative;
      overflow: hidden;
      cursor: pointer;
      padding: 0;
      display: flex;
      align-items: center;
      justify-content: center;
    }
    nav .nav-avatar:hover { border-color: var(--color-accent); }
    nav .nav-avatar-mono {
      font-family: var(--font-sans);
      font-size: var(--text-12);
      font-weight: var(--weight-semibold);
      color: var(--color-text-muted);
      letter-spacing: var(--tracking-1);
    }
    /* The picture layers OVER the monogram, so a missing or broken one degrades to
       initials rather than an empty box. */
    nav .nav-avatar-img {
      position: absolute;
      inset: 0;
      width: 100%;
      height: 100%;
      object-fit: cover;
    }
    /* Right-anchored and capped, so it never pushes the page sideways on a phone. */
    nav .nav-profile {
      position: absolute;
      top: calc(100% + var(--space-8));
      right: 0;
      width: max-content;
      max-width: var(--size-320);
      background: var(--color-surface);
      border: var(--border-thin) solid var(--color-border-subtle);
      border-radius: var(--radius-md);
      box-shadow: var(--shadow-md);
      padding: var(--space-8);
      z-index: 60;   /* same layer as the nav dropdown below */
      display: flex;
      flex-direction: column;
      gap: var(--space-4);
    }
    /* A `display` declaration BEATS the UA's [hidden]{display:none}, so without
       this the menu is permanently open. Toggling is via the hidden attribute
       (setProfileMenu), so the attribute has to win. */
    nav .nav-profile[hidden] { display: none; }
    nav .nav-profile-id {
      display: flex;
      flex-direction: column;
      gap: var(--space-2);
      padding: var(--space-8) var(--space-10) var(--space-10);
      border-bottom: var(--border-thin) solid var(--color-border-subtle);
    }
    nav .nav-profile-name {
      font-size: var(--text-13);
      font-weight: var(--weight-semibold);
      color: var(--color-text-primary);
    }
    /* Its own class, NOT .nav-user-email: that one is truncated at 180px and
       display:none under 760px, which would hide the address on a phone. */
    nav .nav-profile-email {
      font-size: var(--text-12);
      color: var(--color-text-muted);
      overflow-wrap: anywhere;
    }
    nav .nav-profile-sec {
      display: flex;
      flex-direction: column;
      gap: var(--space-2);
      padding-bottom: var(--space-4);
      border-bottom: var(--border-thin) solid var(--color-border-subtle);
    }
    nav .nav-profile-item {
      text-align: left;
      background: none;
      border: none;
      cursor: pointer;
      font-family: var(--font-sans);
      font-size: var(--text-13);
      color: var(--color-text-primary);
      padding: var(--space-8) var(--space-10);
      border-radius: var(--radius-sm);
    }
    nav .nav-profile-item:hover { background: var(--color-accent-bg); }
    nav .nav-profile-item--out { color: var(--color-accent); }
    nav .nav-profile-note {
      font-size: var(--text-11);
      color: var(--color-text-muted);
      padding: 0 var(--space-10) var(--space-6);
    }
    nav .nav-user-email {
      font-size: var(--text-12);
      color: var(--color-text-muted);
      max-width: 180px;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
    nav .nav-logout {
      background: none;
      border: var(--border-thin) solid var(--color-border);
      font-family: var(--font-sans);
      font-size: var(--text-11);
      font-weight: var(--weight-semibold);
      text-transform: uppercase;
      letter-spacing: var(--tracking-6);
      color: var(--color-text-primary);
      cursor: pointer;
      padding: var(--space-6) var(--space-12);
      border-radius: var(--radius-md);
      transition: background 0.15s, color 0.15s, border-color 0.15s;
    }
    nav .nav-logout:hover { border-color: var(--color-text-primary); }

    /* ── Screens ── */
    .screen { display: none; width: 100%; max-width: 1080px; }
    .screen.active { display: block; }

    /* ── Landing card ── */
    .landing-card {
      background: var(--color-surface);
      border: var(--border-thin) solid var(--color-border);
      border-radius: var(--radius-3xl);
      /* Tighter top so the hero header sits higher; bottom stays generous. */
      padding: var(--space-24) var(--space-48) var(--space-64);
      text-align: center;
    }

    .landing-card .logo-mark-lg {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      width: 56px;
      height: 56px;
      background: var(--color-accent);
      border-radius: var(--radius-2xl);
      margin-bottom: var(--space-32);
    }

    .landing-card .logo-mark-lg svg {
      width: 28px;
      height: 28px;
      fill: none;
      stroke: var(--color-surface);
      stroke-width: 2;
      stroke-linecap: round;
      stroke-linejoin: round;
    }

    h1 {
      font-family: var(--font-serif);
      font-size: var(--text-display);
      font-weight: var(--weight-regular);
      letter-spacing: var(--tracking-tighter);
      color: var(--color-text-heading);
      line-height: 1.05;
      margin-bottom: var(--space-10);
    }

    /* TrueUp lockup in the Home hero: the equals-sign mark above the two-tone
       Baskerville wordmark, centered. (Moved here from the top-left nav.) */
    .hero-brand {
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: var(--space-12);
      margin-bottom: var(--space-10);
    }
    .tu-mark.hero-mark { height: 44px; }
    .hero-wordmark { margin-bottom: 0; }
    /* Two-tone wordmark: "True" slate, "Up" crimson — matches the lockup art. */
    .hero-wordmark .brand-true { color: var(--color-brand-slate); }
    .hero-wordmark .brand-up   { color: var(--color-accent); }

    .subtitle {
      font-size: var(--text-16);
      color: var(--color-text-muted);
      font-weight: var(--weight-regular);
      margin-bottom: var(--space-16);
    }

    .divider {
      width: 40px;
      height: 3px;
      background: var(--color-accent);
      border-radius: var(--radius-xs);
      margin: 0 auto var(--space-16);
    }

    .tagline {
      font-size: var(--text-18);
      font-weight: var(--weight-medium);
      color: var(--color-text-primary);
      letter-spacing: var(--tracking-1);
      line-height: 1.8;
      margin-bottom: var(--space-24);
    }

    .tagline .dot { color: var(--color-accent); }

    .btn-primary {
      display: inline-block;
      background: var(--color-accent);
      color: var(--color-text-on-accent);
      border: var(--border-thin) solid transparent;
      border-radius: var(--radius-lg);
      font-family: var(--font-sans);
      font-size: var(--text-14);
      font-weight: var(--weight-semibold);
      text-transform: uppercase;
      letter-spacing: var(--tracking-8);
      padding: var(--space-16) var(--space-32);
      cursor: pointer;
      transition: background 0.15s, color 0.15s, border-color 0.15s;
    }

    .btn-primary:hover { background: var(--color-accent-hover); }
    .btn-primary:disabled { background: var(--color-border); cursor: not-allowed; }

    /* ── Landing workflow (3-step intro) ── */
    .workflow {
      text-align: left;
      margin: var(--space-8) 0 var(--space-36);
    }
    .workflow-head {
      font-size: var(--text-12);
      font-weight: var(--weight-semibold);
      text-transform: uppercase;
      letter-spacing: var(--tracking-8);
      color: var(--color-text-faint);
      text-align: center;
      margin-bottom: var(--space-16);
    }
    .flow-step {
      display: flex;
      align-items: flex-start;
      gap: var(--space-14);
      width: 100%;
      text-align: left;
      background: var(--color-surface-sage);
      border: var(--border-thin) solid var(--color-border-sage);
      border-radius: var(--radius-xl);
      padding: var(--space-16) var(--space-16);
      margin-bottom: var(--space-12);
      font: inherit;
      cursor: pointer;
      transition: border-color 0.15s, background 0.15s, box-shadow 0.15s;
    }
    .flow-step:last-child { margin-bottom: 0; }
    .flow-step:hover {
      border-color: var(--color-accent);
      background: var(--color-surface);
      box-shadow: var(--shadow-hover);
    }
    .flow-num {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      width: 28px;
      height: 28px;
      flex-shrink: 0;
      /* Monochrome ink (matches the TrueUp mark's slate bar) — calmer than the
         crimson accent; the crimson stays reserved for the mark + true accents. */
      background: var(--color-brand-slate);
      color: var(--color-surface);
      border-radius: var(--radius-circle);
      font-size: var(--text-14);
      font-weight: var(--weight-bold);
    }
    .flow-text { flex: 1; }
    .flow-title {
      display: block;
      font-size: var(--text-15);
      font-weight: var(--weight-semibold);
      color: var(--color-text-heading);
      margin-bottom: var(--space-4);
    }
    .flow-desc {
      display: block;
      font-size: var(--text-13);
      line-height: 1.5;
      color: var(--color-text-muted);
    }
    .flow-arrow {
      align-self: center;
      color: var(--color-arrow);
      font-size: var(--text-18);
      flex-shrink: 0;
      transition: color 0.15s, transform 0.15s;
    }
    .flow-step:hover .flow-arrow { color: var(--color-brand-slate); transform: translateX(2px); }
    .flow-step.wf-done .flow-num { background: var(--color-success); }
    .flow-check {
      align-self: center;
      flex-shrink: 0;
      color: var(--color-success);
      font-size: var(--text-18);
      font-weight: var(--weight-bold);
      display: none;
    }
    .flow-step.wf-done .flow-check { display: inline; }
    .flow-step.wf-done .flow-arrow { display: none; }

    /* ── Landing: ONE dominant "start here" (CAM Rec) + a quiet task link row ── */
    /* Centered, prominent primary — the canonical CAM Rec entry (upload the rent roll). */
    .start-here { display: flex; justify-content: center; margin-bottom: var(--space-24); }
    .door--primary {
      max-width: 34rem;
      width: 100%;
      align-items: center;
      text-align: center;
      gap: var(--space-12);
      padding: var(--space-32) var(--space-28);
      background: var(--color-surface);
      border-color: var(--color-accent);
      box-shadow: var(--shadow-md);
    }
    .door--primary:hover { border-color: var(--color-accent); box-shadow: var(--shadow-hover); }
    .door-eyebrow--start { color: var(--color-accent); }
    .door-title--lg { font-size: var(--text-24); }
    .door-cta--lg { font-size: var(--text-16); color: var(--color-accent); }
    /* Supporting tasks: a minimal, subordinate link row — visually out of the way. */
    .task-links {
      display: flex;
      flex-wrap: wrap;
      justify-content: center;
      gap: var(--space-24);
      margin-bottom: var(--space-32);
    }
    /* Muted, non-CTA task chips: slate/teal-grey, deliberately subordinate to the
       crimson "Start here" card — distinguishable but not a call to action. */
    .task-link {
      font: inherit;
      cursor: pointer;
      font-size: var(--text-14);
      font-weight: var(--weight-semibold);
      color: var(--color-brand-slate);
      background: var(--color-surface-slate);
      border: var(--border-thin) solid var(--color-border-slate);
      border-radius: var(--radius-pill);
      padding: var(--space-8) var(--space-16);
      transition: color 0.15s, background 0.15s, border-color 0.15s;
    }
    .task-link:hover { color: var(--color-surface); background: var(--color-brand-slate); border-color: var(--color-brand-slate); }
    /* ── Readiness tracker + any remaining .door cards (lease-first onboarding) ── */
    .doors {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
      gap: var(--space-12);
      margin-bottom: var(--space-32);
    }
    @media (max-width: 640px) { .doors { grid-template-columns: 1fr; } }
    .workflow-head--sub {
      margin-top: var(--space-8);
      color: var(--color-brand-slate);
    }
    .door {
      position: relative;   /* anchor for .door-tip */
      display: flex;
      flex-direction: column;
      align-items: flex-start;
      gap: var(--space-8);
      text-align: left;
      background: var(--color-surface-sage);
      border: var(--border-thin) solid var(--color-border-sage);
      border-radius: var(--radius-xl);
      padding: var(--space-20);
      font: inherit;
      cursor: pointer;
      transition: border-color 0.15s, background 0.15s, box-shadow 0.15s;
    }
    /* Hover/focus explainer bubble on a door (used on Door 3 — the audit door). */
    .door-tip {
      position: absolute;
      left: var(--space-16);
      right: var(--space-16);
      bottom: calc(100% + var(--space-8));
      background: var(--color-surface-dark);
      color: var(--color-text-on-accent);
      font-size: var(--text-12);
      font-weight: var(--weight-regular);
      line-height: 1.45;
      text-align: left;
      padding: var(--space-8) var(--space-12);
      border-radius: var(--radius-md);
      box-shadow: var(--shadow-md);
      opacity: 0;
      visibility: hidden;
      transform: translateY(var(--space-4));
      transition: opacity 0.15s, transform 0.15s, visibility 0.15s;
      z-index: 20;
      pointer-events: none;
    }
    .door:hover .door-tip, .door:focus-visible .door-tip { opacity: 1; visibility: visible; transform: translateY(0); }
    .door:hover { border-color: var(--color-accent); background: var(--color-surface); box-shadow: var(--shadow-hover); }
    .door-eyebrow {
      font-size: var(--text-12);
      font-weight: var(--weight-semibold);
      text-transform: uppercase;
      letter-spacing: var(--tracking-8);
      color: var(--color-text-faint);
    }
    .door-title {
      font-size: var(--text-18);
      font-weight: var(--weight-semibold);
      color: var(--color-text-heading);
    }
    .door-desc {
      font-size: var(--text-13);
      line-height: 1.5;
      color: var(--color-text-muted);
    }
    .door-cta {
      margin-top: var(--space-4);
      font-size: var(--text-13);
      font-weight: var(--weight-semibold);
      color: var(--color-brand-slate);
    }
    .door:hover .door-cta { color: var(--color-accent); }

    .readiness { text-align: left; margin-bottom: var(--space-28); }
    .ready-item {
      display: flex;
      align-items: flex-start;
      gap: var(--space-12);
      padding: var(--space-14) var(--space-16);
      border: var(--border-thin) solid var(--color-border-subtle);
      border-radius: var(--radius-lg);
      margin-bottom: var(--space-8);
      background: var(--color-surface);
    }
    .ready-item.is-next { border-color: var(--color-accent); background: var(--color-surface-sage); }
    .ready-item.is-todo { opacity: 0.6; }
    .ready-mark {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      width: 24px;
      height: 24px;
      flex-shrink: 0;
      border-radius: var(--radius-circle);
      font-size: var(--text-13);
      font-weight: var(--weight-bold);
      background: var(--color-border-subtle);
      color: var(--color-text-muted);
    }
    .ready-item.is-done .ready-mark { background: var(--color-success); color: var(--color-surface); }
    .ready-body { flex: 1; }
    .ready-label {
      display: block;
      font-size: var(--text-15);
      font-weight: var(--weight-semibold);
      color: var(--color-text-heading);
    }
    .ready-status {
      display: block;
      font-size: var(--text-13);
      color: var(--color-text-muted);
      margin-top: var(--space-2);
    }
    .ready-actions { margin-top: var(--space-8); display: flex; flex-wrap: wrap; gap: var(--space-8); }
    .ready-cta { margin-top: var(--space-16); text-align: center; }

    /* ── Guided workflow: the STICKY RAIL + the bottom advance footer ── */
    /* The rail answers one question — "where am I in this, and how much is left?" — and it
       has to answer it at the scroll position the manager is ACTUALLY at. It used to be a
       tall five-column stepper parked at the top of the document: ~84px of chrome that was
       off-viewport the moment anyone scrolled a roster, i.e. gone exactly when they were
       deepest in the work and most likely to wonder. (Same lesson as the roster's per-row
       notes and the property tab strip: a control that is off-viewport does not exist.)
       So: pinned under the nav, one row, 44px — SMALLER than the thing it replaces, and
       always there. Per-step prose is gone; the dots carry 1-2-3-4-5 and ONE line names the
       step you are on. Discreet by default — see docs/cam-character.md. */
    .wf-bar { display: none; }
    .wf-bar.show {
      display: block;
      position: sticky;
      /* Clear the nav AND, when it is showing, the return crumb — otherwise the crumb
         (z-index 40, and LATER in the document on the lease/reconcile screens) pins to the
         same line and covers the rail. --size-return-band is 0 when there is no crumb. */
      top: calc(var(--size-nav) + var(--size-return-band));
      z-index: 35;                       /* under the nav (50) and the return crumb (40) */
      /* A band, not a floating pill: the page scrolls UNDER it, so it needs an opaque
         full-width ground or the content behind it collides with the dots. */
      background: var(--color-canvas);
      border-bottom: var(--border-thin) solid var(--color-border-sage);
      margin: 0 0 var(--space-16);
    }
    .wf-rail {
      display: flex;
      align-items: center;
      gap: var(--space-12);
      min-height: var(--size-wf-rail);
    }
    .wf-steps { display: flex; align-items: center; flex: 0 0 auto; }
    .wf-step {
      display: inline-flex;
      align-items: center;
      background: none;
      border: none;
      font: inherit;
      cursor: pointer;
      padding: var(--space-0);
      color: inherit;
    }
    /* The connector is a real element BEFORE each dot (not an absolutely-positioned
       half-width bar), so the rail lays out at its natural width instead of stretching
       each step to a fifth of the page. */
    .wf-step::before {
      content: "";
      width: var(--size-wf-link);
      height: var(--border-thick);
      background: var(--color-border-sage);
      flex: 0 0 auto;
    }
    .wf-step:first-child::before { display: none; }
    .wf-step.done::before, .wf-step.active::before { background: var(--color-success); }
    .wf-dot {
      width: var(--size-wf-dot);
      height: var(--size-wf-dot);
      border-radius: var(--radius-circle);
      display: inline-flex;
      align-items: center;
      justify-content: center;
      font-size: var(--text-12);
      font-weight: var(--weight-bold);
      background: var(--color-surface);
      color: var(--color-text-faint);
      border: var(--border-thick) solid var(--color-border-sage);
      transition: background 0.15s, color 0.15s, border-color 0.15s;
    }
    .wf-step:hover .wf-dot { border-color: var(--color-border-strong); }
    .wf-step.done .wf-dot { background: var(--color-success); color: var(--color-surface); border-color: var(--color-success); }
    .wf-step.active .wf-dot { background: var(--color-accent); color: var(--color-surface); border-color: var(--color-accent); }
    /* "Step 3 of 5 — Review the AI output". ONE label, for the step you are on: five of
       them is what made the old stepper tall, and on a phone all five were hidden, so the
       rail said nothing at all about where you were. Truncates rather than wrapping — the
       rail's height is the whole point. */
    .wf-now {
      display: flex;
      align-items: baseline;
      gap: var(--space-6);
      min-width: 0;
      font-size: var(--text-13);
      line-height: 1.3;
      white-space: nowrap;
      overflow: hidden;
    }
    .wf-now-count { color: var(--color-text-faint); flex: 0 0 auto; }
    .wf-now-title {
      color: var(--color-text-heading);
      font-weight: var(--weight-semibold);
      overflow: hidden;
      text-overflow: ellipsis;
    }
    .wf-now-short { display: none; }
    @media (max-width: 640px) {
      /* A phone has room for the count and a SHORT name, not the full step title. The
         short name still carries the meaning on its own (it is not a hint on a hover
         nobody has) — the full title rides on the button's title/aria-label. */
      .wf-now-title { display: none; }
      .wf-now-short {
        display: inline;
        color: var(--color-text-heading);
        font-weight: var(--weight-semibold);
        overflow: hidden;
        text-overflow: ellipsis;
      }
      .wf-rail { gap: var(--space-8); }
      .wf-step::before { width: var(--space-8); }
    }

    /* The crumb band is a FIXED height so --size-return-band can clear it without being
       measured at runtime. One line, one button — nothing in it can wrap. */
    .has-return { min-height: var(--size-crumb); box-sizing: border-box; }

    .wf-foot { display: none; margin: var(--space-28) 0 var(--space-8); }
    /* The advance footer STICKS to the bottom of the viewport, so "Continue to Step N"
       is always one click away. Without this, a manager who has scrolled through a long
       roster — or who is reusing reports and needs to do nothing on this step but move
       on — has to scroll all the way back down to advance. It's the last child of each
       workflow screen, so it settles into its natural place at the end of the screen. */
    .wf-foot.show {
      display: block;
      position: sticky;
      bottom: 0;
      z-index: 40;                        /* under the sticky nav (50) */
      background: var(--color-canvas);
      margin-bottom: 0;
      padding-bottom: var(--space-12);
    }
    .wf-foot-inner {
      border-top: var(--border-thin) solid var(--color-border-sage);
      padding-top: var(--space-20);
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: var(--space-16);
      flex-wrap: wrap;
    }
    .wf-foot-text { font-size: var(--text-14); color: var(--color-text-muted); flex: 1; min-width: 12rem; }
    .wf-foot-actions { display: flex; gap: var(--space-10); align-items: center; flex-shrink: 0; }
    /* Mobile: the "Continue to Step N · …" label is long. Stack the footer actions
       full-width and let the button wrap instead of overflowing the viewport. */
    @media (max-width: 640px) {
      /* Pinned to a phone viewport, the guidance sentence would eat a third of the
         screen — the stepper at the top already names the step, so the pinned bar
         carries just the actions. */
      .wf-foot-text { display: none; }
      .wf-foot-actions { width: 100%; flex-wrap: wrap; flex-shrink: 1; }
      .wf-foot-actions .btn-primary,
      .wf-foot-actions .btn-secondary {
        width: 100%; box-sizing: border-box; white-space: normal; text-align: center;
      }
    }

    /* ── Properties screen ── */
    .screen-header {
      margin-bottom: var(--space-24);
    }

    .screen-header h2 {
      font-size: var(--text-24);
      font-weight: var(--weight-bold);
      color: var(--color-text-heading);
      margin-bottom: var(--space-4);
    }

    .screen-header p {
      font-size: var(--text-14);
      color: var(--color-text-muted);
    }

    /* Form card */
    .form-card {
      background: var(--color-surface);
      border-radius: var(--radius-2xl);
      box-shadow: var(--shadow-md);
      padding: var(--space-28);
      margin-bottom: var(--space-24);
    }

    .form-card h3 {
      font-size: var(--text-13);
      font-weight: var(--weight-semibold);
      text-transform: uppercase;
      letter-spacing: var(--tracking-8);
      color: var(--color-text-faint);
      margin-bottom: var(--space-20);
    }

    .field { margin-bottom: var(--space-16); }
    .field:last-of-type { margin-bottom: var(--space-20); }

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

    .field input {
      width: 100%;
      border: var(--border-thin) solid var(--color-border);
      border-radius: var(--radius-md);
      padding: var(--space-10) var(--space-14);
      font: inherit;
      font-size: var(--text-15);
      color: var(--color-text-primary);
      background: var(--color-surface-sunken);
      transition: border-color 0.15s, box-shadow 0.15s;
      outline: none;
    }

    .field input:focus {
      border-color: var(--color-accent);
      box-shadow: var(--shadow-focus);
      background: var(--color-surface);
    }

    .field input::placeholder { color: var(--color-text-placeholder); }

    .form-actions { display: flex; justify-content: flex-end; }

    .btn-add {
      background: var(--color-accent);
      color: var(--color-text-on-accent);
      border: var(--border-thin) solid transparent;
      border-radius: var(--radius-md);
      font-family: var(--font-sans);
      font-size: var(--text-13);
      font-weight: var(--weight-semibold);
      text-transform: uppercase;
      letter-spacing: var(--tracking-8);
      padding: var(--space-12) var(--space-24);
      cursor: pointer;
      transition: background 0.15s;
    }

    .btn-add:hover { background: var(--color-accent-hover); }

    /* Properties list */
    .properties-list { display: flex; flex-direction: column; gap: var(--space-12); }

    .property-item {
      background: var(--color-surface);
      border-radius: var(--radius-xl);
      box-shadow: var(--shadow-sm);
      padding: var(--space-16) var(--space-20);
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: var(--space-16);
    }

    .property-item .prop-name {
      font-weight: var(--weight-semibold);
      font-size: var(--text-15);
      color: var(--color-text-primary);
    }

    /* The sub-line is metadata, and at 14 it wrapped to two lines on a phone — which made
       it the tallest thing on a row whose point is to be scanned. 13 fits it on one. */
    .property-item .prop-sf {
      font-size: var(--text-13);
      color: var(--color-text-muted);
      margin-top: var(--space-2);
    }

    /* The row's chip line: icons with short hints in place of three prose lines. Wraps
       rather than truncating — on a narrow phone two chips take a second line, which is
       still less than the one sentence they replaced. */
    .property-item .prop-chips {
      display: flex;
      flex-wrap: wrap;
      align-items: center;
      gap: var(--space-4) var(--space-8);
      margin-top: var(--space-6);
    }

    .property-item .prop-chip {
      display: inline-flex;
      align-items: center;
      gap: var(--space-4);
      font-size: var(--text-12);
      color: var(--color-text-secondary);
      white-space: nowrap;
    }

    /* Only what is late or nearly late takes the accent. A deadline six months out is
       information, and colouring everything makes the whole list look urgent — which reads
       the same as none of it being urgent. */
    .property-item .prop-chip--alert { color: var(--color-accent); font-weight: var(--weight-semibold); }
    .property-item .prop-chip--soon { color: var(--color-accent); }
    .property-item .prop-chip--warn { color: var(--color-text-primary); }

    /* Sized off the spacing scale, exactly as .pl-btn-icon and .hub-inv-icon are — one
       icon size across the app rather than a new token for this one row. */
    .property-item .prop-chip-icon {
      width: var(--space-16);
      height: var(--space-16);
      flex-shrink: 0;
    }

    /* Muted record-id fragment on the row sub-line — mono marks it as a diagnostic
       handle (telling two same-named rows apart), inheriting prop-sf's muted colour. */
    .property-item .prop-record {
      font-family: var(--font-mono);
      font-size: var(--text-12);
    }

    .property-item .prop-badge {
      background: var(--color-accent-subtle);
      color: var(--color-accent);
      font-size: var(--text-12);
      font-weight: var(--weight-semibold);
      padding: var(--space-4) var(--space-10);
      border-radius: var(--radius-pill);
      white-space: nowrap;
      flex-shrink: 0;
    }

    /* Status cell: the label pill stacked over a thin approval-progress bar, so how far
       along a reconciliation is can be scanned DOWN the list, not just read one row at a
       time. Three-state colour — nothing approved (danger), some approved (in-progress /
       slate, the brand's warning tone since it has no amber), all approved (success). A
       no-tenants row is a plain muted pill (nothing to progress) and skips the bar. */
    .property-item .prop-status {
      display: flex;
      flex-direction: column;
      align-items: flex-end;
      gap: var(--space-4);
      flex-shrink: 0;
    }
    .property-item .prop-badge--zero {
      background: var(--color-surface-danger);
      color: var(--color-danger);
    }
    .property-item .prop-badge--partial {
      background: var(--color-surface-slate);
      color: var(--color-warning-text-strong);
    }
    .property-item .prop-badge--done {
      background: var(--color-surface-success);
      color: var(--color-success-text);
    }
    .property-item .prop-badge--none {
      background: var(--color-surface-muted);
      color: var(--color-text-muted);
    }
    .property-item .prop-progress {
      display: block;
      width: var(--space-64);
      height: var(--space-4);
      background: var(--color-surface-muted);
      border-radius: var(--radius-pill);
      overflow: hidden;
    }
    .property-item .prop-progress-fill {
      display: block;
      height: 100%;
      background: var(--color-success);
      border-radius: var(--radius-pill);
    }
    /* Bar fill echoes the badge state: slate while in progress, green when complete.
       (Zero has width 0, so its fill never shows.) */
    .property-item .prop-progress-fill--partial { background: var(--color-credit); }
    .property-item .prop-progress-fill--done { background: var(--color-success); }

    .empty-state {
      text-align: center;
      padding: var(--space-40) var(--space-16);
      color: var(--color-text-faint);
      font-size: var(--text-14);
    }

    /* ── Lease Reader screen ── */
    .upload-card {
      background: var(--color-surface);
      border-radius: var(--radius-2xl);
      box-shadow: var(--shadow-md);
      padding: var(--space-28);
      margin-bottom: var(--space-24);
    }

    /* Review mode: the Lease Reader is opened to review ONE lease's analysis (from a property's Leases
       screen), so the Step-2 setup chrome — the upload workbench especially — is hidden and only the
       analysis shows. This MUST use `display:none !important`: the chrome elements carry their own inline /
       class `display`, which beats a plain [hidden] attribute (the documented trap), so toggling `hidden`
       in JS alone left the "Extract CAM language" card visible above the analysis. `data-review` is the only
       thing JS toggles. */
    #screen-lease[data-review="1"] #lease-reader-intro,
    #screen-lease[data-review="1"] #lease-desk-hint,
    #screen-lease[data-review="1"] #wf-bar-lease,
    #screen-lease[data-review="1"] #lease-prop-context,
    #screen-lease[data-review="1"] #lease-assign-nudge,
    #screen-lease[data-review="1"] #lease-roster-section,
    #screen-lease[data-review="1"] #lease-upload-card,
    #screen-lease[data-review="1"] #bulk-lease-card,
    #screen-lease[data-review="1"] #lease-log-section,
    #screen-lease[data-review="1"] #wf-foot-lease { display: none !important; }
    .bundle-intro { margin: 0 0 var(--space-16); font-size: var(--text-14); color: var(--color-text-body); line-height: 1.5; }

    /* Building (Step 1) saved-state row + gating hint */
    .building-row {
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: var(--space-16);
      background: var(--color-surface-sage);
      border: var(--border-thin) solid var(--color-border-sage);
      border-radius: var(--radius-lg);
      padding: var(--space-14) var(--space-16);
    }
    .building-info { display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; }
    .building-label {
      font-size: var(--text-11);
      font-weight: var(--weight-semibold);
      text-transform: uppercase;
      letter-spacing: var(--tracking-8);
      color: var(--color-text-faint);
    }
    .building-value { font-size: var(--text-16); font-weight: var(--weight-semibold); color: var(--color-text-heading); word-break: break-word; }

    /* Inferred building-match suggestion (never auto-applied) + the pre-upload note. */
    .building-suggest {
      display: flex;
      flex-wrap: wrap;
      align-items: center;
      gap: var(--space-8) var(--space-12);
      margin-top: var(--space-16);
      padding: var(--space-12) var(--space-14);
      background: var(--color-accent-bg);
      border-left: var(--border-accent) solid var(--color-accent);
      font-size: var(--text-14);
    }
    .building-suggest .bsug-text { color: var(--color-text-secondary); line-height: 1.5; }
    .building-suggest .bsug-actions { display: flex; gap: var(--space-8); margin-left: auto; flex-wrap: wrap; }
    .building-derive-note { margin-top: var(--space-12); font-size: var(--text-13); color: var(--color-text-faint); line-height: 1.45; }

    /* Property readiness readout on "Confirm the building": roster shape + how many
       tenants can be reconciled once lease + GL are in. */
    .prop-readiness {
      margin-top: var(--space-16);
      padding: var(--space-14) var(--space-16);
      background: var(--color-surface-sunken);
      border: var(--border-thin) solid var(--color-border);
    }
    .prop-readiness .rd-stats { display: flex; flex-wrap: wrap; gap: var(--space-8) var(--space-16); }
    .prop-readiness .rd-stat { font-size: var(--text-14); color: var(--color-text-secondary); }
    .prop-readiness .rd-stat strong { color: var(--color-text-heading); font-weight: var(--weight-semibold); }
    .prop-readiness .rd-ready {
      margin-top: var(--space-12);
      font-size: var(--text-16);
      font-weight: var(--weight-semibold);
      color: var(--color-text-muted);
    }
    .prop-readiness .rd-ready--on { color: var(--color-text-heading); }
    .prop-readiness .rd-count {
      font-family: var(--font-serif);
      font-weight: var(--weight-regular);
      font-size: var(--text-24);
      color: var(--color-accent);
      margin-right: var(--space-2);
    }
    .prop-readiness .rd-sub { margin-top: var(--space-6); font-size: var(--text-12); color: var(--color-text-faint); line-height: 1.5; }
    .prop-readiness .rd-nudge { margin-top: var(--space-10); font-size: var(--text-13); color: var(--color-text-secondary); }
    .building-hint, .gate-hint { margin: -var(--space-4) 0 var(--space-16); font-size: var(--text-13); color: var(--color-danger); }

    /* Rent Roll slot reads better as one full-width row */
    #rentroll-card .slot-grid { grid-template-columns: 1fr; max-width: 460px; }

    /* Tenant Roster (Step 1) */
    .roster-meta { display: flex; flex-wrap: wrap; gap: var(--space-10); margin-bottom: var(--space-14); }
    .roster-stat {
      font-size: var(--text-12);
      color: var(--color-text-body);
      background: var(--color-surface-info);
      border-radius: var(--radius-pill);
      padding: var(--space-4) var(--space-12);
    }
    .roster-item {
      display: grid;
      grid-template-columns: 1fr 2fr 1fr;
      gap: var(--space-16);
      align-items: center;
      background: var(--color-surface);
      border-radius: var(--radius-lg);
      box-shadow: var(--shadow-sm);
      padding: var(--space-12) var(--space-16);
      margin-bottom: var(--space-8);
      font-size: var(--text-14);
    }
    .roster-item.roster-head {
      box-shadow: none;
      background: transparent;
      padding-bottom: var(--space-4);
      margin-bottom: var(--space-4);
      font-size: var(--text-11);
      font-weight: var(--weight-semibold);
      text-transform: uppercase;
      letter-spacing: var(--tracking-6);
      color: var(--color-text-faint);
    }
    .roster-item.vacant { background: var(--color-surface-sunken); color: var(--color-text-faint); }
    /* Tenant brand chip (Logo.dev) — square, neutral, monogram base with the
       resolved logo layered on top. On brand: square corners, one-accent palette. */
    .tenant-logo {
      position: relative;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      width: 26px;
      height: 26px;
      flex-shrink: 0;
      overflow: hidden;
      background: var(--color-surface-muted);
      border: var(--border-thin) solid var(--color-border);
      vertical-align: middle;
    }
    .tenant-logo .tl-mono {
      font-family: var(--font-sans);
      font-weight: var(--weight-semibold);
      font-size: var(--text-11);
      letter-spacing: var(--tracking-2);
      line-height: 1;
      color: var(--color-slate-text);
    }
    /* Hidden until it actually loads, so the monogram shows through while the
       request is in flight (and stays if it errors — the img removes itself). */
    .tenant-logo img {
      position: absolute;
      inset: 0;
      width: 100%;
      height: 100%;
      object-fit: contain;
      background: var(--color-surface);
      opacity: 0;
      transition: opacity 0.15s;
    }
    .tenant-logo img.loaded { opacity: 1; }
    .tenant-logo--sm { width: 20px; height: 20px; }
    .tenant-logo--sm .tl-mono { font-size: var(--text-7); }
    .tenant-logo--lg { width: 40px; height: 40px; }
    .tenant-logo--lg .tl-mono { font-size: var(--text-14); }
    /* Name cells become a logo + name row */
    .roster-name { display: flex; align-items: center; gap: var(--space-10); min-width: 0; }
    /* Step-1 roster: name stacked over a lease-status chip. */
    .roster-name .rn-body { display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; }
    .roster-lease { font-size: var(--text-12); font-weight: var(--weight-semibold); letter-spacing: var(--tracking-2); }
    .roster-lease.ok { color: var(--color-success-text); }        /* lease on file — sparing green ✓ */
    .roster-lease.none { color: var(--color-text-faint); font-weight: var(--weight-regular); }
    .roster-lease.act { color: var(--color-accent); }             /* actionable — routes to upload */
    .roster-item.vacant .roster-lease { display: none; }
    /* A roster row is a shortcut into that tenant's lease — clickable, with a hover cue. */
    .roster-item--action { cursor: pointer; transition: background 0.12s, box-shadow 0.12s; }
    .roster-item--action:hover { background: var(--color-surface-sunken); box-shadow: var(--shadow-hover); }
    .reco-tenant-cell { display: inline-flex; align-items: center; gap: var(--space-8); }
    .statement-headline { display: flex; align-items: center; gap: var(--space-14); min-width: 0; }

    .roster-suite { font-weight: var(--weight-semibold); color: var(--color-text-heading); }
    .roster-item.vacant .roster-suite { color: var(--color-text-faint); }
    .roster-sf { text-align: right; color: var(--color-text-secondary); }
    .roster-note { margin-top: var(--space-12); font-size: var(--text-13); color: var(--color-text-subtle); line-height: 1.45; }

    /* Per-tenant lease upload (Step 2) */
    .lease-caption {
      font-size: var(--text-15);
      font-weight: var(--weight-semibold);
      color: var(--color-text-heading);
      background: var(--color-surface-info);
      border-radius: var(--radius-md);
      padding: var(--space-10) var(--space-14);
      margin-bottom: var(--space-16);
    }
    /* Roster rows on the lease screen carry an action column */
    #lease-roster-list .roster-item { grid-template-columns: 1fr 2fr 1fr auto; }
    #lease-roster-list .roster-item.roster-head { grid-template-columns: 1fr 2fr 1fr auto; }
    .roster-actions { display: flex; gap: var(--space-6); justify-content: flex-end; flex-wrap: wrap; }
    .roster-btn {
      border: var(--border-thin) solid var(--color-accent);
      background: var(--color-surface);
      color: var(--color-accent);
      border-radius: var(--radius-sm);
      padding: var(--space-6) var(--space-12);
      font: inherit;
      font-size: var(--text-13);
      font-weight: var(--weight-semibold);
      cursor: pointer;
    }
    .roster-btn.primary { background: var(--color-accent); color: var(--color-surface); }
    .roster-btn:hover { filter: brightness(0.96); }

    /* Uploaded rows carry a highlight strip, so the card becomes a stack:
       the action row on top, the extracted-analysis strip below. */
    #lease-roster-list .roster-item.has-lease { display: block; padding: 0; overflow: hidden; }
    .roster-item.has-lease .roster-main {
      display: grid;
      grid-template-columns: 1fr 2fr 1fr auto;
      gap: var(--space-16);
      align-items: center;
      padding: var(--space-12) var(--space-16);
    }
    .roster-highlight {
      display: flex;
      flex-wrap: wrap;
      gap: var(--space-6);
      align-items: center;
      padding: var(--space-8) var(--space-16) var(--space-12);
      border-top: var(--border-thin) solid var(--color-border-subtle);
      background: var(--color-surface-sunken);
    }
    .lh-pill {
      font-size: var(--text-12);
      font-weight: var(--weight-medium);
      color: var(--color-text-secondary);
      background: var(--color-surface);
      border: var(--border-thin) solid var(--color-border-subtle);
      border-radius: var(--radius-pill);
      padding: var(--space-2) var(--space-10);
      white-space: nowrap;
    }
    .lh-pill.lh-share { color: var(--color-text-heading); font-weight: var(--weight-semibold); }
    .lh-pill.warn {
      color: var(--color-warning-text);
      border-color: var(--color-warning-accent);
      background: var(--color-surface-warning);
    }
    .lh-pill.lh-muted { color: var(--color-text-faint); background: transparent; border-color: transparent; }
    .lh-pill.lh-loading { color: var(--color-text-faint); background: transparent; border-style: dashed; }
    /* Reanalysis in progress: accent-filled and prominent (unlike the faint first-load
       lh-loading), so the one "actively working" signal actually catches the eye. */
    .lh-pill.lh-busy { color: var(--color-accent); background: var(--color-accent-subtle); border-color: var(--color-border-accent); font-weight: var(--weight-semibold); }
    /* Spinning glyph — reuses the checklist spinner keyframe (clspin) for a shared
       "working" language across the app. */
    .lh-spin { display: inline-block; animation: clspin 0.7s linear infinite; }
    .lh-pill.lh-risk-low { color: var(--color-success-text); }
    .lh-pill.lh-risk-med { color: var(--color-warning-text); }
    .lh-pill.lh-risk-high { color: var(--color-danger); }
    .slot-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-12); }
    /* The lease bundle has four slots (lease · commencement · estoppel · amendments);
       flow them so they don't leave a lopsided second row at three-up. */
    #lease-upload-card .slot-grid { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
    @media (max-width: 640px) { .slot-grid { grid-template-columns: 1fr; } }
    .slot {
      position: relative; display: flex; flex-direction: column; gap: var(--space-4);
      border: var(--border-thick) dashed var(--color-border-strong); border-radius: var(--radius-xl); padding: var(--space-16); cursor: pointer;
      transition: border-color .15s, background .15s;
    }
    /* An author `display` BEATS the UA's [hidden] { display: none }, so a slot hidden via the
       `hidden` attribute (folderPickSupported withholding the folder picker on iOS) stayed
       fully visible and inert without this rule — the same trap the nav profile menu carries.
       Caught by the iPhone-emulation test, not by looking at it. */
    .slot[hidden] { display: none; }
    .slot:hover { border-color: var(--color-accent); }
    .slot.filled { border-style: solid; border-color: var(--color-accent); background: var(--color-surface-sage); }
    .slot input[type=file] { position: absolute; inset: 0; opacity: 0; cursor: pointer; }
    .slot-role { font-size: var(--text-13); font-weight: var(--weight-bold); color: var(--color-text-heading); }
    .slot-role .req { color: var(--color-danger); font-weight: var(--weight-semibold); font-size: var(--text-11); }
    .slot-role .opt { color: var(--color-text-faint); font-weight: var(--weight-medium); font-size: var(--text-11); }
    .slot-file { font-size: var(--text-13); color: var(--color-text-muted); }
    .slot.filled .slot-file { color: var(--color-success-text); font-weight: var(--weight-semibold); }
    .doc-chip { display: inline-block; font-size: var(--text-11); font-weight: var(--weight-bold); padding: var(--space-1) var(--space-8); border-radius: var(--radius-full); background: var(--color-surface-muted); color: var(--color-text-body); margin-left: var(--space-4); }

    /* Inline field help: a small "i" icon that reveals its guidance on hover / focus,
       so per-field instructions stay a click away instead of cluttering the screen.
       Lifted above the slot's transparent file-input overlay (z-index) so it's hoverable. */
    .help-tip { position: relative; z-index: 2; display: inline-flex; vertical-align: middle; margin-left: var(--space-4); }
    .help-tip__icon {
      display: inline-flex; align-items: center; justify-content: center;
      width: 1rem; height: 1rem; border-radius: var(--radius-full);
      border: var(--border-thin) solid var(--color-border-strong);
      color: var(--color-text-muted); background: var(--color-surface);
      font-size: var(--text-11); font-weight: var(--weight-bold); font-style: normal; line-height: 1; cursor: help;
    }
    .help-tip:hover .help-tip__icon, .help-tip:focus-visible .help-tip__icon,
    .help-tip:focus-within .help-tip__icon { border-color: var(--color-accent); color: var(--color-accent); }
    .help-tip__pop {
      position: absolute; z-index: 3; top: 100%; left: 0;
      display: none; width: max-content; max-width: 260px;   /* tokens-allow: tooltip width, not a spacing/type value */
      padding: var(--space-8) var(--space-12);
      background: var(--color-surface); color: var(--color-text-body);
      border: var(--border-thin) solid var(--color-border); border-radius: var(--radius-md);
      box-shadow: var(--shadow-md);
      font-size: var(--text-12); font-weight: var(--weight-medium); line-height: 1.45; text-align: left; white-space: normal;
    }
    .help-tip__pop strong { font-weight: var(--weight-semibold); }
    .help-tip:hover .help-tip__pop, .help-tip:focus-visible .help-tip__pop,
    .help-tip:focus-within .help-tip__pop { display: block; }

    /* Multiple amendments / addenda per lease bundle */
    .amend-list { display: flex; flex-wrap: wrap; gap: var(--space-6); margin-top: var(--space-12); }
    .amend-chip {
      display: inline-flex; align-items: center; gap: var(--space-6);
      font-size: var(--text-12); font-weight: var(--weight-semibold); color: var(--color-accent);
      background: var(--color-surface-sage); border: var(--border-thin) solid var(--color-border-success-subtle); border-radius: var(--radius-full);
      padding: var(--space-4) var(--space-4) var(--space-4) var(--space-12);
    }
    .amend-x {
      border: none; background: var(--color-surface-neutral); color: var(--color-slate-icon); cursor: pointer;
      width: 1.15rem; height: 1.15rem; border-radius: var(--radius-full); line-height: 1;
      font-size: var(--text-14); font-weight: var(--weight-bold); display: inline-flex; align-items: center; justify-content: center;
    }
    .amend-x:hover { background: var(--color-slate-border); color: var(--color-slate-text); }
    .amend-hint { margin: var(--space-8) 0 0; font-size: var(--text-12); color: var(--color-text-subtle); line-height: 1.45; }

    /* Bulk lease upload (issue 159): one row per staged/processing lease. */
    .bulk-list { display: flex; flex-direction: column; gap: var(--space-6); margin-top: var(--space-12); }
    .bulk-list:empty { margin-top: 0; }
    .bulk-row {
      display: flex; align-items: center; gap: var(--space-8);
      padding: var(--space-8) var(--space-12);
      background: var(--color-surface-muted); border-radius: var(--radius-md);
      font-size: var(--text-13);
    }
    .bulk-row-name { color: var(--color-text-body); font-weight: var(--weight-semibold); min-width: 0; overflow-wrap: anywhere; }
    .bulk-row-size { color: var(--color-text-muted); font-weight: var(--weight-regular); }
    .bulk-row-status { margin-left: auto; }
    .bulk-row .amend-x { flex: none; }

    /* The task's shape, stated before the first click (issue 431 review feedback): the card
       otherwise offers three equal-looking inputs and says nothing about what follows. */
    .bulk-steps {
      display: flex; flex-wrap: wrap; gap: var(--space-16);
      margin: 0 0 var(--space-12); padding: 0; list-style: none;
      font-size: var(--text-13); color: var(--color-text-muted);
    }
    .bulk-steps li { display: flex; align-items: center; gap: var(--space-6); }
    .bulk-step-n {
      display: inline-flex; align-items: center; justify-content: center;
      width: var(--space-20); height: var(--space-20); flex: none;
      background: var(--color-accent); color: var(--color-surface);
      border-radius: var(--radius-md); font-size: var(--text-11); font-weight: var(--weight-bold);
    }
    /* Answers "does this upload straight away?" and "what about other folders?" while the
       inputs are still untouched. Must not be moved inside the review list, which is hidden
       until files are staged — that is the bug this replaced. */
    .bulk-add-lead {
      margin: 0 0 var(--space-12); font-size: var(--text-13);
      color: var(--color-text-body); line-height: 1.5;
    }

    .bulk-action-lead {
      margin: var(--space-16) 0 var(--space-8); font-size: var(--text-13);
      color: var(--color-text-muted); line-height: 1.5;
    }

    /* Drag-and-drop target (issue 431): the only way to gather leases from SEVERAL folders in
       one gesture — the OS dialog selects within one folder at a time. */
    .bulk-drop {
      margin-top: var(--space-12); padding: var(--space-20) var(--space-16);
      border: var(--border-thick) dashed var(--color-border-strong); border-radius: var(--radius-xl);
      text-align: center; transition: border-color .15s, background .15s;
    }
    .bulk-drop-main { display: block; font-size: var(--text-14); font-weight: var(--weight-semibold); color: var(--color-text-heading); }
    .bulk-drop-hint { display: block; margin-top: var(--space-6); font-size: var(--text-12); color: var(--color-text-subtle); line-height: 1.45; }
    .bulk-drop.is-over { border-color: var(--color-accent); background: var(--color-surface-sage); }
    .bulk-drop.is-over .bulk-drop-main { color: var(--color-accent); }

    /* Review & deselect (issue 431): the staged list becomes a checklist so a folder pick
       can be pruned before anything is uploaded. A deselected row dims but stays visible —
       removing it outright would hide the decision the manager just made. */
    .bulk-review { margin-top: var(--space-12); }
    .bulk-review-head { display: flex; align-items: baseline; gap: var(--space-12); }
    .bulk-review-count { font-size: var(--text-13); font-weight: var(--weight-semibold); color: var(--color-text-heading); }
    .bulk-review-all {
      margin-left: auto; background: none; border: none; padding: 0; font: inherit;
      font-size: var(--text-13); color: var(--color-accent); cursor: pointer; text-decoration: underline;
    }
    .bulk-row-pick { flex: none; margin: 0; }
    .bulk-row-folder { color: var(--color-text-subtle); font-weight: var(--weight-regular); }
    .bulk-row--off { opacity: 0.55; }
    /* One heading per place the batch was assembled from, so a list built out of three
       folders reads as three groups instead of an undifferentiated wall of filenames. */
    .bulk-group-head {
      display: flex; align-items: baseline; gap: var(--space-8);
      margin-top: var(--space-12); font-size: var(--text-12); font-weight: var(--weight-semibold);
      color: var(--color-text-muted); text-transform: uppercase; letter-spacing: var(--tracking-5);
    }
    .bulk-group-head:first-child { margin-top: 0; }
    .bulk-group-count { font-weight: var(--weight-regular); text-transform: none; letter-spacing: normal; color: var(--color-text-subtle); }

    /* The confirm step — the last stop before AI credits are spent. */
    .bulk-confirm {
      margin-top: var(--space-12); padding: var(--space-16);
      border: var(--border-thin) solid var(--color-accent); border-radius: var(--radius-md);
      background: var(--color-surface-muted);
    }
    .bulk-confirm-lead { margin: 0 0 var(--space-8); font-size: var(--text-14); font-weight: var(--weight-semibold); color: var(--color-text-heading); }
    .bulk-confirm-list { margin: 0 0 var(--space-12); padding-left: var(--space-20); font-size: var(--text-13); color: var(--color-text-body); }
    .bulk-confirm-list li { margin-bottom: var(--space-4); line-height: 1.45; }

    /* "You can leave" + resumable batch progress. */
    .bulk-batch-panel {
      margin-top: var(--space-12); padding: var(--space-16);
      border: var(--border-thin) solid var(--color-border-subtle); border-radius: var(--radius-md);
      background: var(--color-surface-muted);
    }
    .bulk-batch-title { margin: 0 0 var(--space-6); font-size: var(--text-14); font-weight: var(--weight-semibold); color: var(--color-text-heading); }
    .bulk-batch-note { margin: 0 0 var(--space-8); font-size: var(--text-13); color: var(--color-text-body); line-height: 1.5; }
    .bulk-batch-meter { height: var(--space-6); border-radius: var(--radius-md); background: var(--color-surface-sunken); overflow: hidden; }
    /* display:block matters — the fill is a <span>, and height:100% does nothing on an
       inline box, so the meter rendered as an empty strip at every percentage. */
    .bulk-batch-fill { display: block; height: 100%; background: var(--color-accent); transition: width 240ms ease; }
    .bulk-batch-actions { display: flex; flex-wrap: wrap; gap: var(--space-8); margin-top: var(--space-12); }

    /* Attach-existing-lease picker (link an analyzed lease to a roster tenant). */
    .attach-lease-row {
      display: flex; align-items: center; justify-content: space-between; gap: var(--space-12);
      padding: var(--space-12); border-bottom: 1px solid var(--color-border);
    }
    .attach-lease-row:last-child { border-bottom: none; }
    .attach-lease-info { display: flex; flex-direction: column; gap: var(--space-4); min-width: 0; }
    .attach-lease-name { color: var(--color-text-body); font-weight: var(--weight-semibold); }
    .attach-lease-detail { color: var(--color-text-muted); font-size: var(--text-13); }

    /* Add a lease by name — search ALL analyzed leases for one the project grouping missed. */
    .lease-add-search { margin-top: var(--space-16); }
    .lease-add-search__lbl { display: block; font-size: var(--text-13); color: var(--color-text-muted); margin-bottom: var(--space-6); }
    .lease-add-results { margin-top: var(--space-8); display: flex; flex-direction: column; gap: var(--space-4); }
    .lease-add-results:empty { display: none; }
    .lease-add-note { font-size: var(--text-13); color: var(--color-text-muted); padding: var(--space-8) var(--space-4); }
    .lease-add-result {
      display: flex; align-items: center; gap: var(--space-12); width: 100%; text-align: left;
      padding: var(--space-8) var(--space-12); font: inherit; color: var(--color-text-body);
      background: var(--color-surface-muted); border: var(--border-thin) solid var(--color-border-subtle);
      border-radius: var(--radius-md); cursor: pointer;
    }
    .lease-add-result:hover:not(:disabled) { border-color: var(--color-accent); background: var(--color-surface-sunken); }
    .lease-add-result:disabled { cursor: default; opacity: 0.6; }
    .lease-add-result__main { display: flex; flex-direction: column; gap: var(--space-4); min-width: 0; }
    .lease-add-result__name { font-weight: var(--weight-semibold); }
    .lease-add-result__meta { font-size: var(--text-13); color: var(--color-text-muted); }
    .lease-add-result__add { margin-left: auto; flex: none; color: var(--color-accent); font-weight: var(--weight-semibold); font-size: var(--text-13); }
    .lease-add-result:disabled .lease-add-result__add { color: var(--color-text-muted); }

    /* Auto-match preview (issue 159): documents clustered by matched tenant. */
    .bulk-match-preview { display: flex; flex-direction: column; gap: var(--space-12); }
    .bulk-match-preview:empty { display: none; }
    .match-group {
      border: var(--border-thin) solid var(--color-border-subtle); border-radius: var(--radius-md);
      padding: var(--space-12); margin-top: var(--space-12);
    }
    .match-group-head { margin: 0 0 var(--space-8); font-size: var(--text-13); font-weight: var(--weight-semibold); color: var(--color-text-heading); }
    .match-group--unmatched { border-color: var(--color-border-danger); background: var(--color-surface-danger); }

    .dropzone {
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: var(--space-6);
      border: var(--border-thick) dashed var(--color-border-strong);
      border-radius: var(--radius-xl);
      padding: var(--space-32) var(--space-16);
      text-align: center;
      color: var(--color-text-muted);
      font-size: var(--text-14);
      cursor: pointer;
      transition: border-color 0.15s, background 0.15s;
    }
    .dropzone .dz-icon {
      width: 28px; height: 28px;
      fill: none; stroke: var(--color-accent); stroke-width: 1.8;
      stroke-linecap: round; stroke-linejoin: round;
      margin-bottom: var(--space-4);
    }

    .dropzone:hover { border-color: var(--color-accent); background: var(--color-surface-sage); }
    .dropzone strong { color: var(--color-accent); font-weight: var(--weight-semibold); }
    .dropzone .hint { display: block; margin-top: var(--space-6); font-size: var(--text-12); color: var(--color-text-faint); }
    .dropzone input { display: none; }

    .file-chip {
      display: none;
      align-items: center;
      gap: var(--space-10);
      margin-top: var(--space-16);
      padding: var(--space-10) var(--space-14);
      background: var(--color-surface-sage);
      border-radius: var(--radius-md);
      font-size: var(--text-14);
      color: var(--color-text-primary);
    }
    .file-chip.show { display: flex; }
    .file-chip .file-size { color: var(--color-text-faint); }

    .privacy-note {
      display: flex;
      gap: var(--space-8);
      margin-top: var(--space-16);
      font-size: var(--text-12);
      line-height: 1.5;
      color: var(--color-text-muted);
    }
    .privacy-note svg {
      width: 15px;
      height: 15px;
      flex-shrink: 0;
      margin-top: var(--space-2);
      fill: none;
      stroke: var(--color-accent);
      stroke-width: 2;
      stroke-linecap: round;
      stroke-linejoin: round;
    }

    .lease-actions { display: flex; justify-content: flex-end; margin-top: var(--space-20); }
    .recheck-actions { display: flex; gap: var(--space-8); flex-wrap: wrap; margin-top: var(--space-12); }
    .btn-add:disabled { background: var(--color-border); color: var(--color-text-on-accent); cursor: not-allowed; }

    /* Resume a saved property (server persistence) on the Home landing card */
    .resume { margin-top: var(--space-28); text-align: left; }
    .resume-head {
      font-size: var(--text-13); font-weight: 700; text-transform: uppercase;
      letter-spacing: var(--tracking-4); color: var(--color-text-muted);
      margin-bottom: var(--space-10);
    }
    .resume-item { display: flex; align-items: stretch; gap: var(--space-8); margin-bottom: var(--space-8); }
    .resume-open {
      flex: 1; display: flex; flex-direction: column; gap: var(--space-4); text-align: left;
      background: var(--color-surface); border: var(--border-thin) solid var(--color-border);
      border-radius: var(--radius-md); padding: var(--space-12) var(--space-16); cursor: pointer;
    }
    .resume-open:hover { border-color: var(--color-accent); background: var(--color-surface-sunken); }
    .resume-name { font-size: var(--text-15); font-weight: 600; color: var(--color-text-heading); }
    .resume-meta { font-size: var(--text-13); color: var(--color-text-muted); }
    .resume-del {
      flex: 0 0 auto; width: var(--space-36); border: var(--border-thin) solid var(--color-border);
      border-radius: var(--radius-md); background: var(--color-surface); color: var(--color-text-muted);
      font-size: var(--text-18); line-height: 1; cursor: pointer;
    }
    .resume-del:hover { color: var(--color-text-primary); border-color: var(--color-border-strong); }

    .lease-status {
      font-size: var(--text-14);
      color: var(--color-text-muted);
      margin-bottom: var(--space-16);
      min-height: 1.1rem;
    }
    .lease-status.error { color: var(--color-danger); }
    .lease-status.notice { color: var(--color-text-secondary); }

    .progress {
      height: 6px;
      background: var(--color-border-sage);
      border-radius: var(--radius-full);
      overflow: hidden;
      margin-bottom: var(--space-20);
    }
    .progress-bar {
      height: 100%;
      width: 40%;
      background: var(--color-accent);
      border-radius: var(--radius-full);
      animation: indeterminate 1.5s ease-in-out infinite;
    }
    @keyframes indeterminate {
      0%   { margin-left: -42%; }
      100% { margin-left: 100%; }
    }

    /* ── Progress checklist (executive stepper) ── */
    .checklist { list-style: none; margin: var(--space-16) 0 0; padding: 0; }
    .cl-item {
      display: flex;
      align-items: center;
      gap: var(--space-12);
      padding: var(--space-6) 0;
      font-size: var(--text-14);
      transition: opacity 0.2s;
    }
    .cl-mark {
      width: 18px; height: 18px;
      border-radius: var(--radius-circle);
      flex-shrink: 0;
      border: var(--border-thick) solid var(--color-border-strong);
      position: relative;
      transition: background 0.2s, border-color 0.2s;
    }
    .cl-item.pending .cl-label { color: var(--color-text-faint); }
    /* Active = in progress: a spinning ring (one accent edge on a sage circle). */
    .cl-item.active .cl-mark {
      border-color: var(--color-border-sage);
      border-top-color: var(--color-accent);
      animation: clspin 0.7s linear infinite;
    }
    .cl-item.active .cl-label { color: var(--color-text-primary); font-weight: var(--weight-semibold); }
    .cl-item.done .cl-mark { background: var(--color-accent); border-color: var(--color-accent); }
    .cl-item.done .cl-mark::after {
      content: ''; position: absolute; left: 5px; top: 2px;
      width: 4px; height: 8px;
      border: solid var(--color-surface); border-width: 0 2px 2px 0;
      transform: rotate(45deg);
    }
    .cl-item.done .cl-label { color: var(--color-text-secondary); }
    @keyframes clspin { to { transform: rotate(360deg); } }

    /* Per-section microcopy — what's being checked, revealed only on the live row */
    .cl-body { display: flex; flex-direction: column; min-width: 0; }
    .cl-note {
      font-size: var(--text-13);
      color: var(--color-text-secondary);
      margin-top: var(--space-2);
      display: none;
    }
    .cl-item.active { align-items: flex-start; }
    .cl-item.active .cl-mark { margin-top: var(--space-2); }
    .cl-item.active .cl-note { display: block; }

    /* "Race the analyst" — our lane sprints (real progress) while the manual-review lane
       creeps at wall-clock ÷ 45 min, so the speed advantage is visible live. */
    .race { display: flex; flex-direction: column; gap: var(--space-12); margin-bottom: var(--space-16); }
    .race-lane { display: flex; flex-direction: column; gap: var(--space-4); }
    .race-head {
      display: flex; justify-content: space-between; align-items: baseline;
      font-size: var(--text-12); color: var(--color-text-secondary);
    }
    .race-name { font-weight: var(--weight-semibold); }
    .race-time { font-variant-numeric: tabular-nums; }
    .race-track {
      height: 6px;
      background: var(--color-border-sage);
      border-radius: var(--radius-full);
      overflow: hidden;
    }
    .race-fill {
      display: block; height: 100%; width: 0;
      border-radius: var(--radius-full);
      transition: width 0.6s ease;
    }
    .race-fill--us { background: var(--color-accent); }
    .race-fill--them { background: var(--color-border); }   /* neutral grey fill (was text-faint, now a non-text grey) */

    /* Progress panel — the "Files being processed" chips and the live checklist read as
       one card (this replaces the separate upload-summary card). */
    .lease-progress-card {
      background: var(--color-surface-sunken);
      border-radius: var(--radius-md);
      padding: var(--space-16);
      margin-bottom: var(--space-16);
    }
    .lease-files { margin-bottom: var(--space-16); }
    .lease-files:empty { display: none; }
    /* Reassurance during the multi-minute background extraction: it's running, it's safe to leave. */
    .lease-bg-note {
      display: flex;
      gap: var(--space-8);
      align-items: flex-start;
      margin: 0 0 var(--space-16);
      padding: var(--space-10) var(--space-12);
      background: var(--color-accent-subtle);
      border: var(--border-thin) solid var(--color-border);
      border-radius: var(--radius-md);
      font-size: var(--text-13);
      line-height: 1.5;
      color: var(--color-text-secondary);
    }
    .lease-bg-note svg {
      width: 16px;
      height: 16px;
      flex-shrink: 0;
      margin-top: var(--space-2);
      fill: none;
      stroke: var(--color-accent);
      stroke-width: 2;
      stroke-linecap: round;
      stroke-linejoin: round;
    }
    .lease-bg-note strong { color: var(--color-text-primary); font-weight: var(--weight-semibold); }
    .lease-files-label {
      margin: 0 0 var(--space-8);
      font-size: var(--text-12);
      color: var(--color-text-secondary);
    }
    .lease-files-chips { display: flex; flex-wrap: wrap; gap: var(--space-8); }
    .lease-file-chip {
      background: var(--color-accent);
      color: var(--color-surface);
      padding: var(--space-6) var(--space-12);
      border-radius: var(--radius-sm);
      font-size: var(--text-12);
      font-weight: var(--weight-semibold);
    }

    /* Rotating "Stiles Standard" tip shown while the lease is reviewed */
    .lease-tip {
      margin-top: var(--space-20);
      padding: var(--space-12) var(--space-16);
      background: var(--color-surface-sage);
      border-left: var(--border-bar) solid var(--color-accent);
      border-radius: var(--radius-lg);
      transition: opacity 0.4s ease;
    }
    .lease-tip .tip-kicker {
      display: block;
      font-size: var(--text-11);
      font-weight: var(--weight-semibold);
      text-transform: uppercase;
      letter-spacing: var(--tracking-8);
      color: var(--color-accent);
      margin-bottom: var(--space-4);
    }
    .lease-tip .tip-text { font-size: var(--text-13); color: var(--color-text-secondary); }
    /* Before the first tip is written the box is empty — don't render it as a
       bare rounded left-border (it reads as a stray "("). */
    .lease-tip:empty { display: none; }

    /* "What we just did" stat strip on the completed result */
    .work-stat {
      display: flex;
      flex-wrap: wrap;
      gap: var(--space-8) var(--space-16);
      margin-bottom: var(--space-16);
      padding: var(--space-12) var(--space-16);
      background: var(--color-surface-muted);
      border-radius: var(--radius-md);
      font-size: var(--text-13);
      color: var(--color-text-secondary);
    }
    .work-stat .ws-num { font-weight: var(--weight-semibold); color: var(--color-text-primary); }

    /* Jump-link bar above the extraction result */
    .section-jump {
      display: flex;
      flex-wrap: wrap;
      gap: var(--space-6);
      margin-bottom: var(--space-16);
    }
    .section-jump-link {
      font-size: var(--text-12);
      color: var(--color-text-secondary);
      text-decoration: none;
      padding: var(--space-2) var(--space-8);
      border: var(--border-thin) solid var(--color-border-subtle);
      border-radius: var(--radius-pill);
    }
    .section-jump-link:hover { color: var(--color-accent); border-color: var(--color-border-accent); }

    /* Confidence chip, color-coded by level for fast scanning */
    .clause-conf.conf-high { color: var(--color-success-text); }
    .clause-conf.conf-medium { color: var(--color-text-muted); }
    .clause-conf.conf-low { color: var(--color-danger); }

    /* Roster: the pro-rata-denominator annotation */
    .roster-denote { color: var(--color-text-faint); }

    /* Subtle build/version badge, pinned to the corner — informational only */
    .footer-meta {
      margin-top: var(--space-4);
      display: flex;
      gap: var(--space-8);
      align-items: center;
      justify-content: center;
      flex-wrap: wrap;
      font-size: var(--text-11);
      letter-spacing: var(--tracking-8);
      /* Readable muted (see footer note) — the 11px meta line was the worst offender
         at --color-text-faint. Inherited by the DB status + version text. */
      color: var(--color-text-secondary);
    }
    .footer-sep { color: var(--color-border); }
    .footer-db {
      display: inline-flex;
      align-items: center;
      gap: var(--space-6);
      border: none;
      background: none;
      font: inherit;
      color: inherit;
      letter-spacing: inherit;
      cursor: pointer;
      padding: 0;
    }
    .footer-db:hover #db-text { text-decoration: underline; }
    /* "What's new" trigger — a quiet text button, matching the DB status idiom. */
    .footer-link {
      border: none;
      background: none;
      font: inherit;
      color: inherit;
      letter-spacing: inherit;
      cursor: pointer;
      padding: 0;
    }
    .footer-link:hover,
    .footer-link:focus-visible { text-decoration: underline; color: var(--color-accent); }
    .db-detail {
      max-width: 40ch;   /* readable line length — content-relative, not a spacing literal */
      margin: var(--space-8) auto 0;
      font-size: var(--text-11);
      line-height: 1.5;
      color: var(--color-text-subtle);
    }

    /* "What's new" popover — the curated changelog. A quiet card centered under the
       footer meta line; left-aligned copy for readability. Read-only informational. */
    .whatsnew-panel {
      max-width: 46ch;   /* readable measure — content-relative, not a spacing literal */
      margin: var(--space-12) auto 0;
      padding: var(--space-16);
      text-align: left;
      background: var(--color-surface);
      border: var(--border-thin) solid var(--color-border);
      border-radius: var(--radius-lg);
      box-shadow: var(--shadow-md);
    }
    .whatsnew-head {
      font-size: var(--text-13);
      font-weight: 600;
      color: var(--color-text-primary);
      margin-bottom: var(--space-12);
    }
    .whatsnew-rel + .whatsnew-rel {
      margin-top: var(--space-16);
      padding-top: var(--space-16);
      border-top: var(--border-hairline) solid var(--color-border);
    }
    .whatsnew-meta {
      font-size: var(--text-11);
      letter-spacing: var(--tracking-8);
      text-transform: uppercase;
      color: var(--color-text-faint);
    }
    .whatsnew-title {
      font-size: var(--text-13);
      font-weight: 600;
      color: var(--color-text-primary);
      margin-top: var(--space-4);
    }
    .whatsnew-list {
      margin: var(--space-8) 0 0;
      padding-left: var(--space-16);
      font-size: var(--text-12);
      line-height: 1.55;
      color: var(--color-text-secondary);
      text-align: left;
    }
    .whatsnew-list li + li { margin-top: var(--space-6); }
    .db-dot {
      width: var(--space-8);
      height: var(--space-8);
      border-radius: var(--radius-pill);
      background: var(--color-na);   /* unknown / not configured — a light neutral dot, not text-faint */
    }
    .db-dot.ok { background: var(--color-success); }   /* connected = green, not the crimson accent */
    .db-dot.warn { background: var(--color-warning); }
    .db-dot.down { background: var(--color-danger); }

    /* ── Sectioned results ── */
    .section-card {
      background: var(--color-surface);
      border-radius: var(--radius-xl);
      box-shadow: var(--shadow-sm);
      margin-bottom: var(--space-14);
      overflow: hidden;
    }
    .section-summary {
      list-style: none;
      cursor: pointer;
      padding: var(--space-16) var(--space-20);
      font-weight: var(--weight-bold);
      font-size: var(--text-16);
      color: var(--color-text-heading);
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: var(--space-12);
    }
    .section-summary::-webkit-details-marker { display: none; }
    .section-summary::after { content: '▸'; color: var(--color-text-faint); transition: transform 0.15s; }
    details[open] > .section-summary::after { transform: rotate(90deg); }
    .section-flag {
      font-size: var(--text-11);
      font-weight: var(--weight-semibold);
      color: var(--color-danger);
      background: var(--color-surface-danger);
      border-radius: var(--radius-pill);
      padding: var(--space-2) var(--space-8);
      margin-left: auto;
    }
    .section-body { padding: 0 var(--space-20) var(--space-10); }
    .item-row { padding: var(--space-12) 0; border-top: var(--border-hairline) solid var(--color-surface-muted); }
    .item-row:first-child { border-top: none; }
    .item-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-10); }
    .item-label { font-weight: var(--weight-semibold); font-size: var(--text-14); color: var(--color-text-secondary); }
    .item-value { font-size: var(--text-14); color: var(--color-text-primary); margin-top: var(--space-4); line-height: 1.5; }
    .item-row.absent .item-value { color: var(--color-text-faint); }

    /* ── Compare ── */
    .cmp-checks { display: flex; flex-direction: column; gap: var(--space-8); }
    .cmp-check {
      display: flex; align-items: center; gap: var(--space-10);
      font-size: var(--text-14); color: var(--color-text-primary); cursor: pointer;
    }
    .cmp-check input { width: 16px; height: 16px; accent-color: var(--color-accent); }
    .cmp-check .cmp-sub { color: var(--color-text-faint); font-size: var(--text-13); }

    .cmp-wrap { overflow-x: auto; border-radius: var(--radius-xl); box-shadow: var(--shadow-sm); }
    .cmp-table { border-collapse: collapse; width: 100%; font-size: var(--text-13); background: var(--color-surface); }
    .cmp-table th, .cmp-table td {
      border: var(--border-hairline) solid var(--color-border-subtle); padding: var(--space-8) var(--space-12);
      text-align: left; vertical-align: top;
    }
    .cmp-table thead th { background: var(--color-surface-dark); color: var(--color-surface); font-weight: var(--weight-semibold); text-transform: uppercase; letter-spacing: var(--tracking-5); font-size: var(--text-11); }
    .cmp-table th.cmp-rowhead, .cmp-table td.cmp-rowhead {
      position: sticky; left: 0; background: var(--color-surface-sunken);
      font-weight: var(--weight-semibold); color: var(--color-text-secondary); min-width: 118px; font-size: var(--text-12);
      cursor: pointer; z-index: 1;
    }
    .cmp-section td {
      background: var(--color-accent-subtle); color: var(--color-accent); font-weight: var(--weight-bold);
      text-transform: uppercase; font-size: var(--text-11); letter-spacing: var(--tracking-6);
      cursor: pointer;
    }
    .cmp-caret { display: inline-block; width: 0.9em; transition: transform 0.15s; }
    .cmp-section.collapsed .cmp-caret { transform: rotate(-90deg); }
    .cmp-diff td { background: var(--color-accent-bg); }
    .cmp-diff td.cmp-rowhead { background: var(--color-accent-bg-strong); }
    .cmp-val { display: block; max-width: 280px; line-height: 1.4; }
    .cmp-na { color: var(--color-na); }
    .cmp-legend { font-size: var(--text-12); color: var(--color-text-faint); margin-bottom: var(--space-12); }
    .cmp-legend .swatch { display: inline-block; width: 11px; height: 11px; background: var(--color-accent-bg-strong); border: var(--border-hairline) solid var(--color-accent); border-radius: var(--radius-xs); vertical-align: middle; margin-right: var(--space-4); }
    .cmp-sec-icon { width: 14px; height: 14px; vertical-align: -2px; margin-right: var(--space-8); }
    .cmp-info { color: var(--color-text-faint); font-size: var(--text-11); margin-left: var(--space-4); }
    .cmp-def { margin-top: var(--space-4); font-size: var(--text-12); line-height: 1.4; color: var(--color-text-muted); font-weight: var(--weight-regular); white-space: normal; }

    /* ── Risk score + consistency rollup ── */
    .risk-banner {
      display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-8) var(--space-12);
      border-radius: var(--radius-lg); padding: var(--space-12) var(--space-16); margin-bottom: var(--space-20); font-size: var(--text-14);
    }
    .risk-banner .risk-label { font-weight: var(--weight-semibold); text-transform: uppercase; letter-spacing: var(--tracking-6); font-size: var(--text-11); opacity: 0.85; }
    .risk-banner .risk-level { font-weight: var(--weight-bold); }
    .risk-banner .risk-detail { opacity: 0.9; }
    .risk-low  { background: var(--color-surface-success); color: var(--color-success-text); }
    .risk-med  { background: var(--color-surface-warning); color: var(--color-warning-text); }
    .risk-high { background: var(--color-surface-danger); color: var(--color-danger); }

    /* ── Gross-up advisory (read-only caution; never applies the gross-up) ── */
    .gu-advisory {
      border-radius: var(--radius-lg); padding: var(--space-14) var(--space-16); margin-bottom: var(--space-20);
      font-size: var(--text-14); border: var(--border-hairline) solid transparent;
    }
    .gu-advisory .gu-head { font-weight: var(--weight-bold); display: flex; align-items: center; gap: var(--space-6); margin-bottom: var(--space-8); }
    .gu-advisory .gu-icon { font-size: var(--text-12); }
    .gu-advisory .gu-lines { margin: 0 0 var(--space-8); padding-left: var(--space-20); }
    .gu-advisory .gu-lines li { margin-bottom: var(--space-4); }
    .gu-advisory .gu-note { opacity: 0.95; line-height: 1.45; }
    .gu-warn { background: var(--color-surface-warning); color: var(--color-warning-text); border-color: var(--color-border-warning); }
    .gu-high { background: var(--color-surface-danger); color: var(--color-danger); border-color: var(--color-border-danger); }

    .rollup-card {
      background: var(--color-surface); border-radius: var(--radius-xl); box-shadow: var(--shadow-sm);
      padding: var(--space-16) var(--space-20) var(--space-16); margin-bottom: var(--space-20);
    }
    .rollup-head { font-weight: var(--weight-bold); color: var(--color-text-heading); margin: var(--space-4) 0 var(--space-12); font-size: var(--text-16); }
    .rollup-list { list-style: none; margin: 0; padding: 0; }
    .rollup-list li { display: flex; align-items: center; gap: var(--space-8); font-size: var(--text-14); padding: var(--space-4) 0; color: var(--color-text-secondary); }
    .rollup-dot { width: 9px; height: 9px; border-radius: var(--radius-circle); flex-shrink: 0; }
    .rollup-dot.ok { background: var(--color-success); }
    .rollup-dot.warn { background: var(--color-warning); }
    .rollup-dot.bad { background: var(--color-danger); }
    .rollup-tag { margin-left: auto; font-size: var(--text-12); font-weight: var(--weight-semibold); white-space: nowrap; }
    .rollup-tag.ok { color: var(--color-success-text); }
    .rollup-tag.warn { color: var(--color-warning-text); }
    .rollup-tag.bad { color: var(--color-danger); }

    .cmp-risk { display: inline-block; margin-top: var(--space-4); font-size: var(--text-11); font-weight: var(--weight-bold); padding: var(--space-2) var(--space-8); border-radius: var(--radius-pill); }
    .cmp-risk.low  { background: var(--color-surface-success-strong); color: var(--color-success-text); }
    .cmp-risk.med  { background: var(--color-surface-warning-strong); color: var(--color-warning-text-strong); }
    .cmp-risk.high { background: var(--color-border-danger); color: var(--color-danger-strong); }

    /* ── Snapshot (visual metrics) ── */
    .snapshot {
      background: var(--color-surface); border-radius: var(--radius-xl); box-shadow: var(--shadow-sm);
      padding: var(--space-16) var(--space-20) var(--space-20); margin-bottom: var(--space-20);
    }
    .stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(92px, 1fr)); gap: var(--space-10); margin-top: var(--space-8); }
    .stat { background: var(--color-surface-sage); border-radius: var(--radius-lg); padding: var(--space-12) var(--space-8); text-align: center; }
    .stat-num { font-size: var(--text-20); font-weight: var(--weight-bold); color: var(--color-text-heading); line-height: 1.1; }
    .stat-lbl { font-size: var(--text-11); color: var(--color-text-muted); margin-top: var(--space-4); }
    .stat.risk-low-tile .stat-num { color: var(--color-success-text); }
    .stat.risk-med-tile .stat-num { color: var(--color-warning-text); }
    .stat.risk-high-tile .stat-num { color: var(--color-danger); }

    .viz-row { display: flex; flex-wrap: wrap; gap: var(--space-20); align-items: center; margin-top: var(--space-16); }
    .donut-wrap { text-align: center; }
    .donut { width: 96px; height: 96px; }
    .donut-text { font-size: var(--text-7); font-weight: var(--weight-bold); fill: var(--color-text-heading); }
    .donut-track { stroke: var(--color-border-sage); }
    .donut-arc { stroke: var(--color-accent); }
    .viz-cap { font-size: var(--text-12); color: var(--color-text-muted); margin-top: var(--space-4); max-width: 120px; }
    .bars { flex: 1; min-width: 190px; }
    .bar-row { margin-bottom: var(--space-10); }
    .bar-row:last-child { margin-bottom: 0; }
    .bar-top { display: flex; justify-content: space-between; font-size: var(--text-12); color: var(--color-text-secondary); margin-bottom: var(--space-4); }
    .bar-val { color: var(--color-text-faint); }
    .bar { height: 8px; background: var(--color-border-sage); border-radius: var(--radius-full); overflow: hidden; }
    .bar-fill { height: 100%; background: var(--color-accent); border-radius: var(--radius-full); }

    .summary-card {
      background: var(--color-accent-subtle);
      border-radius: var(--radius-xl);
      padding: var(--space-16) var(--space-20);
      margin-bottom: var(--space-20);
      font-size: var(--text-15);
      line-height: 1.5;
      color: var(--color-accent-hover);
    }
    .summary-card .summary-label {
      font-size: var(--text-12);
      font-weight: var(--weight-semibold);
      text-transform: uppercase;
      letter-spacing: var(--tracking-8);
      color: var(--color-accent);
      margin-bottom: var(--space-6);
    }

    .result-header {
      font-size: var(--text-12);
      color: var(--color-text-faint);
      margin-bottom: var(--space-12);
    }

    .takeaways-card {
      background: var(--color-surface);
      border-left: var(--border-bar) solid var(--color-accent);
      border-radius: var(--radius-lg);
      box-shadow: var(--shadow-sm);
      padding: var(--space-16) var(--space-20) var(--space-16);
      margin-bottom: var(--space-20);
    }
    .takeaways-card .summary-label {
      font-size: var(--text-12);
      font-weight: var(--weight-semibold);
      text-transform: uppercase;
      letter-spacing: var(--tracking-8);
      color: var(--color-accent);
      margin-bottom: var(--space-8);
    }
    .takeaways-card ul { margin: 0; padding-left: var(--space-20); }
    .takeaways-card li {
      font-size: var(--text-15);
      line-height: 1.55;
      color: var(--color-text-heading);
      margin-bottom: var(--space-8);
    }
    .takeaways-card li:last-child { margin-bottom: 0; }

    /* Key dates */
    .key-group { margin-bottom: var(--space-24); }
    .key-group-head {
      font-size: var(--text-12);
      font-weight: var(--weight-bold);
      text-transform: uppercase;
      letter-spacing: var(--tracking-8);
      color: var(--color-accent);
      margin: 0 0 var(--space-10);
    }
    .key-card {
      background: var(--color-surface);
      border-radius: var(--radius-xl);
      box-shadow: var(--shadow-sm);
      padding: var(--space-16) var(--space-20) var(--space-16);
      margin-bottom: var(--space-14);
      border-left: var(--border-bar) solid var(--color-accent);
    }
    .key-card-head {
      display: flex;
      align-items: baseline;
      justify-content: space-between;
      gap: var(--space-10);
      margin-bottom: var(--space-12);
    }
    .key-card-title { font-weight: var(--weight-bold); color: var(--color-text-heading); font-size: var(--text-16); }
    .key-card-sub { font-size: var(--text-12); color: var(--color-text-subtle); font-weight: var(--weight-regular); }
    .key-rows {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));
      gap: var(--space-12) var(--space-16);
    }
    .key-row { display: flex; flex-direction: column; gap: var(--space-4); }
    .key-label {
      font-size: var(--text-11);
      text-transform: uppercase;
      letter-spacing: var(--tracking-5);
      color: var(--color-text-subtle);
      font-weight: var(--weight-semibold);
    }
    .key-value { font-size: var(--text-15); color: var(--color-text-heading); font-weight: var(--weight-semibold); }
    .key-value .key-na { color: var(--color-text-faint); font-weight: var(--weight-regular); }
    .key-pill {
      display: inline-block;
      margin-top: var(--space-2);
      align-self: flex-start;
      font-size: var(--text-11);
      font-weight: var(--weight-bold);
      padding: var(--space-2) var(--space-8);
      border-radius: var(--radius-full);
      letter-spacing: var(--tracking-2);
    }
    .key-pill.ok   { background: var(--color-surface-success); color: var(--color-success-text); }
    .key-pill.soon { background: var(--color-surface-warning-strong); color: var(--color-warning-text); }
    .key-pill.past { background: var(--color-surface-danger); color: var(--color-danger); }
    .key-pill.none { background: var(--color-surface-muted); color: var(--color-text-subtle); }

    /* Reconcile flow */
    .step-num {
      display: inline-flex; align-items: center; justify-content: center;
      width: 1.5rem; height: 1.5rem; margin-right: var(--space-8);
      background: var(--color-accent); color: var(--color-surface); border-radius: var(--radius-full);
      font-size: var(--text-13); font-weight: var(--weight-bold);
    }
    .reco-grid3 { display: grid; grid-template-columns: 2fr 1fr 1fr; gap: var(--space-12) var(--space-16); }
    @media (max-width: 640px) { .reco-grid3 { grid-template-columns: 1fr; } }
    /* Two-up field pairs — the mid-year sale's owner block (seller | buyer, name then
       remit-to), so each owner reads as a column rather than a flat list of four inputs. */
    .reco-grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-12) var(--space-16); }
    @media (max-width: 640px) { .reco-grid2 { grid-template-columns: 1fr; } }
    .reco-check { display: flex; align-items: center; gap: var(--space-8); margin: var(--space-14) 0 var(--space-6); font-size: var(--text-15); color: var(--color-text-heading); }
    .reco-check input { width: auto; }
    .reco-rows-head { font-size: var(--text-11); font-weight: var(--weight-bold); text-transform: uppercase; letter-spacing: var(--tracking-5); color: var(--color-text-subtle); padding: 0 var(--space-4) var(--space-6); }
    .reco-tenant-grid { display: grid; grid-template-columns: 0.8fr 1.6fr 1fr 1fr 2rem; gap: var(--space-8); }
    .reco-tenant-grid--sold { grid-template-columns: 0.8fr 1.6fr 1fr 1fr 1fr 2rem; }
    .reco-exp-grid { display: grid; grid-template-columns: 2.2fr 1fr 1fr 2rem; gap: var(--space-8); }
    .reco-exp-grid.sold { grid-template-columns: 2fr 1fr 1fr 1.3fr 2rem; }
    /* The Step-3 chart-of-accounts header takes the GL chart's dark bar so the
       merged (now editable) table keeps that read-as-a-ledger look. */
    #reco-exp-head {
      background: var(--color-surface-dark); color: var(--color-surface);
      padding: var(--space-8); border-radius: var(--radius-sm); margin-bottom: var(--space-8);
    }
    .gl-fill { margin-bottom: var(--space-20); padding-bottom: var(--space-16); border-bottom: var(--border-hairline) solid var(--color-border); }
    .gl-fill-grid { grid-template-columns: 1fr; max-width: 460px; }
    .gl-fill .lease-actions { justify-content: flex-start; margin-top: var(--space-12); }
    .reco-period-cell { display: flex; flex-direction: column; gap: var(--space-4); min-width: 0; }
    .annual-flag { align-self: flex-start; font-size: var(--text-11); font-weight: var(--weight-semibold); padding: var(--space-2) var(--space-6); border-radius: var(--radius-full); background: var(--color-surface-warning-strong); color: var(--color-warning-text); border: var(--border-hairline) solid var(--color-border-warning); cursor: help; }
    .period-reco { margin-bottom: var(--space-24); }
    .period-reco .statement-head { margin-bottom: var(--space-8); }
    .reco-row { margin-bottom: var(--space-8); align-items: center; }
    .reco-row input, .reco-row select { width: 100%; box-sizing: border-box; padding: var(--space-8) var(--space-10); border: var(--border-hairline) solid var(--color-border); border-radius: var(--radius-md); font-size: var(--text-14); font-family: inherit; }
    .reco-row .reco-del { background: none; border: none; color: var(--color-danger); font-size: var(--text-18); cursor: pointer; line-height: 1; padding: var(--space-4); }
    @media (max-width: 760px) {
      .reco-rows-head { display: none; }
      .reco-tenant-grid, .reco-exp-grid, .reco-exp-grid.sold { grid-template-columns: 1fr 1fr; gap: var(--space-6); }
      .reco-row .reco-del { grid-column: 1 / -1; justify-self: end; }
    }
    /* Per-account GL adjustment — an audit-trailed signed override + reason under each row. */
    .reco-exp-item { margin-bottom: var(--space-10); }
    .reco-exp-item > .reco-row { margin-bottom: var(--space-4); }
    .reco-adjust-line { padding: 0 var(--space-4); }
    .reco-adjust-toggle { background: none; border: none; color: var(--color-accent); font-size: var(--text-12); font-weight: var(--weight-semibold); cursor: pointer; padding: var(--space-2) 0; }
    .reco-adjust-toggle:hover { text-decoration: underline; }
    .reco-adjust-open { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-8); padding: var(--space-8) var(--space-10); background: var(--color-surface-muted); border: var(--border-hairline) solid var(--color-border); border-radius: var(--radius-md); }
    .reco-adjust-lbl { font-size: var(--text-11); font-weight: var(--weight-bold); text-transform: uppercase; letter-spacing: var(--tracking-5); color: var(--color-text-subtle); }
    .reco-adjust-amt { width: 8rem; box-sizing: border-box; padding: var(--space-6) var(--space-8); border: var(--border-hairline) solid var(--color-border); border-radius: var(--radius-md); font-size: var(--text-13); font-family: inherit; }
    .reco-adjust-reason { flex: 1 1 12rem; min-width: 0; box-sizing: border-box; padding: var(--space-6) var(--space-8); border: var(--border-hairline) solid var(--color-border); border-radius: var(--radius-md); font-size: var(--text-13); font-family: inherit; }
    .reco-adjust-eff { font-size: var(--text-12); color: var(--color-text-muted); font-variant-numeric: tabular-nums; }
    .reco-adjust-eff.on { color: var(--color-success-text); font-weight: var(--weight-semibold); }
    .reco-adjust-clear { background: none; border: none; color: var(--color-danger); font-size: var(--text-12); cursor: pointer; padding: var(--space-4); }
    .reco-adjust-clear:hover { text-decoration: underline; }
    .reco-occupancy { font-size: var(--text-14); color: var(--color-text-body); font-weight: var(--weight-semibold); margin-left: auto; align-self: center; }
    .reco-occupancy.warn { color: var(--color-warning-text); }

    /* Roster health guard (window.RosterHealth) — surfaced by updateOccupancy() when a
       suite has two active tenants or the roster SF exceeds the locked denominator.
       See docs/incident-fishermens-roster.md. */
    .roster-health { margin: var(--space-8) 0 0; padding: var(--space-8) var(--space-12); border-radius: var(--radius-md); background: var(--color-surface-warning); border: var(--border-hairline) solid var(--color-warning-accent); color: var(--color-warning-text); font-size: var(--text-13); line-height: 1.4; }
    .roster-health strong { font-weight: var(--weight-semibold); }
    /* Informational variant: "what changed since the last rent roll" is a FINDING, not a
       warning — the roster-health banner beside it means something is wrong, and this must
       not borrow that alarm. Same shape, neutral surface. */
    /* An undated rent roll, on the one surface that can fix it. Muted, not alarming — a gap to
       fill, not a fault. See acctLogRow. */
    .log-undated { color: var(--color-text-secondary); font-style: italic; }
    .acct-asof-form { display: inline-flex; align-items: center; gap: var(--space-4); flex-wrap: wrap; }
    .acct-asof-label { color: var(--color-text-secondary); }

    .roster-health--info { background: var(--color-surface-info); border-color: var(--color-border-subtle); color: var(--color-text-secondary); }
    .roster-health ul { margin: var(--space-4) 0 0; padding-left: var(--space-16); }

    .reco-table { width: 100%; border-collapse: collapse; margin-top: var(--space-8); font-size: var(--text-14); }
    .reco-table th, .reco-table td { padding: var(--space-8) var(--space-10); text-align: right; border-bottom: var(--border-hairline) solid var(--color-surface-muted); }
    .reco-table th:first-child, .reco-table td:first-child { text-align: left; }
    .reco-table th { font-size: var(--text-11); text-transform: uppercase; letter-spacing: var(--tracking-4); color: var(--color-text-subtle); }
    .reco-table tfoot td { font-weight: var(--weight-bold); border-top: var(--border-thick) solid var(--color-border); border-bottom: none; }
    .reco-gl-table th:nth-child(2), .reco-gl-table td:nth-child(2) { text-align: left; }
    .reco-gl-table thead th { background: var(--color-surface-dark); color: var(--color-surface); text-transform: uppercase; letter-spacing: var(--tracking-5); }
    .reco-gl-table .reco-gl-acct { color: var(--color-text-subtle); font-variant-numeric: tabular-nums; }
    .reco-owes { color: var(--color-danger); font-weight: var(--weight-semibold); }
    .reco-credit { color: var(--color-credit); font-weight: var(--weight-semibold); }
    /* Draft / Reviewed / Approved segmented control — the active segment is
       solid Crimson (a manager click sets it; nothing is final until Approved). */
    .reco-status { display: inline-flex; gap: 0; }
    .reco-status button { font-size: var(--text-11); font-weight: var(--weight-semibold); text-transform: uppercase; letter-spacing: var(--tracking-5); padding: var(--space-4) var(--space-10); border-radius: var(--radius-full); border: var(--border-hairline) solid var(--color-border); border-left-width: 0; background: var(--color-surface); color: var(--color-text-muted); cursor: pointer; }
    .reco-status button:first-child { border-left-width: var(--border-hairline); }
    .reco-status button.on-draft,
    .reco-status button.on-reviewed,
    .reco-status button.on-approved { background: var(--color-accent); color: var(--color-text-on-accent); border-color: var(--color-accent); }
    .reco-note-input { width: 100%; box-sizing: border-box; margin-top: var(--space-4); padding: var(--space-6) var(--space-8); border: var(--border-hairline) solid var(--color-border-subtle); border-radius: var(--radius-sm); font-size: var(--text-13); font-family: inherit; }
    .reco-actions-row { display: flex; gap: var(--space-10); flex-wrap: wrap; margin-top: var(--space-16); }
    .btn-secondary { background: transparent; color: var(--color-slate-text); border: var(--border-thin) solid var(--color-slate-border); border-radius: var(--radius-md); padding: var(--space-12) var(--space-24); font-family: var(--font-sans); font-weight: var(--weight-semibold); text-transform: uppercase; letter-spacing: var(--tracking-8); font-size: var(--text-13); cursor: pointer; transition: background 0.15s, color 0.15s; }
    .btn-secondary:hover { background: var(--color-surface-dark); color: var(--color-text-on-accent); }

    .statement {
      background: var(--color-surface); border-radius: var(--radius-xl); box-shadow: var(--shadow-statement);
      padding: var(--space-32) var(--space-32); margin-top: var(--space-24); border-top: var(--border-accent) solid var(--color-surface-dark);
    }
    .statement-head { display: flex; justify-content: space-between; align-items: flex-start; gap: var(--space-16); flex-wrap: wrap; margin-bottom: var(--space-16); }
    .statement-head h3 { margin: 0; color: var(--color-text-heading); }
    .statement-sub { font-size: var(--text-14); color: var(--color-text-subtle); }
    .statement-math { background: var(--color-surface-sage); border-radius: var(--radius-md); padding: var(--space-12) var(--space-16); font-size: var(--text-14); color: var(--color-text-heading); margin-bottom: var(--space-16); }
    .statement-math .formula { font-family: var(--font-mono); color: var(--color-accent); }
    .statement-balance { font-size: var(--text-16); font-weight: var(--weight-bold); margin-top: var(--space-16); padding-top: var(--space-14); border-top: var(--border-thick) solid var(--color-border-sage); display: flex; justify-content: space-between; }
    .owner-split { background: var(--color-surface); border-radius: var(--radius-xl); box-shadow: var(--shadow-statement); padding: var(--space-32) var(--space-32); margin-top: var(--space-24); border-top: var(--border-accent) solid var(--color-surface-dark); }
    .owner-periods { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-16); margin-bottom: var(--space-16); }
    @media (max-width: 560px) { .owner-periods { grid-template-columns: 1fr; } }
    .owner-period { background: var(--color-surface-sage); border-radius: var(--radius-md); padding: var(--space-12) var(--space-16); }
    .owner-period .op-role { font-size: var(--text-12); text-transform: uppercase; letter-spacing: var(--tracking-5); color: var(--color-text-subtle); font-weight: var(--weight-bold); }
    .owner-period .op-window { font-size: var(--text-14); color: var(--color-text-heading); margin: var(--space-4) 0; }
    .owner-period .op-amount { font-size: var(--text-20); font-weight: var(--weight-bold); color: var(--color-accent); }
    .method-pill { font-size: var(--text-11); font-weight: var(--weight-bold); padding: var(--space-1) var(--space-6); border-radius: var(--radius-full); background: var(--color-surface-muted); color: var(--color-text-body); }

    .clause-card {
      background: var(--color-surface);
      border-radius: var(--radius-xl);
      box-shadow: var(--shadow-sm);
      padding: var(--space-16) var(--space-20);
      margin-bottom: var(--space-14);
    }
    .clause-card.absent { opacity: 0.7; }

    .clause-head {
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: var(--space-12);
      margin-bottom: var(--space-8);
    }
    .clause-title { font-weight: var(--weight-semibold); font-size: var(--text-15); color: var(--color-text-heading); }

    .clause-badge {
      font-size: var(--text-11);
      font-weight: var(--weight-semibold);
      padding: var(--space-4) var(--space-8);
      border-radius: var(--radius-pill);
      white-space: nowrap;
      flex-shrink: 0;
    }
    .clause-badge.found { background: var(--color-accent-subtle); color: var(--color-accent); }
    .clause-badge.missing { background: var(--color-surface-muted); color: var(--color-text-faint); }
    .clause-badge.error { background: var(--color-surface-danger); color: var(--color-danger); }
    .clause-badge.proc { background: var(--color-surface-info); color: var(--color-text-heading); }

    .clause-summary { font-size: var(--text-14); color: var(--color-text-secondary); line-height: 1.5; }

    .clause-quote {
      border-left: var(--border-accent) solid var(--color-accent);
      padding: var(--space-8) var(--space-14);
      margin-top: var(--space-12);
      background: var(--color-surface-sage);
      border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
      font-size: var(--text-14);
      color: var(--color-text-secondary);
      line-height: 1.55;
      font-style: italic;
    }
    .clause-loc {
      margin-top: var(--space-8);
      font-size: var(--text-12);
      color: var(--color-text-faint);
    }
    .clause-conf { font-size: var(--text-12); color: var(--color-text-faint); margin-left: var(--space-6); white-space: nowrap; }

    /* ── Recent leases log ── */
    .log-section { margin-top: var(--space-36); }
    .log-section h2 {
      font-size: var(--text-13);
      font-weight: var(--weight-semibold);
      text-transform: uppercase;
      letter-spacing: var(--tracking-8);
      color: var(--color-text-faint);
      margin-bottom: var(--space-14);
    }
    /* Collapsible reuse picker — a clickable dropdown so the history stays tucked away. */
    details.log-section--collapsible > summary {
      cursor: pointer;
      list-style: none;
      display: flex;
      align-items: baseline;
      gap: var(--space-8);
    }
    details.log-section--collapsible > summary::-webkit-details-marker { display: none; }
    /* Accent colour (not the faint default) so the clickable "reuse a recent one"
       picker reads as an action next to the upload slot instead of disappearing. */
    details.log-section--collapsible > summary h2,
    details.log-section--collapsible > summary .log-count { color: var(--color-accent); }
    details.log-section--collapsible > summary:hover h2,
    details.log-section--collapsible > summary:hover .log-count { color: var(--color-accent-hover); }
    details.log-section--collapsible > summary h2 { margin-bottom: 0; }
    details.log-section--collapsible[open] > summary h2 { margin-bottom: var(--space-14); }
    .log-count {
      font-size: var(--text-13);
      font-weight: var(--weight-semibold);
      color: var(--color-text-muted);
    }
    .log-count::after { content: ' \25b8'; }                                  /* ▸ collapsed */
    details.log-section--collapsible[open] .log-count::after { content: ' \25be'; }   /* ▾ open */

    .log-item {
      background: var(--color-surface);
      border-radius: var(--radius-xl);
      box-shadow: var(--shadow-sm);
      padding: var(--space-14) var(--space-16);
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: var(--space-16);
      margin-bottom: var(--space-12);
    }
    .log-name { font-weight: var(--weight-semibold); font-size: var(--text-14); color: var(--color-text-primary); word-break: break-word; display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-8); }

    /* Loading state for the rent-roll history while it's read from storage. */
    .log-loading { padding: var(--space-16) var(--space-4); font-size: var(--text-14); color: var(--color-text-muted); }
    .log-loading .progress { margin-bottom: var(--space-10); max-width: 320px; }

    /* The roll currently loaded reads as a clearly-active card — crimson left bar,
       tinted fill, crimson outline — not just a small pill. */
    .log-item--active {
      background: var(--color-accent-bg);
      box-shadow: var(--shadow-hover);
      border-left: var(--border-bar) solid var(--color-accent);
    }
    /* Row mid-delete: dimmed + non-interactive while the delete round-trips. */
    .log-item--deleting { opacity: 0.55; pointer-events: none; transition: opacity 0.15s; }
    .log-inuse {
      display: inline-flex;
      align-items: center;
      font-size: var(--text-11);
      font-weight: var(--weight-semibold);
      text-transform: uppercase;
      letter-spacing: var(--tracking-6);
      color: var(--color-text-on-accent);
      background: var(--color-accent);
      padding: var(--space-2) var(--space-8);
      white-space: nowrap;
    }
    .log-sub { font-size: var(--text-12); color: var(--color-text-faint); margin-top: var(--space-2); }
    .log-right { display: flex; align-items: center; gap: var(--space-8); flex-shrink: 0; flex-wrap: wrap; justify-content: flex-end; }
    /* Inline rename field for a saved rent roll (edit its building/property name). */
    .acct-rename-form { display: flex; align-items: center; gap: var(--space-8); flex-wrap: wrap; }
    .acct-rename-input { min-width: 180px; max-width: 100%; }

    .log-btn {
      background: var(--color-surface);
      border: var(--border-thin) solid var(--color-border);
      color: var(--color-text-secondary);
      border-radius: var(--radius-sm);
      font: inherit;
      font-size: var(--text-12);
      font-weight: var(--weight-semibold);
      padding: var(--space-6) var(--space-12);
      cursor: pointer;
      transition: border-color 0.15s, color 0.15s;
    }
    .log-btn:hover { border-color: var(--color-accent); color: var(--color-accent); }
    .log-btn.danger:hover { border-color: var(--color-danger); color: var(--color-danger); }

    /* Repeated uploads of the same rent roll collapse under the latest one; the
       older ones live behind this expander to keep the list clean. */
    .log-group { margin: calc(-1 * var(--space-6)) 0 var(--space-16); }
    .log-more {
      list-style: none;
      cursor: pointer;
      display: inline-flex;
      align-items: center;
      gap: var(--space-6);
      font-size: var(--text-12);
      font-weight: var(--weight-semibold);
      text-transform: uppercase;
      letter-spacing: var(--tracking-6);
      color: var(--color-accent);
      padding: var(--space-8) 0 var(--space-4);
    }
    .log-more::-webkit-details-marker { display: none; }
    .log-more::before {
      content: '▸';
      display: inline-block;
      color: var(--color-text-faint);
      transition: transform 0.15s;
    }
    details[open] > .log-more::before { transform: rotate(90deg); }
    .log-group-items {
      display: flex;
      flex-direction: column;
      gap: var(--space-10);
      margin: var(--space-10) 0 0 var(--space-16);
      padding-left: var(--space-16);
      border-left: var(--border-thin) solid var(--color-border);
    }

    /* Filter box above the Recent-leases list. */
    .log-toolbar { margin-bottom: var(--space-14); display: flex; gap: var(--space-8); align-items: center; flex-wrap: wrap; }
    .log-search { width: 100%; max-width: 360px; flex: 1 1 220px; }
    /* "Needs attention" toggle: a log-btn whose active state inverts to the accent. */
    .log-attention-btn.is-active,
    .log-attention-btn.is-active:hover {
      background: var(--color-accent);
      border-color: var(--color-accent);
      color: var(--color-text-on-accent);
    }
    /* Inline nudge in the collapsed summary when leases need attention. */
    .log-attn-nudge { color: var(--color-danger); font-weight: var(--weight-semibold); }
    /* Header of the "Needs attention" view — what the count is made of, whether the
       failures share one cause, and how the count clears. */
    .log-attn-summary {
      background: var(--color-accent-bg);
      border-left: var(--border-thick) solid var(--color-danger);
      border-radius: var(--radius-md);
      padding: var(--space-14) var(--space-16);
      margin-bottom: var(--space-16);
    }
    .log-attn-head {
      font-size: var(--text-14);
      font-weight: var(--weight-semibold);
      color: var(--color-text-heading);
    }
    .log-attn-cause,
    .log-attn-how { font-size: var(--text-13); color: var(--color-text-secondary); margin-top: var(--space-8); }
    /* Why a failed lease failed, on its row. It carries the identity a failed bundle
       otherwise lacks — the tenant name lives in the extraction that never finished. */
    .log-err {
      font-size: var(--text-12);
      color: var(--color-danger);
      margin-top: var(--space-4);
      max-width: 46rem;
    }
    .log-err--loading { color: var(--color-text-faint); font-style: italic; }
    .log-err-detail { color: var(--color-text-faint); }
    .log-empty,
    .log-hint { padding: var(--space-12) var(--space-4); }

    /* Recent leases grouped by property: a collapsible section per building. */
    .log-property { margin-bottom: var(--space-14); }
    .log-property-head {
      list-style: none;
      cursor: pointer;
      display: flex;
      align-items: center;
      gap: var(--space-8);
      padding: var(--space-8) 0;
    }
    .log-property-head::-webkit-details-marker { display: none; }
    .log-property-head::before {
      content: '▸';
      display: inline-block;
      color: var(--color-text-faint);
      transition: transform 0.15s;
    }
    details.log-property[open] > .log-property-head::before { transform: rotate(90deg); }
    .log-property-name {
      font-size: var(--text-13);
      font-weight: var(--weight-semibold);
      text-transform: uppercase;
      letter-spacing: var(--tracking-6);
      color: var(--color-text-secondary);
    }
    .log-property-count {
      font-size: var(--text-11);
      font-weight: var(--weight-semibold);
      color: var(--color-text-faint);
      background: var(--color-surface-sunken);
      border: var(--border-thin) solid var(--color-border-subtle);
      border-radius: var(--radius-pill);
      padding: var(--space-2) var(--space-8);
    }
    /* AI-spend readout on the Recent-leases log — per-lease tag, per-project subtotal, and
       the running total in the summary line. All estimates (see SPEND_TITLE); kept muted so
       they inform without competing with the status badges. */
    .cost-tag {
      font-size: var(--text-11);
      font-weight: var(--weight-semibold);
      color: var(--color-text-faint);
      background: var(--color-surface-muted);
      border-radius: var(--radius-pill);
      padding: var(--space-1) var(--space-6);
      margin-left: var(--space-6);
      white-space: nowrap;
    }
    .log-property-spend {
      font-size: var(--text-11);
      font-weight: var(--weight-semibold);
      color: var(--color-text-faint);
      background: var(--color-surface-muted);
      border-radius: var(--radius-pill);
      padding: var(--space-2) var(--space-8);
      margin-left: var(--space-6);
    }
    .log-total-spend { color: var(--color-text-faint); font-weight: var(--weight-medium); }
    /* Admin · Estimated AI spend roll-up card. */
    .admin-spend-total { font-size: var(--text-24); font-weight: var(--weight-bold); color: var(--color-text-primary); }
    .admin-spend-sub { margin-top: var(--space-4); }
    .admin-spend-note { margin-top: var(--space-8); }
    /* Time-series bar chart (single series → token-colored marks, no legend). */
    .spend-chart-wrap { margin-top: var(--space-16); }
    .spend-chart-head { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: var(--space-6); }
    .spend-chart { display: block; width: 100%; height: auto; }
    .spend-chart .spend-bar { fill: var(--color-accent); }
    .spend-chart-axis { display: flex; justify-content: space-between; margin-top: var(--space-4); font-size: var(--text-11); color: var(--color-text-faint); }
    /* By-project horizontal breakdown. */
    .spend-projects { margin-top: var(--space-16); display: flex; flex-direction: column; gap: var(--space-6); }
    .spend-projects .text--eyebrow { display: block; margin-bottom: var(--space-4); }
    .spend-proj-row { display: grid; grid-template-columns: minmax(0, 1fr) 2fr auto; align-items: center; gap: var(--space-8); }
    .spend-proj-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .spend-proj-track { height: var(--space-8); background: var(--color-surface-sunken); border-radius: var(--radius-pill); overflow: hidden; }
    .spend-proj-fill { height: 100%; background: var(--color-accent); border-radius: var(--radius-pill); }
    .spend-proj-val { text-align: right; white-space: nowrap; }
    .spend-proj-more { margin-top: var(--space-2); }
    .log-property-items {
      display: flex;
      flex-direction: column;
      gap: var(--space-12);
      margin-top: var(--space-10);
    }
    .log-property-items .log-item { margin-bottom: 0; }
    /* Per-property "Download all" (PDF / Markdown) in the Recent-leases group header. */
    .log-export {
      display: inline-flex;
      align-items: center;
      flex-wrap: wrap;
      gap: var(--space-6);
      margin-left: auto;
    }
    .log-export-status { font-size: var(--text-11); color: var(--color-text-faint); }
    .log-export-status.error { color: var(--color-danger); }

    pre.debug {
      background: var(--color-surface);
      border: var(--border-hairline) solid var(--color-border-subtle);
      border-radius: var(--radius-md);
      padding: var(--space-12);
      font-size: var(--text-12);
      line-height: 1.4;
      white-space: pre-wrap;
      word-break: break-word;
      color: var(--color-text-muted);
      overflow-x: auto;
    }

    /* ── Baskerville display on editorial titles/figures/quotes ──
       Card and section titles, key values, big figures, and the verbatim lease
       quote all render in the serif display face (weight 400) per the brand. */
    .flow-title,
    .resume-name,
    .building-value,
    .key-card-title,
    .clause-title,
    .stat-num,
    .owner-period .op-amount,
    .rollup-head,
    .statement-balance {
      font-family: var(--font-serif);
      font-weight: var(--weight-regular);
    }
    .clause-quote { font-family: var(--font-serif); }

    /* ── Footer ── */
    footer {
      margin-top: var(--space-48);
      padding-bottom: var(--space-24);
      font-size: var(--text-12);
      /* Muted, but still readable: --color-text-faint aliases a BORDER grey (--p-mist),
         which is far too light for text on the cream canvas (~1.5:1). Use the proper
         secondary TEXT token (~6.6:1, passes WCAG AA). Dividers/dot stay subtle. */
      color: var(--color-text-secondary);
      text-align: center;
    }
    /* Brand row — TrueUp, Stiles, and Intelligent Buildings on one line, each pair
       split by a thin hairline divider. Wraps only when a screen is too narrow to
       hold all three. */
    .footer-logos {
      display: flex;
      flex-wrap: wrap;
      align-items: center;
      justify-content: center;
      gap: var(--space-16);
      margin: 0 auto var(--space-16);
    }
    /* The subtle separators between logos. */
    .footer-divider {
      flex: 0 0 auto;
      width: var(--border-hairline);
      height: var(--space-20);
      background: var(--color-divider);
    }

    /* TrueUp — the product; full color, the single crimson hero accent. */
    .footer-logo {
      display: block;
      height: 24px;
      width: auto;
      max-width: 160px;
    }

    /* Stiles + Intelligent Buildings — partner / builder endorsements. Both marks
       share TrueUp's slate + crimson (Stiles) or navy + orange (IB) palettes, so
       they're muted (grayscale, dimmed) by default — keeping crimson as TrueUp's
       single hero accent — and ease to full color on hover/focus. Heights are tuned
       per mark so the wordmark-heavy Stiles logo and the taller IB lockup read at
       the same optical weight. */
    .footer-stiles,
    .footer-ib {
      display: block;
      width: auto;
      filter: grayscale(1);
      opacity: 0.55;
      transition: filter 0.25s ease, opacity 0.25s ease;
    }
    .footer-stiles { height: 18px; max-width: 150px; }
    .footer-ib { height: 26px; max-width: 150px; }
    .footer-stiles:hover,
    .footer-stiles:focus,
    .footer-ib:hover,
    .footer-ib:focus {
      filter: grayscale(0);
      opacity: 1;
    }
    @media (prefers-reduced-motion: reduce) {
      .footer-stiles,
      .footer-ib { transition: none; }
    }

    /* Keep all three on one row on phones: tighten the gap + scale the marks down
       so the row fits a narrow viewport instead of wrapping. */
    @media (max-width: 480px) {
      .footer-logos { gap: var(--space-8); }
      .footer-divider { height: var(--space-16); }
      .footer-logo { height: 20px; }
      .footer-stiles { height: 15px; }
      .footer-ib { height: 22px; }
    }

    /* Lease roster: the desktop 4-column grid (suite · tenant · SF · actions)
       crams and overlaps on phones because the action buttons hold their width.
       Stack it — suite + name + SF on one line, the action buttons on their own
       full-width line below. */
    @media (max-width: 640px) {
      #lease-roster-list .roster-item,
      #lease-roster-list .roster-item.has-lease .roster-main {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        gap: var(--space-6) var(--space-10);
      }
      #lease-roster-list .roster-item.roster-head { display: none; }
      #lease-roster-list .roster-suite { flex: 0 0 auto; }
      #lease-roster-list .roster-name  { flex: 1 1 auto; min-width: 0; }
      #lease-roster-list .roster-sf    { flex: 0 0 auto; margin-left: auto; text-align: right; }
      #lease-roster-list .roster-actions { flex: 1 0 100%; justify-content: flex-start; }
    }

    @media (max-width: 560px) {
      body { padding: var(--space-24) var(--space-16) var(--space-48); }
      /* Compact nav: logo + account (Admin + Sign out) share the top row on the
         right; the primary tabs drop to their own full-width row below. This uses
         the empty space beside the logo instead of stacking Admin/Sign out on the
         left. Achieved with flex order: home(0) · account(1) · links(2, full-width). */
      nav { min-height: 0; gap: var(--space-8) var(--space-12); margin-bottom: var(--space-20); padding: var(--space-10) 0; }
      nav .nav-stiles { height: 20px; }
      .tu-mark.hero-mark { height: 34px; }
      nav .nav-account { order: 1; }
      nav .nav-links { order: 2; flex-basis: 100%; gap: var(--space-2); flex-wrap: wrap; }
      nav .nav-tab { padding: var(--space-6) var(--space-10); font-size: var(--text-14); }
      nav .nav-user { padding-left: var(--space-12); }
      nav .nav-user-email { display: none; }
      /* Slim the landing hero on small screens. */
      .landing-card { padding: var(--space-28) var(--space-20); }
      .landing-card h1 { margin-bottom: var(--space-6); }
      .subtitle { margin-bottom: var(--space-16); }
      .divider { margin: 0 auto var(--space-16); }
      .tagline { font-size: var(--text-16); line-height: 1.5; margin-bottom: var(--space-24); }
      .clause-head { flex-wrap: wrap; gap: var(--space-6); }
      .log-item { flex-wrap: wrap; }
      .log-right { width: 100%; justify-content: flex-start; }
      .cmp-table { font-size: var(--text-12); }
      .cmp-table th, .cmp-table td { padding: var(--space-6) var(--space-8); }
      .cmp-table th.cmp-rowhead, .cmp-table td.cmp-rowhead { min-width: 84px; font-size: var(--text-12); }
      .cmp-val { max-width: 150px; }
    }

    @media (max-width: 480px) {
      .landing-card { padding: var(--space-24) var(--space-16); }
      .form-card { padding: var(--space-20); }
    }

    /* Two-click delete confirmation: the armed button (first click) shows an
       accent "Confirm?" state until a second click or the 4s timeout. Replaces the
       native window.confirm() dialog, which embedded/sandboxed webviews suppress. */
    .log-btn.confirming, .resume-del.confirming {
      background: var(--color-accent);
      color: var(--color-surface);
      border-color: var(--color-accent);
    }

    /* ── Verify calculation components (cap / base-year / gross-up) ──────────────
       The pre-compute review step: per-account controllable/occupancy-variable
       flags, per-tenant lease terms, and a transparent per-tenant breakdown so a
       manager can confirm each adjustment before it's wrapped into the CAM Rec. */
    .cam-section { margin-top: var(--space-20); }
    .cam-section > h4 { margin-bottom: var(--space-8); font-size: var(--text-14); }
    .cam-occ-field { max-width: 280px; margin-bottom: var(--space-8); }
    .cam-acct-table { width: 100%; border-collapse: collapse; margin-bottom: var(--space-8); }
    .cam-acct-table th, .cam-acct-table td {
      text-align: left; padding: var(--space-6) var(--space-8);
      border-bottom: var(--border-thin) solid var(--color-border); font-size: var(--text-13);
    }
    .cam-acct-table th { color: var(--color-text-muted); font-weight: var(--weight-semibold); }
    .cam-acct-table td.cam-chk, .cam-acct-table th.cam-chk { text-align: center; white-space: nowrap; }
    .cam-acct-table td.cam-amt { text-align: right; font-variant-numeric: tabular-nums; }
    .cam-tenant {
      border: var(--border-thin) solid var(--color-border); border-radius: var(--radius-md);
      padding: var(--space-16); margin-bottom: var(--space-12);
    }
    .cam-tenant-head {
      display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-8);
      margin-bottom: var(--space-12); font-weight: var(--weight-semibold);
    }
    .cam-grid {
      display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
      gap: var(--space-12); align-items: end;
    }
    .cam-field label { display: block; font-size: var(--text-12); color: var(--color-text-muted); margin-bottom: var(--space-4); }
    .cam-field input, .cam-field select { width: 100%; }
    /* A required, not-yet-filled field (e.g. the cap's prior-year controllable $, essential to
       compute the cap) — highlighted red so it can't be overlooked. Clears once a value is entered. */
    .cam-field--required label { color: var(--color-danger); font-weight: var(--weight-semibold); }
    .cam-field--required input { border: var(--border-thick) solid var(--color-danger); background: var(--color-surface-danger); }
    .cam-req { color: var(--color-danger); font-weight: var(--weight-bold); font-size: var(--text-11); margin-left: var(--space-4); text-transform: uppercase; letter-spacing: var(--tracking-2); }
    .cam-req-note { display: block; margin-top: var(--space-4); font-size: var(--text-11); color: var(--color-danger); }
    .cam-bd { margin-top: var(--space-12); }
    .cam-bd table { width: 100%; border-collapse: collapse; font-size: var(--text-13); }
    .cam-bd td { padding: var(--space-4) var(--space-8); border-bottom: var(--border-thin) solid var(--color-border); }
    .cam-bd td:last-child { text-align: right; font-variant-numeric: tabular-nums; }
    .cam-bd tr.cam-bd-final td { font-weight: var(--weight-semibold); border-top: var(--border-thin) solid var(--color-border); border-bottom: none; }
    .cam-bd td.cam-bd-note { color: var(--color-text-muted); font-size: var(--text-12); }
    .cam-badge {
      font-size: var(--text-11); color: var(--color-accent); font-weight: var(--weight-semibold);
      text-transform: uppercase; letter-spacing: var(--tracking-8); margin-left: var(--space-4);
    }
    .cam-flag {
      display: inline-block; font-size: var(--text-11); padding: var(--space-2) var(--space-6);
      border-radius: var(--radius-sm); background: var(--color-surface-dark); color: var(--color-surface);
    }
    /* Lease-driven override chip — crimson, to stand apart from the dark default chip. */
    .cam-flag--lease { background: var(--color-accent); }

    /* Per-tenant CAM pool panel — collapsible, mirrors the workbook's per-tenant column. */
    .cam-pool {
      margin: var(--space-12) 0; border: var(--border-thin) solid var(--color-border);
      border-radius: var(--radius-md); background: var(--color-accent-subtle);
    }
    .cam-pool-summary {
      list-style: none; cursor: pointer; padding: var(--space-10) var(--space-14);
      font-weight: var(--weight-semibold); font-size: var(--text-14); color: var(--color-text-heading);
      display: flex; align-items: center; gap: var(--space-8);
    }
    .cam-pool-summary::-webkit-details-marker { display: none; }
    .cam-pool-summary::after { content: '▸'; color: var(--color-text-faint); margin-left: auto; transition: transform 0.15s; }
    .cam-pool[open] > .cam-pool-summary::after { transform: rotate(90deg); }
    .cam-pool > .hint, .cam-pool > .cam-acct-table { padding-left: var(--space-14); padding-right: var(--space-14); }
    .cam-pool > .cam-acct-table { width: calc(100% - var(--space-28)); margin-bottom: var(--space-12); }

    /* Tax-bill quick-assign button — a solid accent call-to-action that pulses so a
       first-time user notices they need to click it (the ghost style read as passive). */
    .tax-assign {
      background: var(--color-accent); color: var(--color-text-on-accent);
      border-color: var(--color-accent); font-weight: var(--weight-semibold);
      animation: tax-assign-pulse 1.8s ease-in-out infinite;
    }
    .tax-assign:hover:not(:disabled) { background: var(--color-accent-hover); border-color: var(--color-accent-hover); }
    @keyframes tax-assign-pulse {
      0%, 100% { box-shadow: 0 0 0 0 var(--color-accent-subtle); }
      50% { box-shadow: 0 0 0 var(--space-6) var(--color-accent-subtle); }
    }
    @media (prefers-reduced-motion: reduce) { .tax-assign { animation: none; } }
    /* An ALREADY-assigned bill: quiet, ticked, and still clickable (clicking takes it back
       off). It must not pulse — the pulse means "this needs you", and a bill that is already
       where it belongs does not. */
    .tax-assign--on {
      background: var(--color-surface); color: var(--color-text-primary);
      border-color: var(--color-accent); font-weight: var(--weight-regular);
      animation: none;
    }
    .tax-assign--on:hover:not(:disabled) { background: var(--color-surface-muted); border-color: var(--color-accent); }

    /* A suggested lease that belongs to a DIFFERENT property. Offered (a lease is sometimes
       filed under a differently-spelled project name) but never mistakable for a local one. */
    .pl-sugg-cross {
      display: inline-block; margin-left: var(--space-4);
      padding: 0 var(--space-4); border-radius: var(--radius-sm);
      background: var(--color-surface-warning); color: var(--color-text-secondary);
      font-size: var(--text-11); text-transform: uppercase; letter-spacing: var(--tracking-1);
    }

    /* ── Lease Intelligence screen ─────────────────────────────────────────────
       Token-only. Reuses .card / .btn / .text / .cmp-risk / .stat where possible;
       these classes cover the intel-specific roster, term-review, and findings UI. */
    .intel-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-12); margin-bottom: var(--space-20); }
    .intel-filter { align-items: center; justify-content: space-between; gap: var(--space-12); flex-wrap: wrap; padding: var(--space-10) var(--space-14); margin-bottom: var(--space-16); border: var(--border-thin) solid var(--color-border); border-left: var(--border-bar) solid var(--color-accent); border-radius: var(--radius-md); background: var(--color-accent-subtle); }
    .intel-search { max-width: 320px; }
    .intel-progress { margin-bottom: var(--space-20); }
    .intel-progress-top { display: flex; justify-content: space-between; font-size: var(--text-12); color: var(--color-text-secondary); margin-bottom: var(--space-6); }
    .intel-progress-count { font-variant-numeric: tabular-nums; color: var(--color-text-muted); }
    .intel-stat-grid { grid-template-columns: repeat(auto-fit, minmax(96px, 1fr)); margin-top: 0; margin-bottom: var(--space-24); }

    .intel-layout { display: grid; grid-template-columns: minmax(240px, 320px) 1fr; gap: var(--space-20); align-items: start; }
    @media (max-width: 760px) { .intel-layout { grid-template-columns: 1fr; } }

    .intel-roster { max-height: 640px; overflow-y: auto; }
    .intel-lease-row {
      display: flex; flex-direction: column; gap: var(--space-2); width: 100%; text-align: left;
      background: transparent; border: none; border-bottom: var(--border-hairline) solid var(--color-border-subtle);
      padding: var(--space-12) var(--space-6); cursor: pointer;
    }
    .intel-lease-row:hover { background: var(--color-surface-muted); }
    .intel-lease-row.is-active { background: var(--color-accent-subtle); box-shadow: inset var(--border-bar) 0 0 0 var(--color-accent); }
    .intel-lease-name { font-size: var(--text-14); font-weight: var(--weight-semibold); color: var(--color-text-heading); }
    .intel-lease-meta { font-size: var(--text-11); color: var(--color-text-muted); }
    .intel-lease-row .cmp-risk { align-self: flex-start; margin-top: var(--space-4); text-transform: uppercase; }

    .intel-detail-head { margin-bottom: var(--space-16); }
    .intel-sub { margin-top: var(--space-20); margin-bottom: var(--space-8); }
    .intel-docs { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: var(--space-4); }
    .intel-docs li { font-size: var(--text-13); color: var(--color-text-secondary); }
    .intel-doc-role { font-weight: var(--weight-semibold); text-transform: uppercase; letter-spacing: var(--tracking-4); font-size: var(--text-11); color: var(--color-text-heading); }

    .intel-term-sec { margin-bottom: var(--space-16); }
    .intel-term-sec-h { font-size: var(--text-11); text-transform: uppercase; letter-spacing: var(--tracking-5); color: var(--color-text-subtle); margin-bottom: var(--space-6); }
    .intel-term { border: var(--border-thin) solid var(--color-border-subtle); border-radius: var(--radius-md); padding: var(--space-12); margin-bottom: var(--space-8); }
    .intel-term--absent { opacity: 0.72; }
    .intel-term-top { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-8); }
    .intel-term-label { font-size: var(--text-13); font-weight: var(--weight-semibold); color: var(--color-text-heading); }
    .intel-term-val { font-size: var(--text-14); color: var(--color-text-primary); margin-top: var(--space-4); }
    .intel-term-cite { margin: var(--space-8) 0 0; padding-left: var(--space-12); border-left: var(--border-bar) solid var(--color-border); font-size: var(--text-12); color: var(--color-text-muted); font-style: italic; }
    .intel-cite-ref { font-style: normal; color: var(--color-text-subtle); }

    .intel-conf { font-size: var(--text-11); font-weight: var(--weight-semibold); text-transform: uppercase; letter-spacing: var(--tracking-4); padding: var(--space-2) var(--space-8); border-radius: var(--radius-pill); }
    .intel-conf--high { background: var(--color-surface-success); color: var(--color-success-text); }
    .intel-conf--med  { background: var(--color-surface-warning); color: var(--color-warning-text); }
    .intel-conf--low  { background: var(--color-surface-danger); color: var(--color-danger); }
    .intel-conf--none { color: var(--color-text-faint); }

    .intel-rev { font-size: var(--text-11); font-weight: var(--weight-semibold); text-transform: uppercase; letter-spacing: var(--tracking-4); padding: var(--space-2) var(--space-8); border-radius: var(--radius-pill); }
    .intel-rev--ok   { background: var(--color-surface-success); color: var(--color-success-text); }
    .intel-rev--edit { background: var(--color-accent-subtle); color: var(--color-accent); }
    .intel-rev--flag { background: var(--color-surface-danger); color: var(--color-danger); }

    .intel-term-actions { display: flex; flex-wrap: wrap; gap: var(--space-6); margin-top: var(--space-10); }
    .intel-mini {
      font-size: var(--text-11); font-weight: var(--weight-semibold); text-transform: uppercase; letter-spacing: var(--tracking-4);
      padding: var(--space-4) var(--space-10); border: var(--border-thin) solid var(--color-slate-border); border-radius: var(--radius-md);
      background: transparent; color: var(--color-slate-text); cursor: pointer;
    }
    .intel-mini:hover { background: var(--color-surface-dark); color: var(--color-text-on-accent); }
    .intel-mini--clear { border-color: var(--color-border); color: var(--color-text-muted); }

    .intel-findings-list { display: flex; flex-direction: column; gap: var(--space-8); }
    .intel-finding { display: flex; align-items: flex-start; gap: var(--space-12); padding: var(--space-12); border: var(--border-thin) solid var(--color-border-subtle); border-radius: var(--radius-md); }
    .intel-finding--done { opacity: 0.55; }
    .intel-finding-body { flex: 1; display: flex; flex-direction: column; gap: var(--space-2); }
    .intel-finding-title { font-size: var(--text-14); font-weight: var(--weight-semibold); color: var(--color-text-heading); }
    .intel-finding-detail { font-size: var(--text-12); color: var(--color-text-secondary); }
    .intel-finding-meta { font-size: var(--text-11); color: var(--color-text-muted); text-transform: uppercase; letter-spacing: var(--tracking-4); }
    .intel-finding-actions { display: flex; flex-wrap: wrap; gap: var(--space-6); }
    @media (max-width: 640px) { .intel-finding { flex-wrap: wrap; } }

    /* ── Admin · Owner Intakes ─────────────────────────────────────────────────
       Token-only. Reuses .card / .btn / .input / .intel-mini. */
    .admin-form { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: var(--space-12); }
    .admin-linkbox { margin-top: var(--space-16); padding-top: var(--space-16); border-top: var(--border-hairline) solid var(--color-border-subtle); }
    .admin-linkrow { display: flex; gap: var(--space-8); align-items: center; margin-top: var(--space-6); }
    .admin-linkrow .input { font-family: var(--font-mono); }
    .admin-intake { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-12); padding: var(--space-14) var(--space-8); border-bottom: var(--border-hairline) solid var(--color-border-subtle); flex-wrap: wrap; }
    .admin-intake-main { display: flex; flex-direction: column; gap: var(--space-2); }
    .admin-intake-who { font-size: var(--text-15); font-weight: var(--weight-semibold); color: var(--color-text-heading); }
    .admin-intake-sub { font-size: var(--text-12); color: var(--color-text-secondary); }
    .admin-intake-meta { font-size: var(--text-11); color: var(--color-text-muted); text-transform: uppercase; letter-spacing: var(--tracking-4); margin-top: var(--space-2); }
    .admin-intake-nda { font-size: var(--text-12); color: var(--color-text-secondary); margin-top: var(--space-4); }
    .admin-intake-actions { display: flex; gap: var(--space-8); flex-wrap: wrap; align-items: center; }

    /* App-wide, one-tap notifications prompt (fixed, unobtrusive, dismissible). */
    .notify-prompt {
      position: fixed;
      left: var(--space-16);
      right: var(--space-16);
      bottom: var(--space-16);
      z-index: 900;
      margin: 0 auto;
      max-width: 40rem;
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: var(--space-16);
      flex-wrap: wrap;
      padding: var(--space-12) var(--space-16);
      background: var(--color-surface);
      border: var(--border-thin) solid var(--color-border);
      border-radius: var(--radius-lg);
      box-shadow: var(--shadow-lg);
    }
    /* The display:flex above beats the UA [hidden]{display:none}, so restate it. */
    .notify-prompt[hidden] { display: none; }
    .notify-prompt-text { font-size: var(--text-14); color: var(--color-text-secondary); flex: 1 1 16rem; }
    .notify-prompt-actions { display: flex; gap: var(--space-8); flex-wrap: wrap; align-items: center; }

    /* ═══ Stacking plan (issue 159 tee-up) ═══════════════════════════════════
       Occupancy map + lease-rollover timeline. Colors come only from the lease
       time-to-expiry tokens (crimson escalation + slate horizon ramp) defined in
       semantic.css — no raw values here. */
    .sp-subhead { font-size: var(--text-12); letter-spacing: 0.04em; text-transform: uppercase;
      color: var(--color-text-muted); font-weight: var(--weight-semibold); margin: var(--space-24) 0 var(--space-12); }

    /* bucket → fill + on-fill ink, shared by blocks, bars, swatches */
    .sp-unit, .sp-bar, .sp-renew, .sp-swatch { --u-bg: var(--color-surface-muted); --u-fg: var(--color-text-primary); }
    [data-bucket="expired"] { --u-bg: var(--color-lease-expired);      --u-fg: var(--color-text-on-accent); }
    [data-bucket="rolling"] { --u-bg: var(--color-lease-rolling);      --u-fg: var(--color-text-primary); }
    [data-bucket="near"]    { --u-bg: var(--color-lease-horizon-near); --u-fg: var(--color-text-primary); }
    [data-bucket="mid"]     { --u-bg: var(--color-lease-horizon-mid);  --u-fg: var(--color-text-on-accent); }
    [data-bucket="far"]     { --u-bg: var(--color-lease-horizon-far);  --u-fg: var(--color-text-on-accent); }
    [data-bucket="unknown"] { --u-bg: var(--color-surface-muted);      --u-fg: var(--color-text-secondary); }

    /* KPI strip */
    .sp-kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr)); gap: var(--border-thin);
      background: var(--color-border); border: var(--border-thin) solid var(--color-border);
      border-radius: var(--radius-md); overflow: hidden; margin-top: var(--space-16); }
    .sp-kpi { background: var(--color-surface); padding: var(--space-12) var(--space-16); min-width: 0; }
    .sp-kpi-v { font-size: var(--text-24); font-weight: var(--weight-semibold); letter-spacing: -0.02em; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
    .sp-kpi-v small { font-size: var(--text-14); font-weight: var(--weight-semibold); color: var(--color-text-secondary); }
    .sp-kpi-v.alert { color: var(--color-lease-expired); }
    .sp-kpi-v.warn { color: var(--color-slate-text); }
    .sp-kpi-l { font-size: var(--text-12); color: var(--color-text-muted); margin-top: var(--space-4); }

    /* Mobile: tighten the explainer, shrink the KPI numbers, and keep everything inside the
       viewport (a big figure must wrap, never clip or overflow the card). */
    @media (max-width: 640px) {
      .sp-howto { padding: var(--space-10) var(--space-12); font-size: var(--text-12); margin-bottom: var(--space-12); }
      .sp-kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); }
      /* An odd last KPI (5 of them → 2+2+1) spans the row so there's no empty half-cell. */
      .sp-kpi:last-child:nth-child(odd) { grid-column: 1 / -1; }
      .sp-kpi { padding: var(--space-10) var(--space-12); }
      .sp-kpi-v { font-size: var(--text-18); }
      .sp-kpi-v small { font-size: var(--text-11); }
      .sp-parcel-head { flex-wrap: wrap; gap: var(--space-2) var(--space-8); }
      .sp-filters { gap: var(--space-8); }
    }

    /* time control */
    .sp-timebar { display: flex; align-items: center; gap: var(--space-16); margin: var(--space-20) 0 var(--space-8); flex-wrap: wrap; }
    .sp-timebar-label { font-size: var(--text-13); color: var(--color-text-secondary); font-weight: var(--weight-semibold); }
    .sp-asof { font-size: var(--text-14); font-weight: var(--weight-semibold); color: var(--color-accent); font-variant-numeric: tabular-nums; min-width: 3.2em; }
    .sp-range { flex: 1; min-width: 14rem; accent-color: var(--color-accent); }
    .sp-hint { font-size: var(--text-12); color: var(--color-text-muted); }

    /* legend */
    .sp-legend { display: flex; flex-wrap: wrap; gap: var(--space-6) var(--space-16); margin: var(--space-10) 0 var(--space-20); }
    .sp-legend span { display: inline-flex; align-items: center; gap: var(--space-8); font-size: var(--text-12); color: var(--color-text-secondary); }
    .sp-swatch { width: 0.85rem; height: 0.85rem; border-radius: var(--radius-sm); flex: none; background: var(--u-bg); }
    .sp-swatch[data-bucket="vacant"], .sp-unit[data-bucket="vacant"] {
      background-color: transparent;
      background-image: repeating-linear-gradient(45deg, var(--color-lease-vacant-line) 0 2px, transparent 2px 6px);
      border: var(--border-thin) dashed var(--color-lease-vacant-line); color: var(--color-text-secondary); }

    /* buildings + stacked units */
    .sp-buildings { display: grid; grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); gap: var(--space-20); }
    /* Project → Parcel → Building: a parcel section spans the row and holds its own building grid. */
    .sp-parcel { grid-column: 1 / -1; }
    .sp-parcel + .sp-parcel { margin-top: var(--space-16); }
    .sp-parcel-head { display: flex; justify-content: space-between; align-items: baseline; gap: var(--space-8);
      padding-bottom: var(--space-6); margin-bottom: var(--space-12); border-bottom: var(--border-thick) solid var(--color-accent); }
    .sp-parcel-name { font-family: var(--font-serif); font-size: var(--text-16); font-weight: var(--weight-semibold); }
    .sp-parcel-meta { font-size: var(--text-12); color: var(--color-text-muted); font-variant-numeric: tabular-nums; }
    .sp-parcel-blds { display: grid; grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); gap: var(--space-20); }
    .sp-bld { background: var(--color-surface); border: var(--border-thin) solid var(--color-border); border-radius: var(--radius-md); padding: var(--space-12); }
    .sp-bld-head { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: var(--space-10); gap: var(--space-8); }
    .sp-bld-name { font-weight: var(--weight-semibold); font-size: var(--text-14); }
    .sp-bld-meta { font-size: var(--text-12); color: var(--color-text-muted); font-variant-numeric: tabular-nums; }
    .sp-stack { display: flex; flex-direction: column; gap: var(--space-4); }
    .sp-unit { position: relative; border-radius: var(--radius-sm); padding: var(--space-8) var(--space-10);
      display: flex; justify-content: space-between; align-items: center; gap: var(--space-8);
      background: var(--u-bg); color: var(--u-fg); overflow: hidden; transition: box-shadow 0.08s ease; }
    .sp-unit:hover { box-shadow: 0 0 0 var(--border-thin) var(--color-surface), var(--shadow-md); z-index: 3; }
    .sp-unit-who { min-width: 0; }
    .sp-unit-t { font-weight: var(--weight-semibold); font-size: var(--text-13); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .sp-unit-s { font-size: var(--text-11); opacity: 0.9; font-variant-numeric: tabular-nums; }
    .sp-unit-rt { font-size: var(--text-11); text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }

    /* rollover timeline */
    .sp-timeline { background: var(--color-surface); border: var(--border-thin) solid var(--color-border); border-radius: var(--radius-md); padding: var(--space-16) var(--space-16) var(--space-8); overflow-x: auto; }
    .sp-tl-grid { position: relative; min-width: 35rem; --sp-lane: 9.5rem; }
    .sp-tl-axis { position: relative; height: 1.25rem; margin-left: var(--sp-lane); border-bottom: var(--border-thin) solid var(--color-border); }
    .sp-yr { position: absolute; top: 0; transform: translateX(-50%); font-size: var(--text-11); color: var(--color-text-muted); font-variant-numeric: tabular-nums; }
    .sp-tl-row { position: relative; height: 1.6rem; display: flex; align-items: center; }
    .sp-lane { width: var(--sp-lane); flex: none; font-size: var(--text-12); color: var(--color-text-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; padding-right: var(--space-10); }
    .sp-tl-track { position: relative; flex: 1; height: 100%; }
    .sp-bar { position: absolute; top: 0.32rem; height: 0.95rem; border-radius: var(--radius-sm); background: var(--u-bg); }
    .sp-renew { position: absolute; top: 0.32rem; height: 0.95rem; border-radius: 0 var(--radius-sm) var(--radius-sm) 0; opacity: 0.45;
      background-color: var(--u-bg); background-image: repeating-linear-gradient(45deg, var(--color-surface) 0 1.5px, transparent 1.5px 5px); }
    .sp-nowline { position: absolute; top: 0; bottom: 0; width: var(--border-thin); background: var(--color-accent); z-index: 4; }
    .sp-nowline::after { content: attr(data-yr); position: absolute; top: -1.15rem; left: 50%; transform: translateX(-50%);
      font-size: var(--text-11); font-weight: var(--weight-bold); color: var(--color-text-on-accent); background: var(--color-accent);
      padding: 0 var(--space-4); border-radius: var(--radius-sm); font-variant-numeric: tabular-nums; }
    .sp-empty { color: var(--color-text-muted); font-size: var(--text-13); padding: var(--space-12) 0; }

    /* tooltip */
    .sp-tip { position: fixed; z-index: 50; pointer-events: none; opacity: 0; transition: opacity 0.1s;
      background: var(--color-slate-text); color: var(--color-text-on-accent); border-radius: var(--radius-sm);
      padding: var(--space-8) var(--space-10); font-size: var(--text-12); max-width: 15rem; box-shadow: var(--shadow-md); line-height: 1.5; }
    .sp-tt { font-weight: var(--weight-bold); margin-bottom: var(--space-4); }
    .sp-row { display: flex; justify-content: space-between; gap: var(--space-12); }
    .sp-row span:last-child { font-variant-numeric: tabular-nums; }
    .sp-note { margin-top: var(--space-6); opacity: 0.85; font-size: var(--text-11); line-height: 1.4; }

    /* Stacking-plan "it builds itself" explainer */
    .sp-howto { background: var(--color-canvas); border-left: var(--border-bar) solid var(--color-accent);
      padding: var(--space-12) var(--space-16); margin: 0 0 var(--space-16); font-size: var(--text-13);
      line-height: 1.5; color: var(--color-text-secondary); }
    .sp-howto strong { color: var(--color-text-primary); }

    /* "Built N ago · Refresh" bar (from-leases path only) */
    .sp-refreshbar { display: flex; align-items: center; justify-content: space-between; gap: var(--space-12);
      flex-wrap: wrap; margin: 0 0 var(--space-16); }
    .sp-built { font-size: var(--text-12); color: var(--color-text-muted); }

    /* Stacking-plan filters + expiring-SF heatmap (issue 159) */
    .sp-filters { display: flex; align-items: center; gap: var(--space-16); flex-wrap: wrap; margin: 0 0 var(--space-8); }
    .sp-filter-group { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-6); }
    .sp-filter-label { font-size: var(--text-11); font-weight: var(--weight-semibold); letter-spacing: var(--tracking-4);
      text-transform: uppercase; color: var(--color-text-muted); margin-right: var(--space-4); }
    .sp-chip-n { font-size: var(--text-11); opacity: 0.7; font-variant-numeric: tabular-nums; }
    .sp-chip { border: var(--border-thin) solid var(--color-border); background: var(--color-surface); color: var(--color-text-secondary);
      border-radius: var(--radius-full); padding: var(--space-4) var(--space-12); font: inherit; font-size: var(--text-12);
      font-weight: var(--weight-semibold); cursor: pointer; }
    .sp-chip:hover { border-color: var(--color-accent); color: var(--color-text-primary); }
    .sp-chip.active { background: var(--color-accent); border-color: var(--color-accent); color: var(--color-text-on-accent); }
    .sp-filter-window { font-size: var(--text-12); color: var(--color-text-secondary); display: inline-flex; align-items: center; gap: var(--space-6); }
    .sp-select { font: inherit; font-size: var(--text-12); padding: var(--space-4) var(--space-8); border-radius: var(--radius-sm);
      border: var(--border-thin) solid var(--color-border); background: var(--color-surface); color: var(--color-text-primary); }
    .sp-unit.sp-dim { opacity: 0.28; }

    .sp-heat-wrap { overflow-x: auto; }
    .sp-heatmap { min-width: 24rem; display: flex; flex-direction: column; gap: var(--space-4); }
    .sp-heat-row { display: flex; align-items: center; gap: var(--space-4); }
    .sp-heat-y { flex: none; width: 2.75rem; font-size: var(--text-11); color: var(--color-text-muted); font-variant-numeric: tabular-nums; text-align: right; padding-right: var(--space-6); }
    .sp-heat-m { flex: 1; text-align: center; font-size: var(--text-11); color: var(--color-text-muted); }
    .sp-heat-head .sp-heat-y { visibility: hidden; }
    .sp-heat-cell { flex: 1; height: 1.35rem; border-radius: var(--radius-sm); background: var(--color-surface-muted); }
    .sp-heat-cell[data-heat="1"] { background: var(--color-surface-danger); }
    .sp-heat-cell[data-heat="2"] { background: var(--color-lease-rolling); }
    .sp-heat-cell[data-heat="3"] { background: var(--color-lease-expired); }
    .sp-heat-cell[data-sf] { cursor: default; }

    /* Visual match grid (issue 159 bulk flow) */
    .sp-match-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: var(--space-12); margin-bottom: var(--space-16); }
    .sp-match-card { border: var(--border-thin) solid var(--color-border); border-radius: var(--radius-md); padding: var(--space-10) var(--space-12); background: var(--color-surface); }
    .sp-match-head { display: flex; align-items: center; gap: var(--space-6); flex-wrap: wrap; }
    .sp-match-head .sp-name { flex: 2 1 8rem; }
    .sp-match-head .sp-suite { flex: 1 1 4rem; }
    .sp-match-head .input { padding: var(--space-4) var(--space-8); font-size: var(--text-13); }
    .sp-match-sub { font-size: var(--text-11); color: var(--color-text-muted); margin: var(--space-4) 0 var(--space-8); font-variant-numeric: tabular-nums; }
    .sp-doc-row { display: flex; align-items: center; gap: var(--space-8); padding: var(--space-4) 0; }
    .sp-doc-meta { flex: 1; min-width: 0; font-size: var(--text-12); color: var(--color-text-secondary); }
    .sp-reassign { flex: none; max-width: 11rem; }
    .sp-match-tray { border: var(--border-thin) dashed var(--color-border-danger); border-radius: var(--radius-md);
      padding: var(--space-8) var(--space-12); margin-bottom: var(--space-16); background: var(--color-surface-danger); }
    .sp-doc-row.sp-dim { opacity: 0.5; }
    #bulk-match-preview .sp-buildings { margin-bottom: var(--space-8); }

    /* Logos + scale in the match grid / stacking plan (issue 159, dozens of tenants) */
    .sp-unit-who { display: flex; align-items: center; gap: var(--space-8); min-width: 0; }
    .sp-unit-txt { min-width: 0; }
    .sp-unit .tenant-logo, .sp-lane .tenant-logo, .sp-match-head .tenant-logo { flex: none; }
    .sp-lane { display: flex; align-items: center; gap: var(--space-6); white-space: normal; }
    .sp-lane-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

    .sp-match-bar { display: flex; justify-content: space-between; align-items: center; gap: var(--space-12); flex-wrap: wrap; margin-bottom: var(--space-8); }
    .sp-match-count { font-size: var(--text-13); color: var(--color-text-secondary); }
    .sp-match-count b { color: var(--color-danger); font-weight: var(--weight-semibold); }
    .sp-match-controls { display: flex; align-items: center; gap: var(--space-12); flex-wrap: wrap; }
    .sp-search { padding: var(--space-4) var(--space-8); font-size: var(--text-12); min-width: 12rem; }
    .sp-review-toggle { font-size: var(--text-12); color: var(--color-text-secondary); display: inline-flex; align-items: center; gap: var(--space-4); white-space: nowrap; }
    .sp-match-card.sp-review { border-left: var(--border-thick) solid var(--color-lease-rolling); }

    /* Receipt-style first-page preview on a match card (issue 159) */
    .sp-thumb { position: relative; display: block; width: 100%; height: 9rem; overflow: hidden; padding: 0;
      border: var(--border-thin) solid var(--color-border); border-radius: var(--radius-sm);
      background: var(--color-surface-muted); cursor: pointer; margin: var(--space-4) 0 var(--space-8); }
    .sp-thumb:hover { border-color: var(--color-accent); }
    .sp-thumb-frame { display: block; width: 100%; height: 26rem; border: 0; pointer-events: none; background: var(--color-surface); }
    .sp-thumb-hint { position: absolute; left: 0; right: 0; bottom: 0; font-size: var(--text-11);
      color: var(--color-text-on-accent); background: var(--color-slate-text); padding: var(--space-4) var(--space-8);
      text-align: center; opacity: 0.92; }

    /* Legal/full tenant name from triage, paired with the first-page preview (issue 159) */
    .sp-legal { font-size: var(--text-11); color: var(--color-text-secondary); margin: 0 0 var(--space-8); line-height: 1.4; }
    .sp-legal b { color: var(--color-text-primary); font-weight: var(--weight-semibold); }

    /* Zoom chip on the preview + the lightbox it opens (issue 159) */
    .sp-thumb-zoom { position: absolute; top: var(--space-6); right: var(--space-6); display: inline-flex;
      align-items: center; justify-content: center; width: 1.5rem; height: 1.5rem; border-radius: var(--radius-full);
      background: var(--color-slate-text); color: var(--color-text-on-accent); opacity: 0.9; }
    .sp-thumb:hover .sp-thumb-zoom { opacity: 1; background: var(--color-accent); }

    .sp-zoom { position: fixed; inset: 0; z-index: 1200; display: flex; align-items: center; justify-content: center; padding: var(--space-16); }
    .sp-zoom-scrim { position: absolute; inset: 0; background: var(--color-scrim); }
    .sp-zoom-panel { position: relative; display: flex; flex-direction: column; width: min(52rem, 94vw); height: min(88vh, 60rem);
      background: var(--color-surface); border-radius: var(--radius-md); overflow: hidden; box-shadow: var(--shadow-md); }
    .sp-zoom-head { display: flex; align-items: center; gap: var(--space-12); padding: var(--space-10) var(--space-16);
      border-bottom: var(--border-thin) solid var(--color-border); }
    .sp-zoom-title { font-weight: var(--weight-semibold); font-size: var(--text-14); color: var(--color-text-primary); flex: 1; min-width: 0;
      white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .sp-zoom-open { font-size: var(--text-12); color: var(--color-accent); text-decoration: none; white-space: nowrap; }
    .sp-zoom-open:hover { text-decoration: underline; }
    .sp-zoom-close { border: 0; background: none; color: var(--color-text-muted); font-size: var(--text-24); line-height: 1; cursor: pointer; padding: 0 var(--space-4); }
    .sp-zoom-close:hover { color: var(--color-text-primary); }
    .sp-zoom-frame { flex: 1; width: 100%; border: 0; background: var(--color-surface-muted); }
    /* "View lease text" overlay — the faithful text copy, read-only, in the same lightbox as the lease preview. */
    .lease-text-body { flex: 1; overflow: auto; padding: var(--space-16); background: var(--color-surface-muted); }
    .lease-text-meta { color: var(--color-text-muted); font-size: var(--text-12); margin-bottom: var(--space-12); }
    .lease-text-note { background: var(--color-warning-accent); border: 1px solid var(--color-border-warning);
      color: var(--color-warning-text-strong); padding: var(--space-10) var(--space-12); border-radius: var(--radius-md);
      margin-bottom: var(--space-12); font-size: var(--text-13); }
    /* Failed-read variant — a mostly-illegible transcription (see transcriptionQuality). Danger border + text
       so it reads as an error, not the benign "long lease" caution. */
    .lease-text-note--warn { border-color: var(--color-border-danger); color: var(--color-danger-strong); }
    .lease-text-note--warn .btn { margin-left: var(--space-8); }
    /* Faithful text copy: the transcription's Markdown is RENDERED (headings, bold, lists, tables,
       blockquotes) into readable HTML — not shown as raw `#`/`**`/`|` syntax — in a proportional font
       with comfortable spacing, so a manager can actually read the lease on any screen. The `— Page N —`
       anchors and the search-hit marks are spliced in unchanged; the source PDF remains the layout authority. */
    .lease-text-pre { word-wrap: break-word; overflow-wrap: anywhere; font-family: var(--font-sans); font-size: var(--text-14);
      line-height: 1.6; color: var(--color-text-primary); background: var(--color-surface);
      border: var(--border-thin) solid var(--color-border); border-radius: var(--radius-md); padding: var(--space-16); margin: 0; }
    .lease-text-doc { font-size: var(--text-18); font-weight: var(--weight-bold); color: var(--color-text-primary);
      margin: var(--space-20) 0 var(--space-12); padding-bottom: var(--space-6); border-bottom: var(--border-thin) solid var(--color-border); }
    .lease-text-doc:first-child { margin-top: 0; }
    .lease-text-h { font-weight: var(--weight-semibold); color: var(--color-text-primary); margin: var(--space-16) 0 var(--space-8); line-height: 1.35; }
    .lease-text-h1 { font-size: var(--text-18); }
    .lease-text-h2 { font-size: var(--text-16); }
    .lease-text-h3, .lease-text-h4, .lease-text-h5, .lease-text-h6 { font-size: var(--text-14); }
    .lease-text-p { margin: 0 0 var(--space-12); }
    .lease-text-list { margin: 0 0 var(--space-12); padding-left: var(--space-20); }
    .lease-text-list li { margin-bottom: var(--space-4); }
    /* A numbered lease clause. Hanging indent (positive padding + negative first-line indent) so the
       enumerator sits in its own gutter while the text stays ONE run — which is what keeps a find
       spanning "5. TAXES" highlighting across it. Depth is the sub-level's indent and nothing more. */
    .lease-text-clause { margin: 0 0 var(--space-12); padding-left: var(--space-32);
      text-indent: calc(-1 * var(--space-32)); }
    .lease-text-clause--d1 { margin-left: var(--space-16); }
    .lease-text-clause--d2 { margin-left: var(--space-32); }
    /* A repeating page stamp / running header: set the way the page sets it — present, quiet, never removed. */
    .lease-text-furniture { margin: 0 0 var(--space-8); font-size: var(--text-12); color: var(--color-text-muted); }
    .lease-text-quote { margin: 0 0 var(--space-12); padding: var(--space-6) var(--space-12);
      border-left: var(--border-thick) solid var(--color-border); color: var(--color-text-secondary); background: var(--color-surface-muted); }
    .lease-text-hr { border: 0; border-top: var(--border-thin) solid var(--color-border); margin: var(--space-16) 0; }
    .lease-text-code { font-family: var(--font-mono); font-size: var(--text-13); background: var(--color-surface-muted);
      border-radius: var(--radius-sm); padding: 0 var(--space-4); }
    /* A wide rent/charge schedule scrolls inside its OWN container — the page body never scrolls sideways. */
    .lease-text-tablewrap { overflow-x: auto; margin: 0 0 var(--space-12); }
    .lease-text-table { border-collapse: collapse; font-size: var(--text-13); }
    .lease-text-table th, .lease-text-table td { border: var(--border-thin) solid var(--color-border);
      padding: var(--space-4) var(--space-8); text-align: left; vertical-align: top; }
    .lease-text-table th { background: var(--color-surface-muted); font-weight: var(--weight-semibold); }
    /* Phase 2 — the split pane: faithful text (left) beside the rendered source PDF (right). */
    .sp-zoom-panel--wide { width: min(80rem, 96vw); height: min(92vh, 62rem); }
    .lease-text-role { font-size: var(--text-12); color: var(--color-text-primary); background: var(--color-surface);
      border: var(--border-thin) solid var(--color-border); border-radius: var(--radius-sm); padding: var(--space-4) var(--space-8); max-width: 40%; }
    .lease-text-toggle { display: none; font-size: var(--text-12); color: var(--color-accent); background: var(--color-surface);
      border: var(--border-thin) solid var(--color-border); border-radius: var(--radius-sm); padding: var(--space-4) var(--space-10); cursor: pointer; }
    .lease-text-toggle:hover { border-color: var(--color-accent); }
    /* The way OUT of the viewer. It is deliberately NOT .lease-text-toggle: that class is
       display:none above 640px (the split pane shows both panes, so there is nothing to toggle),
       and borrowing it hid the close control on desktop entirely. A way back must be visible on
       every surface — CLAUDE.md, the way-back rule. It carries a WORD, not a bare glyph: the old
       "×" sat among a search field, a page stepper and a view toggle, where it read as "clear the
       field" as readily as "leave the lease". */
    .lease-text-close { display: inline-flex; font-size: var(--text-12); color: var(--color-accent);
      background: var(--color-surface); border: var(--border-thin) solid var(--color-border);
      border-radius: var(--radius-sm); padding: var(--space-4) var(--space-10); cursor: pointer; }
    .lease-text-close:hover { border-color: var(--color-accent); }
    .lease-text-split { flex: 1; display: flex; min-height: 0; }
    .lease-text-col { flex: 1 1 50%; min-width: 0; overflow: auto; }
    .lease-text-col--text { background: var(--color-surface-muted); padding: var(--space-16); }
    .lease-text-col--pdf { position: relative; background: var(--color-surface-sunken); border-left: var(--border-thin) solid var(--color-border); padding: var(--space-12); }
    .lease-text-anchor { display: block; width: fit-content; color: var(--color-accent); cursor: pointer; text-decoration: underline;
      text-decoration-style: dotted; font-weight: var(--weight-semibold); font-size: var(--text-12); margin: var(--space-16) 0 var(--space-8); }
    .lease-text-anchor:hover { color: var(--color-accent-hover); background: var(--color-accent-bg); }
    /* Find-in-lease: a search box + match count in the header, highlighted hits in the text. */
    .lease-text-search { font-size: var(--text-13); max-width: 16rem; padding: var(--space-4) var(--space-8); }
    .lease-text-count { font-size: var(--text-12); color: var(--color-text-muted); white-space: nowrap; }
    /* The match stepper (‹ ›) sits with the count. ALWAYS visible — never hover-only — because on a
       phone it is the only way to reach match 2..N (a touch keyboard has no Shift+Enter). Touch-sized. */
    .lease-text-find { display: inline-flex; align-items: center; gap: var(--space-4); flex: 0 0 auto; }
    .lease-text-step { display: inline-flex; align-items: center; justify-content: center;
      min-width: var(--space-32); min-height: var(--space-32); padding: 0 var(--space-6);
      font-size: var(--text-16); line-height: 1; color: var(--color-accent); background: var(--color-surface);
      border: var(--border-thin) solid var(--color-border); border-radius: var(--radius-sm); cursor: pointer; }
    .lease-text-step:hover:not(:disabled) { border-color: var(--color-accent); }
    .lease-text-step:disabled { color: var(--color-text-muted); opacity: 0.45; cursor: default; }
    /* Current-page indicator — always visible, the SAME page whether you're on the text or the image. */
    .lease-text-pageind { font-size: var(--text-12); font-weight: var(--weight-semibold); color: var(--color-text-secondary);
      white-space: nowrap; flex: 0 0 auto; }
    .lease-text-hit { background: var(--color-surface-warning); color: var(--color-text-primary); border-radius: var(--radius-sm); }
    .lease-text-hit.is-active { background: var(--color-accent); color: var(--color-text-on-accent); }
    .lease-text-pdf-note { color: var(--color-text-muted); font-size: var(--text-12); padding: var(--space-10) var(--space-12); }
    .lease-text-pdf-note--warn { color: var(--color-warning-text-strong); background: var(--color-surface-warning);
      border: var(--border-thin) solid var(--color-border-warning); border-radius: var(--radius-md); }
    .lease-text-pdf-pages { display: flex; flex-direction: column; gap: var(--space-12); }
    /* COLUMN, so the "couldn't mark this page" note stacks ABOVE the page rather than beside it.
       (It cannot live inside the frame: the marks layer is `inset: 0` of the frame, so a note in
       there would grow the box every mark is a percentage of.) */
    .lease-text-pdf-page { min-height: var(--space-64); display: flex; flex-direction: column;
      justify-content: center; align-items: center; }
    /* An UNRENDERED page carries a real US-Letter aspect so the page stack has true height and is
       scrollable before anything renders — otherwise short placeholders make scroll-follow land wrong
       and pages never enter the viewport to render (the "PDF doesn't move as I scroll" bug). */
    .lease-text-pdf-page.is-ph { width: 100%; aspect-ratio: 8.5 / 11; }
    .lease-text-pdf-ph { color: var(--color-text-muted); font-size: var(--text-12); padding: var(--space-24); }
    /* The page's frame carries the border/shadow so the marks layer, positioned against the frame's
       PADDING box, lines up with the canvas exactly — a border on the canvas itself would offset
       every mark by its width. */
    .lease-text-pdf-frame { position: relative; width: 100%;
      box-shadow: var(--shadow-md); border: var(--border-thin) solid var(--color-border); background: var(--color-surface); }
    .lease-text-pdf-canvas { display: block; width: 100%; max-width: 100%; height: auto; }
    /* Find-in-lease, marked on the source image. Percentage-positioned against the page box, so one
       paint survives the fast→sharp canvas swap, a resize and the phone pane toggle. Never
       interactive — the image is for reading; the text pane is where a match is stepped through. */
    .lease-text-pdf-marks { position: absolute; inset: 0; pointer-events: none; }
    .lease-text-pdf-mark { position: absolute; background: var(--color-find-mark);
      border: var(--border-thin) solid var(--color-find-mark-edge); border-radius: var(--radius-sm); }
    /* A page we could NOT mark says why, on the page itself — an unmarked image must never be read
       as "no match here" when the truth is "this page is a scan" / "we couldn't read its text" /
       "its own text disagrees with the text copy".
       ⚠ STICKY, NOT ABSOLUTE, and that is the whole point of the note. Absolute-at-top-of-page put
       it ~796px up a page shown in a ~775px pane, and scroll-follow lands the manager AT THE MATCH,
       never at the page top — measured at 1400×900, the explanation sat 399px above the viewport
       while its own page filled it. Sticky keeps it at the top of the pane for as long as any part
       of its page is on screen, which is exactly as long as it has something to explain. */
    .lease-text-pdf-hlnote { position: sticky; top: 0; z-index: 1; width: 100%; line-height: 1.4;
      padding: var(--space-6) var(--space-10); font-size: var(--text-12);
      color: var(--color-warning-text-strong); background: var(--color-surface-warning);
      border-bottom: var(--border-thin) solid var(--color-border-warning); pointer-events: none; }
    /* Narrow / PWA: the split becomes a Text ⇄ PDF toggle (one column at a time) — in PORTRAIT ONLY.
       The toggle exists because two columns on a 390px-wide phone are ~190px each, which is not a
       reading width. ROTATE TO LANDSCAPE and that reason is gone: 844px is two ~410px columns, so the
       text and the source image sit side by side exactly as they do on a desktop, and the toggle has
       nothing left to toggle.
       ⚠ THIS CONDITION IS SHARED WITH THE SCRIPT — `LEASE_TOGGLED_MQ` in index.html is the same string,
       because the JS asks "are the panes toggled?" to decide whether the two panes follow each other.
       If the two ever disagree, the follow silently stops working in the layout where BOTH panes are
       on screen — the worst place for it to stop. Change them together. */
    @media (max-width: 760px) and (orientation: portrait) {
      .lease-text-toggle { display: inline-flex; }
      .lease-text-split .lease-text-col--pdf { display: none; }
      .lease-text-split.is-showing-pdf .lease-text-col--text { display: none; }
      .lease-text-split.is-showing-pdf .lease-text-col--pdf { display: block; }
      /* The header row used to overflow a phone, and the panel clips its overflow — so the close ×
         (and, once added, the match stepper) sat off-screen where they may as well not exist. Wrap it,
         and give the search box its own full-width line — FIRST (order: -1), so the box leads and its
         match count sits directly beneath it, rather than the box landing under the buttons. In
         LANDSCAPE the box keeps its place in the row: there is width to spare and no vertical room to
         spend on a line of its own. */
      #lease-text-overlay .lease-text-search { order: -1; flex: 1 0 100%; max-width: 100%; }
    }
    @media (max-width: 760px) {
      /* Free header room on a phone — the page indicator matters more than the panel title. */
      #lease-text-overlay .sp-zoom-title { display: none; }
      #lease-text-overlay .sp-zoom-head { flex-wrap: wrap; gap: var(--space-8) var(--space-10); }
    }
    /* A landscape phone is WIDE AND SHORT (~844×390): the width buys the second column, and the height
       is the scarce thing. Trim the chrome's padding so the two panes get the room, and let the panel
       use the full viewport — a centred dialog with a margin costs a tenth of the reading height. */
    @media (max-width: 760px) and (orientation: landscape) {
      #lease-text-overlay .sp-zoom-panel--wide { width: 100vw; height: 100vh; max-width: 100vw; }
      #lease-text-overlay .lease-text-col--text { padding: var(--space-8) var(--space-12); }
      #lease-text-overlay .lease-text-col--pdf { padding: var(--space-8); }
      #lease-text-overlay .cam-ask-bar { padding: var(--space-6) var(--space-10); }
    }

    /* Ask Cam — the assistant's read-only surface in the document viewer (docs/cam-character.md).
       The brand IS the restraint: a quiet wordmark, plain copy, no mascot. Token-only. */
    .cam-ask-bar { border-bottom: var(--border-thin) solid var(--color-border); background: var(--color-surface);
      padding: var(--space-6) var(--space-12); }
    .cam-ask-form { display: flex; align-items: center; gap: var(--space-8); }
    /* FOLDED INTO THE HEADER: the ask form is a member of the header row, not a row of its own. It takes
       the slack the title doesn't need (flex 2 against the title's 1) and caps its width so it can't
       crowd the find box out of the row on a wide window. `min-width: 0` is what lets it shrink at all —
       without it the input's intrinsic width would force the row to overflow, and the panel clips. */
    #lease-text-overlay .sp-zoom-head .cam-ask-form { flex: 2 1 0; min-width: 0; max-width: 26rem; }
    /* The ask field: the input plus its clear (×). The button overlays the input's right edge, so the
       field is the positioning context and the input reserves room for it. The × is the touch-reliable
       way to empty a box that still holds the last question (select-on-focus is not, on iOS). */
    .cam-ask-field { position: relative; display: flex; flex: 1; min-width: 0; }
    .cam-ask-input { flex: 1; min-width: 0; padding-right: var(--space-32); }
    .cam-ask-clear { position: absolute; top: 0; right: 0; bottom: 0; width: var(--space-32);
      display: flex; align-items: center; justify-content: center; border: none; background: none;
      padding: 0; font-size: var(--text-18); line-height: 1; color: var(--color-text-muted); cursor: pointer; }
    .cam-ask-clear:hover { color: var(--color-text-primary); }
    /* The question, echoed above Cam's answer — the box empties on submit, so the answer carries it. */
    .cam-asked { font-size: var(--text-12); color: var(--color-text-muted); margin-bottom: var(--space-6); }
    /* Cam's icon — a white "C" glyph on a crimson tile (docs/cam-icon.md). Square-cornered (brand
       --radius-sm is 0), matching the glyph's own square corners. The glyph fill is a token, not raw. */
    .cam-mark { display: inline-flex; align-items: center; justify-content: center; flex: none;
      width: var(--space-20); height: var(--space-20); padding: var(--space-4);
      border-radius: var(--radius-sm); background: var(--color-accent); }
    .cam-mark-svg { width: 100%; height: 100%; display: block; }
    .cam-mark-svg path { fill: var(--color-text-on-accent); }
    .cam-ask-panel:empty { display: none; }
    .cam-ask-panel { margin-top: var(--space-8); }
    .cam-answer { border: var(--border-thin) solid var(--color-border); border-radius: var(--radius-md);
      background: var(--color-surface); padding: var(--space-10) var(--space-12); }
    .cam-answer--warn { border-color: var(--color-warning-text-strong); background: var(--color-surface-warning); }
    .cam-answer-head { display: flex; align-items: center; gap: var(--space-6); margin-bottom: var(--space-6); }
    /* The name beside the mark. Quiet on purpose — muted and small, so the card reads as an
       answer with an author rather than a chat bubble from a product. It carries a little
       letter-spacing because at 12px a four-letter name next to a solid tile reads cramped. */
    .cam-name { font-size: var(--text-12); color: var(--color-text-muted); font-weight: var(--weight-medium);
      letter-spacing: var(--tracking-2); }
    .cam-answer-body { font-size: var(--text-14); color: var(--color-text-primary); }
    .cam-answer-foot { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-8); margin-top: var(--space-8); }
    .cam-cite { border: var(--border-thin) solid var(--color-accent); color: var(--color-accent-strong);
      background: var(--color-surface); border-radius: var(--radius-sm); padding: var(--space-4) var(--space-8);
      font-size: var(--text-12); font-weight: var(--weight-medium); cursor: pointer; }
    .cam-src { font-size: var(--text-12); color: var(--color-text-muted); }
    .cam-conf { margin-left: auto; font-size: var(--text-12); color: var(--color-warning-text-strong);
      font-weight: var(--weight-medium); }
    .cam-gap { margin-top: var(--space-6); font-size: var(--text-12); color: var(--color-text-muted); }
    /* Anticipatory starter chips (Rule 1) — one tap to ask a common question.
       ONE ROW, ALWAYS, at every width. This used to wrap above 760px, and the gate was the bug: it is
       a WIDTH rule for a VERTICAL problem. Four sentence-length chips wrap to two rows at any window
       under ~1050px — and worst of all on a LANDSCAPE PHONE (844 wide, 390 TALL), which cleared the
       760px gate and so got two wrapped rows in the least vertical room the app ever has. The chips
       STAY after an answer, so a manager can tap a follow-up without retyping. */
    .cam-chips { display: flex; flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch;
      gap: var(--space-6); margin-top: var(--space-6); scrollbar-width: none; }
    .cam-chips::-webkit-scrollbar { display: none; }   /* a scrollbar here would re-spend the height we just saved */
    .cam-chip { flex: 0 0 auto; white-space: nowrap;
      border: var(--border-thin) solid var(--color-border); background: var(--color-surface);
      color: var(--color-text-primary); border-radius: var(--radius-pill); padding: var(--space-4) var(--space-10);
      font-size: var(--text-12); cursor: pointer; }
    .cam-chip:hover { border-color: var(--color-accent); color: var(--color-accent-strong); }
    /* IN THE LEASE VIEWER the chips and the answer are not permanent chrome. They are a menu of four
       sentences sitting above the lease — Cam's own standard is "one next step, not a menu" and "silence
       is the DEFAULT state" — so the WHOLE BAR appears when the manager reaches for the ask box (focus,
       which a tap gives on a phone, exactly where typing is the enemy) and stays while an answer is on
       screen for a follow-up. NOT hover: a phone has no hover.
       ⚠ The condition is `.is-asking`, set from JS, NOT `:focus-within` — the ask FORM now lives in the
       header row, outside this element, so focus-within here can no longer see the manager reach for it.
       A display:none bar contributes no height, which is the whole point: with nothing to say, the lease
       starts directly under a single header line. */
    #cam-ask-bar { display: none; }
    #cam-ask-bar.is-asking,
    #cam-ask-bar.has-answer { display: block; }
    /* Ask Cam on the Verify card — collapsed until asked (Rule 2). */
    .cam-verify { margin: var(--space-8) 0; }
    .cam-verify-toggle { display: inline-flex; align-items: center; gap: var(--space-6); background: none;
      border: none; padding: var(--space-4) 0; font-size: var(--text-12); color: var(--color-accent-strong);
      font-weight: var(--weight-medium); cursor: pointer; }
    .cam-verify-body { margin-top: var(--space-8); }
    .cam-verify-body .cam-ask-form { display: flex; align-items: center; gap: var(--space-8); }
    .cam-clause { margin: var(--space-8) 0 0; padding: var(--space-6) var(--space-10);
      border-left: var(--border-thick) solid var(--color-accent); background: var(--color-surface);
      color: var(--color-text-primary); font-size: var(--text-13); }

    /* Triage overview → drill-in (issue 159) */
    .sp-ov-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); gap: var(--space-10); margin-bottom: var(--space-16); }
    .sp-ov-tile { text-align: left; border: var(--border-thin) solid var(--color-border); border-radius: var(--radius-md);
      background: var(--color-surface); padding: var(--space-12) var(--space-16); font: inherit; position: relative; }
    button.sp-ov-tile { cursor: pointer; }
    .sp-ov-drill:hover { border-color: var(--color-accent); }
    .sp-ov-drill::after { content: '\203A'; position: absolute; top: var(--space-8); right: var(--space-12); color: var(--color-text-muted); font-size: var(--text-14); }
    .sp-ov-v { font-size: var(--text-24); font-weight: var(--weight-semibold); letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
    .sp-ov-v.ok { color: var(--color-success-text); }
    .sp-ov-v.alert { color: var(--color-lease-expired); }
    .sp-ov-v.warn { color: var(--color-slate-text); }
    .sp-ov-l { font-size: var(--text-12); color: var(--color-text-muted); margin-top: var(--space-4); }
    .sp-ov-actions { justify-content: flex-start; gap: var(--space-8); flex-wrap: wrap; }
    .sp-back { border: 0; background: none; color: var(--color-accent); font: inherit; font-size: var(--text-13); cursor: pointer; padding: 0; margin-right: var(--space-8); white-space: nowrap; }
    .sp-back:hover { text-decoration: underline; }

    /* Stacking-plan cross-check (issue 196, Phase 1): the reviewable spaces table
       read from a spreadsheet stacking plan — every space, occupied and vacant. */
    .sp-plan-tbl { width: 100%; border-collapse: collapse; font-size: var(--text-14); }
    .sp-plan-tbl th {
      text-align: left;
      font-size: var(--text-12);
      font-weight: var(--weight-semibold);
      letter-spacing: var(--tracking-2);
      color: var(--color-text-muted);
      padding: var(--space-6) var(--space-10);
      border-bottom: var(--border-thin) solid var(--color-border);
    }
    .sp-plan-tbl td {
      padding: var(--space-6) var(--space-10);
      border-bottom: var(--border-thin) solid var(--color-border-subtle);
      color: var(--color-text-primary);
      font-variant-numeric: tabular-nums;
    }
    .sp-plan-tbl tr.sp-row-vacant td { background: var(--color-surface-sunken); color: var(--color-text-faint); }

    /* ── Prior-owner reconciliation audit (Door 3) ── */
    .audit-headline {
      background: var(--color-surface);
      border: var(--border-thin) solid var(--color-border);
      border-radius: var(--radius-xl);
      padding: var(--space-20);
      margin-bottom: var(--space-20);
    }
    .audit-propname {
      font-size: var(--text-20);
      font-weight: var(--weight-bold);
      color: var(--color-text-heading);
      margin-bottom: var(--space-8);
    }
    .audit-verdict {
      font-size: var(--text-16);
      font-weight: var(--weight-semibold);
      color: var(--color-text-heading);
      margin-bottom: var(--space-8);
    }
    .audit-verdict.audit-verdict--issues { color: var(--color-danger); }
    .audit-verdict.audit-verdict--clean { color: var(--color-success-text); }
    /* Click-to-expand itemized issue list — the headline count ties back to it 1:1. */
    .audit-issues { margin-bottom: var(--space-4); }
    .audit-issues > summary { cursor: pointer; list-style: none; margin-bottom: 0; }
    .audit-issues > summary::-webkit-details-marker { display: none; }
    .audit-issues-cue {
      font-size: var(--text-13);
      font-weight: var(--weight-semibold);
      color: var(--color-text-muted);
      white-space: nowrap;
    }
    .audit-issues-cue::after { content: '\25b8'; margin-left: var(--space-4); }   /* ▸ */
    .audit-issues[open] .audit-issues-cue::after { content: '\25be'; }            /* ▾ */
    .audit-issue-list {
      margin: var(--space-12) 0 0;
      padding-left: var(--space-24);
      display: flex;
      flex-direction: column;
      gap: var(--space-8);
    }
    .audit-issue-list li { padding-left: var(--space-4); }
    .audit-issue-title { font-weight: var(--weight-semibold); color: var(--color-text-heading); }
    /* The headline number: net dollar impact of the errors found. */
    .audit-impact {
      display: flex;
      align-items: baseline;
      flex-wrap: wrap;
      gap: var(--space-4) var(--space-16);
      margin-top: var(--space-16);
      padding: var(--space-16) var(--space-20);
      background: var(--color-surface-danger);
      border: var(--border-thin) solid var(--color-danger);
      border-radius: var(--radius-lg);
    }
    .audit-impact-num { font-size: var(--text-24); font-weight: var(--weight-bold); color: var(--color-danger); font-variant-numeric: tabular-nums; }
    .audit-impact-lbl { font-size: var(--text-13); color: var(--color-text-body); flex: 1; min-width: 12rem; }
    .audit-impact--clean { background: var(--color-surface-success); border-color: var(--color-success); }
    .audit-impact--clean .audit-impact-num { color: var(--color-success-text); }
    .audit-metrics {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
      gap: var(--space-12);
      margin-top: var(--space-16);
    }
    .audit-metric {
      background: var(--color-surface-sunken);
      border: var(--border-thin) solid var(--color-border-subtle);
      border-radius: var(--radius-md);
      padding: var(--space-12) var(--space-16);
    }
    .audit-metric-num { font-size: var(--text-20); font-weight: var(--weight-semibold); color: var(--color-text-heading); font-variant-numeric: tabular-nums; }
    .audit-metric-lbl { font-size: var(--text-12); color: var(--color-text-muted); margin-top: var(--space-4); }
    .audit-section { margin-bottom: var(--space-28); }
    .audit-section > h3 {
      font-size: var(--text-16);
      color: var(--color-text-heading);
      margin-bottom: var(--space-4);
    }
    .audit-section > .audit-sub { font-size: var(--text-13); color: var(--color-text-muted); margin-bottom: var(--space-12); }
    .audit-table-wrap { overflow-x: auto; }
    .audit-table {
      width: 100%;
      border-collapse: collapse;
      font-size: var(--text-13);
      font-variant-numeric: tabular-nums;
    }
    .audit-table th, .audit-table td {
      text-align: right;
      padding: var(--space-8) var(--space-12);
      border-bottom: var(--border-thin) solid var(--color-border-subtle);
      white-space: nowrap;
    }
    .audit-table th { color: var(--color-text-muted); font-weight: var(--weight-semibold); font-size: var(--text-12); }
    .audit-table td.audit-l, .audit-table th.audit-l { text-align: left; white-space: normal; overflow-wrap: anywhere; }
    .audit-table tr.audit-row-flag td { background: var(--color-surface-danger); }
    .audit-delta-note { color: var(--color-danger); font-size: var(--text-12); font-weight: var(--weight-semibold); margin-top: var(--space-2); }
    /* How a row was derived (a flat annual charge, a part-year tenant, the share basis).
       Informational, NOT a discrepancy — so it must never read as a red flag. */
    .audit-row-note { color: var(--color-text-muted); font-size: var(--text-12); margin-top: var(--space-2); }
    .audit-table tr.audit-pool-head td {
      background: var(--color-surface-sunken);
      color: var(--color-text-heading);
      font-weight: var(--weight-semibold);
      text-align: left;
    }
    .audit-table td.audit-delta-bad { color: var(--color-danger); font-weight: var(--weight-bold); }
    /* A variance that ties. Muted, but present — an em dash reads as "checked, no
       difference", where an empty cell reads as "not computed". */
    .audit-table td.audit-var-ok { color: var(--color-text-muted); }
    /* WHICH workbook the findings on screen came from — the year it states and the file
       picked. Identity, so it sits with the property name and reads quietly. */
    .audit-source {
      font-size: var(--text-13);
      color: var(--color-text-secondary);
      margin-bottom: var(--space-8);
    }

    /* The audit's own reading of the workbook's columns. The warning is deliberately
       loud — an unrecognised financial column silently distorts every comparison below
       it, so it must not be something a manager has to go looking for. */
    .audit-cols-warn {
      background: var(--color-surface-warning);
      border: var(--border-thin) solid var(--color-border-warning);
      border-left: var(--border-thick) solid var(--color-warning-accent);
      border-radius: var(--radius-md);
      padding: var(--space-12) var(--space-16);
      margin-bottom: var(--space-12);
      font-size: var(--text-13);
      color: var(--color-text-secondary);
      line-height: 1.5;
    }
    .audit-cols { margin-bottom: var(--space-16); }
    .audit-cols > summary {
      cursor: pointer;
      font-size: var(--text-13);
      color: var(--color-text-secondary);
      padding: var(--space-6) 0;
    }
    .audit-cols-table { margin-top: var(--space-8); }

    /* "The workbook states no figure here" — a fact about the workbook, not a finding.
       Muted and italic so it reads as absent data, never as a zero the workbook asserted
       (which is what a $0.00 in this column used to imply). */
    .audit-table td.audit-var-na { color: var(--color-text-muted); font-style: italic; }
    .audit-badge {
      display: inline-block;
      font-size: var(--text-12);
      font-weight: var(--weight-semibold);
      padding: var(--space-2) var(--space-8);
      border-radius: var(--radius-pill);
      white-space: nowrap;
    }
    .audit-badge--error, .audit-badge--mismatch { background: var(--color-surface-danger); color: var(--color-danger); }
    .audit-badge--warn, .audit-badge--seller-only, .audit-badge--lease-only { background: var(--color-surface-warning); color: var(--color-warning-text); }
    .audit-badge--ok, .audit-badge--match { background: var(--color-surface-success); color: var(--color-success-text); }
    .audit-badge--info, .audit-badge--unknown { background: var(--color-surface-muted); color: var(--color-text-muted); }
    /* A finding's lease verdict. "Lease disagrees" reads as the error it is; "Lease
       confirms" closes the item out; the other two are neutral prompts to go look. */
    .audit-badge--contradicts { background: var(--color-surface-danger); color: var(--color-danger); }
    .audit-badge--supports { background: var(--color-surface-success); color: var(--color-success-text); }
    .audit-badge--no-lease { background: var(--color-surface-warning); color: var(--color-warning-text); }
    .audit-badge--not-covered, .audit-badge--unchecked { background: var(--color-surface-muted); color: var(--color-text-muted); }
    /* What to DO about a finding — the line that turns a discrepancy into a task. */
    .audit-next-step {
      margin-top: var(--space-4);
      font-size: var(--text-13);
      color: var(--color-text-secondary);
      border-left: var(--border-thick) solid var(--color-border-strong);
      padding-left: var(--space-8);
    }
    .audit-next-step strong { color: var(--color-text-heading); }
    .audit-next-step--gap { margin-bottom: var(--space-12); }
    .audit-finding {
      display: flex;
      gap: var(--space-12);
      align-items: baseline;
      padding: var(--space-8) 0;
      border-bottom: var(--border-thin) solid var(--color-border-subtle);
    }
    .audit-finding-body { flex: 1; }
    .audit-finding-title { font-weight: var(--weight-semibold); color: var(--color-text-heading); font-size: var(--text-13); }
    .audit-finding-detail { font-size: var(--text-13); color: var(--color-text-body); margin-top: var(--space-2); }
    .audit-clean-note {
      background: var(--color-surface-success);
      border-radius: var(--radius-md);
      padding: var(--space-12) var(--space-16);
      font-size: var(--text-13);
      color: var(--color-success-text);
    }
    .field-hint { font-size: var(--text-12); color: var(--color-text-muted); margin-top: var(--space-4); }
    /* Export-findings toolbar. */
    .audit-toolbar {
      display: flex;
      align-items: center;
      justify-content: flex-end;
      flex-wrap: wrap;
      gap: var(--space-8) var(--space-12);
      margin-bottom: var(--space-16);
    }
    .audit-export-status { font-size: var(--text-13); color: var(--color-text-muted); }
    .audit-export-status.error { color: var(--color-danger); }
    /* Lease-agreement as cards (one per tenant) — wraps instead of a wide, side-
       scrolling table, so no comparison is hidden behind a horizontal scrollbar. */
    .audit-cards { display: flex; flex-direction: column; gap: var(--space-12); }
    .audit-card {
      background: var(--color-surface);
      border: var(--border-thin) solid var(--color-border-subtle);
      border-radius: var(--radius-lg);
      padding: var(--space-16);
    }
    .audit-card-head { margin-bottom: var(--space-12); overflow-wrap: anywhere; }
    .audit-card-name { font-size: var(--text-14); font-weight: var(--weight-semibold); color: var(--color-text-heading); }
    .audit-card-sub { font-size: var(--text-12); color: var(--color-text-muted); margin-top: var(--space-2); overflow-wrap: anywhere; }
    .audit-terms { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: var(--space-12); }
    .audit-term {
      background: var(--color-surface-sunken);
      border-radius: var(--radius-md);
      padding: var(--space-8) var(--space-12);
    }
    .audit-term-label {
      font-size: var(--text-12);
      font-weight: var(--weight-semibold);
      text-transform: uppercase;
      letter-spacing: var(--tracking-8);
      color: var(--color-text-faint);
      margin-bottom: var(--space-4);
    }
    .audit-term-seller { font-size: var(--text-13); color: var(--color-text-primary); margin-bottom: var(--space-4); overflow-wrap: anywhere; }

/* ── Create a Rent Roll from Leases — dedicated 3-step wizard (#rent-roll) ── */
.rr-stepper {
  display: flex;
  gap: var(--space-8);
  flex-wrap: wrap;
  list-style: none;
  padding: 0;
  margin: 0 0 var(--space-16);
}
.rr-stepper li {
  display: flex;
  align-items: center;
  gap: var(--space-8);
  padding: var(--space-8) var(--space-12);
  border: var(--border-thin) solid var(--color-border);
  border-radius: var(--radius-md);
  color: var(--color-text-secondary);
  font-size: var(--text-14);
  cursor: pointer;                 /* the stepper IS the navigation (no separate Next button) */
}
.rr-stepper li:hover { border-color: var(--color-accent); }
.rr-stepper li.rr-active {
  border-color: var(--color-accent);
  color: var(--color-text-heading);
  font-weight: var(--weight-semibold);
}
.rr-stepper li.rr-done { color: var(--color-success-text); }
.rr-dot {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--space-20);
  height: var(--space-20);
  border-radius: var(--radius-lg);
  background: var(--color-accent-bg);
  color: var(--color-accent);
  font-size: var(--text-13);
  font-weight: var(--weight-semibold);
}
.rr-stepper li.rr-active .rr-dot { background: var(--color-accent); color: var(--color-surface); }
.rr-sub { margin-top: var(--space-16); }
.rr-search { margin-top: var(--space-8); width: 100%; }
.rr-selected { margin-top: var(--space-12); }
.rr-selected-box {
  padding: var(--space-8) var(--space-12);
  background: var(--color-surface-sage);
  border: var(--border-thin) solid var(--color-border-sage);
  border-radius: var(--radius-md);
  color: var(--color-text-heading);
}
.rr-picker { display: flex; flex-wrap: wrap; gap: var(--space-8); margin-top: var(--space-8); }
.rr-picker .rr-picked { border-color: var(--color-accent); color: var(--color-text-heading); }
/* A search result already on the roll: a non-clickable "✓ on roll" chip, visibly muted. */
.rr-search-onroll { color: var(--color-text-secondary); cursor: default; opacity: 0.7; }
.rr-foot { display: flex; gap: var(--space-8); flex-wrap: wrap; margin-top: var(--space-16); }
.rr-slot { margin-bottom: var(--space-16); }
.rr-slot-title { font-weight: var(--weight-semibold); color: var(--color-text-heading); margin-bottom: var(--space-8); }
.rr-upload-row { padding: var(--space-4) 0; font-size: var(--text-14); }
.rr-table-wrap { overflow-x: auto; margin-top: var(--space-8); }
.rr-table .rr-vacant { color: var(--color-text-secondary); }
.rr-steps { font-size: var(--text-13); color: var(--color-text-secondary); margin-top: var(--space-4); }
.rr-actions { display: flex; gap: var(--space-8); flex-wrap: wrap; margin-top: var(--space-12); }
.rr-step1-foot { align-items: center; }
/* Resumed view: flag ⚠ on a low-confidence estimate + the "other income" review panel. */
.rr-est-flag { color: var(--color-warning-text); cursor: help; }
.rr-gl-flags { margin-top: var(--space-12); padding: var(--space-12); border: var(--border-thin) solid var(--color-border-warning); background: var(--color-surface-warning); border-radius: var(--radius-md); }
.rr-gl-flags-title { font-weight: var(--weight-semibold); color: var(--color-warning-text); }
.rr-gl-flag-list { margin: 0; padding-left: var(--space-20); color: var(--color-text-heading); }
/* The per-lease list revealed when a project is picked — a tidy Suite · SF · Tenant table. */
.rr-leases-head { font-weight: var(--weight-semibold); color: var(--color-text-heading); margin: var(--space-16) 0 var(--space-8); }
.rr-lease-table-wrap { max-height: 26rem; overflow-y: auto; border: var(--border-thin) solid var(--color-border-subtle); border-radius: var(--radius-md); }
/* Fixed layout: the Suite/SF/× columns are narrow and constant, Tenant takes the rest —
   so a long extracted value can never blow the Suite column out to full width. */
.rr-lease-table { width: 100%; border-collapse: collapse; font-size: var(--text-14); table-layout: fixed; }
.rr-lease-table th { position: sticky; top: 0; background: var(--color-canvas); text-align: left; font-weight: var(--weight-semibold); color: var(--color-text-secondary); }
.rr-lease-table th, .rr-lease-table td { padding: var(--space-4) var(--space-8); border-bottom: var(--border-thin) solid var(--color-border-subtle); vertical-align: middle; }
.rr-lease-table tbody tr:last-child td { border-bottom: none; }
.rr-lease-table tfoot td { font-weight: var(--weight-semibold); color: var(--color-text-heading); border-top: var(--border-thin) solid var(--color-border); border-bottom: none; padding: var(--space-8); }
.rr-lease-table th.rr-lt-suite, .rr-lease-table td.rr-lt-suite { width: 7rem; }
.rr-lease-table th.rr-lt-sf, .rr-lease-table td.rr-lt-sf { width: 5rem; text-align: right; }
.rr-lease-table th.rr-lt-x, .rr-lease-table td.rr-lt-x { width: 2.5rem; text-align: right; }
.rr-cell { width: 100%; padding: var(--space-4) var(--space-8); font-size: var(--text-14); }
.rr-cell-num { text-align: right; }
.rr-lease-x { background: transparent; border: none; color: var(--color-text-secondary); cursor: pointer; font-size: var(--text-16); line-height: 1; padding: 0 var(--space-4); }
.rr-lease-x:hover { color: var(--color-danger); }
/* "Add a missing lease" section at the bottom of Step 1: search + manual entry. */
.rr-addmissing { margin-top: var(--space-24); padding-top: var(--space-16); border-top: var(--border-thin) solid var(--color-border-subtle); }
.rr-addmissing-label { display: block; color: var(--color-text-secondary); font-size: var(--text-13); margin-bottom: var(--space-4); }
.rr-manual-row { display: flex; gap: var(--space-8); flex-wrap: wrap; align-items: center; }
.rr-manual-suite { flex: 0 0 8rem; }
.rr-manual-sf { flex: 0 0 6rem; }
.rr-manual-tenant { flex: 1 1 16rem; }
/* Step 1: two peer entry actions ("Upload…" and "Use my analyzed leases") side by side. */
.rr-actions-row { justify-content: flex-start; align-items: center; gap: var(--space-12); flex-wrap: wrap; }
.rr-or { color: var(--color-text-secondary); font-size: var(--text-13); text-transform: uppercase; letter-spacing: var(--tracking-8); }
/* Outline peer of .btn-add — stands out next to the solid Upload button, visually distinct. */
.btn-add--alt { background: transparent; color: var(--color-accent); border-color: var(--color-accent); }
.btn-add--alt:hover { background: var(--color-accent-bg); }

/* ══ Leases by property (#screen-property-leases) ═══════════════════════════════
   One nav-reachable place per property: the lease documents AND the tenant's
   identity fields on the same row. Token-only (see docs/design-system.md). */
.pl-toolbar {
  display: flex;
  gap: var(--space-12);
  align-items: flex-end;
  flex-wrap: wrap;
  margin-bottom: var(--space-16);
}
.pl-field { display: flex; flex-direction: column; gap: var(--space-4); }
.pl-field--grow { flex: 1 1 16rem; }
/* Embedded in the property drill-down's "Leases" tab, the property is fixed to the one
   being viewed, so the property picker is redundant — hide it there (the standalone
   Leases screen still shows it for switching between properties). */
.pl-embedded #pl-prop-field { display: none; }
/* With the picker gone, the bar that held it has nothing left to show — hide the bar too, or the
   drill-down tab opens on an empty 16px-tall row above the summary. */
.pl-embedded .pl-toolbar { display: none; }
/* The list's own filter, sitting ON the list rather than beside the lease-text search card. Same
   flex shape as .pl-toolbar; a tighter gap below it so it reads as part of the list, not a
   free-standing toolbar. */
.pl-listbar {
  display: flex;
  gap: var(--space-12);
  align-items: flex-end;
  flex-wrap: wrap;
  margin-bottom: var(--space-12);
}
.pl-field-label {
  font-size: var(--text-11);
  font-weight: var(--weight-semibold);
  text-transform: uppercase;
  letter-spacing: var(--tracking-6);
  color: var(--color-text-secondary);
}
.pl-summary {
  display: flex;
  gap: var(--space-16);
  flex-wrap: wrap;
  align-items: center;
  margin-bottom: var(--space-16);
  font-size: var(--text-13);
  color: var(--color-text-secondary);
}
.pl-stat strong { color: var(--color-text-heading); }

/* Lease-text search (slice 3b — docs/design-lease-search.md): portfolio full-text search card on the
   Leases screen. Distinct from the roster "Find". Tokens only. Single column at every width. */
.lts-card { margin-bottom: var(--space-16); padding: var(--space-16); }
.lts-hint { font-size: var(--text-12); color: var(--color-text-muted); margin: var(--space-8) 0 0; }
/* The Cam nudge — shown only when the query reads as a question (ltsRenderNudge). It toggles via the
   `hidden` attribute, so it carries its OWN [hidden] rule: an author `display` beats the UA's
   `[hidden] { display: none }`, which is how the nav profile menu and the bulk folder slot both
   shipped permanently visible. Third time; do not remove. */
.lts-nudge {
  display: block;
  font-size: var(--text-12);
  color: var(--color-text-secondary);
  margin: var(--space-8) 0 0;
}
.lts-nudge[hidden] { display: none; }
.lts-nudge {
  display: flex;
  gap: var(--space-8);
  align-items: center;
  flex-wrap: wrap;
}
.lts-nudge-text { font-size: var(--text-12); color: var(--color-text-secondary); }

/* Cam's cross-lease answer (issue 516). Single column at every width — a finding is a paragraph and a quote,
   which is exactly what a phone reads worst in a grid. */
.lts-answer { margin-top: var(--space-12); }
.lts-answer[hidden] { display: none; }
.lts-answer-card {
  border: var(--border-thin) solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-12);
  background: var(--color-surface-sunken);
}
.lts-answer-status { font-size: var(--text-13); color: var(--color-text-secondary); margin: 0; }
.lts-answer-summary {
  font-size: var(--text-14);
  color: var(--color-text-primary);
  margin: 0 0 var(--space-12);
}
/* The coverage line and the never-approves line are the two sentences that keep the card honest.
   They are quiet, but they are never conditional — see ltsAnswerHtml. */
.lts-answer-coverage,
.lts-answer-foot {
  font-size: var(--text-12);
  color: var(--color-text-muted);
  margin: var(--space-8) 0 0;
}
.lts-finding {
  border-top: var(--border-thin) solid var(--color-border);
  padding-top: var(--space-12);
  margin-top: var(--space-12);
}
.lts-finding-head {
  font-size: var(--text-13);
  font-weight: var(--weight-semibold);
  color: var(--color-text-heading);
}
.lts-finding-flag {
  font-size: var(--text-11);
  font-weight: var(--weight-regular);
  color: var(--color-text-muted);
  text-transform: uppercase;
  letter-spacing: var(--tracking-6);
}
.lts-finding-answer { font-size: var(--text-13); color: var(--color-text-primary); margin: var(--space-4) 0 0; }
.lts-finding-quote {
  font-size: var(--text-12);
  color: var(--color-text-secondary);
  border-left: var(--border-thick) solid var(--color-border);
  padding-left: var(--space-8);
  margin: var(--space-8) 0 0;
}
.lts-finding-src {
  display: inline-block;
  margin-top: var(--space-8);
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  font-size: var(--text-12);
  color: var(--color-accent);
  text-align: left;
}
/* Why a finding carries no page, or carries one nothing has checked (slice 2.4b). Muted, because it
   is a caveat on a real answer — not a warning about it; the quote above is still the lease's. */
.lts-finding-cite {
  font-size: var(--text-12);
  color: var(--color-text-muted);
  margin: var(--space-4) 0 0;
}
/* Not every finding can be opened — an unresolved document must not look like a dead link. */
.lts-finding-src--flat { color: var(--color-text-muted); cursor: default; }
/* Overflow findings — folded, never dropped. The container toggles via the `hidden` attribute, so it
   carries its OWN [hidden] rule: an author `display` beats the UA's `[hidden] { display: none }`
   (the nav profile menu, the bulk folder slot, and the Cam nudge all learned this the hard way). */
.lts-findings-rest { display: block; }
.lts-findings-more { display: block; }
.lts-findings-more[hidden] { display: none; }
.lts-findings-toggle {
  margin-top: var(--space-12);
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  font-size: var(--text-13);
  color: var(--color-accent);
}
.lts-head { display: flex; gap: var(--space-12); align-items: flex-end; flex-wrap: wrap; }
.lts-scope {
  display: inline-flex; align-items: center; gap: var(--space-6);
  font-size: var(--text-13); color: var(--color-text-secondary); white-space: nowrap;
}
.lts-results { display: flex; flex-direction: column; gap: var(--space-8); margin-top: var(--space-12); }
.lts-hit {
  display: flex; flex-direction: column; gap: var(--space-4);
  width: 100%; text-align: left; cursor: pointer;
  padding: var(--space-12);
  border: var(--border-thin) solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
}
.lts-hit:hover { border-color: var(--color-border-strong); background: var(--color-surface-muted); }
/* The row + its name expander. The expander sits BESIDE the row button, not inside it (a button
   within a button is invalid, and the click would open the lease instead of expanding). */
.lts-hit-wrap { display: flex; align-items: flex-start; gap: var(--space-4); }
.lts-hit-wrap .lts-hit { flex: 1 1 auto; min-width: 0; }
.lts-hit-more {
  flex: 0 0 auto; cursor: pointer; font-size: var(--text-13); line-height: 1;
  color: var(--color-text-muted); background: var(--color-surface);
  border: var(--border-thin) solid var(--color-border); border-radius: var(--radius-sm);
  padding: var(--space-8) var(--space-10);
}
.lts-hit-more:hover { border-color: var(--color-border-strong); color: var(--color-text-primary); }
/* An author `display` beats the UA's [hidden] rule — the trap this codebase has hit three times
   (the nav profile menu, the bulk folder slot). These spans carry no display of their own, but the
   rule is explicit so a later layout tweak cannot silently reveal both names at once. */
.lts-hit-name-short[hidden], .lts-hit-name-full[hidden] { display: none; }
.lts-hit-name { font-size: var(--text-13); font-weight: var(--weight-semibold); color: var(--color-text-heading); }
.lts-hit-snip { font-size: var(--text-13); color: var(--color-text-body); }
.lts-hit-snip mark {
  background: var(--color-accent-bg); color: var(--color-accent-strong);
  padding: 0 var(--space-2); border-radius: var(--radius-xs);
}
.lts-hit-loc {
  font-size: var(--text-11); color: var(--color-text-muted);
  text-transform: uppercase; letter-spacing: var(--tracking-6);
}
.lts-foot { font-size: var(--text-12); color: var(--color-text-muted); margin: var(--space-8) 0 0; }
/* Names the passage list so it cannot be mistaken for Cam's answer card above it. */
.lts-results-head {
  font-size: var(--text-11);
  font-weight: var(--weight-semibold);
  text-transform: uppercase;
  letter-spacing: var(--tracking-6);
  color: var(--color-text-secondary);
  margin: 0;
}

.pl-row { margin-bottom: var(--space-12); }
/* Identity: the fields a manager actually edits (suite / tenant / leased SF). */
.pl-ident { display: flex; gap: var(--space-12); flex-wrap: wrap; align-items: flex-end; }
.pl-in { display: flex; flex-direction: column; gap: var(--space-2); flex: 0 0 8rem; }
.pl-in--wide { flex: 1 1 18rem; }
.pl-in > span {
  font-size: var(--text-11);
  font-weight: var(--weight-semibold);
  text-transform: uppercase;
  letter-spacing: var(--tracking-6);
  color: var(--color-text-secondary);
}
.pl-aka-line {
  display: flex;
  gap: var(--space-8);
  flex-wrap: wrap;
  align-items: center;
  padding: var(--space-8) 0 var(--space-4);
  font-size: var(--text-12);
  color: var(--color-text-muted);
}
.pl-aka { color: var(--color-text-muted); }
.pl-rename { display: inline-flex; gap: var(--space-6); align-items: center; flex-wrap: wrap; }

/* Lease block: status, document chips, and every maintenance action. */
.pl-lease {
  display: flex;
  gap: var(--space-8);
  flex-wrap: wrap;
  align-items: center;
  padding-top: var(--space-8);
  border-top: var(--border-thin) solid var(--color-border-subtle);
}
.pl-lease--err { color: var(--color-danger); }
.pl-lease-sub {
  font-size: var(--text-12);
  color: var(--color-text-muted);
  padding-top: var(--space-4);
  word-break: break-word;
}
.pl-msg { font-size: var(--text-12); color: var(--color-text-secondary); }
.pl-lease .log-btn.primary,
.pl-picker .log-btn.primary,
.log-right .log-btn.primary {
  border-color: var(--color-accent);
  color: var(--color-accent);
}
/* Icon + label on a row action button (matches the nav's line-icon style). */
.pl-act, .pl-more-btn { display: inline-flex; align-items: center; gap: var(--space-6); }
.pl-btn-icon { width: var(--space-16); height: var(--space-16); flex: none; }
/* The "More" maintenance menu — a tap-toggled dropdown. */
.pl-more { position: relative; display: inline-flex; }
.pl-more-btn.is-open { border-color: var(--color-accent); color: var(--color-accent); }
.pl-more-scrim { position: fixed; inset: 0; z-index: 40; background: transparent; }
.pl-more-menu {
  position: absolute; top: 100%; right: 0; margin-top: var(--space-4); z-index: 41;
  display: flex; flex-direction: column; white-space: nowrap;
  background: var(--color-surface); border: var(--border-thin) solid var(--color-border);
  border-radius: var(--radius-md); box-shadow: var(--shadow-md); padding: var(--space-4);
}
.pl-more-item {
  display: flex; align-items: center; gap: var(--space-8); width: 100%; text-align: left;
  background: none; border: none; cursor: pointer; font: inherit; font-size: var(--text-13);
  color: var(--color-text-primary); padding: var(--space-8) var(--space-10); border-radius: var(--radius-sm);
}
.pl-more-item:hover { background: var(--color-surface-muted); }
.pl-more-item .pl-btn-icon { color: var(--color-text-muted); }
.pl-more-item--danger:hover { color: var(--color-danger); }
.pl-more-item--danger:hover .pl-btn-icon { color: var(--color-danger); }

/* "Link an existing lease" picker — searches every analyzed lease, because the
   building a lease was extracted under is often not the reconciliation's name. */
.pl-picker {
  margin-top: var(--space-12);
  padding: var(--space-12);
  background: var(--color-surface-sunken);
  border-radius: var(--radius-md);
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
  align-items: flex-start;
}
.pl-picker .input { width: 100%; }
.pl-pick-list { width: 100%; max-height: 18rem; overflow-y: auto; display: flex; flex-direction: column; gap: var(--space-4); }
.pl-pick-row {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  text-align: left;
  width: 100%;
  background: var(--color-surface);
  border: var(--border-thin) solid var(--color-border-subtle);
  border-radius: var(--radius-sm);
  padding: var(--space-8) var(--space-12);
  cursor: pointer;
  font: inherit;
}
.pl-pick-row:hover { border-color: var(--color-accent); }
.pl-pick-name { font-size: var(--text-14); font-weight: var(--weight-semibold); color: var(--color-text-primary); }
.pl-pick-meta { font-size: var(--text-12); color: var(--color-text-muted); }

/* Cold-open state: a deep-linkable screen must offer a way to start. */
.pl-empty { display: flex; flex-direction: column; gap: var(--space-12); align-items: flex-start; }
.pl-empty-actions { display: flex; gap: var(--space-8); flex-wrap: wrap; }

/* 640px, not the 40rem this used to be: 40rem resolves to exactly 640px at the default
   root font size, so this is behaviour-identical for almost everyone — but it was the ONE
   rem breakpoint among 26 px ones, which is precisely the "which width do I reach for?"
   confusion that put a sticky offset on the wrong breakpoint. (Trade-off, recorded so a
   future change is deliberate: a rem breakpoint scales with the reader's font-size setting
   and a px one does not. Going rem is worth considering — but as ONE decision across the
   whole set, never a single rule.) */
@media (max-width: 640px) {
  /* PHONE DENSITY. This used to send all THREE identity fields to their own full-width line — about
     340px of form before a manager reached a single action, times 35 tenants, which is most of what
     made the roster unreadable on a phone. Suite and Leased SF are short and now adjacent in the
     markup, so they PAIR on one line and the wide Tenant field takes the next: two rows, not three.
     Changed HERE rather than by adding a second media block earlier in the file — a media query adds
     no specificity, so an earlier block simply loses to this one and does nothing at all. Measured,
     after guessing wrong twice: one rule for one thing. */
  .pl-in { flex: 1 1 0; min-width: 0; }
  .pl-in--wide { flex: 1 1 100%; }
}
/* The a.k.a. chip's remove control is an inline "×", not a full button. */
.pl-aka .log-btn {
  padding: 0 var(--space-4);
  border: none;
  background: transparent;
  font-size: var(--text-13);
  line-height: 1;
}
/* "Tenant no longer here" — the move-out editor and its summary on a Leases row. */
.pl-row--gone { border-left: var(--border-bar) solid var(--color-border-warning); }
.pl-moveout {
  margin-top: var(--space-12);
  padding: var(--space-12);
  background: var(--color-surface-warning);
  border-radius: var(--radius-md);
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
  align-items: flex-start;
}
.pl-moveout .pl-in { flex: 0 0 auto; }
.pl-moveout-actions { display: flex; gap: var(--space-8); flex-wrap: wrap; }
.pl-moveout-note {
  margin-top: var(--space-8);
  padding-top: var(--space-8);
  border-top: var(--border-thin) solid var(--color-border-subtle);
  font-size: var(--text-12);
  color: var(--color-text-secondary);
}
.pl-warn { color: var(--color-warning-text-strong); font-weight: var(--weight-semibold); }

/* Lease standing — whether this lease's text copy holds up (docs/design-lease-standing.md).
   The chip states its standing IN WORDS; colour is redundant coding and never the signal, so it
   reads the same to a colour-blind manager and in a black-and-white printout. */
.pl-standing {
  display: inline-flex;
  align-items: center;
  gap: var(--space-6);
  font: inherit;
  font-size: var(--text-12);
  font-weight: var(--weight-semibold);
  padding: var(--space-4) var(--space-8);
  border: var(--border-thin) solid var(--color-border-subtle);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  color: var(--color-text-secondary);
  /* At 390px the chip WRAPS onto its own line rather than truncating — the row's meaning must not
     depend on horizontal room, and the page body never scrolls sideways. */
  max-width: 100%;
  text-align: left;
}
.pl-standing > span { min-width: 0; }
/* VISUAL WEIGHT RUNS Check > Not checked > Verified — §4.1's pre-committed fallback, taken 13 Sept
   once the OCR grind made Verified the DEFAULT (75 of 94 linked rows, ~80%) rather than the rare
   mark §4's argument rested on. A mark on four rows in five carries almost no information.
   It stops at QUIET and never goes silent: "no chip" already means "we have nothing to say", and
   that is what the caveat line above the roster explains. A silent Verified would make a blank row
   mean either verified or we-could-not-check — the ambiguity §4 exists to remove, on the 80% of
   rows where the answer is good news. */
.pl-standing--ok { border-color: transparent; background: transparent; color: var(--color-text-muted); }
.pl-standing--muted { color: var(--color-text-muted); }
.pl-standing--warn { border-color: var(--color-border-warning); color: var(--color-warning-text-strong); }
/* A control must LOOK like one: the caret is the signifier, and it is visible at EVERY width
   because a phone has no hover. A chip with nothing to open never gets one. */
.pl-standing--btn { cursor: pointer; transition: border-color 0.15s, color 0.15s; }
.pl-standing--btn:hover { border-color: var(--color-accent); }
.pl-standing-caret { font-size: var(--text-11); opacity: 0.75; }
.pl-standing-panel {
  margin-top: var(--space-12);
  padding: var(--space-12);
  background: var(--color-surface-sunken);
  border-radius: var(--radius-md);
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
  align-items: flex-start;
}
.pl-standing-step { font-size: var(--text-13); color: var(--color-text-secondary); }
/* The NUMBERS live here, never on the row — they are evidence, not a verdict. */
.pl-standing-ev { list-style: none; margin: 0; padding: 0; width: 100%; font-size: var(--text-13); }
.pl-standing-ev li {
  display: flex;
  justify-content: space-between;
  gap: var(--space-12);
  padding: var(--space-4) 0;
  border-bottom: var(--border-thin) solid var(--color-border-subtle);
  color: var(--color-text-secondary);
}
.pl-standing-ev li:last-child { border-bottom: 0; }
.pl-standing-ev strong { color: var(--color-text-heading); white-space: nowrap; }

/* Document inventory — every file behind one lease analysis, with its dates. */
.pl-docs-btn--warn { border-color: var(--color-border-warning); color: var(--color-warning-text-strong); }
.pl-docs {
  margin-top: var(--space-12);
  padding: var(--space-12);
  background: var(--color-surface-sunken);
  border-radius: var(--radius-md);
}
/* Wide content scrolls inside its own container — the page body must never scroll sideways. */
.pl-docs-scroll { overflow-x: auto; }
.pl-doc-table { width: 100%; border-collapse: collapse; font-size: var(--text-13); }
.pl-doc-table th {
  text-align: left;
  font-size: var(--text-11);
  font-weight: var(--weight-semibold);
  text-transform: uppercase;
  letter-spacing: var(--tracking-6);
  color: var(--color-text-secondary);
  white-space: nowrap;
}
.pl-doc-table th, .pl-doc-table td {
  padding: var(--space-6) var(--space-8);
  border-bottom: var(--border-thin) solid var(--color-border-subtle);
  vertical-align: top;
}
.pl-doc-table tbody tr:last-child td { border-bottom: none; }
.pl-doc-row--warn { background: var(--color-surface-warning); }
.pl-doc-title { font-weight: var(--weight-regular); color: var(--color-text-secondary); font-size: var(--text-12); }
.pl-doc-issues {
  margin: var(--space-12) 0 0;
  padding-left: var(--space-20);
  font-size: var(--text-12);
  color: var(--color-warning-text-strong);
}
.pl-doc-issues li { margin-bottom: var(--space-4); }
/* "Ask this lease" — the lease attribute layer read surface (Phase 1). Read-only. */
.pl-attrs {
  margin-top: var(--space-12);
  padding: var(--space-12);
  background: var(--color-surface-sunken);
  border-radius: var(--radius-md);
}
.pl-attrs-intro { margin: 0 0 var(--space-8); }
.pl-attr { padding: var(--space-8) 0; border-top: var(--border-thin) solid var(--color-border-subtle); }
.pl-attr:first-of-type { border-top: none; }
.pl-attr-head { display: flex; gap: var(--space-8); align-items: center; justify-content: space-between; }
.pl-attr-label { font-weight: var(--weight-semibold); font-size: var(--text-13); color: var(--color-text-primary); }
.pl-attr-answer { margin-top: var(--space-8); }
.pl-attr-value { font-size: var(--text-14); color: var(--color-text-primary); margin-bottom: var(--space-6); }
.pl-attr-cite {
  margin: var(--space-6) 0;
  padding: var(--space-4) var(--space-12);
  border-left: var(--border-bar) solid var(--color-border);
  color: var(--color-text-secondary);
  font-style: italic;
  font-size: var(--text-13);
  white-space: pre-wrap;
}
.pl-attr-src { margin-top: var(--space-4); }
.pl-attr-gap { margin-top: var(--space-6); font-size: var(--text-12); }
/* Confidence chip — high reads reassuring, low reads "check me", medium stays neutral. */
.pl-conf {
  display: inline-block;
  padding: var(--space-2) var(--space-8);
  border-radius: var(--radius-pill);
  font-size: var(--text-11);
  font-weight: var(--weight-semibold);
  text-transform: uppercase;
  letter-spacing: var(--tracking-6);
}
.pl-conf--high { background: var(--color-surface-success); color: var(--color-success-text); }
.pl-conf--med  { background: var(--color-surface-neutral); color: var(--color-text-secondary); }
.pl-conf--low  { background: var(--color-surface-warning); color: var(--color-warning-text-strong); }
/* A newly-added roster row, briefly highlighted so the click has a visible result. */
.pl-row--new { box-shadow: var(--shadow-hover); border-left: var(--border-bar) solid var(--color-accent); }
/* Feedback about ONE tenant, rendered on that tenant's row (the screen-level status
   line is off-viewport on a long roster, which reads as "the button did nothing"). */
.pl-row-note {
  margin-top: var(--space-8);
  padding: var(--space-8) var(--space-12);
  background: var(--color-surface-info);
  border-radius: var(--radius-sm);
  font-size: var(--text-12);
  color: var(--color-text-secondary);
  display: flex;
  gap: var(--space-8);
  align-items: center;
  flex-wrap: wrap;
}
.pl-row-note--err { background: var(--color-surface-danger); color: var(--color-danger); }
/* Version promotion (docs/design-version-promotion.md) — a fuller document set is on file for a
   tenant whose lease is already linked. Sits ON the tenant's row, because feedback about ONE
   tenant belongs there and never in the screen-level status line (off-viewport on a long roster). */
.pl-promo {
  margin-top: var(--space-8);
  padding: var(--space-8) var(--space-12);
  background: var(--color-surface-info);
  border-radius: var(--radius-sm);
  font-size: var(--text-12);
  color: var(--color-text-secondary);
  display: flex;
  gap: var(--space-8);
  align-items: center;
  flex-wrap: wrap;
}
.pl-promo--blocked { background: var(--color-surface-muted); }
.pl-promo-diff { flex-basis: 100%; margin-top: var(--space-8); }
.pl-promo-diff--err { color: var(--color-danger); }
.pl-promo-actions { flex-basis: 100%; margin-top: var(--space-8); }
/* A wide table must scroll inside its OWN container — the page body never scrolls sideways. */
.pl-promo-scroll { overflow-x: auto; margin-top: var(--space-8); }
.pl-promo-table { width: 100%; border-collapse: collapse; font-size: var(--text-12); }
.pl-promo-table th,
.pl-promo-table td {
  text-align: left;
  padding: var(--space-4) var(--space-8);
  border-bottom: var(--border-thin) solid var(--color-border-subtle);
  white-space: nowrap;
}
.pl-promo-table th { color: var(--color-text-secondary); font-weight: var(--weight-medium); }
/* The clause behind a changed figure. It is prose in a table of numbers, so give it room and let it
   wrap while the figures stay on one line; the full quote is on the title attribute. */
.pl-promo-table .pl-promo-clause {
  white-space: normal;
  max-width: var(--size-clause);
  color: var(--color-text-secondary);
}
/* The property-level pass (slice C): a reviewed list, never a bulk apply. */
.pl-promo-section { margin-bottom: var(--space-12); }
.pl-promo-list { list-style: none; margin: var(--space-8) 0 0; padding: 0; }
.pl-promo-item {
  display: flex;
  gap: var(--space-8);
  align-items: center;
  flex-wrap: wrap;
  padding: var(--space-6) 0;
  border-bottom: var(--border-thin) solid var(--color-border-subtle);
}
.pl-promo-item--blocked { color: var(--color-text-secondary); }
.pl-promo-item-name { font-weight: var(--weight-medium); color: var(--color-text-primary); }
/* Inline tenant rename on the CAM Rec roster (native prompt() is suppressed in webviews). */
.reco-rename { display: inline-flex; gap: var(--space-6); align-items: center; flex-wrap: wrap; }
/* Trade-name field: keep the "d/b/a" label on the same line as its input — wrapping
   orphans the label above the box and doubles the height of every roster row. */
.reco-dba { flex-wrap: nowrap; }
/* The lease-analysis queue line on the Leases screen: what is running, queued or failed
   across the whole property. Renders nothing when there is nothing in flight. */
.pl-queue { display: flex; gap: var(--space-8); align-items: center; flex-wrap: wrap; width: 100%; margin-top: var(--space-4); }

/* CAM eligibility (TU-003). Quiet when the tenant IS eligible — which is nearly all of
   them — and visibly set apart when they are not, because "this row is not being billed"
   is the kind of fact that must be legible at a glance on a 35-row roster. */
.reco-camstatus { display: inline-flex; gap: var(--space-6); align-items: center; flex-wrap: wrap; }
.reco-camstatus--off {
  padding: 0 var(--space-6); border-radius: var(--radius-sm);
  background: var(--color-surface-warning); color: var(--color-text-secondary);
}
/* Trade name reads as a second line under the legal name, not trailing after the suite. */
.tenant-dba { display: block; }
/* "A new version is ready" — quiet by default, but it must be findable, so it takes the
   accent colour rather than the muted footer treatment. */
.footer-link--update { color: var(--color-accent); font-weight: var(--weight-medium); }
/* Print action inside the statement view — the statement is the thing you print, so the
   action lives with it rather than beside it on the roster. */
.statement-actions { display: flex; justify-content: flex-end; padding: var(--space-8) 0 0; }
/* Sortable table headers (window.TableSort). The caret is a ::after glyph rather than an
   image so it inherits colour and needs no asset; direction comes from aria-sort, which
   is also what a screen reader announces. */
.th-sortable { cursor: pointer; user-select: none; white-space: nowrap; }
.th-sortable:hover { color: var(--color-text-primary); }
/* --border-2 was undefined, so this whole `outline` declaration was DROPPED and a sortable table
   header had no visible focus ring at all — a keyboard-accessibility bug, found by the new
   undefined-token check rather than by anyone looking. A focus ring wants to be plainly visible,
   hence --border-thick. */
.th-sortable:focus-visible { outline: var(--border-thick) solid var(--color-accent); outline-offset: calc(var(--space-4) * -1); }
.th-sortable::after { content: "\2195"; opacity: 0.35; padding-left: var(--space-4); }
.th-sortable[aria-sort="ascending"]::after { content: "\2191"; opacity: 1; }
.th-sortable[aria-sort="descending"]::after { content: "\2193"; opacity: 1; }
/* Inline "name the tenant" on a vacant roster row, and the inline term-override field
   in Lease Intelligence — both replace native prompt(), suppressed in embedded webviews. */
.roster-lease-name { display: inline-flex; gap: var(--space-6); align-items: center; flex-wrap: wrap; }
.intel-override {
  display: flex;
  gap: var(--space-6);
  align-items: center;
  flex-wrap: wrap;
  margin-top: var(--space-8);
}
.intel-override .input { flex: 1 1 12rem; }

/* ── Property gate ─────────────────────────────────────────────────────────
   The prompt shown when a CAM reconciliation, rent roll, audit or lease upload
   starts with no Property in context. Modal (not native prompt(), which embedded
   webviews suppress). Token-only, like everything outside primitives.css. */
.pg { position: fixed; inset: 0; z-index: 1300; display: flex; align-items: center; justify-content: center; padding: var(--space-16); }
.pg-scrim { position: absolute; inset: 0; background: var(--color-scrim); }
.pg-panel {
  position: relative; display: flex; flex-direction: column;
  width: min(38rem, 94vw); max-height: 90vh; overflow-y: auto;
  background: var(--color-surface); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg); padding: var(--space-24);
}
.pg-head { display: flex; align-items: flex-start; gap: var(--space-12); }
.pg-title { flex: 1; min-width: 0; font-size: var(--text-20); color: var(--color-text-heading); }
.pg-close { border: 0; background: none; color: var(--color-text-muted); font-size: var(--text-24); line-height: 1; cursor: pointer; padding: 0 var(--space-4); }
.pg-close:hover { color: var(--color-text-primary); }
.pg-lead { color: var(--color-text-secondary); font-size: var(--text-14); margin-top: var(--space-8); }
.pg-body { display: flex; flex-direction: column; gap: var(--space-20); margin-top: var(--space-20); }
.pg-section-title { font-size: var(--text-12); font-weight: var(--weight-semibold); text-transform: uppercase; letter-spacing: var(--tracking-6); color: var(--color-text-muted); margin-bottom: var(--space-8); }
.pg-list { display: flex; flex-direction: column; gap: var(--space-8); max-height: 18rem; overflow-y: auto; }
.pg-item {
  display: flex; flex-direction: column; gap: var(--space-2); text-align: left; width: 100%;
  background: var(--color-surface-sunken); border: var(--border-thin) solid var(--color-border);
  border-radius: var(--radius-md); padding: var(--space-12) var(--space-14); cursor: pointer;
}
.pg-item:hover { border-color: var(--color-accent); }
.pg-item-name { font-weight: var(--weight-semibold); color: var(--color-text-primary); font-size: var(--text-15); }
.pg-item-meta { color: var(--color-text-secondary); font-size: var(--text-12); }
.pg-empty { color: var(--color-text-secondary); font-size: var(--text-13); }
.pg-create { display: flex; flex-direction: column; gap: var(--space-8); align-items: flex-start; }
.pg-create .input { width: 100%; }
.pg-label { font-size: var(--text-13); color: var(--color-text-primary); font-weight: var(--weight-medium); }
.pg-hint { color: var(--color-text-secondary); font-size: var(--text-12); }
.pg-status { color: var(--color-text-secondary); font-size: var(--text-13); min-height: var(--space-20); margin-top: var(--space-12); }
.pg-status.error { color: var(--color-danger); }
.pg-foot { display: flex; justify-content: flex-end; gap: var(--space-8); margin-top: var(--space-8); }

/* "Filing under <Property>" strip — shown on every screen whose work must be tied
   to a property, so the manager can always see (and change) where it is going. */
.prop-context {
  display: flex; align-items: center; gap: var(--space-8); flex-wrap: wrap;
  background: var(--color-surface-sage); border: var(--border-thin) solid var(--color-border-sage);
  border-radius: var(--radius-md); padding: var(--space-10) var(--space-14);
  margin-bottom: var(--space-16); font-size: var(--text-13); color: var(--color-text-secondary);
}
.prop-context--none { background: var(--color-surface-warning); border-color: var(--color-border-warning); color: var(--color-warning-text); }
.prop-context-name { font-weight: var(--weight-semibold); color: var(--color-text-primary); }
.prop-context--none .prop-context-name { color: var(--color-warning-text); }

/* Unassigned-lease chip on a "Recent leases" row. */
.log-unassigned { color: var(--color-warning-text); font-size: var(--text-12); }

    /* "No team yet — claim it" on a Portfolio row. Legacy projects only: nothing
       recorded who created them, so a human claims one (docs/team-scoping.md). */
    .prop-claim {
      font-family: var(--font-sans);
      font-size: var(--text-11);
      color: var(--color-accent);
      background: var(--color-accent-bg);
      border: var(--border-thin) solid var(--color-border-danger);
      border-radius: var(--radius-pill);
      padding: var(--space-4) var(--space-10);
      cursor: pointer;
      white-space: nowrap;
    }
    .prop-claim:hover { background: var(--color-accent-bg-strong); }
    .prop-claim[disabled] { opacity: 0.6; cursor: default; }
    .prop-claim--err { color: var(--color-danger-strong); }

    /* Who approved a reconciliation, on the row where the decision is made.
       compute-never-auto-approve means a human approves; attribution is what makes
       that claim checkable (migration 0014). */
    .reco-approver {
      font-size: var(--text-11);
      color: var(--color-text-muted);
      margin: var(--space-4) 0 var(--space-2);
    }

    /* A lease-library read that FAILED, distinguished from "no leases linked".
       The two used to look identical, so a backend failure read as data loss. */
    .pl-stat--warn {
      color: var(--color-danger-strong);
      border-color: var(--color-border-danger);
    }
/* "Check these before you compute" — what a build from a completed reconciliation
   workbook could NOT decide for the manager, listed where the build happened. */
.wb-notes {
  margin-top: var(--space-12);
  background: var(--color-surface-warning); border: var(--border-thin) solid var(--color-border-warning);
  border-radius: var(--radius-md); padding: var(--space-12) var(--space-16);
}
.wb-notes h4 { margin: 0 0 var(--space-8); font-size: var(--text-13); color: var(--color-warning-text-strong); }
.wb-notes ul { margin: 0; padding-left: var(--space-20); }
.wb-notes li { font-size: var(--text-13); color: var(--color-text-secondary); line-height: 1.5; margin-bottom: var(--space-6); }
.wb-notes .hint { margin: var(--space-8) 0 0; }
/* ── Calculation input review (Admin) ───────────────────────────────────────
   The reference half of the input ledger (js/cam-inputs.js), rendered for a
   property manager to mark up. A review sheet, not a dashboard: one row per
   number, generous room to write, and the "what we do without it" line given
   the visual weight because that is the thing being judged.
   Design: docs/design-calculation-map.md */
.cir-bar {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--space-8) var(--space-20);
  padding: var(--space-12) var(--space-16);
  background: var(--color-surface-sunken);
  border: var(--border-thin) solid var(--color-border);
  border-radius: var(--radius-sm);
  margin-bottom: var(--space-20);
  font-size: var(--text-13);
}
.cir-bar .label { margin: var(--space-0); }
.cir-bar .input { width: var(--size-232); }
.cir-prog { margin-left: auto; color: var(--color-text-muted); }
.cir-prog strong { color: var(--color-text-primary); }
.cir-sync { flex-basis: 100%; font-size: var(--text-12); color: var(--color-text-muted); }
.cir-sync--warn { color: var(--color-warning-text); }

.cir-group {
  font-family: var(--font-serif);
  font-size: var(--text-18);
  color: var(--color-text-heading);
  margin: var(--space-32) var(--space-0) var(--space-4);
  padding-bottom: var(--space-8);
  border-bottom: var(--border-thin) solid var(--color-border);
}
.cir-group:first-child { margin-top: var(--space-0); }

.cir-row {
  display: grid;
  grid-template-columns: var(--space-28) 1fr;
  gap: var(--space-0) var(--space-12);
  padding: var(--space-18) var(--space-0) var(--space-20);
  border-bottom: var(--border-thin) solid var(--color-border-faint);
}
.cir-n {
  font-size: var(--text-13); font-weight: var(--weight-bold);
  color: var(--color-text-faint); padding-top: var(--space-2);
}
.cir-what {
  font-family: var(--font-serif); font-size: var(--text-16);
  font-weight: var(--weight-bold); color: var(--color-text-heading);
  margin: var(--space-0);
}
.cir-from {
  font-size: var(--text-13); color: var(--color-text-muted);
  margin: var(--space-4) var(--space-0) var(--space-0);
}
/* The judged claim. Amber by default (a silent fallback), neutral where we cannot
   probe at all, danger where we already believe the behaviour is wrong. */
.cir-does {
  margin-top: var(--space-10);
  padding: var(--space-10) var(--space-14);
  background: var(--color-surface);
  border: var(--border-thin) solid var(--color-border);
  border-left: var(--border-bar) solid var(--color-warning-accent);
  border-radius: var(--radius-xs);
  font-size: var(--text-14);
  color: var(--color-text-body);
}
.cir-row--cant .cir-does { border-left-color: var(--color-border-strong); }
.cir-row--known .cir-does {
  border-left-color: var(--color-danger);
  background: var(--color-surface-danger);
}
.cir-doeslbl {
  display: block; font-size: var(--text-10); font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-8); text-transform: uppercase;
  color: var(--color-text-muted); margin-bottom: var(--space-4);
}
.cir-row--known .cir-doeslbl { color: var(--color-danger); }

.cir-verdict { display: flex; flex-wrap: wrap; gap: var(--space-8); margin-top: var(--space-12); }
.cir-v {
  font: inherit; font-size: var(--text-13); font-weight: var(--weight-semibold);
  cursor: pointer; padding: var(--space-6) var(--space-12);
  border: var(--border-thin) solid var(--color-border);
  border-radius: var(--radius-xs);
  background: var(--color-surface); color: var(--color-text-secondary);
}
.cir-v:hover { border-color: var(--color-border-strong); }
.cir-v[aria-pressed="true"][data-cir-v="agree"] {
  background: var(--color-surface-success); border-color: var(--color-border-success); color: var(--color-success-text);
}
.cir-v[aria-pressed="true"][data-cir-v="wrong"] {
  background: var(--color-surface-danger); border-color: var(--color-border-danger); color: var(--color-danger);
}
.cir-v[aria-pressed="true"][data-cir-v="unsure"] {
  background: var(--color-surface-warning); border-color: var(--color-border-warning); color: var(--color-warning-text);
}
.cir-note { margin-top: var(--space-10); resize: vertical; }

/* Phone: the row number is decoration once the column collapses. */
@media (max-width: 560px) {
  .cir-row { grid-template-columns: 1fr; }
  .cir-n { display: none; }
  .cir-bar .input { width: 100%; }
  .cir-prog { margin-left: var(--space-0); }
}

/* Admin section tabs. Its own small strip rather than the property drill-down's
   (PROP_TABS), which is bound to that screen's markup and icon set — worth unifying
   if a third tabbed screen appears. Scrolls sideways on a phone rather than wrapping
   to three rows; the page body never scrolls sideways with it. */
.adm-tabs {
  display: flex; gap: var(--space-4);
  margin-bottom: var(--space-24);
  border-bottom: var(--border-thin) solid var(--color-border);
  overflow-x: auto;
}
.adm-tab {
  font: inherit; font-size: var(--text-14); font-weight: var(--weight-semibold);
  cursor: pointer; white-space: nowrap;
  padding: var(--space-10) var(--space-14);
  background: transparent; color: var(--color-text-muted);
  border: var(--border-thin) solid transparent;
  border-bottom: var(--border-bar) solid transparent;
  border-radius: var(--radius-xs) var(--radius-xs) var(--space-0) var(--space-0);
}
.adm-tab:hover { color: var(--color-text-primary); }
.adm-tab.is-active {
  color: var(--color-text-primary);
  border-bottom-color: var(--color-accent);
}
.adm-panel[hidden] { display: none !important; }
.screen-header--sub { margin-bottom: var(--space-16); }
.screen-header--sub h3 {
  font-family: var(--font-serif); font-size: var(--text-20);
  color: var(--color-text-heading); margin: var(--space-0) var(--space-0) var(--space-4);
}

/* Who answered, when, and what it replaced. */
.cir-meta {
  font-size: var(--text-12); color: var(--color-text-muted);
  margin: var(--space-8) var(--space-0) var(--space-0);
}
.cir-changed { color: var(--color-warning-text); }
.cir-export-box { margin-bottom: var(--space-16); font-family: var(--font-mono); font-size: var(--text-12); }

/* Calibration: answers whose assumption has since changed. Warning, not danger — the
   answer isn't wrong, the question moved. */
.cir-stale {
  padding: var(--space-12) var(--space-16);
  margin-bottom: var(--space-20);
  background: var(--color-surface-warning);
  border: var(--border-thin) solid var(--color-border-warning);
  border-left: var(--border-bar) solid var(--color-warning-accent);
  border-radius: var(--radius-xs);
  font-size: var(--text-13);
  color: var(--color-warning-text-strong);
}
.cir-stale a { color: inherit; }
.cir-stale[hidden] { display: none !important; }
.cir-row--stale { background: var(--color-surface-warning); }
.cir-stale-tag { color: var(--color-warning-text); font-weight: var(--weight-semibold); }
.cir-row--flash { box-shadow: var(--shadow-focus); }

/* A caveat on what a state can and cannot tell you. Sits inside the claim box because it
   qualifies the claim — an `inactive` row carries no call to action, so this is the only
   thing telling a reviewer the state may not mean what it appears to. */
.cir-caveat {
  margin: var(--space-8) var(--space-0) var(--space-0);
  font-size: var(--text-12);
  color: var(--color-text-muted);
}

/* ── Admin › Data quality (docs/design-portfolio-lease-analysis.md §3c) ──────
   The corpus-trust panel: what the lease library actually contains and how much of it is
   solid. Single column at every width by design — it is a read, not a dashboard, and the
   stat grid is the only thing that flows. */
.dq-bar {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--space-8) var(--space-16);
  margin-bottom: var(--space-16);
}
.dq-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(var(--size-stat), 1fr));
  gap: var(--space-12);
  margin-bottom: var(--space-20);
}
.dq-stat {
  padding: var(--space-12) var(--space-16);
  background: var(--color-surface-sunken);
  border: var(--border-thin) solid var(--color-border);
  border-radius: var(--radius-sm);
}
.dq-stat-v { font-size: var(--text-22); color: var(--color-text-primary); }
.dq-stat-l { font-size: var(--text-12); color: var(--color-text-muted); }

.dq-block { margin-bottom: var(--space-20); }
.dq-block h4 { margin: var(--space-0) var(--space-0) var(--space-8); }
/* A block naming something to FIX is marked with a rule on the leading edge rather than a
   fill: the panel is mostly prose, and a coloured background on a text block reads as an
   alert banner for the whole page. */
.dq-block--warn {
  border-left: var(--border-thick) solid var(--color-warning-accent);
  padding-left: var(--space-12);
}
.dq-list {
  margin: var(--space-8) var(--space-0) var(--space-0);
  padding-left: var(--space-20);
  font-size: var(--text-13);
  color: var(--color-text-primary);
}
.dq-list li { margin-bottom: var(--space-4); }
.dq-lease { color: var(--color-text-primary); text-decoration: none; cursor: default; }

/* What this tenant's lease says that the Verify card isn't doing yet — the third routed
   surface for a lease contradiction (docs/design-portfolio-lease-analysis.md §7). A rule on
   the leading edge rather than a fill: the card is already dense with fields, and a coloured
   block here would compete with the ⚠ flags above it. */
.cam-lease-note {
  margin: var(--space-8) var(--space-0);
  padding: var(--space-6) var(--space-10);
  border-left: var(--border-thick) solid var(--color-warning-accent);
}
.cam-lease-note p { margin: var(--space-0) var(--space-0) var(--space-4); }
.cam-lease-note ul {
  margin: var(--space-0);
  padding-left: var(--space-16);
  font-size: var(--text-13);
  color: var(--color-text-primary);
}
.cam-lease-note li { margin-bottom: var(--space-4); }

/* ── Glossary ──────────────────────────────────────────────────────────────────
   Two surfaces over one term list (js/glossary.js): the #glossary screen, and the
   inline definitions that expand next to the copy using the word.

   Unscoped (not theme-new.css) for the same reason the profile menu is: there is one
   markup, so classic renders it properly for the days it has left.

   TRAP (third time in this codebase — the nav profile menu, then the bulk-upload folder
   slot): an author `display` BEATS the UA's `[hidden] { display: none }`. `.gl-def` is
   toggled by the `hidden` attribute and needs `display: block`, so without the
   `[hidden]` rule below every definition renders permanently expanded. A screenshot
   would not catch it — screenshots only ever photograph it open. */
.gl-screen { max-width: var(--size-prose); }
.gl-search { width: 100%; margin-bottom: var(--space-16); }
.gl-list { display: flex; flex-direction: column; gap: var(--space-20); }

.gl-entry {
  border-left: var(--border-thick) solid var(--color-border-subtle);
  padding-left: var(--space-16);
}
.gl-entry:focus { outline: none; }
.gl-entry:focus-visible { box-shadow: var(--shadow-focus); }
.gl-entry-term {
  font-size: var(--text-16);
  font-weight: var(--weight-semibold);
  color: var(--color-text-heading);
  margin: 0 0 var(--space-2);
}
.gl-entry-short {
  font-size: var(--text-14);
  color: var(--color-text-secondary);
  margin: 0 0 var(--space-8);
}
.gl-entry-body {
  font-size: var(--text-14);
  color: var(--color-text-body);
  margin: 0 0 var(--space-8);
}
/* "Not the same as" is the highest-value line in most entries — every term here has a
   specific thing people mistake it for — so it is set apart rather than run together. */
.gl-entry-not {
  font-size: var(--text-13);
  color: var(--color-text-muted);
  background: var(--color-surface-sunken);
  border-radius: var(--radius-sm);
  padding: var(--space-8) var(--space-10);
  margin: 0 0 var(--space-8);
}
.gl-entry-see { font-size: var(--text-13); color: var(--color-text-muted); margin: 0; }
.gl-see { color: var(--color-accent); text-decoration: none; margin-right: var(--space-8); }
.gl-see:hover { text-decoration: underline; }
.gl-foot { margin-top: var(--space-24); }

/* Inline term: reads as part of the sentence, marked as askable by a dotted underline
   rather than a button chrome that would break the line. */
.gl-termwrap { display: inline; }
.gl-term {
  display: inline;
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  color: inherit;
  text-decoration: underline dotted;
  text-underline-offset: var(--space-2);
  cursor: help;
}
.gl-term:hover, .gl-term:focus-visible { color: var(--color-accent); }
.gl-def {
  display: block;
  margin: var(--space-8) 0;
  padding: var(--space-10) var(--space-12);
  background: var(--color-surface-sunken);
  border-left: var(--border-thick) solid var(--color-border-accent);
  border-radius: var(--radius-sm);
  font-size: var(--text-13);
  color: var(--color-text-body);
}
.gl-def[hidden] { display: none; }   /* see the TRAP note above — do not remove */
.gl-def-not { display: block; margin-top: var(--space-6); color: var(--color-text-muted); }
.gl-def-more { display: inline-block; margin-top: var(--space-6); color: var(--color-accent); text-decoration: none; }
.gl-def-more:hover { text-decoration: underline; }
.gl-link { color: var(--color-accent); text-decoration: none; }
.gl-link:hover { text-decoration: underline; }

/* An inline text button that reads as a link inside a sentence — used where the app
   OFFERS something and the manager accepts with one tap (the mid-year sale's "use as
   seller" suggestion). A real <button>, because it performs an action rather than
   navigating; styled as a link so it does not break the line it sits in. */
.link-btn {
  display: inline;
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  color: var(--color-accent);
  text-decoration: underline;
  cursor: pointer;
}
.link-btn:hover, .link-btn:focus-visible { color: var(--color-accent-hover); }

/* Cam at the denominator wall (docs/cam-touchpoints.md #1). Sits INSIDE the warning banner, so
   it takes no border or fill of its own — a second bordered box inside a bordered box reads as
   two problems. A hairline rule separates Cam's explanation from the app's refusal above it. */
.cam-denom {
  margin-top: var(--space-12);
  padding-top: var(--space-12);
  border-top: var(--border-thin) solid var(--color-border-warning);
}
.cam-denom p {
  margin: var(--space-0) var(--space-0) var(--space-6);
  font-size: var(--text-14);
  color: var(--color-warning-text);
}
.cam-denom p:last-child { margin-bottom: var(--space-0); }
.cam-denom-fig { font-size: var(--text-13); }

/* Cam during a long extraction (docs/cam-touchpoints.md #3). Sits in the slot the rotating
   marketing tips used to occupy. Static — no rotation, no animation: the point is that it is
   work to read, not something to watch. Left-aligned prose over a centred tip, because a list
   of things to do is scanned, not admired. */
.cam-wait { text-align: left; }
.cam-wait-lead {
  margin: var(--space-0) var(--space-0) var(--space-6);
  font-size: var(--text-13);
  color: var(--color-text-secondary);
}
.cam-wait-list {
  margin: var(--space-0);
  padding-left: var(--space-20);
  font-size: var(--text-14);
  color: var(--color-text-primary);
}
.cam-wait-list li { margin-bottom: var(--space-4); }
.cam-wait-more { color: var(--color-text-muted); }

/* Cam explaining a medium-confidence match (docs/cam-touchpoints.md #4). Sits inside the match
   card, so no border or fill of its own — a hairline above it separates the reasoning from the
   row's own facts. Deliberately small: it is there to be read once, on the row that needs it,
   not to make every row heavier. */
.cam-why {
  margin-top: var(--space-8);
  padding-top: var(--space-8);
  border-top: var(--border-thin) solid var(--color-border);
}
.cam-why p {
  margin: var(--space-0) var(--space-0) var(--space-4);
  font-size: var(--text-13);
  color: var(--color-text-secondary);
}
.cam-why p:last-child { margin-bottom: var(--space-0); }

/* Cam showing the clause an audit Check rests on (docs/cam-touchpoints.md #5). A quote, styled
   as one — the point is that these are the lease's words, not ours. */
.cam-clause-ev { margin-top: var(--space-8); }
.cam-clause-ev blockquote {
  margin: var(--space-6) var(--space-0) var(--space-0);
  padding: var(--space-6) var(--space-10);
  border-left: var(--border-thick) solid var(--color-accent);
  background: var(--color-surface);
  color: var(--color-text-primary);
  font-size: var(--text-13);
}

/* ── Operating rules tab (docs/design-operating-rules.md) ──────────────────────────────────────
   The building-level view of what these leases oblige the building to do. Token-only, and it obeys
   the mobile hard rule: the wide comparison table scrolls inside its OWN overflow-x container while
   the cards take over at phone width — the page body must never scroll sideways. */
.ops-wrap { padding: var(--space-4) var(--space-0) var(--space-16); }
.ops-h {
  margin: var(--space-0) var(--space-0) var(--space-6);
  font-size: var(--text-18);
  font-weight: var(--weight-semibold);
  color: var(--color-text-primary);
}
.ops-h4 {
  margin: var(--space-0) var(--space-0) var(--space-8);
  font-size: var(--text-14);
  font-weight: var(--weight-semibold);
  color: var(--color-text-primary);
}
.ops-lead, .ops-cov, .ops-sub {
  margin: var(--space-0) var(--space-0) var(--space-12);
  font-size: var(--text-13);
  color: var(--color-text-secondary);
}
.ops-status { font-size: var(--text-13); color: var(--color-text-secondary); }
.ops-status:empty { display: none; }
.ops-none { color: var(--color-text-muted); }
.ops-warn { color: var(--color-danger); }

/* The Monday-morning tiles: one FACT each, with its basis underneath. */
.ops-day { margin-bottom: var(--space-16); }
.ops-tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(var(--size-stat), 1fr));
  gap: var(--space-8);
  margin-bottom: var(--space-12);
}
.ops-tile {
  padding: var(--space-10);
  border: var(--border-thin) solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
}
.ops-tile-l {
  font-size: var(--text-11);
  font-weight: var(--weight-medium);
  color: var(--color-text-muted);
  text-transform: uppercase;
}
.ops-tile-v {
  margin-top: var(--space-4);
  font-size: var(--text-20);
  font-weight: var(--weight-semibold);
  color: var(--color-text-primary);
}
.ops-tile-n { margin-top: var(--space-4); font-size: var(--text-12); color: var(--color-text-secondary); }

/* A tile with somewhere to go is a real button — same box, so the row does not reflow when one
   tile is navigable and its neighbour isn't. `text-align: left` because a <button> centres by
   default and would misalign it against the plain <div> tiles beside it. */
.ops-tile--go {
  text-align: left;
  width: 100%;
  cursor: pointer;
  font: inherit;
  color: inherit;
}
.ops-tile--go:hover { border-color: var(--color-accent); }
.ops-tile--go:focus-visible { outline: var(--border-thick) solid var(--color-accent); outline-offset: var(--space-2); }
/* ALWAYS VISIBLE, never hover-revealed: a phone has no hover, and this is the only thing marking
   the tile as clickable (the hub's rule). */
.ops-tile-go {
  display: block;
  margin-top: var(--space-6);
  font-size: var(--text-11);
  font-weight: var(--weight-medium);
  color: var(--color-accent);
}
/* The holiday-list sentence renders in the danger colour, which reads as a link — so where it IS
   one, it looks like the text it replaces rather than like a button. */
.ops-inline-go {
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: var(--color-warning);
  text-decoration: underline;
  cursor: pointer;
}
.ops-inline-go:focus-visible { outline: var(--border-thin) solid var(--color-accent); }
/* The filter banner carries the count AND the way back, in one place, above the rows it describes. */
.ops-focus {
  margin: var(--space-12) 0 var(--space-8);
  padding: var(--space-8) var(--space-10);
  border-left: var(--border-bar) solid var(--color-accent);
  background: var(--color-surface-sunken);
  font-size: var(--text-13);
  color: var(--color-text-secondary);
}

/* The comparison table. Its own scroll container, never the body's. */
.ops-tablewrap { overflow-x: auto; margin-bottom: var(--space-16); }
.ops-table { width: 100%; border-collapse: collapse; font-size: var(--text-13); }
/* The requirement is the row's content, so it gets the width — but a verbatim-ish obligation runs to
   a couple of hundred characters, and letting it size the column pushes every short column off the
   right edge. Bounded and wrapping: the table already scrolls inside .ops-tablewrap, and the full
   text is in the detail panel. */
.ops-table td.ops-req {
  /* --size-clause already exists for exactly this: "a verbatim lease quote sitting in a table of
     figures". Reused rather than minting a token, per the design-system rule. */
  min-width: var(--size-clause);
  max-width: var(--size-clause);
  white-space: normal;
  color: var(--color-text-primary);
}
.ops-table th, .ops-table td {
  padding: var(--space-6) var(--space-8);
  text-align: left;
  border-bottom: var(--border-thin) solid var(--color-border);
  white-space: nowrap;
}
.ops-table th {
  font-size: var(--text-11);
  font-weight: var(--weight-semibold);
  color: var(--color-text-muted);
  text-transform: uppercase;
}
.ops-row { cursor: pointer; }
.ops-row:hover, .ops-row:focus-visible { background: var(--color-canvas); }
.ops-row--on { background: var(--color-canvas); }
.ops-conf { font-size: var(--text-11); color: var(--color-text-secondary); }
.ops-conf--low { color: var(--color-danger); }

/* ── Grouped by tenant (tenant outer, scheduled-first inner) ──────────────────────────────────
   MEASURED: 54 conditioning rules render 4,476px — five desktop screens — as one flat table, and
   want 640px more width than the container has. Collapsed groups turn that into one line per lease.
   The phone has grouped its cards since the card view shipped; this is the same treatment for the
   table, which never got it because it was gated behind the 640px breakpoint.

   THE ROWS ARE TOGGLED BY THE `hidden` ATTRIBUTE, SO THEY NEED THEIR OWN [hidden] RULE. An author
   `display` beats the UA's `[hidden] { display: none }` — that has now cost this codebase two bugs
   (the nav profile menu stayed permanently open; the bulk folder slot stayed visible and inert), and
   `.ops-table tr` inherits a table display from the UA which any future author rule would override.
   Stated explicitly so a later `display` declaration on a row cannot silently un-hide a whole
   collapsed library. */
.ops-table tr[hidden] { display: none; }

.ops-grouphead > th { padding: 0; border-bottom: var(--border-thin) solid var(--color-border); }
.ops-groupbtn {
  display: flex;
  align-items: baseline;
  gap: var(--space-8);
  width: 100%;
  padding: var(--space-8);
  background: var(--color-canvas);
  border: 0;
  text-align: left;
  cursor: pointer;
  font: inherit;
  color: var(--color-text-primary);
}
.ops-groupbtn:hover, .ops-groupbtn:focus-visible { background: var(--color-surface-muted); }
.ops-group-who { font-weight: var(--weight-semibold); font-size: var(--text-13); }
/* The CONCLUSION for this lease — hours, band, count. A manager who never opens a group still
   learns the finding; a summary that hides it is the trap the Portfolio row chips already named. */
.ops-group-bits { font-size: var(--text-12); color: var(--color-text-secondary); }
.ops-group-caret {
  color: var(--color-text-muted);
  font-size: var(--text-11);
  transition: transform 0.15s;
  display: inline-block;
}
.ops-group-caret::before { content: '\25B8'; }
.ops-groupbtn[aria-expanded="true"] .ops-group-caret { transform: rotate(90deg); }

/* The divider that turns a line of em-dashes into a finding: these rules state no day or hour
   because they are PERMANENT obligations, not because anything is missing. */
.ops-subhead > td {
  padding: var(--space-4) var(--space-8) var(--space-4) var(--space-20);
  font-size: var(--text-11);
  color: var(--color-text-muted);
  text-transform: uppercase;
  letter-spacing: var(--tracking-8);
  white-space: normal;
}
.ops-row--standing td:first-child { box-shadow: inset var(--border-thick) 0 0 0 var(--color-border); }

.ops-viewtoggle { margin: 0 0 var(--space-8); }
/* The sentence that replaces the columns carrying one value — or none. Both are findings; see
   opsColNoteHtml for why they get different wording rather than a silent drop. */
.ops-colnote { margin: 0 0 var(--space-8); }

/* Phone cards — shown only under 640px, where the table is hidden. */
.ops-cards { display: none; }
.ops-card {
  display: block;
  width: 100%;
  margin-bottom: var(--space-8);
  padding: var(--space-10);
  border: var(--border-thin) solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  text-align: left;
  cursor: pointer;
}
.ops-card--on { border-color: var(--color-accent); }
.ops-card-top { display: flex; justify-content: space-between; gap: var(--space-8); }
.ops-card-prem { font-size: var(--text-14); font-weight: var(--weight-semibold); color: var(--color-text-primary); }
.ops-card-dom { font-size: var(--text-12); color: var(--color-text-muted); }
.ops-card-req { margin-top: var(--space-4); font-size: var(--text-13); color: var(--color-text-primary); }
.ops-card-meta { margin-top: var(--space-6); display: flex; flex-wrap: wrap; gap: var(--space-8); }
.ops-card-meta span { font-size: var(--text-12); color: var(--color-text-secondary); }

/* The detail panel — a PANEL, deliberately not an inline table row: TableSort reorders <tbody>
   rows independently and would tear a detail away from the row it belongs to. */
.ops-detail:empty { display: none; }
.ops-panel {
  margin-top: var(--space-16);
  padding: var(--space-12);
  border: var(--border-thin) solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  box-shadow: var(--shadow-sm);
}
.ops-panel-head { display: flex; justify-content: space-between; align-items: baseline; gap: var(--space-8); }

/* TRIGGER -> REQUIREMENT -> RESPONSIBLE -> VERIFICATION, read as the rule it is. */
.ops-rule {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: var(--space-4) var(--space-8);
  margin: var(--space-8) var(--space-0) var(--space-12);
}
.ops-rule-step { display: contents; }
.ops-step-l {
  font-size: var(--text-11);
  font-weight: var(--weight-medium);
  color: var(--color-text-muted);
  text-transform: uppercase;
}
.ops-step-v { font-size: var(--text-13); color: var(--color-text-primary); }

/* The three claim tiers, kept visibly apart: a cited fact, an inference, and a check. */
.ops-tier { margin-top: var(--space-12); padding-top: var(--space-8); border-top: var(--border-thin) solid var(--color-border); }
.ops-tier-l {
  font-size: var(--text-11);
  font-weight: var(--weight-semibold);
  color: var(--color-text-muted);
  text-transform: uppercase;
  margin-bottom: var(--space-6);
}
.ops-tier-p { margin: var(--space-0); font-size: var(--text-13); color: var(--color-text-primary); }
.ops-kvs {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: var(--space-2) var(--space-8);
  margin: var(--space-0);
}
.ops-kv { display: contents; }
.ops-kv dt { font-size: var(--text-12); color: var(--color-text-muted); }
.ops-kv dd { margin: var(--space-0); font-size: var(--text-13); color: var(--color-text-primary); }
.ops-quote {
  margin: var(--space-8) var(--space-0) var(--space-0);
  padding: var(--space-6) var(--space-10);
  border-left: var(--border-thick) solid var(--color-accent);
  background: var(--color-canvas);
  font-size: var(--text-13);
  color: var(--color-text-primary);
}
.ops-cite { margin-top: var(--space-4); font-size: var(--text-11); color: var(--color-text-muted); }
.ops-checks { margin: var(--space-0); padding-left: var(--space-16); }
.ops-checks li { font-size: var(--text-13); color: var(--color-text-primary); margin-bottom: var(--space-4); }

.ops-qs, .ops-needs { margin-top: var(--space-16); }
.ops-qlist { margin: var(--space-0); padding-left: var(--space-16); }
.ops-qlist li { margin-bottom: var(--space-6); }
.ops-q-what { font-size: var(--text-13); color: var(--color-text-primary); }
.ops-q-who { display: block; font-size: var(--text-11); color: var(--color-text-muted); }

.ops-actions { margin-top: var(--space-16); display: flex; flex-wrap: wrap; gap: var(--space-8); align-items: center; }
.ops-note { font-size: var(--text-12); color: var(--color-text-secondary); flex: 1 1 100%; }
.ops-note--err { color: var(--color-danger); }

/* Phone: cards replace the table outright. 640px is one of the canonical breakpoints. */
@media (max-width: 640px) {
  .ops-tablewrap { display: none; }
  .ops-cards { display: block; }
  .ops-tiles { grid-template-columns: 1fr 1fr; }
  /* One column on a phone: the label sits above its value rather than beside it. */
  .ops-rule, .ops-kvs { grid-template-columns: 1fr; gap: var(--space-2); }
  .ops-rule-step { display: block; margin-bottom: var(--space-6); }
  /* EXCEPT the per-lease grid, which has no card equivalent. The rule above hides every
     .ops-tablewrap because each of those tables ships a card view; this one does not, so hiding it
     deleted the surface that answers "do all my leases state business hours?" from the phone
     entirely — heading and sentence rendered, grid gone, nothing said. It is scanned DOWN a column,
     so a card view would destroy the comparison it exists for: it scrolls sideways in its own
     container instead, which is the documented allowance for a wide table. */
  .ops-tablewrap.ops-factwrap { display: block; }
  /* The view toggle and the column note both describe the TABLE — which is display:none here — so on
     a phone they were a control and a caption for something not on the page. "Show as one flat list
     (sortable)" pointing at nothing is the dead-control shape this codebase keeps paying for; a
     control that cannot do anything is worse than one not offered (the same call as
     folderPickSupported hiding the folder slot on iPad). Nothing is lost: the cards carry the same
     rules, grouped by the same tenant key and ordered scheduled-first by the same function. */
  .ops-viewtoggle, .ops-colnote { display: none; }
}

/* Progress for the operating-terms read (docs/design-operating-rules.md). DETERMINATE: the number of
   leases is known up front, so a spinner would be discarding information the manager wants — "8 of
   12" answers "how much longer?" and a spinner does not. It sits beside the button that started the
   work, which is where they are already looking. Shape follows the shipped .prop-progress bar. */
.ops-prog {
  display: block;
  width: 100%;
  max-width: var(--size-232);
  height: var(--space-4);
  margin-bottom: var(--space-4);
  background: var(--color-surface-muted);
  border-radius: var(--radius-pill);
  overflow: hidden;
}
.ops-prog-fill {
  display: block;
  height: 100%;
  background: var(--color-accent);
  border-radius: var(--radius-pill);
  transition: width 0.15s ease;
}
.ops-prog-l { font-size: var(--text-12); color: var(--color-text-secondary); }

/* Per-lease read account (docs/design-operating-rules.md). "1 of 12 state operating terms" is only
   trustworthy if a manager can see WHY the other eleven said nothing — a genuinely silent lease and
   a read that looked in the wrong place must not look identical. Collapsed by default: silence is
   the default state, and the roll-up above is what most visits want. */
/* The next step reads as guidance, not as another statement of the problem: indented under the gap,
   and in the body colour so it is the line the eye lands on second rather than more grey detail. */
.ops-q-step {
  display: block;
  margin-top: var(--space-4);
  padding-left: var(--space-12);
  /* --border-bar is the project idiom for a left rule marking a callout (used in five places). */
  border-left: var(--border-bar) solid var(--color-border);
  font-size: var(--text-13);
  color: var(--color-text-primary);
}
/* The document-inventory caution. Above the table it qualifies, and in the warning colour — a
   caveat a manager reads AFTER the conclusion is not a caveat. */
.pl-doc-caution {
  margin: var(--space-0) var(--space-0) var(--space-12);
  padding: var(--space-8) var(--space-12);
  border-left: var(--border-bar) solid var(--color-warning);
  background: var(--color-surface-sunken);
  font-size: var(--text-13);
  color: var(--color-text-primary);
}
.ops-facts { margin-bottom: var(--space-24); }
/* The grid is a disclosure on a phone (see opsFactsHtml). Its summary is a heading plus the
   conclusion sentence, so it must lay out as a block rather than a marker-and-one-line. */
.ops-facts-sum { cursor: pointer; }
.ops-facts-sum .ops-h4 { display: inline; }
.ops-facts-sum .ops-sub { display: block; margin-top: var(--space-2); }
/* The per-lease grid is scanned DOWN a column ("which leases have no hours?"), so the tenant column
   is a row header and stays legible while the fact cells stay narrow. */
.ops-facttable th[scope="row"] {
  text-align: left;
  font-weight: var(--weight-medium);
  color: var(--color-text-primary);
  white-space: normal;
  max-width: var(--size-stat);
}
.ops-facttable td.ops-fact { white-space: normal; }
/* Four columns squeezed into 390px wrap every cell to three lines — 122px per row, 1,592px for
   twelve leases, which pushed the rules three screens down. The grid has its own horizontal
   scroller (.ops-factwrap), so it keeps its natural width and scrolls sideways instead: same
   information, a fifth of the height, and the down-a-column scan intact. */
.ops-facttable { min-width: var(--size-factgrid); }
/* The section reference sits under the value: it is the "how do we know?" a defensible answer needs,
   and a phone has no hover to reveal a title attribute. */
.ops-fact-src {
  display: block;
  font-size: var(--text-11);
  color: var(--color-text-secondary);
}
/* WHICH SERVICE this rate prices, when it is not the column's own — an elevator charge sitting under
   "After-hours" is true and is not the conditioning rate. Inline with the value rather than under it
   like the citation, because it qualifies the NUMBER; the citation answers a different question. */
.ops-fact-qual {
  margin-left: var(--space-4);
  font-size: var(--text-11);
  color: var(--color-text-secondary);
}
.ops-reads { margin-top: var(--space-16); }
.ops-reads summary {
  cursor: pointer;
  font-size: var(--text-13);
  font-weight: var(--weight-medium);
  color: var(--color-text-primary);
}
.ops-readlist { margin: var(--space-8) var(--space-0) var(--space-0); padding: var(--space-0); list-style: none; }
.ops-read {
  padding: var(--space-6) var(--space-0);
  border-bottom: var(--border-thin) solid var(--color-border);
}
.ops-read-who { display: block; font-size: var(--text-13); color: var(--color-text-primary); }
.ops-read-bits { display: block; font-size: var(--text-12); color: var(--color-text-secondary); }
.ops-read-said, .ops-read-gap {
  display: block;
  margin-top: var(--space-2);
  font-size: var(--text-12);
  color: var(--color-text-muted);
}

/* Where you have latitude — the discretion block. It states lease FACTS with citations and never an
   action, so it is styled as a reference block rather than as an alert: no danger colour, no warning
   iconography. The only emphasised element is the strictest stated value, because that is the one a
   manager cannot go below. Token-only, and every token here is already used elsewhere in this file. */
.ops-h3 {
  margin: var(--space-16) var(--space-0) var(--space-4);
  font-size: var(--text-14);
  font-weight: var(--weight-semibold);
  color: var(--color-text-primary);
}
.ops-ul { margin: var(--space-8) var(--space-0) var(--space-0) var(--space-16); padding: var(--space-0); }
.ops-ul li { font-size: var(--text-13); color: var(--color-text-primary); margin-bottom: var(--space-6); }
.ops-disc { display: grid; gap: var(--space-12); margin-top: var(--space-8); }
.ops-disc-row {
  padding: var(--space-12);
  background: var(--color-surface-sunken);
  border-left: var(--border-bar) solid var(--color-border);
}
.ops-disc-h {
  font-size: var(--text-13);
  font-weight: var(--weight-semibold);
  color: var(--color-text-primary);
  margin-bottom: var(--space-6);
}
.ops-disc-strict { font-weight: var(--weight-regular); color: var(--color-accent); font-size: var(--text-12); }
.ops-disc-v, .ops-disc-l, .ops-disc-u {
  font-size: var(--text-13);
  color: var(--color-text-primary);
  margin-bottom: var(--space-4);
}
/* READ-AND-SILENT and NOT-READ must not look alike: one is a finding about the lease, the other is
   the absence of a finding. The unread line is muted because it supports no claim at all. */
.ops-disc-u { color: var(--color-text-muted); }
.ops-disc-who { font-size: var(--text-12); color: var(--color-text-secondary); }
.ops-disc-gate {
  margin-top: var(--space-8);
  font-size: var(--text-12);
  color: var(--color-text-secondary);
  border-top: var(--border-thin) solid var(--color-border);
  padding-top: var(--space-6);
}

/* .ops-sys and .ops-other carry their OWN summary/spacing rules rather than borrowing .ops-reads:
   reusing that class made it match three elements and broke four strict-mode locators in unrelated
   tests, which is the third time that mistake has been made in this file. */
.ops-sys, .ops-other, .ops-lat { margin-top: var(--space-16); }
.ops-sys > summary, .ops-other > summary, .ops-lat > summary {
  cursor: pointer;
  font-size: var(--text-13);
  font-weight: var(--weight-medium);
  color: var(--color-text-primary);
}

/* Which suites share an air handler — the DECLARATION panel. Styled as a form, not an alert: it is
   asking for a fact, and the surrounding copy already says that nothing here is inferred. */
.ops-sys-acts { display: flex; flex-wrap: wrap; gap: var(--space-8); margin: var(--space-8) var(--space-0); }
.ops-sys-grid { display: grid; gap: var(--space-8); }
.ops-sys-row { display: flex; align-items: center; gap: var(--space-8); flex-wrap: wrap; }
.ops-sys-l {
  /* --size-stat is the existing "one number-and-label tile" width and is the right order of
     magnitude for a suite label; reusing it beats minting a token for one panel. */
  min-width: var(--size-stat);
  font-size: var(--text-13);
  color: var(--color-text-primary);
}
.ops-sys-i { max-width: var(--size-stat); }

/* The latitude block on a PHONE. The tenant lists (.ops-disc-who) wrap to several lines each and
   made this block 686px tall on a 780px screen, pushing the rules three screens down. The counts
   are the decision-relevant part and they stay; the names live in the rules below, which is where
   you go to act on them. NOT a tooltip — a phone has no hover, so a hint may only ADD meaning,
   never carry it (the Portfolio chip rule). 560px is the canonical breakpoint where --size-nav
   shrinks. */
.ops-disc-mob { display: none; }
@media (max-width: 560px) {
  .ops-disc-who { display: none; }
  .ops-disc-mob {
    display: block;
    font-size: var(--text-12);
    color: var(--color-text-secondary);
    margin-top: var(--space-4);
  }
}

/* Phone card GROUPS (one per lease). Above ~12 cards a single column is ten screens of scrolling on
   a real payload, so the cards collapse per tenant and the summary carries that lease's stated
   hours — the conclusion stays visible whether or not the group is opened. Desktop never sees this:
   .ops-cards is display:none above the phone breakpoint. */
.ops-cardgroup {
  border-bottom: var(--border-thin) solid var(--color-border);
  padding-bottom: var(--space-4);
  margin-bottom: var(--space-4);
}
.ops-cardgroup-sum {
  cursor: pointer;
  padding: var(--space-10) var(--space-2);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-8);
  align-items: baseline;
  justify-content: space-between;
}
.ops-cardgroup-who {
  font-size: var(--text-14);
  font-weight: var(--weight-medium);
  color: var(--color-text-primary);
}
.ops-cardgroup-bits {
  font-size: var(--text-12);
  color: var(--color-text-secondary);
}

/* ── Setup guide (TU-008 / TU-009) ─────────────────────────────────────────────
   One end-to-end setup per property: a progress bar, then one row per stage —
   mark · label + state word · one-line detail (+ the per-tenant list on the lease
   stages) · ONE primary action. Single column at every width so a phone never scrolls
   sideways. Unscoped: the guide is the front door in both UIs. Token-only. */
.sg-body { max-width: var(--size-prose); }
.sg-progress {
  height: var(--space-6); border-radius: var(--radius-pill);
  background: var(--color-surface-sunken); overflow: hidden; margin-bottom: var(--space-20);
}
.sg-progress-bar { display: block; height: 100%; background: var(--color-success); }
.sg-stages { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-10); }
.sg-stage {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: var(--space-12); align-items: start;
  padding: var(--space-14); border: var(--border-thin) solid var(--color-border-subtle);
  border-left: var(--border-accent) solid var(--color-border-slate);
  border-radius: var(--radius-md); background: var(--color-surface);
}
.sg-stage.is-next { border-left-color: var(--color-accent); box-shadow: var(--shadow-sm); }
.sg-stage.is-attention { border-left-color: var(--color-warning-accent); }
.sg-stage.is-working { border-left-color: var(--color-slate-border); }
.sg-stage.is-done { border-left-color: var(--color-success); }
.sg-stage.is-blocked { opacity: 0.72; }
.sg-mark {
  width: var(--space-28); height: var(--space-28); border-radius: var(--radius-circle);
  display: inline-flex; align-items: center; justify-content: center;
  font-size: var(--text-12); font-weight: var(--weight-semibold);
  border: var(--border-thin) solid var(--color-border); color: var(--color-text-secondary); background: var(--color-surface);
}
.sg-stage.is-done .sg-mark { background: var(--color-success); border-color: var(--color-success); color: var(--color-text-on-accent); }
.sg-stage.is-next .sg-mark { background: var(--color-accent); border-color: var(--color-accent); color: var(--color-text-on-accent); }
.sg-main { min-width: 0; }
.sg-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-4) var(--space-10); }
.sg-label { font-size: var(--text-15); font-weight: var(--weight-semibold); color: var(--color-text-primary); }
.sg-state {
  font-size: var(--text-10); font-weight: var(--weight-semibold); letter-spacing: var(--tracking-4); text-transform: uppercase;
  color: var(--color-text-muted); padding: var(--space-1) var(--space-6); border-radius: var(--radius-pill); background: var(--color-surface-muted);
}
.sg-state--done { color: var(--color-success-text); background: var(--color-surface-success); }
.sg-state--attention { color: var(--color-warning-text); background: var(--color-surface-warning); }
.sg-state--working { color: var(--color-slate-text); background: var(--color-surface-slate); }
.sg-stage.is-next .sg-state--todo { color: var(--color-accent); background: var(--color-accent-bg); }
.sg-detail { margin: var(--space-4) 0 0; font-size: var(--text-13); color: var(--color-text-secondary); }
.sg-act { align-self: center; }
.sg-tenants-sum { margin: var(--space-8) 0 0; font-size: var(--text-12); color: var(--color-text-muted); }
.sg-tenants { list-style: none; margin: var(--space-6) 0 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.sg-tenant {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-4) var(--space-10);
  font-size: var(--text-13); padding: var(--space-4) 0; border-top: var(--border-hairline) solid var(--color-border-faint);
}
.sg-tenant-name { color: var(--color-text-primary); min-width: 0; }
.sg-tenant-state { font-size: var(--text-11); font-weight: var(--weight-semibold); color: var(--color-text-muted); text-transform: uppercase; letter-spacing: var(--tracking-2); }
.sg-tenant--failed .sg-tenant-state, .sg-tenant--stalled .sg-tenant-state { color: var(--color-danger); }
.sg-tenant--needs-review .sg-tenant-state { color: var(--color-warning-text); }
.sg-tenant--processing .sg-tenant-state, .sg-tenant--queued .sg-tenant-state, .sg-tenant--uploading .sg-tenant-state { color: var(--color-slate-text); }
.sg-tenant--complete .sg-tenant-state { color: var(--color-success-text); }
.sg-tenant-acts { margin-left: auto; display: inline-flex; gap: var(--space-4); }
.sg-inline-acts { margin-top: var(--space-8); display: flex; flex-wrap: wrap; gap: var(--space-6); }
.sg-review { display: inline-flex; align-items: center; gap: var(--space-6); margin-top: var(--space-10); font-size: var(--text-13); color: var(--color-text-primary); cursor: pointer; }
.sg-foot { margin-top: var(--space-16); }
.sg-empty-actions { display: flex; flex-wrap: wrap; gap: var(--space-8); margin-top: var(--space-10); }
/* Home: "Pick up where you left off" — sits ABOVE Start here, same card language. */
.sg-resume-card {
  display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-2); width: 100%;
  text-align: left; font-family: inherit; cursor: pointer; margin-bottom: var(--space-16);
  padding: var(--space-14) var(--space-16); border: var(--border-thin) solid var(--color-border-success);
  border-radius: var(--radius-lg); background: var(--color-surface-success);
}
.sg-resume-card:hover { border-color: var(--color-success); box-shadow: var(--shadow-hover); }
.sg-resume-card:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
.sg-resume-eyebrow { font-size: var(--text-10); font-weight: var(--weight-semibold); letter-spacing: var(--tracking-4); text-transform: uppercase; color: var(--color-success-text); }
.sg-resume-title { font-size: var(--text-16); font-weight: var(--weight-semibold); color: var(--color-text-primary); }
.sg-resume-sub { font-size: var(--text-13); color: var(--color-text-secondary); }
.sg-resume-cta { font-size: var(--text-13); font-weight: var(--weight-semibold); color: var(--color-success-text); margin-top: var(--space-4); }
/* Property hub: the setup standing line under "Where it stands". */
.sg-hub { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-8) var(--space-12); margin-bottom: var(--space-10); }
.sg-hub-text { font-size: var(--text-13); color: var(--color-text-secondary); }
.wf-guide-link { margin-right: var(--space-8); }
@media (max-width: 640px) {
  .sg-stage { grid-template-columns: auto minmax(0, 1fr); }
  .sg-act { grid-column: 2; align-self: start; }
}

/* ── CAM Rec Step 1: identity lock (TU-014) ────────────────────────────────── */
.reco-lock { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-8) var(--space-10); margin-top: var(--space-10); font-size: var(--text-13); color: var(--color-text-secondary); }
.reco-lock[hidden] { display: none; }   /* an author display beats the UA's [hidden] rule */
.reco-lock-note { flex: 1 1 var(--size-232); }
.reco-lock-note--warn { color: var(--color-warning-text); }

/* ── Search scope (TU-015): the property is the default, widening is explicit ─── */
.lts-scope-input { position: absolute; opacity: 0; pointer-events: none; }   /* the checkbox is the state; the line + button are the control */
.lts-scope-line { font-weight: var(--weight-semibold); color: var(--color-text-primary); }
.lts-hit--other { border-style: dashed; background: var(--color-surface-sunken); }
.lts-hit-chip, .pl-pick-chip, .lease-add-chip {
  display: inline-block; font-size: var(--text-10); font-weight: var(--weight-semibold); letter-spacing: var(--tracking-4);
  text-transform: uppercase; color: var(--color-warning-text); background: var(--color-surface-warning);
  border-radius: var(--radius-pill); padding: var(--space-1) var(--space-6); vertical-align: middle;
}
.pl-pick-row--other { border-style: dashed; background: var(--color-surface-sunken); }
.pl-pick-scope { margin: 0 0 var(--space-6); font-size: var(--text-12); color: var(--color-text-muted); }
.lease-add-result--other { border-style: dashed; }

/* ── Bulk review: per-file type + tenant, and pre-upload duplicate notes (TU-010) ─── */
.bulk-row-main { display: flex; flex-direction: column; gap: var(--space-4); min-width: 0; flex: 1 1 auto; }
.bulk-row-assign { display: flex; flex-wrap: wrap; gap: var(--space-6); }
.bulk-row-assign .bulk-row-role { flex: 0 1 var(--size-232); }
.bulk-row-assign .bulk-row-tenant { flex: 1 1 var(--size-232); min-width: 0; }
.bulk-row-note { font-size: var(--text-12); }
.bulk-row-note--dup { color: var(--color-warning-text); }
.bulk-row-note--ver { color: var(--color-text-muted); }
.bulk-assign { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-8); margin-top: var(--space-10); }
.bulk-assign-lbl { font-size: var(--text-13); color: var(--color-text-secondary); }
.bulk-assign .input { flex: 1 1 var(--size-232); }

/* ── Lease terms → this reconciliation (TU-013) ─────────────────────────────── */
.cam-termmap { margin: var(--space-8) var(--space-0) var(--space-12); border: var(--border-thin) solid var(--color-border-subtle); border-radius: var(--radius-md); padding: var(--space-8) var(--space-12); }
.cam-termmap > summary { cursor: pointer; display: flex; flex-wrap: wrap; gap: var(--space-8); align-items: baseline; }
.cam-termmap-sum { font-size: var(--text-13); color: var(--color-text-secondary); }
.cam-terms { list-style: none; margin: var(--space-8) 0 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-8); }
.cam-term { display: flex; flex-direction: column; gap: var(--space-2); padding: var(--space-8) var(--space-10); border-left: var(--border-accent) solid var(--color-border-slate); background: var(--color-surface-muted); border-radius: var(--radius-sm); font-size: var(--text-13); }
.cam-term--manual { border-left-color: var(--color-warning-accent); background: var(--color-surface-warning); }
.cam-term--alert { border-left-color: var(--color-accent); }
.cam-term--input { border-left-color: var(--color-success); }
.cam-term-head { display: flex; flex-wrap: wrap; gap: var(--space-8); align-items: baseline; }
.cam-term-label { font-weight: var(--weight-semibold); color: var(--color-text-primary); }
.cam-term-disp { font-size: var(--text-10); font-weight: var(--weight-semibold); letter-spacing: var(--tracking-4); text-transform: uppercase; color: var(--color-text-muted); }
.cam-term--manual .cam-term-disp { color: var(--color-warning-text); }
.cam-term-detail { color: var(--color-text-secondary); }
.cam-term-next { color: var(--color-text-primary); font-weight: var(--weight-medium); }
.cam-term-quote { color: var(--color-text-muted); font-style: italic; font-size: var(--text-12); }
.cam-deadline { margin-top: var(--space-8); padding: var(--space-8) var(--space-10); border-radius: var(--radius-sm); background: var(--color-surface-info); font-size: var(--text-13); color: var(--color-text-primary); }
.cam-deadline--forfeit { background: var(--color-surface-warning); }
.cam-deadline--overdue { background: var(--color-surface-danger); color: var(--color-danger-strong); }
/* Prior-year tax adjustments panel (Verify): the ledger row beside each lease's refund clause. */
.tadj { margin: var(--space-10) 0; padding: var(--space-10) var(--space-12); border: var(--border-thin) solid var(--color-border); border-radius: var(--radius-md); }
.tadj-row { margin-bottom: var(--space-8); }
.tadj-table { width: 100%; margin: var(--space-8) 0; font-size: var(--text-13); }
.tadj-table td, .tadj-table th { text-align: left; vertical-align: top; padding: var(--space-4) var(--space-6); border-bottom: var(--border-thin) solid var(--color-border); }
.tadj-lease--none, .tadj-lease--unknown, .tadj-lease--stale { color: var(--color-text-muted); font-style: italic; }
.tadj-lease--silent { color: var(--color-warning-text); }
.tadj-form, .tadj-decision { display: flex; flex-direction: column; gap: var(--space-8); }
.tadj-opt { display: block; }
.tadj-ex-wrap { display: flex; flex-wrap: wrap; gap: var(--space-8); padding-left: var(--space-16); }
.tadj-ex { font-size: var(--text-13); }
.cam-overrides { margin: var(--space-10) 0; padding: var(--space-8) var(--space-10); border: var(--border-thin) dashed var(--color-border); border-radius: var(--radius-md); }
.cam-overrides ul { list-style: none; margin: var(--space-4) 0 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-6); }
.cam-override { display: flex; flex-wrap: wrap; gap: var(--space-6) var(--space-12); align-items: center; font-size: var(--text-13); }
.cam-override-reason { flex: 1 1 var(--size-232); min-width: 0; }

/* ── "Add document…" + master lease (TU-011) ────────────────────────────────── */
.pl-adddoc { margin-top: var(--space-8); padding: var(--space-10) var(--space-12); border: var(--border-thin) solid var(--color-border-subtle); border-radius: var(--radius-md); background: var(--color-surface); display: flex; flex-direction: column; gap: var(--space-8); }
.pl-adddoc-lead { margin: 0; font-size: var(--text-13); font-weight: var(--weight-semibold); color: var(--color-text-primary); }
.pl-adddoc-roles, .pl-adddoc-master-row { display: flex; flex-wrap: wrap; gap: var(--space-6); align-items: center; }
.pl-master-chip { display: inline-block; font-size: var(--text-11); font-weight: var(--weight-semibold); color: var(--color-slate-text); background: var(--color-surface-slate); border-radius: var(--radius-pill); padding: var(--space-1) var(--space-8); vertical-align: middle; }
.clause-lineage { margin-top: var(--space-4); font-size: var(--text-12); color: var(--color-text-muted); display: flex; flex-wrap: wrap; gap: var(--space-4) var(--space-10); align-items: center; }
.clause-lineage-ctl { color: var(--color-text-secondary); font-weight: var(--weight-medium); }
.clause-badge.inherited { background: var(--color-surface-slate); color: var(--color-slate-text); }
