:root {
    /* =======================================================
       CentreMove 2026 brand tokens — the single source of all
       colour in the product. Every Tailwind token, Bootstrap
       override and component rule below reads from these custom
       properties, which is also the tenant-override seam: the
       Tenant Branding page re-declares only the overridden
       --cm-* vars in a per-tenant <style> block and the whole
       app recolours, including .btn-accent and the gradient.
       ======================================================= */
    --cm-white:      #FFFFFF;  /* Pure White — card / panel surfaces */
    --cm-dark:       #192620;  /* Dark Green — titles on white, dark surfaces, text on gold CTAs */
    --cm-mid:        #5C7A65;  /* Mid Green — all body text on white, title keyword highlight on white */
    --cm-light:      #B2BDB7;  /* Light Green — body text on dark green, borders, gradient end */
    --cm-gold:       #C8A96E;  /* Gold — CTA buttons, title keyword highlight on green */
    --cm-gold-hover: #B49863;  /* Gold −10% — .btn-accent hover */
    --cm-offwhite:   #F7F5F0;  /* Off White — text on green surfaces; soft card/section surface */
    --cm-border:     #E0E5E2;  /* Hairline borders / dividers */
    --cm-gradient-angle: 90deg; /* Tenant-overridable gradient direction */
    --cm-font-title: 'Cal Sans';
    --cm-font-body:  'Quicksand';

    /* -------------------------------------------------------
       v2 tokens (05/08/2026). The six above could only recolour
       CentreMove; these separate ROLE from VALUE so a tenant can
       express a different brand. See TenantTheme.cs for the
       rationale on each group.
       ------------------------------------------------------- */

    /* Page background, distinct from card white. Defaults THROUGH
       --cm-white so a tenant who overrides only White still gets a
       matching page. */
    --cm-canvas: var(--cm-white);

    /* Text on a filled surface. Gold never carries white text
       (dark-on-gold ≈ 6.1:1; white-on-gold fails AA). */
    --cm-on-primary: var(--cm-offwhite);
    --cm-on-accent:  var(--cm-dark);

    /* The CTA triple. .btn-accent / .btn-cta / Bootstrap's .btn-primary
       all resolve through these rather than through --cm-gold directly,
       which is what lets a tenant put the CTA on their primary colour
       with light text instead. TenantThemeHead re-points these when the
       tenant's CtaRole is "primary". */
    --cm-cta:       var(--cm-gold);
    --cm-cta-hover: var(--cm-gold-hover);
    --cm-on-cta:    var(--cm-on-accent);

    /* Status — FUNCTIONAL, not brand, but tenant-overridable in v2
       because a navy/mint brand needs its own mint. All four carry DARK
       text: white-on-mint/cyan/amber fails AA hard (axe, 19/05/2026). */
    --cm-success:    #42CC80;
    --cm-on-success: var(--cm-dark);
    --cm-info:       #1CCEF4;
    --cm-on-info:    var(--cm-dark);
    --cm-warning:    #FFBF00;
    --cm-on-warning: var(--cm-dark);
    --cm-danger:     #D8385B;
    --cm-on-danger:  #FFFFFF;

    /* Shape. These are Tailwind's own rounded-md / -lg / -xl values, so
       routing the utilities through them is a no-op for the default
       brand — and makes ~770 existing rounded-* usages tenant-controlled
       without touching a single component. rounded-sm / rounded-full
       deliberately keep Tailwind's values. */
    --cm-radius-md: 6px;
    --cm-radius-lg: 8px;
    --cm-radius-xl: 12px;

    /* Card depth. color-mix tints the lift with the brand's dark token
       so it reads as part of the brand, not a default grey Material
       drop-shadow. Matches the previous hard-coded .panel values. */
    --cm-shadow-card:
        0 1px 2px color-mix(in srgb, var(--cm-dark) 5%, transparent),
        0 6px 18px color-mix(in srgb, var(--cm-dark) 9%, transparent);

    /* Sidebar shell surface. Defaults derive from the primary tokens so
       a tenant that never opens this group still gets a coherent
       sidebar; the active pill is the surface lifted toward white,
       because darkening is invisible on an already-dark surface. */
    --cm-sidebar-bg:         var(--cm-dark);
    --cm-sidebar-ink:        var(--cm-on-primary);
    --cm-sidebar-ink-muted:  var(--cm-light);
    --cm-sidebar-active-bg:  #34403A;
    --cm-sidebar-active-ink: var(--cm-sidebar-ink);

    /* Override Bootstrap's theme colours so every Bootstrap utility class
       (.text-primary, .bg-secondary, .btn-info, …) — which uses
       !important and beats Tailwind + element selectors — resolves to the
       CentreMove palette instead of the Bootstrap defaults.
       Status colours (success/info/warning/danger) are FUNCTIONAL, not
       brand — they keep their v1 values pending separate guidance. */
    --bs-primary-rgb:   25,  38,  32;    /* Dark Green */
    --bs-secondary-rgb: 247, 245, 240;   /* Off White */
    --bs-success-rgb:   66,  204, 128;   /* #42CC80 mint */
    --bs-info-rgb:      28,  206, 244;   /* #1CCEF4 cyan */
    --bs-warning-rgb:   255, 191, 0;     /* #FFBF00 amber */
    --bs-danger-rgb:    216, 56,  91;    /* #D8385B functional danger red */
    --bs-bg-opacity: 1;

    /* Bootstrap link colour — Mid Green (links on white must pass AA;
       gold is ~2.1:1 there). The -rgb triple can't read a var; it
       stays the default-brand mid and does not follow tenant
       overrides — acceptable: it only feeds rarely-used Bootstrap
       text-opacity utilities. */
    --bs-link-color:       var(--cm-mid);
    --bs-link-color-rgb:   92, 122, 101;
    --bs-link-hover-color: var(--cm-dark);
}

html, body {
    font-family: var(--cm-font-body), ui-sans-serif, system-ui, sans-serif;
    font-feature-settings: "rlig" 1, "calt" 1;
    color: var(--cm-mid);
    /* Canvas, not white — for the default brand they are the same value,
       but a tenant can now seat white cards on a tinted page. */
    background: var(--cm-canvas);
}

/* Titles — Cal Sans ships a SINGLE style (display-weight by design):
   never add font-bold / italic expecting a weight change. */
h1, h2, h3, h4, h5, h6 {
    font-family: var(--cm-font-title), sans-serif;
    color: var(--cm-dark);
    letter-spacing: -0.015em;
    font-weight: 400;
}

/* Links on white are MID GREEN (the on-white accent role) — gold text
   on white sits at ~2.1:1 and fails WCAG AA (axe-enforced). Gold
   remains the colour of CTAs (.btn-accent, dark text), icons and
   highlights on green surfaces. */
a, .btn-link {
    color: var(--cm-mid);
}

a:hover, .btn-link:hover {
    color: var(--cm-dark);
}

/* Links in body text get an underline so they pass WCAG 2.2 AA
   link-in-text-block (colour alone isn't enough for colour-blind
   users). Navigation, CTAs and chrome links sit outside <p>/<li>
   so they keep their cleaner look. */
p a, li a {
    text-decoration: underline;
    text-underline-offset: 2px;
}

/* AA: Mid Green sits at ~4.3:1 on the Off White surface (the brand's
   flagged borderline pair) — secondary text INSIDE bg-secondary
   surfaces renders Dark Green instead, axe-enforced. The Mud label /
   helper rules carry !important because MudBlazor's variant-specific
   selectors (.mud-input-label-outlined…) out-specify any reasonable
   override — this is an accessibility floor, not theming. */
.bg-secondary .text-muted-foreground,
.bg-secondary.text-muted-foreground,
.bg-secondary th,
thead.bg-secondary {
    color: var(--cm-dark);
}

.bg-secondary .mud-input-label,
.bg-secondary .mud-input-helper-text {
    color: var(--cm-dark) !important;
}

