/* ══════════════════════════════════════════════════════════════════════
   STUDENT LEASE-UP PLAN  (.fr-leaseup-*)
   ----------------------------------------------------------------------
   Frontier reskin of the student Lease-Up Plan page (Renewals → Pre-Lease
   Tiers → GPR Forecast). Per-unit-type cards + editable tables reuse the
   fr-renewal-* vocabulary; this block adds the plan-level chrome: the
   sticky forecast band, the renewals scoreboard, the tier cells, and the
   GPR forecast table. Scoped under .fr-leaseup-page where it touches
   shared form primitives.
   ══════════════════════════════════════════════════════════════════════ */

.fr-leaseup-page {
    font-family: var(--fr-font-sans);
    color: var(--fr-ink);
    /* Breathing room at the page foot so the floating comment bubble
       (bottom-right) sits clear of the final GPR forecast total row.
       !important is required to beat Bootstrap's `.p-3` (padding: 1rem
       !important) on the same element. */
    padding-bottom: 140px !important;
}
/* Provenance, two lines (§5.4). Line 1 is freshness and aligns with the pull
   buttons beside it; line 2 states the bed universe. It replaced one
   ~200-character mono run-on that mixed the two topics. */
.fr-leaseup-snapshot-info {
    font-family: var(--fr-font-mono);
    font-size: 11px;
    letter-spacing: 0.02em;
    color: var(--fr-ink-3);
    margin: 0 2px 14px;
    min-width: 0;
}
.fr-leaseup-prov-line { line-height: 1.65; }
.fr-leaseup-prov-line--universe { color: var(--fr-ink-2); }

/* Snapshot line + in-app Entrata pull buttons share one header row */
.fr-leaseup-refresh-row {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
}
.fr-leaseup-refresh-actions {
    display: flex;
    gap: 8px;
    flex-shrink: 0;
}
.fr-leaseup-refresh-actions .fr-leaseup-ghost-btn:disabled {
    opacity: 0.55;
    cursor: wait;
}

/* "What changed" disclosure banner (pull-over-pull diff + orphaned overrides) */
.fr-leaseup-diff-banner {
    background: #FFFBEB;
    border: 1px solid #FDE68A;
    border-radius: var(--fr-r-md);
    color: var(--fr-ink-2);
    font-family: var(--fr-font-mono);
    font-size: 11.5px;
    letter-spacing: 0.01em;
    line-height: 1.7;
    padding: 10px 36px 10px 14px;
    margin: 0 0 14px;
}
.fr-leaseup-diff-banner .btn-close {
    padding: 12px;
    font-size: 10px;
}
.fr-leaseup-diff-lead {
    color: #92400E;
    font-weight: 600;
}
.fr-leaseup-diff-lead--warn {
    color: #B91C1C;
}
.fr-leaseup-diff-note {
    color: var(--fr-ink-3);
    font-style: italic;
}

/* Shared form primitives (inputs, addons) */
.fr-leaseup-page .form-control {
    font-family: var(--fr-font-mono);
    font-variant-numeric: tabular-nums;
    font-size: 13px;
    color: var(--fr-ink);
    background: var(--fr-bg);
    border: 1px solid var(--fr-hairline-strong);
    border-radius: var(--fr-r-sm);
    box-shadow: none;
}
.fr-leaseup-page .form-control:focus {
    border-color: var(--fr-accent);
    background: var(--fr-surface);
    box-shadow: 0 0 0 3px rgba(180, 83, 9, 0.10);
}
.fr-leaseup-page .form-control::placeholder { color: var(--fr-ink-3); }
.fr-leaseup-page .input-group-text {
    background: var(--fr-bg);
    border: 1px solid var(--fr-hairline-strong);
    color: var(--fr-ink-3);
    font-family: var(--fr-font-sans);
    font-size: 12px;
    font-weight: 500;
}

/* Eyebrow (uppercase tracked label) */
.fr-leaseup-eyebrow {
    font-family: var(--fr-font-sans);
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--fr-ink-3);
}
.fr-leaseup-eyebrow--accent { color: var(--fr-accent); display: block; margin-bottom: 8px; }

/* ══════════════════════════════════════════════════════════════════════
   STICKY PLAN-CHAIN STRIP  (visual-design §5)
   ----------------------------------------------------------------------
   Replaces ~170px of permanently sticky chrome — a hero band, a three-stat
   breakdown, and a JUMP TO link row — with ~100px that carries strictly
   more. The old band's fatal flaw was saying the same thing on every
   screen, which turns it into wallpaper; the chain instead states how its
   numbers relate (renewals + pre-lease + amenity = forecast) and marks
   which step you are currently in.

   No shrink-on-scroll state (§5.5): at this height a progressive collapse
   would add motion complexity and a second layout to maintain for no
   legibility gain.
   ══════════════════════════════════════════════════════════════════════ */
.fr-leaseup-sticky {
    position: sticky;
    top: 0;
    z-index: 20;
    background: var(--fr-surface);
    border: 1px solid var(--fr-hairline);
    border-radius: var(--fr-r-lg);
    box-shadow: 0 1px 2px rgba(20, 24, 29, 0.05);
    padding: 9px 20px 8px;
    margin-bottom: 20px;
}

/* Row 1 — plan year · forecast hero · growth · commit action, on one line.
   `center`, not `baseline`: the button is the tallest thing in the row, and
   baseline-aligning a 38px control against a 27px numeral leaves the line
   visibly crooked. The three text elements share a baseline inside
   .fr-leaseup-strip-figures instead. */
.fr-leaseup-strip-head {
    display: flex;
    align-items: center;
    gap: 18px;
    flex-wrap: wrap;
}
.fr-leaseup-strip-figures {
    display: flex;
    align-items: baseline;
    gap: 16px;
    flex-wrap: wrap;
    min-width: 0;
}
/* Shorter than the global 38px action button: this strip is permanent chrome
   above every screen, so the tallest element in it sets the page's fixed cost. */
.fr-leaseup-strip-head .fr-action-btn { height: 32px; }
.fr-leaseup-strip-plan {
    font-family: var(--fr-font-sans);
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--fr-ink-3);
    white-space: nowrap;
}
.fr-leaseup-strip-hero { display: flex; align-items: baseline; gap: 5px; }
/* Display serif is deliberate — the forecast total is one of the editorial
   single figures §10.1 assigns to the serif, not the mono. Tabular figures
   still matter: this number rewrites itself on every edit, and proportional
   digits make it change WIDTH as it does, which reads as the layout twitching. */
.fr-leaseup-hero-value {
    font-family: var(--fr-font-serif);
    font-variant-numeric: tabular-nums;
    font-size: 27px;
    font-weight: 400;
    line-height: 1.1;
    letter-spacing: -0.01em;
    color: var(--fr-ink);
}
.fr-leaseup-hero-unit {
    font-family: var(--fr-font-sans);
    font-size: 11.5px;
    color: var(--fr-ink-3);
}
/* Sans, not mono — this is a sentence ("+2.7% vs $509,163 today"), not a column
   entry. Tabular figures keep the two numbers inside it from shifting the
   sentence around as they update. */
.fr-leaseup-hero-growth {
    font-family: var(--fr-font-sans);
    font-variant-numeric: tabular-nums;
    font-size: 13px;
    font-weight: 600;
}
.fr-leaseup-hero-growth.is-up   { color: var(--fr-positive); }
.fr-leaseup-hero-growth.is-down { color: var(--fr-negative); }
.fr-leaseup-hero-growth.is-flat { color: var(--fr-ink-3); }

/* Save status sits INLINE to the left of the button, not stacked under it —
   the head row has to stay one line tall. */
.fr-leaseup-actions {
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: 12px;
}
.fr-leaseup-save-status { text-align: right; }
.fr-leaseup-save-msg { font-family: var(--fr-font-mono); font-size: 11px; }
.fr-leaseup-save-msg.is-ok     { color: var(--fr-positive-deep); }
.fr-leaseup-save-msg.is-warn   { color: var(--fr-warning-deep); }
.fr-leaseup-save-msg.is-danger { color: var(--fr-negative-deep); }

/* Row 2 — the chain. Also the page's anchor nav: every step is clickable and
   no step blocks access to another (§5.1). */
.fr-leaseup-chain {
    display: flex;
    align-items: stretch;
    gap: 12px;
    margin-top: 7px;
    padding-top: 7px;
    border-top: 1px solid var(--fr-hairline);
}
.fr-leaseup-chain:empty { display: none; }

/* ① and ② share ONE flexible region, and only they are proportionally sized.
   The amenity term and step ③ take the width they need first; this region
   absorbs whatever is left. Letting all four blocks compete on share pushed
   step ③ — the number the page exists to produce — into ~110px and squeezed the
   amenity term into a stub. The ①:② ratio, which is where the proportional read
   earns anything, is still exact. */
.fr-leaseup-chain-main {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    align-items: stretch;
    gap: 8px;
}

.fr-leaseup-chain-step {
    /* flex-grow is set inline from the term's share of forecast GPR; flex-basis 0
       makes grow the sole size driver. */
    flex: 0 1 0;
    min-width: 96px;
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 2px 7px 3px;
    border-radius: var(--fr-r-sm);
    text-decoration: none;
    color: inherit;
}
/* The active/hover fills live with the hues further down, since each step's
   resting fill is its own tint and the active state has to deepen THAT rather
   than replace it with a neutral. Weight and fill only, never motion — the
   active state is identical under prefers-reduced-motion; only the fade between
   states is suppressed. */
@media (prefers-reduced-motion: no-preference) {
    .fr-leaseup-chain-step { transition: background 140ms ease; }
}
.fr-leaseup-chain-step:focus-visible {
    outline: 2px solid var(--fr-accent);
    outline-offset: 1px;
}

/* Step ③ is a fixed-width terminal block: it is the sum, not a share (§5.1).
   Given a real reserved width rather than min-width:0 — as the sum of everything
   left of it, it should read as the chain's destination, and at whatever width
   the proportional blocks happened to leave over it read as an afterthought. */
.fr-leaseup-chain-step.is-forecast {
    flex: 0 0 auto;
    min-width: 178px;
    justify-content: flex-start;
    padding-left: 15px;
    border-left: 1px solid var(--fr-hairline);
    border-radius: 0 var(--fr-r-sm) var(--fr-r-sm) 0;
}

