/* =========================================================================
   Rose — Shared design system
   Rewritten 2026-08-21 for the coral/navy visual redesign, replacing the
   burgundy/wine/plum palette below with Josh's coral-gradient identity.

   This is the single source of truth for color tokens and shared UI
   components across every couple-facing page (rose-home.html,
   rose-tasks.html, rose-budget.html, etc.), rose-admin.html, and
   rose-chat-widget.js. CORRECTED 2026-09-17: this comment previously
   claimed pages link this file BEFORE their own inline <style> block so
   page-specific overrides would win. That was never actually true of the
   real markup — every page's <link rel="stylesheet" href="rose-theme.css">
   sits AFTER its inline <style> block, so for any selector of equal
   specificity, THIS file wins the cascade, not the page. Found while
   tracing why a per-page override of .brand-lockup-full/.brand-tagline-full
   (stale copies, still sitting in ~24 pages' own <style> blocks) never
   actually applied — they'd been dead code all along. Pages no longer
   define their own :root — this file is the only place that happens now.

   Two deliberate exceptions, unrelated to this redesign and unchanged by
   it:
   - rose-login.html now DOES pick up the coral/navy palette (part of
     "every page" per Josh's 2026-08-21 brief), just applied lightly since
     it's a pre-auth screen.
   - The printable .doc-* content inside rose-handoff.html stays a
     strict black-and-white document (Josh's explicit standing rule,
     confirmed 2026-08-19), with the one deliberate exception of the
     full-color Rose logo in the doc header. That content reuses
     --ink/--ink-soft below, which stay neutral-safe navy tones and are
     NOT meant to leak the coral gradient or pastel accents into a printed
     contract-style document. Don't "fix" this without asking first.
   ========================================================================= */

