/* ============================================================================
   Rentari design tokens, the single source of truth for web.
   ----------------------------------------------------------------------------
   WHY THIS FILE EXISTS
   The product had no token layer. btn-brand.css held 15 custom properties, all
   brand decoration (the gradient, its glow, five avatar fills, a hero
   background). There was no neutral ramp, no radius scale, no spacing scale,
   no type scale, no elevation ladder and no status colours, so every screen
   invented its own. Measured across app/features/<domain>/ui/*.html:

     970    distinct hex literals
     ~3,400 pasted copies of the three brand hexes, rather than the var
     1,250  distinct box-shadow recipes for 2,039 declarations
     ~140   distinct border-radius values
     67     near-identical hairline greys doing the job of one border token

   The mobile app already solved this: mobile/src/theme/tokens.ts is a clean
   semantic token file and the mobile codebase carries TWO hardcoded hexes.
   This file ports that STRUCTURE and NAMING to CSS. It deliberately does NOT
   port mobile's values, because mobile's radius ramp (10/14/15/20/24) is not
   the web's Tailwind ramp (8/12/16/24) and copying it would silently restyle
   every corner in the product. Values here match what the web renders TODAY.

   RULES
   1. This file only DECLARES custom properties. It must not style a single
      element, so adding it changes no rendered pixel. That is the gate.
   2. New work references these vars instead of hardcoding. Existing per-screen
      literals migrate here incrementally, never in one sweep.
   3. Status colours (emerald / amber / rose) encode STATE. They are never
      action colours and never the brand. See DESIGN.md:67.
   4. The brand gradient marks the PRIMARY action, the progress meter, and the
      active/selected state. Approved pastel introduction cards are a scoped
      exception. See DESIGN.md, Pastel brand cards.
   5. Font family is Inter on web. Do NOT port mobile's PlusJakartaSans, which
      DESIGN.md:121 bans as a UI font.

   Loaded FIRST in every shell so anything later can override it.
   ========================================================================== */