/* `center`, not `baseline`: the step marker is a box (a drawn circle), and a box
   has no text baseline to sit on. */
.fr-leaseup-chain-head {
    display: flex;
    align-items: center;
    gap: 7px;
    min-width: 0;
    line-height: 1.3;
    /* Matches the marker's height, so the money line lands at the same offset in
       every block — including the amenity term, which has no marker. Without
       this its figures sit ~5px above the rest of the chain. */
    min-height: 18px;
}

/* The step marker: a CSS-drawn ring around a REAL digit.
   ----------------------------------------------------------------------
   This was `①` / `②` / `③` as Unicode glyphs and the user could not read them
   without zooming to 150% (2026-07-28). Two compounding reasons: the enclosed-
   alphanumeric glyph spends ~85% of its em box on the ring, so a 12px `①` puts
   a ~6px digit on screen; and JetBrains Mono has no such glyph, so it fell back
   to whatever system font did, at whatever metrics that font chose.

   Drawing the ring in CSS puts the digit at its full 11.5px, roughly doubling
   the legible size, and makes it crisp at any zoom. §5.2's numbered markers earn
   their place on this page, so the number stays — it is only the ring that was
   never load-bearing. */
.fr-leaseup-chain-mark {
    flex: 0 0 auto;
    box-sizing: border-box;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    border: 1.5px solid var(--fr-ink-3);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-family: var(--fr-font-mono);
    font-variant-numeric: tabular-nums;
    font-size: 11.5px;
    font-weight: 700;
    line-height: 1;
    color: var(--fr-ink-2);
}
.fr-leaseup-chain-name {
    font-family: var(--fr-font-sans);
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.13em;
    text-transform: uppercase;
    color: var(--fr-ink-2);
    white-space: nowrap;
}
.fr-leaseup-chain-step.is-active .fr-leaseup-chain-name { color: var(--fr-ink); }

/* The marker carries the step's own hue, so it doubles as the block's identity
   colour — and filling it on the active step gives §5.1's "weight and fill"
   highlight a second, much stronger cue than a background tint alone. */
.fr-leaseup-chain-step.is-renewals .fr-leaseup-chain-mark {
    border-color: var(--fr-renewals);
    color: var(--fr-renewals);
}
.fr-leaseup-chain-step.is-prelease .fr-leaseup-chain-mark {
    border-color: var(--fr-prelease);
    color: var(--fr-prelease);
}
.fr-leaseup-chain-step.is-renewals.is-active .fr-leaseup-chain-mark {
    background: var(--fr-renewals);
    color: var(--fr-surface);
}
.fr-leaseup-chain-step.is-prelease.is-active .fr-leaseup-chain-mark {
    background: var(--fr-prelease);
    color: var(--fr-surface);
}
/* Step ③ has no hue — it is the sum of the others, not one of them — so its
   marker takes the ink, which still reads as the chain's strongest term. */
.fr-leaseup-chain-step.is-forecast.is-active .fr-leaseup-chain-mark {
    background: var(--fr-ink-2);
    border-color: var(--fr-ink-2);
    color: var(--fr-surface);
}

/* Per-step completeness (§5.1). The under-allocated case is the page's one
   silent-and-downward failure, so it gets the warning colour reserved by
   §10.2 — an unallocated bed contributes $0 and nothing else says so. */
.fr-leaseup-chain-note {
    font-family: var(--fr-font-mono);
    font-size: 10px;
    letter-spacing: 0.01em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.fr-leaseup-chain-note.is-ok   { color: var(--fr-ink-3); }
.fr-leaseup-chain-note.is-warn { color: var(--fr-negative); font-weight: 600; }

.fr-leaseup-chain-figs {
    display: flex;
    align-items: baseline;
    gap: 6px;
    font-family: var(--fr-font-mono);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    line-height: 1.25;
}
.fr-leaseup-chain-beds { font-size: 12px; color: var(--fr-ink-2); }
.fr-leaseup-chain-sep  { color: var(--fr-hairline-strong); }
.fr-leaseup-chain-gpr  { font-size: 13.5px; font-weight: 600; color: var(--fr-ink); }
.fr-leaseup-chain-gpr.is-total { font-size: 15px; }

/* Bar and share on ONE line, as §5's ASCII draws it (`◄── 39.7% ──►`). The bar
   flexes to fill whatever the block's share-driven width leaves, so the
   segments still read as one bar running across the strip — and the strip pays
   for three lines instead of four on every screen of the page. */
.fr-leaseup-chain-rule {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 1px;
}
.fr-leaseup-chain-bar {
    flex: 1 1 auto;
    min-width: 8px;
    height: 5px;
    border-radius: 999px;
}
.fr-leaseup-chain-step.is-renewals .fr-leaseup-chain-bar { background: var(--fr-renewals); }
.fr-leaseup-chain-step.is-prelease .fr-leaseup-chain-bar { background: var(--fr-prelease); }

/* At rest the BAR carries the hue and the block carries no fill. Tinting every
   block at rest would leave the active state nothing to say — and this strip is
   permanent chrome, so two full-width washes above the first input is exactly
   the visual weight §1 set out to remove. Fill is the active affordance; each
   step deepens into its own hue rather than a neutral, so the highlight still
   reads as part of §10.2's one visual language. */
.fr-leaseup-chain-step.is-renewals.is-active,
.fr-leaseup-chain-step.is-renewals:hover { background: var(--fr-renewals-tint); }
.fr-leaseup-chain-step.is-prelease.is-active,
.fr-leaseup-chain-step.is-prelease:hover { background: var(--fr-prelease-tint); }
.fr-leaseup-chain-step.is-amenity:hover  { background: var(--fr-amenity-tint); }
/* Step ③ has no hue — it is the sum of the others, not one of them. */
.fr-leaseup-chain-step.is-forecast.is-active,
.fr-leaseup-chain-step.is-forecast:hover { background: var(--fr-bg); }

/* GPR share, not bed share — it labels what the bar width encodes (§5.1) */
.fr-leaseup-chain-share {
    flex: 0 0 auto;
    font-family: var(--fr-font-mono);
    font-variant-numeric: tabular-nums;
    font-size: 9.5px;
    color: var(--fr-ink-3);
    letter-spacing: 0.02em;
    line-height: 1;
}

/* The amenity segment is unnumbered and carries NO BAR (§5.6). A bar is a
   proportion claim, and there is no width that is both honest and legible for a
   2–3% term: wide enough to hold "$12,660" overstates it ~4x, honest enough to
   encode 2.4% cannot hold its own label. §5.1a and §10.2 already place amenity
   outside the bar grammar, so the printed share carries it and the hue moves to
   the `+` operator. At $0 the segment is not rendered at all, so there is no
   empty state to style. */
.fr-leaseup-chain-step.is-amenity {
    flex: 0 0 auto;
    min-width: 0;
}
.fr-leaseup-chain-plus {
    font-family: var(--fr-font-mono);
    font-size: 13px;
    font-weight: 700;
    line-height: 1;
    color: var(--fr-amenity);
}
.fr-leaseup-chain-step.is-amenity .fr-leaseup-chain-name { color: var(--fr-ink-3); }
.fr-leaseup-chain-step.is-amenity .fr-leaseup-chain-gpr { font-size: 12.5px; font-weight: 500; }
/* The share reads as part of the figures line here, not as a bar caption. */
.fr-leaseup-chain-step.is-amenity .fr-leaseup-chain-share { font-size: 11px; }

/* The arrows carry the dependency: step 2 cannot be done before step 1 (§5.2).
   Aligned to the step-name line, as drawn. */
.fr-leaseup-chain-arrow {
    flex: 0 0 auto;
    align-self: flex-start;
    padding-top: 3px;
    font-size: 12px;
    line-height: 1.3;
    color: var(--fr-hairline-strong);
}

/* Tablet: the chain is the first thing to stack, since a 4-block flex row of
   96px minimums stops being proportional long before it stops fitting. */
@media (max-width: 1100px) {
    .fr-leaseup-chain { flex-wrap: wrap; row-gap: 6px; }
    .fr-leaseup-chain-main { flex-basis: 100%; }
    .fr-leaseup-chain-arrow { display: none; }
    .fr-leaseup-chain-step.is-forecast {
        border-left: 0;
        padding-left: 7px;
        min-width: 140px;
    }
}

/* Section header (eyebrow + serif title) */
.fr-leaseup-section-header { margin-top: 32px; }
.fr-leaseup-section-header .fr-section-title { margin-bottom: 4px; }

/* Controls bar actions + ghost button */
.fr-leaseup-controls-actions { margin-left: auto; display: flex; align-items: center; gap: 10px; }
.fr-leaseup-ghost-btn {
    height: 34px;
    padding: 0 16px;
    background: var(--fr-surface);
    border: 1px solid var(--fr-hairline-strong);
    border-radius: var(--fr-r-md);
    font-family: var(--fr-font-sans);
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--fr-ink-2);
    cursor: pointer;
    transition: background 120ms ease, color 120ms ease, border-color 120ms ease;
}
.fr-leaseup-ghost-btn:hover { background: #FBF9F4; color: var(--fr-ink); border-color: #C9C5B8; }

/* Tertiary control — a text action, not a button. For destructive-but-rare
   things that were sitting at peer weight beside a constructive primary (§6.3):
   "Reset to pulled prices" throws away the user's website prices, and a
   button-shaped control invites the click it should discourage. */
.fr-leaseup-text-btn {
    background: none;
    border: 0;
    padding: 0 2px;
    font-family: var(--fr-font-sans);
    font-size: 12px;
    color: var(--fr-ink-3);
    text-decoration: underline;
    text-decoration-color: var(--fr-hairline-strong);
    text-underline-offset: 3px;
    cursor: pointer;
}
.fr-leaseup-text-btn:hover { color: var(--fr-negative); text-decoration-color: var(--fr-negative); }
.fr-leaseup-text-btn:focus-visible { outline: 2px solid var(--fr-accent); outline-offset: 2px; }

/* §6.3's sentence: the highest-value copy on the page. It sits under the
   strategy toggle it corrects, and it is the one piece of prose on this page
   that survived invariant 3 — because no column header can say "the control you
   are looking at cannot move these beds". */
.fr-leaseup-strategy-note:empty { display: none; }
.fr-leaseup-strategy-note {
    margin-top: 10px;
    padding-top: 9px;
    border-top: 1px solid var(--fr-hairline);
}
.fr-leaseup-strategy-note-text {
    font-family: var(--fr-font-sans);
    font-variant-numeric: tabular-nums;
    font-size: 12.5px;
    line-height: 1.5;
}
/* Loud once the toggle is doing almost nothing, quiet while it still drives
   most of the property. Not red — this is the normal state of most properties
   (§11.8), not an error. */
.fr-leaseup-strategy-note-text.is-dominant { color: var(--fr-warning-deep); font-weight: 500; }
.fr-leaseup-strategy-note-text.is-minor    { color: var(--fr-ink-3); }
.fr-leaseup-ghost-btn:focus, .fr-leaseup-ghost-btn:active {
    outline: none;
    box-shadow: 0 0 0 3px rgba(180, 83, 9, 0.12);
}

/* Renewals portfolio scoreboard */
.fr-leaseup-scoreboard {
    display: flex;
    flex-wrap: wrap;
    background: var(--fr-surface);
    border: 1px solid var(--fr-hairline);
    border-radius: var(--fr-r-lg);
    overflow: hidden;
    margin-bottom: 18px;
    box-shadow: 0 1px 0 rgba(20, 24, 29, 0.02);
}
.fr-leaseup-score {
    flex: 1 1 180px;
    padding: 18px 22px 16px;
    border-right: 1px solid var(--fr-hairline);
}
.fr-leaseup-score:last-child { border-right: 0; }
.fr-leaseup-score-label {
    font-family: var(--fr-font-sans);
    font-size: 9.5px;
    font-weight: 600;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--fr-ink-3);
    margin-bottom: 8px;
}
/* These four tiles become the §6.7 equation, which §10.1 also assigns to the
   serif — so serif here is not an inconsistency with the mono rule. Tabular
   figures for the same reason as the hero: they all rewrite live. */