:root {
  /* ---------- Core brand tokens ----------
     REPALETTED A THIRD TIME 2026-09-17 (same day), per Josh reversing
     course on the lighter repaletting above: "i dont like this.... change
     it back to a darker dusty rose color. like this" -- with a new flat
     swatch, sampled at ~#A86D71 (H=356 S=25% L=54%).

     That EXACT swatch could not be used as shipped, and this is worth
     stating plainly rather than quietly working around it: at L=54%, ink
     text on it measures 3.63:1 and white text measures 4.13:1 -- both
     fail WCAG AA (4.5:1), so there is no text color that reads reliably
     against that flat value. This isn't a close call or a rounding
     question, both real numbers, both under the bar. Went looking for
     the darkest version of his same hue/saturation direction that ink
     text can still sit on directly (ink is what every nav label, the
     wordmark baked into the logo image, and the "Back to X" links on
     this surface all use) -- that ceiling is L=~61-62%, found by
     solving for the lightness where ink contrast crosses 4.5:1 at his
     hue. --coral below (#BF7D82) sits right at that ceiling, verified at
     4.61:1 -- as dark and as close to his swatch as this surface can go
     without silently shipping something the app's own body text can't
     sit on. If Josh wants the literal #A86D71 (or darker), the sidebar
     nav needs to go back to white text on a solid dark fill -- how the
     app worked before this whole redesign -- not a lighter-text tweak on
     top of the current ink-based nav. Flagged to him rather than picked
     silently either way.

     --coral-mid/--coral-light are the same hue stepped lighter for the
     gradient's other two stops, same 3-stop structure as every version
     this week, this time built outward from the new, darker --coral
     instead of from --coral-mid the way the last two repalettes were.
     --coral-dark (#8F3F47, text/icon/border only, never a surface fill)
     is untouched, same as every round so far -- nothing about how it's
     used changed.

     --navy still holds the app's main ink color despite the variable name
     -- kept as-is rather than renamed, since every page still references
     --navy/--ink by that name and a rename risks missing a spot; it's a
     warm charcoal (#2B2622). Names below (--coral-*, --maroon, --burgundy,
     --rose-badge/--rose-icon) are unchanged from the original coral/navy
     build for the same reason -- the values moved, the names didn't, on
     purpose.

     Re-verified against WCAG AA with these values: ink text on the
     gradient now 4.61-7.64:1 (the top stop is the tightest margin this
     palette has had all day, by design -- it's sitting at the accessible
     ceiling on purpose, not padded the way the lighter stops are). White
     text/logo against the gradient: 1.97-3.25:1 -- still not viable for
     TEXT, but the true-color and white-icon logo marks (not text, exempt
     from this bar) read fine at this range, same as every version this
     week. --coral-dark's own numbers (as text/fill elsewhere, unaffected
     by this change): white on --coral-dark = 7.07:1, --coral-dark as
     text on white/ivory = 6.62-7.07:1, --coral-dark on --coral-bg
     (badges) = 5.64:1, ink on --bg/--card = 14+:1, --muted on --bg/--card
     = 5.33-5.7:1. */
  --coral: #BF7D82;
  --coral-mid: #C89699;
  --coral-light: #D2B1B3;
  --coral-gradient: linear-gradient(180deg, var(--coral) 0%, var(--coral-mid) 55%, var(--coral-light) 100%);
  --coral-dark: #8F3F47;
  --coral-bg: #F3E2DF;
  --navy: #2B2622;
  --bg: #FAF7F2;
  --card: #FFFFFF;
  --muted: #6E655E;
  --line: #E9E1D8;

  /* ---------- Secondary pastel accents (Josh's named palette).
     Small, restrained use only: icon backgrounds, status badges, chips,
     thin borders. Never a large colored section — that's what the coral
     gradient is for. Always pair with --navy text; every one of these
     passes AA contrast with navy, none of them reliably pass with white,
     which is exactly the mistake already caught and fixed once this
     project on a too-pale swatch ("true blush" mockup) — not repeating it
     here. Where a pastel needs to double as a readable icon/label color
     instead of a background, use its *-dark partner below, not the raw
     swatch. ---------- */
  --pastel-coral: var(--coral);
  --pastel-dusty-blue: #9EBBD2;
  --pastel-lavender: #B8A5CC;
  --pastel-sage: #9EAE91;
  --pastel-apricot: #F2C28F;
  --pastel-butter: #F3D69A;
  --pastel-blush: #EFC1BE;
  --pastel-cream: #F8EFE7;

  /* ---------- Legacy names — every real page still references these
     exact variable names in its own (soon-to-be-deleted) local :root and,
     in a handful of spots, as raw hex. Keeping these aliases means
     relinking this file repaints the whole app without hand-editing every
     rule in all ~22 pages. New work should prefer the names above. ---------- */
  --ink: var(--navy);
  --ink-soft: var(--muted);
  --maroon-ink: var(--navy);
  --maroon: var(--coral-dark);
  --maroon-dark: #6E2F2A;
  --burgundy: var(--coral-dark);
  --burgundy-dark: var(--maroon-dark);
  --wine: var(--navy);
  --blush: var(--coral-bg);
  --rose-badge: var(--coral-bg);
  --rose-icon: var(--coral-dark);
  --plum: #7C5F9C;
  --lavender-light: #F1EBF5;
  --mauve-badge: var(--lavender-light);
  --mauve-icon: var(--plum);
  --tan-circle: var(--pastel-apricot);
  --sage-circle: var(--pastel-sage);
  --cream-bg: var(--bg);
  /* 2026-09-18: sidebar now a flat neutral per Josh's explicit hex
     (#ECE5E0), replacing the coral gradient it held all day -- see the
     .sidebar rule below. Was aliased to --coral-dark and unused (the
     sidebar's actual background rule never referenced this token before
     today); now it's real. */
  --sidebar-bg: #ECE5E0;
  --dusty-blue-dark: #4A7EA0;

  /* ---------- Secondary accent: dusty blue-grey ("sky"), added
     2026-09-18 for the site-wide dashboard-mockup rollout. Sampled from
     Josh's blue invitation-suite flatlay (same source as the mockup he
     approved), distinct from the existing --dusty-blue-dark (#4A7EA0,
     used only for the per-brick badge icon) -- that one stays put, this
     is the new general-purpose "generic action" accent: every button,
     form focus ring, and checkbox/radio across the app, replacing
     --coral-gradient/--burgundy in that specific role. Rose stays rose
     for brand moments only now: the sidebar nav active pill, the chat
     widget, alerts/overdue states, and the dashboard hero card -- see
     each of those rules for why they were deliberately left alone.
     Contrast checked the same way as every repalette this session:
     white text on --sky-deep = 5.73:1 (passes AA); navy on --sky-deep =
     2.61:1 (fails -- so --sky-deep is a white-text-only fill, never a
     dark-text one); navy on the pale --sky tints clears easily. */
  --sky: #9BB0C1;
  --sky-deep: #4F6980;
  --sky-pale: #E7EDF1;

  /* Light-tint-bg / dark-icon pairs, added 2026-08-21 for the dashboard's
     per-brick "Up next" icon badges (Josh wants each brick's icon in its
     own color, not one uniform rose tint). Same badge/icon pairing shape
     as --rose-badge/--rose-icon and --mauve-badge/--mauve-icon above, just
     3 more hue families so there's real variety across 16 bricks. Reuses
     hex values already meaningful elsewhere on rose-home.html (sage
     matches the milestone "complete" tone, dusty-blue-icon is the same
     --dusty-blue-dark already defined above) rather than inventing colors
     that mean something different in two places. */
  --sage-badge: #E7EFE2;
  --sage-icon: #4F6B49;
  --apricot-badge: #FBEEDF;
  --apricot-icon: #B5762E;
  --dustyblue-badge: #E4EDF4;
  --dustyblue-icon: var(--dusty-blue-dark);

  /* ---------- Semantic state colors — functional, not brand. Untouched
     by this redesign: these carry real meaning (something failed vs.
     succeeded) and stay visually distinct from the coral brand color on
     purpose, per Josh's own accessibility rule. ---------- */
  --success: #3E7350;
  --success-bg: #E5F3E9;
  --success-line: #A8D9BB;
  --error: #B85C3E;
  --error-bg: #FFF1EB;
  --error-line: #F0B79A;
  --warning: #B8823E;
  --warning-bg: #FBF1E1;
  --warning-line: #E8CE9E;
  --info: var(--plum);
  --info-bg: var(--lavender-light);
  /* --amber/--amber-bg and --green/--green-bg (2026-08-23): rose-admin.html
     has referenced these under these exact names since the escalation and
     knowledge-base badges were first built, for "pending" and
     "resolved/approved" states, but nothing ever defined them, in this file
     or admin's own inline styles. An undefined custom property makes the
     background transparent and the text fall back to inherited ink, so
     every pending/resolved badge, status message, and knowledge-tag color
     across the whole admin dashboard has been silently rendering as plain
     navy text with no colored pill at all. This isn't a missing design
     decision, --warning/--warning-bg and --success/--success-bg above are
     exactly the amber and green this needs, just under different names.
     Aliased here rather than rewriting every var(--amber) call site in
     rose-admin.html. */
  --amber: var(--warning);
  --amber-bg: var(--warning-bg);
  --green: var(--success);
  --green-bg: var(--success-bg);

  --shadow: 0 8px 24px rgba(43, 38, 34, 0.07);
  --shadow-lg: 0 20px 48px rgba(43, 38, 34, 0.14);
  --radius: 14px;
  --radius-sm: 10px;
  --ease: cubic-bezier(0.4, 0, 0.2, 1);
}

