/* Self-hosted fonts (2026-09). Previously loaded from
   fonts.googleapis.com / fonts.gstatic.com, which sent every visitor's
   IP to Google (PDPA/GDPR), added a render-blocking third-party
   request, and left the installed PWA with no fonts offline. Files
   live in /fonts and are precached by the service worker. Licensing:
   see /fonts/OFL.txt (all three families are SIL Open Font License).

   Manrope and Noto Sans Thai are single variable fonts with a `wght`
   axis — one @font-face each with a weight range, not one per weight.
   (They previously shipped as 5 and 4 byte-identical copies of the
   same VF file, downloaded separately because the HTTP cache keys on
   URL.) DM Mono is not variable, so it keeps its two static cuts. */
@font-face { font-family: 'Manrope'; src: url('/fonts/manrope-400.woff2') format('woff2'); font-weight: 200 800; font-style: normal; font-display: swap; }
@font-face { font-family: 'DM Mono'; src: url('/fonts/dm-mono-400.woff2') format('woff2'); font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: 'DM Mono'; src: url('/fonts/dm-mono-500.woff2') format('woff2'); font-weight: 500; font-style: normal; font-display: swap; }
@font-face { font-family: 'Noto Sans Thai'; src: url('/fonts/noto-sans-thai-400-thai.woff2') format('woff2'); font-weight: 100 900; font-style: normal; font-display: swap; unicode-range: U+0E00-0E7F; }

:root {
  --ink: #17171b;
  --muted: #6b6b76;
  --faint: #9b9ba5;
  --line: #e7e7eb;
  --canvas: #f7f7f8;
  --surface: #ffffff;
  --lavender: #7766ed;
  --lavender-dark: #5f50d7;
  --lavender-pale: #f0efff;
  --mint: #e5faf3;
  --mint-ink: #177858;
  --amber: #fff6df;
  --amber-ink: #a85d00;
  --danger: #be3d54;
  --shadow: 0 12px 30px rgba(22, 22, 27, .055), 0 2px 6px rgba(22, 22, 27, .025);
  /* Compact density pass 2026-09: radius pulled in from 18/11 so the
     surfaces read as an ops tool rather than a consumer app. */
  --radius: 14px;
  --radius-sm: 9px;

  /* UI/UX pass 2026-09: shared layout + spacing scale.
     Before this, page width was uncapped and spacing values were
     invented per-component (margin-bottom 12/14/17/21/34, gap 8..24).
     --measure caps the reading width; --space-* is a 4pt scale. */
  --measure: 1360px;         /* default content cap */
  --measure-wide: 1600px;    /* opt-in for wide data tables */
  --shell-rail: 64px;        /* collapsed sidebar width */
  --shell-rail-open: 248px;  /* expanded / pinned sidebar width */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
  --space-7: 48px;
  --space-8: 64px;
  --gutter: 24px;            /* page-container side padding (desktop) */
  --row-h: 34px;             /* target data-table row height */

  /* Type scale (2026-09). Before this there were 33 distinct
     font-size values across ~430 declarations — 11 of them under
     13px alone. These tokens are the intended steps; the small tier
     (2xs..lg) has been swept onto them. The heading tier (18px+) is
     still a mix of literals and responsive clamp() — a follow-up. */
  --fs-2xs: 9px;      /* micro badges, DM Mono counters */
  --fs-xs: 10px;      /* sub-labels, pill text, cell meta */
  --fs-sm: 11px;      /* eyebrows, field hints, table headers */
  --fs-md: 12.5px;    /* secondary text, table cells — the workhorse */
  --fs-base: 14px;    /* body default */
  --fs-lg: 16px;      /* emphasis body, small headings */
  --fs-xl: 20px;      /* panel / dialog section headings (h2 in a panel) */
  --fs-2xl: 24px;     /* mobile page-title override */
  --fs-3xl: 28px;     /* focal h1 (login) */
  /* Big-number / stat displays are a separate rhythm from headings —
     a KPI figure and a section head at the same px read wrong. */
  --fs-num-sm: 18px;  /* compact stat tiles, profile stats, sub-metrics */
  --fs-num: 22px;     /* standard KPI value */
  --fs-num-lg: 28px;  /* hero figure (dashboard / analytics headline) */
}

* { box-sizing: border-box; }
html { min-height: 100%; background: var(--canvas); }

/* The baht symbol (U+0E3F) is a Thai character that Manrope doesn't
   carry. Without an explicit Thai font in the stack, the browser falls
   back to a system font that often renders the baht in a heavy weight
   or substitutes a chunky glyph. We declare a 'Baht Display' face with
   a unicode-range of just U+0E3F so it only kicks in for that one
   character, then list it first in the body font-family. The result
   is a baht that renders the same crisp Thai glyph on every system,
   and the surrounding Latin text still uses Manrope. */
@font-face {
  font-family: 'Baht Display';
  src: local('Noto Sans Thai'), local('Sarabun'), local('Segoe UI'), local('Arial Unicode MS');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0E3F;
}
@font-face {
  font-family: 'Baht Display';
  src: local('Noto Sans Thai Bold'), local('Noto Sans Thai'), local('Sarabun Bold'), local('Segoe UI Bold'), local('Arial Unicode MS');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0E3F;
}
body { margin: 0; min-height: 100%; color: var(--ink); background: var(--canvas); font-family: 'Baht Display', 'Noto Sans Thai', Manrope, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; font-size: var(--fs-base); line-height: 1.5; }

/* A few places render the baht inside a bold context (e.g. .cell-strong,
   .smart-analytics-value, <strong>). Reset the baht to weight 400 so it
   keeps the same visual weight as the surrounding digits and doesn't
   look chunky next to Manrope's already-bold 600/700 cuts. Use a small
   margin so the baht sits visually attached to the number that follows
   it. */
strong .baht, b .baht, .cell-strong .baht, .smart-analytics-value .baht,
.smart-analytics-note .baht, .top-customers-mrr .baht, .top-customers-arr .baht,
.renewal-bucket-amount .baht, .mini-plan-mrr .baht, .donut-legend-mrr .baht {
  font-weight: 400;
}

/* Lined (tabular) digits on every headline figure and money value so
   columns of numbers align and a changing dashboard KPI doesn't jitter
   as the digit widths shift. Manrope's tabular set is only a hair wider
   than its proportional one. */
.stat-card strong, .smart-analytics-value, .kpi-tile-value, .kpi-inline,
.stat-tile .stat-value, .cell-strong, .stat-value, .receipt-amount,
.top-customers-mrr, .top-customers-arr, .renewal-bucket-amount,
.mini-plan-mrr, .donut-legend-mrr, .dashboard-kpi-value {
  font-variant-numeric: tabular-nums;
}
.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }

/* Skip link. Previously had NO rule at all, so it rendered as a
   visible in-flow flex sibling of .main-content and stole ~180px of
   width on mobile (where the sidebar is display:none). Now it is
   off-canvas until focused, then drops in at the top-left. */
.skip-link {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 999;
  transform: translateY(-120%);
  margin: 8px;
  padding: 10px 16px;
  border-radius: 10px;
  background: var(--ink);
  color: #fff;
  font-size: var(--fs-md);
  font-weight: 700;
  box-shadow: var(--shadow);
  transition: transform 140ms ease;
}
.skip-link:focus, .skip-link:focus-visible {
  transform: translateY(0);
  outline: 3px solid var(--lavender);
  outline-offset: 3px;
}

button, input, textarea, select { font: inherit; }
button, a { -webkit-tap-highlight-color: transparent; }
a { color: inherit; text-decoration: none; }
button { cursor: pointer; }
a:focus-visible, button:focus-visible, summary:focus-visible { outline: 3px solid var(--lavender); outline-offset: 3px; }