/* Soft, brand-tinted lift for content panels/cards (dashboard property
   tiles etc.). A resting shadow seats the white card on the Off-White
   page; it deepens on hover. Dark-Green-tinted rather than neutral grey
   so it reads as part of the brand, not a default Material drop-shadow. */
.card-lift {
    box-shadow: var(--cm-shadow-card);
    transition: box-shadow 180ms ease, transform 180ms ease;
}
.card-lift:hover {
    box-shadow: 0 2px 6px color-mix(in srgb, var(--cm-dark) 8%, transparent),
                0 14px 32px color-mix(in srgb, var(--cm-dark) 14%, transparent);
    transform: translateY(-2px);
}

/* Static (non-clickable) content panels — same resting lift as
   .card-lift but no hover movement, so info cards don't jiggle on
   hover. Use .panel for static panels; .card-lift for clickable cards. */
.panel {
    box-shadow: var(--cm-shadow-card);
}

/* Data-table header bar. A solid Mid-Green fill with WHITE text — clearly
   visible on both white panels and the Off-White page, and distinct from
   the gold CTAs elsewhere on these pages. White (not Off-White) text gives
   ~4.7:1 on Mid Green (AA); Off-White would sit just under at ~4.4:1. The
   `.table-head th` rule beats the .bg-secondary th darkening (which would
   otherwise force dark text onto the green bar). */
.table-head {
    background-color: var(--cm-mid);
}
.table-head,
.table-head th {
    color: var(--cm-white);
}

/* Property-workspace active tab pill — solid Mid-Green fill, white text.
   A dedicated class rather than the bg-body utility: the Tailwind Play CDN
   proved unreliable at emitting the custom bg-body utility for the
   dynamically-applied active-tab class, leaving the button white-on-white.
   var(--cm-mid) resolves fine (the same token .table-head uses), so this is
   bulletproof. Hover is pinned so the active pill doesn't change on hover. */
.tab-active,
.tab-active:hover {
    background-color: var(--cm-mid);
    color: var(--cm-white);
}

/* Unified data-table look — every product table carries .cm-table so they
   are visually identical (the green header bar comes from .table-head on
   the <thead>). This standardises cell padding and row separators,
   overriding the per-table differences that crept in; text alignment is
   left to inline text-left/right classes on individual cells. */
.cm-table {
    width: 100%;
    font-size: 0.875rem;
    line-height: 1.25rem;
}
.cm-table th {
    padding: 0.75rem 1rem;
    font-weight: 500;
    vertical-align: middle;
}
.cm-table td {
    padding: 0.75rem 1rem;
    vertical-align: middle;
}
.cm-table tbody tr {
    border: 0;
    border-top: 1px solid var(--cm-border);
}

/* MudDataGrid (Dashboard + Feedback list views) — green header to match
   .table-head. Targets thead th so it's robust to MudBlazor's internal
   classes; the filter/sort popovers render in an overlay outside thead, so
   they're unaffected. `!important` on the background beats MudBlazor's
   sticky-header background (which is more specific under FixedHeader). */
.cm-grid thead th {
    background-color: var(--cm-mid) !important;
}
.cm-grid thead th,
.cm-grid thead th * {
    color: var(--cm-white) !important;
}

/* Make the grid fill its (bounded) flex parent so the header stays pinned at
   the top, the body scrolls inside, and the pager stays pinned at the bottom —
   instead of the whole grid scrolling away. The page markup gives the grid a
   bounded-height flex-column wrapper; these rules turn the grid into a flex
   column whose table-container is the only scroller. Requires FixedHeader on
   the grid for the sticky thead. */
.cm-grid.mud-table {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
}
.cm-grid .mud-table-container {
    flex: 1 1 auto;
    min-height: 0;
    overflow: auto;
}
.cm-grid .mud-table-pagination {
    flex: 0 0 auto;
}

/* The single sanctioned gradient in the product. Stops derive from
   the three brand greens, so a tenant override of --cm-dark/-mid/
   -light re-tints it by construction. No other gradient may exist
   anywhere (the global Hypercolor design standards do not apply —
   CLAUDE.md §Override clauses). */
.bg-brand-gradient {
    background-image: linear-gradient(var(--cm-gradient-angle), var(--cm-dark) 0%, var(--cm-mid) 50%, var(--cm-light) 100%);
}

/* Bootstrap's .btn-primary — the scaffolded Identity buttons (Log in,
   Register, Reset password). Reads the CTA triple so the login page
   follows the tenant's CTA exactly as the in-app buttons do. */
.btn-primary {
    color: var(--cm-on-cta);
    background-color: var(--cm-cta);
    border-color: var(--cm-cta);
}

.btn-primary:hover, .btn-primary:active, .btn-primary:focus {
    background-color: var(--cm-cta-hover);
    border-color: var(--cm-cta-hover);
    color: var(--cm-on-cta);
}

.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
    box-shadow: 0 0 0 0.1rem var(--cm-white), 0 0 0 0.25rem color-mix(in srgb, var(--cm-cta) 40%, transparent);
}

/* ===========================================================
   .btn-accent / .btn-cta — the canonical CTA.
   Apply on <a>, <button>, or pass through MudButton's Class=.
   On hover the BACKGROUND darkens by 10%; the text colour is
   pinned in ALL states. Matches the .btn-primary behaviour used
   by the scaffolded Identity buttons (Log in, Register, etc).

   v2: these resolve through --cm-cta / --cm-on-cta rather than
   --cm-gold / --cm-dark. For the CentreMove brand that is the
   same pair (gold fill, dark text) so nothing moves; for a tenant
   whose CtaRole is "primary" every CTA in the app follows onto
   their primary colour with light text, which was impossible in
   v1. .btn-accent and .btn-cta are the same rule — .btn-accent is
   kept because ~100 call sites use it, .btn-cta is the honest
   name now that the colour is not necessarily the accent.

   The element-tagged hover selectors exist to outrank the global
   `a:hover` rule above so anchor-based CTAs don't pick up the
   link colour on the text.

   USAGE: <a class="btn-accent rounded-md px-4 py-2 text-sm font-medium">
   The .btn-accent class is colour + hover + transition only; the
   caller composes geometry (size, padding, radius) via Tailwind
   utilities. This way the small header CTA and the larger hero
   CTA share one canonical look-and-feel without duplicating any
   colour values.
   =========================================================== */
.btn-accent, .btn-cta {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    background-color: var(--cm-cta);
    color: var(--cm-on-cta);
    border: 1px solid var(--cm-cta);
    font-family: inherit;
    font-weight: 600;
    line-height: 1.5;
    text-decoration: none;
    cursor: pointer;
    transition: background-color 180ms ease, border-color 180ms ease;
}

a.btn-accent:hover, a.btn-accent:focus, a.btn-accent:active,
button.btn-accent:hover, button.btn-accent:focus, button.btn-accent:active,
.mud-button.btn-accent:hover, .mud-button.btn-accent:focus, .mud-button.btn-accent:active,
.btn-accent:hover, .btn-accent:focus, .btn-accent:active,
a.btn-cta:hover, a.btn-cta:focus, a.btn-cta:active,
button.btn-cta:hover, button.btn-cta:focus, button.btn-cta:active,
.mud-button.btn-cta:hover, .mud-button.btn-cta:focus, .mud-button.btn-cta:active,
.btn-cta:hover, .btn-cta:focus, .btn-cta:active {
    background-color: var(--cm-cta-hover);
    border-color: var(--cm-cta-hover);
    color: var(--cm-on-cta);
    text-decoration: none;
    opacity: 1;
}

.btn-accent:focus-visible, .btn-cta:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--cm-cta) 25%, transparent);
}

.btn-accent:disabled,
.btn-accent[disabled],
.btn-accent.mud-button-disabled,
.btn-cta:disabled,
.btn-cta[disabled],
.btn-cta.mud-button-disabled {
    background-color: var(--cm-cta);
    border-color: var(--cm-cta);
    color: var(--cm-on-cta);
    opacity: 0.5;
    cursor: not-allowed;
}