/* =========================================================================
   Reset + base
   ========================================================================= */
* { box-sizing: border-box; }

html, body { min-height: 100vh; }

/* REWORKED 2026-09-18, fourth time this exact bug bit a page today
   (rose-login.html's .card, rose-admin.html's .gate-card,
   rose-platform-admin.html's .gate-card, now .venue-card too). The
   watermark used to be a separate body::before element, position:fixed
   + z-index:0, sitting in its own stacking layer above any plain
   static-flow element that didn't explicitly claim a higher one --
   which meant EVERY card/box on EVERY page, existing or future, needed
   its own position:relative;z-index:1 just to not get the pattern
   painted on top of it. That's not a fix, that's a trap for the next
   card. Root cause removed instead of patched again: the watermark is
   now baked directly into body's own `background`, as a real background
   layer (var(--bg) as the base color, rose-icon-watermark.png tiled on
   top) -- not a separate element at all, so there is no stacking order
   for anything to lose. rose-icon-watermark.png is rose-icon-only.png
   with its alpha channel pre-multiplied by 0.05 -- same exact visual
   weight as the old `opacity: 0.05` on the pseudo-element, just baked
   into the asset since a background-image layer has no opacity
   property of its own to set. Every existing position:relative;z-index:1
   patch from earlier today is now redundant but harmless -- left in
   place rather than churned back out. */
body {
  margin: 0;
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  color: var(--ink);
  display: flex;
  align-items: flex-start;
  line-height: 1.5;
  position: relative;
  background-color: var(--bg);
  background-image: url('/venuexpert-icon-watermark.png');
  background-repeat: repeat;
  background-size: 64px 64px;
}
@media print {
  /* rose-handoff.html's printable content is a deliberate strict
     black-and-white document per Josh's standing rule -- a tiled pink
     icon has no business on a printed day-of coordinator packet even at
     5% opacity. */
  body { background-image: none; }
}

a { color: inherit; }

h1 {
  font-family: 'Playfair Display', serif;
  font-weight: 600;
  color: var(--navy);
  margin-top: 0.35rem;
}

h2 { font-family: 'Playfair Display', serif; font-weight: 600; color: var(--navy); }

.subtitle { color: var(--muted); margin-top: 0; margin-bottom: 1.5rem; }

label { display: block; font-weight: 600; margin-bottom: 0.35rem; color: var(--ink); }

/* =========================================================================
   Sidebar + navigation. UPDATED 2026-09-18: the sidebar surface itself is
   now flat --sidebar-bg (#ECE5E0), not the coral gradient -- the gradient
   is reserved for the active-nav pill only now (see .side-nav a.active
   below). The dashboard mockup this was built from used WHITE text on
   that gradient, which Josh approved there after being shown it fails AA
   (1.97-3.25:1 across the three stops) -- but that approval was for one
   screenshot he looked at once, not a standing call on a nav element that
   now ships on every page. Deliberately did NOT carry the white text into
   production: kept ink/navy text on the active pill instead (4.61-7.64:1,
   passes easily, same fix used everywhere else in this file), same
   visual gradient, same rounded pill, just legible. Flagged to Josh
   rather than silently overriding his approved mockup call without
   saying so -- if he wants white here specifically, that's a one-line
   revert, not a redesign.
   ========================================================================= */
.sidebar {
  width: 246px;
  flex-shrink: 0;
  position: sticky;
  top: 0;
  height: 100vh;
  /* Flat --sidebar-bg (#ECE5E0), not the coral gradient -- 2026-09-18,
     per Josh applying the dashboard mockup's sidebar treatment app-wide.
     The true-color logo (see .brand-lockup-img usage in each page) was
     re-verified against this exact value before shipping, same as it was
     against the coral gradient this morning. */
  background: var(--sidebar-bg);
  border-right: 1px solid var(--line);
  padding: 1.9rem 1.15rem 1.4rem;
  display: flex;
  flex-direction: column;
  overflow-y: auto;
}

.brand-lockup {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0 0.55rem;
  margin-bottom: 1.9rem;
}

