:root {
  color-scheme: light;
  --bg: #f4f5f7;
  --card: #ffffff;
  --border: #e2e5ea;
  /* Hover border (2026-09-09) — a repeated literal (5 places: card/
     bcard/project-card hover, .cancel-btn hover, login's secondary
     button hover), a shade darker than --border for a subtle hover
     step. Needs its own dark-mode value going the other direction (a
     shade LIGHTER than dark mode's --border) rather than reusing this
     same literal, which would render as a near-invisible dark line on
     a dark card. */
  --border-hover: #c9cfd9;
  --ink: #1c2330;
  /* Fixed dark ink (2026-09-09) — a small set of things deliberately
     stay a solid dark fill + white text NO MATTER the theme (the login
     screen's brand panel, the toast bubble, the segmented-control/
     chip-pill "selected" fill, one of the logo mark's six tiles) —
     these reused --ink for that opportunistically back when --ink was
     always dark, which broke the moment --ink itself needed to flip
     light for dark mode (its real, far more common job: body text).
     Same literal value as --ink's own light-mode default, just never
     redefined below. */
  --ink-fixed: #1c2330;
  --muted: #6b7482;
  --accent: #0f6e56;
  /* Secondary neutral surface (2026-09-09) — the muted chip/hover/
     panel background used for things like nav-item hover, the you-tag/
     soon-tag pills, a WhatsApp message preview box, the dimmed Deleted
     card. Consolidates three near-identical literal grays (#f0f2f5/
     #f7f8fa/#f7f8f9) that were scattered through this file into one
     real token — same reason every other structural color is a token:
     so dark mode (below) can actually follow it. */
  --surface-2: #f0f2f5;
  /* Attention/destructive red (2026-09-09) — used via var(--review) in
     several places already (Needs Review tag text, Project Detail's
     Delete button, the error toast, Settings' Log out buttons) but was
     never actually defined here, a real pre-existing bug: those
     declarations were silently invalid and falling back to inherited/
     default color. Every OTHER place that used this exact red as a
     hardcoded literal (there were dozens — error text, delete buttons,
     the Needs Review toggle's active state, etc.) got converted to
     this same token in the same pass, so dark mode (below) only has to
     redefine it once. */
  --review: #b91c1c;
  /* Favourite gold + its hover shade (2026-09-09) — same fix as
     --review: was a literal repeated 6 times (the card corner star,
     the detail panel's favourite button, the header toggle), never a
     token, so dark mode couldn't reach it. */
  --favorite: #d4a017;
  --favorite-dark: #b8890a;
  /* Deleted slate + its badge tint (2026-09-09) — same fix again, for
     the Deleted toggle/badge's own color (distinct from --review's red
     — see that toggle's own original comment: not an urgent/flagged
     state, just a different place to be). */
  --deleted: #334155;
  --deleted-tint: #eef1f4;
  --rent: #b45309;
  --sale: #0f6e56;
  --requirement: #6d28d9;
  --category: #0369a1;
  /* Card tag tints (2026-09-07) — the property card's own top-row tags
     move from the solid-fill .badge pills (still used everywhere else:
     the detail panel header, project cards) to a light-tint-plus-
     solid-text pair, matching the convention the source badges already
     settled on below. Kept as separate tokens from --rent/--sale/etc
     rather than reusing them, since .badge's solid fill and .tag's
     tint fill are deliberately different treatments sharing the same
     hue, not the same color reused twice. */
  --rent-tint: #fdf1e0;
  --sale-tint: #e5f4ef;
  --requirement-tint: #f1e9fb;
  --category-tint: #e2f0fa;
  --review-tint: #fde8e8;
  /* Source badges (2026-09-04) — distinct from every badge color above
     so all three (plus type/recordType/category) stay tellable apart
     at a glance, matching the real office priority order Direct >
     Broker > WhatsApp. WhatsApp's own green is deliberate — leans on
     color recognition people already have, this is a small factual
     data tag, not branding. */
  --source-whatsapp: #1fa855;
  --source-broker: #475569;
  --source-direct: #ca8a04;
  /* Tinted (light bg + colored text) pair for each — settled style
     2026-09-04 after comparing against a solid-fill pill, which read
     as too loud sitting in otherwise-quiet footer text. */
  --source-whatsapp-tint: #e7f7ee;
  --source-whatsapp-text: #178a47;
  --source-broker-tint: #eef1f4;
  --source-broker-text: #3c4a5a;
  --source-direct-tint: #fdf3d9;
  --source-direct-text: #8a5f08;
  /* Extra tile shades for the brand mark only (below) — the rest of
     the app's teal already lives in --accent. */
  --accent-dark: #0c5945;
  --accent-mid: #6fa998;
  --accent-tint: #eaf3f0;
  --accent-pale: #d8e8e3;
  /* Under Construction's PSTATUS-*/PCATEGORY badge pairs (2026-09-09)
     — each only appears once in the actual badge rules, so these never
     earned real semantic token names before; tokenized now purely so
     dark mode below can reach them. */
  --pstatus-upcoming-bg: #eef1f4; --pstatus-upcoming-text: #3c4a5a;
  --pstatus-construction-bg: #fef1e0; --pstatus-construction-text: #9a5b0a;
  --pstatus-possession-bg: #e7f7ee; --pstatus-possession-text: #178a47;
  --pstatus-completed-bg: #eef0f2; --pstatus-completed-text: #4b5563;
  --pcategory-bg: #f3e8ff; --pcategory-text: #6d28d9;
}

/* Dark theme (2026-09-09) — real palette at last (Settings' theme
   toggle has stamped data-theme/persisted the choice since 2026-09-07,
   but nothing here answered it until now — picking Dark just silently
   did nothing). Every color the app actually paints with lives above
   as a token, so this only has to redefine the token VALUES once —
   not a naive invert: each hue is a moderately-bright variant chosen
   to stay legible in its two different roles (as text sitting on its
   own dark tint, AND as a solid badge/button fill under white text),
   the same tension the light palette resolves the other way (a fairly
   dark hue works as both dark-text-on-light-tint and a fill under
   white text). Two blocks, same values, per the standard three-way
   pattern: the media query answers "system" (guarded so an explicit
   Light choice always wins over a dark OS), the [data-theme="dark"]
   block answers an explicit Dark choice either way. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --bg: #10141b;
    --card: #1a212c;
    --border: #2c3444;
    --border-hover: #3d4759;
    --ink: #e8eaee;
    --muted: #8b93a3;
    --accent: #1fae87;
    --surface-2: #242c3a;
    --review: #e2504f;
    --favorite: #ca9a35;
    --favorite-dark: #e6b94a;
    --deleted: #7c8aa0;
    --deleted-tint: #232a36;
    --rent: #d99a3f;
    --sale: #1fae87;
    --requirement: #9575e0;
    --category: #35a8e0;
    --rent-tint: #3a2a14;
    --sale-tint: #12332a;
    --requirement-tint: #2e2140;
    --category-tint: #12293a;
    --review-tint: #3a1616;
    --source-whatsapp: #34d399;
    --source-broker: #9aa5b8;
    --source-direct: #e6b94a;
    --source-whatsapp-tint: #123625;
    --source-whatsapp-text: #4ade80;
    --source-broker-tint: #242c3a;
    --source-broker-text: #c3cbd8;
    --source-direct-tint: #3a2f10;
    --source-direct-text: #e6b94a;
    --accent-dark: #6fe3bd;
    --accent-mid: #4a8a76;
    --accent-tint: #12332a;
    --accent-pale: #1a4a3a;
    --pstatus-upcoming-bg: #242c3a; --pstatus-upcoming-text: #b6c0cf;
    --pstatus-construction-bg: #3a2a14; --pstatus-construction-text: #e6b94a;
    --pstatus-possession-bg: #123625; --pstatus-possession-text: #4ade80;
    --pstatus-completed-bg: #242c3a; --pstatus-completed-text: #9aa5b8;
    --pcategory-bg: #2e2140; --pcategory-text: #a78bfa;
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --bg: #10141b;
  --card: #1a212c;
  --border: #2c3444;
  --border-hover: #3d4759;
  --ink: #e8eaee;
  --muted: #8b93a3;
  --accent: #1fae87;
  --surface-2: #242c3a;
  --review: #e2504f;
  --favorite: #ca9a35;
  --favorite-dark: #e6b94a;
  --deleted: #7c8aa0;
  --deleted-tint: #232a36;
  --rent: #d99a3f;
  --sale: #1fae87;
  --requirement: #9575e0;
  --category: #35a8e0;
  --rent-tint: #3a2a14;
  --sale-tint: #12332a;
  --requirement-tint: #2e2140;
  --category-tint: #12293a;
  --review-tint: #3a1616;
  --source-whatsapp: #34d399;
  --source-broker: #9aa5b8;
  --source-direct: #e6b94a;
  --source-whatsapp-tint: #123625;
  --source-whatsapp-text: #4ade80;
  --source-broker-tint: #242c3a;
  --source-broker-text: #c3cbd8;
  --source-direct-tint: #3a2f10;
  --source-direct-text: #e6b94a;
  --accent-dark: #6fe3bd;
  --accent-mid: #4a8a76;
  --accent-tint: #12332a;
  --accent-pale: #1a4a3a;
  --pstatus-upcoming-bg: #242c3a; --pstatus-upcoming-text: #b6c0cf;
  --pstatus-construction-bg: #3a2a14; --pstatus-construction-text: #e6b94a;
  --pstatus-possession-bg: #123625; --pstatus-possession-text: #4ade80;
  --pstatus-completed-bg: #242c3a; --pstatus-completed-text: #9aa5b8;
  --pcategory-bg: #2e2140; --pcategory-text: #a78bfa;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  font-family: -apple-system, "Segoe UI", Roboto, sans-serif;
  background: var(--bg);
  color: var(--ink);
}

/* App shell (2026-09-02) — sidebar (desktop) + a scrolling main
   column; the sidebar/header/filters stay put, only .main-col's body
   scrolls. Bottom tab bar is the mobile equivalent of the sidebar,
   see the responsive block at the bottom of this file — both sets of
   nav buttons exist in the DOM at all times (app.js keeps them in
   sync), CSS just decides which is visible at the current width. */
