/* 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.
   *
   * Every ramp a WHITE LABEL sits on was verified by sampling 41 points
   * along it, not just the two ends. The worst point is quoted per token.
   *
   * The flat --success / --warning / --danger / --info / --ai in tokens.css
   * 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(160deg, #1f8563 0%, #006747 100%); /* 4.57:1 */
  --warning-grad: linear-gradient(160deg, #bb5a16 0%, #9a3b00 100%); /* 4.58:1 */
  --danger-grad:  linear-gradient(160deg, #d02c4a 0%, #ac002e 100%); /* 5.07:1 */
  /* The light red. 135deg, not the family's 160deg, because its only consumer
     is a 474x38 button: at that aspect ratio 135deg gives a gradient line of
     ~362px against 160deg's ~197px, so the ramp actually reads as a sweep
     instead of a flat fill with a shaded edge. Delta is 0.07 rather than the
     house 0.05 for the same reason, and it is the widest the ramp goes before
     stop2 leaves sRGB at this chroma. Both ends clear AA; see tokens.css for
     why stop1 cannot be lighter. */
  --danger-light-grad: linear-gradient(135deg, #da3450 0%, #c1113d 100%); /* 4.58:1 */
  --info-grad:    linear-gradient(160deg, #2a5fe9 0%, #103dc7 100%); /* 5.37:1 */
  --ai-grad:      linear-gradient(160deg, #7a3deb 0%, #6008c8 100%); /* 5.68: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;
}
