/* =============================================================================
   RealEstate CRM — application shell and component styles.

   Layered on top of Bootstrap 5.3 rather than replacing it. The important idea in
   this file is the bridge in section 3: our design tokens are mapped onto
   Bootstrap's own CSS variables, so every `.btn`, `.form-control`, `.table`,
   `.badge` and `.alert` already in the views picks up the theme without a single
   markup change — and follows it into dark mode.

   Colour is defined here and nowhere else. Nothing in the database describes how
   anything looks, so re-theming the product is an edit to this file.

   Sections
     1. Tokens (light)
     2. Tokens (dark)
     3. Bootstrap bridge
     4. Base, typography, focus
     5. Shell — sidebar, topbar, content, footer
     6. Components — page header, card, table, form, badge, pager, empty state
     7. Lead CRM — stat tiles, charts, kanban, timeline
     8. Inventory — status palette, grid, drawer, gallery, pricing
     9. Sign-in
    10. Responsive, motion, print
   ========================================================================== */

/* =============================================================================
   1. Tokens (light)

   Palette, type and proportions taken from the Dreams Sales CRM template
   (crms.dreamstechnologies.com/sales-crm): vermilion #E41F07 as the one strong
   colour, amber #FFA201 beside it, and a light grey canvas under white cards.

   Only the values are shared. Every rule below this point is written for this
   codebase — none of the template's stylesheet is used, and none of its assets
   are shipped. Golos Text, which the look depends on, is open source (SIL OFL)
   and vendored under `lib/fonts/golos/`.

   Four things carry most of the feel:

   **The sidebar is white.** The rail is the same paper as a card, separated from
   the canvas by one hairline, and position is marked by a pale red pill with the
   icon tile flipped to solid red. The previous palette put a navy rail here; the
   template's whole layout reads lighter without it.

   **Red is spent, not spread.** It is the primary button, the active nav pill and
   the focus ring, and almost nothing else. A colour this hot stops meaning
   "here" the moment it appears twice on a screen.

   **Shadows are grey and flat.** `0 4px 4px 0 rgba(219,219,219,0.25)` is the
   template's one card shadow — a short, soft drop with no spread. Cards separate
   by sitting on a tinted canvas, not by being lifted off it.

   **Corners are tight.** 5px on buttons, 6px on fields and cards. The template
   never rounds a rectangle further than that unless it is going fully round.
   ========================================================================== */

:root {
    color-scheme: light;

    /* -- layout. The template's own measurements: a 240px rail and a 56px bar. -- */
    --crm-sidebar-width: 240px;
    --crm-content-max: 1560px;
    --crm-topbar-height: 56px;

    /* -- surfaces. The canvas is a neutral light grey (#F7F8F9), the template's
          `--light`: warm-neutral rather than blue, so white cards read as clean
          paper on it instead of as a brighter shade of the same colour. -- */
    --crm-canvas: #f7f8f9;
    --crm-surface: #ffffff;
    --crm-surface-2: #f7f8f9;
    --crm-surface-3: #f1f4f7;
    --crm-overlay: rgba(31, 32, 32, 0.5);

    /* -- ink. Headings at #1F2020, body at #707070: the template runs its body
          copy well below black, which is what keeps a dense table calm. -- */
    --crm-ink: #1f2020;
    --crm-ink-2: #707070;
    --crm-muted: #868787;
    --crm-faint: #9d9d9d;

    /* -- lines -- */
    --crm-border: #e2e8f0;
    --crm-border-strong: #cdcdcd;
    --crm-border-soft: #e8e8e8;

    /* -- action / accent. Vermilion does both jobs: it is the primary button and
          the accent, and nothing else on the page competes with it.

          The split between the two matters in dark mode and only there. In light
          they are the same red, so the distinction is invisible and easy to get
          wrong; in dark `--crm-action` stays #E41F07 while `--crm-accent` lifts to
          a salmon that can be read as text on a near-black ground.

          So: anything that becomes a **filled surface with white on top** — a
          button, the active sidebar icon tile, a checked box, the current page in
          a pager, the skip link — takes `--crm-action`, which is paired with
          `--crm-action-ink` and is dark enough for white in both themes. Anything
          that is **text, a border, a bar or a dot** takes `--crm-accent`. Filling
          with `--crm-accent` is the bug to watch for: it looks right in light and
          puts white on salmon at 2.8:1 the moment someone switches theme. -- */
    --crm-action: #e41f07;
    --crm-action-hover: #c11a06;
    --crm-action-ink: #ffffff;

    --crm-accent: #e41f07;
    --crm-accent-hover: #c11a06;
    /* One step down from `--crm-accent` on purpose. `--crm-accent` is the fill —
       buttons, the active icon tile, a checked box — where it sits under white
       text at 4.7:1. `--crm-accent-ink` is the same red as *text*, on white or on
       one of its own tints, and at #E41F07 that is 4.0:1 on the active nav pill.
       Splitting the two is what lets the brand colour stay this hot. */
    --crm-accent-ink: #c11a06;
    --crm-accent-soft: #fce9e6;
    --crm-accent-soft-2: #f8cdc6;
    --crm-accent-ring: rgba(228, 31, 7, 0.18);
    /* No glow. The template's buttons cast a flat grey drop or nothing at all;
       a coloured halo under a colour this hot turns the button into a lamp. */
    --crm-accent-glow: 0 0.0625rem 0.1875rem 0 rgba(228, 31, 7, 0.2);

    /* -- amber. The second half of the brand, `--secondary` in the template.
          It appears in exactly two places: the platform chip in the topbar, and the
          blurred light in the corner of the sign-in panel. Spent any more widely
          beside vermilion it stops reading as special, and the two warm colours
          start fighting over the same part of the page. -- */
    --crm-gold: #ffa201;
    --crm-gold-hover: #e08f01;
    --crm-gold-soft: #fff6e6;

    /* -- semantic. The template's own five, each with its `*-transparent` tint.

          These are bright — `--success` is #1ABE17, a signal green, not the muted
          forest the previous palette used. That is the template's character and it
          is kept, but a bright fill cannot also be the text on its own tint: the
          template puts #1ABE17 on #e8f9e8 and lands at 2.3:1. So each colour has a
          third value, `*-ink`, a darkened step of the same hue used wherever the
          colour has to be read rather than merely seen. Fill and line stay the
          template's; only the text moves. Every `*-ink` clears 5:1 on both its own
          tint and white. -- */
    --crm-success: #1abe17;
    --crm-success-soft: #e8f9e8;
    --crm-success-ink: #0f7a0d;
    --crm-warning: #f9b801;
    --crm-warning-soft: #fef8e6;
    --crm-warning-ink: #8a6400;
    /* Danger and info are the template's `--danger-hover` and `--info-hover`
       rather than its `--danger` and `--info`, a barely visible step down from
       #EF1E1E and #2F80ED. It buys the solid variants white text that passes:
       the brighter pair sits at 4.3:1 and 3.9:1 under white, and a Delete button
       is the last place to be a fifth of a step short. */
    --crm-danger: #d81a1a;
    --crm-danger-soft: #fde9e9;
    --crm-danger-ink: #c01616;
    --crm-info: #2972d5;
    --crm-info-soft: #eaf2fd;
    --crm-info-ink: #1a5fbd;
    --crm-neutral: #707070;

    /* -- fields. White on white, held by a border rather than a fill — the
          template gives an input the card's own background and lets the outline
          do the work.

          One deliberate departure: the template drops focus styling entirely
          (`box-shadow: none`, border unchanged), which leaves a keyboard user
          with nothing to follow. The focus border and ring below are ours. -- */
    --crm-field-bg: #ffffff;
    --crm-field-bg-focus: #ffffff;
    --crm-field-border: #e2e8f0;
    --crm-field-border-hover: #cdcdcd;
    --crm-field-border-focus: #e41f07;
    --crm-field-ring: rgba(228, 31, 7, 0.16);

    /* -- sidebar: white, with a pale red pill on the active item -- */
    --crm-sidebar-bg: #ffffff;
    --crm-sidebar-bg-2: #ffffff;
    --crm-sidebar-ink: #707070;
    --crm-sidebar-ink-strong: #1f2020;
    --crm-sidebar-ink-faint: #868787;
    --crm-sidebar-hover: rgba(228, 31, 7, 0.08);
    --crm-sidebar-active-bg: #fde9e9;
    --crm-sidebar-line: #e8e8e8;
    --crm-sidebar-rail: var(--crm-accent);
    /* The icon tile beside every entry. Grey at rest, solid brand when active —
       the template's clearest single gesture in the rail. */
    --crm-sidebar-icon-bg: #f7f8f9;
    --crm-sidebar-icon-ink: #707070;

    /* -- radii. The template's own ramp: 5 on buttons and small controls, 6 as
          the base for fields, cards and nav pills, 8 and 10 above it. -- */
    --crm-radius-sm: 5px;
    --crm-radius: 6px;
    --crm-radius-lg: 8px;
    --crm-radius-xl: 10px;

    /* -- elevation, grey and short. `--crm-shadow-sm` is the template's card
          shadow exactly; the rest of the ramp is built around it. -- */
    --crm-shadow-xs: 0 0.0625rem 0.125rem 0 rgba(219, 219, 219, 0.3);
    --crm-shadow-sm: 0 0.25rem 0.25rem 0 rgba(219, 219, 219, 0.25);
    --crm-shadow: 0 0.275rem 0.75rem -0.0625rem rgba(222, 222, 222, 0.36);
    --crm-shadow-lg: 0 0.25rem 4.625rem 0 rgba(208, 208, 208, 0.25);

    /* -- type. Both families are vendored under lib/fonts, so nothing is fetched
          at render time. Golos Text carries the whole interface — the template
          sets headings, tables and numbers in one face, and the serif display
          type the previous palette used has no place in it. JetBrains Mono stays
          for codes and reference numbers, where a fixed advance is the point. -- */
    --crm-font-sans: "Golos Text", "Inter", -apple-system, BlinkMacSystemFont,
        "Segoe UI Variable Text", "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;

    --crm-font-display: var(--crm-font-sans);

    --crm-font-mono: "JetBrains Mono", "Cascadia Mono", ui-monospace, SFMono-Regular,
        "SF Mono", Menlo, Consolas, monospace;

    /* -- motion -- */
    --crm-ease: cubic-bezier(0.16, 0.84, 0.44, 1);
    --crm-fast: 120ms;
    --crm-medium: 200ms;
}

/* =============================================================================
   2. Tokens (dark)

   Defined twice on purpose: once for the viewer whose system is dark and who has
   made no choice, and once for the viewer who explicitly picked dark. Only the
   tokens change — no component below knows which theme it is in.

   The template ships its own dark palette and this follows it: a near-black
   indigo ground (#0C0C20 canvas, #030318 surface) rather than a neutral charcoal,
   headings in #D9DCFF and body copy in #828997, with borders at #161641.

   Two departures, both because the template's dark mode is built for its own
   markup and breaks in ours. Its `--primary-transparent` turns grey (#1a1d20) in
   dark, which would drain the colour out of every soft-red surface here, so those
   tints are carried as translucent brand instead. And success, danger and info
   are lifted a few steps: at their light-mode values they sit at roughly 2:1
   against #030318, which is unreadable as text.
   ========================================================================== */

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        color-scheme: dark;

        --crm-canvas: #0c0c20;
        --crm-surface: #030318;
        --crm-surface-2: #090c2a;
        --crm-surface-3: #11112f;
        --crm-overlay: rgba(3, 3, 24, 0.72);

        --crm-ink: #d9dcff;
        --crm-ink-2: #97aac1;
        --crm-muted: #828997;
        --crm-faint: #6d7481;

        --crm-border: #161641;
        --crm-border-strong: #232360;
        --crm-border-soft: #11112f;

        --crm-action: #e41f07;
        --crm-action-hover: #c11a06;
        --crm-action-ink: #ffffff;

        --crm-accent: #ff6b55;
        --crm-accent-hover: #ff8574;
        --crm-accent-ink: #ff8574;
        --crm-accent-soft: rgba(228, 31, 7, 0.2);
        --crm-accent-soft-2: rgba(228, 31, 7, 0.34);
        --crm-accent-ring: rgba(228, 31, 7, 0.45);
        --crm-accent-glow: 0 0.0625rem 0.25rem 0 rgba(0, 0, 0, 0.7);

        --crm-gold: #ffb733;
        --crm-gold-hover: #ffc75c;
        --crm-gold-soft: rgba(255, 162, 1, 0.18);

        --crm-success: #3dd83a;
        --crm-success-soft: rgba(26, 190, 23, 0.18);
        --crm-success-ink: #3dd83a;
        --crm-warning: #ffc21f;
        --crm-warning-soft: rgba(249, 184, 1, 0.18);
        --crm-warning-ink: #ffc21f;
        --crm-danger: #ff5a5a;
        --crm-danger-soft: rgba(239, 30, 30, 0.2);
        --crm-danger-ink: #ff5a5a;
        --crm-info: #5b9cf5;
        --crm-info-soft: rgba(47, 128, 237, 0.2);
        --crm-info-ink: #5b9cf5;
        --crm-neutral: #97aac1;

        --crm-field-bg: #090c2a;
        --crm-field-bg-focus: #030318;
        --crm-field-border: #161641;
        --crm-field-border-hover: #232360;
        --crm-field-border-focus: #ff6b55;
        --crm-field-ring: rgba(228, 31, 7, 0.32);

        --crm-sidebar-bg: #030318;
        --crm-sidebar-bg-2: #030318;
        --crm-sidebar-ink: #97aac1;
        --crm-sidebar-ink-strong: #d9dcff;
        --crm-sidebar-ink-faint: #828997;
        --crm-sidebar-hover: rgba(228, 31, 7, 0.14);
        --crm-sidebar-active-bg: rgba(228, 31, 7, 0.22);
        --crm-sidebar-line: #161641;
        --crm-sidebar-icon-bg: #090c2a;
        --crm-sidebar-icon-ink: #97aac1;

        --crm-shadow-xs: 0 0.0625rem 0.125rem 0 rgba(0, 0, 0, 0.6);
        --crm-shadow-sm: 0 0.25rem 0.875rem 0 rgba(0, 0, 0, 0.75);
        --crm-shadow: 0 0.275rem 0.75rem -0.0625rem rgba(0, 0, 0, 0.75);
        --crm-shadow-lg: 0 0.25rem 2.75rem 0 rgba(0, 0, 0, 0.8);
    }
}