.fr-leaseup-score-value {
    font-family: var(--fr-font-serif);
    font-variant-numeric: tabular-nums;
    font-size: 30px;
    font-weight: 400;
    line-height: 1;
    letter-spacing: -0.01em;
    color: var(--fr-ink);
    font-variant-numeric: tabular-nums;
}
.fr-leaseup-score-value--pos  { color: var(--fr-positive); }
.fr-leaseup-score-value--neg  { color: var(--fr-negative); }
.fr-leaseup-score-value--gold { color: var(--fr-gold); }

/* Per-unit-type card tweaks (reuses fr-renewal-card). No collapse here,
   so neutralize the head's pointer + hover affordance. */
.fr-leaseup-card-head { cursor: default; }
.fr-leaseup-card-head:hover { background: var(--fr-bg); }

/* Renewals card body: website-price rail + editable table side by side. */
.fr-leaseup-renewal-body {
    display: flex;
    align-items: flex-start;
    gap: 20px;
}
.fr-leaseup-side {
    flex: 0 0 150px;
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding-right: 20px;
    border-right: 1px solid var(--fr-hairline);
}
.fr-leaseup-side-label {
    font-family: var(--fr-font-sans);
    font-size: 9.5px;
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--fr-ink-3);
    margin-top: 4px;
}
.fr-leaseup-price-group { flex-wrap: nowrap; }
.fr-leaseup-variance {
    font-family: var(--fr-font-mono);
    font-variant-numeric: tabular-nums;
    font-size: 18px;
    font-weight: 600;
    line-height: 1;
}
.fr-leaseup-variance.is-up   { color: var(--fr-positive); }
.fr-leaseup-variance.is-down { color: var(--fr-negative); }
.fr-leaseup-table-wrap { flex: 1 1 auto; min-width: 0; }

/* ══════════════════════════════════════════════════════════════════════
   RENEWAL ROWS  (.fr-leaseup-rr-*)   visual-design §6.2 / §6.4 / §6.4a / §6.5
   ----------------------------------------------------------------------
   An HTML table of inputs, replacing the Dash DataTable. Group header rows,
   per-row count bars, `at website` text inside a money column, the
   composition sub-line and two editable synced cells per row are all outside
   what the DataTable could express (§13.4) — which is why the DataTable-
   specific rules that used to live here are gone.
   ══════════════════════════════════════════════════════════════════════ */
.fr-leaseup-rr {
    width: 100%;
    border-collapse: collapse;
    font-family: var(--fr-font-mono);
    font-variant-numeric: tabular-nums;
}
.fr-leaseup-rr-th {
    padding: 7px 10px;
    background: var(--fr-ink);
    color: #FFFFFF;
    font-family: var(--fr-font-sans);
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.13em;
    text-transform: uppercase;
    text-align: center;
    white-space: nowrap;
}
.fr-leaseup-rr-th:first-child { text-align: left; }
/* The two editable columns are flagged in the header too, so the pair reads as
   a pair before the user reaches a single row. */
.fr-leaseup-rr-th--edit { background: #22282F; }

/* Group headers carry a bed count and a dollar contribution, so the headers
   ALONE describe the shape of the decision before a single row is read (§6.2).
   Grouping here is labelling, not hiding — groups never collapse. */
.fr-leaseup-rr-group td {
    padding: 12px 10px 5px;
    border-bottom: 1px solid var(--fr-hairline-strong);
    vertical-align: bottom;
    white-space: nowrap;
}
.fr-leaseup-rr-group-name {
    font-family: var(--fr-font-sans);
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: 0.13em;
    text-transform: uppercase;
    color: var(--fr-ink-2);
}
.fr-leaseup-rr-group-detail {
    margin-left: 8px;
    font-family: var(--fr-font-mono);
    font-size: 11px;
    color: var(--fr-ink-3);
}
/* Rides with the label, not in a column of its own — a bed count describing the
   group had been landing under the `2027–28 Offer` heading, stranded mid-band. */
.fr-leaseup-rr-group-sep { margin: 0 7px; color: var(--fr-hairline-strong); }
.fr-leaseup-rr-group-beds {
    font-family: var(--fr-font-mono);
    font-size: 11.5px;
    font-weight: 600;
    color: var(--fr-ink-2);
}
.fr-leaseup-rr-group-lift {
    font-size: 11.5px;
    font-weight: 600;
    text-align: right;
    color: var(--fr-positive);
    white-space: nowrap;
}
/* The zero-lift group is the LARGEST at most properties and earns nothing —
   §6.2 wants that legible, not hidden, so the $0 is muted rather than absent. */
.fr-leaseup-rr-group-lift.is-zero { color: var(--fr-ink-3); font-weight: 400; }
.fr-leaseup-rr-group.is-at_website .fr-leaseup-rr-group-name { color: var(--fr-ink-3); }

.fr-leaseup-rr-row td {
    padding: 4px 10px;
    border-bottom: 1px solid var(--fr-hairline);
    text-align: center;
    vertical-align: middle;
}
.fr-leaseup-rr-row:hover td { background: #FBFAF6; }
/* A bucket that is ENTIRELY assumed backfill is an assumption, not a fact, so
   it takes the lighter treatment. A MIXED bucket deliberately does not — most
   of it is real leases (§6.5). */
.fr-leaseup-rr-row.is-assumed .fr-leaseup-rr-rent,
.fr-leaseup-rr-row.is-assumed .fr-leaseup-rr-offer { color: var(--fr-ink-2); font-style: italic; }

.fr-leaseup-rr-rentcell { text-align: left !important; min-width: 132px; }
.fr-leaseup-rr-rent-line { display: flex; align-items: baseline; gap: 7px; }
.fr-leaseup-rr-rent { font-size: 13px; font-weight: 600; color: var(--fr-ink); }
/* The old lonely COUNT column, merged into the rent cell (§6.4) */
.fr-leaseup-rr-count { font-size: 11px; color: var(--fr-ink-3); }
.fr-leaseup-rr-new {
    font-family: var(--fr-font-sans);
    font-size: 8.5px;
    font-weight: 700;
    letter-spacing: 0.1em;
    color: var(--fr-warning-deep);
    background: var(--fr-warning-tint);
    padding: 1px 4px;
    border-radius: 3px;
}
/* Distribution at a glance, which the bare integer could not give.
   Capped — but the cap has to leave room to COMPARE. Two failures, both seen and
   corrected on 2026-07-28: uncapped, a 100% bar filled the rent column's ~490px
   of slack and read as an accidental underline on the rent above it; at 92px, a
   unit type whose largest bucket is x10 rendered a single bed as 9px, so the bars
   were legible as marks but useless as measurements. ~230px is wide enough that
   x1 against x10 is obvious and short enough to end visibly mid-cell — landed on
   after seeing 92px and 190px in place. */
.fr-leaseup-rr-bar {
    max-width: 230px;
    height: 4px;
    margin-top: 4px;
    background: var(--fr-hairline);
    border-radius: 999px;
}
.fr-leaseup-rr-bar-fill {
    /* The smallest bucket must still register as a bar rather than as nothing:
       at a 190px track a single bed in a 60-bed type is 3px. */
    min-width: 3px;
    height: 100%;
    background: var(--fr-renewals);
    opacity: 0.75;
    border-radius: 999px;
}

/* The editable pair (§6.4a). Matched width, matched chrome, no hierarchy
   implying the % is primary — neither is the "real" one to the user. Persistent
   input chrome, deliberately pitched between the old full-column amber wash
   (the loudest thing in the section) and step 2's hover-only border, which the
   user judged "*really* subtle" and could not read as editable. A reviewer
   scanning thirty rows should not have to hover to find the boxes. */
.fr-leaseup-rr-editcell { width: 108px; }
.fr-leaseup-rr-input-wrap { display: flex; align-items: center; gap: 5px; }
.fr-leaseup-rr-input-group { flex-wrap: nowrap; }
.fr-leaseup-rr-input-group .input-group-text {
    padding: 0 6px;
    background: var(--fr-accent-soft);
    border-color: #E3D6AE;
    color: var(--fr-accent-strong);
    font-size: 11px;
}
.fr-leaseup-page .fr-leaseup-rr-input.form-control {
    width: 54px;
    min-width: 0;
    padding: 2px 5px;
    text-align: center;
    background: #FFFDF6;
    border-color: #E3D6AE;
    font-size: 12.5px;
}
.fr-leaseup-page .fr-leaseup-rr-input.form-control:hover { background: #FFFBEA; }

/* "Did I set this, or did the generator?" — unanswerable an hour into a session
   without it (§10.3). Sits on the DOLLAR box: dollars are the stored quantity,
   and which box the user typed in is deliberately not persisted. */
.fr-leaseup-rr-dot {
    flex: 0 0 auto;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--fr-accent);
}

.fr-leaseup-rr-offer { font-size: 13px; font-weight: 600; color: var(--fr-ink); }
.fr-leaseup-rr-saves { font-size: 12.5px; color: var(--fr-ink-2); }
.fr-leaseup-rr-muted { color: var(--fr-ink-3); font-size: 11.5px; }
/* The ONE warning colour left on this column. A negative saving means the
   renewer would pay MORE than a brand-new resident, which only arises from a
   hand-typed override above the website price — a genuine anomaly, not a
   direction on a gradient. Amber, because it is a deliberate human choice
   rather than an error (§12). */
.fr-leaseup-rr-over { color: var(--fr-warning-deep); font-weight: 600; }

/* §6.5's composition line: the honest place for the caveat that used to sit in
   the section intro. Its own row, so the grid above stays intact. */
.fr-leaseup-rr-noterow td { border-bottom: 1px solid var(--fr-hairline); }
.fr-leaseup-rr-note {
    padding: 0 10px 6px !important;
    text-align: left !important;
    font-family: var(--fr-font-sans);
    font-size: 11px;
    font-style: italic;
    color: var(--fr-ink-3);
}

/* ── Numeric input primitive ─────────────────────────────────────────────
   Every editable number on this page: mono with tabular figures, CENTRED,
   sized to its content.

   Centred is the user's call (2026-07-28): a unit type's rents are almost
   always all-3-digit or all-4-digit, so a shared right edge buys little here.
   What DID matter was the box sprawl — values centred inside a ~360px input
   holding four characters end up far apart no matter how they are aligned.
   Width in ch (not pixels) so it tracks the font. */
.fr-leaseup-num.form-control {
    font-family: var(--fr-font-mono);
    font-variant-numeric: tabular-nums;
    text-align: center;
}
/* 4-5 digits with a thousands separator: 1,659 */
.fr-leaseup-num--price.form-control {
    width: calc(6ch + 1.25rem);
    flex: 0 0 auto;
}
/* 1-3 digits: bed quotas, percentages */
.fr-leaseup-num--count.form-control {
    width: calc(4ch + 1.25rem);
    flex: 0 0 auto;
}

/* Tier controls row */
.fr-leaseup-tier-controls { align-items: flex-end; }
.fr-leaseup-field { display: flex; flex-direction: column; gap: 6px; }
.fr-leaseup-tier-pct-row { display: flex; gap: 6px; }
.fr-leaseup-tier-pct-input.form-control {
    padding-left: 6px;
    padding-right: 6px;
}

/* ══════════════════════════════════════════════════════════════════════
   PRE-LEASE TIER SECTION — generator row, derivation equation, matrix
   docs/STUDENT_LEASEUP_PLAN_VISUAL_DESIGN.md §7.1, §7.3, §7.5
   ══════════════════════════════════════════════════════════════════════ */

/* ── Generator row ─────────────────────────────────────────────────── */
.fr-leaseup-generator-lead {
    font-family: var(--fr-font-sans);
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--fr-ink-3);
    align-self: flex-end;
    padding-bottom: 7px;
}
.fr-leaseup-split-sum {
    font-family: var(--fr-font-mono);
    font-variant-numeric: tabular-nums;
    font-size: 11.5px;
    font-weight: 600;
    white-space: nowrap;
    align-self: center;
    margin-left: 8px;
}
.fr-leaseup-split-sum.is-ok   { color: var(--fr-positive); }
.fr-leaseup-split-sum.is-warn { color: var(--fr-warning-deep); }

/* R4: the generator's inputs do nothing until Regenerate runs, so the state
   between "edited" and "applied" has to be visible. */
.fr-leaseup-pending { display: flex; align-items: center; gap: 8px; }
.fr-leaseup-pending-chip {
    font-family: var(--fr-font-sans);
    font-size: 9.5px;
    font-weight: 700;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--fr-warning-deep);
    background: var(--fr-warning-tint-2);
    border-radius: 999px;
    padding: 3px 9px;
    white-space: nowrap;
}
.fr-leaseup-pending-note {
    font-family: var(--fr-font-serif);
    font-style: italic;
    font-size: 12px;
    color: var(--fr-ink-3);
}