/* A DISABLED gold CTA must not react to the pointer at all. The block above
   can't achieve that on its own: the hover/focus/active rules above are
   element-tagged (`button.btn-accent:hover` = specificity 0,2,1) and so outrank
   a plain `.btn-accent:disabled` (0,2,0) — which meant hovering a disabled
   button darkened it to gold-hover AND reset `opacity: 1`, i.e. the thing lit
   up to look MORE clickable than when idle. Element-tagging these and stacking
   :disabled with the pointer states takes them to 0,3,1, which wins.
   Found 03/08/2026 on the anonymous buyer view's locked "Make an offer". */
a.btn-accent[disabled]:hover, a.btn-accent[disabled]:focus, a.btn-accent[disabled]:active,
button.btn-accent:disabled:hover, button.btn-accent:disabled:focus, button.btn-accent:disabled:active,
button.btn-accent[disabled]:hover, button.btn-accent[disabled]:focus, button.btn-accent[disabled]:active,
.mud-button.btn-accent.mud-button-disabled:hover,
.mud-button.btn-accent.mud-button-disabled:focus,
.mud-button.btn-accent.mud-button-disabled:active,
.btn-accent:disabled:hover, .btn-accent:disabled:focus, .btn-accent:disabled:active,
.btn-accent[disabled]:hover, .btn-accent[disabled]:focus, .btn-accent[disabled]:active,
a.btn-cta[disabled]:hover, a.btn-cta[disabled]:focus, a.btn-cta[disabled]:active,
button.btn-cta:disabled:hover, button.btn-cta:disabled:focus, button.btn-cta:disabled:active,
button.btn-cta[disabled]:hover, button.btn-cta[disabled]:focus, button.btn-cta[disabled]:active,
.mud-button.btn-cta.mud-button-disabled:hover,
.mud-button.btn-cta.mud-button-disabled:focus,
.mud-button.btn-cta.mud-button-disabled:active,
.btn-cta:disabled:hover, .btn-cta:disabled:focus, .btn-cta:disabled:active,
.btn-cta[disabled]:hover, .btn-cta[disabled]:focus, .btn-cta[disabled]:active {
    background-color: var(--cm-cta);
    border-color: var(--cm-cta);
    color: var(--cm-on-cta);
    opacity: 0.5;
    cursor: not-allowed;
}

/* CentreMove-flavoured Bootstrap form-floating inputs used by /Account/** pages.
   Scoped under .auth-panel so the rules don't leak into other Bootstrap
   contexts in the future. */
.auth-panel .form-control {
    border: 1px solid var(--cm-border);
    border-radius: 0.5rem;
    color: var(--cm-dark);
    background-color: var(--cm-white);
}

/* Focus ring and checked state follow the CTA, so they match the Log in
   button sitting right beside them rather than drifting to a colour the
   tenant never chose. Same pair as gold for the default brand. */
.auth-panel .form-control:focus {
    border-color: var(--cm-cta);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--cm-cta) 15%, transparent);
}

.auth-panel .form-floating > label {
    color: var(--cm-mid);
}

.auth-panel .form-floating > .form-control:focus ~ label,
.auth-panel .form-floating > .form-control:not(:placeholder-shown) ~ label {
    color: var(--cm-mid);
}

.auth-panel .form-check-input:checked {
    background-color: var(--cm-cta);
    border-color: var(--cm-cta);
}

.auth-panel hr {
    border-color: var(--cm-border);
    opacity: 1;
}

.auth-panel h1, .auth-panel h2 {
    color: var(--cm-dark);
}

/* Validation errors are FUNCTIONAL red — not brand. Stays raspberry-dark. */
.auth-panel .text-danger {
    color: #B82A4A !important;
}

/* Brand panel behind the /Account/** forms. Reproduces the public waitlist
   page background (wwwroot/waitlist.html body) verbatim so login and waitlist
   read as one surface: a subtle top-left white radial highlight over the
   diagonal sage-green gradient. The two mid/end greens have no --cm-* token
   (they're waitlist-specific tints of Dark Green), so they're inlined here to
   match; the start green is --cm-dark. */
.auth-brand-panel {
    /* Faded-hero treatment matching the 2026 style-sheet cover: the
       lifestyle photo sits beneath a Dark Green scrim so it reads as a
       tinted, faded backdrop. Layers paint top-first: a soft top-left
       highlight, then the green scrim (semi-transparent so the photo
       shows through), then the photo. The panel is `hidden md:flex`, so
       the image is only fetched on desktop. Fallback colour covers the
       load gap / any fetch failure. Tune the scrim alphas for a lighter
       or heavier fade. */
    background:
        radial-gradient(1200px 700px at 12% -10%, rgba(255, 255, 255, 0.06), transparent 60%),
        linear-gradient(158deg, rgba(25, 38, 32, 0.88) 0%, rgba(36, 58, 46, 0.82) 58%, rgba(44, 64, 52, 0.80) 100%),
        url('/brand/moving-hero.jpg') center center / cover no-repeat;
    background-color: var(--cm-dark);
}

.content {
    padding-top: 1.1rem;
}

h1:focus {
    outline: none;
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid #26b050;
}

.invalid {
    outline: 1px solid #e50000;
}

.validation-message {
    color: #e50000;
}

.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

    .blazor-error-boundary::after {
        content: "An error has occurred."
    }

.darker-border-checkbox.form-check-input {
    border-color: #929292;
}

/* Bootstrap renders form-floating placeholders transparent so only the
   floating label shows. A previous override here made them visible and
   right-aligned, which drew the placeholder ON TOP of the label in every
   Identity form field (reported on password reset, 18/07/2026). Do not
   re-add a form-floating placeholder colour override. */
/* ---------------------------------------------------------------------
   Mobile: wide data tables (08fe / mobile audit 04/06/2026)
   Every full-width data table (class "w-full") becomes a horizontal
   scroller on narrow screens instead of clipping or crushing its
   columns — the iPhone 13 Pro Max audit found the searches table
   clipping its last column and TA10/price-paid tables crushing. The
   display:block + nowrap trick turns the table into its own scroll
   container; desktop (>=768px) is untouched. MudBlazor's own grids
   don't use the w-full utility, so they're unaffected.
   --------------------------------------------------------------------- */
@media (max-width: 767px) {
    table.w-full {
        display: block;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
        white-space: nowrap;
    }
    /* Let the descriptive sub-text cells (e.g. the searches "what this
       covers" blurb) keep wrapping so they don't blow the scroll width
       out — only the tabular columns need nowrap. */
    table.w-full p {
        white-space: normal;
    }
}

/* Property Search map needs a REAL height when the page flows (phones, incl.
   landscape) — otherwise its h-full chain resolves to 0 against the now-flowing
   shell and Google Maps renders blank/white. Inside the fixed frame the media
   query below overrides this back to 100% (fill the container). Placed BEFORE
   the @media so the media rule (equal specificity, later, when it matches) wins. */
.search-map-canvas { height: 70vh; }

/* ===========================================================
   Sidebar shell (TenantChrome.Shell = "sidebar").
   Width and the sidebar's own scroll behaviour; the colours come from
   the --cm-sidebar-* tokens via Tailwind's `sidebar` colour group.
   Base rules only — below the frame breakpoint the sidebar is hidden
   entirely (md:flex on the element) and phones use the compact bar +
   the existing MobileNav panel, so there is nothing to lay out here.
   =========================================================== */
.cm-sidebar {
    width: 256px;
    /* A tall nav plus the support card can exceed a short laptop
       viewport, so the column scrolls independently of the page. */
    overflow-y: auto;
    overscroll-behavior: contain;
}

/* An idle nav row sits just under full ink, so the ACTIVE row is
   distinguished by its pill rather than by dimming every other row —
   dropping them all to the muted token makes the nav look disabled.
   A class rather than `text-sidebar-ink/85` because Tailwind cannot
   apply an alpha to a colour that is a bare var(). */