:root[data-theme="dark"] {
    color-scheme: dark;

    --crm-canvas: #0c0c20;
    --crm-surface: #030318;
    --crm-surface-2: #090c2a;
    --crm-surface-3: #11112f;
    --crm-overlay: rgba(3, 3, 24, 0.72);

    --crm-ink: #d9dcff;
    --crm-ink-2: #97aac1;
    --crm-muted: #828997;
    --crm-faint: #6d7481;

    --crm-border: #161641;
    --crm-border-strong: #232360;
    --crm-border-soft: #11112f;

    --crm-action: #e41f07;
    --crm-action-hover: #c11a06;
    --crm-action-ink: #ffffff;

    --crm-accent: #ff6b55;
    --crm-accent-hover: #ff8574;
    --crm-accent-ink: #ff8574;
    --crm-accent-soft: rgba(228, 31, 7, 0.2);
    --crm-accent-soft-2: rgba(228, 31, 7, 0.34);
    --crm-accent-ring: rgba(228, 31, 7, 0.45);
    --crm-accent-glow: 0 0.0625rem 0.25rem 0 rgba(0, 0, 0, 0.7);

    --crm-gold: #ffb733;
    --crm-gold-hover: #ffc75c;
    --crm-gold-soft: rgba(255, 162, 1, 0.18);

    --crm-success: #3dd83a;
    --crm-success-soft: rgba(26, 190, 23, 0.18);
    --crm-success-ink: #3dd83a;
    --crm-warning: #ffc21f;
    --crm-warning-soft: rgba(249, 184, 1, 0.18);
    --crm-warning-ink: #ffc21f;
    --crm-danger: #ff5a5a;
    --crm-danger-soft: rgba(239, 30, 30, 0.2);
    --crm-danger-ink: #ff5a5a;
    --crm-info: #5b9cf5;
    --crm-info-soft: rgba(47, 128, 237, 0.2);
    --crm-info-ink: #5b9cf5;
    --crm-neutral: #97aac1;

    --crm-field-bg: #090c2a;
    --crm-field-bg-focus: #030318;
    --crm-field-border: #161641;
    --crm-field-border-hover: #232360;
    --crm-field-border-focus: #ff6b55;
    --crm-field-ring: rgba(228, 31, 7, 0.32);

    --crm-sidebar-bg: #030318;
    --crm-sidebar-bg-2: #030318;
    --crm-sidebar-ink: #97aac1;
    --crm-sidebar-ink-strong: #d9dcff;
    --crm-sidebar-ink-faint: #828997;
    --crm-sidebar-hover: rgba(228, 31, 7, 0.14);
    --crm-sidebar-active-bg: rgba(228, 31, 7, 0.22);
    --crm-sidebar-line: #161641;
    --crm-sidebar-icon-bg: #090c2a;
    --crm-sidebar-icon-ink: #97aac1;

    --crm-shadow-xs: 0 0.0625rem 0.125rem 0 rgba(0, 0, 0, 0.6);
    --crm-shadow-sm: 0 0.25rem 0.875rem 0 rgba(0, 0, 0, 0.75);
    --crm-shadow: 0 0.275rem 0.75rem -0.0625rem rgba(0, 0, 0, 0.75);
    --crm-shadow-lg: 0 0.25rem 2.75rem 0 rgba(0, 0, 0, 0.8);
}

/* =============================================================================
   3. Bootstrap bridge

   Sixty Razor views were written against Bootstrap's own classes. Rather than
   rewrite them, the tokens above are mapped onto Bootstrap's variables here, so
   `btn-primary`, `form-control`, `table`, `badge` and the rest inherit the theme
   — including dark mode — with no markup change at all.
   ========================================================================== */

:root {
    --bs-body-bg: var(--crm-canvas);
    --bs-body-color: var(--crm-ink-2);
    --bs-emphasis-color: var(--crm-ink);
    --bs-secondary-color: var(--crm-muted);
    --bs-tertiary-color: var(--crm-faint);
    --bs-border-color: var(--crm-border);
    --bs-border-color-translucent: var(--crm-border);
    --bs-secondary-bg: var(--crm-surface-2);
    --bs-tertiary-bg: var(--crm-surface-3);

    --bs-primary: var(--crm-accent);
    --bs-link-color: var(--crm-accent);
    --bs-link-hover-color: var(--crm-accent-hover);
    --bs-link-color-rgb: 228, 31, 7;

    --bs-border-radius: var(--crm-radius);
    --bs-border-radius-sm: var(--crm-radius-sm);
    --bs-border-radius-lg: var(--crm-radius-lg);

    --bs-font-sans-serif: var(--crm-font-sans);
    --bs-font-monospace: var(--crm-font-mono);
}

/* Buttons. Bootstrap resolves these per component, so they are set per variant
   rather than globally. */
.btn {
    --bs-btn-font-weight: 500;
    --bs-btn-padding-y: 0.44rem;
    --bs-btn-padding-x: 0.9rem;
    --bs-btn-border-radius: var(--crm-radius-sm);
    transition: background-color var(--crm-fast) var(--crm-ease),
                border-color var(--crm-fast) var(--crm-ease),
                color var(--crm-fast) var(--crm-ease),
                box-shadow var(--crm-fast) var(--crm-ease),
                transform var(--crm-fast) var(--crm-ease);
}

.btn-sm {
    --bs-btn-padding-y: 0.28rem;
    --bs-btn-padding-x: 0.65rem;
    --bs-btn-font-size: 0.82rem;
}

.btn-lg {
    --bs-btn-padding-y: 0.62rem;
    --bs-btn-padding-x: 1.25rem;
    --bs-btn-font-size: 1rem;
}

/* The commit button is the brand colour, which in this palette means it is the
   loudest thing on the screen. That is the template's own arrangement and it
   works because there is usually exactly one of these per view — Save, Create,
   Sign in — and because nothing else on the page is allowed to be red. The
   moment a second red fill appears beside it, both stop meaning anything. */
.btn-primary {
    --bs-btn-bg: var(--crm-action);
    --bs-btn-border-color: var(--crm-action);
    --bs-btn-color: var(--crm-action-ink);
    --bs-btn-hover-bg: var(--crm-action-hover);
    --bs-btn-hover-border-color: var(--crm-action-hover);
    --bs-btn-hover-color: var(--crm-action-ink);
    --bs-btn-active-bg: var(--crm-action-hover);
    --bs-btn-active-border-color: var(--crm-action-hover);
    --bs-btn-active-color: var(--crm-action-ink);
    --bs-btn-disabled-bg: var(--crm-action);
    --bs-btn-disabled-border-color: var(--crm-action);
    --bs-btn-disabled-color: var(--crm-action-ink);
    box-shadow: var(--crm-shadow-xs);
}

/* Which ink a coloured button carries is decided by the fill's own lightness,
   not by the theme. This palette's green, amber and — in dark — its red are all
   light enough that white text on them is unreadable, so those three take
   near-black in every theme. Red and blue are dark enough for white in light
   mode and get it below. */
.btn-success,
.btn-warning {
    --bs-btn-color: #14120e;
    --bs-btn-hover-color: #14120e;
    --bs-btn-active-color: #14120e;
    --bs-btn-disabled-color: #14120e;
}

:root[data-theme="dark"] .btn-danger,
:root[data-theme="dark"] .btn-info {
    --bs-btn-color: #14120e;
    --bs-btn-hover-color: #14120e;
    --bs-btn-active-color: #14120e;
}

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .btn-danger,
    :root:not([data-theme="light"]) .btn-info {
        --bs-btn-color: #14120e;
        --bs-btn-hover-color: #14120e;
        --bs-btn-active-color: #14120e;
    }
}

.btn-outline-secondary {
    --bs-btn-color: var(--crm-ink-2);
    --bs-btn-border-color: var(--crm-border-strong);
    --bs-btn-bg: var(--crm-surface);
    --bs-btn-hover-color: var(--crm-ink);
    --bs-btn-hover-bg: var(--crm-surface-2);
    --bs-btn-hover-border-color: var(--crm-border-strong);
    --bs-btn-active-color: var(--crm-ink);
    --bs-btn-active-bg: var(--crm-surface-3);
    --bs-btn-active-border-color: var(--crm-border-strong);
}

.btn-outline-primary {
    --bs-btn-color: var(--crm-accent);
    --bs-btn-border-color: var(--crm-accent);
    --bs-btn-hover-bg: var(--crm-action);
    --bs-btn-hover-border-color: var(--crm-action);
    --bs-btn-active-bg: var(--crm-action-hover);
    --bs-btn-active-border-color: var(--crm-action-hover);
}

.btn-danger {
    --bs-btn-bg: var(--crm-danger);
    --bs-btn-border-color: var(--crm-danger);
    --bs-btn-hover-bg: #b91c1c;
    --bs-btn-hover-border-color: #b91c1c;
}

.btn-success {
    --bs-btn-bg: var(--crm-success);
    --bs-btn-border-color: var(--crm-success);
    --bs-btn-hover-bg: #15803d;
    --bs-btn-hover-border-color: #15803d;
}

.btn:not(.btn-link):not(:disabled):active {
    transform: translateY(1px);
}

/* Forms. */
/* A field is white on white, held by its border — the template gives an input
   the card's own background and lets the outline do the work. Arriving in it
   draws the border in brand and lays a soft ring outside it, which is the one
   place this file departs from the template: its own focus state is
   `box-shadow: none` with the border unchanged, leaving nothing for a keyboard
   user to follow. */
.form-control,
.form-select {
    --bs-body-bg: var(--crm-field-bg);
    background-color: var(--crm-field-bg);
    border-color: var(--crm-field-border);
    border-radius: var(--crm-radius);
    color: var(--crm-ink);
    padding: 0.45rem 0.7rem;
    transition: border-color var(--crm-fast) var(--crm-ease),
                box-shadow var(--crm-fast) var(--crm-ease),
                background-color var(--crm-fast) var(--crm-ease);
}

/* Hover only when the field is not already focused: otherwise moving the mouse
   across a field you are typing in would pull its border off brand. */
.form-control:hover:not(:focus):not(:disabled),
.form-select:hover:not(:focus):not(:disabled) {
    border-color: var(--crm-field-border-hover);
}

.form-control:focus,
.form-select:focus {
    background-color: var(--crm-field-bg-focus);
    border-color: var(--crm-field-border-focus);
    box-shadow: 0 0 0 3px var(--crm-field-ring);
    color: var(--crm-ink);
}

.form-control::placeholder {
    color: var(--crm-muted);
}

.form-control:disabled,
.form-select:disabled {
    background-color: var(--crm-surface-3);
    color: var(--crm-muted);
}

.form-control-sm,
.form-select-sm {
    padding: 0.28rem 0.55rem;
    font-size: 0.82rem;
}

.form-label {
    font-size: 0.82rem;
    font-weight: 550;
    color: var(--crm-ink-2);
    margin-bottom: 0.3rem;
}

.form-text {
    color: var(--crm-ink-2);
    font-size: 0.78rem;
}

.form-check-input {
    border-color: var(--crm-border-strong);
    background-color: var(--crm-surface);
}

.form-check-input:checked {
    background-color: var(--crm-action);
    border-color: var(--crm-action);
}

.form-check-input:focus {
    border-color: var(--crm-accent);
    box-shadow: 0 0 0 3px var(--crm-accent-ring);
}

.input-group-text {
    background-color: var(--crm-field-bg);
    border-color: var(--crm-field-border);
    color: var(--crm-muted);
}

/* Dark mode needs the browser's own widgets to match, or a date picker and a
   select arrow arrive as bright white rectangles. */
:root[data-theme="dark"] .form-control,
:root[data-theme="dark"] .form-select {
    color-scheme: dark;
}

/* Tables. */
.table {
    --bs-table-bg: transparent;
    --bs-table-color: var(--crm-ink-2);
    --bs-table-border-color: var(--crm-border-soft);
    --bs-table-hover-bg: var(--crm-surface-2);
    --bs-table-hover-color: var(--crm-ink);
    margin-bottom: 0;
}

.table > thead {
    --bs-table-color: var(--crm-ink-2);
}

/* Body ink rather than muted: at 0.74rem uppercase this is about the smallest
   text in the product, and #868787 does not carry it on the grey header fill. */
.table > thead th {
    font-size: 0.74rem;
    font-weight: 650;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--crm-ink-2);
    border-bottom: 1px solid var(--crm-border);
    padding: 0.6rem 0.75rem;
    white-space: nowrap;
    background: var(--crm-surface-2);
}

.table > tbody td,
.table > tbody th {
    padding: 0.7rem 0.75rem;
    vertical-align: middle;
    border-bottom-color: var(--crm-border-soft);
}

.table > tbody tr:last-child > * {
    border-bottom-width: 0;
}

/* Money and counts line up when the digits are the same width. */
.table td.text-end,
.table th.text-end {
    font-variant-numeric: tabular-nums;
}

/* Badges. Bootstrap's subtle variants are remapped so they survive dark mode. */
.badge {
    font-weight: 600;
    font-size: 0.72rem;
    letter-spacing: 0.01em;
    padding: 0.3em 0.6em;
    border-radius: 999px;
}

/* Every one of these is the colour used as *text*, so every one takes the `-ink`
   step rather than the fill. The primary badge sits on `--crm-accent-soft`, not
   on `-soft-2`: the deeper tint is for borders and rails, and red ink on it comes
   in at 4.3:1. */
.text-bg-success-subtle { background-color: var(--crm-success-soft) !important; color: var(--crm-success-ink) !important; }
.text-bg-warning-subtle { background-color: var(--crm-warning-soft) !important; color: var(--crm-warning-ink) !important; }
.text-bg-danger-subtle { background-color: var(--crm-danger-soft) !important; color: var(--crm-danger-ink) !important; }
.text-bg-info-subtle { background-color: var(--crm-info-soft) !important; color: var(--crm-info-ink) !important; }
.text-bg-primary-subtle { background-color: var(--crm-accent-soft) !important; color: var(--crm-accent-ink) !important; }
.text-bg-secondary-subtle { background-color: var(--crm-surface-3) !important; color: var(--crm-ink-2) !important; }
.text-bg-light { background-color: var(--crm-surface-2) !important; color: var(--crm-ink-2) !important; }

/* `--crm-muted` is #868787, which is 3.6:1 on white — enough for an icon or a
   divider, short of the 4.5:1 that small text needs. These utilities are applied
   to real copy all over the views, so they resolve to the body ink instead.
   Hierarchy still comes from size and weight, as it does in the template. */
.text-secondary { color: var(--crm-ink-2) !important; }
.text-body-secondary { color: var(--crm-ink-2) !important; }
.text-danger { color: var(--crm-danger-ink) !important; }
.text-success { color: var(--crm-success-ink) !important; }
.text-muted { color: var(--crm-ink-2) !important; }

/* Alerts. */
.alert {
    border-radius: var(--crm-radius);
    border-width: 1px;
    padding: 0.7rem 1rem;
    font-size: 0.9rem;
    box-shadow: var(--crm-shadow-xs);
}

.alert-success { background: var(--crm-success-soft); border-color: transparent; color: var(--crm-success-ink); }
.alert-danger  { background: var(--crm-danger-soft);  border-color: transparent; color: var(--crm-danger-ink); }
.alert-warning { background: var(--crm-warning-soft); border-color: transparent; color: var(--crm-warning-ink); }
.alert-info    { background: var(--crm-info-soft);    border-color: transparent; color: var(--crm-info-ink); }
.alert-light   { background: var(--crm-surface-2);    border-color: var(--crm-border); color: var(--crm-ink-2); }

/* Dropdowns, modals and other floating surfaces. */
.dropdown-menu {
    --bs-dropdown-bg: var(--crm-surface);
    --bs-dropdown-border-color: var(--crm-border);
    --bs-dropdown-link-color: var(--crm-ink-2);
    --bs-dropdown-link-hover-bg: var(--crm-surface-2);
    --bs-dropdown-link-hover-color: var(--crm-ink);
    --bs-dropdown-link-active-bg: var(--crm-accent-soft);
    --bs-dropdown-link-active-color: var(--crm-accent-ink);
    --bs-dropdown-header-color: var(--crm-muted);
    border-radius: var(--crm-radius);
    box-shadow: var(--crm-shadow-lg);
    padding: 0.35rem;
    font-size: 0.88rem;
}

.dropdown-item {
    border-radius: var(--crm-radius-sm);
    padding: 0.42rem 0.6rem;
}

.modal-content,
.offcanvas {
    background-color: var(--crm-surface);
    border-color: var(--crm-border);
    border-radius: var(--crm-radius-lg);
}

.nav-tabs {
    --bs-nav-tabs-border-color: var(--crm-border);
    --bs-nav-tabs-link-active-bg: var(--crm-surface);
    --bs-nav-tabs-link-active-color: var(--crm-accent);
    --bs-nav-tabs-link-active-border-color: var(--crm-border) var(--crm-border) var(--crm-surface);
    --bs-nav-link-color: var(--crm-muted);
    --bs-nav-link-hover-color: var(--crm-ink);
}

.breadcrumb {
    --bs-breadcrumb-divider-color: var(--crm-faint);
    --bs-breadcrumb-item-active-color: var(--crm-ink-2);
    margin-bottom: 0.75rem;
}

.breadcrumb a {
    color: var(--crm-muted);
    text-decoration: none;
}

.breadcrumb a:hover {
    color: var(--crm-accent);
}