/* ── Derivation equation (§7.1) ────────────────────────────────────────
   Display-serif numerals with mono captions and REAL operators. Per
   invariant 4 the operators are the content: three KPI cards would say
   "here are three facts", the equation says "this number causes that one".

   Laid out HORIZONTALLY — numeral beside its caption on a shared baseline,
   the whole band left-aligned inside the matrix card. Stacking the caption
   under each numeral and centring the row cost ~150px of mostly-empty grey
   with content only in the middle third of the screen. */
.fr-leaseup-eq {
    display: flex;
    align-items: center;
    gap: 20px;
    flex-wrap: wrap;
    padding: 15px 18px 16px;
    border-bottom: 1px solid var(--fr-hairline-strong);
}
.fr-leaseup-eq-term { display: flex; align-items: baseline; gap: 11px; }
.fr-leaseup-eq-num {
    font-family: var(--fr-font-serif);
    font-variant-numeric: tabular-nums;
    font-size: 34px;
    line-height: 1;
    color: var(--fr-ink);
    letter-spacing: -0.01em;
}
.fr-leaseup-eq-label {
    font-family: var(--fr-font-sans);
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--fr-accent);
}
.fr-leaseup-eq-sub {
    font-family: var(--fr-font-mono);
    font-size: 10.5px;
    line-height: 1.45;
    color: var(--fr-ink-3);
    margin-top: 2px;
}
.fr-leaseup-eq-op {
    font-family: var(--fr-font-serif);
    font-size: 24px;
    line-height: 1;
    color: var(--fr-ink-3);
}

/* ── The matrix (§7.3) ─────────────────────────────────────────────── */
/* Card chrome on the outer element so the equation band shares it; only the
   table scrolls. */
.fr-leaseup-mx-card {
    background: var(--fr-surface);
    border: 1px solid var(--fr-hairline);
    border-radius: var(--fr-r-lg);
    overflow: hidden;
    box-shadow: 0 1px 0 rgba(20, 24, 29, 0.02);
}
.fr-leaseup-mx-wrap { overflow-x: auto; }
.fr-leaseup-mx {
    width: 100%;
    border-collapse: collapse;
    font-family: var(--fr-font-sans);
    color: var(--fr-ink);
}
.fr-leaseup-mx thead th {
    background: #FBFAF6;
    color: var(--fr-ink-3);
    font-size: 10px;
    font-weight: 500;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    padding: 11px 12px;
    border-bottom: 1px solid var(--fr-hairline-strong);
    white-space: nowrap;
    text-align: center;
    vertical-align: bottom;
}
.fr-leaseup-mx-th-label { min-width: 116px; text-align: left !important; }
.fr-leaseup-mx-th-micro { width: 42px; }
.fr-leaseup-mx-th-hint {
    font-family: var(--fr-font-mono);
    font-size: 9px;
    letter-spacing: 0.02em;
    text-transform: none;
    color: var(--fr-accent);
    margin-top: 3px;
}
/* Headers align with the values beneath them. Quota and GPR are read-only
   summary columns whose values genuinely span magnitudes ($10,674 next to
   $394,624), so they stay right-aligned like the GPR Forecast table — and the
   header has to follow, or it floats over a column edge it doesn't share. */
.fr-leaseup-mx-th-quota,
.fr-leaseup-mx-th-gpr { min-width: 96px; text-align: right !important; }

/* Pinned label column, so the unit type stays readable while the tier
   columns scroll — the same pattern the GPR Forecast table uses. */
.fr-leaseup-mx-label,
.fr-leaseup-mx-th-label {
    position: sticky;
    left: 0;
    z-index: 2;
    background: var(--fr-surface);
}
.fr-leaseup-mx-th-label { background: #FBFAF6; }

.fr-leaseup-mx-label {
    padding: 10px 14px;
    border-bottom: 1px solid var(--fr-hairline-strong);
    vertical-align: middle;
}
.fr-leaseup-mx-ut {
    font-size: 14px;
    font-weight: 600;
    letter-spacing: -0.005em;
}
.fr-leaseup-mx-ut-sub {
    font-family: var(--fr-font-mono);
    font-variant-numeric: tabular-nums;
    font-size: 10.5px;
    color: var(--fr-ink-3);
    margin-top: 2px;
}
/* The two micro-labels are what remove all ambiguity about which box is
   which, with no tooltip (§7.3). */
.fr-leaseup-mx-micro {
    font-family: var(--fr-font-sans);
    font-size: 9px;
    font-weight: 600;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--fr-ink-3);
    text-align: right;
    padding: 0 8px 0 4px;
    white-space: nowrap;
}
.fr-leaseup-mx-cell { padding: 3px 6px; }
.fr-leaseup-mx-row-rate .fr-leaseup-mx-cell { padding-top: 10px; }
.fr-leaseup-mx-row-beds .fr-leaseup-mx-cell { padding-bottom: 8px; }

/* Faint tint darkening Tier 1 → Tier 4, no borders. The tint encodes the
   ascending price ladder, and the quota bar reuses the same ramp
   positions — which is what lets the bar go unlabelled (§10.2). */
.fr-leaseup-mx-cell.is-t1, .fr-leaseup-mx-th-tier.is-t1 { background: var(--fr-tier-1-tint); }
.fr-leaseup-mx-cell.is-t2, .fr-leaseup-mx-th-tier.is-t2 { background: var(--fr-tier-2-tint); }
.fr-leaseup-mx-cell.is-t3, .fr-leaseup-mx-th-tier.is-t3 { background: var(--fr-tier-3-tint); }
.fr-leaseup-mx-cell.is-t4, .fr-leaseup-mx-th-tier.is-t4 { background: var(--fr-tier-4-tint); }