.cm-nav-idle {
    color: color-mix(in srgb, var(--cm-sidebar-ink) 82%, transparent);
}
.cm-nav-idle:hover {
    color: var(--cm-sidebar-ink);
}

/* Every link on a dark surface keeps that surface's ink on hover.
   The global `a:hover` above resolves to the dark brand colour, which is
   near-invisible on a dark panel; the nav rows escape it only because
   .cm-nav-idle:hover outranks it. Anything else that happens to be an
   anchor — the support phone number, Sign out, the user chip — inherits
   the bug, so this is scoped to the SURFACE rather than fixed one link
   at a time, and covers links added later without anyone remembering.

   Add .cm-on-dark to any dark panel outside the sidebar that carries
   links.

   CTAs are excluded by name. `.cm-sidebar a:hover` would tie with
   `a.btn-accent:hover` on specificity and win purely by coming later in
   the file, silently repainting gold buttons' text. */
.cm-sidebar a:not(.btn-accent):not(.btn-cta):hover,
.cm-sidebar a:not(.btn-accent):not(.btn-cta):focus-visible,
.cm-sidebar .btn-link:not(.btn-accent):not(.btn-cta):hover {
    color: var(--cm-sidebar-ink);
}

.cm-on-dark a:not(.btn-accent):not(.btn-cta):hover,
.cm-on-dark a:not(.btn-accent):not(.btn-cta):focus-visible,
.cm-on-dark .btn-link:not(.btn-accent):not(.btn-cta):hover {
    color: var(--cm-on-primary);
}

/* App shell — the fixed-height frame (pinned header/footer + inner-scrolling
   <main>) is applied ONLY when the screen is both wide AND tall enough for it.
   The base markup leaves everything flowing as a normal document; this query
   switches the frame on. Phones fall through to flow — crucially INCLUDING
   landscape, which is wide (clears a width-only breakpoint) but very short, so
   the min-height gate keeps it flowing. Real tablets/desktops (wide + tall)
   get the frame. Only affects pages in MainLayout, not the marketing/Identity
   layouts. */
@media (min-width: 768px) and (min-height: 600px) {
    .app-shell {
        height: 100vh;
        overflow: hidden;
    }
    .app-shell-main {
        overflow-y: auto;
    }
    /* The sidebar variant is a ROW frame: the sidebar is a fixed column
       and the page column beside it owns the vertical stack. Same
       gate as the top-bar frame, so a short landscape phone still
       flows as a document rather than being trapped in 100vh. */
    .app-shell--sidebar {
        height: 100vh;
        overflow: hidden;
    }
    .app-shell--sidebar .cm-sidebar {
        height: 100vh;
    }
    /* Dashboard (and similar) root: its own scroll region inside the frame. */
    .app-shell-scroll {
        height: 100%;
        min-height: 0;
        overflow-y: auto;
    }
    /* Sticky sub-header (e.g. the dashboard greeting) within that scroll. */
    .app-shell-sticky {
        position: sticky;
        top: 0;
        z-index: 10;
    }
    /* Pages that use `min-h-screen` as their root would force a viewport-tall
       block + an extra scroll inside the frame — scope it to fill the scroll
       area instead. (On phones min-h-screen keeps meaning 100vh, which is what
       we want for the flowing page.) */
    .app-shell-main > .min-h-screen {
        min-height: 100%;
    }
    /* Property Search: fixed-frame layout inside the shell — the map fills its
       container and the list scrolls. On phones the page flows instead (base
       rules), where the map gets an explicit height. */
    .search-page { height: 100%; min-height: 0; overflow: hidden; }
    .search-map-fill { flex: 1 1 0%; min-height: 0; }
    .search-map-canvas { height: 100%; }
    .search-scroll-fill { flex: 1 1 0%; min-height: 0; overflow: auto; }
}

/* Colour swatch that IS the picker (Design configuration).
   A native `input[type=color]` renders as a bordered button with a fat
   inner margin by default, and differently in every browser; these rules
   strip it back to a flat tile the same size as the plain swatch it
   replaced, so the control reads as a colour chip rather than a form
   field. Clicking it opens the OS picker — no JS, nothing to break in an
   InteractiveServer circuit. */
.cm-swatch {
    -webkit-appearance: none;
    -moz-appearance: none;
    appearance: none;
    width: 2rem;
    height: 2rem;
    padding: 0;
    border: 1px solid var(--cm-border);
    border-radius: var(--cm-radius-md, 8px);
    background: transparent;
    cursor: pointer;
    transition: box-shadow 180ms, border-color 180ms;
}

/* The two vendor-specific inner wrappers that hold the actual colour. */
.cm-swatch::-webkit-color-swatch-wrapper { padding: 0; }
.cm-swatch::-webkit-color-swatch { border: none; border-radius: 6px; }
.cm-swatch::-moz-color-swatch { border: none; border-radius: 6px; }

.cm-swatch:hover { border-color: var(--cm-mid); }

.cm-swatch:focus-visible {
    outline: none;
    border-color: var(--cm-accent);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--cm-accent) 35%, transparent);
}

/* Money box (admin price list).
   A `number` input's stepper arrows take ~20px of a 96px box and are no use
   on a price — nobody sets £15.02 by clicking up 1,502 times — so they are
   removed and the box keeps its full width for the figure. The input stays
   type=number for the numeric keypad on mobile and for browser validation. */
.cm-num::-webkit-outer-spin-button,
.cm-num::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

.cm-num {
    -moz-appearance: textfield;
    appearance: textfield;
}

/* Style guide — printing.
   The guide is a document people hand to a designer or attach to a
   proposal, so "Print / PDF" has to produce something that reads as one.
   Controls and app chrome come out; swatches must keep their fill, which
   browsers strip by default when printing backgrounds. */
@media print {
    .cm-print-hide { display: none !important; }

    .app-shell, .app-shell-main, aside, header, footer { display: revert; }
    .cm-sidebar, .app-shell--sidebar > aside { display: none !important; }

    /* Without this every swatch prints as an empty outline, which makes the
       whole document pointless. */
    * {
        -webkit-print-color-adjust: exact !important;
        print-color-adjust: exact !important;
    }

    /* Keep a token's swatch, name and description on one page. */
    .rounded-xl > div { break-inside: avoid; }
    h2 { break-after: avoid; }

    /* Each section of the style guide starts a fresh page, so the printed
       document reads as chapters rather than as one long scroll cut at
       arbitrary points. The cover gets a page to itself. */
    .cm-guide-section { break-before: page; break-inside: avoid-page; }
    [data-testid="guide-cover"] { break-after: page; }
}

/* ══ "WHAT'S NEXT" PANEL (feedback E46328A6, 18/08/2026) ═══════════════════
   A persistent, draggable, collapsible guide. Brand rules that shape it:
   flat surfaces, rounded-md, no glassmorphism — so the see-through effect is
   plain opacity with NO backdrop blur, and it goes fully solid the moment the
   panel is engaged with. Text over arbitrary page content has to be readable
   when you are actually reading it.

   z-index sits BELOW the modal layer (onboarding modal 2000, dropdown
   backdrops 1400) so dialogs still cover it, and above ordinary page content. */
.cm-whats-next {
    position: fixed;
    right: 16px;
    bottom: 16px;
    z-index: 1200;
    width: 340px;
    max-width: calc(100vw - 32px);
    background: var(--cm-white);
    border: 1px solid var(--cm-border);
    border-radius: 8px;                 /* rounded-md */
    box-shadow: 0 6px 20px rgba(25, 38, 32, 0.12);
    font-family: var(--cm-font-body);
    opacity: 0.85;                      /* idle — you can see what is underneath */
    transition: opacity 180ms ease, box-shadow 180ms ease;
    overflow: hidden;
}

/* Engaged — fully readable. Also while dragging, so it does not ghost as it
   moves under the cursor. */
.cm-whats-next:hover,
.cm-whats-next:focus-within,
.cm-whats-next.is-dragging {
    opacity: 1;
    box-shadow: 0 10px 28px rgba(25, 38, 32, 0.18);
}