.progress {
    background-color: var(--crm-surface-3);
    border-radius: 999px;
}

hr,
.dropdown-divider {
    border-color: var(--crm-border);
    opacity: 1;
}

.btn-close {
    --bs-btn-close-color: var(--crm-ink);
}

:root[data-theme="dark"] .btn-close {
    filter: invert(1) grayscale(1) brightness(2);
}

/* =============================================================================
   4. Base, typography, focus
   ========================================================================== */

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

html {
    -webkit-text-size-adjust: 100%;
}

body.crm-app,
body.crm-plain {
    background-color: var(--crm-canvas);
    color: var(--crm-ink-2);
    font-family: var(--bs-font-sans-serif);
    font-size: 0.925rem;
    line-height: 1.55;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

/* One family for everything, which is the template's typographic idea in full:
   there is no display face, and the difference between a heading and a row of
   table text is weight and colour, not shape. Headings at 700 against a 400 body
   is the contrast the template actually ships. */
h1, h2, h3, h4, h5, h6 {
    color: var(--crm-ink);
    font-family: var(--crm-font-display);
    font-weight: 700;
    letter-spacing: -0.004em;
    line-height: 1.32;
}

/* Kept as an explicit declaration even though `--crm-font-display` now resolves
   to the same stack. These are the elements that are headings by markup and
   labels by function, and naming them here is what keeps a future display face
   out of a 12px table header without anyone having to rediscover the rule. */
.table th,
.form-label,
.badge,
.btn,
.nav-link,
.crm-stat-tile h3,
.crm-stat-tile h4 {
    font-family: var(--crm-font-sans);
}

a {
    text-decoration-thickness: 1px;
    text-underline-offset: 0.18em;
}

/* One focus treatment for the whole application. Keyboard users get a ring they
   cannot miss; mouse users never see it. */
:focus-visible {
    outline: 2px solid var(--crm-accent);
    outline-offset: 2px;
    border-radius: var(--crm-radius-sm);
}

.form-control:focus-visible,
.form-select:focus-visible,
.btn:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px var(--crm-accent-ring);
}

::selection {
    background: var(--crm-accent-soft-2);
    color: var(--crm-accent-ink);
}

/* Scrollbars, so a long sidebar or a wide table does not interrupt the palette. */
* {
    scrollbar-width: thin;
    scrollbar-color: var(--crm-border-strong) transparent;
}

*::-webkit-scrollbar {
    width: 10px;
    height: 10px;
}

*::-webkit-scrollbar-track {
    background: transparent;
}

*::-webkit-scrollbar-thumb {
    background: var(--crm-border-strong);
    border-radius: 999px;
    border: 3px solid transparent;
    background-clip: content-box;
}

*::-webkit-scrollbar-thumb:hover {
    background: var(--crm-faint);
    background-clip: content-box;
}

/* JetBrains Mono runs wider than the Consolas and Menlo this used to fall through
   to, so the size comes down a step to keep a reference number the same length on
   the page as it was before the face was actually vendored. */
.font-monospace {
    font-family: var(--crm-font-mono);
    font-size: 0.82em;
    letter-spacing: -0.015em;
}

/* An identifier in a table cell never wraps. LEAD-2026-000046 broken across three
   lines is unreadable and it drags the whole row taller; the table already scrolls
   sideways inside .table-responsive when a column needs the room. */
.table .font-monospace,
.table td > .font-monospace {
    white-space: nowrap;
}

/* =============================================================================
   5. Shell
   ========================================================================== */

.crm-shell {
    display: flex;
    min-height: 100vh;
    min-height: 100dvh;
}

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

.crm-content {
    flex: 1 1 auto;
    width: 100%;
    max-width: var(--crm-content-max);
    margin: 0 auto;
    padding: 1.5rem 1.75rem 2.5rem;
}

/* ---------- sidebar ---------- */

.crm-sidebar {
    width: var(--crm-sidebar-width);
    flex: 0 0 var(--crm-sidebar-width);
    background: var(--crm-sidebar-bg);
    color: var(--crm-sidebar-ink);
    display: flex;
    flex-direction: column;
    position: sticky;
    top: 0;
    height: 100vh;
    height: 100dvh;
    overflow-y: auto;
    overscroll-behavior: contain;
    border-right: 1px solid var(--crm-sidebar-line);
}

.crm-brand {
    display: flex;
    align-items: center;
    gap: 0.7rem;
    padding: 1.05rem 1.1rem;
    border-bottom: 1px solid var(--crm-sidebar-line);
    position: sticky;
    top: 0;
    background: inherit;
    backdrop-filter: blur(6px);
    z-index: 2;
}

.crm-brand-mark {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    border-radius: var(--crm-radius);
    /* The template's own logo gradient, `--primary-gradient-100`. On a white rail
       the mark is the only saturated thing above the fold, which is what makes it
       read as the logo rather than as the first nav entry. */
    background: linear-gradient(180deg, #e41f07 0%, #ff5641 100%);
    color: #ffffff;
    font-weight: 800;
    font-size: 0.86rem;
    letter-spacing: 0.04em;
    flex: 0 0 auto;
    box-shadow: 0 0.25rem 0.625rem -0.25rem rgba(228, 31, 7, 0.55);
}

.crm-brand-text {
    display: flex;
    flex-direction: column;
    line-height: 1.15;
    min-width: 0;
}

.crm-brand-text strong {
    color: var(--crm-sidebar-ink-strong);
    font-size: 0.98rem;
    font-weight: 640;
    letter-spacing: -0.01em;
}

/* Same contrast reasoning as the group heading: the faint token was readable as
   white-on-navy and is not readable as grey-on-white at 11px. */
.crm-brand-text span {
    font-size: 0.68rem;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: var(--crm-sidebar-ink);
}

.crm-nav {
    padding: 0.6rem 0.55rem 2rem;
    flex: 1 1 auto;
}

.crm-nav ul {
    list-style: none;
    margin: 0;
    padding: 0;
}

/* Entries are 6px-radius rows, not capsules. The template rounds a nav item the
   same amount it rounds a card, and the icon tile inside it the same amount it
   rounds a button — the rail is built from the same two corners as everything
   else on the page, which is most of why it sits quietly beside the content.

   Weight 500 at rest rather than 400: on white the label has to carry itself
   without a dark ground behind it. */
.crm-nav-link {
    position: relative;
    display: flex;
    align-items: center;
    gap: 0.55rem;
    padding: 0.4rem 0.6rem;
    margin: 0.15rem 0;
    border-radius: var(--crm-radius);
    color: var(--crm-sidebar-ink);
    text-decoration: none;
    font-size: 0.875rem;
    font-weight: 500;
    transition: background-color var(--crm-fast) var(--crm-ease),
                color var(--crm-fast) var(--crm-ease),
                box-shadow var(--crm-fast) var(--crm-ease);
}

/* The icon tile: a 28px grey square with the glyph centred in it. This is the
   template's signature in the rail, and it does real work — the tiles line up
   into a column that the eye scans faster than a column of bare glyphs, and the
   active one has somewhere solid to put the brand colour. */
.crm-nav-link .crm-icon {
    box-sizing: content-box;
    width: 18px;
    height: 18px;
    padding: 5px;
    border-radius: var(--crm-radius-sm);
    background: var(--crm-sidebar-icon-bg);
    color: var(--crm-sidebar-icon-ink);
    transition: color var(--crm-fast) var(--crm-ease),
                background-color var(--crm-fast) var(--crm-ease);
}

.crm-nav-link:hover,
.crm-nav-link:focus-visible {
    background: var(--crm-sidebar-hover);
    color: var(--crm-accent-ink);
}

.crm-nav-link:hover .crm-icon,
.crm-nav-link:focus-visible .crm-icon {
    background: var(--crm-action);
    color: #ffffff;
}

/* The active entry: a pale red pill with the icon tile flipped to solid brand.

   Two signals, not one, and that is the point. The pill alone is a very light
   tint — #fde9e9 against white is a two-percent step, easy to miss on a long
   rail — so the tile going saturated is what actually answers "which page". The
   rail down the left edge is ours, kept from the previous palette: it survives
   a forced-colours mode, where both the tint and the fill are discarded. */
.crm-nav-link.active {
    position: relative;
    background: var(--crm-sidebar-active-bg);
    color: var(--crm-accent-ink);
    font-weight: 600;
}

.crm-nav-link.active::before {
    content: "";
    position: absolute;
    left: 0;
    top: 0.3rem;
    bottom: 0.3rem;
    width: 2px;
    border-radius: 0 2px 2px 0;
    background: var(--crm-sidebar-rail);
}

.crm-nav-link.active .crm-icon {
    background: var(--crm-action);
    color: #ffffff;
}

.crm-nav-badge {
    margin-left: auto;
    font-size: 0.62rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.07em;
    padding: 0.12rem 0.45rem;
    border-radius: 999px;
    background: var(--crm-surface-2);
    color: var(--crm-sidebar-ink);
    border: 1px solid var(--crm-sidebar-line);
}

.crm-nav-link.active .crm-nav-badge {
    background: var(--crm-action);
    color: #ffffff;
    border-color: transparent;
}

.crm-icon {
    width: 18px;
    height: 18px;
    flex: 0 0 auto;
}

/* Sidebar groups. A group heading is a label, not a link: every destination
   inside it is a child entry, and the group is dropped entirely when the
   signed-in user may open none of them. */
.crm-nav-group {
    margin-top: 1.1rem;
}

.crm-nav-group-title {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    padding: 0.55rem 0.6rem 0.3rem;
    /* The template sets a group heading in plain sentence case at 13px rather
       than in small uppercase, and leans on the icon tiles below it to mark the
       entries as entries. Kept a step smaller and heavier here so the heading
       still reads as a label and not as a link without an icon.

       Not `--crm-sidebar-ink-faint`: at this size #868787 is 3.5:1 on white, and
       small text needs 4.5:1. #707070 is the template's own `--menu-title-color`
       and clears it. */
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.01em;
    color: var(--crm-sidebar-ink);
}

.crm-nav-group-title .crm-icon {
    width: 14px;
    height: 14px;
    opacity: 0.7;
}

/* The section you are in. This stays deliberately quieter than the active pill:
   the pill answers "which page", the heading only answers "which section", and a
   second loud marker would compete with the one that matters. Colour alone is not
   the signal — the active link also carries aria-current for anyone not seeing it. */
.crm-nav-group.is-active > .crm-nav-group-title {
    color: var(--crm-accent-ink);
}

.crm-nav-group.is-active > .crm-nav-group-title .crm-icon {
    opacity: 1;
}

/* A rail down the children ties the heading to the pill sitting under it, which
   matters most in the long groups where the two are far apart. */
.crm-nav-group.is-active > ul {
    box-shadow: inset 2px 0 0 -1px var(--crm-accent-soft-2);
}

/* The pair of dark-mode overrides that used to sit here is gone. They existed
   because the old accent was a navy dark enough to disappear against the rail;
   `--crm-accent-ink` is now #ff8574 in dark, which reads on #030318 on its own. */

.crm-nav-group > ul {
    list-style: none;
    margin: 0;
    padding: 0;
}

/* Half the indent it used to carry. The 28px icon tile is itself a strong left
   edge now, and the old 1.65rem pushed a child label far enough into a 240px rail
   that the longer destinations started wrapping. */
.crm-nav-sublink {
    padding-left: 1.1rem;
}

/* ---------- topbar ---------- */

.crm-topbar {
    display: flex;
    align-items: center;
    gap: 0.9rem;
    min-height: var(--crm-topbar-height);
    padding: 0.6rem 1.75rem;
    background: color-mix(in srgb, var(--crm-surface) 82%, transparent);
    backdrop-filter: saturate(180%) blur(12px);
    -webkit-backdrop-filter: saturate(180%) blur(12px);
    border-bottom: 1px solid var(--crm-border);
    position: sticky;
    top: 0;
    z-index: 20;
}

/* Browsers without color-mix still need an opaque bar rather than a see-through
   one, so the fallback is declared first and overridden above. */
@supports not (background: color-mix(in srgb, red 50%, blue)) {
    .crm-topbar {
        background: var(--crm-surface);
    }
}

.crm-topbar-title {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    min-width: 0;
}

.crm-topbar-title h1 {
    font-size: 1.02rem;
    font-weight: 620;
    margin: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    letter-spacing: -0.012em;
}

.crm-tenant-chip {
    font-size: 0.67rem;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    padding: 0.18rem 0.5rem;
    border-radius: 999px;
    background: var(--crm-accent-soft);
    color: var(--crm-accent-ink);
    border: 1px solid var(--crm-accent-soft-2);
    white-space: nowrap;
}

/* The one chip that is not the tenant's own name, so it is the one place amber
   earns its keep in the shell: a colour nothing else on a tenant screen uses,
   against a brand that is now entirely red. Amber cannot carry the text itself —
   #FFA201 is 2:1 on its own tint — so the fill and the border are the template's
   `--secondary` and the label is the darkened amber ink. */
.crm-tenant-chip-platform {
    background: var(--crm-gold-soft);
    color: var(--crm-warning-ink);
    border-color: var(--crm-gold);
}

.crm-topbar-actions {
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: 0.35rem;
}

.crm-topbar-user .btn {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.25rem 0.5rem 0.25rem 0.25rem;
    border-radius: 999px;
    color: var(--crm-ink-2);
    text-decoration: none;
    font-size: 0.86rem;
    font-weight: 520;
    transition: background-color var(--crm-fast) var(--crm-ease);
}

.crm-topbar-user .btn:hover {
    background: var(--crm-surface-2);
}

.crm-topbar-user .dropdown-toggle::after {
    margin-left: 0.1rem;
    color: var(--crm-faint);
}

.crm-avatar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    background: linear-gradient(140deg, var(--crm-ink-2) 0%, var(--crm-ink) 100%);
    color: var(--crm-action-ink);
    font-size: 0.76rem;
    font-weight: 700;
    letter-spacing: 0.02em;
    flex: 0 0 auto;
    /* A hairline of brand, so the avatar belongs to the same set as the mark in
       the rail rather than looking like a stray grey circle. */
    box-shadow: inset 0 0 0 1px rgba(228, 31, 7, 0.35);
}

/* An icon-only control in the topbar: the theme switch and anything joining it
   later. */
.crm-icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    padding: 0;
    border: 1px solid transparent;
    border-radius: var(--crm-radius-sm);
    background: transparent;
    color: var(--crm-muted);
    transition: background-color var(--crm-fast) var(--crm-ease),
                color var(--crm-fast) var(--crm-ease),
                border-color var(--crm-fast) var(--crm-ease);
}

.crm-icon-btn:hover {
    background: var(--crm-surface-2);
    border-color: var(--crm-border);
    color: var(--crm-ink);
}

.crm-icon-btn .crm-icon {
    width: 17px;
    height: 17px;
}

/* Only one of the two glyphs is ever shown, and which one depends on the theme
   the viewer is actually in — including when that theme came from the operating
   system rather than from a click. */
.crm-theme-toggle [data-theme-dark] { display: none; }
.crm-theme-toggle [data-theme-light] { display: inline-flex; }

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .crm-theme-toggle [data-theme-light] { display: none; }
    :root:not([data-theme="light"]) .crm-theme-toggle [data-theme-dark] { display: inline-flex; }
}

:root[data-theme="dark"] .crm-theme-toggle [data-theme-light] { display: none; }
:root[data-theme="dark"] .crm-theme-toggle [data-theme-dark] { display: inline-flex; }
:root[data-theme="light"] .crm-theme-toggle [data-theme-light] { display: inline-flex; }
:root[data-theme="light"] .crm-theme-toggle [data-theme-dark] { display: none; }

.crm-sidebar-toggle {
    display: none;
    flex-direction: column;
    gap: 4px;
    padding: 0.45rem;
    border-radius: var(--crm-radius-sm);
}

.crm-sidebar-toggle span {
    display: block;
    width: 18px;
    height: 2px;
    background: var(--crm-ink);
    border-radius: 2px;
}