/* Matched addon width AND matched field width, so the rate line and the beds
   line form ONE grid. Sized to their own content they disagreed visibly — a
   sliver of a `$` addon over a wide `beds` addon, and a wide rate field over a
   narrow beds field — which read as an accident rather than a decision. */
.fr-leaseup-mx-input { flex-wrap: nowrap; }
.fr-leaseup-mx-input .input-group-text {
    min-width: 44px;
    justify-content: center;
    font-size: 10px;
    padding: 0 6px;
    color: var(--fr-ink-3);
    background: transparent;
    border-color: var(--fr-hairline-strong);
}
.fr-leaseup-mx-input .fr-leaseup-num.form-control {
    width: calc(6ch + 1.25rem);
    flex: 0 0 auto;
    background: var(--fr-surface);
}

.fr-leaseup-mx-quota,
.fr-leaseup-mx-gpr {
    text-align: right;
    padding: 10px 14px;
    border-bottom: 1px solid var(--fr-hairline-strong);
    border-left: 1px solid var(--fr-hairline);
    vertical-align: middle;
    white-space: nowrap;
}
/* The pair and its marker stay on ONE line — a lone ✓ on its own row read as
   an orphan rather than as a confirmation of the number above it. */
.fr-leaseup-mx-quota-num {
    font-family: var(--fr-font-mono);
    font-variant-numeric: tabular-nums;
    font-size: 12.5px;
    font-weight: 600;
}
.fr-leaseup-mx-quota-ok {
    font-size: 11px;
    color: var(--fr-positive);
    margin-left: 6px;
}
.fr-leaseup-mx-quota-warn {
    font-family: var(--fr-font-sans);
    font-size: 10px;
    font-weight: 600;
    color: var(--fr-warning-deep);
    margin-left: 6px;
    white-space: nowrap;
}
.fr-leaseup-mx-gpr {
    font-family: var(--fr-font-mono);
    font-variant-numeric: tabular-nums;
    font-size: 13px;
    font-weight: 600;
}

/* Thin, unlabelled: the grid carries the labels, the bar carries only the
   proportion the grid cannot express. First thing to cut if this section
   ever feels busy (§7.3). */
.fr-leaseup-mx-barcell { padding: 0 6px 9px; }
.fr-leaseup-mx-bar {
    display: flex;
    height: 7px;
    border-radius: 999px;
    overflow: hidden;
    background: var(--fr-hairline);
}
.fr-leaseup-mx-bar.is-empty { opacity: 0.4; }
.fr-leaseup-mx-bar-seg.is-t1 { background: var(--fr-tier-1); }
.fr-leaseup-mx-bar-seg.is-t2 { background: var(--fr-tier-2); }
.fr-leaseup-mx-bar-seg.is-t3 { background: var(--fr-tier-3); }
.fr-leaseup-mx-bar-seg.is-t4 { background: var(--fr-tier-4); }

.fr-leaseup-mx-total td {
    background: var(--fr-accent-soft);
    color: var(--fr-accent-strong);
    font-weight: 700;
    border-top: 1px solid rgba(180, 83, 9, 0.30);
    border-bottom: 0;
    padding: 11px 14px;
}
.fr-leaseup-mx-total-label {
    text-align: right;
    font-size: 10px;
    letter-spacing: 0.14em;
    text-transform: uppercase;
}
.fr-leaseup-mx-total .fr-leaseup-mx-quota,
.fr-leaseup-mx-total .fr-leaseup-mx-gpr {
    font-family: var(--fr-font-mono);
    font-variant-numeric: tabular-nums;
    font-size: 13px;
    color: var(--fr-accent-strong);
}

@media (max-width: 900px) {
    .fr-leaseup-eq { gap: 12px; padding: 12px 12px 13px; }
    .fr-leaseup-eq-num { font-size: 27px; }
    .fr-leaseup-eq-op { font-size: 20px; }
}

/* Amenity card */
.fr-leaseup-amenity-field { max-width: 320px; }
.fr-leaseup-field-hint {
    font-family: var(--fr-font-serif);
    font-style: italic;
    font-size: 13px;
    color: var(--fr-ink-3);
    margin-top: 2px;
}

/* GPR forecast table */
.fr-leaseup-fc-wrap {
    background: var(--fr-surface);
    border: 1px solid var(--fr-hairline);
    border-radius: var(--fr-r-lg);
    overflow: hidden;
    box-shadow: 0 1px 0 rgba(20, 24, 29, 0.02);
}
.fr-leaseup-fc-table {
    width: 100%;
    border-collapse: collapse;
    font-family: var(--fr-font-sans);
    font-size: 13px;
    color: var(--fr-ink);
}
.fr-leaseup-fc-table thead th {
    background: #FBFAF6;
    color: var(--fr-ink-3);
    font-size: 10.5px;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    padding: 13px 18px;
    border-bottom: 1px solid var(--fr-hairline-strong);
    white-space: nowrap;
}
.fr-leaseup-fc-table tbody td {
    padding: 11px 18px;
    border-bottom: 1px solid var(--fr-hairline);
    vertical-align: middle;
}
.fr-leaseup-fc-table .is-left  { text-align: left; }
.fr-leaseup-fc-table .is-right { text-align: right; }
.fr-leaseup-fc-table .fr-cell-money,
.fr-leaseup-fc-table .fr-cell-num {
    font-family: var(--fr-font-mono);
    font-variant-numeric: tabular-nums;
}
.fr-leaseup-fc-name { font-weight: 500; }
.fr-leaseup-fc-strong { font-weight: 600; }
.fr-leaseup-fc-table tbody tr:nth-of-type(even):not(.fr-leaseup-fc-subtotal):not(.fr-leaseup-fc-amenity):not(.fr-leaseup-fc-total) td {
    background: #FCFBF7;
}
.fr-leaseup-fc-subtotal td {
    background: #FBFAF6;
    font-weight: 600;
    border-top: 1px solid var(--fr-hairline-strong);
}
.fr-leaseup-fc-amenity td { color: var(--fr-ink-2); }
/* Inline qualifier on the amenity row's label. Carries what the old
   "(static)" was trying to say — property-wide, not derived per unit type —
   without putting a parenthetical in the row's name. */
.fr-leaseup-fc-hint {
    font-size: 10.5px;
    color: var(--fr-ink-3);
    font-weight: 400;
    letter-spacing: 0.01em;
}
.fr-leaseup-fc-total td {
    background: var(--fr-accent-soft);
    color: var(--fr-accent-strong);
    font-weight: 700;
    font-size: 14px;
    border-top: 1px solid rgba(180, 83, 9, 0.30);
    border-bottom: 0;
}
.fr-leaseup-fc-total .is-left {
    text-transform: uppercase;
    letter-spacing: 0.10em;
    font-size: 11px;
}

/* Empty / info notes */
.fr-leaseup-note {
    font-family: var(--fr-font-serif);
    font-style: italic;
    font-size: 15px;
    padding: 14px 4px;
}
.fr-leaseup-note--muted  { color: var(--fr-ink-3); }
.fr-leaseup-note--warn   { color: var(--fr-warning-deep); }
.fr-leaseup-note--danger { color: var(--fr-negative-deep); }


/* ══════════════════════════════════════════════════════════════════════
   STUDENT LEASE-UP TRACKER  (.fr-tracker-*)
   ----------------------------------------------------------------------
   Read-only live-pace dashboard. Reuses the Lease-Up Plan vocabulary
   (.fr-leaseup-page, .fr-section-*, .fr-renewal-controls, .fr-renewal-seg)
   and adds tracker-specific chrome: editorial header, a pace-summary chip
   row, a chart card, and signed-vs-plan tier-progress cells.
   ══════════════════════════════════════════════════════════════════════ */

.fr-tracker-page { font-family: var(--fr-font-sans); }

/* ── Editorial header (eyebrow + serif title) ──────────────────────── */
.fr-tracker-head {
    display: flex;
    flex-direction: column;
    margin: 6px 2px 6px;
}
.fr-tracker-title { margin: 4px 0 0; }

/* ── Filter bar tweaks ─────────────────────────────────────────────── */
.fr-tracker-controls-row { align-items: flex-end; gap: 22px; }
.fr-tracker-field { min-width: 150px; }
/* Wide enough for the full cycle label, e.g. "2026 Cycle (Oct 2025 – Sep 2026)". */
.fr-tracker-field:first-child  { min-width: 300px; flex: 0 1 320px; }
.fr-tracker-field:nth-child(2) { min-width: 240px; flex: 1 1 240px; }
.fr-tracker-field:last-child   { min-width: 200px; flex: 1 1 200px; }
/* The metric segmented control sits a touch above the dropdown baseline;
   nudge it down so its pill aligns with the dropdown controls. */
.fr-tracker-metric-seg { margin-bottom: 1px; }

/* Cycle / benchmark dropdown menu options on the lease-up pages: keep each
   option on a single line so long cycle labels (e.g. "2026 Cycle (Oct 2025
   – Sep 2026)") don't wrap and collide. Paired with the wider Cycle fields. */
.fr-leaseup-page .fr-renewal-ut-dropdown .Select-menu-outer .Select-option {
    white-space: nowrap;
}

/* ── Pace summary chips (this cycle vs prior, same cycle-week) ──────── */
.fr-tracker-pace-summary {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    margin: 2px 2px 14px;
    min-height: 0;
}
.fr-tracker-pace-summary:empty { margin: 0; }
.fr-tracker-pace-chip {
    display: inline-flex;
    align-items: baseline;
    gap: 8px;
    background: var(--fr-surface);
    border: 1px solid var(--fr-hairline);
    border-radius: 100px;
    padding: 7px 15px;
    box-shadow: 0 1px 0 rgba(20, 24, 29, 0.02);
}
.fr-tracker-pace-chip-label {
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--fr-ink-3);
}
.fr-tracker-pace-chip-value {
    font-family: var(--fr-font-mono);
    font-variant-numeric: tabular-nums;
    font-size: 15px;
    font-weight: 600;
    color: var(--fr-ink);
}
.fr-tracker-pace-chip-delta {
    font-size: 12px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}