.cm-whats-next.is-dragging { transition: none; user-select: none; }

/* Header doubles as the drag handle. */
.cm-wn-head {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 10px 12px;
    background: var(--cm-dark);
    color: var(--cm-offwhite);
    cursor: grab;
    touch-action: none;                 /* pointer events, not scrolling */
}
.cm-whats-next.is-dragging .cm-wn-head { cursor: grabbing; }

.cm-wn-title { font-family: var(--cm-font-title); font-size: 0.95rem; flex: 1; }

.cm-wn-count {
    background: var(--cm-gold);
    color: var(--cm-dark);
    border-radius: 999px;
    font-size: 0.7rem;
    font-weight: 700;
    padding: 1px 7px;
    min-width: 20px;
    text-align: center;
}

.cm-wn-toggle {
    background: transparent;
    border: 0;
    color: var(--cm-light);
    cursor: pointer;
    display: inline-flex;
    padding: 2px;
}
.cm-wn-toggle:hover { color: var(--cm-offwhite); }
.cm-wn-toggle:focus-visible { outline: 2px solid var(--cm-gold); outline-offset: 2px; }

.cm-wn-body { padding: 12px; max-height: 60vh; overflow-y: auto; }

/* Journey pills. */
.cm-wn-pills { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 10px; }

.cm-wn-pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    background: var(--cm-offwhite);
    border: 1px solid var(--cm-border);
    border-radius: 999px;
    color: var(--cm-mid);
    font-size: 0.75rem;
    padding: 3px 10px;
    cursor: pointer;
    font-family: inherit;
}
.cm-wn-pill:hover { border-color: var(--cm-light); }
.cm-wn-pill.is-active {
    background: var(--cm-dark);
    border-color: var(--cm-dark);
    color: var(--cm-offwhite);
}
.cm-wn-pill:focus-visible { outline: 2px solid var(--cm-gold); outline-offset: 2px; }
.cm-wn-pill-count { font-weight: 700; }

.cm-wn-address {
    margin: 0 0 10px;
    font-size: 0.75rem;
    color: var(--cm-mid);
}

/* Timeline — hand-rolled rather than MudTimeline. MudTimeline appears nowhere
   else in this codebase, and this project has form for MudBlazor components
   failing silently under InteractiveServer (see the native dropdown). A dot,
   a connector and two lines of text do not justify that risk. */
.cm-wn-timeline { list-style: none; margin: 0; padding: 0; }

.cm-wn-step {
    position: relative;
    display: flex;
    gap: 10px;
    padding: 0 0 14px 0;
}

/* The connector, drawn from each dot down to the next. Not on the last one. */
.cm-wn-step:not(:last-child)::before {
    content: "";
    position: absolute;
    left: 5px;
    top: 14px;
    bottom: 0;
    width: 2px;
    background: var(--cm-border);
}

.cm-wn-dot {
    flex: 0 0 auto;
    width: 12px;
    height: 12px;
    margin-top: 3px;
    border-radius: 50%;
    border: 2px solid var(--cm-light);
    background: var(--cm-white);
    z-index: 1;
}
.cm-wn-step.is-done .cm-wn-dot { background: var(--cm-mid); border-color: var(--cm-mid); }

.cm-wn-step-body { min-width: 0; }

.cm-wn-step-title {
    display: block;
    font-size: 0.82rem;
    font-weight: 600;
    color: var(--cm-dark);
}
.cm-wn-step-title.is-link { color: var(--cm-mid); text-decoration: underline; }
.cm-wn-step-title.is-link:hover { color: var(--cm-dark); }
.cm-wn-step.is-done .cm-wn-step-title { color: var(--cm-mid); font-weight: 400; }

.cm-wn-step-detail {
    margin: 2px 0 0;
    font-size: 0.72rem;
    line-height: 1.4;
    color: var(--cm-mid);
}

.cm-wn-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding-top: 8px;
    border-top: 1px solid var(--cm-border);
}
.cm-wn-foot-link { font-size: 0.75rem; color: var(--cm-mid); font-weight: 600; }

.cm-wn-reset {
    background: transparent;
    border: 0;
    padding: 0;
    font-family: inherit;
    font-size: 0.7rem;
    color: var(--cm-light);
    cursor: pointer;
}
.cm-wn-reset:hover { color: var(--cm-mid); text-decoration: underline; }

/* Collapsed — just the heading, which is the whole point of it being
   dismissable-but-not-gone. */
.cm-whats-next.is-collapsed { width: auto; }
.cm-whats-next.is-collapsed .cm-wn-body { display: none; }

/* MOBILE: docked to the bottom, full width, never dragged. A draggable panel on
   a 375px screen is most of the screen and fights the scroll. JS refuses to drag
   below this breakpoint; this makes the layout agree. */
@media (max-width: 640px) {
    .cm-whats-next {
        left: 0 !important;
        right: 0 !important;
        bottom: 0 !important;
        top: auto !important;
        width: 100%;
        max-width: 100%;
        border-radius: 8px 8px 0 0;
        opacity: 1;                     /* no see-through where it is docked */
    }
    .cm-wn-head { cursor: default; }
    .cm-wn-body { max-height: 45vh; }
}

/* Someone who has asked for less motion gets none. */
@media (prefers-reduced-motion: reduce) {
    .cm-whats-next { transition: none; }
}

/* Agent-claimed seller (feedback E46328A6): the owner's checklist is the same
   one, but part of it may be the agency's job. Naming the agency explains why
   some rows are not theirs to chase. */
.cm-wn-agency {
    display: flex;
    align-items: center;
    gap: 5px;
    margin: -6px 0 10px;
    font-size: 0.72rem;
    color: var(--cm-mid);
}

/* A delegated step is DONE as far as the owner is concerned, but visually
   distinct from something they finished themselves — a gold ring rather than a
   filled dot, so "someone else has this" does not read as "you did this". */
.cm-wn-step.is-delegated .cm-wn-dot {
    background: var(--cm-white);
    border-color: var(--cm-gold);
}
.cm-wn-step.is-delegated .cm-wn-step-title { color: var(--cm-mid); font-weight: 400; }
.cm-wn-step.is-delegated .cm-wn-step-detail { font-style: italic; }

/* Empty state — the ways IN, for someone with nothing outstanding. Deliberately
   not styled as a timeline: these are choices, not a sequence, and numbering or
   connecting them would imply an order that does not exist. */
.cm-wn-empty {
    margin: 0 0 10px;
    font-size: 0.78rem;
    color: var(--cm-mid);
}