/* 2026-09-17, per Josh: "all the logos dont match exactly as the logo i
   uploaded to you and the old tagline is still with all of them, not the
   new one." Both were true. This lockup used to render an icon PNG next
   to HTML text standing in for the wordmark ("Rose" in Playfair Display),
   plus a hardcoded tagline that was still the pre-rebrand "Wedding
   planning, reimagined." — nobody had swapped it for Josh's real one.
   Rendered text can never be a pixel match for a hand-drawn wordmark, and
   a hardcoded string doesn't update itself. Both problems go away the
   same way: stop reconstructing the lockup out of parts and use Josh's
   actual uploaded artwork (rose-logo-full.png) directly — icon, wordmark,
   and his real tagline "Wedding planning that knows your venue" are all
   baked into that one file, so this can never drift from what he
   approved again.

   UPDATED 2026-09-17 (later same day): this first shipped sitting on its
   own small white card, because at the time the sidebar gradient was too
   close in hue/lightness to the icon's own fill for the true-color logo
   to read directly on it (~1.2:1). Josh's reply: "i see that the logo
   has a white background on the darker color... i dont like the white
   background, seems cheap." Removed the card. It no longer needs one
   for the same reason the "too dark" complaint got fixed at the same
   time — --coral-mid (and the whole gradient) moved much lighter this
   round (see the Core brand tokens comment above), so the true-color logo
   now sits directly on it with real, if soft, separation (1.57-2.29:1
   depending which stop) instead of nearly vanishing into it. Soft is the
   right word, not a bug: a muted accent color on a paler ground reads as
   restrained, not as a contrast failure, and logos are exempt from text
   contrast minimums for exactly this reason. If a genuinely darker
   surface ever comes back into this app (nothing currently qualifies —
   see the Buttons/mobile-header comments, everything large went through
   the same lightening pass), use the pre-made white files
   (rose-logo-white.png / rose-logo-full-white.png) directly, not a boxed
   version of the color one — that's what they're for. */
.brand-lockup-full {
  display: block;
  width: fit-content;
  margin: 0 0.05rem 1.5rem;
}

.brand-lockup-img {
  display: block;
  width: 100%;
  max-width: 186px;
  height: auto;
}

/* The staff-gate card (rose-admin.html) and the platform-admin gate card
   both center their content via text-align:center, which only affects
   inline content, not this block-level, fit-content-width box — needs
   its own margin:auto to actually center instead of sitting flush-left
   inside a centered card. */
.gate-card .brand-lockup-full { margin: 0 auto 0.5rem; }

/* Text-accent gradient — for general use on white/ivory surfaces, where a
   coral gradient reads clearly against a light background. */