.fr-tracker-pace-chip-delta.is-up   { color: var(--fr-positive); }
.fr-tracker-pace-chip-delta.is-down { color: var(--fr-negative); }
.fr-tracker-pace-chip-delta.is-flat { color: var(--fr-ink-3); }

/* ── Chart card ────────────────────────────────────────────────────── */
.fr-tracker-chart-card {
    background: var(--fr-surface);
    border: 1px solid var(--fr-hairline);
    border-radius: var(--fr-r-lg);
    padding: 14px 16px 8px;
    box-shadow: 0 1px 0 rgba(20, 24, 29, 0.02);
}

/* Lease-Up Tracker charts (§1 YoY Pace + §3 Lead Demand) — undo the global
   ".js-plotly-plot { height: 500px !important }" force (PLOTLY CHART STYLES,
   ~line 1764) for THESE charts only. That global rule pins every Plotly element
   to 500px and gives it its own border/shadow, so each chart drew its content at
   the figure's real height inside a 500px box (the blank band / slight overflow)
   and stacked a second card inside fr-tracker-chart-card. Scoped to these two
   chart ids so every other page stays untouched. Each graph fills its styled
   height (responsive=True), and the card grows to contain it — one clean box,
   no squish, no overflow. */
#student-tracker-yoy-chart .js-plotly-plot,
#student-tracker-yoy-chart .js-plotly-plot .plotly,
#student-tracker-yoy-chart .js-plotly-plot .svg-container,
#student-tracker-yoy-chart .js-plotly-plot .main-svg,
#student-tracker-demand-chart .js-plotly-plot,
#student-tracker-demand-chart .js-plotly-plot .plotly,
#student-tracker-demand-chart .js-plotly-plot .svg-container,
#student-tracker-demand-chart .js-plotly-plot .main-svg {
    height: 100% !important;
    min-height: 0 !important;
    max-height: none !important;
}
#student-tracker-yoy-chart .js-plotly-plot,
#student-tracker-demand-chart .js-plotly-plot {
    border: 0 !important;
    box-shadow: none !important;
    border-radius: 0 !important;
    background: transparent !important;
    overflow: visible !important;
}

/* ── Tier-progress cells (signed vs plan) ──────────────────────────── */
.fr-tracker-progress-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: 14px;
}
.fr-tracker-progress-cell {
    background: var(--fr-surface);
    border: 1px solid var(--fr-hairline);
    border-radius: var(--fr-r-lg);
    padding: 18px 20px 16px;
    box-shadow: 0 1px 0 rgba(20, 24, 29, 0.02);
}
.fr-tracker-cell-group {
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--fr-ink-3);
    margin-bottom: 10px;
}
.fr-tracker-cell-count { display: flex; align-items: baseline; gap: 7px; margin-bottom: 12px; }
.fr-tracker-cell-signed {
    font-family: var(--fr-font-serif);
    font-size: 34px;
    font-weight: 400;
    line-height: 1;
    letter-spacing: -0.01em;
    color: var(--fr-ink);
    font-variant-numeric: tabular-nums;
}
.fr-tracker-cell-target {
    font-family: var(--fr-font-mono);
    font-size: 14px;
    color: var(--fr-ink-3);
    font-variant-numeric: tabular-nums;
}
.fr-tracker-cell-bar {
    height: 7px;
    border-radius: 100px;
    background: var(--fr-hairline);
    overflow: hidden;
    margin-bottom: 8px;
}
.fr-tracker-cell-bar-fill {
    height: 100%;
    border-radius: 100px;
    background: var(--fr-accent);
    transition: width 240ms ease;
}
.fr-tracker-cell-bar-fill.is-ahead  { background: var(--fr-positive); }
.fr-tracker-cell-bar-fill.is-on     { background: var(--fr-accent); }
.fr-tracker-cell-bar-fill.is-behind { background: var(--fr-warning); }
.fr-tracker-cell-bar-fill.is-risk   { background: var(--fr-negative); }
.fr-tracker-cell-bar-fill.is-none   { background: var(--fr-hairline-strong); width: 0 !important; }
.fr-tracker-cell-status {
    font-family: var(--fr-font-sans);
    font-size: 11px;
    font-weight: 500;
    color: var(--fr-ink-3);
}
.fr-tracker-cell-status.is-ahead  { color: var(--fr-positive-deep); }
.fr-tracker-cell-status.is-behind { color: var(--fr-warning-deep); }
.fr-tracker-cell-status.is-risk   { color: var(--fr-negative-deep); }

/* Empty / loading note */
.fr-tracker-empty {
    font-family: var(--fr-font-serif);
    font-style: italic;
    font-size: 15px;
    color: var(--fr-ink-3);
    padding: 14px 4px;
}


/* ══════════════════════════════════════════════════════════════════════
   STUDENT RATE MANAGER  (.fr-rate-mgr-* / .fr-rm-*)
   ----------------------------------------------------------------------
   In-season pricing cockpit. Shares the editorial header / controls /
   section headers / scoreboard with the Tracker + Plan pages, and adds a
   data-table vocabulary (.fr-rm-table) for the live rate board, the modal
   change preview, and the audit log — mirroring the .fr-leaseup-fc-table
   look so all the lease-up surfaces feel like one ledger.
   ══════════════════════════════════════════════════════════════════════ */

.fr-rate-mgr-page { font-family: var(--fr-font-sans); }

/* ── Filter bar ────────────────────────────────────────────────────── */
/* Top-align so the Cycle label/input lines up with the Benchmark
   label/input even though Benchmark carries a hint line beneath it. */
.fr-rate-mgr-controls-row { align-items: flex-start; gap: 22px; }
/* Wide enough for the full cycle label, e.g. "2026 Cycle (Oct 2025 – Sep 2026)". */
.fr-rate-mgr-field:first-child { min-width: 300px; flex: 0 1 320px; }
.fr-rate-mgr-field:nth-child(2) { min-width: 280px; flex: 1 1 320px; }

/* ── Summary scoreboard wrap + per-score sub-line ──────────────────── */
.fr-rate-mgr-summary-wrap { margin: 2px 0 4px; }
.fr-rm-score-sub {
    font-family: var(--fr-font-sans);
    font-size: 11px;
    color: var(--fr-ink-3);
    margin-top: 7px;
}

/* ── Lead Demand pill (property-level, summary band) ───────────────── */
.fr-rm-demand-pill {
    font-family: var(--fr-font-sans);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.02em;
}
.fr-rm-demand-pill.is-hot    { color: var(--fr-positive); }
.fr-rm-demand-pill.is-normal { color: var(--fr-ink-2); }
.fr-rm-demand-pill.is-cold   { color: var(--fr-negative); }
.fr-rm-demand-pill.is-none   { color: var(--fr-ink-3); }
.fr-rm-demand-detail { margin-top: 4px; }

/* ── Detail pop-out modals: expand affordance + modal body ─────────── */
/* Summary-score header holds the title + (optional) expand icon inline. */
.fr-rm-score-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
}
/* A context-zone figure + its expand icon (board group-header cells). The
   figure grows to fill the cell so the icon sits right beside it (top-right),
   not stranded at the far edge. */
.fr-rm-zone-cell {
    display: flex;
    align-items: flex-start;
    gap: 8px;
}
.fr-rm-zone-cell > :first-child {
    flex: 1 1 auto;
    min-width: 0;
}
/* The expand icon — a small bordered button beside the figure. Quiet by
   default, accent on hover, so the board stays calm but the figure clearly
   signals "there's more behind this." */