.app-shell { display: flex; height: 100vh; overflow: hidden; }

.sidebar {
  width: 224px;
  flex-shrink: 0;
  background: var(--card);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  padding: 20px 12px;
  overflow-y: auto;
}
.sidebar-logo {
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: 'Sora', -apple-system, sans-serif;
  font-size: 18px;
  font-weight: 700;
  padding: 4px 12px 20px;
  cursor: pointer;
}

/* The finalized wordmark's icon — six assembled tiles, the name's own
   meaning (pieces forming a whole) rather than a real-estate literal.
   Six plain <i> tags in the markup, styled here by position to match
   the grid-area layout from the design exploration. */
.brand-mark {
  flex-shrink: 0;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-template-rows: repeat(4, 1fr);
  grid-template-areas: 'a a b c' 'a a b c' 'd e e c' 'd f f f';
  gap: 1px;
  width: 20px;
  height: 20px;
  background: var(--card);
  border-radius: 5px;
}
.brand-mark i { display: block; border-radius: 1px; font-style: normal; }
.brand-mark i:nth-child(1) { grid-area: a; background: var(--accent); }
.brand-mark i:nth-child(2) { grid-area: b; background: var(--accent-tint); }
.brand-mark i:nth-child(3) { grid-area: c; background: var(--ink-fixed); }
.brand-mark i:nth-child(4) { grid-area: d; background: var(--accent-mid); }
.brand-mark i:nth-child(5) { grid-area: e; background: var(--accent-dark); }
.brand-mark i:nth-child(6) { grid-area: f; background: var(--accent-pale); }
.sidebar-nav { display: flex; flex-direction: column; gap: 2px; }
.sidebar-bottom { margin-top: auto; padding-top: 12px; border-top: 1px solid var(--border); }

.navitem {
  position: relative; /* anchors .new-badge, see below */
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 9px 12px;
  border-radius: 8px;
  border: none;
  background: none;
  font-size: 13px;
  font-family: inherit;
  font-weight: 500;
  color: var(--muted);
  cursor: pointer;
  text-align: left;
}
.navitem:hover:not(:disabled) { background: var(--surface-2); color: var(--ink); }
.navitem.active { background: #eaf3f0; color: var(--accent); font-weight: 600; }
.navitem.active:hover { background: var(--accent-tint); }
.navitem.soon, .navitem:disabled { color: #b3b9c4; cursor: default; }
.navitem.soon:hover, .navitem:disabled:hover { background: none; color: #b3b9c4; }
.navitem svg { flex-shrink: 0; }
.soon-tag {
  margin-left: auto;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: #b3b9c4;
  background: var(--surface-2);
  padding: 2px 6px;
  border-radius: 10px;
}

.main-col { flex: 1; min-width: 0; display: flex; flex-direction: column; overflow-y: auto; }

.bottom-tabs { display: none; } /* shown on mobile, see responsive block */

header {
  display: flex;
  align-items: center;
  gap: 24px;
  padding: 14px 24px;
  background: var(--card);
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
  z-index: 10;
}

/* Hidden on desktop (the sidebar already shows MOSAIK) — shown only
   below the responsive breakpoint, since the sidebar itself is hidden
   there. See the @media block at the end of this file. */
.header-logo {
  display: none;
  align-items: center;
  gap: 7px;
  font-family: 'Sora', -apple-system, sans-serif;
  font-size: 18px;
  font-weight: 700;
  cursor: pointer;
}

.summary { color: var(--ink); font-size: 13px; }
.summary b { font-weight: 700; }

/* LIVE indicator (2026-09-08) — styled to match Main.dc.html's .live
   exactly (dot + bold label), while keeping #conn's real meaning: grey
   while the socket is still connecting, green once actually live, red
   if it drops. Not just decorative like the static mockup — a real
   disconnect still needs to be visible. */
.live { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--muted); }
.live-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--muted); }
.live.live .live-dot { background: var(--accent); }
.live.live b { color: var(--accent); font-weight: 700; letter-spacing: 0.03em; }
.live.dead .live-dot { background: var(--review); }
.live.dead b { color: var(--review); font-weight: 700; letter-spacing: 0.03em; }

.header-right { margin-left: auto; display: flex; align-items: center; gap: 10px; }
.header-divider { width: 1px; align-self: stretch; background: var(--border); }

/* 2026-09-08 — made a real flex row (was block flow, where #count's
   margin-left:auto was silently inert) so back-to-tab/title/count sit
   on one line with a real gap, matching every mockup's own toolbar row
   (Main.dc.html's plain title+count and DesktopNeedsReview.dc.html's/
   DesktopDeleted.dc.html's back-link+title+count use the same row). */
.page-title-row { display: flex; align-items: center; gap: 14px; padding: 18px 24px 4px; background: var(--bg); }
.page-title-row .count { margin-left: 0; } /* overrides .count's own margin-left:auto, meant for the unrelated #bf-count toolbar */
.page-title { font-family: "Sora", -apple-system, sans-serif; font-size: 22px; font-weight: 800; }
.back-link { display: flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 600; color: var(--accent); cursor: pointer; }

.filters {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  padding: 12px 24px;
  background: var(--card);
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
  z-index: 9;
}

.filters input, .filters select, .filters button {
  padding: 7px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  font-size: 13px;
  background: var(--card);
}

/* Base chrome for a "toggle" pill button — was only ever set via the
   `.filters button` descendant rule above until #f-review moved into
   <header> (2026-09-04, so it's reachable from every tab, not just
   whichever tab's own filter bar happened to contain it). Explicit
   here now so it doesn't matter which container a `.toggle` element
   lives in. */
.toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  font-size: 13px;
  background: var(--card);
  font-family: inherit;
  cursor: pointer;
  color: var(--ink);
}
/* Plain-link style (2026-09-08) — matches Main.dc.html's Clear button
   exactly: no border/background at rest, just muted text. */
.toggle.link { border: none; background: none; padding: 7px 4px; color: var(--muted); }
.toggle.link:hover { color: var(--ink); }

#f-q { flex: 1; min-width: 160px; max-width: 420px; }

.count { align-self: center; margin-left: auto; color: var(--muted); font-size: 12px; }

/* Single Filter button + popover (2026-09-02) — replaces the old
   always-visible row of filter fields. Sized generously (380px) so
   the price inputs never crowd/overflow the way they did in the old
   flat layout. */
.filter-btn-wrap { position: relative; }
.filter-btn {
  display: flex;
  align-items: center;
  gap: 7px;
  cursor: pointer;
  font-weight: 500;
}
.filter-btn.active { border-color: var(--accent) !important; background: #eaf3f0 !important; color: var(--accent); }
.filter-count {
  background: var(--accent);
  color: #fff;
  font-size: 10px;
  font-weight: 700;
  border-radius: 10px;
  padding: 1px 6px;
}

.filter-panel {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  width: 380px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, .14);
  padding: 18px;
  z-index: 15;
}
.filter-panel-title { font-size: 13px; font-weight: 700; margin-bottom: 14px; }
.filter-field { display: flex; flex-direction: column; gap: 6px; margin-bottom: 14px; }
.filter-field:last-of-type { margin-bottom: 0; }
.filter-field label { font-size: 12px; font-weight: 600; color: var(--muted); }
.filter-field input, .filter-field select { width: 100%; }
.filter-row { display: flex; gap: 10px; }
.filter-row .filter-field { flex: 1; }
.filter-price-range { display: flex; gap: 8px; align-items: center; }
.filter-price-range input { flex: 1; min-width: 0; }
.filter-actions { display: flex; gap: 8px; margin-top: 18px; }
.filter-actions button { flex: 1; padding: 9px; font-weight: 600; cursor: pointer; }
#f-apply { background: var(--accent); color: #fff; border-color: var(--accent) !important; }
#f-apply:hover { opacity: .9; }
#f-clear { color: var(--muted); }

.add-btn { display: inline-flex; align-items: center; gap: 6px; background: var(--accent) !important; color: #fff !important; border-color: var(--accent) !important; font-weight: 600; }
.add-btn:hover { opacity: 0.9; }

/* Buyer/Seller split (2026-09-04) — a first pass matched to the
   picked mockup; a full-width toggle in two equal halves rather than
   compact buttons. Refinement deferred to a later pass over the
   whole dashboard, per the user's own call. */
.bs-split {
  display: flex;
  margin: 0 24px 12px;
  border: 1px solid var(--border);
  border-radius: 10px;
  overflow: hidden;
}
.bs-half {
  flex: 1;
  text-align: center;
  padding: 14px 0;
  background: var(--surface-2);
  border: none;
  border-bottom: 3px solid transparent;
  font-family: inherit;
  font-weight: 600;
  font-size: 14px;
  color: var(--muted);
  cursor: pointer;
}
.bs-half + .bs-half { border-left: 1px solid var(--border); }
.bs-half.active { background: var(--card); border-bottom-color: var(--accent); font-weight: 700; color: var(--ink); }
.bs-count { font-size: 12px; font-weight: 500; margin-left: 4px; }
.bs-half.active .bs-count { color: var(--muted); }

.list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 12px;
  padding: 20px 24px;
}

.card {
  position: relative;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 14px;
  cursor: pointer;
  transition: box-shadow 0.15s, border-color 0.15s;
}