.text-gradient-accent {
  background: linear-gradient(90deg, var(--coral), var(--coral-mid), var(--coral-light));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* The staff gate card's lockup sits on a white card, not the coral
   sidebar, so it doesn't get the white treatment above — but it also had
   its own stale, never-migrated hardcoded pink/purple gradient
   (#FF9075/#C96FA0/#8364D7) left over from before this redesign. Same
   specificity as the rule above (two classes each), placed after it so
   it wins that tie and actually applies. Coral gradient-clip text reads
   fine on white, same treatment as .text-gradient-accent elsewhere. */
.gate-card .accent {
  background: linear-gradient(90deg, var(--coral), var(--coral-mid), var(--coral-light));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  font-weight: 600;
}

/* REMOVED 2026-09-17: this used to force the sidebar's logo mark to solid
   white via brightness(0)/invert(1). It was never actually checked
   against the sidebar's real background — white on the coral gradient
   measures 2.05-3.24:1 depending where on the gradient it sits, which
   fails WCAG AA (4.5:1) at every stop. That's very likely a real
   contributor to Josh's "the logos don't match" complaint: a
   near-invisible white silhouette reads as wrong even before you get to
   whether it's pixel-identical to his artwork. The fix wasn't a better
   filter, it was to stop putting the true-color logo directly on the
   gradient at all — see .brand-lockup-full above, now a light card. */

.brand-mark { width: 34px; height: 34px; display: block; }

.brand-word {
  font-family: 'Poppins', sans-serif;
  font-weight: 400;
  font-size: 1.42rem;
  letter-spacing: -0.01em;
  color: var(--navy);
}

.side-nav { display: flex; flex-direction: column; gap: 0.15rem; }

.side-nav a, .side-nav .nav-placeholder, .side-nav-bottom a, .side-nav-bottom .nav-placeholder {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.62rem 0.75rem;
  border-radius: 12px;
  text-decoration: none;
  font-size: 0.92rem;
  font-weight: 500;
  color: var(--navy);
  transition: background 0.15s var(--ease);
}

.side-nav a svg, .side-nav .nav-placeholder svg, .side-nav-bottom a svg, .side-nav-bottom .nav-placeholder svg {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  color: var(--navy);
}

.side-nav a:hover:not(.active), .side-nav-bottom a:hover { background: rgba(43, 38, 34, 0.08); }

.side-nav a.active {
  /* 2026-09-18, third version today: Josh sent a screenshot of the
     STAFF admin sidebar (.side-nav button.active below, in
     rose-admin.html -- built from <button> not <a>, so it was never
     touched by any of today's edits to this rule and has quietly kept
     its own blush/maroon treatment the whole time) and said to match
     the client portal to it exactly: "rose ish color with the darker
     maroon text." That's --blush/--maroon -- already verified earlier
     today at 5.64:1 (passes AA) for the milestone-alert work, and
     already proven live on the admin side, so this is a known-good
     combo, not a new risk.

     History, kept for context rather than deleted: solid --coral-dark
     + white (7.07:1) shipped first for "make the rose darker so white
     passes," then solid --navy + white (14.97:1) to match the ROSE
     wordmark's ink color. Both superseded by this -- if a future round
     wants either back, the hex/tokens are above. */
  background: var(--blush);
  color: var(--maroon);
  font-weight: 600;
}

.side-nav a.active svg { color: var(--maroon); }

.nav-placeholder { color: rgba(43, 38, 34, 0.45); cursor: default; }
.nav-placeholder svg { color: rgba(43, 38, 34, 0.35); }

.soon-tag {
  margin-left: auto;
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  background: rgba(43, 38, 34, 0.1);
  color: var(--navy);
  padding: 0.15rem 0.4rem;
  border-radius: 999px;
}

.side-nav-bottom {
  margin-top: auto;
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  padding-top: 0.5rem;
  border-top: 1px solid rgba(43, 38, 34, 0.15);
}

/* 2026-08-21, per Josh: had to scroll all the way back to the top of a
   long page to find this link back to the dashboard. Sticky instead, so
   it's reachable at any scroll position, styled as a small floating chip
   (not bare text) so it stays legible over whatever content scrolls
   under it. z-index above .section (which lifts slightly on hover) but
   below the mobile sidebar/hamburger/overlay stack. */
.back-link-row {
  position: sticky;
  top: 1rem;
  z-index: 90;
  margin-bottom: 1.1rem;
  display: inline-flex;
}
.back-link {
  display: inline-flex;
  align-items: center;
  color: var(--muted);
  text-decoration: none;
  font-size: 0.9rem;
  font-weight: 600;
  background: var(--card);
  padding: 0.4rem 0.9rem;
  border-radius: 999px;
  box-shadow: var(--shadow);
  border: 1px solid var(--line);
}
.back-link:hover { color: var(--burgundy); }
@media (max-width: 860px) {
  .back-link-row { top: calc(52px + 0.7rem); }
}
.main-area { flex: 1; min-width: 0; padding: 2.1rem 2.8rem 3.5rem; max-width: 1440px; position: relative; }

.brick-title-row { display: flex; align-items: flex-start; gap: 0.9rem; margin-bottom: 0.5rem; }
.brick-icon-badge {
  width: 3.1rem;
  height: 3.1rem;
  border-radius: 14px;
  background: var(--coral-bg);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--coral-dark);
  box-shadow: 0 6px 16px rgba(43, 38, 34, 0.08);
  flex-shrink: 0;
}
.brick-icon-badge svg { width: 1.6rem; height: 1.6rem; }

/* A secondary icon-badge variant for lavender contexts (guest-related UI,
   inspiration, completed-milestone indicators). Opt in per page with
   .brick-icon-badge.plum, don't apply globally, coral stays the default. */
.brick-icon-badge.plum { background: var(--lavender-light); color: var(--plum); }

/* =========================================================================
   Mobile nav (hamburger) — every real page nests this INSIDE its own
   fixed-position .mobile-header-bar (a flex child, transparent, sized to
   fit that bar), not as a standalone fixed button of its own. An earlier
   draft of this file assumed the opposite (position:fixed, floating on
   its own) — that had never actually been checked against real markup,
   and the moment this file got linked for real it popped every page's
   hamburger button out of its header bar and on top of the wordmark.
   Sizing/color live here; each page's own .mobile-header-bar keeps the
   fixed positioning and background.
   ========================================================================= */
/* UPDATED 2026-09-17, same "too dark" fix as the button rule above: this
   bar used to be a solid --maroon (--coral-dark) fill with white icon/
   text/logo forced on top — the single most prominent dark surface on
   every mobile page load, edge to edge. Now the same light --coral-gradient
   the sidebar uses, with dark ink for the hamburger icon and wordmark
   (4.62-7.30:1, same verified range as the buttons) instead of white
   (which measured 2.05-3.24:1 against this gradient and failed AA). The
   logo mark gets a small white badge behind it rather than the old
   brightness(0)/invert(1) trick — the icon's own dusty-rose fill is too
   close in hue/lightness to this gradient to read on it directly (see the
   comment above .brand-lockup-img), and that's just as true at 24px as it
   was at 66px. */
/* 2026-09-18: Josh sent the countdown pill's blue ("240 days to go",
   --sky-deep) and said "make the menu button this blue color." A
   transparent button can't take on a color by itself, so this gives the
   hamburger a real solid fill for the first time -- same --sky-deep/
   white treatment every other button on the site already uses --
   rather than just recoloring the icon stroke in place. That matters:
   a plain color swap of the icon alone (navy -> sky-deep, background
   staying transparent) was checked and fails hard, 1.77-2.91:1 against
   the coral-gradient header depending on gradient stop -- the icon
   would nearly vanish. Solid --sky-deep fill sidesteps that entirely:
   white icon on --sky-deep verified at 5.73:1, comfortably clears the
   3:1 floor for graphical/icon elements regardless of what's behind it. */
.hamburger-btn {
  display: none;
  width: 38px;
  height: 38px;
  border-radius: 8px !important;
  border: none;
  padding: 0;
  background: var(--sky-deep);
  color: #fff;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  cursor: pointer;
}
.hamburger-btn:active { filter: brightness(0.88); }
/* CHANGED 2026-09-18, per Josh: "make that very top bar match the menu
   color scheme." The bar was --coral-gradient (a leftover from before
   the sidebar itself moved off the gradient onto flat --sidebar-bg,
   2026-09-18 earlier today -- see .sidebar's own comment) -- meaning the
   bar you see before opening the menu and the menu itself, once open,
   were two different color treatments of the same app. Matched to
   --sidebar-bg so the bar reads as part of the same surface the sidebar
   slides out from, not a separate banner. Dark-ink text/icon (already in
   place, tuned against the gradient) stays exactly as legible here --
   --sidebar-bg (#ECE5E0) is close in lightness to --bg itself, where
   this same dark ink is already verified throughout the app. */
.mobile-header-bar {
  display: none;
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 160;
  height: 52px;
  background: var(--sidebar-bg);
  align-items: center;
  justify-content: space-between;
  padding: 0 0.7rem 0 1rem;
  box-shadow: 0 2px 10px rgba(43, 38, 34, 0.2);
}
/* UPDATED 2026-09-17 (later same day): dropped the white circular chip
   behind this icon for the same reason the sidebar lockup's card came
   off — Josh doesn't want a white background shape behind the logo
   anywhere, full stop, not just on the big lockup. --coral-gradient
   moved much lighter in this same round (see the Core brand tokens
   comment), so the true-color icon has real if soft separation from it
   even at 20px, same as the sidebar version. */
.mobile-header-logo { display: flex; align-items: center; gap: 0.4rem; min-width: 0; pointer-events: none; }
.mobile-header-logo img {
  width: 20px;
  height: 20px;
  display: block;
  flex-shrink: 0;
}
.mobile-header-logo span {
  font-family: 'Playfair Display', serif;
  font-weight: 400;
  font-size: 1.1rem;
  color: var(--navy);
  white-space: nowrap;
}
.sidebar-overlay { display: none; position: fixed; inset: 0; background: rgba(43, 38, 34, 0.45); z-index: 150; }

@media (max-width: 860px) {
  .hamburger-btn { display: flex; }
  .mobile-header-bar { display: flex; }
  .sidebar {
    position: fixed;
    top: 0;
    left: 0;
    height: 100vh;
    z-index: 180;
    transform: translateX(-100%);
    transition: transform 0.25s ease;
    box-shadow: 0 0 30px rgba(43, 38, 34, 0.3);
  }
  .sidebar.open { transform: translateX(0); }
  .sidebar-overlay.open { display: block; }
  .main-area { max-width: 100%; width: 100%; padding: 4.6rem 1.1rem 3rem; }
}

/* =========================================================================
   Buttons — one definition, every page used to hardcode its own copy of
   this. UPDATED AGAIN 2026-09-18, per Josh moving the dashboard mockup's
   accent site-wide: the single most-repeated colored element in the app
   moves off the coral gradient onto the new --sky-deep secondary accent
   (#4F6980), solid rather than a gradient, white text.

   Deliberately solid, not a --sky/--sky-deep gradient mirroring
   --coral-gradient below: checked the same two-endpoint contrast this
   file always checks first, and there is no single text color that
   clears AA at both ends of that range -- navy on --sky-deep is 2.61:1
   (fails) and white on --sky (the lighter stop) is 2.24:1 (fails). The
   coral gradient below only gets to be a gradient with ink text because
   its three stops were hand-tuned into an accessible band (see the Core
   brand tokens comment). --sky/--sky-deep were sampled straight from
   Josh's flatlay for the mockup and haven't been through that tuning
   pass, so solid --sky-deep with white (5.73:1, comfortable AA) is the
   honest version of this today. A "fades like the old buttons" gradient
   treatment is possible later but needs its own color pass, not a
   copy-paste of these two exact values.

   Worth flagging directly: Josh's own words on 2026-09-17 about the
   PREVIOUS solid-dark button ("i want it to be more of a dusty rose that
   fades not a dark like you have it") are exactly what a solid fill
   with white text is. Went ahead anyway because (a) he's now approved
   this specific color live on the dashboard mockup's own solid-fill
   elements (the countdown pill, "View and print") without objecting,
   and (b) a blue-grey solid reads cooler/quieter than the maroon he was
   reacting to, not heavier -- but this is a judgment call, not a
   contrast rule, and it's the one part of today's rollout most likely
   to get "no, still don't like it" -- flagged so that's not a surprise.
   ========================================================================= */
button {
  padding: 0.6rem 1.25rem;
  font-size: 1rem;
  cursor: pointer;
  white-space: nowrap;
  font-family: inherit;
}

/* :where() keeps this rule's specificity at plain "button" (0,0,0,1)
   instead of "button:not(:disabled)" (0,0,1,1). Without it, this default
   was tying or beating any page's own single-class button style (.dash-link,
   .wed-more-btn, .side-nav button, .filter-pill, .logout-link, etc, all
   (0,0,1,0) or (0,0,1,1) depending on whether they add a type selector too)
   and silently winning ties since this stylesheet loads after a page's own
   <style> block, in the same login/cascade you'd never look at just for a
   button color. Found 2026-08-23 auditing rose-admin.html: its "View
   info" link and "..." menu button were rendering as solid maroon pills
   instead of the plain-text link / bordered ghost icon their own CSS
   clearly intended, and the filter pills' active state was indistinguishable
   from inactive because both got forced to this same maroon fill. This is
   meant purely as a fallback default for buttons with no page-specific
   class, never something a page has to fight, so it should never be able
   to win against ANY class selector, tie or no tie. */
button:where(:not(:disabled)) {
  background: var(--sky-deep);
  color: #fff;
  border: none;
  border-radius: 999px;
  font-weight: 700;
  transition: transform 0.2s var(--ease), filter 0.2s var(--ease), background 0.2s var(--ease);
}
button:not(:disabled):hover { filter: brightness(1.08); transform: translateY(-1px); }
button:not(:disabled):active { transform: translateY(0) scale(0.97); }
button:not(:disabled):focus-visible { outline: 2px solid var(--plum); outline-offset: 2px; }
button:disabled { opacity: 0.6; cursor: not-allowed; }

button.secondary {
  /* Moved to --sky-deep 2026-09-18 alongside the primary button above,
     same site-wide rollout. --sky-deep on white = 5.73:1, passes AA as
     text same as it does as a fill. */
  background: #fff;
  color: var(--sky-deep);
  border: 1.5px solid var(--sky-deep);
}
button.secondary:not(:disabled):hover { background: var(--sky-pale); filter: none; }

/* A quiet, low-emphasis third tier — for "Remove"/"Cancel" style actions
   that shouldn't compete visually with the primary coral action next to
   them. */
button.ghost-danger {
  background: none;
  border: 1px solid var(--error);
  color: var(--error);
  border-radius: var(--radius-sm);
  font-weight: 600;
  padding: 0.4rem 0.9rem;
  font-size: 0.85rem;
}
button.ghost-danger:not(:disabled):hover { background: var(--error-bg); filter: none; }

/* =========================================================================
   Cards / sections
   ========================================================================= */
.section {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.25rem;
  margin-bottom: 1.5rem;
  background: var(--card);
  box-shadow: var(--shadow);
  transition: transform 0.25s var(--ease), box-shadow 0.25s var(--ease);
}
.section:hover { transform: translateY(-3px); box-shadow: var(--shadow-lg); }
.section-note { color: var(--muted); font-size: 0.85rem; margin-top: 0; margin-bottom: 1rem; }

/* =========================================================================
   Form inputs, selects, checkboxes, radios
   ========================================================================= */
input[type="text"], input[type="number"], input[type="email"], input[type="tel"],
input[type="date"], input[type="url"], input[type="password"], textarea, select {
  width: 100%;
  padding: 0.55rem 0.7rem;
  font-size: 1rem;
  font-family: inherit;
  border: 1px solid #D8D2CE;
  border-radius: var(--radius-sm);
  background: #fff;
  color: var(--ink);
  transition: border-color 0.15s var(--ease), box-shadow 0.15s var(--ease);
}
input:focus, textarea:focus, select:focus {
  outline: none;
  /* 2026-09-18: moved to --sky-deep/--sky-pale with the rest of the
     generic-action surfaces (buttons, checkbox/radio below) -- a focus
     ring is an interaction affordance, not a brand moment, same bucket
     as the button rollout above. */
  border-color: var(--sky-deep);
  box-shadow: 0 0 0 3px var(--sky-pale);
}
input:disabled, textarea:disabled, select:disabled { background: #F5F5F3; color: var(--muted); cursor: not-allowed; }

input[type="checkbox"], input[type="radio"] {
  width: 17px;
  height: 17px;
  accent-color: var(--sky-deep);
  cursor: pointer;
}

/* =========================================================================
   Badges — small status pills. .rose-badge.coral uses --coral-dark for
   the label text (not the raw --coral hue, which is too light to clear
   4.5:1 as small bold text — --coral itself is reserved for large fills
   and non-text graphics, which only need 3:1).
   ========================================================================= */
.rose-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  padding: 0.2rem 0.55rem;
  border-radius: 999px;
  line-height: 1.4;
}
.rose-badge.default { background: rgba(43, 38, 34, 0.08); color: var(--muted); }
.rose-badge.burgundy { background: var(--blush); color: var(--burgundy); }
.rose-badge.plum { background: var(--lavender-light); color: var(--plum); }
.rose-badge.coral { background: var(--coral-bg); color: var(--coral-dark); }
.rose-badge.success { background: var(--success-bg); color: var(--success); }
.rose-badge.warning { background: var(--warning-bg); color: var(--warning); }
.rose-badge.error { background: var(--error-bg); color: var(--error); }

/* =========================================================================
   Alerts / inline messages — loading, error, success, warning.
   ========================================================================= */
.rose-alert {
  border-radius: var(--radius);
  padding: 1rem 1.1rem;
  margin-bottom: 1rem;
  font-size: 0.92rem;
  border: 1px solid transparent;
  display: none;
}
.rose-alert.visible { display: block; }
.rose-alert.loading { color: var(--muted); font-style: italic; border: none; padding: 0; background: none; }
.rose-alert.error { border-color: var(--error-line); background: var(--error-bg); color: var(--error); }
.rose-alert.success { border-color: var(--success-line); background: var(--success-bg); color: var(--success); }
.rose-alert.warning { border-color: var(--warning-line); background: var(--warning-bg); color: var(--warning); }
.rose-alert.info { border-color: var(--plum); background: var(--lavender-light); color: var(--plum); }

/* Back-compat: the #loading / #error id pattern used verbatim across
   every page today. */
#loading { display: none; color: var(--muted); font-style: italic; margin-bottom: 1rem; }
#loading.visible { display: block; }
#error {
  display: none;
  border: 1px solid var(--error-line);
  border-radius: var(--radius);
  padding: 1rem;
  background: var(--error-bg);
  color: var(--error);
  margin-bottom: 1rem;
}
#error.visible { display: block; }