.cm-wn-actions {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.cm-wn-actions a {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 9px 10px;
    border: 1px solid var(--cm-border);
    border-radius: 6px;
    background: var(--cm-offwhite);
    text-decoration: none;
    color: var(--cm-dark);
}
.cm-wn-actions a:hover { border-color: var(--cm-gold); background: var(--cm-white); }
.cm-wn-actions a:focus-visible { outline: 2px solid var(--cm-gold); outline-offset: 2px; }

.cm-wn-action-title { display: block; font-size: 0.82rem; font-weight: 600; }
.cm-wn-action-detail { display: block; font-size: 0.72rem; color: var(--cm-mid); }

/* ══════════════════════════════════════════════════════════════════════════
   PANEL VOCABULARY — the estate-agency + listing design pass (19/08/2026)
   ══════════════════════════════════════════════════════════════════════════

   The problem this solves: every content panel across the /agency pages and
   the buyer's listing view was the same string —

       rounded-xl border border-border bg-white p-6 panel

   — repeated some twenty-five times, with the heading treatment
   (`h2 text-lg tracking-tight` + `p text-muted-foreground text-sm mt-1`)
   re-typed beneath each one. Three costs followed:

     1. NO HIERARCHY. Six identical boxes down a page weigh the same, so the
        eye has nowhere to start. "Add your listings" (the thing an agent came
        to do) looked exactly like "Your agency's details" (a thing they will
        read once).
     2. DRIFT. The rhythm went mt-6 on one page and mb-6 on the next; empty
        states were p-12 here and p-10 there; the h1 was text-3xl on three
        pages and text-2xl on four. Nothing was wrong enough to file, and it
        all added up to looking unconsidered.
     3. A panel was a padded box with a paragraph in it, never a titled
        object — because the title had no rule under it, only whitespace.

   So: name the parts. A .cm-panel is a SURFACE and carries no padding; its
   padding belongs to the blocks inside it, which lets the hairline between
   head and body run edge to edge rather than floating inset. That full-bleed
   rule is the whole point — it is what turns a box into a titled panel, and
   it matches .cm-wn-head, the What's-Next panel that already reads this way.

   Every block takes its rule from border-TOP, with the first child's reset in
   one place. Consequence worth knowing: a head that renders alone (the pure
   navigation panels on /agency, which are a title, a sentence and a link)
   correctly gets no rule at all, with no is-only modifier to remember. The
   rule always means "content follows".
*/

.cm-panel {
    background: var(--cm-white);
    border: 1px solid var(--cm-border);
    border-radius: var(--cm-radius-xl);
    box-shadow: var(--cm-shadow-card);
}

/* The gold spine. ONE per page — the primary action for that screen, and
   nothing else. Gold already means "act" in this brand (it is the .btn-accent
   fill and nothing else uses it), so borrowing it as a 3px edge buys
   hierarchy without introducing a colour. The discipline is the feature: a
   second spine on a page makes the first one meaningless.

   An inset box-shadow rather than border-left, for two reasons — it follows
   the 12px corner radius instead of cutting a square notch across it, and it
   costs no layout, so a panel does not shift 2px when it becomes the lead
   one. .cm-shadow-card is re-declared because box-shadow does not cascade
   additively. */
.cm-panel--lead {
    box-shadow: var(--cm-shadow-card), inset 3px 0 0 var(--cm-gold);
}

/* Secondary/informational panel — Off-White rather than white, for the block
   that explains rather than the block you act in. Note that any muted text
   inside picks up the AA floor from the .bg-secondary rules above only if it
   also carries bg-secondary; on this class use text-mid-green explicitly. */
.cm-panel--quiet { background: var(--cm-offwhite); }

.cm-panel-head,
.cm-panel-body,
.cm-panel-section { border-top: 1px solid var(--cm-border); }
.cm-panel > :first-child { border-top: none; }

/* Title and description left, action right, wrapping to two rows on narrow
   viewports rather than crushing the button. */
.cm-panel-head {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
    padding: 16px 20px;
}

.cm-panel-body    { padding: 18px 20px; }
.cm-panel-section { padding: 16px 20px; }

/* Tighter than the old p-6 (24px) on purpose: with a rule doing the
   separating, the padding no longer has to. */
.cm-panel-head-text { min-width: 0; }

.cm-panel-title {
    font-family: var(--cm-font-title), sans-serif;
    font-size: 1.0625rem;
    line-height: 1.25;
    font-weight: 400;              /* Cal Sans has ONE weight — see CLAUDE.md */
    color: var(--cm-dark);
    margin: 0;
}

/* max-width in ch, not the old max-w-2xl: the measure should follow the text
   size, and 62ch is about the point where a line stops being comfortable.
   Explicit margin because Bootstrap's reboot puts 1rem under a bare <p> and
   this element has no Tailwind mb-* to outrank it. */
.cm-panel-note {
    font-size: 0.8125rem;
    line-height: 1.5;
    color: var(--cm-mid);
    margin: 4px 0 0;
    max-width: 62ch;
}

/* The small uppercase micro-label ("MARKETED BY", "OPENING HOURS"). Mid Green,
   never Light Green — Light on white is 1.9:1 and fails AA outright. */
.cm-eyebrow {
    display: block;
    font-size: 0.6875rem;
    line-height: 1;
    font-weight: 700;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--cm-mid);
    margin: 0;
}

/* Label-left / value-right row, as used by opening hours and key facts.
   tabular-nums so a column of times lines up on the decimal rather than
   shuffling by a pixel per glyph. Works on a dl/dt/dd or on plain divs. */
.cm-datarow {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    font-size: 0.8125rem;
}
.cm-datarow + .cm-datarow { margin-top: 6px; }
.cm-datarow > :first-child { color: var(--cm-mid); margin: 0; }
.cm-datarow > :last-child {
    color: var(--cm-dark);
    margin: 0;
    font-variant-numeric: tabular-nums;
    text-align: right;
}
/* A closed day is still information, but it is not the information someone is
   looking for — drop it back to the label's weight. */
.cm-datarow--off > :last-child { color: var(--cm-mid); }

/* ── Contact rows ───────────────────────────────────────────────────────
   A full-width tappable row per contact method, deliberately NOT two small
   buttons side by side: this block lives in the ~320px left column of the
   listing page, where "Call 01482 000000" and "Email" on one line wrap into
   a mess at the first slightly-long number. Full width also gives a phone a
   target worth the name.

   Same shape as .cm-wn-actions a, so the two read as one family. */
.cm-contact {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 12px;
    border: 1px solid var(--cm-border);
    border-radius: var(--cm-radius-md);
    background: var(--cm-offwhite);
    color: var(--cm-dark);
    font-size: 0.875rem;
    font-weight: 600;
    text-decoration: none;
}
.cm-contact + .cm-contact { margin-top: 8px; }
.cm-contact:hover {
    border-color: var(--cm-gold);
    background: var(--cm-white);
    color: var(--cm-dark);
    text-decoration: none;
}
.cm-contact:focus-visible { outline: 2px solid var(--cm-gold); outline-offset: 2px; }
.cm-contact-icon { color: var(--cm-mid); flex: 0 0 auto; display: flex; }
.cm-contact-text { min-width: 0; }
.cm-contact-sub {
    display: block;
    font-size: 0.72rem;
    font-weight: 400;
    color: var(--cm-mid);
}

/* ── Open-now flag ──────────────────────────────────────────────────────
   "Open now · until 5.30pm" against the branch's own hours. The dot carries
   the state and the words repeat it, so it does not rely on colour alone.
   Functional mint for open (a decorative 7px dot, not text, so the AA text
   rules do not apply); Mid Green when closed. */
.cm-openflag {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--cm-dark);
}
.cm-openflag-dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--cm-mid);
    flex: 0 0 auto;
}
.cm-openflag.is-open .cm-openflag-dot { background: var(--cm-success); }
.cm-openflag-sub { font-weight: 400; color: var(--cm-mid); }

/* ── Page header ────────────────────────────────────────────────────────
   One scale for every page title, because they had drifted to text-3xl on
   three pages and text-2xl on four for no reason anybody could name. */
.cm-page-head { margin-bottom: 24px; }
.cm-page-title {
    font-family: var(--cm-font-title), sans-serif;
    font-size: 1.75rem;
    line-height: 1.15;
    font-weight: 400;
    letter-spacing: -0.015em;
    color: var(--cm-dark);
    margin: 0;
}
@media (min-width: 768px) {
    .cm-page-title { font-size: 2rem; }
}
.cm-page-note {
    font-size: 0.875rem;
    line-height: 1.55;
    color: var(--cm-mid);
    margin: 6px 0 0;
    max-width: 68ch;
}


/* ── A brand-scoped block ───────────────────────────────────────────────────
   AgencyBrandScopeStyle re-declares the --cm-* vars on an element carrying
   data-cm-brand, which re-tints everything inside it because every Tailwind
   token resolves through those vars (see IAgencyBrandScope for why the buyer's
   listing view brands two blocks rather than the page).

   Colours need nothing further. FONTS do, and only the body one: --cm-font-body
   is applied in exactly one place, on `body`, so a scoped override of the var
   changes nothing on its own — the subtree inherits the family from <body>,
   not the variable. Re-asserting it here is what makes an agency's body font
   reach its own blocks.

   Headings need no equivalent rule: the h1–h6 rule and .cm-panel-title already
   declare font-family: var(--cm-font-title), so they pick the scoped value up
   by themselves.

   Static and keyed on the attribute, not on a tenant, so it costs nothing
   per-tenant and cannot drift from the emitted CSS. */