:root {
  /* --- Brand -------------------------------------------------------------
     The identity gradient's three stops. These mirror --brand-* in
     btn-brand.css, which stays the canonical home for the gradient itself and
     its glow so white-label shells can keep overriding it. */
  --brand-purple: #8c6cff;
  --brand-blue:   #6a9eff;
  --brand-teal:   #48e2d9;
  --brand-deep:   #0f766e;
  /* Approved signature navigation artwork, September 17, 2026. */
  --illustration-ink: #102b40;
  --illustration-lavender: #ab8cf2;
  /* Owner-requested Mozart pink wash, reusing the approved brand chart hue. */
  --assistant-pink: #ef80a2;
  /* Owner-approved sidebar background choices. */
  --menu-lavender: #f7f5fc;
  --menu-mint: #f2f8f5;
  --menu-ivory: #faf7f0;
  --menu-blue: #f1f6fb;

  /* Owner-approved landlord Page colors, September 25, 2026. Values match
     the light preview; cards keep --surface and menu preferences stay separate. */
  --page-color-aqua: color-mix(in srgb, var(--brand-teal) 16%, var(--surface));
  --page-color-blue: var(--surface-canvas-app);
  --page-color-lavender: var(--menu-lavender);
  --page-color-mint: var(--menu-mint);
  --page-color-rose: color-mix(in srgb, var(--assistant-pink) 16%, var(--surface));
  --page-color-peach-seed: color-mix(in srgb, var(--assistant-pink) 65%, var(--ink-purple-lime));
  --page-color-peach: color-mix(in srgb, var(--page-color-peach-seed) 20%, var(--surface));
  --page-color-butter: color-mix(in srgb, color-mix(in srgb, var(--ink-purple-lime) 60%, var(--assistant-pink)) 18%, var(--menu-ivory));
  --page-color-sage: color-mix(in srgb, var(--ink-purple-lime) 15%, var(--menu-mint));
  --page-color-lilac: color-mix(in srgb, var(--brand-purple) 20%, var(--surface));
  --page-color-sky: color-mix(in srgb, var(--brand-blue) 16%, var(--surface));
  --page-color-ivory: var(--menu-ivory);
  --page-color-mist: var(--surface-canvas-2);

  /* Owner-approved light introduction cards, September 15, 2026.
     Mix in sRGB to match the reviewed 14% brand / 86% white previews.
     Lime reuses the existing action-fill color; all other stops follow
     the brand tokens, including configured brand overrides. Composition
     and opt-in selectors live in owners-pastel-cards.css. */
  --pastel-strength: 14%;
  --pastel-purple: color-mix(in srgb, var(--brand-purple) var(--pastel-strength), var(--surface));
  --pastel-blue: color-mix(in srgb, var(--brand-blue) var(--pastel-strength), var(--surface));
  --pastel-teal: color-mix(in srgb, var(--brand-teal) var(--pastel-strength), var(--surface));
  --pastel-lime: color-mix(in srgb, var(--ink-purple-lime) var(--pastel-strength), var(--surface));
  --pastel-purple-wash: color-mix(in srgb, var(--brand-purple) var(--pastel-strength), transparent);
  --pastel-blue-wash: color-mix(in srgb, var(--brand-blue) var(--pastel-strength), transparent);
  --pastel-teal-wash: color-mix(in srgb, var(--brand-teal) var(--pastel-strength), transparent);
  --pastel-lime-wash: color-mix(in srgb, var(--ink-purple-lime) var(--pastel-strength), transparent);

  /* --brand-deep as a TINT and a hairline (2026-09-09), the pair the overview's
     at-a-glance icons need so the third tone can sit beside --info-bg/-border
     and --ai-bg/-border without borrowing a status or a role colour. Derived,
     not picked: --brand-deep's own hue and chroma held, lightness raised to
     0.966 and 0.905, the same two rungs --info-bg and --info-border sit at. */
  --brand-deep-tint:   #e4f7f4;
  --brand-deep-border: #c3e8e2;

  /* The deep stops (2026-08-16). Same hue arc, lowered lightness until white
     text clears WCAG AA without the dark scrim that used to be layered over
     every brand button: #7B58EA 4.74:1, #4070CD 4.75:1, #00817A 4.75:1, and
     4.74:1 at the worst point along the ramp. Composed into
     --brand-gradient-deep in btn-brand.css, which stays the canonical home for
     gradients (this file declares values, never compositions, and must never
     grow an @supports block: test_design_tokens.py parses it and fails on any
     rule that is not a plain :root custom property). */
  --brand-deep-purple: #7b58ea;
  --brand-deep-blue:   #4070cd;
  --brand-deep-teal:   #00817a;

  /* Dark brand surfaces retain neutral light labels. */
  --brand-purple-fill-1: #5220b4;
  --brand-purple-fill-2: #4602a3;   /* white caption 9.44:1 / 11.68:1 */

  /* Keep status accents on the approved pair; use neutral text where needed. */
  --ink-purple-red: var(--danger);
  --ink-purple-amber: var(--attention-gold);   /* Warning yellow on a dark brand surface. */
  --ink-purple-lime:  #84d828;   /* the lime fill,      5.32:1 / 6.58:1 */
  --ink-purple-blue:  #6a9eff;   /* --brand-blue as is, 3.57:1 / 4.42:1 */
  --ink-purple-teal:  #48e2d9;   /* --brand-teal as is, 5.91:1 / 7.32:1 */

  /* The brand purple as a deep SURFACE (2026-09-11), the two stops of the
     public footer's purple option. Hue is the brand purple's own (289.0 and
     289.1 after 8-bit rounding) and only lightness is chosen: 0.28 and 0.22.
     Chroma cannot be held at the brand's 0.209 down here, because sRGB runs
     out below L 0.37 (exactly where --brand-purple-fill-2 already sits on
     the gamut edge), so each stop carries the MOST chroma sRGB allows at its
     lightness: 0.159 and 0.125. Same purple, as saturated as a screen can
     show it that dark. On them: --text-on-dark 14.97:1 / 17.18:1,
     --ink-purple-lime 8.82:1 / 10.13:1, --brand-teal 9.81:1 / 11.26:1, the
     lockup's #8C6CFF 4.25:1 / 4.88:1. */
  --brand-purple-deep-1: #2d006f;
  --brand-purple-deep-2: #1e004f;


  /* Green/red compatibility stops resolve to the approved status pair. */
  --action-1: #6164ef;  --action-2: #4943cc;   /* 4.59:1 */
  --success-1: var(--success); --success-2: var(--success);
  --warning-1:var(--attention-gold); --warning-2:var(--attention-gold);
  --danger-1: var(--danger); --danger-2: var(--danger);
  --danger-light-1: var(--danger); --danger-light-2: var(--danger);
  --info-1:   #2a5fe9;  --info-2:   #103dc7;   /* 5.37:1 */
  --ai-1:     #7a3deb;  --ai-2:     #6008c8;   /* 5.68:1 */

  /* Role fills. 2.08 to 3.12:1, so NEVER put white text on these. */
  --accent-landlord-1:#79aeff; --accent-landlord-2:#5b8eee;
  --accent-tenant-1:  #9b7cff; --accent-tenant-2:  #7e5ced;
  --accent-vendor-1:  #34c8b6; --accent-vendor-2:  #00a897;
  /* Their text-safe twins. */
  --accent-landlord-deep-1:#4273d0; --accent-landlord-deep-2:#2654af;
  --accent-tenant-deep-1:  #7d5aec; --accent-tenant-deep-2:  #6239c9;
  --accent-vendor-deep-1:  #008575; --accent-vendor-deep-2:  #006759;

  /* Surface stops. Delta chosen for a 7 to 9 step channel difference: enough
     to read as depth, wide enough not to band on an 8-bit display. */
  --surface-canvas-2:  #eff1f3;
  --surface-sunken-2:  #e8ecf0;
  --surface-tint-2:    #eceaf6;
  --surface-command-1: #121927;  --surface-command-2: #060b19;

  /* --- The email palette -------------------------------------------------
     Email is the one surface that CANNOT use any of this. Most clients do not
     support custom properties, and Outlook drops CSS gradients outright, so
     every email template inlines literal hex and always will. That is not
     drift, it is the medium.

     What WAS drift is that nobody wrote the values down, so the same panel
     grew three near-identical tints across 50-odd templates. These three are
     the ones actually in use (measured 2026-08-16: #f8faff in 47 email files,
     #eef0f6 in 33, #eef4ff in 31). Declared here so they have names and so the
     colour ratchet counts them as on-system. PASTE THE LITERAL into an email
     template, never var(); the var will not resolve there. */
  --email-surface: #f8faff;   /* the panel / card fill */
  --email-border:  #eef0f6;   /* its hairline */
  --email-tint:    #eef4ff;   /* the cooler second panel */

  /* --- Concierge tier -----------------------------------------------------
     Gold is NOT drift, and it is NOT a contrast defect. It is the deliberate
     identity of the top pricing tier (.pricing-card-tag-concierge and friends)
     and of the gold sidebar theme. It was the last undocumented colour family
     after the 2026-08-16 audit, so it is named here and nothing about its
     rendering changed.

     MEASURED, and worth recording because it is easy to get wrong: gold is
     rendered on .pricing-card-inner, a DARK card (#0b1220), where #d4af37
     measures 8.90:1. A first pass at this "fixed" it to a deep gold on the
     strength of a contrast walker that composited background-COLOR only and
     ignored the card's dark background-IMAGE, so it reported the white page
     body and claimed 1.88:1. On the real surface that deep gold would have
     been 4.02:1, i.e. the fix was the regression. Measure against
     background-image too, or do not measure.

     The deep pair below exists only for the case where gold ever has to sit on
     a LIGHT surface (4.65:1 there). Nothing uses it today. */
  --tier-concierge:        #d4af37;   /* 8.90:1 on the dark concierge card */
  --tier-concierge-light:  #f4e5b9;
  --tier-concierge-dark:   #b08d57;
  --tier-concierge-deep-1: #936f00;   /* only for gold on a LIGHT surface */
  --tier-concierge-deep-2: #755200;

  /* Chart series. FLAT on purpose: a gradient per series makes two adjacent
     series harder to separate, and the colourblind validator scores flat
     values. Validated all-pairs in BOTH light and dark for the first four,
     adjacent-pairs for all six. Never reuse a status colour as a series. */
  /* Owner-approved monthly unit chart: outlined blue/pink series, 2026-09-15.
     Chart-only colours; labels use --text-muted and exact values have a table. */
  --viz-unit-occupied: #68afde;
  --viz-unit-vacant: #ef80a2;
  --viz-1: #6366f1;  --viz-2: #c2410c;  --viz-3: #0d9488;
  --viz-4: #a21caf;  --viz-5: #0891b2;  --viz-6: #7c3aed;
  /* The first three lifted for a DARK card (2026-09-09), because the values
     above are mid-tones tuned against --surface and go muddy on
     --surface-command-1. Same three families, one rung brighter: lightness
     raised, hue and chroma held. Only the first three exist because only the
     dashboard's rent band uses them; add the rest when something needs six on
     a dark ground. */
  --viz-1-on-dark: #818cf8;  --viz-2-on-dark: #fb923c;  --viz-3-on-dark: #2dd4bf;

  /* Chart status marks share the approved green/red pair.
     Keep labels and exact values alongside colored marks. */
  --mark-alert: var(--danger); --mark-alert-ink: var(--danger);
  /* Yellow is the one mark that cannot carry its own contrast, and the owner
     asked for a bright one rather than the goldenrod this was. A real yellow
     measures 1.4 to 1.9 on a white card however it is mixed, so the only
     values that clear 3:1 alone are the olive-browns nobody wants. It is
     drawn instead as a BRIGHT FILL INSIDE A DARKER RING of the same hue: the
     edge is what makes the shape legible, so the fill is free to stay yellow.
     --mark-warn-edge is 4.92:1 on --surface and 4.49:1 on the track, and it
     doubles as the -ink for text. Never paint --mark-warn without its edge. */
  --mark-warn:  #f5c518;  --mark-warn-ink:  #8a6d00;  --mark-warn-edge: #8a6d00;
  --mark-good: var(--success); --mark-good-ink: var(--success);
  --mark-info:  #3e8fd0;  --mark-info-ink:  #1d6aad;
  --mark-busy:  #12968a;  --mark-busy-ink:  #0f7f75;
  --mark-quiet: #7787a1;  --mark-quiet-ink: #5b6a80;

  /* --- Action ------------------------------------------------------------ */
  --action:            #6366f1;  /* brand-primary, indigo-500 */
  --action-hover:      #4f46e5;
  --action-tint:       #e7edff;
  --action-tint-strong:#e0e7ff;

  /* --- Surfaces ----------------------------------------------------------
     NOTE: the app canvas is theme-driven (the landlord shell ships a Cream
     default plus ~10 named themes via [data-theme]). --surface-canvas is the
     DESIGN.md:55 baseline for surfaces that are not themed; themed shells keep
     using their own --main-bg. Do not force this onto a themed shell. */
  --surface:          #ffffff;   /* cards, modals, sidebars. OPAQUE. */
  --surface-alt:      #f1f5f9;   /* sunken rows, segmented tracks */
  --surface-canvas:   #f8fafc;   /* unthemed page canvas */
  --surface-card-sky: #cceeff;
  --surface-canvas-app:#e8f4fb;  /* the landlord app-shell canvas, read off
                                    an owner-supplied swatch 2026-09-08 */
  /* Neutral frame for the public advertising walkthrough phone previews. */
  --marketing-walkthrough-device: #171717;
  --surface-inverse:  #0f172a;
  --surface-command:  #0b1220;   /* dark "command" hero base, DESIGN.md:261 */
  --surface-scrim:    rgba(15, 23, 42, 0.45);

  /* --- The landlord rail (/owners/*) ------------------------------------
     RECOVERED, not picked. These are `[data-theme="midnight-slate"]` out of
     app/features/landlord_dashboard/ui/dashboard_base.html, the landlord shell
     deleted in 18b38ad5, read back with `git show 18b38ad5^:<path>`. That file
     declared midnight-slate TWICE at equal specificity, so the live value of
     each property is the later block's except --ow-rail-accent, which only the
     earlier one set and which therefore survived. The rebuilt shell shipped a
     white rail; the owner asked for the dark one back.

     The selected row is a brand-gradient pill, the same colour as the hover
     ring, which is how that shell drew it (--sb-active-pill) and what the
     owner asked for on 2026-09-08. --ow-rail-active-glow is its shadow, also
     recovered. It replaced a 4px #38bdf8 left border that shipped earlier the
     same day; --ow-rail-accent is gone with it.

     Rail text is 10.4:1, icons and group labels 6.2:1, both measured against
     the TOP of the gradient, which is its lightest point.

     Landlord only, hence the --ow- prefix: the vendor shell has its own dark
     rail under --sb-*, a different palette with a green accent. Do not merge
     the two. */
  --ow-rail-bg:         #0f172a;
  --ow-rail-gradient:   linear-gradient(180deg, #0f172a 0%, #020617 100%);
  --ow-rail-border:     rgba(255, 255, 255, 0.07);
  --ow-rail-hover:      rgba(255, 255, 255, 0.06);
  --ow-rail-text:       rgba(255, 255, 255, 0.75);
  --ow-rail-text-hover: #ffffff;
  --ow-rail-icon:       rgba(255, 255, 255, 0.55);
  --ow-rail-active-glow: 0 8px 22px -10px rgba(140, 108, 255, 0.55),
                         0 2px 6px -2px rgba(72, 226, 217, 0.35);

  /* --- Borders -----------------------------------------------------------
     Two rungs replace the 67 near-identical greys. */
  --border:        #e2e8f0;
  --border-strong: #cbd5e1;
  --border-hairline: #eef2f7;    /* dense-table dividers, DESIGN.md:251 */

  /* --- Text --------------------------------------------------------------
     Darkened 2026-09-03 to close the measured 22-point OKLCH lightness gap
     against TurboTenant. Hue and chroma are held, only lightness moves; the
     full derivation and the reason it is a lightness problem and not a hue
     one live in the slate block in tailwind.config.js. Keep the two in sync:
     these values ARE slate-600 / slate-500 as that config now defines them.

     --text-muted is the floor for body copy, now 10.0:1 on white.
     --text-disabled deliberately did NOT move and no longer equals slate-400.
     Its job is to read inactive, so it stays light, which also means it stays
     unfit for text: 2.56:1, for genuinely inactive controls only. */
  --text:          #0f172a;
  --text-secondary:#293649;
  --text-muted:    #354358;
  --text-disabled: #94a3b8;
  --text-on-dark:  #f8fafc;
  --text-on-dark-muted: rgba(255, 255, 255, 0.82);

  /* --- Status. State only, never actions, never brand. ------------------- */
  --success:    #47ae72;  --success-bg: color-mix(in srgb, var(--success) 12%, white); --success-border: color-mix(in srgb, var(--success) 40%, white);
  /* Owner rule, September 24: warnings are yellow with dark ink, never orange. */
  --warning: var(--attention-gold); --warning-bg: var(--attention-gold);
  --warning-ink: var(--attention-ink); --warning-border: var(--attention-gold);
  --danger:     #d8293e;  --danger-bg: color-mix(in srgb, var(--danger) 12%, white); --danger-border: color-mix(in srgb, var(--danger) 40%, white);
  --info:       #1d4ed8;  --info-bg:    #e5f0fe;  --info-border:    #bfdbfe;
  --ai:         #6d28d9;  --ai-bg:      #f1eeff;  --ai-border:      #ddd6fe;

  /* --- The attention flag (2026-09-21, at the owner's request) -----------
     ATTENTION PAIR: yellow with dark ink for warnings and pending work across
     the product. September 24 owner direction expands this pair from dashboard
     counters and pending signatures to all warning surfaces. Warning text uses
     --warning-ink; fills use --warning or --warning-bg. Never put white or yellow
     text on a yellow warning surface.

     Not derived from the palette, and there is no point pretending otherwise:
     nothing we own reaches this saturation, so the pair was sampled off the
     reference the owner supplied. --attention-ink on --attention-gold measures
     10.83:1. The gold on --surface is 1.5:1, which is fine here because the
     text carries the meaning and the badge is a label, never a control. */
  --attention-gold: #ffcd00;  --attention-ink: #042238;

  /* Legacy status names remain aliases, never alternate green/red shades. */
  --success-soft: var(--success);
  --danger-soft: var(--danger);

  /* --- Radius. Matches the Tailwind ramp the product already uses. ------- */
  --radius-xs:   4px;
  --radius-sm:   6px;
  --radius-md:   8px;    /* rounded-lg */
  --radius-lg:   12px;   /* rounded-xl */
  --radius-card: 16px;   /* rounded-2xl, the default card */
  --radius-xl:   24px;   /* rounded-3xl */
  --radius-pill: 9999px;

  /* --- Spacing. 4pt grid. ------------------------------------------------ */
  --space-2xs: 2px;
  --space-xs:  4px;
  --space-sm:  8px;
  --space-md:  12px;
  --space-lg:  16px;
  --space-xl:  20px;
  --space-2xl: 24px;
  --space-3xl: 32px;
  --space-4xl: 48px;

  /* --- Type --------------------------------------------------------------
     IMPORTANT: the landlord shell pins html { font-size: 14px } and offers a
     working Small / Medium / Large picker (12.5 / 14 / 16px). Every rem value
     below therefore scales WITH the user's choice, which is the accessible
     behaviour and the reason these are rem and not px. At the 14px default:
       --text-xs   = 10.5px      --text-base = 14px
       --text-sm   = 12.25px     --text-lg   = 15.75px
     Hardcoded px in templates does NOT scale with the picker, which is exactly
     the defect these tokens exist to retire. Prefer these over px. */
  --text-2xs:  0.625rem;
  --text-xs:   0.75rem;
  --text-sm:   0.875rem;
  --text-base: 1rem;
  --text-lg:   1.125rem;
  --text-xl:   1.25rem;
  --text-2xl:  1.5rem;
  --text-3xl:  1.875rem;
  --text-4xl:  2.25rem;

  /* Weights. DESIGN.md hierarchy is weight + tracking, not size alone. The
     product currently sets 77% of its type at 700+, which flattens hierarchy;
     body and labels belong at 400-600 and 700 is the exception. */
  --weight-regular:  400;
  --weight-medium:   500;
  --weight-semibold: 600;
  --weight-bold:     700;

  /* THE PRODUCT'S BOLD IS 600, AND THIS LINE IS WHAT MAKES IT SO.
     Added 2026-08-27, owner-approved.

     `font-bold` in tailwind.config.js resolves to var(--fw-bold, 700), so
     whichever surface declares this variable decides what bold means there.
     This file is loaded on the five APP shells (landlord, tenant, vendor,
     owner, admin) and deliberately NOT on the public marketing shell, so:

       product   -> 600, because this line applies
       marketing -> 700, because it falls through to the fallback

     That is exactly DoorLoop's arrangement, read out of their two
     stylesheets: 700 on marketing headings, and `--font-weight-bold: 600` in
     their app's theme layer with no 700 override anywhere in the file.
     Provenance: Downloads/doorloop-type-framework/README.md.

     It is also the fix for the note three lines above this one, which has
     been true and unactioned since July: when the label, the value and the
     row title are all 700, nothing leads. This changes all 2,267 `font-bold`
     sites in the product without editing a single template.

     To revert, delete this one declaration. Nothing else depends on it. */
  --fw-bold: 600;

  --leading-tight:  1.2;
  --leading-snug:   1.35;
  --leading-normal: 1.55;
  --measure:        68ch;   /* max line length for long-form body copy */

  --tracking-tight: -0.02em;
  --tracking-label:  0.08em;  /* uppercase micro-labels */

  /* --- Elevation ---------------------------------------------------------
     Four rungs replace 1,250 bespoke recipes. All slate-900 tinted per
     DESIGN.md:192. Never pure black: rgba(0,0,0,...) reads muddy on the warm
     and tinted canvases the themes ship. Brand-tinted glows stay in
     btn-brand.css, where they belong to brand surfaces only. */
  --elev-1: 0 1px 2px rgba(15, 23, 42, 0.04);
  --elev-2: 0 1px 3px rgba(15, 23, 42, 0.06), 0 4px 12px -4px rgba(15, 23, 42, 0.08);
  --elev-3: 0 8px 24px -8px rgba(15, 23, 42, 0.12);
  --elev-4: 0 24px 48px -16px rgba(15, 23, 42, 0.18);

  /* --- Focus, the Gradient Halo (DESIGN.md:216-226) ----------------------
     Defined ONCE. Do not re-paste these literals per page; 23 files currently
     do, which is why the halo drifts. */
  --halo-hover:       0 0 0 4px rgba(106, 158, 255, 0.10);
  --halo-focus:       0 0 0 3px rgba(140, 108, 255, 0.18), 0 0 0 6px rgba(72, 226, 217, 0.12);
  --halo-border:      #6a9eff;

  /* --- Motion (DESIGN.md:147-165) ---------------------------------------- */
  --ease-out:     cubic-bezier(0.23, 1, 0.32, 1);
  --ease-in-out:  cubic-bezier(0.77, 0, 0.175, 1);
  --ease-drawer:  cubic-bezier(0.32, 0.72, 0, 1);
  --dur-press:    140ms;
  --dur-fast:     180ms;
  --dur-base:     220ms;
  --dur-drawer:   340ms;

  /* --- Per-role accent ---------------------------------------------------
     Deliberate wayfinding, mirroring mobile's roleAccent: a landlord, tenant
     and vendor session feel distinct without leaving the brand. Each maps to
     one stop of the identity gradient. This divergence is INTENTIONAL; the
     divergence to unify is components, type and spacing, not accent. */
  --accent-landlord: #6a9eff;  --accent-landlord-tint: #e5f0fe;
  --accent-tenant:   #8c6cff;  --accent-tenant-tint:   #f1eeff;
  --accent-vendor:   #14b8a6;  --accent-vendor-tint:   #e9f5ee;

  /* --- Dense data screens (DESIGN.md:232-269) ---------------------------- */
  --row-height:      52px;
  --row-height-dense:44px;
  --table-divider:   var(--border-hairline);
}