/* Favourites star (2026-09-06) — corner placement rather than inline
   with the .top badge row, which can already get crowded (status +
   type + record + category badges on one property). Gold when active,
   a deliberately new color not reused from any existing badge/status
   tint in this file. Bordered icon-button box (2026-09-07 fix) — this
   used to be border:none/transparent, which had quietly drifted from
   what the live site actually rendered (confirmed against a real
   screenshot, not just re-reading this file) — same convention as
   .icon-btn elsewhere, not a bare floating glyph. */
.fav-btn {
  position: absolute;
  top: 10px;
  right: 10px;
  width: 28px;
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--card);
  color: var(--muted);
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
}
.fav-btn:hover { background: var(--bg); }
.fav-btn.active { border-color: var(--favorite); color: var(--favorite); }
.fav-btn.active:hover { border-color: var(--favorite-dark); color: var(--favorite-dark); }

.card:hover { box-shadow: 0 4px 14px rgba(0, 0, 0, 0.08); border-color: var(--border-hover); }

.card .top { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; padding-right: 34px; }
/* Sora (2026-09-07) — matches the property NAME everywhere else that
   already uses it (project cards, the brand wordmark), extended here
   rather than left as the one card title still on the system font. */
.card .name { font-family: "Sora", -apple-system, sans-serif; font-weight: 800; font-size: 18px; margin-top: 10px; }

.badge {
  font-size: 11px;
  font-weight: 700;
  padding: 2px 8px;
  border-radius: 20px;
  color: #fff;
  margin-left: 6px;
  vertical-align: middle;
}
.badge.RENT { background: var(--rent); }
.badge.SALE { background: var(--sale); }
.badge.REQUIREMENT { background: var(--requirement); }
.badge.SOURCE-WHATSAPP { background: var(--source-whatsapp-tint); color: var(--source-whatsapp-text); }
.badge.SOURCE-BROKER_CALL { background: var(--source-broker-tint); color: var(--source-broker-text); }
.badge.SOURCE-DIRECT { background: var(--source-direct-tint); color: var(--source-direct-text); }
.badge.CATEGORY { background: var(--category); }

/* Property card's own top-row tags (2026-09-07) — see the --*-tint
   tokens' own comment above for why this is a separate class from
   .badge rather than a variant of it. */
.tag {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  padding: 3px 9px;
  border-radius: 6px;
}
.tag.RENT { background: var(--rent-tint); color: var(--rent); }
.tag.SALE { background: var(--sale-tint); color: var(--sale); }
.tag.LISTING, .tag.REQUIREMENT { background: var(--requirement-tint); color: var(--requirement); }
.tag.CATEGORY { background: var(--category-tint); color: var(--category); }
.tag.REVIEW { background: var(--review-tint); color: var(--review); }

.card .loc { display: flex; align-items: center; gap: 5px; color: var(--muted); font-size: 13px; margin-top: 4px; }
.card .loc svg { flex-shrink: 0; }
.card .price { font-size: 19px; font-weight: 800; color: var(--accent); margin-top: 10px; }
.card .price .unit { font-size: 13px; color: var(--muted); font-weight: 500; }
.card .meta, .bcard-body .meta { margin-top: 8px; display: flex; flex-wrap: wrap; gap: 7px; }
.chip {
  font-size: 12px;
  color: var(--ink);
  background: var(--surface-2);
  border-radius: 7px;
  padding: 5px 10px;
  font-weight: 500;
}
.chip strong { color: var(--ink); }
/* Single consolidated footer row (2026-09-07) — replaces the old two-
   line footer (a "shared by N brokers / last seen" line above a
   separate hash-id/contact line). Broker count didn't disappear, just
   moved: it's a .meta chip now, alongside BHK/carpet/etc rather than
   its own sentence — see renderList()'s own comment on this. */
.card .foot {
  margin-top: 12px;
  padding-top: 11px;
  border-top: 1px dashed var(--border);
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 13px;
}
.card .foot svg { color: var(--muted); flex-shrink: 0; }
.card .foot strong { font-weight: 700; }
.card .foot-time { margin-left: auto; font-size: 12px; color: var(--muted); white-space: nowrap; }

.empty { grid-column: 1 / -1; color: var(--muted); text-align: center; padding: 60px 0; }

/* Broker Database card (2026-09-07 redesign) — avatar-forward, its own
   classes rather than reusing .card/.top/.name/.loc (see renderBrokerList's
   own comment on why). */