[data-cm-brand] {
    font-family: var(--cm-font-body), ui-sans-serif, system-ui, sans-serif;
}

/* ── .cm-panel--dark : the agency's own surface ─────────────────────────────
   The Marketed-by block on a buyer's listing, painted in the marketing agency's
   DARK colour rather than white (19/08/2026, second pass).

   The first attempt tinted this card's text and borders with the agency's
   palette and left it white, which was reported back as "barely looks any
   different" — and it was right: navy-ish ink on white beside dark-green ink on
   white is a difference you have to be told about. A surface colour is not. One
   dark block in a column of white ones reads as "this part is theirs" before you
   have read a word of it.

   Tinted by whoever's theme is in scope (see IAgencyBrandScope), so an agency on
   the CentreMove default gets Dark Green and MyPad gets their navy — one design,
   many brands, no per-tenant CSS.

   Text follows the brand's own on-dark rules (CLAUDE.md §Palette): Off-White for
   anything that matters, Light for supporting text. Both pass AA on the two
   surfaces in use — Light on MyPad navy is ~5.6:1, Light Green on Dark Green
   ~7:1. No glass, no blur: the raised rows are a solid lighter shade, which is
   what --cm-sidebar-active-bg already means (a raised row on a dark surface). */
.cm-panel--dark {
    background: var(--cm-dark);
    border-color: var(--cm-dark);
    color: var(--cm-on-primary);

    /* A DEEPER shadow than .cm-panel's, and not an arbitrary one: measured.
       Reported as "I don't think it has the same box-shadow as the rest of the
       panels" — and the declared rule was in fact byte-identical, inherited from
       .cm-panel. The difference is perceptual. A shadow works by darkening the
       page AROUND the card; beside a white card that is an obvious step, but
       beside a near-black card the eye has just adapted to a very dark region and
       a 9% darkening of the adjacent Off-White is swamped by the card's own edge.
       So the same shadow reads as no shadow. Elevation has to be tuned per
       surface, which is what every considered design system does.

       18%/22% was picked by rendering 12/16, 18/22 and 24/30 beside a white
       .cm-panel and choosing the one whose apparent lift matched: 12/16 was still
       faint, 24/30 was heavier than the reference.

       It also fixes a tint bug. --cm-shadow-card is declared on :root, and a
       custom property's nested var() resolves where it is DECLARED, not where it
       is used — so --cm-shadow-card bakes in the default --cm-dark and a tenant
       scope cannot re-tint it (only an explicit ShadowCard override can, which
       TenantThemeCss already emits). Here var(--cm-dark) is used directly in the
       rule, so it resolves in the element's context: MyPad's card casts a navy
       shadow and CentreMove's a dark-green one. */
    box-shadow:
        0 1px 3px color-mix(in srgb, var(--cm-dark) 18%, transparent),
        0 8px 24px color-mix(in srgb, var(--cm-dark) 22%, transparent);
}

/* Section rules have to invert too — the hairline border colour is invisible on
   a dark ground. A mix of the ink rather than a new token, so it follows a
   tenant's Off-White without needing one. */
.cm-panel--dark .cm-panel-section,
.cm-panel--dark .cm-panel-head,
.cm-panel--dark .cm-panel-body {
    border-top-color: color-mix(in srgb, var(--cm-on-primary) 16%, transparent);
}

.cm-panel--dark .cm-eyebrow { color: var(--cm-light); }
.cm-panel--dark .cm-panel-title { color: var(--cm-on-primary); }

/* Label left / value right: the label is supporting, the value is the answer. */
.cm-panel--dark .cm-datarow > :first-child { color: var(--cm-light); }
.cm-panel--dark .cm-datarow > :last-child { color: var(--cm-on-primary); }
.cm-panel--dark .cm-datarow--off > :last-child { color: var(--cm-light); }

/* A raised contact row on a dark surface. */
.cm-panel--dark .cm-contact {
    background: var(--cm-sidebar-active-bg);
    border-color: transparent;
    color: var(--cm-on-primary);
}
.cm-panel--dark .cm-contact:hover {
    background: color-mix(in srgb, var(--cm-sidebar-active-bg) 80%, var(--cm-on-primary));
    border-color: var(--cm-gold);
    color: var(--cm-on-primary);
}
.cm-panel--dark .cm-contact-icon,
.cm-panel--dark .cm-contact-sub { color: var(--cm-light); }

.cm-panel--dark .cm-openflag { color: var(--cm-on-primary); }
.cm-panel--dark .cm-openflag-sub { color: var(--cm-light); }

/* The agency's logo, seated on white.

   Not decoration — a correctness fix. An agency logo is whatever ink the agency
   uses, and MyPad's is near-navy: on a navy panel it disappears entirely. A
   solid white tile makes any logo legible on any surface colour, which no
   filter-based trick can promise for a coloured mark. It also reads as
   deliberate, the way a partner badge does. */
.cm-logo-tile {
    background: #FFFFFF;
    border-radius: var(--cm-radius-md);
    padding: 6px 8px;
    display: inline-flex;
    align-items: center;
    flex: 0 0 auto;
}

/* ── Honeypot field ─────────────────────────────────────────────────────────
   A real input that no human sees, on the anonymous agent-enquiry form. A bot
   that fills every field it finds fills this one too, and the server discards
   the submission.

   Positioned OFF-SCREEN rather than display:none or type="hidden", both of which
   a bot can trivially detect and skip — the point is that it looks like an
   ordinary field to a scraper.

   The markup pairs this with aria-hidden and tabindex="-1", which matters more
   than the styling does: without them a screen reader announces the field and a
   keyboard user tabs into it, and a genuine enquiry would then be silently
   discarded with nobody the wiser. */
.cm-hp {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
    opacity: 0;
}

/* ── Overlay scrim + dismiss, and WHY they are classes ───────────────────────
   Tailwind's opacity modifier does not work on our brand tokens. Every --cm-*
   token is a plain hex var and the Tailwind config carries no <alpha-value>
   placeholder, so `bg-foreground/50` and `text-primary-foreground/70` compile to
   a declaration the browser DROPS — the element then falls back to whatever it
   inherits.

   Both failures were visible in one screenshot (feedback 2FB359EC, 21/09/2026):
   the checkout panel had NO backdrop dim, and its "Cancel ×" inherited the
   page's body colour, which on a tenant with a navy palette put navy text on the
   dark-green panel header at about 1.3:1. The fee label beside it was fine
   because `text-accent` carries no modifier — the same header showed both the
   working and the broken case.

   color-mix is the fix and is already how the card shadow tints itself, so the
   tenant override seam is unaffected: these follow --cm-dark and --cm-on-primary
   like everything else. */
.cm-scrim {
    background: color-mix(in srgb, var(--cm-dark) 50%, transparent);
}

.cm-overlay-dismiss {
    color: var(--cm-on-primary);
    opacity: 0.8;
}
.cm-overlay-dismiss:hover,
.cm-overlay-dismiss:focus-visible {
    opacity: 1;
    text-decoration: underline;
}

/* ===========================================================
   Messages — the inbox page, the header dropdown and the
   property Communications tab (24/09/2026 restyle).

   Every colour is derived from the --cm-* tokens, so a tenant
   theme recolours the whole inbox (MyPad: navy bubbles, mint
   accents) with nothing tenant-specific here. Tints are
   color-mix, not Tailwind's /opacity modifier: the modifier does
   nothing on the var()-backed colours in the CDN build.

   The page surface is its own class rather than .bg-secondary on
   purpose: `.bg-secondary .text-muted-foreground` forces DARK text
   on every descendant (an AA floor for text sitting on off-white),
   and the white panel inside would lose its muted hierarchy.
   =========================================================== */
.cm-inbox-page {
    background: var(--cm-offwhite);
}