.field-error { color: var(--error); font-size: 0.85rem; margin-top: -0.6rem; margin-bottom: 1rem; display: none; }
.field-error.visible { display: block; }
.save-status { margin-top: 0.5rem; font-size: 0.9rem; }

/* =========================================================================
   Empty states
   ========================================================================= */
.not-set-up, .no-members, .empty-note {
  color: var(--muted);
  font-style: italic;
  font-size: 0.9rem;
  margin: 0 0 0.75rem;
}

/* =========================================================================
   Progress bars
   ========================================================================= */
.rose-progress {
  width: 100%;
  height: 8px;
  border-radius: 999px;
  background: var(--line);
  overflow: hidden;
}
.rose-progress-fill {
  height: 100%;
  border-radius: 999px;
  background: var(--burgundy);
  transition: width 0.3s var(--ease);
}
.rose-progress-fill.plum { background: var(--plum); }
.rose-progress-fill.coral { background: var(--coral); }
.rose-progress-fill.success { background: var(--success); }

/* =========================================================================
   Tabs
   ========================================================================= */
.rose-tabs { display: flex; gap: 0.3rem; border-bottom: 1px solid var(--line); margin-bottom: 1.5rem; }
/* NOTE: these need the "button." prefix, not just ".rose-tab", to beat
   the generic button:not(:disabled) pill-button rule on specificity —
   without it every tab rendered as a solid coral pill instead of a flat
   underlined tab. Same trick button.secondary/button.ghost-danger already
   used; .rose-tab alone was the one spot that had been missed. */