html, body { overflow-x: hidden; }
.app-shell { display: flex; min-height: 100vh; }
.sidebar { position: fixed; inset: 0 auto 0 0; z-index: 40; display: flex; width: var(--shell-rail); flex-direction: column; justify-content: space-between; padding: 16px 9px 12px; background: #ffffff; color: var(--ink); border-right: 1px solid var(--line); box-sizing: border-box; overflow: hidden; transition: width 180ms ease, padding 180ms ease; }
.sidebar-top { display: flex; flex: 1 1 auto; min-height: 0; flex-direction: column; min-width: 0; overflow-y: auto; }

/* 2026-09: the sidebar is now a plain toggle — collapsed rail (icons) or
   expanded (labels), switched by the button in the header, remembered via
   the `sb` cookie. Hover-to-peek was removed: it reflowed the layout on an
   accidental graze and moved the toggle out from under the cursor. */
.nav-link { font-size: 0; justify-content: center; transition: font-size 150ms ease; }
.sidebar-pinned .sidebar .nav-link { font-size: var(--fs-md); justify-content: flex-start; }
.brand { font-size: 0; transition: font-size 150ms ease; display: inline-flex; width: fit-content; align-items: center; gap: 9px; color: var(--ink); font-weight: 800; letter-spacing: -1px; }
.sidebar-pinned .sidebar .brand { font-size: 18px; }
.workspace-label { font-size: 0; opacity: 0; transition: opacity 150ms ease; }
.sidebar-pinned .sidebar .workspace-label { font-size: var(--fs-2xs); opacity: 1; }
.account-copy { font-size: 0; opacity: 0; transition: opacity 150ms ease; }
.account-action span { font-size: 0; opacity: 0; transition: opacity 150ms ease; }
.sidebar-pinned .sidebar .account-copy { font-size: var(--fs-sm); opacity: 1; }
.sidebar-pinned .sidebar .account-action span { font-size: var(--fs-xs); opacity: 1; }
/* Collapsed rail: the Settings / Sign-out buttons stack into an icon column. */
.app-shell:not(.sidebar-pinned) .account-actions { flex-direction: column; }

/* v47-T2: icons inside the sidebar MUST have explicit width
   and height. Without this, an inline SVG with only viewBox
   defaults to 300Ã—150 in some browsers, which causes the
   sidebar to scroll horizontally and the icon to be a tiny
   dot in the corner. The collapsed-sidebar rules above hide
   the text labels; these rules keep the icons visible at the
   intended size. */
.nav-icon { display: inline-flex; width: 18px; height: 18px; align-items: center; justify-content: center; flex: 0 0 18px; color: var(--lavender, #7766ed); }
.nav-icon svg { width: 18px; height: 18px; display: block; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.nav-sub .nav-icon { width: 14px; height: 14px; }
.nav-sub .nav-icon svg { width: 14px; height: 14px; }

/* v47-T2: when the sidebar is hovered, the main content shifts
   right to give the rest of the page more room. The ~ selector
   works because .sidebar and .main-content are adjacent
   siblings in the app.ejs layout. */
.main-content { width: calc(100% - var(--shell-rail)); min-width: 0; min-height: calc(100vh - var(--shell-rail)); margin-left: var(--shell-rail); }

/* Pinned = expanded, and it now applies at EVERY width (was >=1280px only,
   which is why the collapse toggle "didn't work" on smaller screens). The
   default is pinned unless the user collapsed it (sb=0 cookie). Below 720px
   the sidebar is display:none and the mobile menu takes over, so these
   rules are naturally inert there. */
.sidebar-pinned .sidebar { width: var(--shell-rail-open); padding: 16px 14px 12px; }
.sidebar-pinned .main-content { width: calc(100% - var(--shell-rail-open)); margin-left: var(--shell-rail-open); }

/* Collapse toggle: always visible, works at every width and on every
   screen in the app (it was hover-only and gated to >=1280px before).
   In the collapsed rail the brand mark + toggle stack so neither is
   crowded; expanded, they sit on one row. */
.sidebar-brand-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-width: 0; }
.app-shell:not(.sidebar-pinned) .sidebar-brand-row { flex-direction: column; gap: 9px; }
.sidebar-pin-toggle { display: grid; flex: 0 0 auto; width: 26px; height: 26px; place-items: center; border: 1px solid var(--line); border-radius: 7px; background: var(--surface); color: var(--muted); cursor: pointer; transition: color .15s, border-color .15s, background .15s, transform .15s; }
.sidebar-pin-toggle:hover { color: var(--lavender); border-color: var(--lavender); background: var(--lavender-pale); }
/* Chevron points left when open (click = collapse), right when it's a
   rail (click = expand). */
.sidebar-pinned .sidebar-pin-toggle svg { transform: rotate(0deg); }
.app-shell:not(.sidebar-pinned) .sidebar-pin-toggle svg { transform: rotate(180deg); }
@media (max-width: 720px) { .sidebar-pin-toggle { display: none !important; } }

.sidebar .brand { padding: 0 10px; color: var(--ink); }
.brand-mark { position: relative; display: inline-flex; width: 24px; height: 24px; align-items: end; gap: 2px; padding: 4px; overflow: hidden; border-radius: 7px; background: #8e81ff; transform: rotate(-4deg); }
.brand-mark i { display: block; width: 4px; border-radius: 4px 4px 1px 1px; background: #fff; }
.brand-mark i:nth-child(1) { height: 8px; opacity: .82; }
.brand-mark i:nth-child(2) { height: 13px; }
.brand-mark i:nth-child(3) { height: 17px; opacity: .92; }
.workspace-label { margin: 15px 10px 8px; color: var(--faint); font-family: "DM Mono", monospace; font-size: var(--fs-2xs); letter-spacing: .09em; text-transform: uppercase; }
.side-nav { display: grid; gap: 2px; min-width: 0; }
.nav-link { display: flex; align-items: center; gap: 11px; border-radius: 8px; padding: 7px 9px; color: var(--muted); font-weight: 600; transition: background .16s, color .16s; min-width: 0; }
.nav-sub { padding-left: 30px; font-size: var(--fs-md); font-weight: 500; color: var(--muted); opacity: 0.92; }
.nav-sub .nav-icon { width: 14px; height: 14px; }
.nav-sub.is-active { color: var(--lavender, #8e81ff); font-weight: 600; }
.nav-link:hover { background: var(--canvas); color: var(--ink); }
.nav-link.is-active { background: var(--lavender-pale, #f4f0ff); color: var(--lavender-dark); font-weight: 700; }
.nav-link.is-active .nav-icon { color: var(--lavender-dark); }
.nav-icon { width: 17px; color: #a59bff; font-family: Georgia, serif; font-size: 21px; font-weight: 400; line-height: 13px; text-align: center; flex: 0 0 17px; }
.nav-icon-property { display: grid; height: 18px; place-items: center; }
.nav-icon-property svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2; }
.account-menu { display: flex; flex-direction: column; gap: 8px; border-top: 1px solid var(--line); padding: 10px 3px 0; min-width: 0; }
.account-row { display: flex; align-items: center; gap: 8px; min-width: 0; padding: 0 3px; }
.avatar { display: grid; width: 26px; height: 26px; flex: 0 0 26px; place-items: center; border-radius: 50%; background: var(--lavender-pale); color: var(--lavender-dark); font-size: var(--fs-xs); font-weight: 800; }
.account-copy { min-width: 0; flex: 1; }
.account-copy strong, .account-copy span { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.account-copy strong { color: var(--ink); font-size: var(--fs-sm); font-weight: 700; }
.account-copy span { color: var(--faint); font-family: "DM Mono", monospace; font-size: var(--fs-2xs); letter-spacing: .05em; text-transform: uppercase; }

/* Settings + Sign out — a matched pair of compact buttons on one row
   (name/role above them shrank; Sign out was a big full-width button). */
.account-actions { display: flex; gap: 5px; }
.account-logout { display: flex; flex: 1 1 0; min-width: 0; }
.account-action {
  flex: 1 1 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-width: 0;
  width: 100%;
  padding: 6px 7px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--surface);
  color: var(--muted);
  font-size: var(--fs-xs);
  font-weight: 700;
  cursor: pointer;
  transition: color 130ms ease, border-color 130ms ease, background 130ms ease;
}
.account-action span { white-space: nowrap; }
.account-action svg { flex: 0 0 auto; width: 14px; height: 14px; }
.account-action:hover,
.account-action.is-active { color: var(--lavender-dark); border-color: var(--lavender); background: var(--lavender-pale); }
.account-action-danger:hover { color: var(--danger); border-color: var(--danger); background: #fff7f8; }
.account-action:focus-visible { outline: 2px solid var(--lavender); outline-offset: 1px; }
.account-install-button[hidden] { display: none; }
.icon-button { display: grid; width: 30px; height: 30px; place-items: center; border: 0; border-radius: 8px; background: transparent; color: var(--muted); font-size: 18px; transition: .18s; }
.icon-button:hover { background: var(--lavender-pale, #f4f0ff); color: var(--lavender, #8e81ff); }

.page-container { width: 100%; max-width: var(--measure); margin: 0 auto; padding: var(--space-5) var(--gutter) var(--space-7); }
/* Opt-in for data-dense pages (portfolio table, system log) that
   genuinely use the extra width. */
.page-container--wide { max-width: var(--measure-wide); }
.mobile-header { display: none; }
.public-shell { background: var(--canvas); }
.public-main { width: 100%; padding: 25px 54px 70px; }
.public-nav { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 20px; }
.public-nav > .button { justify-self: end; }
.public-nav-links { display: flex; align-items: center; justify-content: center; gap: 23px; }
.public-nav-links a { color: var(--muted); font-size: var(--fs-sm); font-weight: 800; transition: color .18s; }
.public-nav-links a:hover, .public-nav-links a[aria-current="page"] { color: var(--lavender); }
.landing-page { width: min(1220px, 100%); margin: 38px auto 0; }
.landing-hero { position: relative; display: grid; grid-template-columns: minmax(0, 1.02fr) minmax(390px, .98fr); align-items: stretch; gap: clamp(28px, 5vw, 72px); overflow: hidden; border: 1px solid var(--line); border-radius: 28px; padding: clamp(26px, 5vw, 66px); background: linear-gradient(140deg, var(--lavender-pale), var(--surface) 54%, var(--mint)); box-shadow: var(--shadow); }
.landing-copy { position: relative; z-index: 1; display: flex; min-height: 460px; flex-direction: column; align-items: flex-start; justify-content: center; }
.landing-copy h1 { max-width: 650px; margin: 10px 0 18px; font-size: clamp(42px, 5.45vw, 72px); font-weight: 800; letter-spacing: -3.6px; line-height: 1.01; }
.landing-copy > p:last-of-type { max-width: 560px; margin: 0; color: var(--muted); font-size: var(--fs-base); line-height: 1.8; }
.landing-actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 28px; }
.landing-actions .button { min-height: 44px; padding-inline: 17px; }
.landing-points { display: flex; flex-wrap: wrap; gap: 9px 18px; margin: 31px 0 0; padding: 0; color: var(--muted); font-size: var(--fs-sm); font-weight: 800; list-style: none; }
.landing-points li { display: inline-flex; align-items: center; gap: 6px; }
.landing-points span { display: grid; width: 17px; height: 17px; place-items: center; border-radius: 50%; background: var(--mint); color: var(--mint-ink); font-size: var(--fs-xs); }
.landing-showcase { position: relative; min-height: 460px; overflow: hidden; border-radius: 21px; background: var(--ink); box-shadow: var(--shadow); isolation: isolate; }
.landing-showcase::after { position: absolute; inset: 0; z-index: 4; background: linear-gradient(180deg, transparent 32%, color-mix(in srgb, var(--ink) 66%, transparent)); content: ''; pointer-events: none; }
.landing-orb { position: absolute; z-index: 0; border-radius: 50%; background: var(--lavender); filter: blur(1px); opacity: .46; }
.landing-orb-one { width: 250px; height: 250px; top: -90px; right: -80px; animation: landing-drift 12s ease-in-out infinite alternate; }
.landing-orb-two { width: 190px; height: 190px; bottom: -82px; left: -68px; background: var(--mint); animation: landing-drift 14s ease-in-out -4s infinite alternate-reverse; }
.landing-showcase-label { position: absolute; top: 20px; left: 20px; z-index: 5; display: inline-flex; align-items: center; gap: 7px; border: 1px solid color-mix(in srgb, var(--surface) 24%, transparent); border-radius: 999px; padding: 7px 10px; background: color-mix(in srgb, var(--ink) 62%, transparent); color: var(--surface); font-family: "DM Mono", monospace; font-size: var(--fs-2xs); letter-spacing: .055em; text-transform: uppercase; backdrop-filter: blur(12px); }
.landing-pulse { width: 6px; height: 6px; border-radius: 50%; background: var(--mint); box-shadow: 0 0 0 5px color-mix(in srgb, var(--mint) 24%, transparent); animation: landing-pulse 2.4s ease-out infinite; }
.landing-photo-stack { position: absolute; inset: 0; z-index: 1; }
.landing-photo { position: absolute; margin: 0; overflow: hidden; border: 4px solid color-mix(in srgb, var(--surface) 88%, transparent); border-radius: 17px; box-shadow: var(--shadow); transform-origin: center; }
.landing-photo img { display: block; width: 100%; height: 100%; object-fit: cover; filter: saturate(.92) contrast(1.04); animation: landing-photo-zoom 15s ease-in-out infinite alternate; }
.landing-photo-1 { width: 57%; height: 63%; top: 15%; left: 7%; z-index: 3; transform: rotate(-6deg); animation: landing-float-one 8s ease-in-out infinite; }
.landing-photo-2 { width: 37%; height: 43%; top: 10%; right: 6%; z-index: 2; transform: rotate(7deg); animation: landing-float-two 9s ease-in-out -2s infinite; }
.landing-photo-3 { width: 40%; height: 40%; right: 9%; bottom: 8%; z-index: 4; transform: rotate(-4deg); animation: landing-float-three 8.5s ease-in-out -4s infinite; }
.landing-photo-4 { width: 28%; height: 31%; bottom: 7%; left: 8%; z-index: 2; transform: rotate(8deg); animation: landing-float-two 10s ease-in-out -1s infinite; }
.landing-visual-empty { display: grid; height: 100%; place-items: center; color: var(--surface); font-family: Georgia, serif; font-size: 90px; }
.landing-showcase-card { position: absolute; right: 19px; bottom: 18px; left: 19px; z-index: 5; display: flex; align-items: center; gap: 10px; border: 1px solid color-mix(in srgb, var(--surface) 22%, transparent); border-radius: 13px; padding: 12px; background: color-mix(in srgb, var(--ink) 74%, transparent); color: var(--surface); backdrop-filter: blur(12px); }
.landing-card-icon { display: grid; width: 33px; height: 33px; flex: 0 0 33px; place-items: center; border-radius: 9px; background: var(--lavender-pale); color: var(--lavender-dark); }
.landing-card-icon svg { width: 18px; height: 18px; fill: currentColor; }
.landing-showcase-card strong, .landing-showcase-card > div > span { display: block; }.landing-showcase-card strong { font-size: var(--fs-sm); }.landing-showcase-card > div > span { margin-top: 1px; color: color-mix(in srgb, var(--surface) 73%, transparent); font-size: var(--fs-2xs); }
.landing-value { display: grid; grid-template-columns: minmax(220px, .68fr) minmax(0, 1.32fr); gap: 42px; margin-top: 21px; border: 1px solid var(--line); border-radius: var(--radius); padding: clamp(24px, 4vw, 44px); background: var(--surface); box-shadow: var(--shadow); }
.landing-value h2 { max-width: 290px; margin: 8px 0 0; font-size: clamp(27px, 3.5vw, 42px); letter-spacing: -1.8px; line-height: 1.08; }
.landing-value-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px; }.landing-value-grid article { min-width: 0; border-radius: 13px; padding: 17px; background: var(--canvas); }.landing-value-icon { display: grid; width: 30px; height: 30px; place-items: center; border-radius: 9px; background: var(--lavender-pale); color: var(--lavender-dark); font-size: var(--fs-lg); }.landing-value-grid h3 { margin: 17px 0 7px; font-size: var(--fs-md); letter-spacing: -.35px; }.landing-value-grid p { margin: 0; color: var(--muted); font-size: var(--fs-xs); line-height: 1.65; }
.public-info-page { width: min(980px, 100%); margin: 42px auto 0; }
.public-info-hero { padding: clamp(30px, 6vw, 68px); border: 1px solid var(--line); border-radius: 27px; background: linear-gradient(130deg, var(--lavender-pale), var(--surface) 65%, var(--mint)); box-shadow: var(--shadow); }
.public-info-hero h1 { max-width: 710px; margin: 9px 0 16px; font-size: clamp(38px, 5vw, 64px); letter-spacing: -3px; line-height: 1.04; }
.public-info-hero > p:last-of-type { max-width: 650px; margin: 0; color: var(--muted); font-size: var(--fs-base); line-height: 1.75; }
.public-info-hero .button { margin-top: 25px; }
.public-info-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; margin-top: 18px; }
.public-info-card { min-height: 224px; border: 1px solid var(--line); border-radius: var(--radius); padding: 28px; background: var(--surface); box-shadow: var(--shadow); }
.public-info-card h2 { margin: 0 0 13px; font-size: var(--fs-lg); letter-spacing: -.6px; }
.public-info-card p { margin: 0; color: var(--muted); font-size: var(--fs-md); line-height: 1.75; }
.public-info-card p + p { margin-top: 12px; }
.public-brick-gallery { display: grid; grid-auto-flow: dense; grid-auto-rows: 96px; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 16px; }
.public-gallery-card { position: relative; grid-row: span 4; min-width: 0; overflow: hidden; border: 0; border-radius: var(--radius); padding: 0; background: var(--lavender-pale); box-shadow: var(--shadow); color: inherit; font: inherit; text-align: left; }
.public-gallery-card.is-featured { grid-column: span 2; grid-row: span 6; }
.public-gallery-card.is-wide { grid-column: span 2; grid-row: span 4; }
.public-gallery-card.is-tall { grid-row: span 5; }
.public-gallery-media { position: absolute; inset: 0; }
.public-gallery-media::after { position: absolute; inset: 35% 0 0; background: linear-gradient(transparent, var(--ink)); content: ''; }
.public-gallery-media img { width: 100%; height: 100%; object-fit: cover; transition: transform .45s ease; }
.public-gallery-card:hover .public-gallery-media img { transform: scale(1.04); }
.public-gallery-copy { position: absolute; right: 0; bottom: 0; left: 0; z-index: 1; padding: 18px; color: var(--surface); }
.public-gallery-copy .status-badge { margin-bottom: 22px; background: var(--mint); color: var(--mint-ink); }
.public-gallery-copy h2 { margin: 0 0 3px; font-size: var(--fs-base); letter-spacing: -.4px; }
.public-gallery-copy p { margin: 0; font-size: var(--fs-xs); line-height: 1.65; opacity: .9; }
.public-gallery-unit { display: block; margin-top: 11px; color: var(--surface); font-size: var(--fs-2xs); font-weight: 800; opacity: .78; }
.public-empty-state { margin-top: 10px; }
.public-condo-dialog { width: min(1040px, calc(100% - 34px)); max-height: min(810px, calc(100vh - 34px)); overflow: auto; border: 0; border-radius: 22px; padding: 0; background: var(--surface); box-shadow: var(--shadow); color: var(--ink); }
.public-condo-dialog::backdrop { background: color-mix(in srgb, var(--ink) 58%, transparent); }
.public-condo-dialog-frame { position: relative; min-height: 0; }
.public-condo-dialog-close { position: absolute; top: 15px; right: 15px; z-index: 2; display: grid; width: 34px; height: 34px; place-items: center; border: 1px solid var(--line); border-radius: 50%; background: var(--surface); color: var(--ink); font-size: 23px; line-height: 1; box-shadow: var(--shadow); }
.public-condo-dialog-close:hover { color: var(--lavender); }
.public-condo-dialog-content { min-height: 0; }
.public-dialog-loading { display: grid; min-height: 350px; place-items: center; margin: 0; color: var(--muted); font-size: var(--fs-md); font-weight: 700; }
.public-dialog-layout { display: grid; grid-template-columns: minmax(0, 1.3fr) minmax(280px, .7fr); min-height: 0; }
.public-dialog-gallery { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); grid-auto-rows: 185px; gap: 8px; padding: 8px; background: var(--canvas); }
.public-dialog-photo { margin: 0; overflow: hidden; border-radius: 14px; background: var(--lavender-pale); }
.public-dialog-photo:first-child { grid-column: span 2; grid-row: span 2; }
.public-dialog-photo img { width: 100%; height: 100%; object-fit: cover; }
.public-dialog-photo-empty { display: grid; grid-column: span 2; min-height: 220px; place-items: center; border-radius: 14px; background: var(--lavender-pale); color: var(--lavender); font-family: Georgia, serif; font-size: 70px; }
.public-dialog-details { display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 28px 32px; }
.public-dialog-details .status-badge { margin-bottom: 23px; }
.public-dialog-details h2 { margin: 0 0 10px; font-size: clamp(26px, 3vw, 38px); letter-spacing: -1.4px; line-height: 1.12; }
.public-dialog-address { margin: 0; color: var(--muted); font-size: var(--fs-md); line-height: 1.75; white-space: pre-line; }
.public-dialog-metadata { width: 100%; margin: 31px 0 0; border-top: 1px solid var(--line); }
.public-dialog-metadata > div { display: flex; align-items: baseline; justify-content: space-between; gap: 15px; border-bottom: 1px solid var(--line); padding: 13px 0; }
.public-dialog-metadata dt { color: var(--faint); font-family: "DM Mono", monospace; font-size: var(--fs-2xs); text-transform: uppercase; }
.public-dialog-metadata dd { margin: 0; color: var(--ink); font-size: var(--fs-sm); font-weight: 800; text-align: right; }
.page-heading { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--space-5); margin-bottom: var(--space-5); }
.page-heading h1, .login-card h1, .empty-state h1 { margin: 4px 0 7px; font-size: clamp(27px, 3vw, 35px); font-weight: 800; letter-spacing: -1.6px; line-height: 1.17; }
/* Was implicit (a plain block div), fine while every page only ever put
   one child in it. The reports page (2026-09-04) puts a date-range
   picker AND an export button side by side, so it needs a real row
   layout. */
.page-heading-actions { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.page-heading > div > p:last-child { margin: 0; color: var(--muted); font-size: var(--fs-md); }
.eyebrow { margin: 0; color: #8175e7; font-family: "DM Mono", monospace; font-size: var(--fs-xs); font-weight: 500; letter-spacing: .1em; text-transform: uppercase; }
.back-link { display: inline-block; margin-bottom: 22px; color: var(--muted); font-size: var(--fs-md); font-weight: 700; transition: color .18s; }
.back-link:hover { color: var(--lavender); }
.narrow-heading { margin-bottom: 25px; }
.detail-heading { align-items: center; }
.title-with-status { display: flex; align-items: center; gap: 19px; }

.button { display: inline-flex; min-height: 38px; align-items: center; justify-content: center; gap: 8px; border: 1px solid transparent; border-radius: 9px; padding: 8px 14px; font-size: var(--fs-md); font-weight: 800; transition: background .18s, border-color .18s, transform .18s, box-shadow .18s; }
.button:hover { transform: translateY(-1px); }
.button:disabled, .button[disabled] { cursor: not-allowed; opacity: .5; transform: none; }
.button-primary { background: var(--ink); color: #fff; box-shadow: 0 3px 8px rgba(23, 23, 27, .15); }
.button-primary:hover { background: #303038; box-shadow: 0 6px 13px rgba(23, 23, 27, .17); }
.button-secondary { border-color: #dedee4; background: var(--surface); color: #4b4b53; }
.button-secondary:hover { border-color: #bdbdc8; background: #fafafa; }
.button-danger { background: var(--danger); color: #fff; box-shadow: 0 3px 8px rgba(190, 61, 84, .18); }
.button-danger:hover { background: #a43246; box-shadow: 0 6px 13px rgba(190, 61, 84, .22); }
.button-danger-outline { border-color: #f0c9d0; background: #fff7f8; color: var(--danger); }
.button-danger-outline:hover { border-color: var(--danger); background: #ffeef0; }
.button-ghost { border-color: transparent; background: transparent; color: var(--muted); font-weight: 700; }
.button-ghost:hover { border-color: var(--line); background: var(--surface); color: var(--ink); }
.button-large { min-height: 46px; width: 100%; font-size: var(--fs-md); }
.button span { font-size: var(--fs-lg); font-weight: 400; line-height: 12px; }
.text-link { color: #6257c7; font-size: var(--fs-md); font-weight: 800; }
.text-link:hover { color: #4236a8; }
.text-link span { margin-left: 4px; font-size: var(--fs-lg); }
.row-actions { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 14px; justify-content: flex-end; }
.row-action-danger { color: var(--danger); }
.row-action-danger:hover { color: #8a2a3c; }
.row-action-icon { display: inline-grid; place-items: center; width: 28px; height: 28px; border-radius: 8px; color: var(--danger); text-decoration: none; transition: background-color .12s ease, color .12s ease; }
.row-action-icon:hover { background-color: #fdecef; color: #8a2a3c; }
.row-action-icon svg { display: block; }

.stats-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-2); margin-bottom: var(--space-2); }
.stats-grid-three { grid-template-columns: repeat(3, 1fr); }
.stat-card { min-height: 72px; padding: 10px 13px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface); box-shadow: 0 1px 0 rgba(20, 20, 26, .015); }
.stat-card-accent { border-color: #ddd9ff; background: #f6f5ff; }
.stat-label, .stat-note { display: block; }
.stat-label { color: var(--muted); font-family: "DM Mono", monospace; font-size: var(--fs-xs); letter-spacing: .025em; }
.stat-card strong { display: block; margin: 4px 0 3px; font-size: var(--fs-num); font-weight: 800; letter-spacing: -1.1px; }
.stat-note { color: var(--faint); font-size: var(--fs-2xs); line-height: 1.3; }
.content-grid { display: grid; gap: var(--space-3); margin-bottom: var(--space-3); }
.content-grid-half { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--space-3); }
.content-grid-half > .panel { margin-bottom: 0; }
@media (max-width: 980px) { .content-grid-half { grid-template-columns: 1fr; } }
.two-up { grid-template-columns: minmax(0, 1.6fr) minmax(260px, 1fr); }
.panel, .form-panel { border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface); box-shadow: 0 1px 0 rgba(20, 20, 26, .015); }
.panel { padding: 13px 16px; }
.panel-heading { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-3); margin-bottom: var(--space-3); }
.panel-heading h2, .panel h2, .empty-state h2 { margin: 2px 0 0; font-size: var(--fs-lg); letter-spacing: -.5px; }
.panel-highlight { display: flex; min-height: 245px; flex-direction: column; justify-content: center; border-color: #e0ddff; background: linear-gradient(135deg, #f2f1ff, #fbfbff 72%); }
.panel-highlight h2 { margin: 6px 0 9px; font-size: var(--fs-lg); }
.panel-highlight > p:not(.eyebrow) { max-width: 270px; margin: 0 0 19px; color: var(--muted); font-size: var(--fs-md); line-height: 1.7; }
.compact-list { display: grid; }
.compact-row { display: flex; min-width: 0; align-items: center; gap: 10px; padding: 11px 0; border-top: 1px solid #eeeef1; }
.compact-row:first-child { border-top: 0; padding-top: 0; }
.compact-row:last-child { padding-bottom: 0; }
.home-mark { display: grid; width: 34px; height: 34px; flex: 0 0 34px; place-items: center; border-radius: 10px; background: #f0efff; color: #7669e1; font-family: Georgia, serif; font-size: 21px; }
.compact-main { min-width: 0; flex: 1; }
.compact-main strong, .compact-main span { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.compact-main strong { font-size: var(--fs-sm); }
.compact-main span { margin-top: 2px; color: var(--muted); font-size: var(--fs-xs); }
.status-badge { display: inline-flex; width: fit-content; align-items: center; gap: 5px; border-radius: 999px; padding: 5px 8px; font-size: var(--fs-2xs); font-weight: 800; }
.status-badge i { width: 5px; height: 5px; border-radius: 50%; background: currentColor; }
.status-for-rent,
.status-available { background: var(--mint); color: var(--mint-ink); }
.status-for-sale { background: var(--mint); color: var(--mint-ink); }
.status-for-rent-and-sale { background: var(--mint); color: var(--mint-ink); }
.status-rented { background: var(--lavender-pale); color: var(--lavender-dark); }
.status-under-maintenance,
.status-maintenance { background: var(--amber); color: var(--amber-ink); }
.table-panel { padding: 14px 0 0; overflow: hidden; }
.table-panel .panel-heading { padding: 0 18px; margin-bottom: 10px; }
/* Horizontal scroll hint. When a wide table (e.g. the six-column rent
   collection summary) overflows inside .table-wrap, fade the left and
   right edges so the user knows there are more columns off-screen. The
   two cover gradients use background-attachment: local so they follow
   the scroll position; the shadow gradients use scroll so they stay
   anchored. */
.table-wrap {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  /* Fix (2026-09-04): overflow-x:auto alone contains the table
     visually and gives it its own scrollbar, but a sufficiently wide
     table (e.g. an unbroken run of characters that can't wrap) could
     still inflate document.documentElement's reported scrollWidth on
     mobile, dragging the WHOLE PAGE into a horizontal scroll on
     touch/wheel rather than just this box — `overflow-x: hidden` on
     html/body did not stop it. `contain: paint` makes the box a hard
     containment boundary: nothing inside it can affect an ancestor's
     (or the root's) scrollable area, which is exactly the guarantee
     `overflow` alone doesn't make. Confirmed live: without this,
     document.documentElement.scrollWidth was 732px at a 375px
     viewport; with it, 375px. */
  contain: paint;
  background:
    linear-gradient(to right, var(--surface) 30%, rgba(255, 255, 255, 0)) left center / 24px 100% no-repeat local,
    linear-gradient(to left, var(--surface) 30%, rgba(255, 255, 255, 0)) right center / 24px 100% no-repeat local,
    radial-gradient(farthest-side at 0 50%, rgba(0, 0, 0, 0.12), rgba(0, 0, 0, 0)) left center / 12px 100% no-repeat scroll,
    radial-gradient(farthest-side at 100% 50%, rgba(0, 0, 0, 0.12), rgba(0, 0, 0, 0)) right center / 12px 100% no-repeat scroll;
}
table { width: 100%; border-collapse: collapse; text-align: left; table-layout: auto; }
th { padding: 0 10px 8px; color: var(--faint); font-family: "DM Mono", monospace; font-size: var(--fs-xs); font-weight: 400; letter-spacing: .06em; text-transform: uppercase; }
td { padding: 6px 10px; border-top: 1px solid #eeeeF1; color: var(--muted); font-size: var(--fs-md); line-height: 1.35; max-width: 260px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.table-action-column { max-width: none; overflow: visible; white-space: nowrap; }

/* Compact mode: opt-in class for tables that need to fit more rows
   per viewport (e.g. tenants/owners lists). Keeps columns legible
   but tightens padding, line-height, and gaps so each row is
   ~50% shorter than the default. */
table.is-compact th { padding-bottom: 4px; font-size: var(--fs-2xs); }
table.is-compact td { padding: 4px 10px; line-height: 1.3; font-size: var(--fs-sm); }
table.is-compact .person-cell strong { font-size: var(--fs-xs); }
table.is-compact .person-cell span { font-size: var(--fs-2xs); }
table.is-compact .avatar-small { width: 22px; height: 22px; flex-basis: 22px; font-size: var(--fs-2xs); }
table.is-compact .lease-window { gap: 1px; }
table.is-compact .lease-window > div { display: flex; align-items: baseline; gap: 4px; }
table.is-compact .lease-due { display: none; }
table.is-compact .lease-badge { margin-top: 2px; padding: 1px 6px; font-size: var(--fs-2xs); }
table.is-compact .cell-strong { font-size: var(--fs-sm); }
table.is-compact .cell-sub { font-size: var(--fs-2xs); line-height: 1.25; }
table.is-compact .code-pill-small { font-size: var(--fs-2xs); padding: 1px 5px; }
table.is-compact .contact-channel { min-width: 0; margin-right: 4px; font-size: var(--fs-2xs); }
table.is-compact .contact-text { font-size: var(--fs-xs); }
table.is-compact .contact-link { font-size: var(--fs-xs); }
table.is-compact .lease-dates { font-size: var(--fs-xs); color: var(--ink); white-space: nowrap; }
table.is-compact .lease-dates .cell-sub { display: inline; font-size: var(--fs-2xs); color: var(--faint); margin-left: 4px; }

/* Portfolio table: fixed layout, respect column widths, never overflow
   the viewport horizontally.
   Fix (2026-09-04, part 1): this whole block targeted
   `table.portfolio-table` (a class), but the template only ever puts
   `data-portfolio-table` (an attribute, no matching class) on the
   element — so none of this ever applied. Without table-layout:fixed
   the table sized itself to its widest natural content and forced its
   ancestors wider than the viewport, which on mobile dragged the whole
   page into a horizontal scroll (not just the intended .table-wrap).
   Selector switched to the attribute that's actually on the markup.
   Fix (2026-09-04, part 2): gated to >=721px. At true mobile widths,
   forcing 6 columns into table-layout:fixed divides the row evenly
   into ~60px slices regardless of content — "Property" wrapped one
   character per line. Below 720px the table now keeps its natural
   sizing and relies on .table-wrap's own horizontal scroll (with its
   edge-fade hint) instead of squeezing; the portfolio page's default
   Cards view is the intended way to browse a portfolio on a phone,
   this only covers a user who explicitly forces ?view=table there. */
@media (min-width: 721px) {
  table[data-portfolio-table] { table-layout: fixed; }
  table[data-portfolio-table] col { width: auto; }
  table[data-portfolio-table] th, table[data-portfolio-table] td { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  table[data-portfolio-table] th[hidden], table[data-portfolio-table] td[hidden] { display: none; }
  /* The tenant / lease / next-due cells are wider and benefit from
     showing the address on a second line — let them wrap. */
  table[data-portfolio-table] td[data-column-key="tenant"],
  table[data-portfolio-table] td[data-column-key="lease"],
  table[data-portfolio-table] td[data-column-key="next_due"],
  table[data-portfolio-table] td[data-column-key="property"] {
    white-space: normal;
  }
  table[data-portfolio-table] td[data-column-key="tenant"] .cell-sub,
  table[data-portfolio-table] td[data-column-key="lease"] .cell-sub,
  table[data-portfolio-table] td[data-column-key="next_due"] .cell-sub {
    display: block;
    font-size: var(--fs-2xs);
    color: var(--faint);
    line-height: 1.3;
  }
}
/* Below 721px: th[hidden]/td[hidden] still needs to hide de-selected
   columns (that part of the rule isn't width-related), just without
   the fixed-layout squeeze. */
@media (max-width: 720px) {
  table[data-portfolio-table] th[hidden], table[data-portfolio-table] td[hidden] { display: none; }
}
tr[data-href] { cursor: pointer; transition: background-color .12s ease; }
tr[data-href]:hover td { background-color: var(--lavender-pale); }
tr[data-href]:hover { background-color: var(--lavender-pale); }
th[data-column-key="property"], td[data-column-key="property"] { max-width: 220px; }
th[data-column-key="status"], td[data-column-key="status"] { max-width: 95px; }
th[data-column-key="area"], td[data-column-key="area"] { max-width: 110px; }
th[data-column-key="location"], td[data-column-key="location"] { max-width: 120px; }
th[data-column-key="address"], td[data-column-key="address"] { max-width: 180px; }
th[data-column-key="unit"], td[data-column-key="unit"] { max-width: 75px; }
th[data-column-key="bedrooms"], td[data-column-key="bedrooms"] { max-width: 60px; }
th[data-column-key="bathrooms"], td[data-column-key="bathrooms"] { max-width: 60px; }
th[data-column-key="view"], td[data-column-key="view"] { max-width: 100px; }
th[data-column-key="pet_policy"], td[data-column-key="pet_policy"] { max-width: 90px; }
th[data-column-key="monthly_rent"], td[data-column-key="monthly_rent"] { max-width: 110px; text-align: right; }
.cell-num { font-variant-numeric: tabular-nums; white-space: nowrap; }
th[data-column-key="tenant"], td[data-column-key="tenant"] { max-width: 130px; }
th[data-column-key="lease"], td[data-column-key="lease"] { max-width: 110px; }
th[data-column-key="next_due"], td[data-column-key="next_due"] { max-width: 140px; }
.table-pagination { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 10px; padding: 12px 23px; border-top: 1px solid var(--line); background: var(--canvas); }
.table-pagination-single { justify-content: flex-end; }
.pagination-info { color: var(--muted); font-size: var(--fs-sm); }
.pagination-links { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.pagination-prev, .pagination-next, .pagination-page { display: inline-flex; align-items: center; justify-content: center; min-width: 28px; height: 28px; padding: 0 8px; border: 1px solid var(--line); border-radius: 6px; color: var(--ink); font-size: var(--fs-sm); text-decoration: none; background: var(--surface); }
.pagination-prev:hover, .pagination-next:hover, .pagination-page:hover { background: var(--lavender-pale); color: var(--lavender-dark); border-color: var(--lavender); }
.pagination-page.is-current { background: var(--ink); color: #fff; border-color: var(--ink); }
.pagination-prev.is-disabled, .pagination-next.is-disabled { color: var(--faint); cursor: not-allowed; background: transparent; border-color: transparent; }
.pagination-ellipsis { color: var(--faint); padding: 0 4px; font-size: var(--fs-sm); }
.person-cell { display: flex; align-items: center; gap: 9px; min-width: 0; }
.person-cell > div { min-width: 0; }
/* Fix (2026-09-04): a name/owner value with no spaces (a paste error,
   a stress-test string, anything) has nowhere to soft-wrap, so
   overflow-wrap: anywhere (set globally on th/td) was forcing it to
   break character-by-character instead — a single 255-char value
   turned a table row from ~40px into ~370px tall. line-clamp caps
   each value at a fixed line count with an ellipsis no matter how
   long or space-free the underlying string is; normal short values
   are completely unaffected either way.
   Found during the 768px tablet pass: a 1-line clamp on .person-cell
   strong also caught perfectly normal, shortish property names
   ("City Garden Tower", "Northpoint Pattaya") once the column
   narrowed — they used to wrap onto a 2nd line (the portfolio
   table's own td[data-column-key="property"] rule already sets
   white-space: normal specifically so names can wrap), so 1-line
   clamp was a readability regression on top of the bug fix. The
   name gets 2 lines — still a hard, predictable cap (2 lines of a
   255-char value is harmless, nowhere near the original 369px row);
   the subtitle (owner name / role) stays at 1, since those are
   reliably short. */
.person-cell strong { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.person-cell span { display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical; overflow: hidden; }
.person-cell strong { color: var(--ink); font-size: var(--fs-sm); }
.person-cell span { margin-top: 1px; color: var(--muted); font-size: var(--fs-xs); }
.avatar-small { width: 27px; height: 27px; flex-basis: 27px; font-size: var(--fs-xs); }
.count-pill { border-radius: 7px; padding: 3px 7px; background: #f1f1f4; color: #55555e; font-size: var(--fs-xs); font-weight: 800; }
.inline-empty { display: flex; min-height: 115px; align-items: center; justify-content: center; gap: 8px; color: var(--muted); font-size: var(--fs-sm); text-align: center; }
.inline-empty span { color: #897ee7; font-family: Georgia, serif; font-size: 25px; }

.filter-bar { display: grid; grid-template-columns: minmax(220px, 1fr) minmax(170px, .44fr) auto; align-items: end; gap: var(--space-3); margin-bottom: var(--space-4); border: 1px solid var(--line); border-radius: var(--radius-sm); padding: var(--space-3); background: var(--surface); box-shadow: 0 1px 0 rgba(20, 20, 26, .015); }
/* Collapsible filter shell (mobile/tablet). On <=900px the filter grid
   is wrapped in <details class="filter-collapse"> so the ~8 stacked
   controls don't push the table a full screen down on first load. */
.filter-collapse { margin-bottom: var(--space-4); }
.filter-collapse > summary { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); list-style: none; padding: 11px var(--space-3); border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--surface); font-size: var(--fs-md); font-weight: 800; cursor: pointer; }
.filter-collapse > summary::-webkit-details-marker { display: none; }
.filter-collapse > summary .filter-collapse-count { display: inline-flex; align-items: center; justify-content: center; min-width: 18px; height: 18px; padding: 0 5px; border-radius: 999px; background: var(--lavender); color: #fff; font-size: var(--fs-xs); }
.filter-collapse > summary .filter-collapse-count:empty { display: none; }
.filter-collapse > summary .filter-collapse-chevron { transition: transform .15s ease; color: var(--muted); }
.filter-collapse[open] > summary { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
.filter-collapse[open] > summary .filter-collapse-chevron { transform: rotate(180deg); }
.filter-collapse[open] > .filter-bar { border-top-left-radius: 0; border-top-right-radius: 0; margin-top: -1px; margin-bottom: 0; }
@media (min-width: 901px) { .filter-collapse > summary { display: none; } .filter-collapse > .filter-bar { margin-bottom: 0; } }
.filter-bar .field { gap: 5px; }
.filter-actions { display: flex; min-height: 36px; align-items: center; gap: 12px; }
.filter-actions .button { white-space: nowrap; }

/* Customizable filter bar: the .filter-bar-customizable variant makes the
   grid flow so the layout survives when the user adds/removes criteria.
   NOTE: the previous value was `repeat(auto-fit, minmax(180px,1fr)) auto`
   — a trailing intrinsic (`auto`) track after an auto-fit repeat is
   INVALID, so the browser dropped the whole rule and the portfolio
   filter silently fell back to the rigid 3-column base grid. This is a
   valid auto-fit track list; the action cluster gets its own full row. */
.filter-bar-customizable { grid-template-columns: repeat(auto-fit, minmax(172px, 1fr)); }
.filter-bar-customizable .filter-actions { grid-column: 1 / -1; justify-content: flex-end; min-height: 0; padding-top: var(--space-1); }
.filter-bar-customizable .tag-search-field { grid-column: span 2; }
/* R9: the "Customize filters" control (which criteria fields the bar
   shows) sits on its own top row, right-aligned and set apart from the
   Apply / Clear action cluster at the bottom. Keeps the two view-config
   entry points — this one and the table's "Columns" button — each in an
   obvious, self-explaining place. */
.filter-bar-tools { grid-column: 1 / -1; display: flex; justify-content: flex-end; margin-bottom: calc(var(--space-1) * -1); }
.rent-range-inputs-standalone { display: flex; align-items: center; gap: 8px; }
.rent-range-inputs-standalone input { flex: 1 1 0; min-width: 0; }

/* "Saved view" hint shown above the table when the URL has no query
   string but the agent has a saved default view applied. */
.filter-saved-hint { grid-column: 1 / -1; margin: 4px 0 0; color: var(--lavender); font-size: var(--fs-md); display: flex; align-items: center; gap: 6px; }
.filter-saved-hint-mark { color: var(--lavender); }

/* Customize-filters dialog list. Each row is a clickable card so the
   checkbox can be small and the row remains easy to hit. */
.filter-criteria-list { list-style: none; margin: 14px 0; padding: 0; display: grid; gap: 6px; max-height: 50vh; overflow-y: auto; }
.filter-criteria-item { margin: 0; }
.filter-criteria-toggle { display: grid; grid-template-columns: auto 1fr; gap: 10px; align-items: center; padding: 6px 10px; border: 1px solid var(--line); border-radius: 8px; background: var(--canvas); cursor: pointer; transition: border-color .12s, background .12s; }
.filter-criteria-toggle:hover { border-color: #bdbdc8; background: var(--surface); }
.filter-criteria-toggle:has(input:checked) { border-color: var(--lavender); background: #f0efff; }
.filter-criteria-toggle input { margin: 0; }
.filter-criteria-toggle-info { display: grid; gap: 0; line-height: 1.3; }
.filter-criteria-label { font-size: var(--fs-md); font-weight: 700; }
.filter-criteria-type { color: var(--muted); font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: .04em; }
/* The "Set default values" dialog renders the enabled criteria as a
   compact 2-column grid with a small label above each input. */
.filter-defaults-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px 14px; margin: 12px 0; max-height: 50vh; overflow-y: auto; }
.filter-default-item { display: grid; gap: 4px; }
.filter-default-label { color: var(--muted); font-family: "DM Mono", monospace; font-size: var(--fs-xs); letter-spacing: .04em; text-transform: uppercase; }
.filter-default-item input, .filter-default-item select { width: 100%; }
.filter-criteria-hint { color: var(--muted); font-size: var(--fs-md); }

/* The dialog heading copy is a bit longer than the export dialog so it
   wraps nicely. */
.export-dialog-help { margin: 0 0 6px; color: var(--muted); font-size: var(--fs-md); line-height: 1.5; }
.export-dialog-actions { display: flex; justify-content: flex-end; gap: 8px; flex-wrap: wrap; }
.export-dialog-actions .button-ghost { margin-right: auto; }
.panel-heading-actions { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
/* Table / Cards view switch on /portfolio (the old /condos card page
   is merged in). Segmented control, same language as .granularity-tabs. */
/* Dashboard "show more" disclosure — the rent-due / delayed-bucket /
   tenant-activity tables sit behind this, collapsed by default, so the
   overview fits ~1.5 screens instead of ~3. */
.dash-more { margin-top: var(--space-4); }
.dash-more > summary { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); list-style: none; padding: 11px var(--space-4); border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--surface); color: var(--muted); font-size: var(--fs-md); font-weight: 800; cursor: pointer; }
.dash-more > summary::-webkit-details-marker { display: none; }
.dash-more > summary:hover { color: var(--ink); }
.dash-more[open] > summary { margin-bottom: var(--space-3); }
.dash-more[open] > summary .filter-collapse-chevron { transform: rotate(180deg); }
.dash-more .summary-tables { margin-top: 0; }

.view-switch { display: inline-flex; flex: 0 0 auto; gap: 3px; padding: 3px; border: 1px solid var(--line); border-radius: 999px; background: var(--canvas); }
.view-switch-option { display: inline-flex; align-items: center; gap: 6px; padding: 5px 11px; border-radius: 999px; color: var(--muted); font-size: var(--fs-sm); font-weight: 800; transition: background-color .15s, color .15s; }
.view-switch-option svg { flex: 0 0 auto; }
.view-switch-option:hover { color: var(--ink); }
.view-switch-option.is-active { background: var(--surface); color: var(--lavender-dark); box-shadow: 0 1px 3px rgba(20,20,26,.08); }
@media (max-width: 560px) { .view-switch-option span { display: none; } .view-switch-option { padding: 6px 10px; } }
.button-ghost { border-color: var(--line); background: var(--surface); color: var(--ink); box-shadow: none; }
.button-ghost:hover { border-color: #bdbdc8; background: #fafafa; transform: none; }
.button-small { min-height: 32px; padding: 6px 12px; font-size: var(--fs-sm); }
.column-toggle { position: relative; }
.column-toggle-menu { position: absolute; z-index: 8; top: calc(100% + 6px); right: 0; display: grid; gap: 2px; min-width: 220px; border: 1px solid var(--line); border-radius: 10px; padding: 8px; background: var(--surface); box-shadow: 0 8px 20px rgba(20, 20, 26, .08); }
.column-toggle-menu[hidden] { display: none; }
.column-toggle-heading { margin: 4px 6px 6px; color: var(--muted); font-size: var(--fs-xs); font-weight: 700; letter-spacing: .8px; text-transform: uppercase; }
.column-toggle-row { display: flex; align-items: center; gap: 10px; border-radius: 6px; padding: 6px 8px; cursor: pointer; font-size: var(--fs-md); font-weight: 600; }
.column-toggle-row:hover { background: var(--lavender-pale); }
.column-toggle-row input { accent-color: var(--lavender); }
.column-toggle-footer { display: flex; align-items: center; justify-content: space-between; gap: 8px; border-top: 1px solid var(--line); margin-top: 6px; padding: 8px 6px 2px; }
.column-toggle-status { color: var(--muted); font-size: var(--fs-xs); font-weight: 700; letter-spacing: .4px; text-transform: uppercase; }
.column-toggle-status[data-tone="error"] { color: var(--danger); }
.column-toggle-status[data-tone="success"] { color: var(--lavender-dark); }
.tag-search-control { display: flex; min-height: 36px; flex-wrap: wrap; align-items: center; gap: 6px; border: 1px solid #dedee4; border-radius: 8px; padding: 5px 8px; background: var(--surface); transition: border-color .18s, box-shadow .18s; }
.tag-search-control:focus-within { border-color: #8d80eb; box-shadow: 0 0 0 3px rgba(118, 102, 237, .13); }
.tag-search-chips { display: contents; }
.tag-search-control input { width: auto; min-width: 150px; height: 29px; flex: 1 1 160px; border: 0; padding: 0 2px; box-shadow: none; }
.tag-search-control input:focus { border: 0; box-shadow: none; }
.tag-search-chip { display: inline-flex; max-width: 100%; align-items: center; gap: 5px; border-radius: 999px; padding: 4px 6px 4px 9px; background: var(--lavender-pale); color: var(--lavender-dark); font-size: var(--fs-xs); font-weight: 800; }
.tag-search-remove { display: grid; width: 16px; height: 16px; place-items: center; border: 0; border-radius: 50%; padding: 0; background: var(--surface); color: var(--lavender-dark); font-size: var(--fs-base); line-height: 1; }
.tag-search-remove:hover { background: var(--lavender); color: var(--surface); }
.rent-range { display: grid; gap: 4px; margin-top: 8px; padding: 10px 12px; border: 1px dashed var(--line); border-radius: 8px; background: var(--canvas); }
.rent-range[hidden] { display: none; }
.rent-range-label { color: var(--muted); font-size: var(--fs-xs); font-weight: 700; letter-spacing: .4px; text-transform: uppercase; }
.rent-range-inputs { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.rent-range-inputs input { width: 96px; min-height: 36px; padding: 0 10px; font-variant-numeric: tabular-nums; }
.rent-range-separator { color: var(--muted); font-size: var(--fs-sm); font-weight: 700; }
.editable-dropdown { position: relative; }
.editable-dropdown-control { display: flex; align-items: center; min-height: 36px; overflow: hidden; border: 1px solid #dedee4; border-radius: 8px; background: var(--surface); transition: border-color .18s, box-shadow .18s; }
.editable-dropdown-control:focus-within { border-color: #8d80eb; box-shadow: 0 0 0 3px rgba(118, 102, 237, .13); }
.editable-dropdown-control input { min-width: 0; flex: 1; border: 0; padding-right: 6px; box-shadow: none; }
.editable-dropdown-control input:focus { border: 0; box-shadow: none; }
.editable-dropdown-toggle { display: grid; width: 38px; align-self: stretch; place-items: center; border: 0; border-left: 1px solid var(--line); background: var(--canvas); color: var(--lavender-dark); font-size: var(--fs-lg); line-height: 1; }
.editable-dropdown-toggle:hover { background: var(--lavender-pale); }
.editable-dropdown-menu { position: absolute; z-index: 5; top: calc(100% + 5px); right: 0; left: 0; display: grid; overflow: hidden; border: 1px solid var(--line); border-radius: 8px; background: var(--surface); box-shadow: var(--shadow); }
.editable-dropdown-menu[hidden] { display: none; }
.editable-dropdown-menu button { border: 0; border-bottom: 1px solid var(--line); padding: 10px 12px; background: var(--surface); color: var(--ink); font: inherit; font-size: var(--fs-sm); font-weight: 700; text-align: left; }
.editable-dropdown-menu button:last-child { border-bottom: 0; }
.editable-dropdown-menu button:hover, .editable-dropdown-menu button:focus-visible { background: var(--lavender-pale); color: var(--lavender-dark); }

.condo-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 16px; }
.condo-grid-full { grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); }
.condo-card { display: block; min-width: 0; overflow: hidden; border: 1px solid var(--line); border-radius: 14px; background: #fff; box-shadow: 0 1px 0 rgba(20, 20, 26, .015); transition: border-color .18s, box-shadow .18s, transform .18s; }
.condo-card:hover { border-color: #cfc9ff; box-shadow: var(--shadow); transform: translateY(-2px); }
.condo-card-media { position: relative; aspect-ratio: 4 / 3; max-height: 220px; overflow: hidden; background: #efeeff; }
.condo-card-media img { width: 100%; height: 100%; object-fit: cover; transition: transform .35s ease; }
.condo-card:hover .condo-card-media img { transform: scale(1.035); }
.condo-card-placeholder { display: grid; width: 100%; height: 100%; place-items: center; color: #7766ed; font-family: Georgia, serif; font-size: 50px; }
/* When a <img> src 404s (file missing on disk), the onerror
   handler in the template adds .photo-missing. We swap in a
   controlled placeholder so the page never shows a broken
   icon. Property detail / list / dashboard all use the same
   class so the fix is in one place. */
.condo-card-media img.photo-missing,
.condo-detail-main-photo img.photo-missing,
.condo-detail-thumbnails img.photo-missing,
.condo-photo-thumb img.photo-missing {
  display: none;
}
.condo-card-media:has(img.photo-missing),
.condo-detail-main-photo.photo-missing-button,
.condo-detail-thumbnails button.photo-missing-button,
.condo-photo-thumb:has(img.photo-missing) {
  background: linear-gradient(180deg, #f3f1fb 0%, #ece8f9 100%);
  color: #7766ed;
  display: grid;
  place-items: center;
}
.condo-card-media:has(img.photo-missing)::after,
.condo-photo-thumb:has(img.photo-missing)::after {
  content: "Photo unavailable";
  font-size: var(--fs-md);
  font-weight: 600;
  color: #6c5add;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}
.condo-detail-main-photo.photo-missing-button::after,
.condo-detail-thumbnails button.photo-missing-button::after {
  content: "Photo unavailable";
  font-size: var(--fs-md);
  font-weight: 600;
  color: #6c5add;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}
.condo-card-top { position: absolute; inset: 12px 12px auto; display: flex; align-items: flex-start; justify-content: space-between; }
.condo-card .home-mark { width: 31px; height: 31px; flex-basis: 31px; border-radius: 8px; background: rgba(255, 255, 255, .86); color: #6c5add; font-size: 18px; backdrop-filter: blur(8px); }
.photo-count { position: absolute; right: 12px; bottom: 12px; border-radius: 999px; padding: 5px 8px; background: rgba(23, 23, 27, .72); color: #fff; font-size: var(--fs-2xs); font-weight: 800; backdrop-filter: blur(8px); }
.condo-card-copy { padding: 10px 12px 11px; }
.condo-card h2, .condo-card h3 { margin: 0 0 2px; overflow: hidden; font-size: var(--fs-md); letter-spacing: -.3px; text-overflow: ellipsis; white-space: nowrap; }
.condo-card p { min-height: 26px; margin: 0; color: var(--muted); font-size: var(--fs-2xs); line-height: 1.5; }
.condo-card-footer { display: flex; justify-content: space-between; margin-top: 8px; border-top: 1px solid #f0f0f2; padding-top: 7px; color: #767680; font-size: var(--fs-2xs); font-weight: 700; }
.condo-card-footer span:last-child { color: #6d60d7; font-size: var(--fs-base); line-height: 9px; }
.condo-card-footer .condo-card-label { color: var(--faint); font-size: var(--fs-2xs); line-height: 1.5; }
.soft-notice { display: flex; align-items: center; gap: 12px; margin-top: 17px; border: 1px solid #e1deff; border-radius: 13px; padding: 15px 18px; background: #f4f3ff; }
.notice-icon { display: grid; width: 33px; height: 33px; place-items: center; border-radius: 9px; background: #e2dfff; color: #6558c8; font-size: 19px; }
.soft-notice > div { min-width: 0; flex: 1; }
.soft-notice strong, .soft-notice p { display: block; }
.soft-notice strong { font-size: var(--fs-sm); }
.soft-notice p { margin: 1px 0 0; color: var(--muted); font-size: var(--fs-xs); }

/* 2026-09: the property page now stacks — gallery full-width on top,
   details flowing below. The old two-column split left the photo column
   (which is much shorter than the details column) sticky with a tall
   empty gutter beside every panel. */
.condo-detail-layout { display: flex; flex-direction: column; align-items: stretch; gap: 20px; }
.condo-detail-layout--no-media .condo-detail-media { position: static; }
.condo-detail-media { position: static; display: grid; gap: 12px; }
.condo-detail-main-photo { position: relative; width: 100%; height: clamp(260px, 44vh, 460px); overflow: hidden; border: 0; border-radius: var(--radius); padding: 0; background: var(--lavender-pale); box-shadow: var(--shadow); text-align: left; }
.condo-detail-main-photo img { width: 100%; height: 100%; object-fit: cover; transition: transform .35s ease; }
.condo-detail-main-photo:hover img { transform: scale(1.025); }
.condo-detail-main-photo > span { position: absolute; right: 13px; bottom: 13px; border-radius: 999px; padding: 6px 10px; background: var(--surface); color: var(--ink); font-size: var(--fs-xs); font-weight: 800; box-shadow: var(--shadow); }
.condo-detail-main-photo > span b { margin-left: 4px; color: var(--lavender); font-size: var(--fs-base); line-height: 10px; }
.condo-detail-thumbnails { display: grid; grid-template-columns: repeat(auto-fit, minmax(74px, 1fr)); gap: 9px; }
.condo-detail-thumbnails button { height: 80px; overflow: hidden; border: 0; border-radius: 10px; padding: 0; background: var(--lavender-pale); }
.condo-detail-thumbnails img { width: 100%; height: 100%; object-fit: cover; transition: transform .2s ease; }
.condo-detail-thumbnails button:hover img { transform: scale(1.04); }
.condo-detail-no-photo { display: grid; height: clamp(320px, 52vh, 540px); place-items: center; border-radius: var(--radius); background: var(--lavender-pale); color: var(--lavender); font-family: Georgia, serif; font-size: 68px; }

/* Part 2.10 — Photo carousel on /condos/:id (src/views/condos/detail.ejs).
   Main stage + prev/next chevrons + counter + horizontal thumb strip +
   earlier/later reorder controls. Includes the photo-missing placeholder
   rules for the new class names so missing images render as a "Photo
   unavailable" tile instead of a broken-image icon (mirrors the older
   .condo-card-media / .condo-detail-main-photo rules above). */
.photo-carousel { display: grid; gap: 14px; }
/* Full-width hero on the property page (the layout stacks now). */
.photo-carousel-stage { position: relative; width: 100%; height: clamp(320px, 50vh, 540px); overflow: hidden; border-radius: var(--radius); background: var(--lavender-pale); box-shadow: var(--shadow); }
.photo-carousel-stage-trigger { position: absolute; inset: 0; display: block; width: 100%; height: 100%; border: 0; padding: 0; background: transparent; cursor: zoom-in; text-align: left; }
.photo-carousel-stage img { width: 100%; height: 100%; object-fit: cover; transition: transform .35s ease; }
.photo-carousel-stage-trigger:hover img { transform: scale(1.025); }
.photo-carousel-prev, .photo-carousel-next { position: absolute; top: 50%; transform: translateY(-50%); width: 36px; height: 36px; display: grid; place-items: center; border: 0; border-radius: 999px; background: rgba(255,255,255,.92); color: var(--ink); box-shadow: var(--shadow); cursor: pointer; transition: background .15s, transform .15s, opacity .15s; }
.photo-carousel-prev { left: 10px; }
.photo-carousel-next { right: 10px; }
.photo-carousel-prev:hover, .photo-carousel-next:hover { background: #fff; transform: translateY(-50%) scale(1.06); }
.photo-carousel-prev:disabled, .photo-carousel-next:disabled { opacity: 0; pointer-events: none; }
.photo-carousel-counter { position: absolute; right: 12px; bottom: 12px; border-radius: 999px; padding: 5px 10px; background: rgba(23,23,27,.72); color: #fff; font-size: var(--fs-xs); font-weight: 800; backdrop-filter: blur(8px); letter-spacing: 0.02em; }
.photo-carousel-thumbs { display: grid; grid-template-columns: repeat(auto-fit, minmax(74px, 1fr)); gap: 9px; }
.photo-carousel-thumb { position: relative; height: 80px; overflow: hidden; border: 0; border-radius: 10px; padding: 0; background: var(--lavender-pale); cursor: pointer; outline: 2px solid transparent; transition: outline-color .15s, transform .15s; }
.photo-carousel-thumb img { width: 100%; height: 100%; object-fit: cover; transition: transform .2s ease, opacity .2s; opacity: .78; }
.photo-carousel-thumb:hover img { transform: scale(1.05); opacity: 1; }
.photo-carousel-thumb.is-active { outline-color: var(--lavender); }
.photo-carousel-thumb.is-active img { opacity: 1; }
.photo-carousel-reorder { display: flex; gap: 10px; justify-content: flex-end; align-items: center; padding: 6px 2px 0; }
.condo-photo-arrow { display: inline-flex; align-items: center; gap: 6px; border: 1px solid var(--line); border-radius: 999px; padding: 6px 12px; background: #fff; color: var(--ink); font-size: var(--fs-sm); font-weight: 700; cursor: pointer; transition: border-color .15s, color .15s, background .15s, opacity .15s; }
.condo-photo-arrow:hover:not(:disabled) { border-color: var(--lavender); color: var(--lavender-dark); }
.condo-photo-arrow:disabled { opacity: .4; cursor: not-allowed; }
/* No-photo state: a slim prompt, not a 540px void. */
.photo-carousel-empty { display: grid; height: clamp(160px, 22vh, 220px); place-items: center; border-radius: var(--radius); background: var(--lavender-pale); color: var(--lavender); font-family: Georgia, serif; font-size: 40px; gap: 6px; }
.photo-carousel-empty p { font-family: 'Manrope', system-ui, sans-serif; font-size: var(--fs-sm); font-weight: 700; color: var(--lavender-dark); letter-spacing: .04em; text-transform: uppercase; }
.photo-carousel-empty svg { width: 40px; height: 40px; }
.photo-carousel-stage-trigger img.photo-missing,
.photo-carousel-thumb img.photo-missing { display: none; }
.photo-carousel-stage-trigger:has(img.photo-missing),
.photo-carousel-thumb:has(img.photo-missing) {
  background: linear-gradient(180deg, #f3f1fb 0%, #ece8f9 100%);
  color: #6c5add;
  display: grid;
  place-items: center;
}
.photo-carousel-stage-trigger:has(img.photo-missing)::after,
.photo-carousel-thumb:has(img.photo-missing)::after {
  content: "Photo unavailable";
  font-size: var(--fs-sm);
  font-weight: 700;
  color: #6c5add;
  letter-spacing: .04em;
  text-transform: uppercase;
}

/* Details flow into columns under the full-width gallery so panels sit
   side-by-side instead of one tall stack. "At a glance" keeps the full
   width (it holds ~20 fields and reads best as a wide 3-up list). */
.condo-detail-info { display: grid; gap: 14px; grid-template-columns: 1fr; align-items: start; }
@media (min-width: 900px) {
  .condo-detail-info { grid-template-columns: 1fr 1fr; }
  .condo-detail-info > .details-panel { grid-column: 1 / -1; }
}
.details-panel h2, .notes-panel h2 { margin-bottom: 12px; font-size: var(--fs-lg); }
.detail-list { display: grid; grid-template-columns: 1fr 1fr 1fr; margin: 0; gap: 0 24px; }
.detail-list > div { min-height: 38px; padding: 5px 0; border-top: 1px solid #eeeef1; }
.detail-list > div:nth-child(3n+1) { padding-right: 0; }
.detail-list > div:nth-child(3n+2) { padding-right: 0; padding-left: 24px; }
.detail-list > div:nth-child(3n+3) { padding-left: 24px; }
.detail-list > div:nth-child(3n+2) + .detail-list > div { border-left: 1px solid #eeeef1; }
/* The vertical divider should sit between cells, not on every other cell.
   Use a left border on cells 2 and 3 of each row, and adjust the negative
   margin so the border sits inside the gap. */
.detail-list > div { border-left: 0; }
.detail-list > div:nth-child(3n+2),
.detail-list > div:nth-child(3n+3) { border-left: 1px solid #eeeef1; }
.detail-list dt { margin-bottom: 7px; color: var(--faint); font-family: "DM Mono", monospace; font-size: var(--fs-2xs); text-transform: uppercase; }
.detail-list dd { margin: 0; color: #3f3f46; font-size: var(--fs-sm); font-weight: 700; line-height: 1.6; }
.notes-panel { display: flex; min-height: 80px; flex-direction: column; }
.amenities-panel { display: flex; min-height: 80px; flex-direction: column; }
.amenities-panel h2 { margin-bottom: 16px; }
.amenity-list { display: flex; flex: 1; flex-wrap: wrap; gap: 8px; margin: 0 0 17px; padding: 0; list-style: none; }
.amenity-list li { border-radius: 999px; padding: 6px 9px; background: var(--lavender-pale); color: var(--lavender-dark); font-size: var(--fs-xs); font-weight: 800; }
.notes-copy { flex: 1; margin: 0; color: var(--muted); font-size: var(--fs-md); line-height: 1.75; white-space: pre-line; }
.contact-details-panel { display: flex; min-height: 80px; flex-direction: column; }
.contact-details-panel h2 { margin: 0 0 16px; }
.contact-list { display: grid; flex: 1; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px 20px; margin: 0 0 18px; }
.contact-list > div { min-width: 0; }
.contact-list dt { margin-bottom: 5px; color: var(--faint); font-family: "DM Mono", monospace; font-size: var(--fs-2xs); text-transform: uppercase; }
.contact-list dd { margin: 0; overflow-wrap: anywhere; color: #3f3f46; font-size: var(--fs-sm); font-weight: 700; line-height: 1.6; }
.condo-image-lightbox { width: min(1180px, calc(100% - 32px)); max-width: none; max-height: calc(100vh - 32px); overflow: visible; border: 0; padding: 0; background: transparent; }
.condo-image-lightbox::backdrop { background: color-mix(in srgb, var(--ink) 68%, transparent); }
.condo-image-lightbox-frame { position: relative; display: grid; max-height: calc(100vh - 32px); place-items: center; border-radius: var(--radius); background: var(--ink); box-shadow: var(--shadow); }
.condo-image-lightbox img { display: block; max-width: 100%; max-height: calc(100vh - 32px); object-fit: contain; }
.condo-image-lightbox-close { position: absolute; top: 14px; right: 14px; z-index: 2; display: grid; width: 36px; height: 36px; place-items: center; border: 1px solid var(--line); border-radius: 50%; background: var(--surface); color: var(--ink); font-size: 24px; line-height: 1; box-shadow: var(--shadow); }
.condo-image-lightbox-close:hover { color: var(--lavender); }
.condo-image-lightbox-nav { position: absolute; top: 50%; z-index: 2; display: grid; width: 42px; height: 42px; transform: translateY(-50%); place-items: center; border: 1px solid var(--line); border-radius: 50%; background: var(--surface); color: var(--ink); box-shadow: var(--shadow); cursor: pointer; transition: background .15s, transform .15s; }
.condo-image-lightbox-nav:hover:not(:disabled) { background: var(--lavender-pale); color: var(--lavender); transform: translateY(-50%) scale(1.06); }
.condo-image-lightbox-nav:disabled { opacity: 0; pointer-events: none; }
.condo-image-lightbox-prev { left: 14px; }
.condo-image-lightbox-next { right: 14px; }
.condo-image-lightbox-counter { position: absolute; bottom: 14px; left: 50%; z-index: 2; transform: translateX(-50%); padding: 3px 10px; border-radius: 999px; background: color-mix(in srgb, var(--ink) 62%, transparent); color: var(--surface); font-family: "DM Mono", monospace; font-size: var(--fs-2xs); letter-spacing: .04em; }
@media (max-width: 720px) {
  .condo-image-lightbox-nav { width: 38px; height: 38px; }
  .condo-image-lightbox-prev { left: 8px; }
  .condo-image-lightbox-next { right: 8px; }
}

.form-panel { width: min(900px, 100%); padding: 26px; }
.form-panel-wide { width: min(820px, 100%); }
.condo-form-layout { display: grid; grid-template-columns: minmax(300px, .86fr) minmax(440px, 1.14fr); align-items: start; gap: 18px; }
.condo-form-media, .condo-form-details { border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface); box-shadow: var(--shadow); }
.condo-form-media { position: sticky; top: 26px; padding: 24px; }
.condo-form-media-heading h2 { margin: 4px 0 7px; font-size: var(--fs-lg); letter-spacing: -.8px; }
.condo-form-media-heading > p:last-child { margin: 0 0 19px; color: var(--muted); font-size: var(--fs-sm); line-height: 1.7; }
.condo-photo-input { position: absolute; width: 1px; height: 1px; overflow: hidden; opacity: 0; }
.condo-photo-dropzone { display: grid; min-height: 190px; place-items: center; align-content: center; gap: 6px; border: 1px dashed var(--faint); border-radius: 14px; padding: 22px; background: var(--canvas); color: var(--muted); cursor: pointer; text-align: center; transition: border-color .18s, background .18s; }
.condo-photo-dropzone:hover, .condo-form-media:focus-within .condo-photo-dropzone { border-color: var(--lavender); background: var(--lavender-pale); }
.condo-photo-dropzone strong { color: var(--ink); font-size: var(--fs-md); }
.condo-photo-dropzone > span:last-child { max-width: 210px; font-size: var(--fs-xs); line-height: 1.6; }
.condo-photo-dropzone-mark { display: grid; width: 38px; height: 38px; place-items: center; margin-bottom: 3px; border-radius: 11px; background: var(--surface); color: var(--lavender); font-size: 26px; font-weight: 400; box-shadow: var(--shadow); }
.condo-photo-source-row { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 12px; }
.condo-photo-source-button { flex: 1 1 140px; min-width: 0; padding: 10px 14px; font-size: var(--fs-md); }
.condo-photo-hint { margin-top: 10px; }

/* Tenant document upload (blue book, tenant contract, tenant ID document).
   Mirrors the property photos dropzone so the form reads consistently —
   each upload gets the same "pick a file" surface + Take photo / Choose
   from gallery buttons. The block wrapper turns an inline field into a
   self-contained card so it reads like the property-photos aside even
   when it's nested inside a form-grid. */
.tenant-doc-block { display: grid; gap: 12px; padding: 18px; border: 1px solid var(--line); border-radius: 14px; background: var(--canvas); }
.tenant-doc-block[hidden] { display: none; }
.tenant-doc-heading { display: grid; gap: 4px; }
.tenant-doc-heading .eyebrow { margin: 0; color: var(--lavender); font-family: "DM Mono", monospace; font-size: var(--fs-2xs); letter-spacing: .12em; text-transform: uppercase; }
.tenant-doc-heading h3 { margin: 0; color: var(--ink); font-size: var(--fs-base); letter-spacing: -.2px; }
.tenant-doc-heading p { margin: 0; color: var(--muted); font-size: var(--fs-sm); line-height: 1.55; }
.tenant-doc-dropzone { display: grid; min-height: 130px; place-items: center; align-content: center; gap: 6px; border: 1px dashed var(--faint); border-radius: 14px; padding: 18px; background: var(--surface); color: var(--muted); cursor: pointer; text-align: center; transition: border-color .18s, background .18s; }
.tenant-doc-dropzone:hover, .tenant-doc-block:focus-within .tenant-doc-dropzone { border-color: var(--lavender); background: var(--lavender-pale); }
.tenant-doc-dropzone strong { color: var(--ink); font-size: var(--fs-md); }
.tenant-doc-dropzone > span:last-child { max-width: 240px; font-size: var(--fs-xs); line-height: 1.6; }
.tenant-doc-dropzone-mark { display: grid; width: 34px; height: 34px; place-items: center; margin-bottom: 3px; border-radius: 11px; background: var(--surface); color: var(--lavender); font-size: 22px; font-weight: 400; box-shadow: var(--shadow); }
.tenant-doc-source-row { display: flex; flex-wrap: wrap; gap: 10px; }
.tenant-doc-source-button { flex: 1 1 140px; min-width: 0; padding: 10px 14px; font-size: var(--fs-md); }
.tenant-doc-input { position: absolute; width: 1px; height: 1px; overflow: hidden; opacity: 0; }
.tenant-doc-hint { margin: 0; color: var(--muted); font-size: var(--fs-sm); line-height: 1.6; }
.condo-photo-preview-grid, .condo-saved-photo-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(134px, 1fr)); gap: 10px; margin-top: 15px; }
.condo-photo-preview-grid figure, .condo-saved-photo { margin: 0; overflow: hidden; border: 1px solid var(--line); border-radius: 11px; background: var(--canvas); }
.condo-photo-preview-grid img, .condo-saved-photo img { display: block; width: 100%; aspect-ratio: 3 / 4; object-fit: cover; }
.condo-photo-preview-grid figcaption, .condo-saved-photo figcaption { display: flex; align-items: center; justify-content: space-between; gap: 6px; padding: 7px 8px; color: var(--muted); font-size: var(--fs-2xs); }
.condo-saved-photos { margin-top: 21px; border-top: 1px solid var(--line); padding-top: 17px; }
.condo-saved-photos-heading { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.condo-saved-photos-heading strong { font-size: var(--fs-md); }
.condo-form-details { padding: 26px; }
/* Single implicit column. `minmax(0, 1fr)` (not the default `auto`,
   whose min is min-content) so a wide, hard-to-shrink child — e.g. a
   multi-step wizard stepper — can never blow the column out past the
   viewport and force the whole form to scroll sideways. */
.form-stack { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-4); }
.field { display: grid; gap: 7px; align-content: start; }
/* Compact field: for 1-3 digit numeric inputs (bed/bath/balcony/size)
   that were rendering as full grid-cell-width boxes. */
.field-compact input, .field-compact select { max-width: 116px; }
@media (pointer: coarse) { .field-compact input, .field-compact select { max-width: 140px; } }
.field label { color: #37373e; font-size: var(--fs-sm); font-weight: 800; }
.field label span { margin-left: 3px; color: var(--faint); font-size: var(--fs-2xs); font-weight: 600; }
.field-label-row { display: flex; justify-content: space-between; }
.field-hint { margin: 0; color: var(--faint); font-size: var(--fs-xs); }
input, textarea, select { width: 100%; outline: none; border: 1px solid #dedee4; border-radius: 8px; background: #fff; color: var(--ink); font-size: var(--fs-md); transition: border-color .18s, box-shadow .18s; }
/* Fix (2026-09-04): was 41px against a 12.5px font (--fs-md) — ~14px
   of dead space above and below the text on every field in the app,
   the single biggest source of the "boxes have a lot of extra space"
   complaint. 36px keeps a full 44x44 tap target once the field's
   8px vertical margin/gap is counted, and matches the app's own
   --row-h (34px) density elsewhere. */
input, select { height: 36px; padding: 0 11px; }
textarea { min-height: 92px; resize: vertical; padding: 8px 11px; }
input::placeholder, textarea::placeholder { color: #b5b5bf; }
input:focus, textarea:focus, select:focus { border-color: #8d80eb; box-shadow: 0 0 0 3px rgba(118, 102, 237, .13); }
.input-prefix { position: relative; }
/* Direct child only. The ฿ marker is `<span><span class="baht">฿</span></span>`;
   a descendant selector positioned BOTH spans, so the glyph was double-offset
   (12px + 12px) and rendered on top of the first typed digit. */
.input-prefix > span { position: absolute; top: 50%; left: 12px; transform: translateY(-50%); color: var(--faint); font-size: var(--fs-md); pointer-events: none; }
.input-prefix > span .baht { position: static; margin: 0; font-weight: 400; font-size: inherit; color: inherit; }
.input-prefix input { padding-left: 25px; }
.input-suffix { position: relative; }
.input-suffix span { position: absolute; top: 50%; right: 12px; transform: translateY(-50%); color: var(--faint); font-size: var(--fs-md); }
.input-suffix input { padding-right: 28px; }
.password-toggle { position: absolute; top: 50%; right: 6px; display: grid; width: 30px; height: 30px; transform: translateY(-50%); place-items: center; border: 0; border-radius: 8px; background: transparent; color: var(--faint); cursor: pointer; transition: color .18s, background .18s; }
.password-toggle:hover { color: var(--lavender); background: var(--lavender-pale); }
.password-toggle:focus-visible { outline: 3px solid var(--lavender); outline-offset: 2px; }
.password-toggle .password-toggle-icon { display: grid; place-items: center; }
.password-forgot { font-size: var(--fs-xs); }
.change-password-dialog { width: min(420px, calc(100% - 32px)); border: 0; border-radius: 18px; padding: 0; background: var(--surface); color: var(--ink); box-shadow: var(--shadow); }
.change-password-dialog::backdrop { background: color-mix(in srgb, var(--ink) 62%, transparent); backdrop-filter: blur(2px); }
.change-password-dialog form { display: grid; gap: 16px; padding: 28px; }
.change-password-dialog-header { display: grid; gap: 6px; }
.change-password-dialog h2 { margin: 0; font-size: var(--fs-lg); letter-spacing: -.6px; }
.change-password-dialog p { margin: 0; color: var(--muted); font-size: var(--fs-md); line-height: 1.65; }
.change-password-dialog-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 9px; }
.change-password-dialog-actions .button { flex: 1 1 auto; min-height: 42px; padding-inline: 16px; }
.form-section { display: grid; gap: 13px; border-top: 1px solid #ececf0; padding-top: 20px; }
.form-section[hidden] { display: none; }
.field[hidden] { display: none; }
/* Fix (2026-09-04): .field-group had zero CSS -- it rendered as a
   bare browser-default <fieldset> (its own border and padding, an
   unstyled <legend>), nothing like the rest of the app's
   .form-section styling right next to it. Mirrors .form-section's
   look. (.field-row, the same-era companion fix, is gone -- both
   agent forms now use .grid-12 for that row as of 2026-09-05.) */
.field-group { display: grid; gap: 13px; min-width: 0; margin: 0; border: 0; padding: 0; border-top: 1px solid #ececf0; padding-top: 20px; }
.field-group legend { padding: 0; margin: 0 0 -3px; color: #8a80db; font-family: "DM Mono", monospace; font-size: var(--fs-xs); font-weight: 500; letter-spacing: .1em; text-transform: uppercase; }
/* :first-of-type, not :first-child: a hidden CSRF <input> is often
   the literal first child of the <form>, which made :first-child
   never match and left a stray top border on an otherwise-first
   section (found on the tenant quick-add form after it dropped its
   wizard wrapper). :first-of-type ignores the differently-tagged
   sibling and matches the first .form-section regardless. */
.form-section:first-of-type { border-top: 0; padding-top: 0; }
.form-section-label { margin: 0; color: #8a80db; font-family: "DM Mono", monospace; font-size: var(--fs-xs); letter-spacing: .08em; text-transform: uppercase; }
.form-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 13px; }
@media (min-width: 1200px) { .form-grid { grid-template-columns: repeat(4, 1fr); } }
.field-full { grid-column: 1 / -1; }
/* Fix (2026-09-04): .field-full spans the whole grid row so a
   textarea, an editable-dropdown, or a long select has room to
   breathe — but a plain single-line text input doesn't need to
   stretch that wide too. On a 4-column desktop row that's ~960px for
   "123 Ocean Drive", almost all of it dead space. Cap just the
   <input> itself; textareas, selects, and the editable-dropdown
   control are untouched and still fill the row. Mobile is already
   narrower than the cap, so this is a no-op there. */
.field-full input:not([type="checkbox"]):not([type="radio"]) { max-width: 520px; }
/* Fix (2026-09-04): a run of short single-line values (Address +
   Address line 2, Company name + phone, Full name + Email, ...) used
   to each get their own full-width .field-full row -- capping input
   width (above) fixed the individual boxes but left the reclaimed
   space empty rather than used, when two such fields are logically
   one group and belong on the same row. .field-pair puts two .field
   children side by side on an even 1fr/1fr split (e.g. name + email,
   comparable lengths); add field-pair-2-1 when the first field
   genuinely needs more room than the second (e.g. Address vs.
   Address line 2, Company name vs. phone). Wrap with .field-full so
   the pair still spans the outer form-grid row. */
.field-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 13px; }
.field-pair.field-pair-2-1 { grid-template-columns: 2fr 1fr; }
/* Bug found live at 375px: .field-pair-2-1's two-class selector
   (0,2,0) outranks a plain .field-pair override (0,1,0) regardless
   of the media query, so the mobile rule was silently losing the
   cascade and Address/Address line 2 stayed side by side, overflowing
   the viewport. Matching the .field-pair-2-1 selector here restores
   equal specificity so source order (this comes later) decides it. */
@media (max-width: 720px) {
  .field-pair, .field-pair.field-pair-2-1 { grid-template-columns: 1fr; }
}

/* ================================================================
   Content-aware 12-column field grid (2026-09-05).
   Piloted offline, approved, landing here first on the property
   wizard's Step 1 (condos/form.ejs) only -- .form-grid/.field-full/
   .field-pair above are UNCHANGED and still power every other form
   in the app (owner/tenant quick-add, admin forms, leases, tasks,
   feedback, plan/agent forms). This isn't a replacement for those
   yet; it's the first real page migrated onto the new system,
   with the rest to follow page by page.

   .form-grid gives every field the same width regardless of what it
   holds -- a bedroom count and a street address end up the same
   size. .grid-12 instead sizes each field to a span (c1..c12) picked
   for what that specific kind of value needs: c1 for a 1-2 digit
   counter, c2 for a short numeric value (postal code, size in sqm,
   unit number), c3-c4 for a dropdown or short line of text, c5 for a
   name, c8 for a street address, c12 for full width. A row doesn't
   have to sum to 12 -- Pet policy below stops at c4 instead of being
   stretched just to use the last few columns.

   Breakpoints mirror .form-grid's own convention (>=1200px "desktop"
   step-up, <=720px "mobile" collapse) rather than inventing new
   thresholds:
     - default (721-1199px, "tablet"): 6 base columns, each c-span
       roughly halved -- a 1-2-digit counter still only takes a
       fraction of the row, it just shares with fewer neighbors.
     - >=1200px ("desktop"): the true 12-column spans below.
     - <=720px ("mobile"): 2 base columns. Small counters (Bed/Bath,
       Balconies/Size) pair up two-to-a-row instead of each claiming
       a full-width row with a narrow, mostly-empty input -- a real
       density improvement over how .form-grid handles the same
       fields on a phone today. */
.grid-12 { display: grid; grid-template-columns: repeat(6, 1fr); gap: 13px; }
/* default / "tablet" tier (721-1199px): 6 base columns, small spans
   roughly halved, anything c5+ takes a full row. */
.grid-12 .c1 { grid-column: span 2; }
.grid-12 .c2 { grid-column: span 2; }
.grid-12 .c3 { grid-column: span 3; }
.grid-12 .c4 { grid-column: span 3; }
.grid-12 .c5, .grid-12 .c6, .grid-12 .c7, .grid-12 .c8,
.grid-12 .c9, .grid-12 .c10, .grid-12 .c11, .grid-12 .c12 { grid-column: span 6; }
@media (min-width: 1200px) {
  .grid-12 { grid-template-columns: repeat(12, 1fr); }
  .grid-12 .c1 { grid-column: span 1; }
  .grid-12 .c2 { grid-column: span 2; }
  .grid-12 .c3 { grid-column: span 3; }
  .grid-12 .c4 { grid-column: span 4; }
  .grid-12 .c5 { grid-column: span 5; }
  .grid-12 .c6 { grid-column: span 6; }
  .grid-12 .c7 { grid-column: span 7; }
  .grid-12 .c8 { grid-column: span 8; }
  .grid-12 .c9 { grid-column: span 9; }
  .grid-12 .c10 { grid-column: span 10; }
  .grid-12 .c11 { grid-column: span 11; }
  .grid-12 .c12 { grid-column: span 12; }
}
@media (max-width: 720px) {
  .grid-12 { grid-template-columns: repeat(2, 1fr); }
  .grid-12 .c1, .grid-12 .c2 { grid-column: span 1; }
  .grid-12 .c3, .grid-12 .c4, .grid-12 .c5, .grid-12 .c6, .grid-12 .c7,
  .grid-12 .c8, .grid-12 .c9, .grid-12 .c10, .grid-12 .c11, .grid-12 .c12 { grid-column: span 2; }
  /* The global `@media (max-width:720px) .field-full { grid-column: auto }`
     rule (further down, added for .form-grid which collapses to ONE column
     at mobile) is wrong for .grid-12, which stays 2-column at mobile -- a
     .field-full there would shrink to one of the two tracks. Re-assert the
     full-row span with a .grid-12-scoped selector that outweighs it. */
  .grid-12 .field-full { grid-column: 1 / -1; }
}
/* A field inside .grid-12 sizes its own label to what the label
   text needs (see .field's own min-width: 0 above, which is the
   right default for a table cell showing user data but not for a
   short label string this codebase writes itself) so e.g.
   "Balconies" gets a full line instead of wrapping inside a track
   sized only for its 1-2 digit value. */
.grid-12 .field { min-width: auto; }
.grid-12 .field label { white-space: nowrap; }
.listing-type-field, .amenity-fieldset { min-width: 0; margin: 0; border: 0; padding: 0; }
.listing-type-field { grid-column: 1 / -1; }
.listing-type-field .choice-group { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.listing-type-field legend, .amenity-fieldset legend { margin-bottom: 7px; padding: 0; color: #37373e; font-size: var(--fs-sm); font-weight: 800; }
.amenity-fieldset legend span { margin-left: 3px; color: var(--faint); font-size: var(--fs-2xs); font-weight: 600; }
.choice-group { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
.choice-option, .amenity-option { display: flex; align-items: center; gap: 8px; border: 1px solid var(--line); border-radius: 8px; padding: 10px; background: var(--surface); color: var(--muted); font-size: var(--fs-sm); font-weight: 700; cursor: pointer; transition: border-color .18s, background .18s, color .18s; }
.choice-option:hover, .amenity-option:hover { border-color: var(--lavender); background: var(--lavender-pale); color: var(--ink); }
.choice-option:has(input:checked), .amenity-option:has(input:checked) { border-color: var(--lavender); background: var(--lavender-pale); color: var(--ink); }
.choice-option:has(input:focus-visible), .amenity-option:has(input:focus-visible) { outline: 3px solid var(--lavender); outline-offset: 2px; }
.choice-option input, .amenity-option input { width: 16px; height: 16px; margin: 0; padding: 0; accent-color: var(--lavender); }
/* Fix (2026-09-05): this was a grid of equal-width columns (4 across
   on wide forms), so "Gym" got exactly as wide a box as "Basketball
   court" -- the short labels sat in a pill mostly empty on one side,
   and the ragged label lengths made the row look unaligned even
   though every box measured the same. Flex-wrap instead: each pill
   sizes to its own checkbox + label text (no flex-grow), so short
   options pack tighter and more of them fit per row, and consistent
   left edges + an even gap is what actually reads as "aligned" for a
   set of differently-sized chips -- not equal box widths. This also
   means .amenity-grid-4's old per-breakpoint column counts are gone:
   flex-wrap reflows to whatever fits at any width without them. */
.amenity-grid { display: flex; flex-wrap: wrap; gap: 8px; }
.current-blue-book { margin-top: 2px; }
.form-actions { display: flex; justify-content: flex-end; gap: 9px; border-top: 1px solid #ececf0; padding-top: 21px; }
.form-errors { margin-bottom: 20px; border: 1px solid #f0c9d0; border-radius: 10px; padding: 12px 14px; background: #fff6f7; color: #9f3044; font-size: var(--fs-sm); }
.form-errors ul { margin: 6px 0 0; padding-left: 18px; }
.form-errors > ul { padding-left: 0; list-style: none; }
.form-errors .wizard-error-step { margin-top: 8px; }
.form-errors .wizard-error-step > strong { display: block; color: #6e1f30; font-size: var(--fs-xs); letter-spacing: 0.04em; text-transform: uppercase; }
.form-errors .wizard-error-step > ul { margin: 4px 0 0; padding-left: 18px; list-style: disc; }
.form-errors a { color: #9f3044; text-decoration: underline; }
.form-errors a:hover { color: #6e1f30; }

/* Invitation credentials page: the temporary password is shown in a
   monospace code block with a Copy button next to it. The pill is the
   small role chip used on the Company Admins table (Company Admin /
   Agent). */
.invite-credentials-code {
  display: inline-block;
  padding: 4px 8px;
  margin-right: 8px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--canvas);
  color: var(--ink);
  font-family: "DM Mono", monospace;
  font-size: var(--fs-md);
  letter-spacing: 0.04em;
  user-select: all;
}
.pill {
  display: inline-block;
  padding: 2px 9px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--canvas);
  color: var(--muted);
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.pill-active { color: #2c6e3f; border-color: #b9dec4; background: #eef9f1; }
.pill-disabled { color: #9f3044; border-color: #e9c0c8; background: #fff6f7; }

/* Company code chip. The same code (C-A0001, C-A0002, ...) appears on
   the admin Agents table and on the company admin's "My team" page so
   every reader can see at a glance which company a row belongs to. */
.company-code-chip {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 6px;
  font-family: "DM Mono", monospace;
  font-size: var(--fs-xs);
  letter-spacing: 0.04em;
  color: #3a3f55;
  background: #eef0f7;
  border: 1px solid #d3d7e6;
  border-radius: 4px;
  vertical-align: middle;
}
.company-cell {
  display: flex;
  flex-direction: column;
  gap: 2px;
  align-items: flex-start;
}

/* Per-property "Edit history" panel on the detail page. The action
   string (e.g. "condo_updated") is shown in a small monospace chip so
   the reader can tell at a glance which event was a content edit vs
   a photo upload or file replace. */
/* Edit history now opens in a dialog from the "History" button in the
   page header, instead of sitting open at the foot of every property. */
.condo-history-dialog { width: min(720px, calc(100% - 32px)); max-width: none; max-height: min(80vh, 720px); border: 1px solid var(--line); border-radius: var(--radius); padding: 0; background: var(--surface); color: var(--ink); box-shadow: var(--shadow); overflow: hidden; }
.condo-history-dialog::backdrop { background: color-mix(in srgb, var(--ink) 45%, transparent); }
.condo-history-dialog-head { position: sticky; top: 0; z-index: 1; display: flex; align-items: flex-start; justify-content: space-between; gap: 14px; padding: 16px 18px 12px; background: var(--surface); border-bottom: 1px solid var(--line); }
.condo-history-dialog-head h2 { margin: 2px 0 0; font-size: var(--fs-lg); }
.condo-history-dialog-close { flex: 0 0 auto; width: 30px; height: 30px; display: grid; place-items: center; border: 1px solid var(--line); border-radius: 8px; background: var(--surface); color: var(--muted); font-size: 20px; line-height: 1; cursor: pointer; }
.condo-history-dialog-close:hover { color: var(--lavender); border-color: var(--lavender); background: var(--lavender-pale); }
.condo-history-dialog .notes-copy { margin: 0; padding: 12px 18px 4px; font-size: var(--fs-sm); }
.condo-history-dialog .table-wrap { max-height: calc(80vh - 150px); overflow-y: auto; padding: 4px 8px 14px; }
.edit-history-table { font-size: var(--fs-md); width: 100%; }
.edit-history-table th { color: var(--muted); font-weight: 700; text-transform: uppercase; font-size: var(--fs-2xs); letter-spacing: 0.08em; padding: 8px 10px; }
.edit-history-table td { padding: 9px 10px; vertical-align: top; }
.edit-history-table tbody tr + tr td { border-top: 1px solid var(--line); }
.audit-action-code {
  display: inline-block;
  padding: 2px 6px;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: var(--canvas);
  color: var(--ink);
  font-family: "DM Mono", monospace;
  font-size: var(--fs-xs);
  letter-spacing: 0.02em;
  white-space: nowrap;
}
input[type="file"] { height: auto; padding: 9px 11px; }
.photo-manager { width: min(820px, 100%); margin-top: 17px; }
.photo-manager-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(145px, 1fr)); gap: 12px; }
.photo-manager-item { margin: 0; overflow: hidden; border: 1px solid var(--line); border-radius: 11px; background: var(--canvas); }
.photo-manager-item img { display: block; width: 100%; aspect-ratio: 3 / 4; object-fit: cover; }
.photo-manager-item figcaption { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 8px 9px; color: var(--muted); font-size: var(--fs-xs); }
.photo-manager-item form { margin: 0; }
.photo-delete { border: 0; padding: 0; background: transparent; }
/* Photo cards (both the saved-photo grid on the edit form and the staged
   preview grid) are narrow, so the caption stacks: label on top, then a
   reorder/remove row. A single flex row of two arrows + "Remove" clips on
   a ~110px card. */
.condo-saved-photo figcaption, .condo-photo-preview-grid figcaption {
  flex-direction: column;
  align-items: stretch;
  gap: 5px;
}
.condo-saved-photo-actions {
  display: flex;
  align-items: center;
  gap: 4px;
  flex-wrap: wrap;
}
.condo-saved-photo-actions .icon-button-reorder { border: 1px solid var(--line); }
/* Position row: a "Pos [n]" box + the earlier/later arrows on one line;
   Remove sits on its own line below. Type a number to jump a photo to
   that slot. */
.condo-saved-photo-pos { display: flex; align-items: center; justify-content: space-between; gap: 6px; }
.condo-saved-photo-pos label { display: inline-flex; align-items: center; gap: 4px; font-family: "DM Mono", monospace; font-size: var(--fs-2xs); letter-spacing: .04em; text-transform: uppercase; color: var(--faint); }
.condo-saved-photo-pos input[type="number"] { width: 38px; padding: 3px 2px; border: 1px solid var(--line); border-radius: 6px; background: var(--surface); color: var(--ink); font-family: 'Manrope', system-ui, sans-serif; font-size: var(--fs-md); font-weight: 700; text-align: center; -moz-appearance: textfield; }
.condo-saved-photo-pos input[type="number"]::-webkit-outer-spin-button,
.condo-saved-photo-pos input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.condo-saved-photo-pos input[type="number"]:focus-visible { outline: 2px solid var(--lavender); outline-offset: 1px; border-color: var(--lavender); }
.condo-saved-photo .photo-delete,
.condo-photo-preview-grid .photo-delete { align-self: flex-start; }
/* Drag-to-reorder: every card in both grids is draggable (see
   setupPhotoReorder / the staged-photo render() in app.js). The image
   shows a grab cursor as the affordance; the dragged card dims and the
   card under the pointer gets a lavender outline as the drop target.
   HTML5 drag/drop is mouse-only — the Pos box and arrows above remain
   the way to reorder on a touch device. */
.condo-saved-photo img, .condo-photo-preview-grid img { cursor: grab; }
.condo-saved-photo.is-dragging, .condo-photo-preview-grid figure.is-dragging { opacity: .4; }
.condo-saved-photo.is-drop-target, .condo-photo-preview-grid figure.is-drop-target { outline: 2px solid var(--lavender); outline-offset: -2px; }
/* Reorder / remove status line under the saved-photo grid. */
.condo-saved-photos-status { margin: 12px 0 0; }
.condo-saved-photos-status.is-error { color: var(--danger, #b3261e); }
/* Photo position badge: a small numbered circle in the top-left
   corner of every saved photo and every preview figure, so the
   order is visible at a glance. The number is the position
   (1-indexed). Both the form grid and the preview grid are
   position:relative, so absolute positioning anchors the badge
   to each figure consistently. */
.condo-saved-photo, .condo-photo-preview-grid figure { position: relative; }
.photo-position-badge {
  position: absolute;
  top: 6px;
  left: 6px;
  min-width: 24px;
  height: 24px;
  padding: 0 6px;
  border-radius: 12px;
  background: var(--lavender);
  color: #fff;
  display: grid;
  place-items: center;
  font-weight: 700;
  font-size: var(--fs-md);
  line-height: 1;
  box-shadow: 0 1px 2px rgba(0,0,0,.35);
  pointer-events: none;
  z-index: 1;
}
/* Detail-page carousel thumbnail: smaller badge, top-left, same
   style. Sits inside the <button> so it gets pointer-events: none
   to avoid swallowing the click. */
.photo-carousel-thumb { position: relative; }
.photo-carousel-thumb .photo-position-badge {
  top: 4px;
  left: 4px;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  font-size: var(--fs-xs);
  border-radius: 9px;
}
.icon-button-reorder { width: 22px; height: 22px; font-size: var(--fs-xs); color: var(--muted); }
.icon-button-reorder:hover:not(:disabled) { color: var(--accent, #2c2c31); background: rgba(255, 255, 255, 0.08); }
.icon-button-reorder:disabled { opacity: 0.32; cursor: default; }
.icon-button-disabled { opacity: 0.3; pointer-events: none; }

.tenant-id-button { width: 100%; justify-content: flex-start; flex-direction: column; align-items: flex-start; gap: 4px; padding: 10px 14px; text-align: left; }
.tenant-id-button-label { font-weight: 600; }
.tenant-id-button-hint { color: var(--muted); font-size: var(--fs-xs); }

.owner-cell, .tenant-cell { align-items: center; }
.owner-cell-meta, .tenant-cell-meta { display: block; margin-top: 1px; color: var(--muted); font-size: var(--fs-xs); }
.property-stack { display: grid; gap: 4px; margin: 0; padding: 0; list-style: none; }
.property-stack li { font-size: var(--fs-sm); color: var(--muted); }
.property-stack-more { color: var(--faint); font-size: var(--fs-xs); }
.contact-channel { display: inline-block; min-width: 56px; margin-right: 6px; color: var(--faint); font-family: "DM Mono", monospace; font-size: var(--fs-2xs); letter-spacing: .06em; text-transform: uppercase; }
.contact-link { color: var(--ink); font-size: var(--fs-sm); font-weight: 600; }
.contact-link:hover { color: var(--lavender); }
.contact-text { color: var(--ink); font-size: var(--fs-sm); }
.contact-empty { color: var(--faint); font-size: var(--fs-xs); font-style: italic; }
.portfolio-mix { display: inline-flex; flex-wrap: wrap; gap: 5px; }
.mix-pill { display: inline-block; border-radius: 999px; padding: 2px 8px; font-size: var(--fs-2xs); font-weight: 800; letter-spacing: .04em; }
.mix-rented { background: var(--lavender-pale); color: var(--lavender-dark); }
.mix-available { background: var(--mint); color: var(--mint-ink); }
.mix-maintenance { background: var(--amber); color: var(--amber-ink); }
.cell-strong { display: block; color: var(--ink); font-size: var(--fs-md); font-weight: 800; letter-spacing: -.2px; }
.cell-sub { display: block; color: var(--faint); font-size: var(--fs-xs); line-height: 1.4; }
.lease-window { display: grid; gap: 3px; }
.lease-window > div { display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap; }
.lease-window > div .contact-channel { flex: 0 0 auto; }
.lease-window > div .contact-text { flex: 1 1 auto; }
.lease-due { color: var(--faint); font-size: var(--fs-xs); }
.lease-badge { display: inline-block; margin-top: 6px; border-radius: 999px; padding: 2px 8px; font-size: var(--fs-2xs); font-weight: 800; letter-spacing: .04em; }
.lease-badge-active { background: var(--mint); color: var(--mint-ink); }
.lease-badge-upcoming { background: var(--lavender-pale); color: var(--lavender-dark); }
.lease-badge-ended { background: #f0e1e5; color: var(--danger); }

/* R1b: stacked-card fallback for the wide people tables (owners, tenants).
   On a phone a 5-6 column table forces a horizontal scroll that hides the
   rent and contact columns off-screen. Below 720px each row re-flows into a
   self-contained card: the header row is removed and every cell gets its
   column name back via `data-label`. Desktop is untouched — these rules are
   entirely inside the media query. The primary cell (person + avatar) spans
   the full card width with no label. */
@media (max-width: 720px) {
  .table-wrap:has(.stacked-card-table) {
    overflow-x: visible;
    background: none;
  }
  .stacked-card-table,
  .stacked-card-table tbody,
  .stacked-card-table tr,
  .stacked-card-table td { display: block; width: 100%; }
  .stacked-card-table thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }
  .stacked-card-table tr {
    margin: 0 18px 12px;
    padding: 10px 12px;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--surface);
  }
  .stacked-card-table td {
    max-width: none;
    padding: 4px 0;
    border: 0;
    white-space: normal;
    text-overflow: clip;
    overflow: visible;
    display: grid;
    grid-template-columns: 84px 1fr;
    gap: 10px;
    align-items: start;
  }
  .stacked-card-table td + td { border-top: 1px solid var(--hairline); padding-top: 6px; margin-top: 2px; }
  .stacked-card-table td::before {
    content: attr(data-label);
    color: var(--faint);
    font-family: "DM Mono", monospace;
    font-size: var(--fs-2xs);
    letter-spacing: .06em;
    text-transform: uppercase;
    padding-top: 2px;
  }
  .stacked-card-table td.stacked-card-primary { grid-template-columns: 1fr; gap: 0; padding-top: 0; }
  .stacked-card-table td.stacked-card-primary::before { content: none; }
  .stacked-card-table td.cell-num { text-align: left; }
  /* "MONTHLY RENT" label already says it — drop the "per month" sub-line
     that would otherwise dangle under the figure in the card view. */
  .stacked-card-table td.cell-num .cell-sub { display: none; }
  .stacked-card-table .contact-channel { min-width: 52px; }
  /* Cells that carry their own row of sub-labels (Contact channels, the
     lease window, the property list) read better as a full-width block
     with the column name on its own line rather than squeezed into the
     84px label gutter. */
  .stacked-card-table td:has(.contact-channel),
  .stacked-card-table td:has(.lease-window),
  .stacked-card-table td:has(.property-stack) { display: block; }
  .stacked-card-table td:has(.contact-channel)::before,
  .stacked-card-table td:has(.lease-window)::before,
  .stacked-card-table td:has(.property-stack)::before { display: block; margin-bottom: 5px; }
  .stacked-card-table .property-stack { padding-left: 2px; }
}

.detail-heading-actions { display: inline-flex; flex-wrap: wrap; gap: 9px; align-items: center; }
.danger-eyebrow { color: var(--danger); }
.delete-confirm-layout { display: grid; gap: 18px; max-width: 720px; }
.delete-summary-panel h2 { margin-bottom: 18px; }
.delete-impact-panel { border-color: #f0c9d0; background: #fff7f8; }
.delete-impact-list { margin: 0; padding: 0; list-style: none; display: grid; gap: 10px; }
.delete-impact-list li { display: flex; align-items: center; gap: 12px; color: var(--ink); font-size: var(--fs-md); }
.delete-impact-list li code { font-size: var(--fs-sm); }
.delete-impact-count { display: inline-grid; min-width: 30px; height: 24px; place-items: center; padding: 0 8px; border-radius: 999px; background: #fff; color: var(--danger); font-size: var(--fs-sm); font-weight: 800; }
.delete-impact-empty { color: var(--muted); }
.delete-impact-list li.delete-impact-empty .delete-impact-count { display: none; }
.delete-impact-warning { margin: 16px 0 0; padding: 10px 12px; border: 1px solid #f0c9d0; border-radius: 10px; background: #fff; color: var(--danger); font-size: var(--fs-sm); font-weight: 700; }
.delete-confirm-form { display: grid; gap: 8px; padding: 20px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface); box-shadow: var(--shadow); }
.delete-confirm-field { gap: 6px; }
.delete-confirm-field input { width: 100%; }
.delete-confirm-actions { padding-top: 0; border-top: 0; }
@media (max-width: 720px) {
  .detail-heading-actions { width: 100%; }
  .detail-heading-actions .button { flex: 1 1 0; min-width: 0; }
  .row-actions { justify-content: flex-start; }
}

.empty-state { display: grid; min-height: 260px; place-items: center; align-content: center; padding: 30px; text-align: center; }
.empty-state-actions { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; margin-top: 14px; }
.empty-icon { display: grid; width: 48px; height: 48px; place-items: center; margin-bottom: 12px; border-radius: 14px; background: #efeeff; color: #7568dd; font-family: Georgia, serif; font-size: 29px; }
.empty-state h2 { margin: 0; }
.empty-state p { max-width: 300px; margin: 7px 0 18px; color: var(--muted); font-size: var(--fs-md); }
.error-state { min-height: 60vh; }
.error-ref { margin: 4px 0 14px; color: var(--faint); font-size: var(--fs-md); }
.error-ref code { font-family: "DM Mono", monospace; font-size: var(--fs-sm); background: var(--canvas); padding: 1px 6px; border-radius: 4px; }

.flash { display: flex; align-items: center; gap: 8px; margin-bottom: 18px; border: 1px solid; border-radius: 10px; padding: 10px 12px; font-size: var(--fs-sm); animation: arrive .25s ease-out; }
.flash > span { display: grid; width: 17px; height: 17px; flex: 0 0 17px; place-items: center; border-radius: 50%; font-size: var(--fs-xs); font-weight: 800; }
.flash p { margin: 0; flex: 1; }
.flash-success { border-color: #bcead9; background: #f0fff9; color: #177658; }.flash-success > span { background: #bcead9; color: #106649; }
.flash-error { border-color: #f0c9d0; background: #fff7f8; color: #ad3450; }.flash-error > span { background: #f6d7dd; color: #a82f49; }
.flash-dismiss { border: 0; background: none; color: currentColor; font-size: 18px; line-height: 14px; opacity: .75; }

/* Native <dialog> for the system-log export. The browser supplies
   the modal backdrop and focus trap; we just size + center the panel
   and add a touch of padding. The form inside reuses the existing
   .form-stack and .form-actions styles. */
.export-dialog { width: min(440px, calc(100% - 32px)); border: 1px solid var(--border, #ececf0); border-radius: 14px; padding: 0; background: var(--card, #fff); color: inherit; }
.export-dialog::backdrop { background: rgba(20, 18, 38, 0.45); }
.export-dialog .form-stack { padding: 22px 24px; }
.export-dialog-title { margin: 0 0 4px; font-size: var(--fs-lg); }
.export-dialog-intro { margin: 0 0 12px; color: var(--muted); font-size: var(--fs-md); }
.required-marker { color: #ad3450; margin-left: 2px; }

.auth-shell { min-height: 100vh; background: var(--canvas); }
/* The form is the job; the aside is decoration. It used to be a hard
   50/50 split — half the viewport was orbit circles and three lines of
   copy while the form sat cramped in a 400px card with an 84px gap
   under the logo. Give the form the larger share and calm the aside. */
.auth-main { display: grid; min-height: 100vh; grid-template-columns: minmax(440px, 1.15fr) minmax(340px, 0.85fr); }
.auth-frame { display: flex; width: min(430px, calc(100% - 64px)); flex-direction: column; justify-content: center; margin: 0 auto; padding: 38px 0; }
.auth-frame > .brand { margin-bottom: 44px; }
.login-card { max-width: 400px; }
.login-card h1 { margin-top: 7px; font-size: var(--fs-3xl); }
.intro { margin: 0 0 26px; color: var(--muted); font-size: var(--fs-md); }
.login-note { margin: 19px 0 0; color: var(--faint); font-size: var(--fs-xs); text-align: center; }
.auth-aside { position: relative; overflow: hidden; background: #201c3b; color: #fff; }
.auth-aside::before { position: absolute; inset: 0; background: radial-gradient(circle at 81% 12%, rgba(159, 148, 255, .35), transparent 29%), radial-gradient(circle at 34% 75%, rgba(99, 80, 213, .38), transparent 45%); content: ""; }
.orbit { position: absolute; border: 1px solid rgba(255,255,255,.15); border-radius: 50%; }.orbit-one { width: 700px; height: 700px; top: -270px; right: -250px; }.orbit-two { width: 500px; height: 500px; bottom: -270px; left: -170px; }
.aside-copy { position: absolute; right: 12%; bottom: 11%; left: 15%; max-width: 390px; }.aside-copy span { color: #b8aeff; font-family: "DM Mono", monospace; font-size: var(--fs-xs); letter-spacing: .1em; }.aside-copy h2 { margin: 15px 0 13px; font-size: clamp(28px, 3.2vw, 46px); letter-spacing: -2.2px; line-height: 1.15; }.aside-copy p { margin: 0; color: #bbb6da; font-size: var(--fs-md); }

@keyframes landing-drift { from { transform: translate3d(-12px, 9px, 0) scale(.94); } to { transform: translate3d(14px, -12px, 0) scale(1.08); } }
@keyframes landing-pulse { 0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--mint) 28%, transparent); } 55% { box-shadow: 0 0 0 7px color-mix(in srgb, var(--mint) 0%, transparent); } }
@keyframes landing-float-one { 0%, 100% { translate: 0 0; } 50% { translate: 0 -10px; } }
@keyframes landing-float-two { 0%, 100% { translate: 0 0; } 50% { translate: -7px 11px; } }
@keyframes landing-float-three { 0%, 100% { translate: 0 0; } 50% { translate: 9px -8px; } }
@keyframes landing-photo-zoom { from { scale: 1; } to { scale: 1.09; } }
@keyframes arrive { from { opacity: 0; transform: translateY(-5px); } to { opacity: 1; transform: translateY(0); } }
@media (prefers-reduced-motion: reduce) { .landing-orb, .landing-pulse, .landing-photo, .landing-photo img { animation: none; } }
@media (max-width: 980px) { .sidebar-pinned .sidebar { width: 216px; }.sidebar-pinned .main-content { width: calc(100% - 216px); margin-left: 216px; }.page-container, .public-main { padding: 42px 32px 54px; }.landing-hero { grid-template-columns: 1fr; }.landing-copy { min-height: auto; padding-inline: 4%; }.landing-showcase { min-height: 430px; }.landing-value { grid-template-columns: 1fr; gap: 25px; }.landing-value h2 { max-width: none; }.public-brick-gallery { grid-auto-rows: 82px; grid-template-columns: repeat(3, minmax(0, 1fr)); }.condo-form-layout { grid-template-columns: 1fr; }.condo-form-media { position: static; }.stats-grid { grid-template-columns: repeat(2, 1fr); }.condo-grid { grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); }.auth-main { grid-template-columns: minmax(390px, 1fr) minmax(330px, .8fr); } }
/* Tablet (721-980px): a slightly narrower expanded width and a bigger
   toggle target for touch. Collapse/expand is the toggle button only. */
@media (min-width: 721px) and (max-width: 980px) {
  .sidebar-pinned .sidebar { width: 216px; padding: 16px 12px 12px; }
  .sidebar-pinned .main-content { width: calc(100% - 216px); margin-left: 216px; }
  .sidebar-pin-toggle { width: 30px; height: 30px; }
}

@media (max-width: 720px) { .sidebar { display: none; }.main-content { width: 100%; margin-left: 0; }.mobile-header { position: relative; z-index: 20; display: grid; grid-template-columns: minmax(0, 1fr) auto auto; align-items: center; gap: 10px; padding: 18px 20px 0; }.mobile-header .brand { min-width: 0; }.mobile-header .icon-button, .mobile-menu-trigger { display: grid; width: 38px; height: 38px; place-items: center; border: 1px solid var(--line); border-radius: 10px; background: var(--surface); color: var(--ink); box-shadow: var(--shadow); }.mobile-header .icon-button:hover, .mobile-menu-trigger:hover { background: var(--lavender-pale); color: var(--lavender); }.mobile-menu { position: relative; }.mobile-menu-trigger { list-style: none; cursor: pointer; font-size: var(--fs-lg); }.mobile-menu-trigger::-webkit-details-marker { display: none; }.mobile-menu[open] .mobile-menu-trigger { border-color: var(--lavender); background: var(--lavender-pale); color: var(--lavender); }.mobile-nav { position: absolute; top: calc(100% + 10px); right: 0; z-index: 1; display: grid; width: min(250px, calc(100vw - 40px)); gap: 4px; border: 1px solid var(--line); border-radius: var(--radius-sm); padding: 8px; background: var(--surface); box-shadow: var(--shadow); }.mobile-nav .nav-link { color: var(--muted); }.mobile-nav .nav-link:hover, .mobile-nav .nav-link.is-active { background: var(--lavender-pale); color: var(--ink); }.page-container, .public-main { padding: 31px 20px 44px; }.public-nav { grid-template-columns: minmax(0, 1fr) auto; }.public-nav-links { grid-column: 1 / -1; grid-row: 2; flex-wrap: wrap; justify-content: flex-start; gap: 8px 16px; }.landing-page { margin-top: 28px; }.landing-hero { gap: 27px; padding: 24px; }.landing-copy h1 { font-size: clamp(38px, 11vw, 56px); letter-spacing: -2.7px; }.landing-copy > p:last-of-type { font-size: var(--fs-md); }.landing-showcase { min-height: 365px; }.landing-value { gap: 24px; padding: 22px; }.landing-value-grid { grid-template-columns: 1fr; }.landing-value-grid article { padding: 15px; }.public-info-page { margin-top: 30px; }.public-info-grid { grid-template-columns: 1fr; }.public-info-card { min-height: 0; padding: 24px; }.public-brick-gallery { grid-auto-rows: 94px; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }.public-condo-dialog { width: min(calc(100% - 20px), 620px); max-height: calc(100vh - 20px); }.public-dialog-layout { grid-template-columns: 1fr; }.public-dialog-gallery { grid-auto-rows: 145px; }.public-dialog-details { padding: 31px 25px; }.condo-detail-layout, .condo-form-layout { grid-template-columns: 1fr; }.condo-detail-media, .condo-form-media { position: static; }.condo-detail-main-photo, .condo-detail-no-photo { height: min(52vh, 410px); }.condo-form-media, .condo-form-details { padding: 19px; }.condo-photo-dropzone { min-height: 165px; }.condo-image-lightbox { width: min(calc(100% - 20px), 720px); max-height: calc(100vh - 20px); }.condo-image-lightbox-frame, .condo-image-lightbox img { max-height: calc(100vh - 20px); }.public-section-heading { align-items: flex-start; flex-direction: column; gap: 7px; }.page-heading { align-items: flex-start; flex-direction: column; gap: 19px; margin-bottom: 26px; }.page-heading .button { width: 100%; }.detail-heading .button { width: auto; }.stats-grid, .stats-grid-three, .content-grid, .detail-grid { grid-template-columns: 1fr; }.stat-card { min-height: 116px; }.stat-card strong { margin: 8px 0; }.condo-grid, .condo-grid-full { grid-template-columns: 1fr; }.condo-card { min-height: 180px; }.filter-bar { grid-template-columns: 1fr; }.filter-actions { min-height: 0; }.form-panel { padding: 19px; }.form-grid { grid-template-columns: 1fr; }.field-full { grid-column: auto; }.detail-list { grid-template-columns: 1fr; }.detail-list > div:nth-child(odd), .detail-list > div:nth-child(even) { padding: 13px 0; border-left: 0; }.title-with-status { align-items: flex-start; flex-direction: column; gap: 9px; }.auth-main { display: block; }.auth-frame { min-height: 100vh; width: min(400px, calc(100% - 40px)); justify-content: flex-start; padding-top: 29px; }.auth-frame > .brand { margin-bottom: min(9vh, 52px); }.auth-aside { display: none; } }
@media (max-width: 430px) { .page-container, .public-main { padding-right: 15px; padding-left: 15px; }.mobile-header { padding-right: 15px; padding-left: 15px; }.landing-hero { padding: 18px; }.landing-copy h1 { font-size: 39px; }.landing-actions { width: 100%; flex-direction: column; }.landing-actions .button { width: 100%; }.landing-showcase { min-height: 310px; }.landing-photo-1 { width: 60%; height: 61%; }.landing-photo-2 { width: 42%; height: 40%; }.landing-photo-3 { width: 44%; height: 38%; }.landing-photo-4 { display: none; }.public-brick-gallery { grid-auto-rows: 88px; grid-template-columns: 1fr; }.public-gallery-card.is-featured, .public-gallery-card.is-wide { grid-column: span 1; }.stats-grid { grid-template-columns: 1fr; }.form-actions { justify-content: stretch; flex-direction: column-reverse; }.form-actions .button { width: 100%; }.detail-heading .button { width: 100%; }.table-panel .panel-heading { padding: 0 16px; } th, td { padding-right: 16px; padding-left: 16px; } }

/* Rent collection overview */
.rent-overview-panel { display: flex; flex-direction: column; gap: 0; margin-bottom: var(--space-3); padding: var(--space-5); }
.rent-overview-panel > .panel-heading { align-items: center; margin-bottom: 26px; border-bottom: 1px solid var(--line); padding-bottom: 21px; }
.summary-tables { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 14px; }
.summary-table-card { display: flex; min-width: 0; flex-direction: column; }
.summary-table-card .panel-heading { align-items: center; margin-bottom: 0; border-bottom: 1px solid var(--line); padding: 16px 20px; }
.summary-table-card .panel-heading h3 { margin: 4px 0 0; font-size: var(--fs-lg); letter-spacing: -.4px; }
.summary-table-card .panel-heading .eyebrow { margin: 0; }
.summary-table-card-wide { grid-column: 1 / -1; }
.summary-table-pill { display: inline-flex; flex: 0 0 auto; align-items: center; gap: 6px; padding: 6px 12px; border: 1px solid var(--line); border-radius: 999px; background: var(--canvas); color: var(--muted); font-size: var(--fs-sm); font-weight: 600; white-space: nowrap; }
.summary-table-pill strong { color: var(--ink); font-size: var(--fs-md); font-weight: 800; }

/* Rent delayed buckets. Three side-by-side cards; the 11+ bucket pulses
   red so the agent can't miss it. The animation is a slow opacity fade
   rather than a hard blink — easier on the eyes during a long day. */
.rent-buckets { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; padding: 18px 20px; }
.rent-bucket { display: flex; flex-direction: column; gap: 4px; align-items: flex-start; padding: 14px; border: 1px solid var(--line); border-radius: 10px; }
.rent-bucket-label { font-size: var(--fs-xs); font-weight: 700; text-transform: uppercase; letter-spacing: .8px; color: var(--muted); }
.rent-bucket-count { font-size: var(--fs-num-lg); font-weight: 800; letter-spacing: -1px; line-height: 1; }
.rent-bucket-amount { font-size: var(--fs-sm); color: var(--muted); }
.rent-bucket-amber-soft { background: #fff8d9; border-color: #f4e2a3; }
.rent-bucket-amber-soft .rent-bucket-count { color: #6b4f00; }
.rent-bucket-amber { background: #fde6cf; border-color: #f3b97a; }
.rent-bucket-amber .rent-bucket-count { color: #843d00; }
.rent-bucket-red-flash { background: #fde2e6; border-color: #e8949c; animation: rent-bucket-flash 1.4s ease-in-out infinite; }
.rent-bucket-red-flash .rent-bucket-count { color: #8a1020; }
@keyframes rent-bucket-flash { 0%, 100% { background: #fde2e6; } 50% { background: #ffb1bb; } }
.rent-buckets-pill { background: #fde2e6; color: #8a1020; }

/* Smart analytics panel. Four equally-sized cards with a big number,
   a short label, and a one-line context sentence. The accent card uses
   the existing brand colour so the eye lands on the collection rate. */
/* auto-fit collapses 4 -> 2 -> 1 as the container narrows. Previously a
   hard `repeat(4, …)` with no media query, so the cards overflowed
   horizontally (and text broke one word per line) below ~700px. */
.smart-analytics-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 12px; padding: 18px 20px 22px; }
.smart-analytics-card { display: flex; flex-direction: column; gap: 4px; padding: 14px 16px; border: 1px solid var(--line); border-radius: 12px; background: var(--canvas); }
.smart-analytics-card-accent { background: linear-gradient(180deg, #f4f0ff 0%, #ece6ff 100%); border-color: #d6cdf0; }
.smart-analytics-label { font-size: var(--fs-xs); font-weight: 700; text-transform: uppercase; letter-spacing: .8px; color: var(--muted); }
.smart-analytics-value { font-size: var(--fs-num-lg); font-weight: 800; letter-spacing: -1px; line-height: 1.05; }
.smart-analytics-note { font-size: var(--fs-sm); color: var(--muted); }
.smart-analytics-disclaimer { margin: 0 20px; padding: 8px 0 0; color: var(--muted); font-size: var(--fs-sm); }

.property-code-cell { display: inline-block; padding: 1px 7px; border-radius: 6px; background: var(--canvas); color: var(--muted); font-family: var(--mono, ui-monospace, SFMono-Regular, Menlo, monospace); font-size: var(--fs-sm); }
.property-code-display { margin: 0; }

/* Multi-select views on the add/edit property form. A 3-up grid on
   desktop, 2-up on tablet, 1-up on mobile. Each option is a label that
   wraps a hidden checkbox + the option text, so the entire tile is
   clickable and the checkbox stays accessible. */
.view-checkbox-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; margin-top: 6px; }
.view-checkbox-option { display: flex; align-items: center; gap: 8px; padding: 10px 12px; border: 1px solid var(--line); border-radius: 10px; background: var(--canvas); cursor: pointer; font-size: var(--fs-md); }
.view-checkbox-option:hover { border-color: var(--accent, #c8c2e0); }
.view-checkbox-option input { margin: 0; }
.view-checkbox-option input:checked + span { font-weight: 700; color: var(--ink); }
@media (max-width: 700px) { .view-checkbox-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 430px) { .view-checkbox-grid { grid-template-columns: 1fr; } }

/* Detail page renders the saved views as a row of pills so the agent
   (and anyone who can see the property) can tell at a glance that a
   unit has both views, not just the last one picked. */
.view-pill-row { display: flex; flex-wrap: wrap; gap: 6px; }
.view-pill { display: inline-block; padding: 3px 10px; border: 1px solid var(--line); border-radius: 999px; background: var(--canvas); color: var(--ink); font-size: var(--fs-sm); font-weight: 600; }
.granularity-tabs { display: inline-flex; flex: 0 0 auto; gap: 4px; padding: 4px; border: 1px solid var(--line); border-radius: 999px; background: var(--canvas); }
.granularity-tab { padding: 6px 14px; border-radius: 999px; color: var(--muted); font-size: var(--fs-md); font-weight: 700; text-decoration: none; transition: background-color .15s ease, color .15s ease; }
.granularity-tab:hover { color: var(--ink); background: var(--surface); }
.granularity-tab.is-active { background: var(--lavender-pale); color: var(--lavender); }
.summary-table { width: 100%; table-layout: fixed; border-collapse: collapse; }
.summary-table th, .summary-table td { padding: 10px 18px; border-top: 1px solid var(--line); color: var(--muted); font-size: var(--fs-md); text-align: left; }
.summary-table thead th { padding: 10px 18px; border-top: 0; border-bottom: 1px solid var(--line); color: var(--faint); font-family: "DM Mono", monospace; font-size: var(--fs-2xs); font-weight: 500; letter-spacing: .07em; text-transform: uppercase; }
.summary-table tbody th[scope="row"] { color: var(--ink); font-weight: 700; font-size: var(--fs-md); }
.summary-table tfoot th, .summary-table tfoot td { border-top: 1px solid var(--line); border-bottom: 0; background: var(--canvas); color: var(--ink); font-weight: 700; font-size: var(--fs-md); }
.summary-table tfoot th { color: var(--ink); }
.summary-table .num { text-align: right; font-variant-numeric: tabular-nums; font-family: "DM Mono", monospace; font-size: var(--fs-sm); }
.summary-table tbody td.is-warning, .summary-table tfoot td.is-warning { color: var(--amber-ink); }
.summary-table td.is-positive, .summary-table tfoot td.is-positive { color: var(--lavender); }
.summary-table td.is-negative, .summary-table tfoot td.is-negative { color: var(--amber-ink); }
.summary-table-collection thead th:nth-child(1), .summary-table-collection tbody th[scope="row"], .summary-table-collection tfoot th[scope="row"] { width: 24%; }
.summary-table-collection .num { width: 15.2%; }
.summary-table-due thead th:nth-child(1), .summary-table-due tbody th[scope="row"], .summary-table-due tfoot th[scope="row"] { width: 44%; }
.summary-table-due .num { width: 28%; }
.summary-table-tenant thead th:nth-child(1), .summary-table-tenant tbody th[scope="row"], .summary-table-tenant tfoot th[scope="row"] { width: 34%; }
.summary-table-tenant .num { width: 22%; }
/* The rent collection table has six fixed-width columns. On a 360-430px
   phone the column widths get squeezed below readable size, so we give the
   table a sensible minimum width and let the .table-wrap parent scroll
   horizontally. The numbers stay on a single line so the alignment is
   preserved. The cell padding is also tightened so the table fits more
   comfortably on a tablet without needing the scroll. */
.summary-table-collection { min-width: 480px; }
.summary-table .num { white-space: nowrap; }
.summary-empty-state { margin: 16px 20px 20px; padding: 18px; border-radius: var(--radius-sm); background: var(--canvas); }
.table-action-column { text-align: right; white-space: nowrap; }
@media (max-width: 1000px) { .summary-tables { grid-template-columns: minmax(0, 1fr); } .summary-table-card-wide { grid-column: auto; } }
@media (max-width: 560px) { .rent-overview-panel { padding: 20px; }.rent-overview-panel > .panel-heading { align-items: flex-start; flex-direction: column; gap: 12px; }.summary-table-card .panel-heading { align-items: flex-start; flex-direction: column; gap: 10px; }.granularity-tabs { width: 100%; }.granularity-tab { flex: 1; padding-right: 8px; padding-left: 8px; text-align: center; } }

/* ============================================================
   Responsive overhaul (covers cell phones, tablets, laptops)
   - Touch targets (>=44px) on coarse pointer devices
   - Form grids, choice groups, amenity grids collapse to 1 col
   - Page heading and panel heading stack vertically
   - Filter bar becomes a single column
   - Detail page layouts stack on tablet
   - Column toggle popover fills the viewport width and scrolls
   - Tables keep their horizontal scroll inside .table-wrap
   - iOS Safari: 16px font on inputs prevents the focus zoom
   ============================================================ */

/* Touch device sizing (independent of width) */
@media (pointer: coarse) {
  /* 44px tap target for text inputs / selects / textareas only.
     Checkboxes and radios stay native-sized so the accent colour doesn't
     balloon into huge purple squares. */
  input[type="text"], input[type="email"], input[type="tel"], input[type="url"],
  input[type="number"], input[type="date"], input[type="password"],
  input[type="search"], input[type="time"], select, textarea { min-height: 44px; font-size: var(--fs-lg); }
  .button { min-height: 44px; padding: 10px 16px; font-size: var(--fs-md); }
  .icon-button { width: 38px; height: 38px; }
  .nav-link { min-height: 44px; }
  .mobile-header .icon-button,
  .mobile-header .mobile-menu-trigger { width: 42px; height: 42px; }
}

/* Checkbox / radio sizing — keep compact even on touch devices.
   Explicit width/height prevents the browser from stretching them when
   they sit inside a flex row that targets 44px touch targets. */
input[type="checkbox"], input[type="radio"] { width: 18px; height: 18px; flex-shrink: 0; accent-color: var(--lavender); }
.choice-option input[type="radio"], .amenity-option input[type="checkbox"] { width: 16px; height: 16px; }

/* Column-toggle checkboxes: small square box with a white background. The tick
   is drawn large enough to fill the box, so the visual indicator and the box
   read as the same size. Strip the native control so the browser's default
   blue/purple tick and fill do not bleed through. Reset padding / box-sizing
   so the inherited `padding: 0 11px` and content-box sizing from the global
   input rule cannot stretch the box into a rectangle. */
.column-toggle-row input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  box-sizing: border-box;
  flex: 0 0 16px;
  width: 16px;
  height: 16px;
  min-width: 16px;
  min-height: 16px;
  max-width: 16px;
  max-height: 16px;
  margin: 0;
  padding: 0;
  border: 1.5px solid #c5c5cf;
  border-radius: 3px;
  background: #ffffff;
  cursor: pointer;
  position: relative;
  transition: border-color .15s, box-shadow .15s;
}
.column-toggle-row input[type="checkbox"]:hover { border-color: #8a8a96; }
.column-toggle-row input[type="checkbox"]:focus-visible { outline: 2px solid var(--lavender); outline-offset: 2px; }
.column-toggle-row input[type="checkbox"]:checked {
  border-color: #17171b;
}
.column-toggle-row input[type="checkbox"]:checked::after {
  content: '';
  position: absolute;
  left: 2px;
  top: 0px;
  width: 5px;
  height: 10px;
  border: solid #17171b;
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}
.column-toggle-row input[type="checkbox"]:indeterminate {
  border-color: #17171b;
}
.column-toggle-row input[type="checkbox"]:indeterminate::after {
  content: '';
  position: absolute;
  left: 2px;
  top: 6px;
  width: 9px;
  height: 2px;
  background: #17171b;
}

/* Tablet: stack detail and form-side-by-side layouts that desktop keeps as 2 col */
@media (max-width: 980px) {
  .condo-detail-layout { grid-template-columns: 1fr; }
  .condo-detail-media { position: static; }
  .condo-detail-main-photo { height: clamp(280px, 48vh, 460px); }
  .condo-detail-no-photo { height: clamp(280px, 48vh, 460px); }
  .two-up { grid-template-columns: 1fr; }
  .detail-list { grid-template-columns: 1fr; }
  .detail-list > div:nth-child(odd) { padding-right: 0; }
  .detail-list > div:nth-child(even) { padding-left: 0; border-left: 0; }
  .contact-list { grid-template-columns: 1fr; gap: 12px; }
}

/* Small tablet / large phone: page heading, panel heading, filter bar stack,
   form grids become a single column, column-toggle popover goes full-width */
@media (max-width: 720px) {
  .page-heading { flex-direction: column; align-items: stretch; gap: 14px; margin-bottom: 24px; }
  .page-heading > .button { width: 100%; }
  .page-heading > div > p:last-child { font-size: var(--fs-md); }
  .title-with-status { flex-direction: column; align-items: flex-start; gap: 10px; }
  .detail-heading { flex-direction: column; align-items: stretch; gap: 14px; }

  .panel-heading { flex-direction: column; align-items: stretch; gap: 12px; margin-bottom: 16px; }
  .panel-heading-actions { width: 100%; flex-wrap: wrap; }
  .panel-heading-actions > .button { flex: 1 1 140px; min-width: 0; }
  .panel-heading-actions > .text-link { flex: 0 0 auto; }

  .filter-bar { grid-template-columns: 1fr; gap: 10px; }
  .filter-actions { width: 100%; }
  .filter-actions .button { flex: 1 1 0; min-width: 0; }

  .form-grid { grid-template-columns: 1fr; gap: 13px; }
  .choice-group { grid-template-columns: 1fr; }
  .listing-type-field .choice-group { grid-template-columns: 1fr; }
  .form-section { padding-top: 16px; }

  .panel { padding: 18px; }
  .form-panel { padding: 18px; }
  .condo-form-media, .condo-form-details { padding: 18px; }
  .rent-overview-panel { padding: 18px; }
  .summary-empty-state { margin: 14px 18px 18px; padding: 14px; }
  .summary-table-card .panel-heading { padding: 14px 18px; }

  .condo-photo-dropzone { min-height: 160px; padding: 18px; }
  .condo-photo-dropzone-mark { width: 34px; height: 34px; font-size: 22px; }

  /* Column-toggle popover: expand the wrapper to full panel width so the
     100%-wide menu has somewhere to grow into. Inside the menu, give each
     row a comfortable tap height without turning the checkbox into a slab. */
  .column-toggle { width: 100%; }
  .column-toggle-menu { left: 0; right: 0; min-width: 0; width: 100%; max-height: 70vh; overflow-y: auto; padding: 10px; }
  .column-toggle-row { padding: 10px 12px; gap: 12px; font-size: var(--fs-md); }
  .editable-dropdown-menu { max-height: 50vh; overflow-y: auto; }

  .soft-notice { padding: 13px 14px; }
  .empty-state { padding: 24px 18px; }

  .row-actions { justify-content: flex-start; gap: 12px; }
}

/* Phone: single-column stat cards and condo grids, drop form-panel padding further */
@media (max-width: 560px) {
  .stats-grid { grid-template-columns: 1fr; }
  .condo-grid, .condo-grid-full { grid-template-columns: 1fr; }
  .filter-actions { flex-direction: column; align-items: stretch; }
  .filter-actions .button { width: 100%; }
  .filter-actions .text-link { text-align: center; }

  .panel { padding: 16px; }
  .form-panel { padding: 16px; }
  .condo-form-media, .condo-form-details { padding: 16px; }
  .soft-notice { flex-direction: column; align-items: flex-start; gap: 8px; }
  .form-actions { justify-content: stretch; }
  .form-actions .button { width: 100%; }
  .detail-heading-actions { width: 100%; }
  .detail-heading-actions .button { flex: 1 1 0; min-width: 0; }

  .column-toggle-row { padding: 8px 10px; }
  .column-toggle-heading { font-size: var(--fs-sm); }
}

/* Small phone: tighter padding, smaller headings */
@media (max-width: 430px) {
  .page-heading h1, .login-card h1, .empty-state h1 { font-size: var(--fs-2xl); letter-spacing: -1.2px; }
  .panel-heading h2, .panel h2, .empty-state h2 { font-size: var(--fs-lg); }
  .stat-card { padding: 16px; min-height: 120px; }
  .stat-card strong { font-size: var(--fs-num); }

  .form-panel, .panel { padding: 14px; }
  .condo-form-media, .condo-form-details { padding: 14px; }
  .rent-overview-panel { padding: 16px; }
  .form-section { padding-top: 14px; }

  .button { padding: 8px 12px; }
  .button-large { min-height: 44px; }

  .row-actions { gap: 10px; }
  .text-link { font-size: var(--fs-sm); }

  .condo-photo-dropzone { min-height: 140px; padding: 16px; }
  .condo-photo-dropzone-mark { width: 32px; height: 32px; font-size: 20px; }
  .condo-photo-preview-grid, .condo-saved-photo-grid { grid-template-columns: repeat(auto-fill, minmax(112px, 1fr)); }

  .summary-table th, .summary-table td { padding: 9px 10px; }
  .summary-table-card .panel-heading { padding: 12px 14px; }
  /* The rent collection table has six fixed-width columns. On a narrow
     phone the columns still get squeezed below readable size even with
     tight padding, so we shrink the cell padding further and lean on the
     .table-wrap horizontal scroll (the table has min-width: 480px so
     headers and "0 / 0" cells no longer wrap mid-word). */
  .summary-table-collection th, .summary-table-collection td { padding: 8px 6px; font-size: var(--fs-sm); }
  .summary-table-collection thead th { font-size: var(--fs-2xs); letter-spacing: 0.04em; }

  .form-errors { padding: 11px 12px; font-size: var(--fs-sm); }

  .amenity-list li { font-size: var(--fs-xs); padding: 5px 8px; }
}

/* ------------------------------------------------------------------ */
/* System log viewer                                                  */
/* ------------------------------------------------------------------ */

.log-summary { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; margin-bottom: 12px; }
.log-stat-card { padding: 10px 14px; border-left: 3px solid var(--muted); }
.log-stat-error { border-left-color: var(--rose); }
.log-stat-warn { border-left-color: var(--amber); }
.log-stat-info { border-left-color: var(--accent); }
.log-stat-audit { border-left-color: #6b6b76; }
.log-stat-card .stat-label { color: var(--faint); }
.log-stat-card .stat-note { font-size: var(--fs-sm); }

.system-log-table { font-size: var(--fs-sm); }
.system-log-table td { vertical-align: top; }
.system-log-table .log-when { white-space: nowrap; color: var(--faint); font-family: "DM Mono", monospace; }
.system-log-table .log-time { color: var(--muted); margin-left: 4px; }
.system-log-table .log-message-cell { min-width: 200px; max-width: 320px; }
.system-log-table .log-message { display: block; }
.system-log-table .log-url code { font-size: var(--fs-xs); word-break: break-all; }
.system-log-table .log-method { font-size: var(--fs-2xs); color: var(--faint); margin-left: 4px; text-transform: uppercase; }
.system-log-table .log-duration { font-family: "DM Mono", monospace; }
table.is-compact.system-log-table td { font-size: var(--fs-xs); }
table.is-compact.system-log-table .log-when { font-size: var(--fs-2xs); }
table.is-compact.system-log-table .log-message-cell { min-width: 160px; max-width: 260px; }

.log-level-pill { display: inline-block; padding: 2px 7px; border-radius: 4px; font-size: var(--fs-2xs); font-weight: 600; text-transform: uppercase; letter-spacing: 0.06em; }
.log-level-error { background: rgba(225, 50, 80, 0.12); color: var(--rose); }
.log-kind-pill { display: inline-block; padding: 2px 7px; border-radius: 4px; font-size: var(--fs-2xs); font-weight: 600; text-transform: uppercase; letter-spacing: 0.06em; }
.log-kind-pill-audit { background: rgba(120, 130, 200, 0.16); color: #3a405c; }
.log-kind-pill-system { background: rgba(80, 95, 180, 0.14); color: #4a5cbf; }
.log-role-pill { display: inline-block; padding: 1px 6px; border-radius: 8px; font-size: var(--fs-2xs); font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; margin-top: 2px; }
.log-role-pill-admin { background: rgba(80, 95, 180, 0.16); color: #2a3162; }
.log-role-pill-company_admin { background: rgba(58, 142, 95, 0.18); color: #1e6e3d; }
.log-role-pill-agent { background: rgba(196, 112, 56, 0.18); color: #8a4818; }
.field-hint-inline { font-size: var(--fs-2xs); color: var(--muted); text-transform: uppercase; letter-spacing: 0.04em; font-weight: 600; margin-left: 4px; }
.log-level-warn { background: rgba(220, 165, 60, 0.14); color: var(--amber); }
.log-level-info { background: rgba(120, 130, 200, 0.14); color: var(--accent); }

.log-status { display: inline-block; padding: 1px 6px; border-radius: 3px; font-family: "DM Mono", monospace; font-size: var(--fs-xs); }
.log-status-2xx { background: rgba(80, 170, 120, 0.14); color: var(--green); }
.log-status-3xx { background: rgba(120, 130, 200, 0.14); color: var(--accent); }
.log-status-4xx { background: rgba(220, 165, 60, 0.14); color: var(--amber); }
.log-status-5xx { background: rgba(225, 50, 80, 0.12); color: var(--rose); }

.source-tag { font-family: "DM Mono", monospace; font-size: var(--fs-2xs); color: var(--faint); background: var(--canvas); padding: 1px 6px; border-radius: 3px; }

.log-details-toggle { margin-top: 6px; }
.log-details-toggle summary { font-size: var(--fs-xs); color: var(--accent); cursor: pointer; user-select: none; }
.log-details-pre { background: var(--canvas); border: 1px solid var(--hairline); border-radius: 4px; padding: 10px; margin-top: 6px; max-height: 240px; overflow: auto; font-size: var(--fs-xs); line-height: 1.5; }

@media (max-width: 720px) {
  .log-summary { grid-template-columns: 1fr; }
  .system-log-table .log-message-cell { min-width: 0; max-width: 100%; }
}

/* ------------------------------------------------------------------ */
/* Subscriber analytics                                                */
/* ------------------------------------------------------------------ */

/* KPI strip — five tiles on a single row on wide screens, two on
   tablet, one on phone. The tile variants (`-accent`, `-warning`,
   `-danger`, `-soft`) are colour-coded so the headline numbers are
   scannable from across the room. */
.kpi-strip {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 12px;
  margin: 14px 0 18px;
}
.kpi-tile { padding: 16px 18px; border: 1px solid var(--line); border-radius: 12px; background: var(--surface); display: grid; gap: 4px; }
.kpi-tile-accent { background: linear-gradient(135deg, #eef0fa, #fdf6f0); border-color: #d2d6e8; }
.kpi-tile-warning { background: linear-gradient(135deg, #fff7e6, #fdf2e3); border-color: #f0d8a8; }
.kpi-tile-danger { background: linear-gradient(135deg, #fcebee, #fff5f6); border-color: #f1c4cb; }
.kpi-tile-soft { background: linear-gradient(135deg, #ecf6ed, #f6fbf7); border-color: #c8e0cb; }
.kpi-tile-label { font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: 0.08em; color: var(--muted); font-weight: 700; }
.kpi-tile-value { font-size: var(--fs-num); font-weight: 700; letter-spacing: -0.6px; }
.kpi-tile-meta { font-size: var(--fs-sm); color: var(--muted); }

/* Two graphs side-by-side on the dashboard subscriber panel. The
   right column (plan mix) is a list; the left column (growth) is a
   column chart. */
.subscriber-graphs {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 18px;
}
.subscriber-graph { display: grid; gap: 8px; align-content: start; }
.subscriber-graph .eyebrow { margin: 0; }

/* Mini column chart — 6 bars, equal width, no axes. Each bar is
   coloured with a 30-day gradient so the most recent month stands
   out. */
.mini-column-chart {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 6px;
  align-items: end;
  height: 80px;
  padding: 6px 0;
  border-bottom: 1px solid var(--line);
}
.mini-column {
  width: 100%;
  background: linear-gradient(180deg, #6f7ad5 0%, #4a5cbf 100%);
  border-radius: 4px 4px 0 0;
  min-height: 4px;
  opacity: 0.85;
}
.mini-column:last-child { background: linear-gradient(180deg, #e3945b 0%, #c47038 100%); opacity: 1; }
.mini-column-labels { display: grid; grid-template-columns: repeat(6, 1fr); gap: 6px; font-size: var(--fs-2xs); color: var(--faint); text-align: center; text-transform: uppercase; letter-spacing: 0.04em; }

.mini-plan-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.mini-plan-list li { display: grid; grid-template-columns: 1fr auto auto; align-items: center; gap: 10px; padding: 8px 10px; border: 1px solid var(--line); border-radius: 8px; background: var(--canvas); font-size: var(--fs-md); }
.mini-plan-name { font-weight: 600; }
.mini-plan-count { font-size: var(--fs-xs); color: var(--muted); }
.mini-plan-mrr { font-weight: 600; font-family: "DM Mono", monospace; font-size: var(--fs-sm); }

/* Big chart panels on /admin/subscribers. */
.chart-panel { padding: 18px 20px; }
.panel-header { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; margin-bottom: 12px; }
.panel-header h2 { margin: 0 0 4px; font-size: var(--fs-lg); letter-spacing: -0.4px; }
.panel-header p { margin: 0; color: var(--muted); font-size: var(--fs-md); }

/* Horizontal bar chart (plan mix). Each row gets a label column and
   a track column with two stacked bars: one for subscription count,
   one for MRR contribution. */
.bar-chart { list-style: none; margin: 0; padding: 0; display: grid; gap: 14px; }
.bar-chart-row { display: grid; grid-template-columns: 160px 1fr; gap: 14px; align-items: center; }
.bar-chart-label { display: grid; gap: 2px; }
.bar-chart-label strong { font-size: var(--fs-md); }
.bar-chart-label .muted { font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: 0.06em; }
.bar-chart-track { position: relative; height: 36px; background: var(--canvas); border: 1px solid var(--line); border-radius: 8px; overflow: hidden; }
.bar-chart-bar { height: 18px; display: flex; align-items: center; padding: 0 8px; color: white; font-size: var(--fs-xs); font-weight: 600; }
.bar-chart-bar-count { background: linear-gradient(90deg, #4a5cbf, #6f7ad5); }
.bar-chart-bar-mrr { background: linear-gradient(90deg, #c47038, #e3945b); margin-top: 2px; }
.bar-chart-bar span { white-space: nowrap; }

/* Full-height column chart (growth, on subscribers page). */
.column-chart { display: grid; grid-template-columns: repeat(6, 1fr); gap: 12px; align-items: end; height: 220px; padding: 10px 0; border-bottom: 1px solid var(--line); }
.column-chart-col { display: grid; grid-template-rows: 1fr auto; gap: 6px; align-items: end; }
.column-chart-bar { background: linear-gradient(180deg, #6f7ad5 0%, #4a5cbf 100%); border-radius: 6px 6px 0 0; min-height: 4px; position: relative; transition: opacity 120ms; }
.column-chart-col:last-child .column-chart-bar { background: linear-gradient(180deg, #e3945b 0%, #c47038 100%); }
.column-chart-col:hover .column-chart-bar { opacity: 0.85; }
.column-chart-value { position: absolute; top: -18px; left: 0; right: 0; text-align: center; font-size: var(--fs-sm); font-weight: 600; color: var(--ink); }
.column-chart-label { text-align: center; font-size: var(--fs-xs); color: var(--faint); text-transform: uppercase; letter-spacing: 0.06em; }

.dashboard-strip { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 12px; margin-bottom: 18px; }
.kpi-card { padding: 16px 18px; border: 1px solid var(--line); border-radius: 12px; background: var(--surface); display: grid; gap: 4px; }
.kpi-card-accent { background: linear-gradient(135deg, #eef0fa, #fdf6f0); border-color: #d2d6e8; }
.kpi-card-warning { background: linear-gradient(135deg, #fff7e6, #fdf2e3); border-color: #f0d8a8; }
.kpi-card-danger { background: linear-gradient(135deg, #fcebee, #fff5f6); border-color: #f1c4cb; }
.kpi-card-soft { background: linear-gradient(135deg, #ecf6ed, #f6fbf7); border-color: #c8e0cb; }
.kpi-label { font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: 0.08em; color: var(--muted); font-weight: 700; }
.kpi-value { font-size: var(--fs-num-lg); font-weight: 700; letter-spacing: -0.6px; }
.kpi-meta { font-size: var(--fs-sm); color: var(--muted); }

.dashboard-row { display: grid; grid-template-columns: 1.4fr 1fr; gap: 18px; margin-bottom: 18px; }

/* Renew popover on the company-admins list. The <details> element
   stays closed by default; opening it shows the renewal form so the
   page doesn't have to round-trip to a separate route for the
   common case. */
.renew-popover { position: relative; display: inline-block; }
.renew-popover[open] { position: relative; }
.renew-popover summary { list-style: none; cursor: pointer; }
.renew-popover summary::-webkit-details-marker { display: none; }
.renew-form {
  position: absolute;
  right: 0;
  top: calc(100% + 4px);
  z-index: 30;
  width: 320px;
  padding: 14px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 12px;
  box-shadow: 0 10px 30px rgba(20, 20, 30, 0.18);
  display: grid;
  gap: 10px;
  text-align: left;
}
.renew-form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.renew-form label { display: grid; gap: 4px; font-size: var(--fs-sm); color: var(--muted); }
.renew-form input, .renew-form select { width: 100%; }
.renew-form .checkbox-row { display: flex; align-items: center; gap: 6px; }

@media (max-width: 1100px) {
  .analytics-hero { grid-template-columns: repeat(3, 1fr); }
  .donut-pair { grid-template-columns: 1fr; }
}
@media (max-width: 900px) {
  .kpi-strip { grid-template-columns: repeat(2, 1fr); }
  .subscriber-graphs { grid-template-columns: 1fr; }
  .dashboard-strip { grid-template-columns: repeat(2, 1fr); }
  .dashboard-row { grid-template-columns: 1fr; }
  .bar-chart-row { grid-template-columns: 1fr; }
  .analytics-hero { grid-template-columns: repeat(2, 1fr); }
  .analytics-sub-strip { grid-template-columns: repeat(2, 1fr); }
  .analytics-row { grid-template-columns: 1fr; }
  .donut-and-legend { grid-template-columns: 1fr; }
  .renewal-buckets { grid-template-columns: 1fr; }
}
@media (max-width: 540px) {
  .kpi-strip { grid-template-columns: 1fr; }
  .dashboard-strip { grid-template-columns: 1fr; }
  .analytics-hero { grid-template-columns: 1fr; }
  .analytics-sub-strip { grid-template-columns: 1fr; }
  .renewal-buckets { grid-template-columns: 1fr; }
  .top-customers li { grid-template-columns: 1fr; }
}

/* ------------------------------------------------------------------ */
/* Subscribers analytics — new view                                  */
/* ------------------------------------------------------------------ */

/* Period selector — pill nav with active state. The current window
   re-renders the page with `?period=...` so the URL is shareable. */
.period-selector { display: inline-flex; padding: 4px; background: var(--canvas); border: 1px solid var(--line); border-radius: 10px; gap: 2px; }
.period-tab { padding: 6px 12px; border-radius: 7px; font-size: var(--fs-sm); color: var(--muted); text-decoration: none; transition: background-color 120ms, color 120ms; }
.period-tab:hover { color: var(--ink); }
.period-tab.is-active { background: var(--surface); color: var(--ink); box-shadow: 0 1px 2px rgba(20, 20, 30, 0.08); font-weight: 600; }

/* Hero KPI strip — 6 large tiles. The first tile is accent (the
   headline MRR), the second is neutral (active subs), the third is
   neutral (ARPU), the fourth warning (churn), the fifth warning
   (expiring), the sixth danger when overdue > 0. */
.analytics-hero { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 12px; margin-bottom: 14px; }
.analytics-tile { position: relative; padding: 18px 20px 8px; border: 1px solid var(--line); border-radius: 14px; background: var(--surface); display: grid; gap: 4px; min-height: 142px; overflow: hidden; }
.analytics-tile-accent { background: linear-gradient(135deg, #eef0fa, #fdf6f0); border-color: #d2d6e8; }
.analytics-tile-warning { background: linear-gradient(135deg, #fff7e6, #fdf2e3); border-color: #f0d8a8; }
.analytics-tile-danger { background: linear-gradient(135deg, #fcebee, #fff5f6); border-color: #f1c4cb; }
.analytics-tile-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 8px; }
.analytics-tile-label { font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: 0.08em; color: var(--muted); font-weight: 700; }
.analytics-tile-value { font-size: var(--fs-num-lg); font-weight: 700; letter-spacing: -0.6px; line-height: 1.1; }
.analytics-tile-meta { font-size: var(--fs-sm); color: var(--muted); margin-bottom: 6px; }
.analytics-tile-accent, .analytics-tile-warning, .analytics-tile-danger { color: var(--ink); }

/* Delta chip on the MRR tile. Up is green, down is rose, so the
   reader can see the direction at a glance. */
.analytics-delta { display: inline-flex; align-items: center; gap: 2px; padding: 2px 7px; border-radius: 12px; font-size: var(--fs-xs); font-weight: 700; }
.analytics-delta-up { color: #1e6e3d; background: rgba(80, 170, 120, 0.18); }
.analytics-delta-down { color: #9f3044; background: rgba(225, 80, 110, 0.16); }

/* Sparkline (SVG). Sits at the bottom of the hero tile and inherits
   the tile's colour so it always reads. */
.analytics-spark { position: absolute; bottom: 8px; left: 16px; right: 16px; width: calc(100% - 32px); height: 30px; color: rgba(80, 95, 180, 0.6); }
.analytics-spark-bars { color: rgba(120, 130, 200, 0.7); }
.analytics-tile-accent .analytics-spark { color: rgba(80, 95, 180, 0.55); }
.analytics-tile-warning .analytics-spark { color: rgba(220, 165, 60, 0.6); }
.analytics-tile-danger .analytics-spark { color: rgba(225, 80, 110, 0.6); }

/* Sub-strip — 4 smaller tiles, no sparkline, just a number. */
.analytics-sub-strip { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; margin-bottom: 18px; }
.analytics-sub-tile { padding: 12px 14px; border: 1px solid var(--line); border-radius: 10px; background: var(--surface); display: grid; gap: 2px; }
.analytics-sub-tile-label { font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.08em; color: var(--muted); font-weight: 700; }
.analytics-sub-tile-value { font-size: var(--fs-num-sm); font-weight: 700; letter-spacing: -0.4px; }
.analytics-sub-tile-meta { font-size: var(--fs-xs); color: var(--muted); }

/* MRR trend chart — 12 vertical columns, value above the bar,
   month label below. The bars are coloured by intensity so the
   growth shape is visible at a glance. The minmax(0, 1fr) keeps
   each column from growing past its share when the price label
   (e.g. ฿999.9M) is wider than 1fr would otherwise allow. */
.trend-chart { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); gap: 8px; align-items: end; height: 200px; padding: 14px 0 24px; border-bottom: 1px solid var(--line); margin-bottom: 12px; min-width: 0; }
.trend-col { display: grid; grid-template-rows: auto 1fr auto; gap: 4px; align-items: end; min-width: 0; }
.trend-value { text-align: center; font-size: var(--fs-xs); color: var(--muted); font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.trend-bar { background: linear-gradient(180deg, #6f7ad5 0%, #4a5cbf 100%); border-radius: 4px 4px 0 0; min-height: 2px; position: relative; transition: opacity 120ms; }
.trend-col:last-child .trend-bar { background: linear-gradient(180deg, #e3945b 0%, #c47038 100%); }
.trend-col:hover .trend-bar { opacity: 0.82; }
.trend-label { text-align: center; font-size: var(--fs-xs); color: var(--faint); text-transform: uppercase; letter-spacing: 0.04em; }

/* Analytics row — donut + top customers side by side. */
.analytics-row { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; margin-bottom: 18px; }

/* Donut + legend. The donut is a 160×160 SVG; the legend is a
   vertically-stacked list. */
.donut-and-legend { display: grid; grid-template-columns: 200px 1fr; gap: 18px; align-items: center; }
.donut { width: 200px; height: 200px; display: block; }
.donut-legend { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.donut-legend li { display: grid; grid-template-columns: 14px 1fr auto auto; align-items: center; gap: 10px; padding: 7px 10px; border: 1px solid var(--line); border-radius: 8px; background: var(--canvas); font-size: var(--fs-md); }
.donut-swatch { width: 12px; height: 12px; border-radius: 3px; }
.donut-legend-content { display: grid; gap: 1px; }
.donut-legend-content strong { font-size: var(--fs-md); }
.donut-legend-content .muted { font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.06em; }
.donut-legend-meta { display: grid; gap: 1px; text-align: right; }
.donut-legend-meta strong { font-size: var(--fs-md); color: #1e6e3d; }
.donut-legend-meta .muted { font-size: var(--fs-xs); }
.donut-legend-mrr { font-family: "DM Mono", monospace; font-size: var(--fs-md); font-weight: 600; min-width: 80px; text-align: right; }

/* Top 10 customers list. Each row is a rank + name + MRR + ARR. */
.top-customers { list-style: none; margin: 0; padding: 0; display: grid; gap: 4px; }
.top-customers li { display: grid; grid-template-columns: 28px 1fr auto auto; align-items: center; gap: 12px; padding: 9px 12px; border: 1px solid var(--line); border-radius: 8px; background: var(--canvas); }
.top-customers-rank { font-family: "DM Mono", monospace; font-size: var(--fs-md); color: var(--faint); font-weight: 700; text-align: right; }
.top-customers-content { display: grid; gap: 1px; }
.top-customers-name { font-weight: 600; color: var(--ink); text-decoration: none; }
.top-customers-name:hover { text-decoration: underline; }
.top-customers-meta { font-size: var(--fs-xs); color: var(--muted); display: inline-flex; align-items: center; gap: 4px; }
.top-customers-mrr { font-family: "DM Mono", monospace; font-size: var(--fs-md); font-weight: 700; text-align: right; }
.top-customers-mrr .muted { font-size: var(--fs-xs); font-weight: 400; }
.top-customers-arr { font-family: "DM Mono", monospace; font-size: var(--fs-sm); color: var(--muted); text-align: right; min-width: 70px; }

/* Renewal pipeline — three urgency buckets with count + ARR-at-risk. */
.renewal-buckets { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; margin-bottom: 14px; }
.renewal-bucket { padding: 12px 14px; border-radius: 10px; border: 1px solid; display: grid; gap: 4px; }
.renewal-bucket header { display: flex; justify-content: space-between; align-items: baseline; }
.renewal-bucket h3 { margin: 0; font-size: var(--fs-md); font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; }
.renewal-bucket-count { font-size: var(--fs-num); font-weight: 700; }
.renewal-bucket-meta { margin: 0; font-size: var(--fs-sm); color: var(--muted); }
.renewal-bucket-urgent { background: #fcebee; border-color: #f1c4cb; color: #9f3044; }
.renewal-bucket-urgent .renewal-bucket-meta { color: #6b2334; }
.renewal-bucket-soon { background: #fff7e6; border-color: #f0d8a8; color: #8a5a17; }
.renewal-bucket-soon .renewal-bucket-meta { color: #6b4514; }
.renewal-bucket-later { background: #ecf6ed; border-color: #c8e0cb; color: #1e6e3d; }
.renewal-bucket-later .renewal-bucket-meta { color: #2c5439; }

/* Days-to-renewal pill in the renewal table — same colour logic as
   the bucket headers so the row reads as a glance. */
.renewal-days { display: inline-block; padding: 3px 9px; border-radius: 12px; font-family: "DM Mono", monospace; font-size: var(--fs-sm); font-weight: 700; }
.renewal-days-urgent { background: #fcebee; color: #9f3044; }
.renewal-days-soon { background: #fff7e6; color: #8a5a17; }
.renewal-days-later { background: #ecf6ed; color: #1e6e3d; }

/* Cohort grid (acquisition heatmap). Each cell's background opacity
   is set via --intensity from the JS so the hottest months pop. */
.cohort-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(72px, 1fr)); gap: 6px; }
.cohort-cell {
  display: grid;
  gap: 2px;
  padding: 12px 10px;
  text-align: center;
  border-radius: 8px;
  background: color-mix(in srgb, #4a5cbf calc(var(--intensity) * 60%), #eef0fa);
  color: #1a1d2e;
  font-weight: 600;
}
.cohort-month { font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.04em; color: #3a405c; }
.cohort-count { font-size: var(--fs-num-sm); font-family: "DM Mono", monospace; }

/* Generic data table — used by the renewal pipeline list. */
.data-table th { font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); }
.data-table td { vertical-align: top; }

/* Sparkline attached to the dashboard's MRR tile. */
.kpi-spark { position: absolute; bottom: 6px; left: 16px; right: 16px; width: calc(100% - 32px); height: 24px; color: rgba(80, 95, 180, 0.55); }
.kpi-tile-accent .kpi-spark { color: rgba(80, 95, 180, 0.55); }

/* ------------------------------------------------------------------ */
/* v33 analytics v2 — richer, more graphical view                    */
/* ------------------------------------------------------------------ */

/* Subscription health bar — one horizontal bar with 5 colour-coded
   segments and a legend. Drives the "what's the state of the
   platform" question at a glance. */
.health-bar { display: flex; height: 14px; width: 100%; border-radius: 7px; overflow: hidden; background: var(--canvas); border: 1px solid var(--line); }
.health-bar-seg { height: 100%; transition: width 240ms ease; }
.health-legend { list-style: none; margin: 14px 0 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 8px; }
.health-legend li { display: grid; grid-template-columns: 12px 1fr auto auto; align-items: center; gap: 8px; padding: 7px 10px; border: 1px solid var(--line); border-radius: 8px; background: var(--canvas); font-size: var(--fs-sm); }
.health-swatch { width: 11px; height: 11px; border-radius: 3px; }
.health-legend li strong { font-size: var(--fs-md); }
.health-legend li .muted { font-size: var(--fs-xs); }

/* Dual donut — by MRR + by count, side by side. Each card has the
   same SVG donut structure but different legend values. */
.donut-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
.donut-card { padding: 16px; border: 1px solid var(--line); border-radius: 12px; background: var(--canvas); }
.donut-card-title { margin: 0 0 12px; font-size: var(--fs-sm); text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); font-weight: 700; }

/* Stacked trend chart — same 12-column structure as the trend
   chart, but each column is split into base / per-user / discount
   segments stacked vertically. Heights are set via inline % from
   the view. */
.trend-chart-stacked .trend-col { grid-template-rows: auto 1fr auto; }
.trend-bar-stack { display: flex; flex-direction: column-reverse; width: 100%; min-height: 2px; border-radius: 4px 4px 0 0; overflow: hidden; }
.trend-bar-seg { width: 100%; }
.trend-bar-seg-base { background: linear-gradient(180deg, #6f7ad5, #4a5cbf); }
.trend-bar-seg-user { background: linear-gradient(180deg, #5fa6c4, #3a8e9b); }
.trend-bar-seg-discount { background: linear-gradient(180deg, #e8a4b3, #c45f7a); }

/* Revenue composition legend swatches. */
.rev-comp-swatch { display: inline-block; width: 10px; height: 10px; border-radius: 2px; margin-left: 12px; }
.rev-comp-swatch-base { background: #4a5cbf; }
.rev-comp-swatch-user { background: #3a8e9b; }
.rev-comp-swatch-discount { background: #c45f7a; }

/* Renewal timeline (gantt) — a list of rows. Each row has a
   company label on the left, a 100%-wide track with an absolutely
   positioned coloured bar, and a days-remaining pill on the
   right. The track is 90 days wide starting at today. */
.timeline-axis { position: relative; height: 18px; margin-bottom: 4px; padding-left: 200px; padding-right: 80px; }
.timeline-axis-grid { position: relative; height: 100%; border-bottom: 1px dashed var(--line); }
.timeline-axis-grid span { position: absolute; top: 0; bottom: 0; width: 1px; background: var(--line); opacity: 0.5; }
.timeline-axis-tick { font-size: var(--fs-xs); color: var(--muted); margin-left: 8px; }
.timeline-rows { list-style: none; margin: 0; padding: 0; display: grid; gap: 4px; }
.timeline-row { display: grid; grid-template-columns: 200px 1fr 80px; align-items: center; gap: 10px; padding: 6px 0; }
.timeline-label { display: grid; gap: 1px; font-size: var(--fs-md); min-width: 0; }
.timeline-label strong { font-size: var(--fs-md); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.timeline-track { position: relative; height: 18px; background: var(--canvas); border: 1px solid var(--line); border-radius: 4px; }
.timeline-bar { position: absolute; top: 1px; bottom: 1px; border-radius: 3px; transition: opacity 120ms; }
.timeline-bar-urgent { background: linear-gradient(180deg, #f5a3b1, #c45f7a); }
.timeline-bar-soon { background: linear-gradient(180deg, #f0d28a, #d4a73a); }
.timeline-bar-later { background: linear-gradient(180deg, #7fc39a, #3a8e5f); }
.timeline-bar:hover { opacity: 0.8; }
.timeline-days { text-align: right; }

@media (max-width: 700px) {
  .timeline-axis { display: none; }
  .timeline-row { grid-template-columns: 1fr 60px; }
  .timeline-track { grid-column: 1 / -1; }
}

/* ---------------------------------------------------------------------------
   Agent User console
   --------------------------------------------------------------------------- */
/* Stats strip: 4 tiles in a single row, distinct from .stats-grid
   (which is used by the dashboard). Each tile carries its own tone so
   the active / expiring / disabled counts are colour-coded at a glance. */
.stats-strip { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; margin-bottom: 17px; }
.stat-tile { padding: 16px 18px; border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--surface); box-shadow: 0 1px 0 rgba(20, 20, 26, .015); display: flex; flex-direction: column; gap: 4px; }
.stat-tile .stat-label { color: var(--faint); font-family: "DM Mono", monospace; font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: .6px; }
.stat-tile .stat-value { font-size: var(--fs-num-lg); font-weight: 800; letter-spacing: -1.2px; line-height: 1.05; color: var(--ink); }
.stat-tile-total { border-left: 3px solid var(--ink); }
.stat-tile-active { border-left: 3px solid #3a8e5f; }
.stat-tile-active .stat-value { color: #3a8e5f; }
.stat-tile-pending { border-left: 3px solid #d4a73a; }
.stat-tile-pending .stat-value { color: #b58723; }
.stat-tile-disabled { border-left: 3px solid var(--faint); }
.stat-tile-disabled .stat-value { color: var(--muted); }
@media (max-width: 900px) { .stats-strip { grid-template-columns: repeat(2, 1fr); } }

/* Filter bar variants. The legacy .filter-bar uses a 3-col grid; the
   Agent User console has 4 fields plus an action cluster, so we use
   a flexible grid that wraps on narrow screens. */
.filter-bar { gap: 12px; }
.filter-field { display: flex; flex-direction: column; gap: 4px; }
.filter-field label { color: var(--faint); font-family: "DM Mono", monospace; font-size: var(--fs-2xs); text-transform: uppercase; }
.filter-field input, .filter-field select { font-size: var(--fs-md); }
.filter-actions { display: inline-flex; align-items: center; gap: 8px; align-self: end; }
.filter-panel { padding: 0; }
.filter-panel > .filter-bar { margin-bottom: 0; border: 0; box-shadow: none; padding: 14px 16px; }

/* Right-aligned numeric column. Used on the agent user table for
   portfolio size so the digits line up with the rest of the row. */
.numeric-col { text-align: right; font-variant-numeric: tabular-nums; }

/* Per-row "table-action-header" anchors the icon button cluster to
   the right edge. */
.table-action-header { text-align: right; }

/* Panel heading row. Some panels (the list) need a "X records — page
   Y of Z" caption above the table; this is a smaller, more compact
   alternative to .panel-heading. */
.panel-heading-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 14px; }
.panel-heading-info { color: var(--muted); font-size: var(--fs-md); }
.panel-heading-info strong { color: var(--ink); font-weight: 700; }

/* Title + subtitle inside a panel. */
.panel-title { margin: 0 0 12px; font-size: var(--fs-base); font-weight: 800; letter-spacing: -.4px; }
.panel-subtitle { margin: 18px 0 10px; font-size: var(--fs-md); font-weight: 700; color: var(--ink); }

/* Disabled button — visual marker when a pagination link is the
   boundary page. */
.button.is-disabled { opacity: 0.4; pointer-events: none; cursor: not-allowed; }

/* Inline link styled as a button (used for the "Clear" link in the
   filter bar). */
.button-link { background: transparent; color: var(--muted); padding: 6px 8px; font-size: var(--fs-md); font-weight: 600; }
.button-link:hover { color: var(--lavender); background: transparent; box-shadow: none; }

/* Two-column detail grid for the agent profile page. Each column is
   a .panel so the layout balances on wide screens and stacks on
   narrow ones. */
.detail-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-bottom: 14px; }
.detail-grid .panel { margin-bottom: 0; }
@media (max-width: 900px) { .detail-grid { grid-template-columns: 1fr; } }

/* Detail list (dl/dt/dd) with a vertical rhythm. */
.detail-list { display: grid; grid-template-columns: 140px 1fr; gap: 6px 18px; margin: 0 0 14px; }
.detail-list dt { color: var(--faint); font-family: "DM Mono", monospace; font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: .6px; padding-top: 3px; }
.detail-list dd { margin: 0; color: #3f3f46; font-size: var(--fs-md); font-weight: 600; line-height: 1.55; word-break: break-word; }

/* Restore the 3-col grid for the condo detail page ("At a glance" panel).
   The generic .detail-list above uses 2-col 140px 1fr, which is correct
   for owner/tenant profile pages but wrong for the condo detail page.
   To get 3-col back on /condos/:id without breaking the profile pages,
   scope the overrides here to the parent .condo-detail-info. Only
   applies at wide viewports — narrow viewports keep the existing
   1-col layout from the @media (max-width: 980px) block. */
/* .detail-list-blocks: for lists whose markup wraps each pair in a
   <div> (delete-property summary, feedback diagnostics). The bare
   .detail-list is 140px/1fr — right for a FLAT dt/dd list, but with
   <div> wrappers it drops every other pair into the 140px column and
   crams the value. This flows the pairs as equal blocks instead. */
.detail-list.detail-list-blocks { grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 14px 22px; }
.detail-list.detail-list-blocks > div { border: 0; padding: 0; min-height: 0; }

@media (min-width: 981px) {
  .condo-detail-info .detail-list { display: grid; grid-template-columns: 1fr 1fr 1fr; margin: 0; gap: 0 24px; }
  .condo-detail-info .detail-list > div { min-height: 46px; padding: 7px 0; border-top: 1px solid #eeeef1; border-left: 0; }
  .condo-detail-info .detail-list > div:nth-child(3n+1) { padding-right: 0; }
  .condo-detail-info .detail-list > div:nth-child(3n+2) { padding-right: 0; padding-left: 24px; border-left: 1px solid #eeeef1; }
  .condo-detail-info .detail-list > div:nth-child(3n+3) { padding-left: 24px; border-left: 1px solid #eeeef1; }
  .condo-detail-info .detail-list dt { margin-bottom: 7px; padding-top: 0; }
  .condo-detail-info .detail-list dd { font-size: var(--fs-sm); font-weight: 700; line-height: 1.6; }
}

/* The `@media (max-width: 980px) .detail-list { grid-template-columns:
   1fr }` rule earlier in the file is dead — the plain `140px 1fr`
   redefinition above it (source order) wins even on a phone, so a
   value like a long email wrapped inside ~170px. Re-assert the single
   column below 640px, after that redefinition so it actually applies. */
@media (max-width: 640px) {
  .detail-list { grid-template-columns: 1fr; gap: 12px 0; }
  .detail-list > div { border-left: 0; padding-left: 0; padding-right: 0; }
}

/* Pending pill — used in the password-pending state on the detail
   page. Distinct from .status-badge so it can sit inside a dd without
   dominating the line. */
.pill-pending { background: #fff5d6; color: #8a5c00; border: 1px solid #f0d28a; }

/* Pagination bar with prev/next + page-info in the middle. Used on
   the agent user list because the dataset is bounded and a single
   pair of arrows is enough. */
.pagination-bar { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 16px; }
.pagination-bar .button { font-size: var(--fs-md); padding: 7px 12px; }
.pagination-bar .pagination-info { color: var(--muted); font-size: var(--fs-md); }

/* Error / success flashes used by the detail page when a password
   reset succeeds or fails. The CSS class names already exist on the
   company-admin pages; we just declare them here so the agent
   console can use them too without an additional <style> block. */
.flash { padding: 10px 14px; border-radius: 8px; margin-bottom: 16px; font-size: var(--fs-md); font-weight: 600; }
.flash-success { background: #e7f5ec; color: #1d6a3a; border: 1px solid #b8e0c5; }
.flash-error { background: #fdecef; color: #9a1f3a; border: 1px solid #f0c2cb; }

/* ---------------------------------------------------------------------------
   Subscriber smart analytics (/admin/smart-analytics)
   --------------------------------------------------------------------------- */
/* 5-tile hero strip. Wider than the legacy .stats-strip so the
   5 KPIs (active / MRR / ARR / new / churn) get enough horizontal
   room without truncating the ARPU footnote. The 4-tile secondary
   strip below uses the same width but the tiles are more compact. */
.stats-strip-subscriber { grid-template-columns: repeat(5, 1fr); }
.stats-strip-subscriber.stats-strip-secondary { grid-template-columns: repeat(4, 1fr); margin-top: -8px; }
@media (max-width: 1100px) {
  .stats-strip-subscriber { grid-template-columns: repeat(2, 1fr); }
  .stats-strip-subscriber.stats-strip-secondary { grid-template-columns: repeat(2, 1fr); }
}

.subscriber-tile { display: flex; flex-direction: column; gap: 4px; }
.subscriber-tile .subscriber-tile-label { color: var(--faint); font-family: "DM Mono", monospace; font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: .6px; }
.subscriber-tile .subscriber-tile-value { font-size: var(--fs-num-lg); font-weight: 800; letter-spacing: -1.2px; line-height: 1.05; color: var(--ink); }
.subscriber-tile .subscriber-tile-value-small { font-size: var(--fs-num); }
.subscriber-tile .subscriber-tile-note { font-size: var(--fs-xs); color: var(--muted); }
.subscriber-tile .subscriber-tile-note.delta-up { color: #1d6a3a; font-weight: 700; }
.subscriber-tile .subscriber-tile-note.delta-down { color: #9a1f3a; font-weight: 700; }

.subscriber-tile-active { border-left: 3px solid #3a8e5f; }
.subscriber-tile-active .subscriber-tile-value { color: #3a8e5f; }
.subscriber-tile-mrr { border-left: 3px solid #4a5cbf; }
.subscriber-tile-mrr .subscriber-tile-value { color: #4a5cbf; }
.subscriber-tile-arr { border-left: 3px solid #5fa6c4; }
.subscriber-tile-arr .subscriber-tile-value { color: #2c7896; }
.subscriber-tile-new { border-left: 3px solid #d4a73a; }
.subscriber-tile-new .subscriber-tile-value { color: #b58723; }
.subscriber-tile-churn { border-left: 3px solid #c45f7a; }
.subscriber-tile-churn .subscriber-tile-value { color: #9a1f3a; }
.subscriber-tile-compact { border-left: 3px solid var(--faint); }

/* Two-column grid for the Plan-mix + Top-customers pair. Stacks on
   narrow screens. */
.smart-analytics-grid-two { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.smart-analytics-grid-two .panel { margin-bottom: 0; }
@media (max-width: 1000px) { .smart-analytics-grid-two { grid-template-columns: 1fr; } }

/* MRR chart. Renders an inline SVG, no JS chart library needed. */
.chart-wrap { width: 100%; overflow: hidden; }
.mrr-chart { display: block; width: 100%; height: auto; max-width: 100%; }

/* Donut + legend side-by-side inside a panel. */
.donut-wrap { display: flex; align-items: center; gap: 24px; flex-wrap: wrap; }
.donut-wrap .donut { width: 160px; height: 160px; flex: 0 0 160px; }
.donut-legend { list-style: none; margin: 0; padding: 0; flex: 1 1 200px; display: flex; flex-direction: column; gap: 6px; }
.donut-legend li { display: grid; grid-template-columns: 12px 1fr auto; align-items: center; gap: 8px; font-size: var(--fs-sm); }
.donut-legend-swatch { width: 12px; height: 12px; border-radius: 3px; display: block; }
.donut-legend-label { color: var(--ink); font-weight: 600; }
.donut-legend-value { color: var(--muted); font-size: var(--fs-xs); }

/* Top 10 customers — ranked list with bar + MRR. */
.top-customer-list { display: flex; flex-direction: column; gap: 6px; }
.top-customer-row { display: grid; grid-template-columns: 24px 1fr 90px auto; align-items: center; gap: 10px; padding: 6px 0; border-top: 1px solid #eeeef1; }
.top-customer-row:first-child { border-top: 0; }
.top-customer-rank { font-size: var(--fs-sm); color: var(--faint); font-weight: 700; text-align: center; }
.top-customer-name { display: flex; flex-direction: column; }
.top-customer-name strong { font-size: var(--fs-md); color: var(--ink); }
.top-customer-name .muted { font-size: var(--fs-xs); }
.top-customer-bar { background: #f3f3f7; border-radius: 4px; height: 6px; overflow: hidden; }
.top-customer-bar-fill { background: linear-gradient(90deg, #4a5cbf, #6c7fd9); height: 100%; border-radius: 4px; }
.top-customer-mrr { text-align: right; font-size: var(--fs-sm); }
.top-customer-mrr .muted { color: var(--muted); font-size: var(--fs-2xs); margin-left: 2px; }
@media (max-width: 600px) {
  .top-customer-row { grid-template-columns: 24px 1fr auto; }
  .top-customer-bar { display: none; }
}

/* Renewal pipeline buckets. Three side-by-side cards colour-coded
   by urgency. Stacks on narrow screens. */
.renewal-buckets { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
.renewal-bucket { padding: 14px 16px; border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--surface); display: flex; flex-direction: column; gap: 4px; }
.renewal-bucket-label { color: var(--faint); font-family: "DM Mono", monospace; font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: .6px; }
.renewal-bucket-count { font-size: var(--fs-num); font-weight: 800; letter-spacing: -1px; color: var(--ink); }
.renewal-bucket-arr { font-size: var(--fs-sm); color: var(--muted); }
.renewal-bucket-urgent { border-left: 3px solid #c45f7a; }
.renewal-bucket-urgent .renewal-bucket-count { color: #9a1f3a; }
.renewal-bucket-soon { border-left: 3px solid #d4a73a; }
.renewal-bucket-soon .renewal-bucket-count { color: #b58723; }
.renewal-bucket-later { border-left: 3px solid #5fa6c4; }
.renewal-bucket-later .renewal-bucket-count { color: #2c7896; }
@media (max-width: 800px) { .renewal-buckets { grid-template-columns: 1fr; } }

/* Acquisition cohort heatmap. 12 cells in a 6x2 grid, each coloured
   by intensity (0 = empty, 1-4 = increasing). */
.cohort-heatmap { display: grid; grid-template-columns: repeat(6, 1fr); gap: 6px; }
.cohort-cell { aspect-ratio: 1.6; border-radius: 6px; padding: 8px; display: flex; flex-direction: column; justify-content: space-between; font-size: var(--fs-xs); line-height: 1.2; }
.cohort-cell-count { font-size: var(--fs-num-sm); font-weight: 800; letter-spacing: -.6px; color: var(--ink); }
.cohort-cell-month { font-size: var(--fs-2xs); color: var(--muted); font-weight: 600; }
.cohort-cell-empty { background: #f6f6f9; color: var(--faint); }
.cohort-cell-empty .cohort-cell-count { color: var(--faint); }
.cohort-cell-1 { background: #e7eaf7; }
.cohort-cell-2 { background: #c5cce8; }
.cohort-cell-3 { background: #9eacd6; color: #fff; }
.cohort-cell-3 .cohort-cell-count, .cohort-cell-3 .cohort-cell-month { color: #fff; }
.cohort-cell-4 { background: #4a5cbf; color: #fff; }
.cohort-cell-4 .cohort-cell-count, .cohort-cell-4 .cohort-cell-month { color: #fff; }
@media (max-width: 700px) { .cohort-heatmap { grid-template-columns: repeat(3, 1fr); } }

/* ---------------------------------------------------------------------------
   Smart analytics (/admin/smart-analytics) — rental panel
   --------------------------------------------------------------------------- */
/* The rental section uses the existing .smart-analytics-grid + cards
   but lives in a borderless .smart-analytics-panel-rental so there's
   no horizontal line at the top of the page from a card border. The
   grid + card look is preserved by reusing the .smart-analytics-card
   classes; this just removes the wrapper border + shadow. */
.smart-analytics-panel-rental { border: 0; padding: 0; background: transparent; box-shadow: none; }
.smart-analytics-panel-rental .panel-heading { margin-bottom: 18px; }

/* ---------------------------------------------------------------------------
   Global overflow safety net
   --------------------------------------------------------------------------- */
/* Three common overflow causes get a global fix here so individual
   components don't have to remember them:
   1. Long unbroken text in table cells (emails, hashes, URLs) —
      word-break + overflow-wrap lets the cell wrap instead of
      blowing out the column width.
   2. Images without explicit max-width — we cap them at the
      container width so logos / icons never spill over.
   3. The body itself — overflow-x: hidden on the html element is
      a last-resort safety net. The sidebar is position: fixed at
      width 252px, the main content is `width: calc(100% - 252px)`
      so they're accounted for in the layout flow. Hidden
      overflow on the root catches the rare case where an
      absolutely-positioned element (toast, dropdown) pokes out
      the right edge. Real overflow problems should be fixed at
      the source — this is just a band-aid. */
html, body { overflow-x: hidden; }
th, td { word-break: break-word; overflow-wrap: anywhere; }
/* Fix (2026-09-04): the rule above is for body-cell content (emails,
   hashes, URLs, per the comment above) but was also hitting <th>
   headers -- short, deliberate one/two-word labels. On a wide table
   (system-log's 10 columns), table-layout: auto + width: 100% will
   compress every column to fit, and overflow-wrap: anywhere let it
   compress a header like "Status" or "Duration" all the way down to
   one letter per line rather than letting the table (inside its own
   .table-wrap, already overflow-x: auto) grow past 100% and scroll
   instead -- which is what should happen. Headers keep one line;
   scrolling handles the rest. */
th { white-space: nowrap; }
img { max-width: 100%; height: auto; }

/* v41: owner / tenant identity pills. The pill renders the
   O-NNNN / T-NNNN code in a small, monospace, muted badge so it
   reads as a stable identifier rather than a value the user
   edits. Used on the /owners and /tenants list rows and on the
   profile page header next to the entity's name. */
.code-pill {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--accent-soft, #eef2ff);
  color: var(--accent, #3730a3);
  font-family: var(--mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: var(--fs-sm);
  font-weight: 600;
  letter-spacing: 0.02em;
  vertical-align: middle;
}
.code-pill-small { font-size: var(--fs-xs); padding: 1px 6px; margin-left: 6px; }
.owner-name-link, .tenant-name-link { color: inherit; text-decoration: none; }
.owner-name-link:hover, .tenant-name-link:hover { text-decoration: underline; }

/* v41: profile page. Two-column hero (contact + portfolio) sits
   above a full-width table of properties so the user sees the
   headline numbers and the underlying rows on the same screen
   without scrolling. */
/* R11: this block was authored against a token vocabulary the rest of
   app.css never adopted — `--card`, `--card-border`, `--text` are not
   defined anywhere, so every rule here was silently using its hardcoded
   fallback, and the radii (12/8px) were off the --radius scale. Realigned
   to the real tokens so the profile pages match every other panel. */
.profile-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-4);
  margin: var(--space-4) 0 var(--space-5);
}
@media (max-width: 768px) { .profile-grid { grid-template-columns: 1fr; } }
.profile-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--space-4) var(--space-5);
}
.profile-card-title { margin: 0 0 var(--space-3); font-size: var(--fs-md); text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); }
.profile-contact-row { display: flex; gap: var(--space-2); align-items: baseline; padding: var(--space-1) 0; font-size: var(--fs-base); }
.profile-contact-row .contact-channel { min-width: 80px; color: var(--muted); font-size: var(--fs-md); }
.profile-stat-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); gap: var(--space-3); margin-bottom: var(--space-3); }
.profile-stat { display: flex; flex-direction: column; padding: var(--space-2); background: var(--canvas); border-radius: var(--radius-sm); }
.profile-stat-value { font-size: var(--fs-num-sm); font-weight: 600; color: var(--ink); }
.profile-stat-label { font-size: var(--fs-sm); color: var(--muted); text-transform: uppercase; letter-spacing: 0.04em; }
.profile-stat-total { display: flex; justify-content: space-between; align-items: baseline; padding-top: var(--space-2); border-top: 1px solid var(--line); }
.profile-stat-total-label { color: var(--muted); font-size: var(--fs-md); }
.profile-stat-total-value { font-size: var(--fs-num-sm); font-weight: 600; }
.profile-empty { color: var(--muted); font-size: var(--fs-md); margin: 0; }
.profile-subtitle { margin: var(--space-2) 0 0; display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.profile-subtitle-meta { color: var(--muted); font-size: var(--fs-md); }
.profile-table-address { display: block; }
.profile-table-address-sub { display: block; color: var(--muted); font-size: var(--fs-sm); }
.profile-table-lease { display: block; font-size: var(--fs-md); }
.profile-table-lease-sub { display: block; color: var(--muted); font-size: var(--fs-sm); }
/* R11: the profile properties table now reuses the app-wide hyphenated
   `.status-*` colour classes (see the `.status-badge` block above), so the
   four underscored one-offs with hardcoded hex — including a second,
   conflicting `.status-rented` — are gone. Only the pill shape stays. */
.status-pill { display: inline-block; padding: 2px 8px; border-radius: 999px; font-size: var(--fs-sm); font-weight: 600; text-transform: capitalize; }
.status-under_maintenance { background: #fee2e2; color: #991b1b; }
.status-sold { background: #f3f4f6; color: #374151; }

/* Tenant-profile "Where the paperwork lives" summary. These
   .document-summary* classes shipped with no CSS at all: the three
   cards stacked full-width and their value/label/description spans ran
   together inline at one size. Give the grid three content-sized
   columns (one on mobile) and each card the icon + value + label +
   note hierarchy the markup already implies. (2026-09-06) */
.document-summary-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(262px, 1fr)); gap: var(--space-3); margin-bottom: var(--space-3); }
.document-summary-grid > .panel { margin-bottom: 0; }
.document-summary { display: grid; grid-template-columns: auto 1fr; gap: 8px 12px; align-items: start; }
.document-summary-icon { display: flex; align-items: center; justify-content: center; width: 34px; height: 34px; border-radius: var(--radius-sm); background: var(--canvas); color: var(--muted); }
.document-summary-body { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.document-summary-value { font-size: var(--fs-num-sm); font-weight: 600; color: var(--ink); font-variant-numeric: tabular-nums; }
.document-summary-label { font-size: var(--fs-sm); color: var(--muted); text-transform: uppercase; letter-spacing: 0.04em; }
.document-summary-body .muted { font-size: var(--fs-sm); line-height: 1.45; }

/* v41: multi-step form wizard. The stepper is a sticky bar at the
   top of the form that shows the user where they are in the flow.
   Each .form-step block represents one step; only the step whose
   data-step matches the URL ?step=N is visible. */
.wizard-stepper {
  position: sticky;
  top: 0;
  z-index: 5;
  display: flex;
  gap: 4px;
  padding: 12px 16px;
  margin: 0 0 24px;
  background: var(--card, #fff);
  border: 1px solid var(--card-border, #e5e7eb);
  border-radius: 10px;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
}
.wizard-step {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  padding: 8px 10px;
  border-radius: 8px;
  background: transparent;
  border: 1px solid transparent;
  cursor: pointer;
  font: inherit;
  text-align: left;
  color: var(--muted);
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.wizard-step:hover { background: var(--canvas, #f9fafb); }
.wizard-step[data-active="true"] {
  background: var(--accent-soft, #eef2ff);
  border-color: var(--accent, #3730a3);
  color: var(--accent, #3730a3);
}
.wizard-step[data-complete="true"] { color: var(--text); }
.wizard-step-number {
  display: inline-block;
  width: 22px;
  height: 22px;
  line-height: 22px;
  text-align: center;
  border-radius: 999px;
  background: var(--canvas, #f3f4f6);
  color: var(--muted);
  font-size: var(--fs-md);
  font-weight: 600;
  margin-bottom: 4px;
}
.wizard-step[data-active="true"] .wizard-step-number { background: var(--accent, #3730a3); color: #fff; }
.wizard-step[data-complete="true"] .wizard-step-number { background: #16a34a; color: #fff; }
.wizard-step-label { font-size: var(--fs-md); font-weight: 500; }
.wizard-step-hint { font-size: var(--fs-sm); color: var(--muted); margin-top: 2px; }
.wizard-step[data-active="true"] .wizard-step-hint { color: var(--accent, #3730a3); }

.wizard-step-panel { display: none; }
.wizard-step-panel[data-active="true"] { display: block; }
.wizard-actions {
  position: sticky;
  bottom: 0;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  padding: 14px 16px;
  margin-top: 24px;
  background: var(--card, #fff);
  border: 1px solid var(--card-border, #e5e7eb);
  border-radius: 10px;
  box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.04);
}
.wizard-actions .wizard-step-indicator { color: var(--muted); font-size: var(--fs-md); }
.wizard-actions-right { display: flex; gap: 8px; }

/* When the form is in wizard mode, each step-panel becomes a
   focused single-page view. Override the form-section spacing so
   the panel feels like one cohesive step rather than a part of a
   long scrollable form. */
.wizard-step-panel .form-section { padding: 14px 0; }
.wizard-step-panel .form-section:first-of-type { padding-top: 0; }
.wizard-step-panel .form-section:last-of-type { padding-bottom: 0; }

/* Mobile: the stepper is a flex row of labelled steps. With 6 steps
   (owner quick-add) the labels don't wrap, so its min-content is
   ~480px — wider than a phone. Collapse to a compact numbered row;
   the panel's own "STEP N —" heading and the "Step N of 6" indicator
   carry the context. Also let the action bar wrap so Next never
   clips off the right edge. */
@media (max-width: 720px) {
  .wizard-stepper { gap: 6px; padding: 10px 12px; overflow-x: auto; }
  .wizard-step { flex: 1 1 0; min-width: 0; align-items: center; padding: 6px 4px; }
  .wizard-step-number { margin-bottom: 0; }
  .wizard-step-label, .wizard-step-hint { display: none; }
  .wizard-actions { flex-wrap: wrap; gap: 8px 10px; padding: 12px; }
  .wizard-actions .wizard-step-indicator { order: 3; flex-basis: 100%; text-align: center; }
}

/* v41: dashboard layout. The original stacked 6 stat cards, 4 smart
   analytics cards, and 4 sub-panels in a 2-col grid took 3+
   viewports to scan. This compresses the same data into a single
   strip + 4-col overview row, with the recent properties below.
   Desktop users see the full dashboard in 1.5 viewports. */
.dashboard-strip { display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, 1.4fr); gap: 14px; margin-bottom: 14px; }
.dashboard-stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; padding: 14px; border: 1px solid var(--line, #e5e7eb); border-radius: 12px; background: var(--surface, #fff); }
.dashboard-stats .stat-card { padding: 0; min-height: 0; border: 0; background: transparent; box-shadow: none; display: flex; flex-direction: column; gap: 2px; }
.dashboard-stats .stat-label { font-size: var(--fs-xs); }
.dashboard-stats .stat-card strong { font-size: var(--fs-num-sm); }
.dashboard-stats .stat-card-divider { display: none; }
.dashboard-analytics { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; padding: 14px; border: 1px solid var(--line, #e5e7eb); border-radius: 12px; background: var(--surface, #fff); align-content: center; }
.dashboard-analytics .smart-analytics-card { padding: 0; background: transparent; border: 0; box-shadow: none; }
.dashboard-analytics .smart-analytics-label { font-size: var(--fs-xs); }
.dashboard-analytics .smart-analytics-value { font-size: var(--fs-lg); }
.dashboard-overview { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; margin-bottom: 14px; }
.overview-card { display: flex; flex-direction: column; border: 1px solid var(--line, #e5e7eb); border-radius: 12px; background: var(--surface, #fff); min-width: 0; }
.overview-card-heading { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 12px 14px 8px; border-bottom: 1px solid var(--line, #e5e7eb); }
.overview-card-heading .eyebrow { margin: 0; font-size: var(--fs-xs); }
.overview-card-heading h3 { margin: 2px 0 0; font-size: var(--fs-md); letter-spacing: -.2px; }
.overview-card-heading .summary-table-pill { font-size: var(--fs-xs); padding: 3px 8px; }
.overview-card .table-wrap { padding: 0; }
.overview-card .summary-table { width: 100%; }
.overview-card .summary-table th, .overview-card .summary-table td { padding: 6px 10px; font-size: var(--fs-sm); }
.overview-card .summary-table thead th { font-size: var(--fs-xs); }
.overview-list { list-style: none; margin: 0; padding: 4px 0; }
.overview-list li { display: grid; grid-template-columns: 1fr auto auto; gap: 10px; align-items: baseline; padding: 7px 14px; font-size: var(--fs-md); border-bottom: 1px solid var(--canvas, #f9fafb); }
.overview-list li:last-child { border-bottom: 0; }
.overview-list .num { font-variant-numeric: tabular-nums; color: var(--muted); }
.overview-list .num strong { color: var(--ink); }
.overview-list strong.is-positive { color: #166534; }
.overview-list strong.is-negative { color: #991b1b; }
.overview-card-footer { display: block; padding: 8px 14px 12px; font-size: var(--fs-md); }
.dashboard-recent { padding: 22px; }
.dashboard-recent .condo-grid { grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); }
.granularity-tab { padding: 4px 8px; font-size: var(--fs-xs); min-width: 24px; text-align: center; }
@media (max-width: 1180px) {
  .dashboard-overview { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 980px) {
  .dashboard-strip { grid-template-columns: 1fr; }
  .dashboard-analytics { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 640px) {
  .dashboard-overview { grid-template-columns: 1fr; }
  .dashboard-analytics { grid-template-columns: 1fr; }
  .dashboard-stats { grid-template-columns: 1fr 1fr; }
}

/* v41: full-width form layout. Drops the photos sidebar so the
   form uses the entire main column — denser grids, less scrolling,
   and no empty left gutter. Photos are now a wizard step
   (step 4) so they share the same form chrome. */
.condo-form-full {
  max-width: 1080px;
  margin: 0 auto;
  padding: 26px;
  border: 1px solid var(--line, #e5e7eb);
  border-radius: 14px;
  background: var(--surface, #fff);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
}
.condo-form-full .form-grid-wide { grid-template-columns: repeat(3, 1fr); gap: 14px 16px; }
.condo-form-full .form-section-label { margin-bottom: 4px; }
.condo-form-full .form-section { gap: 10px; }
.condo-form-full .field { gap: 5px; }
.condo-form-full .field label { font-size: var(--fs-xs); letter-spacing: 0.02em; }
.condo-form-full .field-hint { font-size: var(--fs-xs); margin: 0; }
.condo-form-full .amenity-grid-tight { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 6px 8px; }
.condo-form-full .amenity-option { font-size: var(--fs-sm); padding: 4px 6px; }
.condo-form-full .tenant-doc-block { padding: 12px; gap: 8px; }
.condo-form-full .tenant-doc-block h3 { font-size: var(--fs-md); margin: 2px 0; }
.condo-form-full .tenant-doc-block p { font-size: var(--fs-sm); }
.condo-form-full .condo-photo-dropzone { min-height: 140px; padding: 18px; }
@media (max-width: 980px) {
  .condo-form-full .form-grid-wide { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 640px) {
  .condo-form-full .form-grid-wide { grid-template-columns: 1fr; }
}

/* Language picker. Compact two-button strip that fits in the
   auth top bar, the public top bar, the sidebar account menu, and
   the mobile menu drawer. The active option gets a checkmark and
   a stronger background so the user can see which is in effect
   without having to click. */
.lang-picker {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: var(--fs-md);
  color: var(--muted);
}
.lang-picker ul {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.lang-picker-option {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 10px;
  border-radius: 999px;
  background: transparent;
  color: var(--ink);
  font-weight: 500;
  text-decoration: none;
  border: 1px solid transparent;
  white-space: nowrap;
  transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.lang-picker-option:hover { background: var(--canvas); }
.lang-picker-option.is-active {
  background: var(--canvas);
  border-color: var(--line);
  color: var(--ink);
}
.lang-picker-mark {
  font-size: var(--fs-xs);
  color: var(--accent);
  font-weight: 700;
}
.lang-picker-label {
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-weight: 600;
  font-size: var(--fs-xs);
}

/* Top bars (public + auth) hold the logo + the language picker
   side by side. The sidebar's account menu already lays its
   children out vertically, so the language picker just appears
   inline. */
.auth-topbar,
.public-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 28px;
}
.auth-topbar > .brand,
.public-topbar > .brand {
  margin-bottom: 0;
}
.mobile-menu-locale {
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--line);
}
.account-menu > .lang-picker {
  padding: 0 5px 4px;
  border-bottom: 1px solid var(--line);
  padding-bottom: 12px;
  margin-bottom: 4px;
  width: 100%;
}
.account-menu > .lang-picker ul {
  display: flex;
  width: 100%;
}
.account-menu > .lang-picker li { flex: 1; }
.account-menu > .lang-picker .lang-picker-option {
  justify-content: center;
  width: 100%;
}

/* Trash list. One card per trashed item, with the kind badge on
   the left, the body in the middle, and the actions on the
   right. The "1 day until permanent deletion" pill turns red
   when the user is about to lose the item. */
.trash-list {
  list-style: none;
  margin: 24px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.trash-item {
  display: grid;
  grid-template-columns: 110px 1fr auto;
  align-items: center;
  gap: 18px;
  padding: 18px 20px;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--surface);
}
.trash-item-body { min-width: 0; }
/* Fix (2026-09-04): same root cause as .person-cell (see there) --
   a value with no spaces forces the grid track wider instead of
   wrapping, since a plain grid item's min-width is auto by default.
   min-width: 0 above lets the track shrink to the grid's 1fr share;
   line-clamp caps the title/subtitle at one line with an ellipsis no
   matter how long the underlying string is. */
.trash-item-title {
  font-size: var(--fs-lg);
  font-weight: 600;
  margin: 0 0 4px;
  color: var(--ink);
  display: -webkit-box;
  -webkit-line-clamp: 1;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.trash-item-subtitle {
  font-size: var(--fs-md);
  margin: 0 0 4px;
  display: -webkit-box;
  -webkit-line-clamp: 1;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.trash-item-meta {
  font-size: var(--fs-md);
  margin: 0;
}
.trash-item-actions {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 8px;
}
.trash-item-actions form { margin: 0; }
.trash-item-actions button {
  font-size: var(--fs-md);
  padding: 6px 14px;
}
.trash-urgent {
  color: #b91c1c;
  font-weight: 600;
  font-size: var(--fs-md);
}
.trash-kind-pill {
  display: inline-flex;
  align-items: center;
  padding: 4px 12px;
  border-radius: 999px;
  font-size: var(--fs-sm);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  background: var(--canvas);
  color: var(--muted);
  border: 1px solid var(--line);
}
.trash-kind-pill.trash-kind-condo { color: #4338ca; border-color: #c7d2fe; background: #eef2ff; }
.trash-kind-pill.trash-kind-user { color: #047857; border-color: #a7f3d0; background: #ecfdf5; }
.trash-kind-pill.trash-kind-company { color: #b45309; border-color: #fde68a; background: #fffbeb; }

@media (max-width: 720px) {
  .trash-item {
    grid-template-columns: 1fr;
    gap: 10px;
  }
  .trash-item-actions {
    flex-direction: row;
    align-items: center;
    flex-wrap: wrap;
    justify-content: flex-start;
  }
}

/* Trash warning banner — shown on the overview / dashboard
   page when there's at least one item in the trash. Yellow by
   default, red when the item is about to be purged. */
.trash-warning {
  margin: 18px 0 0;
  padding: 14px 18px;
  border-radius: 12px;
  background: #fffbeb;
  border: 1px solid #fde68a;
  color: #92400e;
  font-size: var(--fs-md);
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}
.trash-warning.trash-warning-urgent {
  background: #fef2f2;
  border-color: #fecaca;
  color: #991b1b;
}
.trash-warning-icon {
  font-size: 18px;
  flex-shrink: 0;
}
.trash-warning-body { flex: 1; min-width: 0; }
.trash-warning-title { font-weight: 700; margin: 0 0 2px; }
.trash-warning-text { margin: 0; }
.trash-warning-action { flex-shrink: 0; }

@media (max-width: 640px) {
  .trash-warning { flex-direction: column; align-items: flex-start; }
  .trash-warning-action { width: 100%; }
}


/* ============================================================
   V47 (UI/UX Tier 1 + Tier 2) — mainline styles
   ============================================================
   Covers:
   - Tier 1.1 dashboard greeting + heading layout
   - Tier 1.2 dashboard date-range form
   - Tier 1.3 $ KPI stat cards (money / warn / accent)
   - Tier 1.4 wizard gradient + progress bar support
   - Tier 2 Tasks feature
   - Tier 2 Leases feature
   - Tier 2 Reports feature
   - Tier 2 Messages feature
   - Tier 2 Settings feature
   - Tier 2 Feedback (pills + audit log)
   ============================================================ */

/* V47 Tier 1.1: dashboard greeting header
   Sits inside the .page-heading wrapper, but uses flex-wrap so
   the date-range form drops below the heading on narrow screens. */
.dashboard-greeting { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--space-5); flex-wrap: wrap; }
.dashboard-greeting > div { flex: 1 1 320px; min-width: 0; }
/* On mobile .page-heading switches to a column; `flex: 1 1 320px` then
   means a 320px flex-basis on the *vertical* axis, forcing a big empty
   gap under the greeting. Neutralise it. */
@media (max-width: 720px) {
  .dashboard-greeting { flex-direction: column; align-items: stretch; gap: var(--space-3); }
  .dashboard-greeting > div { flex: 0 0 auto; }
}

/* V47 Tier 1.2: dashboard date-range form
   Two date inputs + Apply button + human label, all in a single
   compact row. Sits under the dashboard greeting. */
/* The date range only re-scopes the collection figures — it's a
   secondary control, so it sits behind a "04 Aug – 03 Sep ▾" summary
   (collapsed by default) rather than taking a whole row on every load. */
.dashboard-date-range-wrap { margin-top: var(--space-2); }
.dashboard-date-range-wrap > summary { display: inline-flex; align-items: center; gap: 6px; list-style: none; padding: 5px 10px; border: 1px solid var(--line); border-radius: 999px; background: var(--canvas); cursor: pointer; }
.dashboard-date-range-wrap > summary::-webkit-details-marker { display: none; }
.dashboard-date-range-wrap[open] > summary .filter-collapse-chevron { transform: rotate(180deg); }
.dashboard-date-range-wrap[open] > summary { margin-bottom: var(--space-2); }
/* One tidy row: [ from ] -> [ to ] [ Apply ]. The date inputs used to
   inherit the global `input { width:100% }` and stack into 3-4 rows. */
.dashboard-date-range { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); padding: var(--space-2) var(--space-3); border: 1px solid var(--line); border-radius: 10px; background: var(--canvas); }
.dashboard-date-range input[type="date"] { width: auto; flex: 1 1 132px; min-width: 0; max-width: 190px; height: 34px; padding: 4px 8px; border: 1px solid var(--line); border-radius: 6px; font-family: "DM Mono", monospace; font-size: var(--fs-md); background: var(--surface); }
.dashboard-date-range .button { flex: 0 0 auto; }
.dashboard-date-range-sep { flex: 0 0 auto; color: var(--muted); font-size: var(--fs-base); }
.dashboard-date-range-label { color: var(--muted); font-size: var(--fs-sm); font-weight: 700; font-family: "DM Mono", monospace; letter-spacing: .03em; }
@media (max-width: 460px) { .dashboard-date-range-sep { display: none; } .dashboard-date-range input[type="date"] { max-width: none; } }

/* V47 Tier 1.3: 6-up stat-card grid (replaces the old 4-up).
   Three breakpoints: 6-up on wide, 3-up on medium, 2-up on small. */
.stats-grid-6 { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 12px; margin-bottom: 12px; }
@media (max-width: 1180px) { .stats-grid-6 { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 720px)  { .stats-grid-6 { grid-template-columns: repeat(2, 1fr); } }

/* V47 Tier 1.3: $ KPI cards — money styling.
   .stat-card-money is the lavender-tinted "money" variant.
   .stat-card-warn is the red-tinted "overdue" variant.
   .stat-card-accent already existed in the codebase. */
.stat-card-money { background: linear-gradient(180deg, #fff, #fbfaff); border-color: #ece9ff; }
.stat-card-money strong { font-size: var(--fs-num); font-weight: 800; letter-spacing: -1.2px; }
.stat-card-warn   { background: linear-gradient(180deg, #fff5f5, #fff); border-color: #ffd9d9; }
.stat-card-warn strong { color: #c62828; }

/* V47 Tier 1.3: baht currency symbol */
.baht { color: var(--muted); font-weight: 600; margin-right: 2px; font-size: var(--fs-base); }

/* V47 Tier 1.4: compact stat-card variant for the dashboard.
   NOTE: scoped to .stat-card — the bare `.is-compact` selector was
   also matching `.page-container.is-compact` in the app layout and
   silently forcing every page's padding down to 10px 12px. */
.stat-card.is-compact { min-height: 88px; padding: 10px 12px; }
.stat-card.is-compact strong { font-size: var(--fs-num-sm); }

/* V47 Tier 2: Tasks feature */
.task-list { display: grid; gap: 8px; margin: 12px 0 0; padding: 0; list-style: none; }
.task-row { display: grid; grid-template-columns: 1fr auto; gap: 10px; align-items: center; padding: 10px 12px; border: 1px solid var(--line); border-radius: 8px; background: var(--surface); }
.task-row-title { font-weight: 600; }
.task-row-done .task-row-title { color: var(--muted); text-decoration: line-through; }
.task-row-actions { display: flex; gap: 6px; align-items: center; }
.task-row-due { display: inline-flex; align-items: center; gap: 4px; padding: 2px 8px; border-radius: 999px; font-size: var(--fs-sm); font-family: "DM Mono", monospace; }
.task-row-due-overdue { background: #fde8e8; color: #c62828; }
.task-row-due-today   { background: #fff3cd; color: #856404; }
.task-row-due-soon    { background: #dde7fa; color: #2545d0; }
.task-row-due-later   { background: #eee; color: #666; }
.task-row-due-none    { background: #f5f5f5; color: #888; }
.task-add-panel { margin-top: 16px; }

/* V47 Tier 2: Leases feature */
.lease-add-disclosure { display: inline-block; }
.lease-add-disclosure summary { list-style: none; cursor: pointer; }
.lease-add-disclosure summary::-webkit-details-marker { display: none; }
.lease-add-panel { margin-top: 16px; padding: 16px; border: 1px solid var(--line); border-radius: 10px; background: var(--canvas); }
/* The disclosure lives in .page-heading-actions (the flex item on the
   right of the header). While it is open the whole 7-field form has to
   fit there — at <=820px that column collapsed to ~200px and the form
   was unusable. When open, let the heading wrap and give the actions
   column (hence the disclosure + its panel) the full row. */
.page-heading:has(.lease-add-disclosure[open]) { flex-wrap: wrap; align-items: flex-start; }
.page-heading:has(.lease-add-disclosure[open]) .page-heading-actions { flex: 1 0 100%; }
.lease-add-disclosure[open] { display: block; }
.lease-status-tabs { display: flex; gap: 4px; margin-bottom: 12px; padding: 4px; border: 1px solid var(--line); border-radius: 8px; background: var(--canvas); flex-wrap: wrap; }
.lease-status-tab { padding: 6px 14px; border-radius: 6px; text-decoration: none; color: var(--muted); font-size: var(--fs-md); font-weight: 600; }
.lease-status-tab.is-active { background: var(--surface); color: var(--ink); box-shadow: 0 1px 2px rgba(0,0,0,0.04); }
/* contain: paint — same page-level-horizontal-scroll fix as
   .table-wrap above; this wrapper has the identical shape. */
.lease-table-wrap { overflow-x: auto; contain: paint; }
.lease-table { width: 100%; border-collapse: collapse; }
.lease-table th, .lease-table td { padding: 10px 8px; border-top: 1px solid #eeeef1; font-size: var(--fs-md); text-align: left; }
.lease-table th { color: var(--muted); font-weight: 600; font-size: var(--fs-sm); text-transform: uppercase; letter-spacing: .04em; }
.lease-condo-address { color: var(--muted); font-size: var(--fs-sm); }
.lease-row-actions { display: flex; gap: 6px; }

/* V47 Tier 2: Reports feature */
.reports-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 14px; }
.report-card { padding: 18px; display: flex; flex-direction: column; gap: 10px; }
.report-card-head { display: flex; align-items: center; gap: 10px; }
.report-card-icon { display: grid; width: 32px; height: 32px; flex: 0 0 32px; place-items: center; border-radius: 8px; background: var(--lavender-pale); color: var(--lavender); }
.report-card-icon svg { width: 18px; height: 18px; }
.report-card-title { margin: 0; font-size: var(--fs-lg); font-weight: 700; }
.report-card-summary { margin: 0; color: var(--muted); font-size: var(--fs-md); line-height: 1.5; }
.report-card-actions { display: flex; justify-content: flex-end; margin-top: auto; }

/* V47 Tier 2: Messages feature */
.message-thread-list { list-style: none; padding: 0; margin: 0; }
.message-thread { display: grid; grid-template-columns: 36px 1fr auto; gap: 12px; align-items: center; padding: 12px; border-top: 1px solid #eeeef1; }
.message-thread:first-child { border-top: 0; }
.message-thread-unread { background: color-mix(in srgb, var(--lavender) 4%, var(--surface)); }
.message-thread-avatar { display: grid; width: 36px; height: 36px; flex: 0 0 36px; place-items: center; border-radius: 50%; background: var(--lavender-pale); color: var(--lavender); font-weight: 700; }
.message-thread-body { min-width: 0; }
.message-thread-head { display: flex; align-items: center; gap: 8px; }
.message-thread-name { font-weight: 600; }
.message-thread-role { color: var(--faint); font-size: var(--fs-sm); }
.message-thread-preview { color: var(--muted); font-size: var(--fs-md); max-width: 360px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.message-thread-time { color: var(--faint); font-size: var(--fs-sm); }
.message-thread-badge { background: var(--lavender); color: #fff; border-radius: 999px; padding: 1px 7px; font-size: var(--fs-xs); font-weight: 700; }

/* V47 Tier 2: Settings feature */
/* auto-fit, not auto-fill: auto-fill reserves an empty track when there
   are fewer cards than columns; auto-fit collapses it so the real cards
   fill the row. */
.settings-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 14px; }
/* Inside a ~340px settings card the generic two-block layout squeezed
   the email address into ~155px and broke it mid-word. One block per
   row so each value gets the full card width. */
.settings-panel .detail-list { grid-template-columns: 1fr; gap: 12px 0; }
.settings-panel .detail-list > div { border: 0; padding: 0; min-height: 0; }
.settings-panel .detail-list dd { word-break: normal; overflow-wrap: anywhere; }
.settings-panel { padding: 16px; }

/* V47 Tier 2: Feedback (pills + audit log) */
.pill { display: inline-flex; align-items: center; gap: 4px; padding: 2px 8px; border-radius: 999px; font-size: var(--fs-sm); font-weight: 600; }
.pill-active { background: #e6f4ea; color: #1e7a39; }
.pill-success { background: #e6f4ea; color: #1e7a39; }
.pill-danger { background: #fde8e8; color: #c62828; }
.pill-disabled { background: #eee; color: #777; }
.pill-neutral { background: #f5f5f5; color: #555; }
.audit-action-code { font-family: "DM Mono", monospace; font-size: var(--fs-sm); padding: 1px 6px; border-radius: 4px; background: var(--canvas); border: 1px solid var(--line); }
.kpi-inline { display: inline-flex; align-items: baseline; gap: 4px; }
.count-pill { display: inline-flex; align-items: center; justify-content: center; min-width: 18px; height: 18px; padding: 0 5px; border-radius: 999px; background: var(--lavender-pale); color: var(--lavender); font-size: var(--fs-xs); font-weight: 700; }
.cell-meta { color: var(--faint); font-size: var(--fs-xs); }


/* ============================================================
   Print — an agent PDFs a property sheet for an owner, a lease
   summary, an owners/tenants list, or the rent-collection table.
   Strip the app chrome and the interactive controls, flatten to
   black-on-white, let the content use the whole page, and keep
   table rows from splitting across a page break. The rent receipt
   carries its own scoped @media print and is unaffected.
   ============================================================ */
@media print {
  :root { --shadow: none; }
  html, body { background: #fff !important; color: #000; }
  .app-shell { display: block; }

  /* App chrome + navigation */
  .sidebar, .mobile-header, .skip-link, .sidebar-pin-toggle,
  .flash, .install-app-button, [data-pwa-install] { display: none !important; }

  .main-content { margin-left: 0 !important; width: 100% !important; }
  .page-container, .page-container--wide { max-width: none !important; padding: 0 !important; }

  /* Actions and controls that mean nothing on paper */
  .page-heading-actions, .panel-heading-actions, .detail-heading-actions,
  .row-actions, .filter-collapse, .filter-bar, .filter-bar-tools,
  .view-switch, .column-toggle, .pagination, .dashboard-date-range-wrap,
  button, .button, .text-link[href*="/edit"], .condo-wizard-nav,
  .empty-state-actions { display: none !important; }

  /* Collapsed <details> should print fully */
  details, .dash-more, .filter-collapse { display: block !important; }
  details > summary { display: none !important; }

  /* Flatten surfaces */
  .panel, .stat-card, .profile-card, .condo-wizard-panel, .item, article.panel {
    border: 1px solid #bbb !important;
    box-shadow: none !important;
    background: #fff !important;
    break-inside: avoid;
  }
  a { color: #000; text-decoration: none; }
  a[href^="http"]::after { content: " (" attr(href) ")"; font-size: var(--fs-xs); color: #555; word-break: break-all; }

  /* Tables: show everything, don't split rows */
  .table-wrap { overflow: visible !important; background: none !important; }
  table { width: 100% !important; }
  thead { display: table-header-group; }
  tr, .stacked-card-table tr { break-inside: avoid; }
  td { white-space: normal !important; max-width: none !important; }

  .page-heading h1 { font-size: 20pt; }
  @page { margin: 16mm 14mm; }
}