.fr-rm-expand-btn {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    border: 1px solid var(--fr-hairline);
    background: var(--fr-paper, #FFFFFF);
    color: var(--fr-ink-2);
    font-size: 14px;
    line-height: 1;
    border-radius: 6px;
    cursor: pointer;
    opacity: 0.85;
    transition: opacity 0.12s ease, color 0.12s ease, background 0.12s ease, border-color 0.12s ease;
}
.fr-rm-expand-btn:hover {
    opacity: 1;
    color: var(--fr-accent, #B45309);
    background: var(--fr-accent-soft, #FEF3C7);
    border-color: var(--fr-accent, #B45309);
}
.fr-rm-expand-btn:focus-visible {
    outline: 2px solid var(--fr-accent, #B45309);
    outline-offset: 1px;
    opacity: 1;
}

/* Detail modal body: intro/footnote text + the one-line numeric read. */
.fr-rm-detail-modal .modal-body { padding-top: 8px; }
.fr-rm-modal-intro {
    font-family: var(--fr-font-sans);
    font-size: 12.5px;
    color: var(--fr-ink-2);
    margin: 0 0 10px;
    max-width: 70ch;
}
.fr-rm-modal-foot {
    font-family: var(--fr-font-sans);
    font-size: 11px;
    color: var(--fr-ink-3);
    margin: 8px 0 0;
    max-width: 80ch;
}
.fr-rm-modal-read {
    display: flex;
    align-items: baseline;
    gap: 8px;
    flex-wrap: wrap;
    margin: 0 0 8px;
}
.fr-rm-modal-read-text {
    font-family: var(--fr-font-sans);
    font-size: 13px;
    color: var(--fr-ink);
    font-weight: 500;
}

/* ── Benchmark save/reset controls (pace comparison set) ───────────── */
.fr-rate-mgr-benchmark-actions {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 8px;
}
.fr-rate-mgr-benchmark-btn {
    font-size: 11px;
    padding: 4px 10px;
}
.fr-rate-mgr-benchmark-status {
    font-family: var(--fr-font-sans);
    font-size: 11px;
    margin-left: 2px;
}

/* ── Push controls ─────────────────────────────────────────────────── */
.fr-rate-mgr-push-bar { margin-top: 16px; }
.fr-rate-mgr-modal-note {
    font-family: var(--fr-font-sans);
    font-size: 11.5px;
    color: var(--fr-ink-3);
    margin-top: 10px;
    line-height: 1.5;
}

/* ── Data table (rate board / modal preview / audit log) ───────────── */
.fr-rm-table-wrap {
    background: var(--fr-surface);
    border: 1px solid var(--fr-hairline);
    border-radius: var(--fr-r-lg);
    overflow-x: auto;
    box-shadow: 0 1px 0 rgba(20, 24, 29, 0.02);
}
.fr-rm-table {
    width: 100%;
    border-collapse: collapse;
    font-family: var(--fr-font-sans);
    font-size: 13px;
    color: var(--fr-ink);
}
.fr-rm-table thead th {
    background: #FBFAF6;
    color: var(--fr-ink-3);
    font-size: 10.5px;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    text-align: left;
    padding: 13px 16px;
    border-bottom: 1px solid var(--fr-hairline-strong);
    white-space: nowrap;
}
.fr-rm-table tbody td {
    padding: 11px 16px;
    border-bottom: 1px solid var(--fr-hairline);
    vertical-align: middle;
}
.fr-rm-table--compact thead th { padding: 10px 14px; }
.fr-rm-table--compact tbody td { padding: 9px 14px; }
.fr-rm-table .is-right { text-align: right; }
.fr-rm-table .fr-cell-money {
    font-family: var(--fr-font-mono);
    font-variant-numeric: tabular-nums;
}

/* Option B column model: the pricing zone (name + rate cluster) is pinned to
   fixed widths so the rates hug the floor-plan name; the context zone
   (Pre-Lease Pace · Renewal Pace · Comp) absorbs the remaining width, leaving
   room for the planned pace/comp modals. */
.fr-rm-table th.fr-rm-col-name { width: 220px; }
.fr-rm-table th.fr-rm-col-rate { width: 250px; }

/* Hairline divider between the pricing zone and the context zone. Lives on the
   first context column (Pre-Lease Pace) in both head and body so it runs as one
   continuous rule down the table. */
.fr-rm-table th.fr-rm-zone,
.fr-rm-table td.fr-rm-zone { border-left: 1px solid var(--fr-hairline); }

/* Unit-type group header row — warm tint + accent top rule delineate groups */
.fr-rm-group-row td {
    background: #FBF6EC;
    border-top: 2px solid rgba(180, 83, 9, 0.28);
    border-bottom: 1px solid var(--fr-hairline-strong);
}
.fr-rm-group-row:first-child td { border-top: 0; }
.fr-rm-group-name { font-weight: 600; color: var(--fr-ink); }
.fr-rm-strong { font-weight: 600; color: var(--fr-ink); }

/* Space-option sub-row cell */
.fr-rm-so-cell { display: flex; align-items: center; padding-left: 14px; }
.fr-rm-so-label { color: var(--fr-ink-2); }
.fr-rm-check { display: inline-block; margin-right: 9px; vertical-align: middle; }

/* Editable new-rate input (inherits .fr-leaseup-page .form-control base) */
.fr-rm-input.form-control {
    width: 104px;
    height: 32px;
    padding: 4px 10px;
    font-family: var(--fr-font-mono);
    font-variant-numeric: tabular-nums;
}

/* Tight pricing cluster: current → [input] [inline Δ], hugging the name. */
.fr-rm-rate-cell {
    display: flex;
    align-items: center;
    gap: 9px;
    white-space: nowrap;
}
.fr-rm-cur {
    font-family: var(--fr-font-mono);
    font-variant-numeric: tabular-nums;
    color: var(--fr-ink-2);
}
.fr-rm-arrow { color: var(--fr-ink-3); }
.fr-rm-delta-inline {
    font-family: var(--fr-font-mono);
    font-variant-numeric: tabular-nums;
    font-size: 11.5px;
    font-weight: 600;
}

/* Context row: "rates as of …" info + the demoted Refresh link, inline. */
.fr-rate-mgr-context-row {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    margin-bottom: 6px;
}
/* Demoted "Refresh from Entrata" — quiet accent text-link, not a button. */
.fr-rm-refresh-link {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    background: none;
    border: 0;
    cursor: pointer;
    padding: 2px 6px;
    font-family: var(--fr-font-sans);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.04em;
    color: var(--fr-accent);
    border-radius: var(--fr-r-sm);
    transition: background 0.12s ease, color 0.12s ease;
}
.fr-rm-refresh-link:hover { background: var(--fr-accent-soft); color: var(--fr-accent-strong); }
.fr-rm-refresh-link:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(180, 83, 9, 0.12); }
.fr-rm-refresh-ico { font-size: 13px; line-height: 1; }

/* ── Inline status / delta / comp colorings ────────────────────────── */
.fr-rm-subtle {
    font-family: var(--fr-font-sans);
    font-size: 11.5px;
    color: var(--fr-ink-3);
}
/* ── Pace cell (Variant D): leased % headline + surfaced gap + mini bar ─────
   leased % is the headline (labeled "leased"); the gap ("▼ 16% behind") is
   colored and sits beside it; a mini progress bar fills to leased % with a dark
   tick at the ideal — "how far behind" reads as a visible distance. Mirrors the
   New Leases percentile-bar idiom. */
.fr-rm-pace { line-height: 1.3; min-width: 132px; }
.fr-rm-pace-top {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
}
.fr-rm-pace-leased {
    font-family: var(--fr-font-mono);
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    font-size: 16px;
    color: var(--fr-ink);
    white-space: nowrap;
}
.fr-rm-pace-lab {
    font-family: var(--fr-font-sans);
    font-weight: 500;
    font-size: 11px;
    color: var(--fr-ink-3);
    margin-left: 3px;
}
.fr-rm-pace-gap {
    font-family: var(--fr-font-sans);
    font-weight: 600;
    font-size: 12px;
    white-space: nowrap;
}
.fr-rm-pace-gap.dir-ahead  { color: var(--fr-positive); }
.fr-rm-pace-gap.dir-behind { color: var(--fr-negative); }
.fr-rm-pace-gap.dir-on     { color: var(--fr-ink-2); }
.fr-rm-pace-gap.dir-none   { color: var(--fr-ink-3); font-weight: 500; }
.fr-rm-pacebar {
    position: relative;
    height: 8px;
    border-radius: 5px;
    background: var(--fr-hairline);
    margin-top: 7px;
}
.fr-rm-pacebar-fill {
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    border-radius: 5px;
    transition: width 0.25s ease-out;
}
.fr-rm-pacebar-fill.is-ahead  { background: var(--fr-positive); }
.fr-rm-pacebar-fill.is-behind { background: var(--fr-negative); }
.fr-rm-pacebar-fill.is-on     { background: var(--fr-accent); }
.fr-rm-pacebar-fill.is-none   { background: var(--fr-hairline-strong); }
/* Ideal tick — a dark vertical line that pokes just beyond the track. */
.fr-rm-pacebar-mark {
    position: absolute;
    top: -3px;
    bottom: -3px;
    width: 2px;
    background: var(--fr-ink);
    border-radius: 1px;
}
.fr-rm-pace-ideal {
    font-size: 11px;
    color: var(--fr-ink-3);
    margin-top: 5px;
}
/* Count-fallback primary (capacity unknown — no bar). */
.fr-rm-pace-primary {
    font-family: var(--fr-font-mono);
    font-variant-numeric: tabular-nums;
    font-weight: 600;
    margin-right: 8px;
    color: var(--fr-ink);
}
.fr-rm-status { font-weight: 600; font-size: 12px; }
.fr-rm-status.is-ahead  { color: var(--fr-positive); }
.fr-rm-status.is-on     { color: var(--fr-ink-2); }
.fr-rm-status.is-behind { color: var(--fr-negative); }
.fr-rm-status.is-none   { color: var(--fr-ink-3); }
.fr-rm-delta {
    font-family: var(--fr-font-mono);
    font-variant-numeric: tabular-nums;
    font-weight: 600;
}
.fr-rm-delta.is-up   { color: var(--fr-positive); }
.fr-rm-delta.is-down { color: var(--fr-negative); }
.fr-rm-delta.is-flat { color: var(--fr-ink-3); }
.fr-rm-comp { font-weight: 600; font-variant-numeric: tabular-nums; }
.fr-rm-comp.is-strong { color: var(--fr-positive); }
.fr-rm-comp.is-mid    { color: var(--fr-warning); }
.fr-rm-comp.is-weak   { color: var(--fr-negative); }
.fr-rm-comp.is-none   { color: var(--fr-ink-3); }

/* ── Comp cell: market-position dot-line (ported from the New Leases card) ───
   A line spanning the market best-price min→max, a hollow dot per competitor,
   and a filled strength-colored "You" dot (with a caret) marking our rate. */
.fr-rm-comp-cell { min-width: 150px; }
.fr-rm-comp-head {
    display: flex;
    align-items: baseline;
    gap: 8px;
    font-size: 12.5px;
}
.fr-rm-comp-you-rate {
    font-family: var(--fr-font-mono);
    font-variant-numeric: tabular-nums;
    font-weight: 600;
    color: var(--fr-ink-2);
}
.fr-rm-comp-n { color: var(--fr-ink-3); font-size: 11px; }
.fr-rm-comp-line {
    position: relative;
    height: 6px;
    border-radius: 100px;
    background: var(--fr-hairline);
    box-shadow: inset 0 0 0 1px var(--fr-hairline-strong);
    margin: 16px 0 6px;   /* top room for the You caret */
}
.fr-rm-comp-track { position: absolute; inset: 0; border-radius: 100px; }
.fr-rm-comp-dot {
    position: absolute;
    top: 50%;
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: var(--fr-paper, #FFFFFF);
    border: 1px solid rgba(20, 24, 29, 0.35);
    transform: translate(-50%, -50%);
    z-index: 3;
}
.fr-rm-comp-you {
    position: absolute;
    top: 50%;
    width: 14px;
    height: 14px;
    border-radius: 50%;
    background: var(--fr-ink);
    border: 2px solid var(--fr-paper, #FFFFFF);
    box-shadow: 0 0 0 1px var(--fr-ink);
    transform: translate(-50%, -50%);
    z-index: 5;
}
.fr-rm-comp-you.is-strong { background: var(--fr-positive); box-shadow: 0 0 0 1px var(--fr-positive); }
.fr-rm-comp-you.is-mid    { background: var(--fr-warning);  box-shadow: 0 0 0 1px var(--fr-warning); }
.fr-rm-comp-you.is-weak   { background: var(--fr-negative); box-shadow: 0 0 0 1px var(--fr-negative); }
.fr-rm-comp-you-caret {
    position: absolute;
    bottom: 100%;
    width: 0;
    height: 0;
    border-left: 4px solid transparent;
    border-right: 4px solid transparent;
    border-top: 5px solid var(--fr-ink);
    transform: translate(-50%, -3px);
    z-index: 6;
}
.fr-rm-comp-you-caret.is-strong { border-top-color: var(--fr-positive); }
.fr-rm-comp-you-caret.is-mid    { border-top-color: var(--fr-warning); }
.fr-rm-comp-you-caret.is-weak   { border-top-color: var(--fr-negative); }
.fr-rm-comp-ends {
    display: flex;
    justify-content: space-between;
    font-family: var(--fr-font-mono);
    font-variant-numeric: tabular-nums;
    font-size: 11px;
    color: var(--fr-ink-3);
}

/* Audit-log specifics */
.fr-rm-notes { color: var(--fr-ink-2); font-size: 12px; }
.fr-rm-flag {
    font-family: var(--fr-font-sans);
    font-size: 9px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--fr-warning-deep);
    background: var(--fr-warning-tint);
    padding: 2px 6px;
    border-radius: 4px;
    margin-left: 6px;
}

/* Error / warning context-line states */
.fr-rm-error { color: var(--fr-negative-deep); font-family: var(--fr-font-mono); font-size: 11px; }
.fr-rm-warn  { color: var(--fr-warning-deep); font-family: var(--fr-font-mono); font-size: 11px; }

/* Empty / loading note */
.fr-rate-mgr-empty {
    font-family: var(--fr-font-serif);
    font-style: italic;
    font-size: 15px;
    color: var(--fr-ink-3);
    padding: 14px 4px;
}

/* ─────────────────────────────────────────────────────────────────────
   Eviction Watch panel (app/layouts/eviction_watch.py)
   Read-only delinquency → eviction risk forecast. Scoped under .fr-evw-page.
   ───────────────────────────────────────────────────────────────────── */
.fr-evw-page { max-width: 1280px; }

/* Portfolio summary tiles */
.fr-evw-stats {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 14px;
    margin: 4px 0 18px;
}
.fr-evw-stat {
    background: var(--fr-surface);
    border: 1px solid var(--fr-hairline);
    border-radius: var(--fr-r-md);
    padding: 16px 18px;
}
.fr-evw-stat-value {
    font-family: var(--fr-font-serif);
    font-size: 30px;
    line-height: 1.1;
    color: var(--fr-ink);
}
.fr-evw-stat-value--accent { color: var(--fr-accent); }
.fr-evw-stat-label {
    font-family: var(--fr-font-sans);
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--fr-ink-3);
    margin-top: 6px;
}

/* Portfolio horizon row */
.fr-evw-horizon-row {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    background: var(--fr-surface);
    border: 1px solid var(--fr-hairline);
    border-radius: var(--fr-r-md);
    padding: 12px 16px;
    margin-bottom: 20px;
}
.fr-evw-horizon-title {
    font-family: var(--fr-font-sans);
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--fr-ink-3);
    margin-right: 8px;
}
.fr-evw-hz-cell {
    display: flex;
    align-items: baseline;
    gap: 6px;
    padding: 2px 12px;
    border-left: 1px solid var(--fr-hairline);
}
.fr-evw-hz-num { font-family: var(--fr-font-serif); font-size: 20px; color: var(--fr-ink); }
.fr-evw-hz-label { font-size: 12px; color: var(--fr-ink-2); }

/* Risk chips */
.fr-evw-chip {
    font-family: var(--fr-font-sans);
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    padding: 2px 8px;
    border-radius: 999px;
    white-space: nowrap;
}
.fr-evw-chip--critical { color: #fff;                 background: var(--fr-negative); }
.fr-evw-chip--high     { color: var(--fr-negative-deep); background: var(--fr-negative-tint); }
.fr-evw-chip--elevated { color: var(--fr-warning-deep);  background: var(--fr-warning-tint); }
.fr-evw-chip--watch    { color: var(--fr-ink-2);         background: var(--fr-hairline); }
.fr-evw-chip--low      { color: var(--fr-ink-3);         background: var(--fr-hairline); }

/* Per-property collapsible sections */
.fr-evw-props { display: flex; flex-direction: column; gap: 10px; }
.fr-evw-prop {
    background: var(--fr-surface);
    border: 1px solid var(--fr-hairline);
    border-radius: var(--fr-r-md);
    overflow: hidden;
}
.fr-evw-prop > summary {
    cursor: pointer;
    list-style: none;
    padding: 14px 18px;
}
.fr-evw-prop > summary::-webkit-details-marker { display: none; }
.fr-evw-prop[open] > summary { border-bottom: 1px solid var(--fr-hairline); }
.fr-evw-prop-summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
}
.fr-evw-prop-id { display: flex; align-items: baseline; gap: 10px; }
.fr-evw-prop-name { font-family: var(--fr-font-serif); font-size: 19px; color: var(--fr-ink); }
.fr-evw-prop-state {
    font-family: var(--fr-font-mono);
    font-size: 11px;
    color: var(--fr-ink-3);
    border: 1px solid var(--fr-hairline-strong);
    border-radius: 4px;
    padding: 1px 5px;
}
.fr-evw-prop-metrics { display: flex; gap: 18px; flex-wrap: wrap; }
.fr-evw-prop-metric { font-size: 13px; color: var(--fr-ink-2); }
.fr-evw-prop-metric strong { color: var(--fr-ink); font-weight: 600; }
.fr-evw-prop-metric--accent strong { color: var(--fr-accent); }
.fr-evw-horizon-strip { display: flex; gap: 12px; flex-wrap: wrap; }
.fr-evw-horizon-bit { font-size: 12px; color: var(--fr-ink-3); }
.fr-evw-horizon-bit strong { color: var(--fr-ink-2); }

/* Flagged-lease table */
.fr-evw-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.fr-evw-table thead th {
    font-family: var(--fr-font-sans);
    font-size: 10px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--fr-ink-3);
    text-align: left;
    padding: 10px 14px;
    border-bottom: 1px solid var(--fr-hairline);
}
.fr-evw-table tbody td {
    padding: 10px 14px;
    border-bottom: 1px solid var(--fr-hairline);
    color: var(--fr-ink-2);
    vertical-align: top;
}
.fr-evw-table tbody tr:last-child td { border-bottom: none; }
.fr-evw-table tbody tr:nth-child(even) { background: var(--fr-bg); }
.fr-evw-td-unit { font-family: var(--fr-font-mono); color: var(--fr-ink); white-space: nowrap; }
.fr-evw-td-num { font-family: var(--fr-font-mono); white-space: nowrap; }
.fr-evw-td-why { color: var(--fr-ink-3); font-size: 12px; max-width: 320px; }

/* Excluded benign-tail footnote — reconciles the visible list with the full
   delinquency count without alarming the reader. */
.fr-evw-minor-note {
    margin: 8px 0 2px;
    padding: 8px 14px 0;
    font-family: var(--fr-font-sans);
    font-size: 12px;
    color: var(--fr-ink-3);
    border-top: 1px dashed var(--fr-hairline);
}
.fr-evw-nl-minor { color: var(--fr-ink-3); }

/* Note + empty/error states */
.fr-evw-note {
    margin-top: 18px;
    font-family: var(--fr-font-sans);
    font-size: 12.5px;
    line-height: 1.5;
    color: var(--fr-ink-3);
}
/* Workman page — keep the lede legible sans, not the editorial italic serif. */
.fr-evw-page .fr-page-lede {
    font-family: var(--fr-font-sans);
    font-style: normal;
    font-size: 15px;
    color: var(--fr-ink-2);
}
/* Unit Type column */
.fr-evw-td-utype {
    font-family: var(--fr-font-mono);
    font-size: 12px;
    color: var(--fr-ink);
    white-space: nowrap;
}
.fr-evw-empty {
    background: var(--fr-surface);
    border: 1px solid var(--fr-hairline);
    border-radius: var(--fr-r-md);
    padding: 40px 28px;
    text-align: center;
}
.fr-evw-empty-title { font-family: var(--fr-font-serif); font-size: 20px; color: var(--fr-ink); margin-bottom: 6px; }
.fr-evw-empty-sub { font-size: 14px; color: var(--fr-ink-3); }

@media (max-width: 900px) {
    .fr-evw-stats { grid-template-columns: repeat(2, 1fr); }
}

/* Eviction Watch — New Leases surfacing (subtle property-scoped strip) */
.fr-evw-nl {
    margin: 0 0 18px;
    background: var(--fr-warning-tint);
    border: 1px solid var(--fr-hairline);
    border-left: 3px solid var(--fr-warning);
    border-radius: var(--fr-r-md);
    overflow: hidden;
}
.fr-evw-nl > summary {
    cursor: pointer;
    list-style: none;
    padding: 11px 16px;
}
.fr-evw-nl > summary::-webkit-details-marker { display: none; }
.fr-evw-nl-headline {
    display: flex;
    align-items: baseline;
    gap: 12px;
    flex-wrap: wrap;
    font-size: 13px;
    color: var(--fr-ink-2);
}
.fr-evw-nl-mark { color: var(--fr-warning); font-size: 14px; }
.fr-evw-nl-title {
    font-family: var(--fr-font-sans);
    font-weight: 600;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--fr-warning-deep);
}
.fr-evw-nl-dash { color: var(--fr-ink-3); }
.fr-evw-nl-stat strong { color: var(--fr-ink); font-weight: 600; }
.fr-evw-nl-stat--accent strong { color: var(--fr-accent); }
.fr-evw-nl-chev { margin-left: auto; color: var(--fr-ink-3); transition: transform 0.15s ease; }
.fr-evw-nl[open] .fr-evw-nl-chev { transform: rotate(90deg); }
.fr-evw-nl-body {
    padding: 4px 8px 10px;
    background: var(--fr-surface);
    border-top: 1px solid var(--fr-hairline);
}
.fr-evw-nl-note {
    margin: 8px 8px 2px;
    font-family: var(--fr-font-sans);
    font-size: 12px;
    line-height: 1.5;
    color: var(--fr-ink-3);
}
