/* btn-brand.css, SCOPED brand-gradient rules.
 *
 * Linked from dashboard / vendor / admin / tenant base templates so
 * brand-gradient CTAs and surfaces render correctly even when
 * style.css / pricing.css aren't loaded on the surface (they can't
 * be: style.css has a global `button { padding: 12px 24px; ... }`
 * rule that broke every Tailwind-styled button - see commit aa5db5d
 * (revert) and BUG_REPORT_FINAL_SUMMARY.md).
 *
 * This file contains ZERO global element selectors (no `button`,
 * no `*`, no `.btn`). The `:root` block below only declares custom
 * properties, which never style an element on their own, so it is
 * safe and additive.
 *
 * Mirror of style.css brand tokens + .btn-brand rules. Keep both in
 * sync, since pricing/landing surfaces still load style.css directly.
 */

/* Brand tokens at :root so every consumer of var(--brand-gradient)
 * resolves correctly, not just .btn-brand. Without this, tenant /
 * dashboard pages using inline `style="background: var(--brand-gradient);"`
 * fell through to transparent and rendered white text on the body
 * background (the hero card on tenant_dashboard.html, etc.). */
:root {
  --brand-purple:     #8C6CFF;
  --brand-blue:       #6A9EFF;
  --brand-cyan:       #48E2D9;
  --brand-gradient:   linear-gradient(135deg, #8C6CFF 0%, #6A9EFF 50%, #48E2D9 100%);
  /* The same three stops walked horizontally. 51 sites wanted a left-to-right
     brand sweep and each spelled out the literal, so none of them followed the
     token or picked up the OKLCH interpolation. Angle is the ONLY difference;
     do not add a third variant without a real second use. */
  --brand-gradient-h: linear-gradient(90deg, #8C6CFF 0%, #6A9EFF 50%, #48E2D9 100%);
  --brand-glow:       0 14px 30px -10px rgba(106, 158, 255, 0.55);
  --brand-glow-hover: 0 24px 40px -10px rgba(106, 158, 255, 0.65);

  /* Dense-screen design tokens (single home for the DESIGN.md "Dense data
   * screens" standard: rent roll, ledgers, lists, rosters). Additive only,
   * custom properties never style an element on their own. New dense screens
   * should reference these instead of hardcoding; existing per-screen copies
   * migrate here incrementally. Resolves on any app-shell surface that loads
   * this file (landlord / tenant / vendor / admin bases). */
  --rt-avatar-1:      linear-gradient(135deg, #8C6CFF, #6A9EFF);
  --rt-avatar-2:      linear-gradient(135deg, #6A9EFF, #48E2D9);
  --rt-avatar-3:      linear-gradient(135deg, #7C82FF, #A66BFF);
  --rt-avatar-4:      linear-gradient(135deg, #2FBFD4, #48E2D9);
  --rt-avatar-5:      linear-gradient(135deg, #5B8DEF, #48C6D9);
  --rt-avatar-shadow: 0 5px 12px -5px rgba(15, 23, 42, 0.35);

  /* --- Deep brand gradient (added 2026-08-16) -------------------------
   * Same hue arc, lowered lightness until every stop clears WCAG AA on its
   * own, so text-bearing controls no longer need the dark scrim laid over
   * them. Measured vs #ffffff: #7B58EA 4.74:1, #4070CD 4.75:1, #00817A
   * 4.75:1, and the worst point ANYWHERE along the ramp is 4.74:1 (sampled
   * at 1% steps, not just at the stops). Hue is preserved to within 1.2
   * degrees in OKLCH: 289.1 / 262.1 / 188.1 against the light stops'
   * 289.1 / 261.9 / 189.3.
   *
   * Same approach .btn-brand-blue already took further down this file, and
   * for the same reason: a purpose-built dark stop reads cleaner than a
   * black film over a light one, which mutes the colour it is protecting.
   * The scrim survives for white-label only, see --btn-brand-bg below. */
  --brand-deep-purple: #7B58EA;
  --brand-deep-blue:   #4070CD;
  --brand-deep-teal:   #00817A;
  --brand-gradient-deep:
    linear-gradient(135deg, #7B58EA 0%, #4070CD 50%, #00817A 100%);

  /* What .btn-brand actually paints.
   *
   * OWNER DECISION 2026-08-23: this points at the LIGHT --brand-gradient,
   * not at --brand-gradient-deep. He compared the two side by side and
   * chose the bright ramp for every brand fill in the product. That is a
   * deliberate trade: white label text on it measures 3.68:1 at the purple
   * stop, 2.64:1 at the blue and 1.60:1 at the teal, so brand buttons no
   * longer meet WCAG AA (1.4.3). Do NOT "fix" this back to the deep token
   * on your own initiative; it is his call to reverse, and reversing it is
   * this one line. --brand-gradient-deep stays defined, stays AA-correct
   * and stays asserted by tests/buildgate/test_color_standard.py, so it is
   * one word away if he changes his mind.
   *
   * The four white-label shells (landlord / tenant / vendor / owner)
   * override THIS, not --brand-gradient, with scrim-over-PM-colours: their
   * PM palette is only validated to 3.0:1 by white_label.validate_color, so
   * a PM sitting at 3.2:1 still needs the film to reach 4.5:1. They keep
   * setting --brand-gradient too, because 39 other files read it. */
  --btn-brand-bg: var(--brand-gradient);

  /* --- Surface gradients (added 2026-08-16) ---------------------------
   * DESIGN.md:45 requires a gradient on every fill and then offered no
   * token for the quiet case, so screens invented one: the audit found 107
   * distinct bespoke near-white greys. These are the three that cover it.
   * --surface-aurora is the light-canvas counterpart to --rt-hero-bg, which
   * only ever worked on the dark command hero. Composited over white its
   * tints leave #0f172a body text at 14.9:1 to 16.3:1, so it is safe under
   * real copy rather than decoration only. */
  --surface-raised:      linear-gradient(180deg, #ffffff 0%, #f6f8fc 100%);
  --surface-sunken:      linear-gradient(180deg, #f1f5f9 0%, #e8ecf0 100%);
  --surface-brand-tint:  linear-gradient(160deg, #f1eeff 0%, #eceaf6 100%);
  --surface-canvas-grad: linear-gradient(180deg, #f8fafc 0%, #eff1f3 100%);
  --surface-command-grad:linear-gradient(180deg, #121927 0%, #060b19 100%);

  /* --- The gradient system (added 2026-08-16) --------------------------
   * ONE generator, not a set of hand-picked gradients:
   *
   *     stop1 = the flat base            <- also the email / PDF fallback
   *     stop2 = OKLCH(L - d), C and H HELD
   *     d = 0.05 accents | 0.022-0.028 surfaces | 0.03 dark
   *
   * Holding C and H in OKLCH is what stops the darker stop drifting hue and
   * going muddy, the same method that kept --brand-gradient-deep inside 1.2
   * degrees of the light stops. Because it is a pure function of ONE colour,
   * a white-label PM's two picks run through the identical generator instead
   * of falling out of the system.
   *
   * Brand ramps were verified by sampling 41 points
   * along it, not just the two ends. The worst point is quoted per token.
   *
   * Green/red status fills use the approved flat pair. Other status tokens
   * are NOT replaced. They stay flat because they are also text and border
   * colours, and a gradient cannot be a text colour. These are the FILL
   * counterparts. Use the flat one on a glyph or a 1px border, the gradient
   * on a filled surface. */
  --action-grad:  linear-gradient(160deg, #6164ef 0%, #4943cc 100%); /* 4.59:1 */
  --success-grad: linear-gradient(var(--success, #47ae72), var(--success, #47ae72));
  --warning-grad: linear-gradient(160deg, var(--warning) 0%, var(--warning) 100%); /* Use --warning-ink. */
  --danger-grad: linear-gradient(var(--danger, #d8293e), var(--danger, #d8293e));
  --danger-light-grad: var(--danger-grad);
  --info-grad:    linear-gradient(160deg, #2a5fe9 0%, #103dc7 100%); /* 5.37:1 */
  --ai-grad:      linear-gradient(160deg, #7a3deb 0%, #6008c8 100%); /* 5.68:1 */
  /* The brand purple as a FILL, added 2026-09-08 for the payments money tiles.
     It is NOT --ai-grad, which this app already spends on the Mozart mark, and
     it is NOT --accent-tenant-deep, which is role wayfinding. Hue and chroma
     come straight off --brand-purple; tokens.css carries the derivation and
     the reason it sits as low as it does. */
  --brand-purple-grad: linear-gradient(160deg, #5220b4 0%, #4602a3 100%); /* white 9.44:1 */

  /* Role accents are WAYFINDING FILLS AND RAILS. They measure 2.08 to
   * 3.12:1, so white text on one is a bug, not a style choice. Each ships a
   * deep twin for the case where a label genuinely has to sit on it. */
  --accent-landlord-grad: linear-gradient(160deg, #79aeff 0%, #5b8eee 100%); /* fill only, 2.26:1 */
  --accent-tenant-grad:   linear-gradient(160deg, #9b7cff 0%, #7e5ced 100%); /* fill only, 3.12:1 */
  --accent-vendor-grad:   linear-gradient(160deg, #34c8b6 0%, #00a897 100%); /* fill only, 2.08:1 */
  --accent-landlord-deep: linear-gradient(160deg, #4273d0 0%, #2654af 100%); /* 4.57:1 */
  --accent-tenant-deep:   linear-gradient(160deg, #7d5aec 0%, #6239c9 100%); /* 4.62:1 */
  --accent-vendor-deep:   linear-gradient(160deg, #008575 0%, #006759 100%); /* 4.55:1 */

  /* Report-category tiles (added 2026-08-21). The Reports hub paints 50 tiles
   * and 8 section headers, every one of them a WHITE GLYPH on a category fill.
   * Until now that fill was the flat hue straight out of REPORT_CATEGORIES,
   * and all seven failed: indigo 4.47:1, violet 4.23:1, blue 3.68:1, sky
   * 2.77:1, orange 2.80:1, emerald 2.54:1, amber 2.15:1. An amber tile at
   * 2.15:1 is a white icon you cannot actually see.
   *
   * Each one is the SAME hue as before, run through the house generator: L
   * lowered in OKLCH with C and H held until stop1 clears the 4.5:1 floor,
   * then stop2 = OKLCH(L - 0.05). Holding hue is what keeps "financial" still
   * reading as indigo and "transaction" still reading as green, so nobody has
   * to relearn the hub. Worst of 41 sampled points is quoted per token.
   *
   * These are CATEGORY colours, not status colours. Do not point a status at
   * one, and do not point one of these at a chart series (viz-1..6 stay flat
   * on purpose). --report-mine is the landlord's own saved reports, so it
   * takes the brand identity rather than a category hue. */
  --report-financial-grad:   linear-gradient(160deg, #6163ee 0%, #5453dd 100%); /* 4.64:1 */
  --report-market-grad:      linear-gradient(160deg, #8555ef 0%, #7744de 100%); /* 4.62:1 */
  --report-rental-grad:      linear-gradient(160deg, #2970e3 0%, #1860d1 100%); /* 4.64:1 */
  --report-property-grad:    linear-gradient(160deg, #b15f00 0%, #a15000 100%); /* 4.66:1 */
  --report-transaction-grad: linear-gradient(160deg, #008652 0%, #007744 100%); /* 4.63:1 */
  --report-compliance-grad:  linear-gradient(160deg, #007abc 0%, #006bab 100%); /* 4.66:1 */
  --report-ops-grad:         linear-gradient(160deg, #cc4900 0%, #ba3800 100%); /* 4.65:1 */
  /* Tracks --btn-brand-bg, not --brand-gradient-deep directly, so that on a
   * white-label brand the "My reports" chip and the hero above it stay the
   * SAME fill. Pointing it at the deep token would have split them: the hero
   * follows the PM's scrimmed colours and this would have stayed Rentari's. */
  --report-mine-grad:        var(--btn-brand-bg, var(--brand-gradient)); /* 3.68:1 since 2026-08-23 */
  /* The neutral one, for a section that is deliberately NOT a category:
   * "nothing to chart yet", "needs financial access". Built from --text-muted
   * (#64748b), which tokens.css already names as the floor for readable grey,
   * precisely because slate-400 #94a3b8 is too light: it was carrying a white
   * glyph at 2.56:1. */
  --report-muted-grad:       linear-gradient(160deg, #64748b 0%, #56667c 100%); /* 4.76:1 */

  /* Concierge tier, the top pricing plan's identity. Bright gold renders on a
     DARK card and measures 8.90:1 there, so it needs no fixing; the deep pair
     is only for the case where gold ever lands on a light surface. See
     tokens.css for the measurement, and for why the first attempt at
     "fixing" it was the regression. */
  --tier-concierge-grad: linear-gradient(135deg, #f4e5b9 0%, #d4af37 50%, #b08d57 100%);
  --tier-concierge-deep: linear-gradient(135deg, #936f00 0%, #755200 100%); /* 4.65:1 */
  --surface-aurora:
    radial-gradient(120% 140% at 8% -20%, rgba(140, 108, 255, 0.16), transparent 55%),
    radial-gradient(100% 120% at 100% 10%, rgba(72, 226, 217, 0.18), transparent 52%),
    radial-gradient(90% 140% at 60% 120%, rgba(106, 158, 255, 0.14), transparent 50%),
    #ffffff;
  --rt-hero-bg:
    radial-gradient(120% 150% at 88% -30%, rgba(140, 108, 255, 0.40), transparent 52%),
    radial-gradient(120% 130% at 108% 130%, rgba(72, 226, 217, 0.26), transparent 48%),
    linear-gradient(135deg, #0c1426 0%, #0b1220 100%);
  --rt-hero-shadow:   0 24px 60px -34px rgba(13, 20, 40, 0.8);
  --rt-num-grad:      linear-gradient(120deg, #ffffff, #cfe0ff 42%, #9ff0e6);
}

/* --- OKLCH interpolation (added 2026-08-16) ---------------------------------
 * Identical stops, a rounder walk between them. sRGB interpolation cuts a
 * straight line through a cube, which desaturates the middle of a wide hue
 * arc; OKLCH interpolates hue angularly and holds chroma up. Measured at the
 * midpoint: blue -> teal gains +22.9% chroma (#59C0EC 0.114 -> #25C5F7 0.140)
 * and a direct purple -> teal sweep gains +33.3%. Purple -> blue is unchanged
 * at -1.2%, which is why only the gradients that actually reach teal are
 * listed below; re-walking the purple pairs would be churn for nothing.
 *
 * NOTE for anyone editing this: `in oklab` is NOT the same thing and is WORSE
 * here. It is rectangular, so across this ~100 degree arc it also cuts a
 * chord: measured -5.9% chroma against sRGB. Use oklch.
 *
 * @supports is load-bearing, not decoration. Custom properties accept any
 * token sequence at parse time, so the usual `--x: fallback; --x: better;`
 * trick does NOT fall back: an unsupporting browser would keep the invalid
 * value and every `background: var(--brand-gradient)` in the product would
 * compute to garbage and paint nothing. The guard is what makes the base
 * declarations above the real fallback. */
@supports (background: linear-gradient(in oklch, red, blue)) {
  :root {
    --brand-gradient:
      linear-gradient(135deg in oklch, #8C6CFF 0%, #6A9EFF 50%, #48E2D9 100%);
    --brand-gradient-deep:
      linear-gradient(135deg in oklch, #7B58EA 0%, #4070CD 50%, #00817A 100%);
    /* Only the two avatar fills that cross into teal. 2 gains +22.9% and
     * 5 gains +11.8%; 1, 3 and 4 measured within +/-2% and are left alone. */
    --brand-gradient-h:
      linear-gradient(90deg in oklch, #8C6CFF 0%, #6A9EFF 50%, #48E2D9 100%);
    --rt-avatar-2: linear-gradient(135deg in oklch, #6A9EFF, #48E2D9);
    --rt-avatar-5: linear-gradient(135deg in oklch, #5B8DEF, #48C6D9);
  }
}

/* The history here matters, because this rule has been three different
 * things and each change was deliberate.
 *
 * White label text on the light gradient fails WCAG AA (1.4.3) at every
 * stop vs #fff: #8C6CFF 3.68:1, #6A9EFF 2.64:1, #48E2D9 1.60:1. Until
 * 2026-08-16 the answer was a graded dark scrim (rgba(2,6,23) at 16/30/45%)
 * laid over the light gradient. That passed, but it meant every brand
 * button was the brand colour with a black film on it, muting exactly the
 * colour it protected. On 2026-08-16 the scrim came off and the button
 * moved to --brand-gradient-deep: same hue arc, lowered lightness, 4.74:1
 * or better everywhere along the ramp, no film.
 *
 * On 2026-08-23 the owner looked at both and chose the bright one. So
 * --btn-brand-bg now resolves to the LIGHT --brand-gradient and the ratios
 * in the first paragraph are what brand buttons measure today. He was told
 * before deciding. Reversing it is one line at the --btn-brand-bg
 * declaration above, not a rewrite of this rule.
 *
 * The scrim is NOT gone, it moved. White-label shells override
 * --btn-brand-bg with scrim-over-PM-gradient, because
 * white_label.validate_color only rejects below 3.0:1, so a PM sitting at
 * 3.2:1 still needs it to reach 4.5:1 and must keep their own hue. */
/* THE ACTION LAYER (2026-08-27). Buttons now read --btn-action-bg /
 * --btn-action-fg FIRST, declared only in static/css/btn-action.css, which
 * only the app shells (and the moved auth/apply pages) load. So on the
 * dashboards this paints lime with a near black label (9.36:1, AAA), and on
 * every page without btn-action.css the chain falls through to
 * --btn-brand-bg and renders exactly as before. The white-label blocks
 * override the ACTION pair too, so a configured PM keeps their scrimmed
 * colours. Rollback is unlinking btn-action.css; no call site holds lime. */
.btn-brand,
.btn-brand.btn-brand {
  background: var(--btn-action-bg, var(--btn-brand-bg,
    linear-gradient(135deg, #8C6CFF 0%, #6A9EFF 50%, #48E2D9 100%))) !important;
  color: var(--btn-action-fg, #ffffff) !important;
  border: 0 !important;
  box-shadow: var(--btn-action-glow, 0 14px 30px -10px rgba(106, 158, 255, 0.55)) !important;
  transition: transform 0.2s ease, opacity 0.2s ease, background-position 0.4s ease !important;
}
.btn-brand.btn-brand:hover {
  transform: translateY(-2px);
  /* One generator step darker on the action fill; identical to the base
     fill anywhere the action tokens are absent, so this line is a no-op on
     the marketing shell. */
  background: var(--btn-action-hover, var(--btn-action-bg, var(--btn-brand-bg,
    linear-gradient(135deg, #8C6CFF 0%, #6A9EFF 50%, #48E2D9 100%)))) !important;
  box-shadow: var(--btn-action-glow-hover, 0 24px 40px -10px rgba(106, 158, 255, 0.65)) !important;
}
.btn-brand.btn-brand:active {
  transform: translateY(0);
  box-shadow: var(--btn-action-glow, 0 14px 30px -10px rgba(106, 158, 255, 0.55)) !important;
}
/* Focus: a 2px hole in the surface colour, then the deep green ring. On the
 * lime fill a green ring with no hole is invisible, so the hole layer is
 * load-bearing. Both ring layers fall back to TRANSPARENT and the glow layer
 * to the blue glow, so on a page without btn-action.css this rule paints the
 * resting state and changes nothing. The UA focus outline is deliberately
 * left alone: suppressing it here would blind keyboard users on exactly the
 * pages where the ring resolves transparent. */
.btn-brand.btn-brand:focus-visible {
  box-shadow:
    0 0 0 2px var(--btn-action-ring-hole, transparent),
    0 0 0 4px var(--btn-action-ring, transparent),
    var(--btn-action-glow, 0 14px 30px -10px rgba(106, 158, 255, 0.55)) !important;
}
.btn-brand.btn-brand:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  transform: none;
  box-shadow: none !important;
}

/* --- .brand-gradient-bg, the plain brand fill ---
 * This class was defined ONLY inside public_website/ui/base.html and
 * index.html, i.e. only on the marketing shell. App-shell templates used it
 * anyway, and there it resolved to nothing: the element got no background at
 * all while keeping its `text-white`, so several primary buttons rendered as
 * white text on a white card and were completely invisible, and one progress
 * bar had no fill. Confirmed broken call sites, all on dashboard_base:
 *   landlord_dashboard/ui/inspection_prep.html:40 (progress bar) and :61
 *   landlord_dashboard/ui/pha_directory.html:43 and :176
 *   landlord_dashboard/ui/soi_check.html:81
 *   properties/ui/property_detail.html:3395
 * plus vendor_directory/ui/{vendor_detail,vendor_directory}.html and
 * tenants/ui/tenant_dashboard.html on their own shells.
 * recert_prep.html had already grown a local #rp_progress_bar patch for this.
 * Defining it here fixes every app shell at once (landlord / tenant / vendor /
 * admin / owner all load this file); the marketing shell keeps its own copy.
 *
 * No scrim on the base class: it also fills progress meters and hero panels,
 * and the .btn-brand comment above is explicit that the scrim must not go on
 * non-button gradient surfaces. Text-bearing controls get it below. */
.brand-gradient-bg {
  background: var(--brand-gradient, linear-gradient(135deg, #8C6CFF 0%, #6A9EFF 50%, #48E2D9 100%));
  color: #ffffff;
}

/* Buttons and links carry label text, so they follow .btn-brand (which since
 * 2026-08-23 means the light gradient too, and the white-label scrim when a
 * PM palette is set). The split between this rule and the bare
 * .brand-gradient-bg above is kept because it is what makes a future reversal
 * to the deep token a one-line change rather than a sweep. */
button.brand-gradient-bg,
a.brand-gradient-bg,
.brand-gradient-bg[role="button"] {
  background: var(--btn-action-bg, var(--btn-brand-bg,
    linear-gradient(135deg, #8C6CFF 0%, #6A9EFF 50%, #48E2D9 100%)));
  /* Element+class beats the bare class's color: #ffffff above, so the label
     follows the fill onto the action pair without touching non-button uses
     (meters, hero panels) of .brand-gradient-bg. */
  color: var(--btn-action-fg, #ffffff);
}
.brand-gradient-bg:hover {
  filter: brightness(1.06) saturate(1.05);
}

/* --- Brand Blue Action Button ---
 * Sister to .btn-brand: a 3-stop indigo, brand-blue, brand-cyan gradient.
 * Used as the primary CTA on document_studio steps, vault uploads, billing
 * upgrade links, eviction detail, etc. Mirrors the rule in style.css:222.
 * Without this, the dashboard surfaces fall back to an unstyled transparent
 * button (e.g. Next Step on the radon disclosure step of /dashboard/document-studio). */
/* WCAG AA: the old #6A9EFF (2.64:1) and #48E2D9 (1.60:1) stops failed
 * vs white text. Same indigo-blue-teal journey, deepened: #4F46E5
 * 6.29:1, #3A6BE0 4.82:1, #0C827A 4.68:1 (all >= 4.5:1). Literal stops
 * are fine here; this variant never follows --brand-gradient. */
.btn-brand-blue,
.btn-brand-blue.btn-brand-blue {
  background: linear-gradient(135deg, #4F46E5 0%, #3A6BE0 50%, #0C827A 100%) !important;
  color: #ffffff !important;
  border: 0 !important;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.20),
    0 14px 30px -10px rgba(79, 70, 229, 0.55) !important;
  transition: transform 0.2s ease, opacity 0.2s ease, box-shadow 0.3s ease !important;
}
.btn-brand-blue.btn-brand-blue:hover {
  transform: translateY(-2px);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.30),
    0 24px 40px -10px rgba(79, 70, 229, 0.7) !important;
}
.btn-brand-blue.btn-brand-blue:active {
  transform: translateY(0);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.20),
    0 14px 30px -10px rgba(79, 70, 229, 0.55) !important;
}
.btn-brand-blue.btn-brand-blue:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  transform: none;
  box-shadow: none !important;
}