.crm-sidebar-backdrop {
    display: none;
    position: fixed;
    inset: 0;
    background: var(--crm-overlay);
    backdrop-filter: blur(2px);
    z-index: 25;
}

/* ---------- footer ---------- */

.crm-footer {
    padding: 0.9rem 1.75rem;
    border-top: 1px solid var(--crm-border);
    background: var(--crm-surface);
    font-size: 0.82rem;
    color: var(--crm-muted);
}

/* =============================================================================
   6. Components
   ========================================================================== */

/* ---------- page header ---------- */

.crm-page-intro {
    margin-bottom: 1.35rem;
}

.crm-page-intro h2 {
    font-size: 1.42rem;
    font-weight: 640;
    letter-spacing: -0.018em;
    margin-bottom: 0.28rem;
}

.crm-page-intro p {
    color: var(--crm-muted);
    max-width: 72ch;
    margin-bottom: 0;
    font-size: 0.9rem;
}

/* ---------- card ---------- */

/* The workhorse. Padding and a surface live here rather than in each view, so a
   card is a card everywhere and nobody has to remember `p-3`. */
.crm-card {
    background: var(--crm-surface);
    border: 1px solid var(--crm-border);
    border-radius: var(--crm-radius-lg);
    box-shadow: var(--crm-shadow-sm);
    padding: 1.15rem 1.25rem;
}

/* A card that is only a frame around a table gives the table its edges back. */
.crm-card > .table-responsive:first-child,
.crm-card > .table-responsive:only-child {
    margin: -1.15rem -1.25rem;
    width: auto;
    border-radius: inherit;
}

.crm-card > .table-responsive > .table > thead th:first-child { padding-left: 1.25rem; }
.crm-card > .table-responsive > .table > thead th:last-child { padding-right: 1.25rem; }
.crm-card > .table-responsive > .table > tbody td:first-child,
.crm-card > .table-responsive > .table > tbody th:first-child { padding-left: 1.25rem; }
.crm-card > .table-responsive > .table > tbody td:last-child { padding-right: 1.25rem; }

.crm-card > .table-responsive:first-child + .crm-pager {
    margin: 0 -1.25rem -1.15rem;
    padding: 0.85rem 1.25rem;
    border-top: 1px solid var(--crm-border);
}

/* A card that links somewhere lifts on hover; one that does not, does not. */
a.crm-card {
    transition: border-color var(--crm-fast) var(--crm-ease),
                box-shadow var(--crm-fast) var(--crm-ease),
                transform var(--crm-fast) var(--crm-ease);
}

a.crm-card:hover {
    border-color: var(--crm-border-strong);
    box-shadow: var(--crm-shadow);
    transform: translateY(-1px);
}

/* A card made clickable by a .stretched-link inside it (the report KPI tiles).
   position: relative is what keeps the link's overlay inside the card — without
   it the overlay grows to the nearest positioned ancestor and swallows every click
   on the page. */
.crm-card-link {
    position: relative;
    transition: border-color var(--crm-fast) var(--crm-ease),
                box-shadow var(--crm-fast) var(--crm-ease),
                transform var(--crm-fast) var(--crm-ease);
}

.crm-card-link:hover {
    border-color: var(--crm-border-strong);
    box-shadow: var(--crm-shadow);
    transform: translateY(-1px);
}

.crm-card-title {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.94rem;
    font-weight: 640;
    color: var(--crm-ink);
    letter-spacing: -0.008em;
    margin-bottom: 0.9rem;
}

/* ---------- description list ---------- */

.crm-dl {
    display: grid;
    grid-template-columns: minmax(130px, 34%) 1fr;
    gap: 0.55rem 1.25rem;
    margin: 0;
    font-size: 0.885rem;
}

.crm-dl dt {
    color: var(--crm-muted);
    font-weight: 500;
}

.crm-dl dd {
    margin: 0;
    color: var(--crm-ink);
    word-break: break-word;
}

/* ---------- pager ---------- */

.crm-pager {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin-top: 1rem;
    font-size: 0.84rem;
    color: var(--crm-muted);
}

.pagination {
    --bs-pagination-color: var(--crm-ink-2);
    --bs-pagination-bg: var(--crm-surface);
    --bs-pagination-border-color: var(--crm-border);
    --bs-pagination-hover-bg: var(--crm-surface-2);
    --bs-pagination-hover-color: var(--crm-ink);
    --bs-pagination-hover-border-color: var(--crm-border-strong);
    --bs-pagination-active-bg: var(--crm-action);
    --bs-pagination-active-border-color: var(--crm-action);
    --bs-pagination-disabled-bg: var(--crm-surface-2);
    --bs-pagination-disabled-color: var(--crm-faint);
    --bs-pagination-disabled-border-color: var(--crm-border);
    --bs-pagination-border-radius: var(--crm-radius-sm);
    margin-bottom: 0;
}

/* ---------- empty state ---------- */

.crm-empty-state {
    max-width: 520px;
    margin: 2.5rem auto;
    text-align: center;
    padding: 1rem;
}

.crm-empty-state .crm-icon {
    width: 40px;
    height: 40px;
    color: var(--crm-accent);
    margin-bottom: 0.75rem;
    opacity: 0.85;
}

.crm-empty-state h2 {
    font-size: 1.1rem;
    font-weight: 620;
    margin-bottom: 0.4rem;
}

.crm-empty-state p {
    color: var(--crm-muted);
    font-size: 0.9rem;
    margin-bottom: 1.1rem;
}

/* Keeps a wide permission matrix readable: the module column stays put while the
   verb columns scroll horizontally inside the table container. */
.table-responsive th[scope="row"] {
    position: sticky;
    left: 0;
    background: var(--crm-surface);
    z-index: 1;
}

.table-responsive {
    border-radius: inherit;
}

/* =============================================================================
   7. Lead CRM (Phase 4)
   ========================================================================== */

/* ---------- stat tiles ---------- */

.crm-stat-tile {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    height: 100%;
    padding: 0.95rem 1.05rem;
    background: var(--crm-surface);
    border: 1px solid var(--crm-border);
    border-radius: var(--crm-radius-lg);
    box-shadow: var(--crm-shadow-sm);
    overflow: hidden;
    transition: border-color var(--crm-fast) var(--crm-ease),
                box-shadow var(--crm-fast) var(--crm-ease),
                transform var(--crm-fast) var(--crm-ease);
}

/* The accent is a hairline down the left edge, painted as a pseudo-element so it
   sits inside the rounded corner instead of squaring it off. */
.crm-stat-tile::before {
    content: "";
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: 3px;
    background: var(--crm-stat-accent, var(--crm-accent));
}

.crm-stat-value {
    font-size: 1.65rem;
    font-weight: 660;
    line-height: 1.1;
    color: var(--crm-ink);
    letter-spacing: -0.025em;
    font-variant-numeric: tabular-nums;
}

.crm-stat-label {
    font-size: 0.78rem;
    color: var(--crm-muted);
}

.crm-stat-primary   { --crm-stat-accent: var(--crm-accent); }
.crm-stat-secondary { --crm-stat-accent: var(--crm-faint); }
.crm-stat-info      { --crm-stat-accent: var(--crm-info); }
.crm-stat-success   { --crm-stat-accent: var(--crm-success); }
.crm-stat-warning   { --crm-stat-accent: var(--crm-warning); }
.crm-stat-danger    { --crm-stat-accent: var(--crm-danger); }

/* A tile that links somewhere should say so on hover. */
.crm-stat-tile:has(.stretched-link):hover {
    border-color: var(--crm-border-strong);
    box-shadow: var(--crm-shadow);
    transform: translateY(-1px);
}

/* ---------- charts ---------- */

/* Chart.js sizes to its container, so the container needs an explicit height —
   without one the canvas grows on every resize event. */
.crm-chart {
    position: relative;
    height: 300px;
}

.crm-chart-sm {
    height: 220px;
}

/* ---------- kanban ---------- */

.crm-kanban {
    display: flex;
    gap: 0.8rem;
    /* The board scrolls sideways rather than squeezing ten columns into the
       viewport; a 90px-wide column is not a column. */
    overflow-x: auto;
    padding-bottom: 0.85rem;
    align-items: flex-start;
}

.crm-kanban-column {
    flex: 0 0 17.5rem;
    max-width: 17.5rem;
    background: var(--crm-surface-2);
    border: 1px solid var(--crm-border);
    border-radius: var(--crm-radius-lg);
    display: flex;
    flex-direction: column;
    max-height: calc(100vh - 16rem);
    transition: background-color var(--crm-fast) var(--crm-ease),
                outline-color var(--crm-fast) var(--crm-ease);
}

.crm-kanban-column.is-won {
    background: var(--crm-success-soft);
    border-color: transparent;
}

.crm-kanban-column.is-lost {
    background: var(--crm-danger-soft);
    border-color: transparent;
}

.crm-kanban-column.is-drop-target {
    outline: 2px dashed var(--crm-accent);
    outline-offset: -3px;
    background: var(--crm-accent-soft);
}

.crm-kanban-header {
    padding: 0.7rem 0.85rem 0.55rem;
    border-bottom: 1px solid var(--crm-border);
    position: sticky;
    top: 0;
    background: inherit;
    border-radius: var(--crm-radius-lg) var(--crm-radius-lg) 0 0;
    z-index: 1;
}

.crm-kanban-title {
    font-size: 0.83rem;
    font-weight: 660;
    color: var(--crm-ink);
    letter-spacing: -0.005em;
}

.crm-kanban-count {
    display: inline-block;
    margin-left: 0.4rem;
    padding: 0 0.45rem;
    border-radius: 999px;
    background: var(--crm-surface-3);
    color: var(--crm-muted);
    font-size: 0.7rem;
    font-weight: 650;
    font-variant-numeric: tabular-nums;
}

.crm-kanban-value {
    font-size: 0.72rem;
    color: var(--crm-muted);
    margin-top: 0.12rem;
    font-variant-numeric: tabular-nums;
}

.crm-kanban-body {
    padding: 0.6rem;
    overflow-y: auto;
    flex: 1 1 auto;
    min-height: 6rem;
}

.crm-kanban-empty {
    color: var(--crm-faint);
    font-size: 0.78rem;
    text-align: center;
    margin: 1.6rem 0;
}

.crm-kanban-card {
    background: var(--crm-surface);
    border: 1px solid var(--crm-border);
    border-radius: var(--crm-radius);
    padding: 0.62rem 0.72rem;
    margin-bottom: 0.55rem;
    box-shadow: var(--crm-shadow-xs);
    transition: box-shadow var(--crm-fast) var(--crm-ease),
                border-color var(--crm-fast) var(--crm-ease),
                opacity var(--crm-fast) var(--crm-ease),
                transform var(--crm-fast) var(--crm-ease);
}

.crm-kanban-card[draggable="true"] {
    cursor: grab;
}

.crm-kanban-card:hover {
    box-shadow: var(--crm-shadow);
    border-color: var(--crm-border-strong);
    transform: translateY(-1px);
}

.crm-kanban-card.is-dragging {
    opacity: 0.45;
    transform: rotate(1.5deg);
    cursor: grabbing;
}

/* While the server decides, the card is visibly not yet settled. */
.crm-kanban-card.is-saving {
    opacity: 0.6;
    pointer-events: none;
}

.crm-kanban-name {
    font-weight: 600;
    font-size: 0.86rem;
    color: var(--crm-ink);
}

.crm-kanban-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    font-size: 0.74rem;
    color: var(--crm-muted);
    margin-top: 0.2rem;
}

.crm-kanban-project {
    font-size: 0.74rem;
    color: var(--crm-accent);
    margin-top: 0.15rem;
}

.crm-kanban-footer {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-top: 0.5rem;
    padding-top: 0.45rem;
    border-top: 1px dashed var(--crm-border);
}

.crm-kanban-owner {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: var(--crm-accent-soft-2);
    color: var(--crm-accent-ink);
    font-size: 0.64rem;
    font-weight: 700;
    flex: 0 0 auto;
}

.crm-kanban-more {
    display: block;
    text-align: center;
    font-size: 0.75rem;
    padding: 0.4rem;
    color: var(--crm-accent);
    text-decoration: none;
    border-radius: var(--crm-radius-sm);
}

.crm-kanban-more:hover {
    background: var(--crm-surface-3);
}

/* ---------- activity timeline ---------- */

.crm-timeline-day {
    font-size: 0.7rem;
    font-weight: 680;
    text-transform: uppercase;
    letter-spacing: 0.09em;
    color: var(--crm-faint);
    margin: 1.25rem 0 0.6rem;
}

.crm-timeline-day:first-child {
    margin-top: 0;
}

.crm-timeline {
    list-style: none;
    margin: 0;
    padding: 0 0 0 1.6rem;
    position: relative;
}

/* The spine. Drawn on the list rather than per item so it does not break between
   entries. */
.crm-timeline::before {
    content: "";
    position: absolute;
    left: 0.65rem;
    top: 0.4rem;
    bottom: 0.4rem;
    width: 2px;
    background: var(--crm-border);
    border-radius: 2px;
}

.crm-timeline-item {
    position: relative;
    padding: 0 0 0.95rem 0.9rem;
}

.crm-timeline-dot {
    position: absolute;
    left: -1.6rem;
    top: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.55rem;
    height: 1.55rem;
    border-radius: 50%;
    background: var(--crm-surface);
    border: 2px solid var(--crm-border);
    color: var(--crm-muted);
    box-shadow: 0 0 0 3px var(--crm-canvas);
}

.crm-timeline-dot .crm-icon {
    width: 12px;
    height: 12px;
}

.crm-timeline-dot-primary   { border-color: var(--crm-accent);  color: var(--crm-accent); }
.crm-timeline-dot-success   { border-color: var(--crm-success); color: var(--crm-success); }
.crm-timeline-dot-danger    { border-color: var(--crm-danger);  color: var(--crm-danger); }
.crm-timeline-dot-warning   { border-color: var(--crm-warning); color: var(--crm-warning); }
.crm-timeline-dot-info      { border-color: var(--crm-info);    color: var(--crm-info); }
.crm-timeline-dot-secondary { border-color: var(--crm-faint);   color: var(--crm-faint); }

.crm-timeline-body {
    font-size: 0.86rem;
    line-height: 1.45;
}

/* =============================================================================
   8. Inventory (Phase 3)
   ========================================================================== */

/* The single source of truth for what each unit status looks like. The database
   stores the status; the colour lives here and nowhere else, so re-theming is a
   CSS edit rather than a data migration. Each status gets a fill, a line and an
   ink colour, so the same class works on a tile, a dot and a bar segment. */

/* Six hues drawn from the template's own extended set, so a held unit is the
   same amber as its `--secondary` and a booked one the same blue as its `--info`.
   Nothing here invents a colour the template does not ship.

   Two of the six had to move when the brand went red. `sold` used to be the brand
   colour and `cancelled` the danger colour; with a vermilion brand those are
   #E41F07 and #EF1E1E, near-identical reds sitting in the same legend. Sold takes
   the template's `--purple` instead — the one hue in its palette that collides
   with nothing else here — and cancelled keeps the red, which is where a viewer
   expects to find it.

   Brand red is deliberately absent from this legend. It is the primary button
   and the active nav pill; a unit tile wearing it would read as a control.

   Each row is a tint, the full-strength colour, and a darkened ink. The fill is
   too light to carry text at full strength, so the ink is a deeper step of the
   same hue — every one of the six clears 5:1 on its own fill. The line is a
   border or a dot, never the only carrier of the meaning: the template's green
   and amber are bright enough that they come in under 3:1 on their own tints,
   and the label beside them is what the reader is actually meant to use. */