.bcard { background: var(--card); border: 1px solid var(--border); border-radius: 10px; padding: 16px; display: flex; gap: 14px; align-items: flex-start; cursor: pointer; transition: box-shadow 0.15s, border-color 0.15s; }
.bcard:hover { box-shadow: 0 4px 14px rgba(0, 0, 0, 0.08); border-color: var(--border-hover); }
.bavatar { width: 42px; height: 42px; border-radius: 50%; background: var(--accent-tint); color: var(--accent-dark); display: flex; align-items: center; justify-content: center; font-size: 14px; font-weight: 700; flex-shrink: 0; }
.bcard-body { min-width: 0; flex: 1; }
.bname { font-family: "Sora", -apple-system, sans-serif; font-weight: 800; font-size: 15px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.bfirm { display: flex; align-items: center; gap: 5px; color: var(--muted); font-size: 12px; margin-top: 3px; }
.bphone { color: var(--muted); font-size: 12px; margin-top: 2px; font-family: ui-monospace, monospace; }
.bcard-foot { margin-top: 8px; font-size: 12px; color: var(--muted); }

/* Floating window (2026-09-07, replaces the side panel) — #detail is
   now the dimmed backdrop covering the whole viewport; #detail-panel
   (below) is the actual floating white card centered inside it. Click
   the backdrop itself (not the card) to close — see core.js's own
   listener for this, added once, not per-feature. */
.detail {
  position: fixed;
  inset: 0;
  background: rgba(28, 35, 48, 0.4);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  z-index: 20;
}

.floating-panel {
  width: 640px;
  max-width: 100%;
  max-height: calc(100vh - 48px);
  background: var(--card);
  border-radius: 14px;
  box-shadow: 0 24px 70px rgba(0, 0, 0, 0.28);
  padding: 28px;
  overflow-y: auto;
}
/* Property Detail specifically got widened after real feedback ("if
   you feel the need widen the window") — every other view (add/upload
   forms, broker/project detail, Manage Staff, Settings) keeps the
   default width above. */
.floating-panel.wide { width: 820px; }

.detail .close { float: right; cursor: pointer; border: 1px solid var(--border); background: var(--card); border-radius: 6px; padding: 4px 10px; }
.detail h2 { margin-top: 0; }
.detail .row { display: flex; justify-content: space-between; padding: 7px 0; border-bottom: 1px solid var(--border); font-size: 13px; }
.detail .row span:last-child { text-align: right; max-width: 65%; }
.detail h3 { margin: 18px 0 6px; font-size: 13px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.04em; }
.share { border: 1px solid var(--border); border-radius: 8px; padding: 10px; margin-bottom: 8px; font-size: 12px; }
.share .msg { background: var(--surface-2); border-radius: 6px; padding: 8px; margin-top: 6px; white-space: pre-wrap; color: var(--ink); max-height: 180px; overflow-y: auto; }
.detach-btn {
  flex-shrink: 0;
  padding: 3px 8px;
  border: 1px solid var(--border);
  background: var(--card);
  color: var(--muted);
  border-radius: 6px;
  font-size: 11px;
  cursor: pointer;
}
.detach-btn:hover:not(:disabled) { border-color: var(--review); color: var(--review); }
.detach-btn:disabled { opacity: 0.5; cursor: default; }
.detach-error { display: block; color: var(--review); font-size: 11px; margin-top: 4px; }

.merge-section .merge-search {
  width: 100%;
  padding: 7px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  font-size: 13px;
  box-sizing: border-box;
}
.merge-candidate {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
  padding: 8px 4px;
  border-bottom: 1px dashed var(--border);
  font-size: 12px;
}
.merge-candidate:last-child { border-bottom: none; }
.merge-pick-btn {
  flex-shrink: 0;
  padding: 4px 10px;
  border: 1px solid var(--accent);
  background: var(--card);
  color: var(--accent);
  border-radius: 6px;
  font-size: 11px;
  font-weight: 600;
  cursor: pointer;
}
.merge-pick-btn:hover:not(:disabled) { background: var(--accent); color: #fff; }
.merge-pick-btn:disabled { opacity: 0.5; cursor: default; }
.merge-error { display: block; color: var(--review); font-size: 11px; margin-top: 4px; }

.delete-btn {
  padding: 6px 12px;
  border: 1px solid var(--review);
  background: var(--card);
  color: var(--review);
  border-radius: 6px;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
}
.delete-btn:hover { background: var(--review); color: #fff; }

/* Favourite (2026-09-06) */
.detail-fav-btn {
  padding: 6px 12px;
  border: 1px solid var(--border);
  background: var(--card);
  color: var(--muted);
  border-radius: 6px;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
}
.detail-fav-btn:hover { border-color: var(--favorite); color: var(--favorite); }
.detail-fav-btn.active { border-color: var(--favorite); background: var(--favorite); color: #fff; }
.detail-fav-btn.active:hover { background: var(--favorite-dark); border-color: var(--favorite-dark); }

/* detail-top (2026-09-08) — matches DesktopDetailFloating.dc.html:
   action buttons grouped on the left, close on the right, one flex
   row — replaces the old float:right stacking every action button had
   clustered near the close button in. */
.detail-top { display: flex; align-items: center; justify-content: space-between; margin-bottom: 4px; }
.detail-actions { display: flex; align-items: center; gap: 8px; }

/* Details section (2026-09-08) — a real 2-column grid, matching the
   mockup exactly; used to be a single-column list of .row (still is,
   for Broker Detail, which never had this grid in its own mockup —
   only Property Detail and Project Detail do). */
.fields-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0 24px; }

.reextract-btn {
  flex-shrink: 0;
  padding: 3px 8px;
  border: 1px solid var(--border);
  background: var(--card);
  color: var(--muted);
  border-radius: 6px;
  font-size: 11px;
  cursor: pointer;
}
.reextract-btn:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
.reextract-btn:disabled { opacity: 0.5; cursor: default; }
.reextract-results:empty { display: none; }
.reextract-results { margin-top: 6px; }
.reextract-note { font-size: 11px; color: var(--muted); padding: 4px 0; }
.reextract-candidate {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
  padding: 6px 8px;
  background: var(--surface-2);
  border-radius: 6px;
  margin-top: 4px;
  font-size: 11px;
}
.reextract-already { color: var(--muted); font-style: italic; flex-shrink: 0; }
.reextract-create-btn {
  flex-shrink: 0;
  padding: 3px 10px;
  border: 1px solid var(--accent);
  background: var(--card);
  color: var(--accent);
  border-radius: 6px;
  font-size: 11px;
  font-weight: 600;
  cursor: pointer;
}
.reextract-create-btn:hover:not(:disabled) { background: var(--accent); color: #fff; }
.reextract-create-btn:disabled { opacity: 0.6; cursor: default; }

.add-form { display: flex; flex-direction: column; gap: 12px; margin-top: 10px; }
.add-form label { display: flex; flex-direction: column; gap: 4px; font-size: 12px; color: var(--muted); font-weight: 600; }
.add-form input, .add-form select {
  padding: 7px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  font-size: 13px;
  font-weight: 400;
  color: var(--ink);
  font-family: inherit;
}
.add-submit-btn {
  padding: 9px 14px;
  border: none;
  background: var(--accent);
  color: #fff;
  border-radius: 6px;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  margin-top: 4px;
}
.add-submit-btn:hover:not(:disabled) { opacity: 0.9; }
.add-submit-btn:disabled { opacity: 0.5; cursor: default; }
.add-error { display: block; color: var(--review); font-size: 12px; margin-top: 4px; }

/* Add Property (2026-09-08, rebuilt to match DesktopAddProperty.dc.html
   exactly) — paired 2-column rows instead of one flat stacked column,
   and Deal Type/Record Type as full-width segmented toggles instead of
   <select> dropdowns. .add-form itself stays flex-column (shared by
   other add-forms elsewhere), each .field-row is just one grid child
   of it spanning the full width. */
.add-form .field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.add-form .field-segmented { display: flex; border: 1px solid var(--border); border-radius: 7px; overflow: hidden; }
.add-form .field-segmented span { flex: 1; text-align: center; padding: 9px; font-size: 13px; font-weight: 600; color: var(--muted); cursor: pointer; border-right: 1px solid var(--border); }
.add-form .field-segmented span:last-child { border-right: none; }
.add-form .field-segmented span.active { background: var(--accent); color: #fff; }
.add-form .submit-row { display: flex; gap: 10px; margin-top: 4px; }
.add-form .cancel-btn { flex: 1; padding: 12px; border-radius: 8px; font-size: 13px; font-weight: 600; border: 1px solid var(--border); background: var(--card); color: var(--ink); cursor: pointer; }
.add-form .cancel-btn:hover { border-color: var(--border-hover); }
.add-form .save-btn { flex: 2; padding: 12px; border-radius: 8px; font-size: 13px; font-weight: 600; border: none; background: var(--accent); color: #fff; cursor: pointer; margin-top: 0; }
.add-form .save-btn:hover:not(:disabled) { opacity: 0.9; }
.add-form .save-btn:disabled { opacity: 0.5; cursor: default; }

/* Broker Inventory / Direct Client picker + upload forms (2026-09-04) */
.add-form-section {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--border);
}
.upload-note { font-size: 12px; color: var(--muted); line-height: 1.5; }
.upload-status { display: block; color: var(--accent); font-size: 12px; margin-top: 4px; }

/* Upload dropzone (2026-09-08, rebuilt to match UploadDropzone.dc.html
   exactly) — a real drag-and-drop target with a file-row preview,
   replacing the old plain <input type=file>. */
.dropzone { border: 2px dashed var(--accent); border-radius: 12px; background: var(--accent-tint); padding: 32px 20px; text-align: center; cursor: pointer; }
.dropzone.drag-over { background: var(--accent-pale); }
.dropzone-icon { width: 52px; height: 52px; margin: 0 auto 14px; border-radius: 50%; background: var(--card); display: flex; align-items: center; justify-content: center; color: var(--accent); }
.dropzone-title { font-weight: 700; font-size: 14px; color: var(--ink); }
.dropzone-sub { font-size: 12px; color: var(--muted); margin-top: 4px; }
.browse-link { color: var(--accent); font-weight: 600; text-decoration: underline; cursor: pointer; }
.dropzone-formats { margin-top: 14px; font-size: 11px; color: var(--muted); }
.file-row { display: flex; align-items: center; gap: 10px; padding: 10px 12px; border: 1px solid var(--border); border-radius: 8px; font-size: 13px; }
.file-icon { width: 28px; height: 28px; border-radius: 6px; background: var(--accent-tint); color: var(--accent-dark); display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.file-name { font-weight: 600; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.file-status { font-size: 11px; color: var(--accent); font-weight: 600; flex-shrink: 0; }

/* !important is deliberate: .hidden's entire purpose is "unconditionally
   hidden" — found for real 2026-09-02 that the mobile .filter-panel
   media-query rule (display:flex, defined later in this file) was
   beating this at equal specificity, leaving the bottom-sheet filter
   panel visibly present — and blocking clicks to what's underneath it
   — even while carrying the "hidden" class. */
.hidden { display: none !important; }

.detail .row.editable span:last-child { cursor: pointer; }
.detail .row.editable:hover span:last-child { color: var(--accent); text-decoration: underline dotted; }
.detail .row.editing span:last-child { cursor: default; }
.detail .row.editing:hover span:last-child { color: inherit; text-decoration: none; }
.detail .row.editing { flex-wrap: wrap; }
.edit-input { width: 100%; padding: 3px 6px; border: 1px solid var(--accent); border-radius: 4px; font-size: 13px; font-family: inherit; box-sizing: border-box; }
.edit-error { flex-basis: 100%; color: var(--review); font-size: 11px; padding: 2px 0 6px; text-align: right; }
.edit-row { font-size: 12px; padding: 6px 0; border-bottom: 1px dashed var(--border); }
/* Revert (2026-09-05, canRestore-only) */
.edit-row.reverted { opacity: 0.55; }
.reverted-tag { margin-left: 8px; font-size: 10px; font-weight: 700; letter-spacing: .03em; text-transform: uppercase; color: var(--muted); }
.revert-btn {
  margin-left: 8px;
  padding: 2px 8px;
  border: 1px solid var(--border);
  background: var(--card);
  color: var(--muted);
  border-radius: 6px;
  font-size: 11px;
  cursor: pointer;
}
.revert-btn:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
.revert-btn:disabled { opacity: 0.5; cursor: default; }

/* Restore (2026-09-05) — reuses .review-banner's look (already used
   for the Needs Review banner) rather than a third banner style. */
.review-banner .restore-btn {
  margin-top: 8px;
  padding: 6px 12px;
  border: 1px solid var(--accent);
  background: var(--card);
  color: var(--accent);
  border-radius: 6px;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
}
.review-banner .restore-btn:hover:not(:disabled) { background: var(--accent); color: #fff; }
.restore-error { display: block; color: var(--review); font-size: 11px; margin-top: 4px; }

/* Manage Staff (2026-09-05, rebuilt 2026-09-07 to match
   DesktopManageStaff.dc.html exactly — bordered table card, round
   avatar initials, labeled toggle switches instead of checkboxes). */
.staff-table {
  max-width: 820px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 10px;
  overflow: hidden;
}
.staff-row {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 16px 20px;
  border-bottom: 1px solid var(--border);
  font-size: 13px;
}
.staff-row:last-child { border-bottom: none; }
.staff-table .avatar {
  width: 38px;
  height: 38px;
  border-radius: 50%;
  background: var(--accent-tint);
  color: var(--accent-dark);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
  font-weight: 700;
  flex-shrink: 0;
}
.staff-name { font-weight: 700; font-size: 14px; }
.staff-phone { color: var(--muted); font-size: 12px; margin-top: 1px; font-family: ui-monospace, monospace; }
.staff-controls { margin-left: auto; display: flex; align-items: center; gap: 20px; flex-shrink: 0; }
.staff-controls .control { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--muted); font-weight: 600; }
.you-tag {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
  background: var(--surface-2);
  padding: 2px 7px;
  border-radius: 10px;
  margin-left: 8px;
}
.invite-btn {
  display: flex;
  align-items: center;
  gap: 7px;
  margin-top: 20px;
  background: var(--accent);
  color: #fff;
  border: none;
  padding: 10px 18px;
  border-radius: 8px;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}
.invite-btn:hover { background: var(--accent-dark); }

/* Needs-review — reuses the existing "attention" red already used for
   .conn.dead / .edit-error, rather than introducing a new color. */
.badge.REVIEW { background: var(--review); }
/* Left-edge accent only (2026-09-08, matches DesktopNeedsReview.dc.html
   exactly) — the other three sides stay the normal thin card border;
   this used to color all four sides red uniformly. */
.card.needs-review { border-color: var(--border); border-left: 3px solid var(--review); }
.card .review-reason { font-size: 12px; color: var(--review); margin-top: 4px; padding: 9px 11px; background: var(--review-tint); border-radius: 7px; }

#f-review.toggle.active { background: var(--review); color: #fff; border-color: var(--review); }

/* Inline review/restore actions directly on the list card (2026-09-08,
   matches DesktopNeedsReview.dc.html/DesktopDeleted.dc.html exactly —
   both mockups act right from the card, no need to open the detail
   panel first). "Detach a share…" opens the same detail panel Sources
   section the card click already does — there's a real per-share
   picker to choose from there, not a second one duplicated here. */
.review-actions { margin-top: 12px; display: flex; gap: 8px; }
.review-actions button { flex: 1; padding: 8px; border-radius: 7px; font-size: 12px; font-weight: 600; cursor: pointer; }
.review-actions .card-resolve-btn { border: none; background: var(--accent); color: #fff; }
.review-actions .card-resolve-btn:hover:not(:disabled) { background: var(--accent-dark); }
.review-actions .card-resolve-btn:disabled { opacity: 0.6; cursor: default; }
.review-actions .card-detach-open-btn { border: 1px solid var(--border); background: var(--card); color: var(--ink); }

/* "Review All" bulk button (2026-09-09) — sits in the page-title row,
   pushed to the far right via margin-left:auto (that row is otherwise
   just back-link + title + count, all left-aligned/flex, so this is
   the only right-aligned thing in it). Same solid-accent treatment as
   the per-card "Mark Reviewed" button above, since it's the same
   action, just bulk. */
.review-all-btn { margin-left: auto; border: none; padding: 9px 16px; border-radius: 8px; font-size: 13px; font-weight: 700; background: var(--accent); color: #fff; cursor: pointer; }
.review-all-btn:hover:not(:disabled) { background: var(--accent-dark); }
.review-all-btn:disabled { opacity: 0.6; cursor: default; }

/* "Delete All" / Empty Bin bulk button (2026-09-09) — same slot/right-
   alignment as .review-all-btn (only ever one of the two visible at a
   time, see setMode()/core.js), styled with the same "danger" red
   already used for .conn.dead/.edit-error/review — this one's
   genuinely irreversible (permanent delete, not a soft delete), so the
   danger styling is earned, not just a visual echo. */
.delete-all-btn { margin-left: auto; border: 1px solid var(--review); padding: 8px 16px; border-radius: 8px; font-size: 13px; font-weight: 700; background: var(--card); color: var(--review); cursor: pointer; }
.delete-all-btn:hover:not(:disabled) { background: var(--review-tint); }
.delete-all-btn:disabled { opacity: 0.6; cursor: default; }

.badge.DELETED { background: var(--deleted-tint); color: var(--deleted); }
.card.deleted { background: var(--surface-2); opacity: 0.8; }
.card.deleted .name { color: var(--muted); }
.card.deleted .price { color: var(--muted); }
.deleted-meta { margin-top: 12px; padding-top: 11px; border-top: 1px dashed var(--border); font-size: 12px; color: var(--muted); }
.card .card-restore-btn { margin-top: 12px; width: 100%; padding: 8px; border-radius: 7px; font-size: 12px; font-weight: 600; border: 1px solid var(--accent); color: var(--accent); background: var(--card); cursor: pointer; }
.card .card-restore-btn:hover:not(:disabled) { background: var(--accent); color: #fff; }
.card .card-restore-btn:disabled { opacity: 0.6; cursor: default; }
/* Delete Permanently (2026-09-09) — a quieter, smaller link under the
   primary Restore button, not another full-width button — this is the
   less-common, irreversible path, Restore is the one most people want. */
.card .card-permanent-delete-link { display: block; text-align: center; margin-top: 8px; font-size: 11px; font-weight: 600; color: var(--review); cursor: pointer; }
.card .card-permanent-delete-link:hover { text-decoration: underline; }

/* Deleted view (2026-09-05) — #f-deleted had no rules of its own at
   all: its .active class (toggled in app.js) was doing nothing, so the
   button looked identical whether you were looking at the trash or not.
   A distinct dark slate rather than #f-review's red — this isn't an
   urgent/flagged state like Needs Review, just a different place to be. */
#f-deleted { position: relative; }
#f-deleted.toggle.active { background: var(--deleted); color: #fff; border-color: var(--deleted); }
#f-deleted.toggle.active .badge-count { background: #fff; color: #334155; }

/* Favourites toggle (2026-09-06) — same gold as the card corner star,
   not #f-review's red or #f-deleted's slate: neither urgent nor a
   trash/archive state, just a personal filter being on. */
#f-favorites.toggle.active { background: var(--favorite); color: #fff; border-color: var(--favorite); }

/* Jump-to-broker links from a property card/Sources list (2026-09-01) */
.broker-link { color: var(--accent); text-decoration: none; }
.broker-link:hover { text-decoration: underline; }

#f-review { position: relative; }
.badge-count {
  position: absolute;
  top: -7px;
  right: -7px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 999px;
  background: var(--review);
  color: #fff;
  font-size: 10px;
  font-weight: 700;
  line-height: 16px;
  text-align: center;
  border: 1px solid var(--card);
}
#f-review.toggle.active .badge-count { background: #fff; color: var(--review); }

/* Per-device "new since I last looked" bubble (2026-09-04) — read
   state lives entirely in this browser's localStorage (app.js), never
   on the server, since this dashboard is shared by multiple office
   staff and "read" was deliberately scoped private-per-device rather
   than synced. Reuses .badge-count's exact look; .navitem/.tab already
   carry position:relative to anchor it in the corner of the nav
   button. */
.new-badge {
  position: absolute;
  top: 4px;
  right: 8px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 999px;
  background: var(--review);
  color: #fff;
  font-size: 10px;
  font-weight: 700;
  line-height: 16px;
  text-align: center;
  border: 1px solid var(--card);
}
#f-mark-read.toggle { color: var(--accent); border-color: var(--accent); }
#f-mark-read.toggle:hover { background: var(--accent-tint); }

/* Small "NEW" pill on an individual property card, same trigger as the
   nav badges above (firstSeenAt after this device's checkpoint, and
   not yet individually opened) — cleared the moment that card's detail
   view is opened. */
.card .new-tag {
  background: var(--review);
  color: #fff;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  padding: 2px 6px;
  border-radius: 10px;
}

.review-banner {
  background: var(--review-tint);
  border: 1px solid var(--review);
  border-radius: 8px;
  padding: 10px 12px;
  margin-bottom: 14px;
  font-size: 13px;
}
.review-banner .resolve-btn {
  margin-top: 8px;
  padding: 6px 12px;
  border: 1px solid var(--review);
  background: var(--card);
  color: var(--review);
  border-radius: 6px;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
}
.review-banner .resolve-btn:hover { background: var(--review); color: #fff; }
.review-banner .resolve-btn:disabled { opacity: 0.5; cursor: default; }
.review-banner .resolve-error { display: block; color: var(--review); font-size: 11px; margin-top: 4px; }

/* Login (2026-09-05) — matches the approved mockup exactly: a dark
   brand panel + a white functional panel. Own styles rather than
   reusing .filters/.card etc — this is a full-page moment, not a
   dashboard component. */
.login-screen {
  display: flex;
  min-height: 100vh;
  font-family: -apple-system, "Segoe UI", Roboto, sans-serif;
  background: var(--card);
}
.login-left {
  width: 460px;
  flex-shrink: 0;
  background: var(--ink-fixed);
  padding: 56px 48px;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  position: relative;
  overflow: hidden;
}
.login-brand-tile-echo {
  position: absolute;
  right: -60px;
  bottom: -60px;
  width: 260px;
  height: 260px;
  opacity: 0.06;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-template-rows: repeat(4, 1fr);
  grid-template-areas: "a a b c" "a a b c" "d e e c" "d f f f";
  gap: 6px;
}
.login-brand-tile-echo div { border-radius: 6px; }
.login-brand-tile-echo div:nth-child(1) { grid-area: a; background: var(--accent); }
.login-brand-tile-echo div:nth-child(2) { grid-area: b; background: var(--accent-tint); }
.login-brand-tile-echo div:nth-child(3) { grid-area: c; background: var(--ink-fixed); }
.login-brand-tile-echo div:nth-child(4) { grid-area: d; background: var(--accent-mid); }
.login-brand-tile-echo div:nth-child(5) { grid-area: e; background: var(--accent-dark); }
.login-brand-tile-echo div:nth-child(6) { grid-area: f; background: var(--accent-pale); }
.login-logo {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: "Sora", sans-serif;
  font-weight: 700;
  font-size: 21px;
  color: #fff;
}
.login-logo .brand-mark { width: 30px; height: 30px; border-radius: 7px; }
.login-hero { margin-top: auto; }
.login-hero-title {
  font-family: "Sora", sans-serif;
  font-weight: 800;
  font-size: 30px;
  line-height: 1.28;
  color: #fff;
  text-wrap: balance;
  max-width: 340px;
}
.login-hero-sub { margin-top: 14px; font-size: 14px; line-height: 1.6; color: #9aa8b3; max-width: 320px; }
.login-hero-note { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--muted); }

.login-right { flex: 1; display: flex; align-items: center; justify-content: center; padding: 40px 24px; }
.login-form { width: 360px; }
.login-title { font-family: "Sora", sans-serif; font-weight: 800; font-size: 24px; color: var(--ink); }
.login-subtitle { margin-top: 8px; font-size: 14px; color: var(--muted); line-height: 1.5; }
.login-field-label { display: block; margin-top: 24px; margin-bottom: 8px; font-size: 12px; font-weight: 600; color: var(--muted); }
.login-form input {
  width: 100%;
  padding: 11px 14px;
  border: 1px solid var(--border);
  border-radius: 8px;
  font-size: 14px;
  font-family: inherit;
  color: var(--ink);
  box-sizing: border-box;
}
.login-phone-row { display: flex; gap: 8px; }
.login-phone-prefix {
  width: 52px;
  flex-shrink: 0;
  padding: 11px 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  font-size: 14px;
  color: var(--ink);
  background: var(--surface-2);
  text-align: center;
}
.login-phone-row input { flex: 1; }

/* Pairing-method choice (2026-09-11) — desktop only, see its own
   comment in index.html for why. Same segmented-control visual
   language as .segmented elsewhere in the dashboard (active state =
   solid ink fill), scaled down for this form's width. */
.login-mode-toggle { display: flex; margin-top: 20px; border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }
.login-mode-btn {
  flex: 1;
  padding: 9px 0;
  border: none;
  background: var(--card);
  color: var(--muted);
  font-size: 13px;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
}
.login-mode-btn + .login-mode-btn { border-left: 1px solid var(--border); }
.login-mode-btn.active { background: var(--ink-fixed); color: #fff; }
.login-primary-btn {
  margin-top: 20px;
  width: 100%;
  padding: 13px 0;
  background: var(--accent);
  color: #fff;
  border: none;
  border-radius: 8px;
  font-size: 14px;
  font-weight: 600;
  font-family: inherit;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  cursor: pointer;
}
.login-primary-btn:hover:not(:disabled) { opacity: 0.92; }
.login-primary-btn:disabled { opacity: 0.6; cursor: default; }
.login-fineprint { margin-top: 20px; font-size: 12px; color: var(--muted); line-height: 1.6; }

/* Connecting state (2026-09-05) — indeterminate, not a real
   percentage (see app.js's comment for why an honest "please wait"
   beats a fake progress number here). */
.login-connecting { margin-top: 16px; }
.login-progress-track {
  width: 100%;
  height: 4px;
  border-radius: 999px;
  background: var(--accent-tint);
  overflow: hidden;
}
.login-progress-bar {
  width: 40%;
  height: 100%;
  border-radius: 999px;
  background: var(--accent);
  animation: login-progress-slide 1.2s ease-in-out infinite;
}
@keyframes login-progress-slide {
  0% { transform: translateX(-100%); }
  100% { transform: translateX(250%); }
}
.login-connecting-text { margin-top: 10px; font-size: 12px; color: var(--muted); line-height: 1.5; }
@media (prefers-reduced-motion: reduce) {
  .login-progress-bar { animation: none; width: 100%; opacity: 0.5; }
}
.login-error { display: block; margin-top: 10px; color: var(--review); font-size: 12px; }
/* Scam-warning heads-up (2026-09-10) — same soft-tinted informational
   tone as .review-reason elsewhere (accent, not review-red — this
   isn't a warning about MOSAIK, it's a heads-up about WhatsApp's own
   UI so it doesn't look alarming). */
.login-scam-note { margin-top: 18px; padding: 10px 12px; background: var(--accent-tint); color: var(--accent-dark); border-radius: 8px; font-size: 12px; line-height: 1.5; }

.login-back-link { display: flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; color: var(--muted); cursor: pointer; width: fit-content; }
.login-code-box {
  margin-top: 22px;
  padding: 22px 0;
  background: var(--accent-tint);
  border: 1px solid var(--accent-pale);
  border-radius: 12px;
  text-align: center;
}
.login-code-display {
  font-family: "Sora", sans-serif;
  font-weight: 800;
  font-size: 32px;
  letter-spacing: 0.14em;
  color: var(--accent-dark);
}
.login-code-timer { margin-top: 10px; display: flex; align-items: center; justify-content: center; gap: 6px; font-size: 12px; font-weight: 600; color: var(--accent-dark); }
.login-code-actions { margin-top: 16px; display: flex; gap: 10px; }
/* QR step (2026-09-11) — same box treatment as .login-code-box, just
   framing an image instead of a text code. */
.login-qr-box {
  margin-top: 22px;
  padding: 22px 0;
  background: var(--accent-tint);
  border: 1px solid var(--accent-pale);
  border-radius: 12px;
  text-align: center;
}
.login-qr-image { width: 176px; height: 176px; border-radius: 8px; background: #fff; padding: 8px; box-sizing: border-box; }
.login-secondary-btn {
  flex: 1;
  padding: 11px 0;
  border: 1px solid var(--border);
  background: var(--card);
  border-radius: 8px;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
  font-family: inherit;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  cursor: pointer;
}
.login-secondary-btn:hover { border-color: var(--border-hover); }
.login-code-btn { margin-top: 0; flex: 1; padding: 11px 0; font-size: 13px; }
.login-steps { margin-top: 26px; display: flex; flex-direction: column; gap: 12px; }
.login-step { display: flex; align-items: flex-start; gap: 10px; font-size: 13px; color: var(--ink); line-height: 1.5; }
.login-step-num {
  flex-shrink: 0;
  width: 20px;
  height: 20px;
  border-radius: 999px;
  background: var(--accent-tint);
  color: var(--accent);
  font-size: 11px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Company code step (2026-09-08) — real implementation of
   DesktopCompanyCode.dc.html, now Step 1 of the real 3-step flow (see
   the step-tag pattern below, carried through login-step-phone/
   login-step-code/login-step-qr the same way the mockup settled it).
   Reuses .login-form/.login-title/.login-field-label/.login-primary-btn/
   .login-error wholesale — only what's genuinely new lives here. */
.step-tag {
  display: inline-block;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--accent-dark);
  background: var(--accent-tint);
  padding: 4px 10px;
  border-radius: 999px;
}
.company-code-input {
  font-family: "Sora", sans-serif;
  font-weight: 800;
  font-size: 20px;
  letter-spacing: 0.18em;
  text-align: center;
  text-transform: uppercase;
}
.confirm-box {
  margin-top: 16px;
  padding: 13px 15px;
  background: var(--accent-tint);
  border: 1px solid var(--accent-pale);
  border-radius: 10px;
  display: flex;
  align-items: center;
  gap: 10px;
}
.confirm-box svg { flex-shrink: 0; color: var(--accent-dark); }
.confirm-text { font-size: 12.5px; color: var(--accent-dark); line-height: 1.45; }
.confirm-text strong { font-family: "Sora", sans-serif; }

/* Suspension / stale-code screens (2026-09-08) — reached from
   checkAuthAndInit when /api/auth/me reports errorCode
   company_suspended / company_code_stale (see auth.js's requireAuth and
   login.js). Both stay inside the same #login-screen shell as every
   other step — a session that can't get past this gate is the same
   kind of "not fully in the dashboard" state the pairing steps are. */
.login-suspended-icon { color: var(--review); margin-bottom: 4px; }

@media (max-width: 860px) {
  .login-screen { flex-direction: column; min-height: 100vh; }
  .login-left { width: auto; padding: 28px 24px; }
  .login-hero { margin-top: 24px; }
  .login-hero-title { font-size: 22px; max-width: none; }
  .login-hero-sub { max-width: none; }
  .login-right { padding: 32px 20px; }
  .login-form { width: 100%; max-width: 360px; }
  /* A phone can't scan its own screen — code entry stays the only
     option here, exactly as it always has been. */
  .login-mode-toggle { display: none; }
}

/* Header account menu (2026-09-05) */
.account-menu { display: flex; align-items: center; gap: 8px; cursor: pointer; }
.account-menu .avatar { width: 26px; height: 26px; border-radius: 50%; background: var(--accent-tint); color: var(--accent-dark); display: flex; align-items: center; justify-content: center; font-size: 11px; font-weight: 700; flex-shrink: 0; }
.account-name { font-size: 13px; font-weight: 600; color: var(--ink); cursor: pointer; }
.account-name:hover { color: var(--accent); }

/* Builder Properties (2026-09-04) — its own grid/card/detail styling.
   #builder-filters/#bf-* reuse the .filters/.filter-panel/.filter-btn/
   .add-btn/.count classes wholesale (see index.html) rather than
   duplicating them here — only what's genuinely new lives below. */

.builder-chips {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  padding: 0 24px 14px;
}
.chip-pill {
  font-size: 12px;
  font-weight: 600;
  padding: 5px 13px;
  border-radius: 20px;
  background: var(--card);
  border: 1px solid var(--border);
  color: var(--ink);
  cursor: pointer;
}
.chip-pill.active { background: var(--ink-fixed); border-color: var(--ink-fixed); color: #fff; }

/* Image-forward cards (settled 2026-09-03 after three structural
   rounds — see the design canvas linked in the memory log) — a bit
   wider than the default .card grid so a photo/gradient header has
   room to read. */
.builder-grid { grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 14px; }

.project-card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 12px;
  overflow: hidden;
  cursor: pointer;
  transition: box-shadow 0.15s, border-color 0.15s;
}
.project-card:hover { box-shadow: 0 4px 14px rgba(0, 0, 0, 0.08); border-color: var(--border-hover); }
.project-card-header { height: 110px; position: relative; }
.project-card-header img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* No elevation image on file — a color-gradient placeholder, not a
   broken image or an icon+"No photo yet" label (2026-09-04): the
   mixed-use mockup card's own gradient background already read better
   as a fallback than a muted icon did, once it was sitting next to
   real photos. gradientFor() in app.js picks one of a curated set
   deterministically from the project's id. */
.project-card-gradient { width: 100%; height: 100%; }
.project-card-tags { position: absolute; left: 0; bottom: 0; padding: 8px; display: flex; gap: 5px; flex-wrap: wrap; }
.project-card-tags .badge { background: #fff; }
.project-card-body { padding: 12px 14px 14px; }
.project-card-body .name { font-family: 'Sora', -apple-system, sans-serif; font-weight: 800; font-size: 15px; color: var(--ink); }
.project-card-body .loc { margin-top: 1px; }
.project-card-body .price { font-size: 16px; font-weight: 700; margin-top: 8px; color: var(--ink); }
.project-card-body .meta { margin-top: 8px; display: flex; flex-wrap: wrap; gap: 6px; }

/* Status/category badges — own colors so they're tellable apart from
   the property card's RENT/SALE/REQUIREMENT/CATEGORY/SOURCE badges at
   a glance. NEARING_POSSESSION intentionally shares the "good" green
   family with AVAILABLE below — both mean "close to done" in their
   own context. */
.badge.PSTATUS-UPCOMING, .pstatus.PSTATUS-UPCOMING { background: var(--pstatus-upcoming-bg); color: var(--pstatus-upcoming-text); }
.badge.PSTATUS-UNDER_CONSTRUCTION, .pstatus.PSTATUS-UNDER_CONSTRUCTION { background: var(--pstatus-construction-bg); color: var(--pstatus-construction-text); }
.badge.PSTATUS-NEARING_POSSESSION, .pstatus.PSTATUS-NEARING_POSSESSION { background: var(--pstatus-possession-bg); color: var(--pstatus-possession-text); }
.badge.PSTATUS-COMPLETED, .pstatus.PSTATUS-COMPLETED { background: var(--pstatus-completed-bg); color: var(--pstatus-completed-text); }
.badge.PCATEGORY { background: var(--pcategory-bg); color: var(--pcategory-text); }
.project-card-tags .badge.PSTATUS-UPCOMING, .project-card-tags .badge.PSTATUS-UNDER_CONSTRUCTION,
.project-card-tags .badge.PSTATUS-NEARING_POSSESSION, .project-card-tags .badge.PSTATUS-COMPLETED,
.project-card-tags .badge.PCATEGORY { background: #fff; }

/* Project Detail panel (2026-09-08, rebuilt to match
   DesktopProjectDetail.dc.html exactly) — a full-bleed elevation
   banner with the close button overlaid on it, breaking out of
   .floating-panel's own padding via negative margins, followed by a
   status pill above the title and an action-row below it. This
   replaces the old rounded-thumbnail-inside-the-padding image and the
   shared .detail-top/.detail-actions header Property Detail uses —
   Project Detail's own mockup is structurally different (image+overlay
   close, not a flex title-row), so it gets its own pattern here. */
.elevation {
  height: 140px;
  margin: -28px -28px 20px;
  border-radius: 14px 14px 0 0;
  position: relative;
  overflow: hidden;
  background-size: cover;
  background-position: center;
}
.elevation-img { width: 100%; height: 100%; object-fit: cover; display: block; }
.elevation .close {
  position: absolute;
  top: 14px;
  right: 14px;
  float: none;
  cursor: pointer;
  border: 1px solid rgba(255, 255, 255, 0.4);
  background: rgba(0, 0, 0, 0.2);
  color: #fff;
  border-radius: 6px;
  padding: 6px 12px;
  font-size: 13px;
}
.pstatus {
  display: inline-block;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  padding: 3px 9px;
  border-radius: 6px;
}
.action-row { margin: 16px 0 4px; display: flex; gap: 8px; }
.action-btn { padding: 7px 13px; border-radius: 7px; font-size: 12px; font-weight: 600; cursor: pointer; border: 1px solid var(--border); background: var(--card); color: var(--ink); }
.action-btn.transfer { border-color: var(--accent); color: var(--accent); }
.action-btn.transfer:hover { background: var(--accent); color: #fff; }
.action-btn.delete { border-color: var(--review); color: var(--review); }
.action-btn.delete:hover { background: var(--review); color: #fff; }
.project-config {
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 12px;
  margin-bottom: 8px;
  font-size: 12px;
}
.project-config select { padding: 3px 6px; border: 1px solid var(--border); border-radius: 6px; font-size: 11px; font-family: inherit; }
.cfg-delete-btn {
  flex-shrink: 0;
  padding: 3px 8px;
  border: 1px solid var(--border);
  background: var(--card);
  color: var(--muted);
  border-radius: 6px;
  font-size: 11px;
  cursor: pointer;
}
.cfg-delete-btn:hover { border-color: var(--review); color: var(--review); }
.cfg-add-error { display: block; color: var(--review); font-size: 12px; margin-top: 4px; }

/* Transfer to Builder (2026-09-04) — property detail panel action,
   same visual family as the neighboring Delete button. Float dropped
   2026-09-08 — see .detail-top's own comment, all three action
   buttons are flex children now, not floated. */
.transfer-btn {
  padding: 6px 12px;
  border: 1px solid var(--accent);
  background: var(--card);
  color: var(--accent);
  border-radius: 6px;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
}
.transfer-btn:hover { background: var(--accent); color: #fff; }
.transfer-section {
  clear: both;
  display: flex;
  gap: 8px;
  align-items: center;
  flex-wrap: wrap;
  background: var(--surface-2);
  border-radius: 8px;
  padding: 10px;
  margin: 10px 0;
}
.transfer-section input {
  flex: 1;
  min-width: 160px;
  padding: 6px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  font-size: 12px;
  font-family: inherit;
}
.transfer-confirm-btn {
  padding: 6px 12px;
  border: 1px solid var(--accent);
  background: var(--accent);
  color: #fff;
  border-radius: 6px;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
}
.transfer-error { display: block; flex-basis: 100%; color: var(--review); font-size: 11px; }

/* Reassign channel control (2026-09-04) — inline on each share in the
   property detail panel's Sources list. */
.channel-reassign select { padding: 4px 6px; border: 1px solid var(--border); border-radius: 6px; font-size: 12px; font-family: inherit; }
.channel-attribution { display: flex; gap: 6px; }
.channel-attribution input { padding: 4px 6px; border: 1px solid var(--border); border-radius: 6px; font-size: 12px; font-family: inherit; }
.channel-save-btn {
  padding: 3px 10px;
  border: 1px solid var(--accent);
  background: var(--accent);
  color: #fff;
  border-radius: 6px;
  font-size: 11px;
  font-weight: 600;
  cursor: pointer;
}
.channel-error { display: block; flex-basis: 100%; color: var(--review); font-size: 11px; }

.pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  padding: 8px 24px 28px;
}
.pagination button {
  padding: 7px 14px;
  border: 1px solid var(--border);
  border-radius: 6px;
  font-size: 13px;
  background: var(--card);
  cursor: pointer;
}
.pagination button:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
.pagination button:disabled { opacity: 0.4; cursor: default; }
.pagination #page-indicator { font-size: 13px; color: var(--muted); }
.page-jump { display: flex; align-items: center; gap: 6px; font-size: 13px; color: var(--muted); }
.page-jump input {
  width: 48px;
  padding: 5px 6px;
  border: 1px solid var(--border);
  border-radius: 6px;
  font-size: 13px;
  text-align: center;
  background: var(--card);
  /* Hide the native spinner arrows — this is a "type a number, press
     Enter" jump box, not a stepper (Prev/Next already cover +/-1). */
  -moz-appearance: textfield;
}
.page-jump input::-webkit-outer-spin-button,
.page-jump input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
.page-jump input:focus { outline: none; border-color: var(--accent); }

/* Responsive (2026-09-02) — "will have to be adjusting to the device
   it is on, be it mobile or pcs with diff dimensions of screens."
   Sidebar swaps for a bottom tab bar below the breakpoint; a
   persistent nav either way, never a hamburger (see the design
   brainstorm — this is a daily-use tool for the same office staff,
   discoverability matters more than screen space). The filter popover
   becomes a bottom sheet, matching the approved mockup. */
@media (max-width: 860px) {
  .sidebar { display: none; }
  .bottom-tabs {
    display: flex;
    background: var(--card);
    border-top: 1px solid var(--border);
    padding: 6px 4px calc(6px + env(safe-area-inset-bottom, 0px));
    flex-shrink: 0;
  }
  .tab {
    position: relative; /* anchors .new-badge, see the badge's own comment */
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 3px;
    flex: 1;
    padding: 6px 0;
    border: none;
    background: none;
    color: var(--muted);
    font-size: 10px;
    font-weight: 500;
    font-family: inherit;
    cursor: pointer;
  }
  .tab.active { color: var(--accent); font-weight: 700; }
  .tab.soon, .tab:disabled { color: #d5d8de; cursor: default; }

  .app-shell { flex-direction: column; height: 100vh; }
  .main-col { flex: 1; min-height: 0; }

  header { padding: 12px 14px; gap: 12px; }
  .header-logo { display: flex; font-size: 15px; }
  .header-logo .brand-mark { width: 17px; height: 17px; }
  .summary { display: none; } /* the stat strip doesn't fit a phone width — the card grid is the point */

  .filters { padding: 10px 14px; }
  #f-q { max-width: none; }
  .add-btn span { display: none; }

  .list { grid-template-columns: 1fr; padding: 14px; gap: 10px; }

  /* Filter panel becomes a bottom sheet: fixed to the viewport, not
     anchored to the button, full width, scrollable body with the
     actions pinned at the bottom — same shape as the approved mockup. */
  .filter-panel {
    position: fixed;
    top: auto;
    left: 0;
    right: 0;
    bottom: 0;
    width: auto;
    max-height: 80vh;
    border-radius: 16px 16px 0 0;
    display: flex;
    flex-direction: column;
    padding: 0;
  }
  .filter-panel-title { padding: 16px 18px 12px; margin: 0; border-bottom: 1px solid var(--border); }
  .filter-panel > .filter-field, .filter-panel > .filter-row {
    margin: 14px 18px 0;
  }
  .filter-panel > .filter-field:first-of-type { margin-top: 16px; }
  .filter-actions { margin: 18px 18px 0; padding: 14px 0; border-top: 1px solid var(--border); position: sticky; bottom: 0; background: var(--card); }

  /* Floating card fills the screen edge-to-edge on mobile (2026-09-07)
     — a 640px card centered with 24px padding wouldn't fit at all
     below that width. */
  .detail { padding: 0; }
  .floating-panel, .floating-panel.wide { width: 100vw; max-width: 100vw; max-height: 100vh; border-radius: 0; }
}

/* Toast confirmations (2026-09-07) — a shared, personalised stand-in
   for the native alert()s this app used to fall back on (see
   app.js's showToast()). Stacks bottom-right, newest on top. */
.toast-container {
  position: fixed;
  bottom: 24px;
  right: 24px;
  z-index: 500;
  display: flex;
  flex-direction: column-reverse;
  gap: 10px;
  pointer-events: none;
}
.toast {
  display: flex;
  align-items: center;
  gap: 10px;
  max-width: 360px;
  padding: 13px 16px;
  border-radius: 10px;
  background: var(--ink-fixed);
  color: #fff;
  font-size: 13px;
  font-weight: 500;
  box-shadow: 0 12px 32px rgba(28, 35, 48, 0.22);
  cursor: pointer;
  pointer-events: auto;
  animation: toast-in 0.2s ease-out;
}
.toast.leaving { animation: toast-out 0.2s ease-in forwards; }
.toast-icon {
  flex-shrink: 0;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--accent);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  font-weight: 700;
}
.toast.error .toast-icon { background: var(--review); }
@keyframes toast-in {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: translateY(0); }
}
@keyframes toast-out {
  from { opacity: 1; transform: translateY(0); }
  to { opacity: 0; transform: translateY(8px); }
}
@media (prefers-reduced-motion: reduce) {
  .toast, .toast.leaving { animation: none; }
}

/* Confirm dialog (2026-09-08) — a styled stand-in for the native
   confirm() every destructive/consequential action used to fall back
   on. Same design language as .toast (dark ink accents, the app's own
   radii/shadow) rather than a generic modal — this is meant to read as
   part of the app, not a browser chrome element. */
.confirm-overlay {
  position: fixed;
  inset: 0;
  z-index: 600;
  background: rgba(28, 35, 48, 0.45);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  animation: confirm-overlay-in 0.15s ease-out;
}
.confirm-dialog {
  width: 100%;
  max-width: 380px;
  background: var(--card);
  border-radius: 14px;
  padding: 24px;
  box-shadow: 0 24px 60px rgba(28, 35, 48, 0.28);
  animation: confirm-dialog-in 0.18s ease-out;
}
.confirm-dialog-title {
  font-family: "Sora", sans-serif;
  font-weight: 800;
  font-size: 17px;
  color: var(--ink);
}
.confirm-dialog-message {
  margin-top: 10px;
  font-size: 13.5px;
  line-height: 1.55;
  color: var(--muted);
}
.confirm-dialog-actions {
  margin-top: 22px;
  display: flex;
  justify-content: flex-end;
  gap: 10px;
}
.confirm-dialog-cancel,
.confirm-dialog-ok {
  padding: 9px 16px;
  border-radius: 8px;
  font-size: 13px;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  border: 1px solid var(--border);
}
.confirm-dialog-cancel { background: var(--card); color: var(--ink); }
.confirm-dialog-cancel:hover { border-color: var(--border-hover); }
.confirm-dialog-ok { background: var(--accent); color: #fff; border-color: var(--accent); }
.confirm-dialog-ok:hover { opacity: 0.92; }
/* Destructive actions (delete, permanently-delete, regenerate a code
   already handed out) get the review-red treatment instead of the
   ordinary accent — matches how .review-banner/.logout-btn already use
   --review for "this needs real attention," not just any confirmation. */
.confirm-dialog-ok.danger { background: var(--review); border-color: var(--review); }
.confirm-dialog-ok:disabled { opacity: 0.5; cursor: default; }

/* Type-to-confirm row (2026-09-09) — hidden by default, only shown
   when a caller passes showConfirm({ confirmText }). Sits between the
   message and the action buttons. */
.confirm-dialog-typecheck { margin-top: 16px; }
.confirm-dialog-typecheck label { display: block; font-size: 12px; font-weight: 600; color: var(--muted); margin-bottom: 6px; }
.confirm-dialog-typecheck input { width: 100%; padding: 9px 12px; border: 1px solid var(--border); border-radius: 8px; font-size: 13.5px; font-family: inherit; color: var(--ink); background: var(--bg); }
.confirm-dialog-typecheck input:focus { outline: none; border-color: var(--review); }

@keyframes confirm-overlay-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes confirm-dialog-in {
  from { opacity: 0; transform: translateY(6px) scale(0.98); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .confirm-overlay, .confirm-dialog { animation: none; }
}

/* Settings (2026-09-07) — the toggle switch and segmented control the
   Preferences/WhatsApp Groups rows use. Reused as-is by both (a plain
   on/off switch is a plain on/off switch whether it's muting a group
   or a notification preference). */
.switch {
  width: 38px;
  height: 22px;
  border-radius: 12px;
  background: var(--accent);
  position: relative;
  cursor: pointer;
  flex-shrink: 0;
  transition: background 0.15s;
}
.switch::after {
  content: "";
  position: absolute;
  top: 2px;
  right: 2px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--card);
  transition: right 0.15s, left 0.15s;
}
.switch.off { background: var(--border); }
.switch.off::after { right: auto; left: 2px; }
/* A switch representing your own row in Manage Staff — visibly inert,
   since you can't change your own admin/active status. */
.switch.disabled { cursor: default; opacity: 0.5; }

.segmented {
  display: flex;
  border: 1px solid var(--border);
  border-radius: 8px;
  overflow: hidden;
}
.segmented span {
  padding: 7px 14px;
  font-size: 12px;
  font-weight: 600;
  color: var(--muted);
  cursor: pointer;
  border-right: 1px solid var(--border);
}
.segmented span:last-child { border-right: none; }
.segmented span.active { background: var(--ink-fixed); color: #fff; }

/* Settings / Manage Staff full pages (2026-09-09) — these used to open
   in the shared floating detail panel; now they're real pages reached
   via the sidebar (setMode("settings")/setMode("manage-staff")), so
   they get the mockup's own <h1>+subtitle header and a real max-width
   reading column for .panel, instead of just filling whatever width
   the floating card happened to be. */
.settings-page { padding: 40px 48px 48px; }
.settings-page h1 { font-family: "Sora", -apple-system, sans-serif; font-size: 24px; font-weight: 800; margin: 0 0 6px; }
.settings-page .subtitle { color: var(--muted); font-size: 14px; margin-bottom: 28px; }

/* Settings panels (2026-09-07, rebuilt to match DesktopSettings.dc.html
   exactly) — bordered per-section panel + uppercase section-label,
   replacing the earlier plain-row layout. */
.section-label {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 24px 0 10px;
}
.section-label:first-of-type { margin-top: 0; }
.panel {
  max-width: 560px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 20px 24px;
}
.profile-row {
  display: flex;
  align-items: center;
  gap: 14px;
  padding-bottom: 18px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 4px;
}
.profile-row .avatar {
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: var(--accent-tint);
  color: var(--accent-dark);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 16px;
  font-weight: 700;
  flex-shrink: 0;
}
.profile-name { font-weight: 700; font-size: 15px; }
.profile-meta { font-size: 12px; color: var(--muted); margin-top: 2px; }
.admin-tag {
  display: inline-block;
  margin-top: 4px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--accent-dark);
  background: var(--accent-tint);
  padding: 2px 8px;
  border-radius: 10px;
}
.setting-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 20px;
  padding: 16px 0;
  border-bottom: 1px solid var(--border);
  font-size: 13px;
}
.setting-row:last-child { border-bottom: none; }
.setting-label { font-weight: 600; }

/* Billing panel (2026-09-08, Settings) — seat-usage bar, matches the
   design canvas's own .seats-bar-track/.seats-bar-fill exactly. */
.seats-bar-track { height: 6px; border-radius: 999px; background: var(--bg); overflow: hidden; margin-top: 10px; }
.seats-bar-fill { height: 100%; background: var(--accent); border-radius: 999px; }
.setting-desc { color: var(--muted); font-size: 12px; margin-top: 2px; }
.logout-btn {
  padding: 8px 16px;
  border-radius: 7px;
  font-size: 13px;
  font-weight: 600;
  border: 1px solid var(--review);
  color: var(--review);
  background: var(--card);
  cursor: pointer;
  white-space: nowrap;
}
.logout-btn:hover { background: var(--review); color: #fff; }
.setting-select {
  padding: 7px 10px;
  border: 1px solid var(--border);
  border-radius: 7px;
  font-size: 12px;
  color: var(--ink);
  background: var(--card);
  flex-shrink: 0;
}
.export-btn {
  padding: 8px 14px;
  border-radius: 7px;
  font-size: 13px;
  font-weight: 600;
  border: 1px solid var(--accent);
  color: var(--accent);
  background: var(--card);
  cursor: pointer;
  white-space: nowrap;
  text-decoration: none;
  display: inline-block;
}
.group-row, .device-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 20px;
  padding: 14px 0;
  border-bottom: 1px solid var(--border);
  font-size: 13px;
}
.group-row:last-of-type, .device-row:last-of-type { border-bottom: none; }
.group-name { font-weight: 600; display: flex; align-items: center; gap: 8px; }
.group-desc, .device-desc { color: var(--muted); font-size: 12px; margin-top: 2px; }
.current-tag {
  display: inline-block;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--accent-dark);
  background: var(--accent-tint);
  padding: 2px 8px;
  border-radius: 10px;
}
.device-icon { color: var(--muted); flex-shrink: 0; }
.panel-foot-action { display: flex; justify-content: flex-end; padding-top: 16px; margin-top: 4px; border-top: 1px solid var(--border); }
.panel-foot-action span { font-size: 12px; font-weight: 600; color: var(--review); cursor: pointer; }
.disclosure {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 14px 0 0;
  font-size: 13px;
  font-weight: 600;
  color: var(--accent);
  cursor: pointer;
  user-select: none;
}
.disclosure svg { transition: transform 0.15s; flex-shrink: 0; }
.disclosure.open svg { transform: rotate(90deg); }
.group-list { margin-top: 6px; padding-top: 6px; }
.group-row-mini {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  padding: 10px 0 10px 26px;
  border-top: 1px solid var(--border);
  font-size: 13px;
}