button.rose-tab {
  background: none;
  border: none;
  border-radius: 0;
  padding: 0.7rem 1rem;
  font-weight: 600;
  font-size: 0.92rem;
  color: var(--muted);
  cursor: pointer;
  border-bottom: 2.5px solid transparent;
  margin-bottom: -1px;
}
button.rose-tab:hover:not(.active) { color: var(--burgundy); filter: none; transform: none; }
button.rose-tab.active { color: var(--burgundy); border-bottom-color: var(--burgundy); font-weight: 700; }

/* =========================================================================
   Tooltip
   ========================================================================= */
.rose-tooltip-wrap { position: relative; display: inline-flex; }
.rose-tooltip-wrap::after {
  content: attr(data-tooltip);
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%) translateY(4px);
  background: var(--navy);
  color: #fff;
  font-size: 0.78rem;
  font-weight: 500;
  padding: 0.4rem 0.65rem;
  border-radius: 8px;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s var(--ease), transform 0.15s var(--ease);
  z-index: 50;
}
.rose-tooltip-wrap:hover::after, .rose-tooltip-wrap:focus-visible::after {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}

/* =========================================================================
   Modal
   ========================================================================= */
.rose-modal-backdrop {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(43, 38, 34, 0.45);
  z-index: 300;
  align-items: center;
  justify-content: center;
  padding: 1.5rem;
}
.rose-modal-backdrop.open { display: flex; }
.rose-modal {
  background: var(--card);
  border-radius: 20px;
  box-shadow: var(--shadow-lg);
  padding: 1.8rem;
  max-width: 480px;
  width: 100%;
  max-height: 90vh;
  overflow-y: auto;
}
.rose-modal h2 { margin-top: 0; }
.rose-modal-actions { display: flex; justify-content: flex-end; gap: 0.6rem; margin-top: 1.4rem; }

/* =========================================================================
   Tables
   ========================================================================= */
.rose-table { width: 100%; border-collapse: collapse; margin-bottom: 0.5rem; }
.rose-table td, .rose-table th {
  padding: 0.5rem 0.25rem;
  border-bottom: 1px solid var(--line);
  font-size: 0.95rem;
  vertical-align: middle;
  text-align: left;
}
.rose-table th { font-size: 0.8rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.03em; }
.rose-table td.amount { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }

/* =========================================================================
   Chat with Rose — bubble/panel base tokens for rose-chat-widget.js
   to key off, keeping its JS-built styles referencing the same palette
   this file defines instead of its own separate hardcoded hex.
   ========================================================================= */
.rose-chat-bubble, .rose-chat-panel { --chat-burgundy: var(--burgundy); --chat-wine: var(--wine); }