.status-available { --crm-status-fill: #e8f9e8; --crm-status-line: #1abe17; --crm-status-ink: #0f7a0d; }
.status-hold      { --crm-status-fill: #fff6e6; --crm-status-line: #ffa201; --crm-status-ink: #8a6400; }
.status-blocked   { --crm-status-fill: #f1f4f7; --crm-status-line: #707070; --crm-status-ink: #545555; }
.status-booked    { --crm-status-fill: #eaf2fd; --crm-status-line: #2972d5; --crm-status-ink: #1a5fbd; }
.status-sold      { --crm-status-fill: #f1ebf7; --crm-status-line: #800080; --crm-status-ink: #6b006b; }
.status-cancelled { --crm-status-fill: #fde9e9; --crm-status-line: #d81a1a; --crm-status-ink: #c01616; }

/* On a dark canvas all three roles have to move. The pale fills above turn into
   glare, so the fill becomes a tint. And the deepened lines — which are what make
   these tones calm in light mode — go nearly invisible against a dark ground, so
   they lift to the same hue at a lighter value. Hue is what carries the meaning
   and hue is what stays fixed; value follows the theme. */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .status-available { --crm-status-fill: rgba(26, 190, 23, 0.2); --crm-status-line: #3dd83a; --crm-status-ink: #86efac; }
    :root:not([data-theme="light"]) .status-hold      { --crm-status-fill: rgba(255, 162, 1, 0.2); --crm-status-line: #ffb733; --crm-status-ink: #ffd28a; }
    :root:not([data-theme="light"]) .status-blocked   { --crm-status-fill: rgba(151, 170, 193, 0.22); --crm-status-line: #97aac1; --crm-status-ink: #c6cfdc; }
    :root:not([data-theme="light"]) .status-booked    { --crm-status-fill: rgba(47, 128, 237, 0.22); --crm-status-line: #5b9cf5; --crm-status-ink: #a8c9fb; }
    :root:not([data-theme="light"]) .status-sold      { --crm-status-fill: rgba(128, 0, 128, 0.3); --crm-status-line: #d07ad0; --crm-status-ink: #e3aee3; }
    :root:not([data-theme="light"]) .status-cancelled { --crm-status-fill: rgba(239, 30, 30, 0.22); --crm-status-line: #ff5a5a; --crm-status-ink: #ffa0a0; }
}

:root[data-theme="dark"] .status-available { --crm-status-fill: rgba(26, 190, 23, 0.2); --crm-status-line: #3dd83a; --crm-status-ink: #86efac; }
:root[data-theme="dark"] .status-hold      { --crm-status-fill: rgba(255, 162, 1, 0.2); --crm-status-line: #ffb733; --crm-status-ink: #ffd28a; }
:root[data-theme="dark"] .status-blocked   { --crm-status-fill: rgba(151, 170, 193, 0.22); --crm-status-line: #97aac1; --crm-status-ink: #c6cfdc; }
:root[data-theme="dark"] .status-booked    { --crm-status-fill: rgba(47, 128, 237, 0.22); --crm-status-line: #5b9cf5; --crm-status-ink: #a8c9fb; }
:root[data-theme="dark"] .status-sold      { --crm-status-fill: rgba(128, 0, 128, 0.3); --crm-status-line: #d07ad0; --crm-status-ink: #e3aee3; }
:root[data-theme="dark"] .status-cancelled { --crm-status-fill: rgba(239, 30, 30, 0.22); --crm-status-line: #ff5a5a; --crm-status-ink: #ffa0a0; }

.crm-status-dot {
    display: inline-block;
    width: 0.7rem;
    height: 0.7rem;
    border-radius: 50%;
    background-color: var(--crm-status-fill, var(--crm-surface-3));
    border: 1px solid var(--crm-status-line, var(--crm-border));
    flex: 0 0 auto;
}

.crm-status-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem 1rem;
}

.crm-status-legend-item {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    font-size: 0.8rem;
    white-space: nowrap;
}

.crm-status-legend-label {
    color: var(--crm-muted);
}

.crm-status-legend-count {
    font-variant-numeric: tabular-nums;
    font-weight: 650;
    color: var(--crm-ink);
}

/* ---------- summary bar ---------- */

.crm-inventory-summary {
    display: flex;
    flex-direction: column;
    gap: 0.65rem;
}

/* A proportional bar rather than six loose numbers: the mix of a project is what
   a sales head reads at a glance, and proportions read faster than counts. */
.crm-status-bar {
    display: flex;
    width: 100%;
    height: 0.8rem;
    border-radius: 999px;
    overflow: hidden;
    background-color: var(--crm-surface-3);
    border: 1px solid var(--crm-border);
}

.crm-status-bar-segment {
    background-color: var(--crm-status-line, var(--crm-border-strong));
    min-width: 2px;
    transition: flex-basis var(--crm-medium) var(--crm-ease);
}

.crm-status-bar-segment:not(:last-child) {
    border-right: 1px solid var(--crm-surface);
}

/* ---------- visual grid ---------- */

.crm-floor-row {
    display: flex;
    align-items: flex-start;
    gap: 0.75rem;
    padding: 0.45rem 0;
    border-bottom: 1px solid var(--crm-border-soft);
}

.crm-floor-row:last-child {
    border-bottom: 0;
}

.crm-floor-label {
    flex: 0 0 8.5rem;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding-top: 0.5rem;
    font-size: 0.8rem;
    font-weight: 620;
    color: var(--crm-muted);
}

.crm-floor-count {
    font-weight: 400;
    font-variant-numeric: tabular-nums;
    color: var(--crm-faint);
}

.crm-floor-units {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
}

/* Tiles are buttons, not divs: keyboard users reach the drawer for free, and the
   focus ring lands where it should. */
.crm-unit-tile {
    flex: 0 0 auto;
    width: 6.4rem;
    display: flex;
    flex-direction: column;
    gap: 0.08rem;
    padding: 0.42rem 0.55rem;
    text-align: left;
    border: 1px solid var(--crm-status-line, var(--crm-border));
    border-left-width: 3px;
    border-radius: var(--crm-radius-sm);
    background-color: var(--crm-status-fill, var(--crm-surface));
    color: var(--crm-status-ink, var(--crm-ink));
    line-height: 1.2;
    cursor: pointer;
    transition: transform var(--crm-fast) var(--crm-ease),
                box-shadow var(--crm-fast) var(--crm-ease);
}

.crm-unit-tile:hover {
    transform: translateY(-2px);
    box-shadow: var(--crm-shadow);
}

.crm-unit-tile:focus-visible {
    outline: 2px solid var(--crm-accent);
    outline-offset: 2px;
}

.crm-unit-number {
    font-weight: 650;
    font-size: 0.8rem;
    letter-spacing: -0.005em;
}

.crm-unit-bhk {
    font-size: 0.68rem;
    opacity: 0.9;
}

.crm-unit-price {
    font-size: 0.68rem;
    font-variant-numeric: tabular-nums;
    opacity: 0.9;
}

/* ---------- unit drawer ---------- */

.crm-drawer {
    position: fixed;
    inset: 0;
    z-index: 1050;
    pointer-events: none;
    visibility: hidden;
}

.crm-drawer.is-open {
    pointer-events: auto;
    visibility: visible;
}

/* Transparent is not the same as absent. An element at opacity 0 still takes every
   click that lands on it, so the backdrop states its own pointer-events rather than
   relying on the drawer above it to switch them off — the nesting is correct now, and
   this makes the rule hold even if the markup is moved again. */
.crm-drawer-backdrop {
    position: absolute;
    inset: 0;
    background-color: var(--crm-overlay);
    backdrop-filter: blur(2px);
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--crm-medium) var(--crm-ease);
}

.crm-drawer-backdrop.is-visible {
    opacity: 1;
    pointer-events: auto;
}

.crm-drawer-panel {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    width: min(31rem, 100%);
    display: flex;
    flex-direction: column;
    background-color: var(--crm-surface);
    border-left: 1px solid var(--crm-border);
    box-shadow: var(--crm-shadow-lg);
    transform: translateX(100%);
    transition: transform var(--crm-medium) var(--crm-ease);
}

.crm-drawer.is-open .crm-drawer-panel {
    transform: translateX(0);
}

.crm-drawer-content {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
}

.crm-drawer-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
    padding: 1.1rem 1.35rem;
    border-bottom: 1px solid var(--crm-border);
}

.crm-drawer-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    padding: 1.1rem 1.35rem;
}

.crm-drawer-footer {
    display: flex;
    justify-content: flex-end;
    gap: 0.5rem;
    padding: 0.8rem 1.35rem;
    border-top: 1px solid var(--crm-border);
    background-color: var(--crm-surface-2);
}

.crm-drawer-section {
    font-size: 0.7rem;
    font-weight: 680;
    text-transform: uppercase;
    letter-spacing: 0.09em;
    color: var(--crm-faint);
    margin: 1.4rem 0 0.6rem;
}

.crm-drawer-loading {
    padding: 2.5rem 1.35rem;
    text-align: center;
    color: var(--crm-muted);
}

body.crm-drawer-open {
    overflow: hidden;
}

/* The full unit page reuses the same card partial, so the drawer chrome has to
   stop behaving like a slide-over when it is simply sitting inside a card. */
.crm-unit-page .crm-drawer-header,
.crm-unit-page .crm-drawer-footer {
    padding-left: 0;
    padding-right: 0;
    background-color: transparent;
}

.crm-unit-page .crm-drawer-body {
    overflow: visible;
    padding-left: 0;
    padding-right: 0;
}

.crm-unit-page [data-drawer-close] {
    display: none;
}

/* ---------- price break-up ---------- */

.crm-price-table td {
    padding: 0.32rem 0;
    border-color: var(--crm-border-soft);
    font-size: 0.87rem;
}

.crm-price-table td:last-child {
    font-variant-numeric: tabular-nums;
}

.crm-price-total td {
    border-top: 2px solid var(--crm-border);
    font-weight: 700;
    font-size: 1rem;
    color: var(--crm-ink);
    padding-top: 0.5rem;
}

/* ---------- project media gallery ---------- */

.crm-gallery {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
    gap: 0.75rem;
}

.crm-gallery-item {
    position: relative;
    border: 1px solid var(--crm-border);
    border-radius: var(--crm-radius);
    overflow: hidden;
    background-color: var(--crm-surface-2);
    transition: border-color var(--crm-fast) var(--crm-ease),
                box-shadow var(--crm-fast) var(--crm-ease);
}

.crm-gallery-item:hover {
    border-color: var(--crm-border-strong);
    box-shadow: var(--crm-shadow-sm);
}

.crm-gallery-item img {
    display: block;
    width: 100%;
    height: 8rem;
    object-fit: cover;
}

.crm-gallery-actions {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.35rem;
    padding: 0.45rem 0.55rem;
    font-size: 0.78rem;
}

/* =============================================================================
   9. Sign-in

   A two-panel page: the form on the left, and on the right a brand panel that
   says what this product is. The panel is decoration and is dropped on small
   screens rather than stacked, because nobody scrolls past a gradient to reach a
   password field.
   ========================================================================== */

.crm-auth {
    min-height: 100vh;
    min-height: 100dvh;
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    background: var(--crm-surface);
}

.crm-auth-main {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 2.5rem 1.5rem;
    min-width: 0;
}

.crm-auth-card {
    width: 100%;
    max-width: 400px;
}

.crm-auth-brand {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin-bottom: 2rem;
}

.crm-auth-brand div {
    display: flex;
    flex-direction: column;
    line-height: 1.2;
}

.crm-auth-brand strong {
    color: var(--crm-ink);
    font-size: 1rem;
    font-weight: 640;
}

.crm-auth-brand div span {
    font-size: 0.78rem;
    color: var(--crm-muted);
}

.crm-auth-title {
    font-size: 1.7rem;
    font-weight: 700;
    letter-spacing: -0.004em;
    margin-bottom: 0.35rem;
}

.crm-auth-subtitle {
    color: var(--crm-muted);
    font-size: 0.9rem;
    margin-bottom: 1.6rem;
}

.crm-auth-footer {
    margin-top: 2rem;
    color: var(--crm-faint);
    font-size: 0.78rem;
}

/* The brand panel. */
.crm-auth-aside {
    position: relative;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 1.5rem;
    padding: 3rem;
    overflow: hidden;
    /* The template's own brand gradient runs #E41F07 to #FFA201. Taken
       literally it puts white copy on amber at 2:1, so the panel keeps the red
       end and deepens instead of brightening; the amber arrives as the blurred
       light in the corner below, where nothing has to be read on top of it. */
    background: linear-gradient(150deg, #e41f07 0%, #b81806 50%, #7a1003 100%);
    color: rgba(255, 255, 255, 0.86);
}

/* Two soft lights behind the copy, so the panel has depth without an image file
   to ship, cache and keep in sync with the palette. */
.crm-auth-aside::before,
.crm-auth-aside::after {
    content: "";
    position: absolute;
    border-radius: 50%;
    filter: blur(80px);
    pointer-events: none;
}

.crm-auth-aside::before {
    width: 26rem;
    height: 26rem;
    top: -8rem;
    right: -6rem;
    background: rgba(255, 255, 255, 0.28);
}

.crm-auth-aside::after {
    width: 22rem;
    height: 22rem;
    bottom: -7rem;
    left: -5rem;
    background: rgba(255, 162, 1, 0.38);
}

.crm-auth-aside > * {
    position: relative;
    z-index: 1;
    max-width: 34rem;
}

.crm-auth-aside h2 {
    color: #fff;
    font-size: 2rem;
    font-weight: 660;
    letter-spacing: -0.03em;
    line-height: 1.2;
}

.crm-auth-aside p {
    color: rgba(255, 255, 255, 0.86);
    font-size: 1rem;
    margin-bottom: 0;
}

.crm-auth-points {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.7rem;
}

.crm-auth-points li {
    display: flex;
    align-items: flex-start;
    gap: 0.65rem;
    font-size: 0.92rem;
    color: rgba(255, 255, 255, 0.9);
}

.crm-auth-points .crm-icon {
    width: 18px;
    height: 18px;
    margin-top: 0.15rem;
    color: #ffc266;
    flex: 0 0 auto;
}

/* =============================================================================
   10. Responsive, motion, print
   ========================================================================== */

@media (max-width: 1199.98px) {
    .crm-auth {
        grid-template-columns: 1fr;
    }

    /* Decoration, not content: on a narrow screen it is dropped rather than
       stacked above the form. */
    .crm-auth-aside {
        display: none;
    }
}

@media (max-width: 991.98px) {
    .crm-sidebar {
        position: fixed;
        left: 0;
        top: 0;
        z-index: 30;
        transform: translateX(-100%);
        transition: transform var(--crm-medium) var(--crm-ease);
        box-shadow: var(--crm-shadow-lg);
    }

    .crm-sidebar.is-open {
        transform: translateX(0);
    }

    .crm-sidebar-toggle {
        display: flex;
    }

    .crm-sidebar-backdrop.is-visible {
        display: block;
    }

    .crm-topbar {
        padding-left: 1rem;
        padding-right: 1rem;
    }

    .crm-content {
        padding: 1.1rem 1rem 2rem;
    }

    .crm-footer {
        padding-left: 1rem;
        padding-right: 1rem;
    }

    .crm-dl {
        grid-template-columns: 1fr;
        gap: 0.1rem;
    }

    .crm-dl dt {
        font-size: 0.78rem;
    }

    .crm-dl dd {
        margin-bottom: 0.65rem;
    }
}

@media (max-width: 767.98px) {
    .crm-card {
        padding: 1rem;
    }

    .crm-card > .table-responsive:first-child,
    .crm-card > .table-responsive:only-child {
        margin: -1rem;
    }

    .crm-card > .table-responsive > .table > thead th:first-child,
    .crm-card > .table-responsive > .table > tbody td:first-child,
    .crm-card > .table-responsive > .table > tbody th:first-child { padding-left: 1rem; }

    .crm-card > .table-responsive > .table > thead th:last-child,
    .crm-card > .table-responsive > .table > tbody td:last-child { padding-right: 1rem; }

    .crm-page-intro h2 {
        font-size: 1.25rem;
    }

    .crm-kanban-column {
        flex-basis: 15rem;
        max-width: 15rem;
    }

    .crm-chart {
        height: 240px;
    }

    .crm-floor-row {
        flex-direction: column;
        gap: 0.35rem;
    }

    .crm-floor-label {
        flex: 1 1 auto;
        width: 100%;
        padding-top: 0;
    }

    .crm-unit-tile {
        width: calc(50% - 0.2rem);
    }

    .crm-drawer-panel {
        width: 100%;
    }

    .crm-stat-value {
        font-size: 1.4rem;
    }
}

/* Someone who has asked their system to stop animating things means it. */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }

    .crm-card:hover,
    .crm-stat-tile:hover,
    .crm-unit-tile:hover,
    .crm-kanban-card:hover {
        transform: none;
    }
}

@media print {
    .crm-sidebar,
    .crm-topbar,
    .crm-footer,
    .crm-drawer,
    .crm-sidebar-backdrop,
    .btn {
        display: none !important;
    }

    body.crm-app {
        background: #fff;
    }

    .crm-content {
        padding: 0;
        max-width: none;
    }

    .crm-card {
        border-color: #cdcdcd;
        box-shadow: none;
        break-inside: avoid;
    }
}

/* =============================================================================
   11. Shell odds and ends
   ========================================================================== */

/* A keyboard user should not have to tab through the whole sidebar on every page
   to reach the thing they came for. Off-screen until focused. */
.crm-skip-link {
    position: absolute;
    left: 0.75rem;
    top: -3rem;
    z-index: 60;
    padding: 0.5rem 0.9rem;
    border-radius: var(--crm-radius-sm);
    background: var(--crm-action);
    color: #fff;
    font-size: 0.85rem;
    font-weight: 600;
    text-decoration: none;
    transition: top var(--crm-fast) var(--crm-ease);
}

.crm-skip-link:focus {
    top: 0.75rem;
    color: #fff;
}

/* Who is signed in, at the head of the account menu. The email used to be a
   dropdown-header, which truncated it and read as a section label rather than as
   the answer to "which account am I in?" */
.crm-menu-identity {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.5rem 0.6rem 0.6rem;
}

.crm-menu-identity div {
    display: flex;
    flex-direction: column;
    line-height: 1.3;
    min-width: 0;
}

.crm-menu-identity strong {
    font-size: 0.86rem;
    font-weight: 620;
    color: var(--crm-ink);
}

.crm-menu-identity span {
    font-size: 0.76rem;
    color: var(--crm-muted);
    overflow: hidden;
    text-overflow: ellipsis;
}

.crm-topbar-user .dropdown-menu {
    min-width: 15rem;
}

.crm-topbar-user .dropdown-menu form {
    margin: 0;
}

/* A card that is the whole page — an error, a refusal, a module that does not
   exist yet — rather than one panel among several. */
.crm-card-centered {
    max-width: 640px;
    margin: 2rem auto;
}

.crm-error-code {
    font-size: 3.4rem;
    font-weight: 700;
    line-height: 1;
    letter-spacing: -0.04em;
    color: var(--crm-ink);
    margin-bottom: 0.5rem;
    font-variant-numeric: tabular-nums;
}

code {
    background: var(--crm-surface-3);
    color: var(--crm-ink-2);
    padding: 0.1rem 0.35rem;
    border-radius: var(--crm-radius-sm);
    font-size: 0.85em;
}

/* The brand is a link now, so it needs to stop looking like body text and start
   showing that it responds. */
a.crm-brand {
    text-decoration: none;
    transition: background-color var(--crm-fast) var(--crm-ease);
}

a.crm-brand:hover {
    background: var(--crm-sidebar-hover);
}

/* =============================================================================
   12. Filter bars

   Every list screen opens with a GET form of dropdowns. Left on the same white
   as the data below it, that strip reads as content; recessed slightly, it reads
   as controls — which is what it is. Matched on shape rather than on a new class
   so the twelve views that already have one need no edit.
   ========================================================================== */

.crm-card:has(> form[method="get"]) {
    background: var(--crm-surface-2);
    box-shadow: none;
    padding: 0.9rem 1rem;
}

.crm-card:has(> form[method="get"]) .form-label {
    font-size: 0.74rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    color: var(--crm-muted);
    margin-bottom: 0.25rem;
}

.crm-card:has(> form[method="get"]) .form-control,
.crm-card:has(> form[method="get"]) .form-select {
    background-color: var(--crm-surface);
}

/* Browsers without :has() get the plain card. It is a shade of grey, not a
   feature, and nothing is unusable without it. */

/* =============================================================================
   13. Small refinements that apply everywhere
   ========================================================================== */

/* A sortable column header should look like something you can click. */
.table thead a {
    color: inherit;
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    border-radius: var(--crm-radius-sm);
}

.table thead a:hover {
    color: var(--crm-accent);
}

/* Row actions stay put while the eye scans, and stop competing with the data. */
.table .btn-group-sm > .btn,
.table .btn-sm {
    --bs-btn-padding-y: 0.2rem;
    --bs-btn-padding-x: 0.55rem;
    --bs-btn-font-size: 0.78rem;
}

/* Ids and codes are read character by character, so they get a face built for it
   and a tone that keeps them out of the way of the name beside them. */
.font-monospace,
code {
    color: var(--crm-muted);
}

.table .font-monospace {
    font-size: 0.78rem;
}

/* Bootstrap's `.small` inside a muted context was landing at two different greys
   depending on the parent. One grey. */
.small,
small {
    font-size: 0.82rem;
}

/* Forms: a section of a long form reads better with a rule under its heading than
   with more whitespace, which a dense form has none of to spare. */
form .crm-card-title {
    padding-bottom: 0.55rem;
    border-bottom: 1px solid var(--crm-border-soft);
}

/* The validation summary is an error list, and an empty one should not reserve a
   red box's worth of space at the top of every form. */
.validation-summary-valid {
    display: none;
}

.field-validation-error,
.text-danger.small {
    display: inline-block;
    margin-top: 0.25rem;
    font-size: 0.78rem;
}

.form-control.input-validation-error,
.form-select.input-validation-error {
    border-color: var(--crm-danger);
}

.form-control.input-validation-error:focus {
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--crm-danger) 25%, transparent);
}

/* Anything the user can drag or select-drag inside the boards. */
[draggable="true"] {
    -webkit-user-drag: element;
    user-select: none;
}

/* =============================================================================
   14. Display type

   One family now. The template sets its whole interface in Golos Text and
   separates a heading from a label by weight and colour alone, so
   `--crm-font-display` resolves to the body stack and the split that used to
   exist here — a serif for headings, a grotesque for working text — is gone.

   The rules below stay because they were never really about the family. They are
   the few places that need their own size and tracking rather than inheriting the
   global heading rule, and they still do.
   ========================================================================== */

.crm-auth-aside h2 {
    font-family: var(--crm-font-display);
    font-weight: 700;
    font-size: 2.1rem;
    line-height: 1.24;
    letter-spacing: -0.008em;
}

.crm-error-code {
    font-family: var(--crm-font-display);
    font-weight: 800;
    letter-spacing: -0.02em;
}

.crm-brand-text strong {
    font-family: var(--crm-font-sans);
    font-weight: 650;
    font-size: 1.05rem;
    letter-spacing: -0.01em;
}

/* A hairline under the sign-in headline. One rule, and the panel stops looking
   like a gradient with words on it. */
.crm-auth-aside h2::after {
    content: "";
    display: block;
    width: 3.5rem;
    height: 2px;
    margin-top: 1.1rem;
    background: rgba(255, 255, 255, 0.55);
    border-radius: 2px;
}

/* =============================================================================
   15. Weight and spacing

   The palette above is most of the premium feel; the rest is restraint. Cards
   sit on lighter borders and softer shadows, and headings get a little more room
   than a dense admin template usually allows.
   ========================================================================== */

.crm-page-intro {
    margin-bottom: 1.5rem;
}

.crm-page-intro h2 {
    font-weight: 600;
    letter-spacing: -0.021em;
}

.crm-card {
    padding: 1.25rem 1.35rem;
}

.crm-card > .table-responsive:first-child,
.crm-card > .table-responsive:only-child {
    margin: -1.25rem -1.35rem;
}

.crm-card > .table-responsive > .table > thead th:first-child,
.crm-card > .table-responsive > .table > tbody td:first-child,
.crm-card > .table-responsive > .table > tbody th:first-child { padding-left: 1.35rem; }

.crm-card > .table-responsive > .table > thead th:last-child,
.crm-card > .table-responsive > .table > tbody td:last-child { padding-right: 1.35rem; }

.crm-card > .table-responsive:first-child + .crm-pager {
    margin: 0 -1.35rem -1.25rem;
    padding: 0.9rem 1.35rem;
}

/* Column headers were shouting in full uppercase at 650 weight. Same idea,
   quieter. */
.table > thead th {
    font-weight: 600;
    font-size: 0.72rem;
    letter-spacing: 0.06em;
    background: transparent;
    border-bottom-color: var(--crm-border-strong);
}

.crm-stat-value {
    font-weight: 600;
    letter-spacing: -0.028em;
}

/* A stat tile with no semantic colour of its own carries the brand colour. */
.crm-stat-primary { --crm-stat-accent: var(--crm-accent); }

/* =============================================================================
   16. Component weight

   The palette does most of the work; the rest is proportion. Cards are rounder
   and float rather than sit inside a border, and body copy runs at 15px with a
   tight 1.375 line-height.

   The primary button no longer glows. An earlier palette had it cast its own
   colour as a signature; a red this saturated casting red reads as a lamp rather
   than as a button, and the template it now follows puts nothing under its
   buttons at all.
   ========================================================================== */

body.crm-app,
body.crm-plain {
    font-size: 0.9375rem;
    line-height: 1.375;
}

h1, h2, h3, h4, h5, h6 {
    color: var(--crm-ink);
    font-weight: 700;
    letter-spacing: -0.004em;
}

/* Cards float on the canvas instead of being outlined on it. The border is kept
   at hairline weight for the one case a shadow cannot carry — a card sitting
   directly on another card. */
.crm-card,
.crm-stat-tile {
    border-color: transparent;
    box-shadow: var(--crm-shadow-xs);
    border-radius: var(--crm-radius-lg);
}

.crm-card:hover,
a.crm-card:hover,
.crm-stat-tile:has(.stretched-link):hover {
    border-color: transparent;
    box-shadow: var(--crm-shadow-sm);
}

/* Filter strips stay outlined rather than floating — two stacked shadows read as
   one blurred edge, and the strip is chrome, not content. */
.crm-card:has(> form[method="get"]) {
    background: var(--crm-surface);
    border-color: var(--crm-border);
    box-shadow: none;
}

/* The primary button casts its own colour. This is the single most recognisable
   thing about the style and it is worth exactly one line. */
.btn-primary {
    box-shadow: var(--crm-accent-glow);
}

.btn-primary:hover {
    box-shadow: var(--crm-accent-glow);
}

.btn-primary:disabled {
    box-shadow: none;
}

/* Bootstrap's outline buttons pick the brand colour up automatically; the
   secondary one is remapped so it reads as neutral rather than as a second
   brand. */
.btn-outline-secondary {
    --bs-btn-color: var(--crm-ink-2);
    --bs-btn-border-color: var(--crm-border-strong);
}

/* Badges are pills of tinted colour, matching the status treatment. */
.badge {
    font-weight: 500;
    letter-spacing: 0;
    padding: 0.32em 0.7em;
}

/* Column headers: small, spaced, and the same soft grey as body copy. */
.table > thead th {
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.08em;
    color: var(--crm-muted);
    border-bottom-color: var(--crm-border);
}

.table > tbody td,
.table > tbody th {
    padding: 0.78rem 0.75rem;
}

/* Inputs sit slightly taller than Bootstrap's default, which is what makes a form
   in this style feel unhurried. */
.form-control,
.form-select {
    padding: 0.53rem 0.85rem;
    border-radius: var(--crm-radius-sm);
}

.form-control:focus,
.form-select:focus {
    box-shadow: var(--crm-accent-glow);
}

.form-label {
    font-size: 0.8125rem;
    font-weight: 400;
    color: var(--crm-ink);
    text-transform: none;
    letter-spacing: 0;
}

.crm-card:has(> form[method="get"]) .form-label {
    text-transform: none;
    letter-spacing: 0;
    font-size: 0.8125rem;
    font-weight: 400;
    color: var(--crm-ink);
}

/* The topbar loses its border and floats over the canvas, so the sidebar and the
   content area read as one surface with a header sitting on it. */
.crm-topbar {
    border-bottom: 1px solid var(--crm-border);
}

.crm-footer {
    background: transparent;
    border-top-color: var(--crm-border);
}

/* The avatar and the mark in the rail now sit on the same white ground and wear
   the same gradient, running the other way: the mark brightens downward, the
   avatar darkens, so the two read as a pair without being the same object. Both
   stay dark enough for white initials at 0.76rem. */
.crm-avatar {
    background: linear-gradient(140deg, #e41f07 0%, #a81505 100%);
    color: #fff;
    box-shadow: none;
}

/* Kanban columns on a light canvas want a tint, not a border. */
.crm-kanban-column {
    background: var(--crm-surface-3);
    border-color: transparent;
}

.crm-kanban-card {
    border-color: transparent;
    box-shadow: var(--crm-shadow-xs);
    border-radius: var(--crm-radius);
}

.crm-kanban-card:hover {
    border-color: transparent;
    box-shadow: var(--crm-shadow-sm);
}

.crm-kanban-owner {
    background: var(--crm-accent-soft);
    color: var(--crm-accent-ink);
}

/* Drawer and dropdown corners follow the card radius. */
.crm-drawer-panel,
.dropdown-menu,
.modal-content {
    border-radius: var(--crm-radius-lg);
}

.crm-drawer-panel {
    border-left: 0;
}

/* Unit tiles keep their status border but pick up the softer corner. */
.crm-unit-tile {
    border-radius: var(--crm-radius-sm);
}

/* ==========================================================================
   Live updates
   ==========================================================================
   The toast host and the connection indicator. Both are quiet by design: a
   realtime feature that draws attention to itself is a realtime feature people
   turn off.
   -------------------------------------------------------------------------- */

.crm-toast-host {
    position: fixed;
    right: 1rem;
    bottom: 1rem;
    z-index: 1090;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    max-width: min(22rem, calc(100vw - 2rem));
}

.crm-toast-host .toast {
    background: var(--crm-surface);
    color: var(--crm-ink);
    border: 1px solid var(--crm-border);
    box-shadow: var(--crm-shadow);
}

/* A dot, not a banner. Grey when nothing is connected, which is the honest
   default rather than an optimistic green. */
.crm-live-dot {
    display: inline-block;
    width: 0.5rem;
    height: 0.5rem;
    border-radius: 50%;
    background: var(--crm-border);
    vertical-align: middle;
}

.crm-live-dot[data-state="live"] {
    background: var(--crm-success);
}

.crm-live-dot[data-state="connecting"] {
    background: var(--crm-warning);
}

.crm-live-dot[data-state="offline"] {
    background: var(--crm-ink-2);
    opacity: 0.4;
}

/* ==========================================================================
   Sales CRM dashboard
   ==========================================================================
   The dashboard is the first screen of the working day, so the hierarchy is
   deliberate: four numbers large enough to read without stopping, a rank of
   smaller ones below them, and then the panels that need actual attention.
   -------------------------------------------------------------------------- */

.crm-kpi {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    height: 100%;
    padding: 1.15rem 1.25rem;
    background: var(--crm-surface);
    border: 1px solid var(--crm-border);
    border-radius: var(--crm-radius-lg);
    box-shadow: var(--crm-shadow-sm);
    transition: border-color var(--crm-fast) var(--crm-ease),
                box-shadow var(--crm-fast) var(--crm-ease);
}

.crm-kpi:hover {
    box-shadow: var(--crm-shadow);
}

.crm-kpi-label {
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--crm-muted);
}

.crm-kpi-value {
    font-size: 2rem;
    font-weight: 680;
    line-height: 1.05;
    letter-spacing: -0.03em;
    color: var(--crm-ink);
    font-variant-numeric: tabular-nums;
}

.crm-kpi-foot {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    font-size: 0.78rem;
    color: var(--crm-muted);
}

/* Direction is carried by the arrow as well as the colour, so the meaning
   survives a monochrome print and a red-green colour deficiency. */
.crm-delta {
    display: inline-flex;
    align-items: center;
    gap: 0.15rem;
    padding: 0.1rem 0.4rem;
    border-radius: 999px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

.crm-delta-up {
    color: var(--crm-success);
    background: color-mix(in srgb, var(--crm-success) 12%, transparent);
}

.crm-delta-down {
    color: var(--crm-danger);
    background: color-mix(in srgb, var(--crm-danger) 12%, transparent);
}

/* The icon sits above the number in the small tiles, muted, so it labels the
   tile without competing with the figure. */
.crm-stat-icon {
    font-size: 0.95rem;
    line-height: 1;
    color: var(--crm-stat-accent, var(--crm-accent));
    opacity: 0.75;
}

/* ---------------------------------------------------------------- funnel */

.crm-funnel {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.85rem;
}

.crm-funnel-head {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 0.5rem;
    margin-bottom: 0.3rem;
}

.crm-funnel-label {
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--crm-ink);
}

.crm-funnel-count {
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--crm-ink);
    font-variant-numeric: tabular-nums;
}

/* The step-to-step rate, not the share of the total: "40% of qualified leads
   were won" is the question a sales head actually asks. */
.crm-funnel-rate {
    margin-left: 0.4rem;
    font-weight: 500;
    color: var(--crm-muted);
}

.crm-funnel-bar {
    height: 0.6rem;
    border-radius: 999px;
    background: var(--crm-canvas);
    overflow: hidden;
}

.crm-funnel-bar > span {
    display: block;
    height: 100%;
    border-radius: 999px;
    background: linear-gradient(90deg,
        var(--crm-accent) 0%,
        color-mix(in srgb, var(--crm-accent) 65%, var(--crm-info)) 100%);
    transition: width 400ms var(--crm-ease);
}

.crm-funnel-step:last-child .crm-funnel-bar > span {
    background: linear-gradient(90deg,
        var(--crm-success) 0%,
        color-mix(in srgb, var(--crm-success) 70%, var(--crm-accent)) 100%);
}

/* ---------------------------------------------------------------- agenda */

.crm-agenda {
    list-style: none;
    margin: 0;
    padding: 0;
}

.crm-agenda-item {
    display: flex;
    align-items: flex-start;
    gap: 0.65rem;
    padding: 0.55rem 0;
    border-bottom: 1px solid var(--crm-border);
}

.crm-agenda-item:last-child {
    border-bottom: 0;
    padding-bottom: 0;
}

.crm-agenda-when {
    flex: 0 0 2.75rem;
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--crm-muted);
    font-variant-numeric: tabular-nums;
    padding-top: 0.05rem;
}

.crm-agenda-item.is-overdue .crm-agenda-when {
    color: var(--crm-danger);
}

.crm-agenda-body {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    line-height: 1.3;
}

.crm-agenda-lead {
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--crm-ink);
    text-decoration: none;
}

.crm-agenda-lead:hover {
    color: var(--crm-accent);
}

/* One line, clipped. A follow-up subject can be a paragraph, and a panel that
   grows to fit the longest one stops being scannable. */
.crm-agenda-subject {
    font-size: 0.78rem;
    color: var(--crm-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.crm-agenda-flag {
    flex: 0 0 auto;
    font-size: 0.7rem;
}

/* ------------------------------------------------------------ mini stats */

.crm-mini-stat {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.1rem;
    padding: 0.7rem 0.4rem;
    background: var(--crm-canvas);
    border-radius: var(--crm-radius);
}

.crm-mini-value {
    font-size: 1.4rem;
    font-weight: 660;
    line-height: 1.1;
    color: var(--crm-ink);
    font-variant-numeric: tabular-nums;
}

.crm-mini-label {
    font-size: 0.72rem;
    color: var(--crm-muted);
}

/* ----------------------------------------------------------- owner rows */

.crm-owner-row {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    padding: 0.45rem 0;
    border-bottom: 1px solid var(--crm-border);
}

.crm-owner-row:last-child {
    border-bottom: 0;
}

.crm-owner-name {
    flex: 0 0 auto;
    max-width: 8rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Takes the leftover width, so the bars line up regardless of name length. */
.crm-owner-bar {
    flex: 1 1 auto;
    height: 0.35rem;
    border-radius: 999px;
    background: var(--crm-canvas);
    overflow: hidden;
}

.crm-owner-bar > span {
    display: block;
    height: 100%;
    border-radius: 999px;
    background: var(--crm-accent);
    opacity: 0.75;
}

@media (max-width: 575.98px) {
    .crm-kpi-value {
        font-size: 1.7rem;
    }

    .crm-owner-name {
        max-width: 6rem;
    }
}

/* ==========================================================================
   Notifications
   ==========================================================================
   The bell, its dropdown, and the full list. One set of row styles serves both
   surfaces, because they render the same partial shapes and two sets would
   drift the moment a new notification type appeared.
   -------------------------------------------------------------------------- */

.crm-notify {
    position: relative;
}

.crm-notify-toggle {
    position: relative;
}

/* A count, not a dot: "3" tells somebody whether to look now or later. Hidden
   entirely at zero, so a quiet bell looks quiet. */
.crm-notify-badge {
    position: absolute;
    top: 0.15rem;
    right: 0.1rem;
    min-width: 1rem;
    padding: 0 0.25rem;
    border-radius: 999px;
    background: var(--crm-danger);
    color: #fff;
    font-size: 0.6rem;
    font-weight: 700;
    line-height: 1rem;
    text-align: center;
    font-variant-numeric: tabular-nums;
    box-shadow: 0 0 0 2px var(--crm-surface);
}

.crm-note-menu {
    width: min(24rem, calc(100vw - 2rem));
    max-height: min(28rem, 70vh);
    overflow-y: auto;
    padding: 0;
}

.crm-note-menu-head,
.crm-note-menu-foot {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 0.5rem;
    padding: 0.6rem 0.9rem;
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--crm-muted);
}

/* Sticky so "mark all read" stays reachable in a long list without scrolling
   back up for it. */
.crm-note-menu-head {
    position: sticky;
    top: 0;
    z-index: 1;
    background: var(--crm-surface);
    border-bottom: 1px solid var(--crm-border);
}

.crm-note-menu-foot {
    justify-content: center;
    border-top: 1px solid var(--crm-border);
}

.crm-note-empty {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
    padding: 1.5rem 1rem;
    margin: 0;
    font-size: 0.83rem;
    color: var(--crm-muted);
}

.crm-note-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.crm-note {
    display: flex;
    align-items: flex-start;
    gap: 0.65rem;
    padding: 0.7rem 0.9rem;
    border-bottom: 1px solid var(--crm-border);
}

.crm-note:last-child {
    border-bottom: 0;
}

/* Unread is a tinted row plus a bar on the leading edge. Two signals, because
   a tint alone disappears on a low-contrast screen. */
.crm-note.is-unread {
    background: var(--crm-accent-soft);
    box-shadow: inset 3px 0 0 0 var(--crm-accent);
}

.crm-note-icon {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.9rem;
    height: 1.9rem;
    border-radius: 50%;
    background: var(--crm-canvas);
    color: var(--crm-muted);
}

.crm-note-danger { color: var(--crm-danger); background: color-mix(in srgb, var(--crm-danger) 12%, transparent); }
.crm-note-warning { color: var(--crm-warning); background: color-mix(in srgb, var(--crm-warning) 14%, transparent); }
.crm-note-accent { color: var(--crm-accent); background: var(--crm-accent-soft-2); }
.crm-note-muted { color: var(--crm-muted); }

.crm-note-body {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
}

.crm-note-title {
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--crm-ink);
    text-decoration: none;
    line-height: 1.3;
}

.crm-note-title:hover {
    color: var(--crm-accent);
}

/* Two lines then clipped. A notification body can be a paragraph, and a
   dropdown that grows to fit the longest one stops being scannable. */
.crm-note-text {
    font-size: 0.78rem;
    color: var(--crm-muted);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.crm-note-meta {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-top: 0.25rem;
    font-size: 0.72rem;
    color: var(--crm-muted);
}

.crm-note-time {
    flex: 0 0 auto;
    font-size: 0.72rem;
    color: var(--crm-faint);
    white-space: nowrap;
    padding-top: 0.1rem;
}

.crm-note-actions {
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 0.2rem;
}

/* The full-page list gets more room than the dropdown. */
.crm-note-list-page .crm-note {
    padding: 0.9rem 0.4rem;
}

.crm-note-list-page .crm-note.is-unread {
    border-radius: var(--crm-radius-sm);
    padding-left: 0.75rem;
}

/* ==========================================================================
   Conversion: site visit calendar, quotation panel, approval timeline
   ========================================================================== */

.crm-calendar {
    --crm-calendar-gap: 1px;
}

.crm-calendar-head,
.crm-calendar-grid {
    display: grid;
    grid-template-columns: repeat(7, minmax(0, 1fr));
    gap: var(--crm-calendar-gap);
}

.crm-calendar-weekday {
    padding: 0.4rem 0.5rem;
    font-size: 0.72rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--crm-muted);
}

.crm-calendar-grid {
    background: var(--crm-border);
    border: 1px solid var(--crm-border);
    border-radius: var(--crm-radius-sm);
    overflow: hidden;
}

/* Six fixed rows. A month whose visits all land in one week should not make
   that week three times as tall as the others. */
.crm-calendar-day {
    background: var(--crm-surface);
    min-height: 6.5rem;
    padding: 0.35rem;
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
}

.crm-calendar-day--muted {
    background: var(--crm-surface-2);
}

.crm-calendar-day--muted .crm-calendar-date {
    color: var(--crm-faint);
}

.crm-calendar-day--today {
    box-shadow: inset 0 0 0 2px var(--crm-accent);
}

.crm-calendar-date {
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--crm-muted);
}

.crm-calendar-visit {
    display: flex;
    align-items: baseline;
    gap: 0.3rem;
    padding: 0.15rem 0.35rem;
    border-radius: var(--crm-radius-sm);
    font-size: 0.72rem;
    text-decoration: none;
    color: inherit;
    overflow: hidden;
}

.crm-calendar-visit:hover {
    filter: brightness(0.96);
}

.crm-calendar-time {
    flex: 0 0 auto;
    font-variant-numeric: tabular-nums;
    font-weight: 600;
}

.crm-calendar-lead {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.crm-calendar-more {
    font-size: 0.7rem;
    color: var(--crm-muted);
    text-decoration: none;
}

@media (max-width: 767.98px) {
    .crm-calendar-day {
        min-height: 4.5rem;
    }

    .crm-calendar-lead {
        display: none;
    }
}

/* The pricing panel follows the form down a long page, so the total is always
   in view while the components are being typed. */
@media (min-width: 992px) {
    .crm-quote-summary {
        position: sticky;
        top: 1rem;
    }
}

.crm-quote-table td,
.crm-quote-table th {
    padding: 0.4rem 0.5rem;
}

.crm-quote-table td:last-child,
.crm-quote-table th:last-child {
    font-variant-numeric: tabular-nums;
}

/* One row per approval level, with the connecting line drawn between the
   markers rather than around them. */
.crm-approval-timeline {
    list-style: none;
    margin: 0;
    padding: 0;
}

.crm-approval-step {
    position: relative;
    display: flex;
    gap: 0.75rem;
    padding-bottom: 1rem;
}

.crm-approval-step:last-child {
    padding-bottom: 0;
}

.crm-approval-step::before {
    content: "";
    position: absolute;
    left: 0.85rem;
    top: 1.75rem;
    bottom: 0;
    width: 2px;
    background: var(--crm-border);
}

.crm-approval-step:last-child::before {
    display: none;
}

.crm-approval-marker {
    flex: 0 0 auto;
    width: 1.75rem;
    height: 1.75rem;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.78rem;
    font-weight: 600;
    background: var(--crm-surface);
    border: 2px solid var(--crm-border);
    color: var(--crm-muted);
    z-index: 1;
}

.crm-approval-body {
    flex: 1 1 auto;
    min-width: 0;
}

.crm-approval-step--done .crm-approval-marker {
    border-color: var(--bs-success, #198754);
    color: var(--bs-success, #198754);
}

.crm-approval-step--pending .crm-approval-marker {
    border-color: var(--bs-warning, #ffc107);
    color: var(--bs-warning, #ffc107);
}

.crm-approval-step--rejected .crm-approval-marker {
    border-color: var(--bs-danger, #dc3545);
    color: var(--bs-danger, #dc3545);
}

.crm-approval-step--moot {
    opacity: 0.6;
}

/* ==========================================================================
   AI: the lead panel, the assistant modals, the provider cards
   ========================================================================== */

/* A tinted edge rather than a heavier border. The panel sits above the lead's
   own facts and should read as commentary on them, not as another record. */
.crm-ai-panel {
    border-left: 3px solid color-mix(in srgb, var(--crm-accent) 55%, transparent);
}

.crm-ai-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
    padding: 1.5rem 1rem;
    text-align: center;
    color: var(--crm-muted);
}

.crm-ai-empty p {
    max-width: 44ch;
    font-size: 0.85rem;
    margin: 0;
}

.crm-ai-stat {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    height: 100%;
    padding: 0.65rem 0.75rem;
    background: var(--crm-canvas);
    border-radius: var(--crm-radius-sm);
}

.crm-ai-stat-value {
    font-size: 1.5rem;
    font-weight: 660;
    line-height: 1.1;
    color: var(--crm-ink);
    font-variant-numeric: tabular-nums;
}

.crm-ai-stat-value small {
    font-size: 0.8rem;
    font-weight: 500;
    color: var(--crm-muted);
}

.crm-ai-stat-label {
    font-size: 0.72rem;
    color: var(--crm-muted);
}

/* Bands, not a gradient: a salesperson reads hot / warm / cold, and a continuous
   ramp makes 61 and 64 look meaningfully different when they are not. */
.crm-ai-score--hot {
    box-shadow: inset 3px 0 0 var(--crm-danger);
}

.crm-ai-score--warm {
    box-shadow: inset 3px 0 0 var(--crm-warning, #f0a020);
}

.crm-ai-score--cool {
    box-shadow: inset 3px 0 0 var(--crm-info, #3b9edb);
}

.crm-ai-score--cold {
    box-shadow: inset 3px 0 0 var(--crm-border);
}

/* rule + AI = final, shown as arithmetic. The point is that a salesperson can
   see which half moved. */
.crm-ai-scores {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem;
    padding: 0.5rem 0.75rem;
    background: var(--crm-canvas);
    border-radius: var(--crm-radius-sm);
    font-size: 0.82rem;
    color: var(--crm-muted);
}

.crm-ai-scores strong {
    color: var(--crm-ink);
    font-variant-numeric: tabular-nums;
}

.crm-ai-scores-sep {
    color: var(--crm-faint);
}

.crm-ai-action {
    padding: 0.65rem 0.85rem;
    background: color-mix(in srgb, var(--crm-accent) 7%, transparent);
    border-radius: var(--crm-radius-sm);
}

.crm-ai-action-label {
    display: block;
    font-size: 0.7rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--crm-muted);
}

.crm-ai-list-label {
    font-size: 0.7rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--crm-muted);
}

.crm-ai-list {
    list-style: none;
    margin: 0.35rem 0 0;
    padding: 0;
    font-size: 0.85rem;
}

.crm-ai-list li {
    position: relative;
    padding-left: 1.15rem;
    margin-bottom: 0.2rem;
}

/* The mark carries the meaning as well as the colour, so it survives a
   monochrome print and a red-green colour deficiency. */
.crm-ai-list--good li::before {
    content: "\2713";
    position: absolute;
    left: 0;
    color: var(--crm-success);
}

.crm-ai-list--bad li::before {
    content: "\26A0";
    position: absolute;
    left: 0;
    color: var(--crm-warning, #f0a020);
}

.crm-ai-objection {
    padding: 0.55rem 0.7rem;
    background: var(--crm-canvas);
    border-radius: var(--crm-radius-sm);
}

.crm-ai-busy {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-top: 0.75rem;
    font-size: 0.85rem;
    color: var(--crm-muted);
}

.crm-ai-draft {
    padding-top: 0.75rem;
    border-top: 1px solid var(--crm-border);
}

.crm-ai-provider-card {
    display: flex;
    flex-direction: column;
}

.crm-ai-provider-row {
    padding: 0.6rem 0;
    border-bottom: 1px solid var(--crm-border);
}

.crm-ai-provider-row:last-child {
    border-bottom: none;
}

/* =============================================================================
   17. Permission tree (role rights)
   ========================================================================== */

/* Depth is carried by an indent and a rule down the left of each body, so the
   nesting stays readable when a branch is collapsed and the caret is all there is
   to go on. Everything below is token-driven, so dark mode needs no second block. */

.perm-tree__toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.6rem;
    padding-bottom: 0.9rem;
    margin-bottom: 0.35rem;
    border-bottom: 1px solid var(--crm-border);
}

.perm-tree__search {
    flex: 1 1 16rem;
    min-width: 12rem;
}

.perm-tree__actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem;
}

.perm-tree__summary {
    color: var(--crm-muted);
    font-size: 0.82rem;
    white-space: nowrap;
    margin-left: auto;
}

.perm-tree__bulk {
    min-width: 15rem;
    padding: 0.35rem;
}

.perm-tree__bulk-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 0.1rem 0.5rem;
}

.perm-tree__bulk-verb {
    font-size: 0.85rem;
    font-weight: 560;
}

.perm-tree__bulk-buttons .btn-link {
    padding: 0.1rem 0.3rem;
    font-size: 0.78rem;
    text-decoration: none;
}

.perm-tree__empty {
    color: var(--crm-muted);
    font-size: 0.88rem;
    padding: 1.25rem 0.25rem;
    margin: 0;
}

/* ---------- nodes ---------- */

.perm-node--group {
    border-top: 1px solid var(--crm-border-soft);
    padding-top: 0.15rem;
}

.perm-node--group:first-of-type {
    border-top: none;
}

.perm-node__head {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.4rem 0.35rem;
    border-radius: var(--crm-radius-sm);
}

.perm-node__head:hover {
    background: var(--crm-surface-3);
}

.perm-node__toggle {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.4rem;
    height: 1.4rem;
    padding: 0;
    border: none;
    border-radius: var(--crm-radius-sm);
    background: transparent;
    color: var(--crm-faint);
    cursor: pointer;
}

.perm-node__toggle:hover {
    color: var(--crm-ink);
    background: var(--crm-surface-2);
}

/* A CSS caret rather than an icon font: one less asset, and it rotates cleanly. */
.perm-node__caret {
    width: 0;
    height: 0;
    border-left: 5px solid currentColor;
    border-top: 4px solid transparent;
    border-bottom: 4px solid transparent;
    transform: rotate(90deg);
    transition: transform 0.14s ease;
}

.perm-node.is-collapsed > .perm-node__head .perm-node__caret {
    transform: rotate(0deg);
}

.perm-node__check {
    margin: 0;
    min-height: 0;
    display: flex;
    align-items: center;
    gap: 0.45rem;
}

.perm-node__check .form-check-input {
    margin: 0;
    float: none;
}

.perm-node__label {
    cursor: pointer;
    line-height: 1.2;
}

.perm-node--group > .perm-node__head .perm-node__label {
    font-size: 0.95rem;
    font-weight: 640;
    letter-spacing: -0.01em;
}

.perm-node--module > .perm-node__head .perm-node__label {
    font-size: 0.875rem;
    font-weight: 560;
}

/* The count is the whole point of a collapsed branch: it says how much is granted
   without making anyone expand it to find out. */
.perm-node__count {
    margin-left: auto;
    font-size: 0.74rem;
    font-variant-numeric: tabular-nums;
    color: var(--crm-muted);
    background: var(--crm-surface-3);
    border-radius: 999px;
    padding: 0.08rem 0.5rem;
}

.perm-node__head.is-partial .perm-node__count {
    color: var(--crm-warning);
    background: var(--crm-warning-soft);
}

.perm-node__head.is-full .perm-node__count {
    color: var(--crm-success);
    background: var(--crm-success-soft);
}

.perm-node__body {
    padding-left: 1.55rem;
    margin-left: 0.35rem;
    border-left: 1px solid var(--crm-border-soft);
}

.perm-node--module {
    padding: 0.05rem 0;
}

/* ---------- leaves ---------- */

.perm-node__leaves {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
    gap: 0.1rem 1rem;
    padding-top: 0.15rem;
    padding-bottom: 0.4rem;
}

.perm-leaf {
    margin: 0;
    padding: 0.28rem 0.4rem;
    border-radius: var(--crm-radius-sm);
    min-height: 0;
}

.perm-leaf:hover {
    background: var(--crm-surface-3);
}

.perm-leaf .form-check-input {
    margin-top: 0.2rem;
}

.perm-leaf .form-check-label {
    cursor: pointer;
    display: block;
}

.perm-leaf__name {
    display: block;
    font-size: 0.845rem;
    line-height: 1.3;
}

.perm-leaf__hint {
    display: block;
    font-size: 0.745rem;
    line-height: 1.35;
    color: var(--crm-muted);
}

@media (max-width: 575.98px) {
    .perm-tree__summary {
        margin-left: 0;
        width: 100%;
    }

    .perm-node__body {
        padding-left: 0.9rem;
    }
}

@media (prefers-reduced-motion: reduce) {
    .perm-node__caret {
        transition: none;
    }
}

/* =============================================================================
   18. Loading indicator

   Two pieces, one idea: after a click, say so.

   This is a server-rendered application, so every navigation and every save is a
   full page load. Between the click and the new paint the browser shows only its
   own tab spinner, which on a wide monitor is about as far from where the user is
   looking as it is possible to be. On a slow query — a lead list with every filter
   applied, a report over a year — that gap reads as a dead click, and the reliable
   consequence is a second click on the same button.

   **The bar** is two pixels of brand red across the very top of the viewport. It
   creeps toward the right while the request is out and never quite arrives: it
   cannot know how far along it is, so it must not pretend to. Fixed, and above
   everything including the impersonation banner, because it is the one thing that
   should still be visible when a modal is open.

   **The button** that was pressed grows a spinner and stops accepting clicks. That
   is the half that prevents the duplicate lead, the duplicate booking and the
   double refund; the bar is only reassurance.

   Neither takes the page away. An overlay would have to be dismissed correctly on
   every path out of here, and this application has two paths that never unload the
   page at all — a `File(...)` download and a `target="_blank"` link — so an overlay
   would strand somebody on the first CSV they exported.
   ========================================================================== */

.crm-loadbar {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 2px;
    /* Above the topbar (20), the sidebar backdrop (25), Bootstrap's modal (1055)
       and the impersonation banner (1080). Nothing on this page should be able to
       cover it, and at two pixels of pointer-events: none it costs nothing. */
    z-index: 2000;
    pointer-events: none;
    opacity: 0;
    transition: opacity var(--crm-fast) linear;
}

.crm-loadbar.is-active {
    opacity: 1;
}

.crm-loadbar span {
    display: block;
    width: 100%;
    height: 100%;
    background: var(--crm-action);
    transform: scaleX(0);
    transform-origin: 0 50%;
    /* Transform rather than width: this animates on the compositor, so the bar
       keeps moving while the main thread is busy rendering the page it is
       announcing — which is exactly when it is being looked at. */
    transition: transform 260ms var(--crm-ease);
    box-shadow: 0 0 0.5rem 0 rgba(228, 31, 7, 0.45);
}

/* The spinner on the pressed button. Sized in em so it follows whatever button it
   lands in, including .btn-sm in a table row. */
.crm-busy-spinner {
    display: inline-block;
    width: 0.85em;
    height: 0.85em;
    margin-right: 0.45em;
    vertical-align: -0.1em;
    border: 2px solid currentColor;
    border-right-color: transparent;
    border-radius: 50%;
    opacity: 0.85;
    animation: crm-busy-spin 620ms linear infinite;
}

@keyframes crm-busy-spin {
    to { transform: rotate(360deg); }
}

/* Clicks are refused for as long as the form is in flight. `pointer-events: none`
   rather than the `disabled` attribute on purpose: a disabled submit button is
   omitted from the form data, so disabling it here would silently drop the
   button's own name and value from the POST — and several forms in this
   application post which button was pressed. */
.btn.crm-busy {
    pointer-events: none;
    opacity: 0.75;
}

/* Reduced motion: the creep and the spin both go.

   The blanket rule in section 10 already flattens the transition, which would
   leave the bar jumping between positions every quarter second — busier than the
   animation it replaced. So under this preference the bar is simply present, full
   width, for as long as the wait lasts, and the button's spinner holds still
   rather than turning. Both still answer "is something happening"; neither moves. */
@media (prefers-reduced-motion: reduce) {
    .crm-loadbar span {
        transform: scaleX(1) !important;
    }

    .crm-busy-spinner {
        animation: none;
    }
}

/* The tenant's uploaded logo, in the place the "RE" tile otherwise occupies.

   `contain` rather than `cover`: a logo is a shape with meaning, and cropping it to
   fill a square is how a wordmark loses its last letter. The box is fixed so the rail
   does not reflow between a tall logo and a wide one, and a tenant who uploads a
   banner gets it fitted rather than gets a different layout. */
.crm-brand-logo {
    width: 38px;
    height: 38px;
    flex: 0 0 auto;
    object-fit: contain;
    border-radius: var(--crm-radius-sm);
    background: transparent;
}

/* =============================================================================
   19. Branding editor

   The screen where a tenant sets its own colours. Everything here is written in
   the product's own tokens rather than in the tenant's, on purpose: the editor has
   to stay readable while somebody is halfway through choosing an unreadable
   palette, and a preview that inherited the values being edited would go dark at
   exactly the moment it was needed most. Only the preview panel takes the
   candidate colours, and it takes them as inline styles from the script.
   ========================================================================== */

.crm-logo-preview {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 64px;
    height: 64px;
    flex: 0 0 auto;
    border: 1px solid var(--crm-border);
    border-radius: var(--crm-radius);
    background: var(--crm-surface-2);
    overflow: hidden;
}

.crm-logo-preview img {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
}

.crm-colour-row {
    display: flex;
    align-items: center;
    gap: 0.4rem;
}

.crm-colour-row .form-control-color {
    width: 2.6rem;
    flex: 0 0 auto;
    padding: 0.2rem;
}

.crm-colour-row .form-control[type="text"] {
    flex: 1 1 auto;
    min-width: 0;
}

.crm-contrast-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

.crm-contrast-list li {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    font-size: 0.875rem;
}

.crm-contrast-swatch {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 1.6rem;
    flex: 0 0 auto;
    border: 1px solid var(--crm-border);
    border-radius: var(--crm-radius-sm);
    font-size: 0.75rem;
    font-weight: 700;
}

.crm-contrast-label {
    flex: 1 1 auto;
    min-width: 0;
}

.crm-contrast-list .badge {
    font-variant-numeric: tabular-nums;
}

/* A small, honest picture of the shell: a rail, a card and a button, which between
   them use every colour on the form. Not a screenshot and not an iframe — both would
   cost more than they tell somebody choosing a colour. */
.crm-brand-preview {
    display: flex;
    min-height: 11rem;
    border: 1px solid var(--crm-border);
    border-radius: var(--crm-radius);
    overflow: hidden;
    font-size: 0.8rem;
}

.crm-brand-preview-rail {
    width: 8.5rem;
    flex: 0 0 auto;
    padding: 0.6rem;
    background: var(--crm-sidebar-bg);
    border-right: 1px solid var(--crm-border);
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.crm-brand-preview-brand {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin-bottom: 0.5rem;
    min-width: 0;
}

.crm-brand-preview-brand img,
.crm-brand-preview-brand .crm-brand-mark {
    width: 24px;
    height: 24px;
    font-size: 0.6rem;
    object-fit: contain;
    flex: 0 0 auto;
}

.crm-brand-preview-brand strong {
    font-size: 0.78rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.crm-brand-preview-item {
    padding: 0.25rem 0.4rem;
    border-radius: var(--crm-radius-sm);
}

.crm-brand-preview-item.is-active {
    background: var(--crm-sidebar-active-bg);
    color: var(--crm-accent-ink);
    font-weight: 600;
}

.crm-brand-preview-body {
    flex: 1 1 auto;
    padding: 0.75rem;
    background: var(--crm-canvas);
}

.crm-brand-preview-card {
    padding: 0.7rem;
    border-radius: var(--crm-radius);
    background: var(--crm-surface);
    border: 1px solid var(--crm-border);
}

.crm-brand-preview-card p {
    margin: 0.3rem 0 0.6rem;
    font-size: 0.75rem;
}

.crm-brand-preview-button {
    display: inline-block;
    padding: 0.28rem 0.7rem;
    border-radius: var(--crm-radius-sm);
    background: var(--crm-action);
    color: var(--crm-action-ink);
    font-size: 0.75rem;
    font-weight: 500;
}

/* ------------------------------------------------------------------
   Branding — mobile app theme preview: a phone, painted as the colours
   are picked. Fixed product colours for everything the theme does not
   control, so the preview shows exactly what changes and nothing else.
   ------------------------------------------------------------------ */
.crm-phone-preview {
    width: 100%;
    max-width: 15rem;
    margin: 0 auto;
    border: 6px solid #1f2937;
    border-radius: 1.6rem;
    overflow: hidden;
    background: #f5f6fa;
    font-size: 0.72rem;
    box-shadow: 0 6px 18px rgba(17, 24, 39, 0.12);
}

.crm-phone-header {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.9rem 0.7rem 1.1rem;
    background: #1e1b4b;
    color: #fff;
}

.crm-phone-header small {
    display: block;
    opacity: 0.7;
}

.crm-phone-avatar {
    width: 1.9rem;
    height: 1.9rem;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: rgba(255, 255, 255, 0.2);
    font-weight: 700;
}

.crm-phone-ring {
    margin-left: auto;
    width: 1.9rem;
    height: 1.9rem;
    border-radius: 50%;
    border: 3px solid #f59e0b;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-weight: 700;
}

.crm-phone-body {
    padding: 0.6rem;
}

.crm-phone-tabs {
    display: flex;
    gap: 0.35rem;
    margin-bottom: 0.5rem;
}

.crm-phone-tabs span {
    padding: 0.2rem 0.55rem;
    border-radius: 999px;
    background: #fff;
    border: 1px solid #e5e7eb;
    color: #4b5563;
}

.crm-phone-tabs span.is-active {
    background: #4338ca;
    border-color: #4338ca;
    color: #fff;
}

.crm-phone-card {
    padding: 0.55rem;
    border-radius: 0.6rem;
    background: #fff;
    color: #111827;
    margin-bottom: 0.55rem;
}

.crm-phone-button {
    display: block;
    text-align: center;
    padding: 0.45rem;
    border-radius: 0.6rem;
    background: #4338ca;
    color: #fff;
    font-weight: 600;
}

.crm-phone-tabbar {
    display: flex;
    justify-content: space-around;
    padding: 0.45rem 0.3rem;
    background: #fff;
    border-top: 1px solid #f1f2f6;
    color: #9ca3af;
}

.crm-phone-tabbar span:first-child {
    color: #4338ca;
    font-weight: 600;
}