/* Party avatars. Three tones cycle by party number (see
   MessagingDisplay.AvatarTone) so a party keeps its colour across
   the list, the thread header and the dropdown. */
.cm-avatar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    border-radius: 9999px;
    font-weight: 600;
    color: var(--cm-dark);
}
.cm-avatar-a { background: color-mix(in srgb, var(--cm-mid) 26%, var(--cm-white)); }
.cm-avatar-b { background: var(--cm-gold); color: var(--cm-on-accent); }
.cm-avatar-c { background: color-mix(in srgb, var(--cm-light) 45%, var(--cm-white)); }

/* Inbox rows. The selected row wears the same surface as the property
   card at the top of the open thread (off-white + hairline border), so the
   two read as a pair, plus a primary-coloured bar on its leading edge.
   Both are inset shadows, so the row's size never moves. Hover is a
   lighter wash of the same colour, so it can't be mistaken for selection. */
.cm-thread-row {
    /* The reset lives here, not in .bg-transparent / .border-0 classes:
       Bootstrap ships both as !important, which silently beat the
       selected and hover backgrounds below (24/09/2026). */
    background: transparent;
    border: 0;
    transition: background-color 180ms ease;
}
.cm-thread-row-divided {
    border-bottom: 1px solid var(--cm-border);
}
.cm-thread-row:hover {
    background: color-mix(in srgb, var(--cm-offwhite) 45%, var(--cm-white));
}
.cm-thread-row[aria-current="true"] {
    background: var(--cm-offwhite);
    box-shadow: inset 4px 0 0 var(--cm-dark), inset 0 0 0 1px var(--cm-border);
}

/* Soft accent chips: "2 unread" on the page, "Your sale" on the
   property card. Dark text on a pale wash of the accent reads on
   any tenant's accent, where text in the accent itself would not
   (gold on white is ~2.1:1). */
.cm-chip-accent-soft {
    background: color-mix(in srgb, var(--cm-gold) 30%, var(--cm-white));
    color: var(--cm-dark);
}
.cm-chip-accent-outline {
    border: 1px solid var(--cm-gold);
    background: color-mix(in srgb, var(--cm-gold) 12%, var(--cm-white));
    color: var(--cm-dark);
}

/* Message bubbles. Mine in the primary colour, theirs in a pale
   wash of the body colour so it separates from the white panel. */
/* 24/09/2026: theirs is the brand's tint — the same 12% accent over
   off-white the chat background used before it went white (CentreMove's
   beige, MyPad's pale aqua) — with a firm border and the house card shadow
   to lift it off the pattern. The border is a mix of the body colour rather
   than --cm-border, which is too faint to hold an edge against the pattern.
   Each bubble carries its own time (.cm-bubble-meta), so no small text sits
   on the pattern. */
.cm-bubble {
    border-radius: var(--cm-radius-xl);
    padding: 0.625rem 1rem 0.5rem;
    font-size: 0.9375rem;
    line-height: 1.45;
    overflow-wrap: anywhere;
}
.cm-bubble-in {
    background: color-mix(in srgb, var(--cm-gold) 12%, var(--cm-offwhite));
    border: 1px solid color-mix(in srgb, var(--cm-mid) 38%, var(--cm-white));
    box-shadow: var(--cm-shadow-card);
    color: var(--cm-dark);
}
.cm-bubble-out {
    background: var(--cm-dark);
    border: 1px solid var(--cm-dark);
    color: var(--cm-on-primary);
}
/* Time inside the bubble. Body green on the tint is ~4.0:1, under AA for
   small text, so theirs takes the darker mix (~7.4:1); on the dark bubble
   a light mix of its own text colour, ~10:1. */
.cm-bubble-in .cm-bubble-meta {
    color: color-mix(in srgb, var(--cm-dark) 50%, var(--cm-mid));
}
.cm-bubble-out .cm-bubble-meta {
    color: color-mix(in srgb, var(--cm-on-primary) 78%, var(--cm-dark));
}
/* The day divider on the pattern: the same white-and-border treatment. */
.cm-day-pill {
    background: var(--cm-white);
    border: 1px solid color-mix(in srgb, var(--cm-mid) 38%, var(--cm-white));
    color: var(--cm-dark);
}
.cm-bubble-out a,
.cm-bubble-out a:hover {
    color: var(--cm-on-primary);
}

/* The thread's ⋯ menu rows. Own class rather than borrowing .menu-item,
   which lives in UserMenu's inline <style> and exists only while that
   component is on the page. One class on anchors and buttons alike, with
   alignment set explicitly (a button centres its text by default). */
.cm-menu-item {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    width: 100%;
    padding: 9px 14px;
    border: 0;
    background: transparent;
    text-align: left;
    text-decoration: none;
    font: inherit;
    font-size: 14px;
    color: var(--cm-dark);
    cursor: pointer;
}
.cm-menu-item:hover {
    background: var(--cm-offwhite);
    color: var(--cm-dark);
}
.cm-menu-item > span {
    flex: 1;
    text-align: left;
}
.cm-menu-item-danger,
.cm-menu-item-danger:hover {
    color: var(--cm-danger);
}
.cm-menu-item-muted,
.cm-menu-item-muted:hover {
    color: var(--cm-mid);
    background: transparent;
    cursor: default;
}

/* The conversation pane's illustrated background (24/09/2026).

   A WHITE ground with the line drawings in the tenant's own tint: beige for
   CentreMove, a soft blue for MyPad. Done in CSS, not in the artwork:

   - the tile is a greyscale version of the drawing, contrast-stretched so
     the ground is pure white and the strongest line-work is pure black;
   - `screen` blends it with the background COLOUR, so white stays white
     and black takes that colour — the lines are drawn in it;
   - the colour is the theme's tint (12% accent over off-white — beige for
     CentreMove, aqua-blue for MyPad), deepened in OKLCH to a fixed
     lightness with more chroma, so it keeps its hue but shows on white;
   - a white veil on top sets the strength: 70% veil = 30% strength, as
     chosen. 0% is the drawing at full strength.

   Source artwork 1254px square (1.5 MB PNG); shipped as a 123 KB WebP and
   drawn at 720px, sharp on a 2x screen. The tile is seamless. A browser
   without relative colour syntax draws the lines grey instead — harmless. */
.cm-chat-pattern {
    --cm-chat-tint: color-mix(in srgb, var(--cm-gold) 12%, var(--cm-offwhite));
    --cm-chat-veil: color-mix(in srgb, var(--cm-white) 70%, transparent);
    background-color: oklch(from var(--cm-chat-tint) 0.76 calc(c * 4) h);
    background-image:
        linear-gradient(var(--cm-chat-veil), var(--cm-chat-veil)),
        url('images/messages/chat-pattern-lines.webp');
    background-repeat: no-repeat, repeat;
    background-size: 100% 100%, 720px 720px;
    background-blend-mode: normal, screen;
}

/* Small secondary text that sits directly on that pattern ("About …", the
   composer hint, empty states). The ground is white now, but line-work
   still passes behind the text, so it keeps a darker mix than body green —
   halfway towards the primary ink, ~7.4:1 — and stays visibly quieter. */
.cm-on-pattern-muted {
    color: color-mix(in srgb, var(--cm-dark) 50%, var(--cm-mid));
}

/* Fills for the inbox. NOT .bg-primary / .bg-secondary: Bootstrap ships both
   as !important utilities built from --bs-*-rgb triples pinned to the
   CentreMove palette above, so on a tenant theme they stay CentreMove green
   and beige (found 24/09/2026 checking MyPad). The hover:bg-* Tailwind
   variants are a different class name and are unaffected. */
.cm-fill-primary {
    background: var(--cm-dark);
    color: var(--cm-on-primary);
}
.cm-fill-soft {
    background: var(--cm-offwhite);
}

/* A plain icon button. Same reason as .cm-thread-row: Bootstrap's
   .bg-transparent is !important and would block a hover:bg-* tint. */
.cm-btn-plain {
    background: transparent;
    border: 0;
}
