/* =============================================================================
   PlugMySite — Homepage Design System (design tokens)
   -----------------------------------------------------------------------------
   Source-of-truth token layer for the homepage overhaul (Wave 2). Green-led to
   match the established storefront brand (forest #0b3b2c → mid #15803d → lime
   #84cc16). A warm coral/amber pair exists ONLY as a sparing highlight accent
   (badges, "new" pips) — never as a primary CTA fill. This file is ADDITIVE:
   every token is a new `--pms-*` custom property, so it cannot clobber the
   existing `--accent / --gradient / --bg` variables the rest of the page uses.

   Scope: component helpers are namespaced under `.pms-homepage` (set on <body>),
   so linking this file from another page would not restyle that page. The
   `:root` block only DEFINES properties; they take effect only where referenced.

   Linked from plugmysite-platform.html <head>. Served locally by
   express.static(__dirname) and in production by an explicit sendFile route in
   server.js (Vercel's nft tracer does not follow express.static).
   ============================================================================= */

/* ---- Metric-matched font fallbacks (L-092, CLS) -----------------------------------
   Google Fonts load with display=swap on most pages. Until Inter/Outfit arrive the browser paints a
   system font whose glyphs are narrower/wider and whose line box is taller/shorter, so text reflows
   when the real font swaps in (layout shift). These faces re-skin a local system font to the web
   font's metrics, so the pre-swap layout is (nearly) the post-swap layout. They are listed straight
   after the web font in every stack below, so they only ever paint while the web font is loading.
   Inter-vs-Arial values are the widely published capsize/fontaine numbers. Outfit's are an approximation
   (the font files are not in the repo): re-derive both with capsize when the fonts are self-hosted. */
@font-face {
  font-family: "Inter Fallback";
  src: local("Arial"), local("Helvetica Neue"), local("Helvetica"), local("Liberation Sans");
  size-adjust: 107.4%;
  ascent-override: 90.2%;
  descent-override: 22.48%;
  line-gap-override: 0%;
}
@font-face {
  font-family: "Outfit Fallback";
  src: local("Arial"), local("Helvetica Neue"), local("Helvetica"), local("Liberation Sans");
  size-adjust: 104%;
  ascent-override: 96.2%;
  descent-override: 25%;
  line-gap-override: 0%;
}

:root {
  /* ---- Brand ramp — forest green, MATCHES LIVE MAIN --------------------
     The 2026 indigo rebrand (8568bc3) swapped this ramp to slate; the revert
     (f88e0e0) restored --pms-primary but left the ramp grey, so every
     brand-token consumer (section eyebrows, eyebrow tick gradients, ghost-CTA
     hover borders — ~84 uses on the storefront alone) rendered cold slate
     instead of the brand green. Values below are byte-identical to main. */
  --pms-brand-950: #08291f;
  --pms-brand-900: #0b3b2c;   /* deep forest — primary brand */
  --pms-brand-800: #0f5238;
  --pms-brand-700: #15803d;   /* mid green */
  --pms-brand-600: #1d9e6f;
  --pms-brand-500: #22b673;
  --pms-brand-lime: #84cc16;  /* lime — DECORATIVE only (halos, art), never text on light */

  /* ---- Semantic aliases (preferred names going forward) ------------------
     2026 rebrand REVERTED: indigo/slate primary read cold + blue-on-blue.
     Primary is now teal-emerald green (Stripe-ish, keeps gravitas, not clinical). */
  --pms-primary: #0e6b4b;         /* teal-700 — modern professional green (was slate #334155) */
  --pms-primary-600: #0b543b;     /* darker teal — hover (was slate #1e293b) */
  --pms-primary-hover: #0b543b;   /* alias for hover consumers */
  --pms-primary-fg: #ffffff;      /* text on primary bg */
  --pms-accent: #059669;      /* emerald-600 — CTA / data-positive */
  --pms-accent-600: #047857;
  --pms-legacy-green: #15803d;/* kept for intentional per-vertical accents */

  /* ---- Warm highlight accent (use sparingly) ----------------------------- */
  --pms-coral: #f97316;
  --pms-amber: #f59e0b;

  /* ---- Surfaces (cool near-white, Kite-flat) ----------------------------- */
  --pms-surface-0: #fafafa;   /* page bg */
  --pms-surface-1: #ffffff;   /* solid card */
  --pms-surface-2: #f3f5f1;   /* subtle cool tint */
  --pms-surface-ink: #0b1f17; /* dark surface (POS-style sections) */

  /* ---- Ink (text) — slate (all AA on white; tertiary is large/decorative) */
  --pms-ink-primary: #0f172a;
  --pms-ink-secondary: #475569;
  --pms-ink-muted: #586578;    /* AA 5.9 on white, 5.4 on cream (#f6f7f3) — real body-muted text */
  --pms-ink-tertiary: #94a3b8; /* faint — large text / decorative only */
  --pms-ink-inverse: #ffffff;

  /* ---- Borders (hairline) ------------------------------------------------ */
  --pms-border: rgba(15, 23, 42, 0.08);
  --pms-border-strong: rgba(15, 23, 42, 0.14);
  --pms-border-glass: rgba(30, 58, 95, 0.12);

  /* ---- Semantic ---------------------------------------------------------- */
  --pms-success: #16a34a;     /* teal accent = up / positive / paid */
  --pms-success-text: #0e6b4b;
  --pms-warn: #d97706;
  --pms-danger: #c53030;      /* muted brick, not screaming red */

  /* ---- Gradients (teal → emerald green) ---------------------------------- */
  --pms-gradient-brand: linear-gradient(135deg, #0e6b4b 0%, #047857 100%); /* both stops >=5.4:1 with white text */
  --pms-gradient-brand-deep: linear-gradient(135deg, #064e3b 0%, #0e6b4b 60%, #047857 100%);
  --pms-gradient-glow: linear-gradient(135deg, #6ee7a8 0%, #34d399 100%); /* always paired with dark ink (#07271c, >=6:1 at both stops) — never white text */
  --pms-gradient-warm: linear-gradient(135deg, #c2410c 0%, #b45309 100%); /* white text >=5:1 at both stops */
  --pms-gradient-soft: linear-gradient(135deg, #eef2ec 0%, #e1e6df 100%);
  --pms-gradient-mesh:
    radial-gradient(60% 55% at 78% 18%, rgba(22,163,74,0.16) 0%, transparent 60%),
    radial-gradient(55% 50% at 12% 30%, rgba(5,150,105,0.16) 0%, transparent 58%),
    radial-gradient(45% 45% at 50% 100%, rgba(14,107,75,0.10) 0%, transparent 60%);

  /* ---- Typography -------------------------------------------------------- */
  /* Display = Outfit (geometric, editorial). Body = Inter/system for legibility.
     The pairing is the single biggest lever against the "Inter-everywhere" look. */
  --pms-font-display: "Outfit", "Outfit Fallback", "Inter", "Inter Fallback", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --pms-font-body: "Inter", "Inter Fallback", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;

  --pms-fs-xs: 0.75rem;        /* 12 */
  --pms-fs-sm: 0.875rem;       /* 14 */
  --pms-fs-base: clamp(1rem, 1.4vw, 1.125rem);   /* 16 → 18 body */
  --pms-fs-lg: 1.25rem;        /* 20 */
  --pms-fs-xl: 1.5rem;         /* 24 */
  --pms-fs-2xl: clamp(1.625rem, 2.6vw, 2.125rem); /* section h2 26 → 34 */
  --pms-fs-3xl: clamp(2rem, 3.6vw, 2.75rem);
  --pms-fs-display: clamp(3rem, 7vw, 6rem);       /* hero headline — make it commit */
  --pms-fs-section: clamp(1.9rem, 4vw, 3.25rem);  /* section h2 — bigger, confident */

  --pms-lh-tight: 1.08;
  --pms-lh-snug: 1.25;
  --pms-lh-normal: 1.6;

  --pms-fw-regular: 400;
  --pms-fw-medium: 500;
  --pms-fw-semibold: 600;
  --pms-fw-bold: 700;
  --pms-fw-extrabold: 800;
  --pms-fw-black: 900;

  --pms-tracking-tight: -0.02em;
  --pms-tracking-tighter: -0.04em;  /* display headlines — hand-tuned, premium */
  --pms-tracking-eyebrow: 0.16em;   /* uppercase section labels */
  --pms-tracking-wide: 0.06em;

  /* ---- Spacing scale (4px base) ----------------------------------------- */
  --pms-space-1: 4px;
  --pms-space-2: 8px;
  --pms-space-3: 12px;
  --pms-space-4: 16px;
  --pms-space-5: 20px;
  --pms-space-6: 24px;
  --pms-space-8: 32px;
  --pms-space-10: 40px;
  --pms-space-12: 48px;
  --pms-space-16: 64px;
  --pms-space-section: clamp(44px, 5vw, 80px); /* major-section padding-block (was 64-128px: ~230px of blank band between sections on desktop) */

  /* ---- Radius (Kite-tight: 8px cards, 6px buttons, 4px inputs) ----------- */
  --pms-radius-sm: 6px;
  --pms-radius-md: 8px;
  --pms-radius-lg: 12px;
  --pms-radius-xl: 16px;
  --pms-radius-full: 999px;

  /* ---- Shadows (crisp, neutral slate — depth via border + subtle lift) --- */
  --pms-shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.05), 0 1px 3px rgba(15, 23, 42, 0.04);
  --pms-shadow-md: 0 4px 12px rgba(15, 23, 42, 0.07), 0 2px 6px rgba(15, 23, 42, 0.05);
  --pms-shadow-lg: 0 14px 34px rgba(15, 23, 42, 0.10), 0 4px 10px rgba(15, 23, 42, 0.06);
  --pms-shadow-xl: 0 30px 70px rgba(15, 23, 42, 0.16), 0 10px 24px rgba(15, 23, 42, 0.08);
  --pms-glow-brand: 0 10px 34px rgba(14,107,75, 0.22);
  --pms-glow-lime: 0 0 0 1px rgba(22,163,74, 0.25), 0 12px 30px rgba(22,163,74, 0.18);

  /* ---- Motion (functional, deliberate — no bounce) ----------------------- */
  --pms-ease-out-quart: cubic-bezier(0.4, 0, 0.2, 1);
  --pms-ease-spring: cubic-bezier(0.4, 0, 0.2, 1);
  --pms-duration-fast: 120ms;
  --pms-duration-normal: 160ms;
  --pms-duration-slow: 240ms;
}

/* =============================================================================
   Dark mode — token overrides. First-class surface: activated by
   <html data-theme="dark"> (toggle persists per tenant) OR the OS preference.
   Real dark palette, not an inverted filter.
   ============================================================================= */
:root[data-theme="dark"] {
  --pms-surface-0: #0b0f14;
  --pms-surface-1: #111820;
  --pms-surface-2: #161f2b;
  --pms-surface-ink: #060a10;
  --pms-ink-primary: #e1e6df;
  --pms-ink-secondary: #94a3b8;
  --pms-ink-muted: #8b9aa8; /* was #64748b (4.04:1 on surface-0, fails AA) -> ~5.6:1 */
  --pms-border: rgba(255, 255, 255, 0.08);
  --pms-border-strong: rgba(255, 255, 255, 0.16);
  --pms-brand-900: #0e6b4b;   /* teal-700 — legible brand green on dark surfaces */
  --pms-primary: #86efac;     /* teal-300 — legible green on dark surfaces */
  --pms-primary-600: #6ee7a8; /* teal-400 */
  --pms-primary-hover: #6ee7a8;
  --pms-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
  --pms-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.5);
  --pms-shadow-lg: 0 14px 34px rgba(0, 0, 0, 0.55);
}
/* Dark mode is intentionally NOT auto-applied. Nearly every section paints a hard-coded
   light surface; flipping only the ink tokens for prefers-color-scheme:dark produced
   light-grey headings on light cards (invisible text) for every visitor on a dark OS.
   The explicit [data-theme="dark"] opt-in above is kept for pages that ship a full dark skin. */
:root:not([data-theme="dark"]) { color-scheme: light; }

/* =============================================================================
   Homepage component helpers (scoped under .pms-homepage)
   ============================================================================= */

.pms-homepage { scroll-behavior: smooth; }

/* Section rhythm — generous, breathing whitespace */
.pms-section { padding-block: var(--pms-space-section); }
.pms-section--tight { padding-block: clamp(40px, 5vw, 72px); }

/* Soft gradient divider (replaces hard <hr>) */
.pms-divider {
  height: 1px; border: 0; max-width: 1100px; margin: 0 auto;
  background: linear-gradient(90deg, transparent, var(--pms-border-strong) 18%, var(--pms-border-strong) 82%, transparent);
}

/* NOTE: reveal-on-scroll (.pms-reveal / .in) is owned by the existing inline
   system near the bottom of plugmysite-platform.html — do NOT redefine it here,
   or the two definitions conflict. This file only supplies the static tokens and
   the non-reveal component helpers below. */

/* Card lift on hover */
.pms-lift { transition: transform var(--pms-duration-normal) var(--pms-ease-out-quart),
                        box-shadow var(--pms-duration-normal) var(--pms-ease-out-quart),
                        border-color var(--pms-duration-normal) var(--pms-ease-out-quart); }
.pms-lift:hover { transform: translateY(-4px); box-shadow: var(--pms-shadow-lg); }

/* Button micro-interactions */
.pms-cta {
  transition: transform var(--pms-duration-fast) var(--pms-ease-out-quart),
              box-shadow var(--pms-duration-normal) var(--pms-ease-out-quart);
}
.pms-cta:hover { transform: scale(1.02); }
.pms-cta--brand { background: var(--pms-gradient-brand); color: var(--pms-ink-inverse); box-shadow: var(--pms-glow-brand); }
.pms-cta--brand:hover { box-shadow: 0 16px 44px rgba(11, 59, 44, 0.30); }
.pms-cta--ghost { background: var(--pms-surface-1); color: var(--pms-ink-primary); border: 1.5px solid var(--pms-border-strong); }
.pms-cta--ghost:hover { border-color: var(--pms-brand-900); background: var(--pms-surface-2); box-shadow: var(--pms-shadow-md); }

/* Step number medallion (How it works) */
.pms-step-num {
  width: 44px; height: 44px; border-radius: var(--pms-radius-full);
  background: var(--pms-gradient-brand); color: #fff;
  display: grid; place-items: center; font-weight: var(--pms-fw-black); font-size: 18px;
  box-shadow: var(--pms-glow-brand);
}

@media (prefers-reduced-motion: reduce) {
  .pms-homepage { scroll-behavior: auto; }
  .pms-lift, .pms-cta { transition: none !important; }
  .pms-lift:hover, .pms-cta:hover { transform: none !important; }
}

/* =============================================================================
   V3 TYPOGRAPHY LAYER — Fortune-500 type scale (Wave 3 redesign)
   -----------------------------------------------------------------------------
   Scoped under .pms-homepage so it outranks the page's later inline base rules
   (the inline <style> block loads after this file, so equal-specificity rules
   would otherwise win — the .pms-homepage prefix buys the specificity to lead).
   This is the cross-cutting type foundation every redesigned section builds on:
   massive display headlines on Outfit, editorial uppercase eyebrows, and a body
   size that reads premium rather than template-grade.
   ============================================================================= */

/* Body lifts from 16 → 17px; headings adopt the Outfit display face. */
.pms-homepage { font-size: 17px; }

.pms-homepage h1, .pms-homepage h2, .pms-homepage h3,
.pms-homepage .pms-hero-h1, .pms-homepage .section-head h2 {
  font-family: var(--pms-font-display);
}

/* Hero headline — make it BIG and commit. Tight tracking, sub-1 line-height. */
.pms-homepage .pms-hero-h1 {
  font-size: var(--pms-fs-display);
  font-weight: var(--pms-fw-black);
  line-height: 0.95;
  letter-spacing: var(--pms-tracking-tighter);
  text-wrap: balance;
}

/* Section headlines — confident, larger, tighter than the template default. */
.pms-homepage .section-head h2,
.pms-homepage .pms-h2 {
  font-family: var(--pms-font-display);
  font-size: var(--pms-fs-section);
  font-weight: var(--pms-fw-extrabold);
  line-height: 1.04;
  letter-spacing: var(--pms-tracking-tighter);
  color: var(--pms-ink-primary);
  text-wrap: balance;
}

/* Editorial eyebrow — the one premium trope worth keeping. Tiny, uppercase,
   wide-tracked, accent-green TEXT (not a pill). Replaces the old gradient chip
   everywhere on the homepage; .pms-eyebrow is the reusable element for new
   sections, and the legacy .section-head .eyebrow / .hero .eyebrow inherit it. */
.pms-homepage .pms-eyebrow,
.pms-homepage .section-head .eyebrow,
.pms-homepage .hero .eyebrow {
  display: inline-flex; align-items: center; gap: 9px;
  background: none !important; border: none !important; box-shadow: none !important;
  padding: 0 !important; margin-bottom: var(--pms-space-4);
  font-family: var(--pms-font-body);
  font-size: 12.5px; font-weight: var(--pms-fw-bold);
  letter-spacing: var(--pms-tracking-eyebrow); text-transform: uppercase;
  color: var(--pms-brand-700) !important;
  -webkit-text-fill-color: var(--pms-brand-700);  /* override any clip-text inherit */
}
/* Leading tick mark for the editorial eyebrow */
.pms-homepage .pms-eyebrow::before,
.pms-homepage .section-head .eyebrow::before {
  content: ""; width: 22px; height: 2px; border-radius: 2px;
  background: linear-gradient(90deg, var(--pms-brand-700), var(--pms-brand-lime));
  flex: 0 0 auto;
}

/* Section sub-copy — larger, calmer, generous measure. */
.pms-homepage .section-head p {
  font-size: 1.0625rem; line-height: 1.6; color: var(--pms-ink-muted);
  max-width: 56ch; font-weight: var(--pms-fw-regular);
}

/* Hero sub-copy — slightly larger than feels safe, muted, roomy. */
.pms-homepage .pms-hero-sub {
  font-size: clamp(1.0625rem, 1.5vw, 1.1875rem);
  line-height: 1.5; font-weight: var(--pms-fw-regular);
  color: var(--pms-ink-secondary); max-width: 52ch;
}

@media (max-width: 600px) {
  .pms-homepage { font-size: 16.5px; }
  .pms-homepage .pms-eyebrow,
  .pms-homepage .section-head .eyebrow,
  .pms-homepage .hero .eyebrow { font-size: 11.5px; letter-spacing: 0.14em; }
}

/* =============================================================================
   Shared responsive utilities (GLOBAL — not namespaced)
   -----------------------------------------------------------------------------
   Reusable, device-agnostic helpers. Opt-in by adding the class to an element.
   Safe to use on any page that links this stylesheet.

   Alignment convention (documented):
     • Hero headlines  → left at all breakpoints (editorial anchor).
     • Section intros  → may center on desktop over symmetric content, but
       UNIFY LEFT on mobile (≤640px) to match the hero — a single narrow
       column removes the layout cue that justifies centering. Use
       `.pms-intro-mobile-left` to apply that rule to a custom intro block.
   ========================================================================== */

/* Balanced multi-line headline breaks (avoids a lone trailing word). */
.pms-text-wrap-balance { text-wrap: balance; }

/* Prettier paragraph rag (avoids typographic orphans where supported). */
.pms-text-pretty { text-wrap: pretty; }

/* Never let a long unbroken token (URL, email, product id) break the layout.
   `min-width:0` also fixes the classic flex bug where a text child refuses to
   shrink below its content width. */
.pms-no-overflow { min-width: 0; word-break: break-word; overflow-wrap: anywhere; }

/* WCAG / Apple-HIG minimum 44×44 hit area — expand via padding without
   forcing the visual size to grow. */
.pms-tap-target {
  min-width: 44px; min-height: 44px;
  display: inline-flex; align-items: center; justify-content: center;
}

/* Fluid horizontal page gutter that stays comfortable from 320 → desktop. */
.pms-container-mobile-safe { padding-inline: clamp(16px, 5vw, 64px); }

/* Fluid vertical section rhythm — tight on phones, generous on desktop. */
.pms-clamp-section { padding-block: clamp(48px, 8vw, 128px); }

/* Section intro that left-aligns on a narrow column (see convention above). */
@media (max-width: 640px) {
  .pms-intro-mobile-left,
  .pms-intro-mobile-left * { text-align: left !important; }
  .pms-intro-mobile-left :is(p, h1, h2, h3) { margin-inline: 0 !important; }
}

@media (prefers-reduced-motion: reduce) {
  /* Utilities above are static; nothing to disable. Placeholder kept so future
     motion-bearing utilities respect reduced-motion in one obvious spot. */
}

/* =============================================================================
   V4 PREMIUM LAYER — glassmorphism, gradient mesh, "expensive" motion, dark mode
   -----------------------------------------------------------------------------
   Additive. New --pms-* tokens + a small set of utility classes that give any
   surface Linear/Stripe/Notion-grade depth. Nothing here changes existing
   selectors; opt in per element with the class. Dark mode is a variable layer
   toggled with `body.pms-dark` (or [data-pms-theme="dark"]) and only affects
   elements that read --pms-* tokens, so it can be adopted page-by-page.
   ============================================================================= */
:root {
  /* Expensive easing (long, decelerating settle) + luxe durations. NOTE: kept
     as NEW tokens — the existing --pms-duration-normal (280ms) is intentionally
     left alone so current .pms-lift / .pms-cta timing does not change. */
  --pms-ease-expensive: cubic-bezier(0.16, 1, 0.3, 1);
  --pms-ease-in-out-soft: cubic-bezier(0.45, 0, 0.15, 1);
  --pms-duration-lux: 600ms;
  --pms-duration-panel: 420ms;

  /* Glass surface tokens (light theme) */
  --pms-glass-bg: rgba(255, 255, 255, 0.62);
  --pms-glass-bg-strong: rgba(255, 255, 255, 0.78);
  --pms-glass-border: rgba(11, 59, 44, 0.10);
  --pms-glass-highlight: rgba(255, 255, 255, 0.65);
  --pms-glass-blur: 20px;
}

/* ---- Glassmorphism card ------------------------------------------------- */
.pms-glass-card,
.pms-glass {
  position: relative;
  background: var(--pms-glass-bg);
  -webkit-backdrop-filter: saturate(160%) blur(var(--pms-glass-blur));
  backdrop-filter: saturate(160%) blur(var(--pms-glass-blur));
  border: 1px solid var(--pms-glass-border);
  border-radius: var(--pms-radius-xl);
  box-shadow:
    0 1px 0 var(--pms-glass-highlight) inset,          /* top inner highlight */
    0 20px 50px rgba(11, 59, 44, 0.12),                /* soft drop */
    0 2px 8px rgba(11, 59, 44, 0.06);
}
.pms-glass--strong { background: var(--pms-glass-bg-strong); }
/* Progressive enhancement: on browsers without backdrop-filter, fall back to a
   near-opaque surface so text stays legible. */
@supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  .pms-glass-card, .pms-glass { background: var(--pms-surface-1); }
}

/* ---- Gradient mesh background ------------------------------------------- */
/* Layers the mesh token over an optional base. Add to any section wrapper. */
.pms-mesh-bg {
  position: relative;
  background-image: var(--pms-gradient-mesh);
  background-repeat: no-repeat;
}
/* A stronger, animated mesh for hero-scale sections. Motion is gentle and
   respects reduced-motion below. */
.pms-mesh-hero {
  position: relative;
  background-image:
    radial-gradient(50% 45% at 80% 12%, rgba(132,204,22,0.20) 0%, transparent 62%),
    radial-gradient(48% 42% at 8% 26%, rgba(21,128,61,0.18) 0%, transparent 60%),
    radial-gradient(52% 48% at 50% 108%, rgba(11,59,44,0.12) 0%, transparent 60%);
  background-repeat: no-repeat;
}
.pms-mesh-hero::before {
  content: ""; position: absolute; inset: -20% -10% auto -10%; height: 140%;
  background: radial-gradient(40% 40% at 30% 20%, rgba(34,182,115,0.10), transparent 70%);
  pointer-events: none; z-index: 0;
  animation: pms-mesh-drift 18s var(--pms-ease-in-out-soft) infinite alternate;
}
@keyframes pms-mesh-drift {
  from { transform: translate3d(-3%, -2%, 0) scale(1.02); }
  to   { transform: translate3d(4%, 3%, 0) scale(1.08); }
}

/* ---- Expensive lift (longer settle than .pms-lift) --------------------- */
.pms-lift-lux {
  transition: transform var(--pms-duration-lux) var(--pms-ease-expensive),
              box-shadow var(--pms-duration-lux) var(--pms-ease-expensive);
  will-change: transform;
}
.pms-lift-lux:hover { transform: translateY(-6px); box-shadow: var(--pms-shadow-xl); }

/* =============================================================================
   Dark mode variable layer — body.pms-dark / [data-pms-theme="dark"]
   ============================================================================= */
body.pms-dark,
[data-pms-theme="dark"] {
  --pms-surface-0: #0a1712;
  --pms-surface-1: #10231a;
  --pms-surface-2: #16291f;
  --pms-surface-ink: #060f0b;

  --pms-ink-primary: #eaf3ee;
  --pms-ink-secondary: #b7c7be;
  --pms-ink-muted: #8b9c93;
  --pms-ink-inverse: #0a1712;

  --pms-border: #24382d;
  --pms-border-strong: #2f463a;
  --pms-border-glass: rgba(255, 255, 255, 0.10);

  --pms-glass-bg: rgba(16, 35, 26, 0.55);
  --pms-glass-bg-strong: rgba(16, 35, 26, 0.78);
  --pms-glass-border: rgba(255, 255, 255, 0.10);
  --pms-glass-highlight: rgba(255, 255, 255, 0.06);

  --pms-shadow-sm: 0 1px 2px rgba(0,0,0,0.35);
  --pms-shadow-md: 0 6px 18px rgba(0,0,0,0.40);
  --pms-shadow-lg: 0 16px 40px rgba(0,0,0,0.48);
  --pms-shadow-xl: 0 30px 70px rgba(0,0,0,0.55);
}

@media (prefers-reduced-motion: reduce) {
  .pms-mesh-hero::before { animation: none; }
  .pms-lift-lux { transition: none; }
  .pms-lift-lux:hover { transform: none; }
}

/* =============================================================================
   AMBIENT AURORA LAYER (SITE-WIDE, marketing chrome only)
   -----------------------------------------------------------------------------
   Cordia-style layered graphic depth: two soft brand-green aurora glows pinned
   to the top corners of the viewport plus a faint floor glow, all blurred and
   sitting BEHIND content (z-index:-1 paints above the canvas background but
   below everything in-flow, so opaque cards/sections simply cover it). Applied
   automatically to every page that carries the shared marketing header
   (.pmsh-nav injected by shared-header.js — dashboards/POS/portal stay clean).
   body.pmsh-page is the no-:has() fallback class set by shared-header.js.
   ============================================================================= */
body:has(.pmsh-nav)::before,
body.pmsh-page::before {
  content: ""; position: fixed; left: -10%; right: -10%; top: -12%;
  height: 132vh; z-index: -1; pointer-events: none;
  /* SINGLE-HUE, LOW-ALPHA. An earlier revision used four saturated sources
     (teal + lime + emerald + teal at up to 0.30 alpha, plus saturate(125%)).
     Multiple competing hues blurred together is the visual signature of a
     generated template — it reads as decoration applied TO the page rather
     than as the page's own light. One cool source at 0.10, no saturation
     boost, is enough to lift the canvas off flat white without announcing
     itself. Restraint here is the point; do not re-add a second hue. */
  background:
    radial-gradient(46% 38% at 84% -4%, rgba(14,107,75, 0.10), transparent 68%),
    radial-gradient(40% 32% at 8% 4%, rgba(14,107,75, 0.05), transparent 66%);
  filter: blur(60px);
}
/* Section-level aurora — stronger, for heroes / feature bands. Opt-in. */
.pms-aurora { position: relative; isolation: isolate; }
.pms-aurora::before {
  /* Vertical bleed only (`-8% 0`, NOT `-8%`): a negative horizontal inset makes
     the glow wider than its full-bleed section, and an absolutely positioned
     child DOES contribute to scrollable overflow — that produced 4px (375px)
     and 19px (1280px) of horizontal page scroll on every section using this
     utility. Blur is a filter effect and never widens the scroll area. */
  content: ""; position: absolute; inset: -8% 0; z-index: -1; pointer-events: none;
  /* Single hue, matching the body layer — see the note there on why the
     previous teal/lime/forest triad was removed. */
  background:
    radial-gradient(46% 46% at 78% 4%, rgba(14,107,75, 0.08), transparent 66%),
    radial-gradient(50% 50% at 50% 112%, rgba(11, 59, 44, 0.06), transparent 68%);
  filter: blur(44px);
}
/* Precision dot-grid over the glows — the STRUCTURAL half of the graphic layer.
   Blurred colour alone reads as a pale wash; a fine measured grid is what makes
   it read as a designed surface. Masked to fade out by ~70% viewport height so
   it frames the fold without cluttering body content. (Replaces an earlier film
   grain that used mix-blend-mode:overlay — which is a no-op on white.) */
body:has(.pmsh-nav)::after,
body.pmsh-page::after {
  content: ""; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background-image: radial-gradient(rgba(11, 59, 44, 0.07) 1px, transparent 1px);
  background-size: 22px 22px;
  -webkit-mask-image: linear-gradient(180deg, #000 0%, rgba(0,0,0,0.38) 44%, transparent 74%);
  mask-image: linear-gradient(180deg, #000 0%, rgba(0,0,0,0.38) 44%, transparent 74%);
}
@media (prefers-reduced-motion: reduce) {
  /* static layers — nothing animates; kept for the future pulse variant */
}
/* =============================================================================
   INK BAND — full-bleed dark section (the RHYTHM primitive)
   -----------------------------------------------------------------------------
   A page where every section is white reads flat no matter how good the type is;
   alternating dark bands is what gives a modern marketing page its cadence.
   Add `pms-band-ink` to a <section> (pair it with `pms-aurora pms-aurora--ink`
   for the glow). Child overrides are deliberately scoped to this class so the
   same components keep their light styling everywhere else.
   ============================================================================= */
.pms-band-ink {
  position: relative; isolation: isolate;
  background: linear-gradient(180deg, #08211a 0%, #0d2c23 52%, #08211a 100%);
  color: #e8f2ee;
}
/* !important is REQUIRED here, not lazy: component rules like `.bapps-card p`
   and `.bapps-feats li` are (0,2,0) while a `.pms-band-ink :is(p)` descendant
   override is only (0,1,1), so without it every card kept its near-black body
   copy and rendered black-on-dark inside the band. Scoped entirely to
   .pms-band-ink, so no component's light-theme styling is affected elsewhere.
   Accent-coloured children (CTAs, chips, eyebrows) are re-asserted below. */
.pms-band-ink :is(h1, h2, h3, h4, b, strong) { color: #f3f8f6 !important; }
.pms-band-ink :is(p, li, .sub, .desc, .kpi-sub, small, td, th) { color: #b9d0c6 !important; }
/* Specificity note: the light V3 layer sets `.pms-homepage .section-head h2`
   (0,2,1) and `.pms-homepage .section-head p`. A `:is()` override above is only
   (0,1,1) and LOSES, which rendered dark-on-dark. These match at (0,2,1) and
   win on source order (this block is later in the file). */
.pms-band-ink .section-head h2, .pms-band-ink .ds-head h2 { color: #f3f8f6; }
.pms-band-ink .section-head p { color: #aec6bb; }
/* Gradient headline spans clip to a DARK ramp designed for white backgrounds —
   invisible here. Re-point the clip at the on-dark accent ramp. */
.pms-band-ink .gradient-text {
  background: linear-gradient(135deg, #86efac 0%, #84cc16 100%);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: transparent;
}
.pms-band-ink a:not([class]) { color: #86efac; }
/* Inline-styled links (color:var(--pms-brand-700)) lose to the rule above and fall to a dark green on dark. */
.pms-band-ink a[style]:not([class]) { color: #86efac !important; }
/* Eyebrows: the light-theme rule sets an !important brand-green + text-fill. */
.pms-band-ink :is(.pms-eyebrow, .section-head .eyebrow) {
  color: #86efac !important; -webkit-text-fill-color: #86efac !important;
}
.pms-band-ink :is(.pms-eyebrow, .section-head .eyebrow)::before {
  background: linear-gradient(90deg, #86efac, #84cc16);
}
/* Any card-ish child becomes glass-on-dark instead of an opaque white slab. */
.pms-band-ink :is(.bapps-card, .pms-glass-card, .card, .tpl, .pr-card, .pr-sub, .step, .kpi-card) {
  background: rgba(255, 255, 255, 0.045) !important;
  border-color: rgba(255, 255, 255, 0.11) !important;
  box-shadow: 0 1px 0 rgba(255,255,255,0.05) inset, 0 18px 44px rgba(0,0,0,0.34);
  color: #e8f2ee;
}
.pms-band-ink :is(.bapps-card, .card, .tpl):hover {
  background: rgba(255, 255, 255, 0.075) !important;
  border-color: rgba(134,239,172, 0.34) !important;
}
/* Small chips/tags inside a dark band. */
.pms-band-ink :is(.bapps-demo-tag, .badge, .chip, .cat) {
  background: rgba(134,239,172, 0.14); color: #a7f3d0 !important;
  border-color: rgba(134,239,172,0.26);
}
.pms-band-ink .bapps-ic { background: rgba(134,239,172, 0.13); color: #86efac; }
.pms-band-ink .bapps-cta, .pms-band-ink .bapps-cta b, .pms-band-ink .bapps-all { color: #86efac !important; }
/* Icon strokes inside the band (feature ticks, arrows) follow the accent. */
.pms-band-ink :is(.bapps-feats, .bapps-cta) svg { stroke: #86efac; }

/* Dark app surfaces (POS-style .pms-surface-ink sections) get a cooler glow. */
.pms-aurora--ink::before {
  background:
    radial-gradient(44% 46% at 82% 4%, rgba(134,239,172, 0.14), transparent 62%),
    radial-gradient(40% 44% at 8% 26%, rgba(110,231,168, 0.10), transparent 60%);
}

/* =============================================================================
   Anchor-scroll offsets (SITE-WIDE) — sticky-nav-aware landing
   -----------------------------------------------------------------------------
   The shared header (.pmsh-nav) is position:fixed, so a native `#anchor` jump
   lands the target UNDER the nav — which reads as "scrolled to the middle of the
   section". `scroll-padding-top` on the scroll container plus `scroll-margin-top`
   on section-level anchor targets push the landing point clear of the nav so the
   informative top (heading + intro) is visible. The mobile nav is a touch
   shorter, so the offset tightens below 768px (the user reported this breaking on
   mobile most of all). Purely additive; `:where()` keeps specificity at 0 so any
   page-level rule always wins. The homepage additionally uses a JS smart-scroll
   handler (content-visibility drift correction) that supersedes these on click.
   ============================================================================= */
:root { --pms-nav-h: 68px; }
@media (max-width: 768px){ :root { --pms-nav-h: 60px; } }

html { scroll-padding-top: calc(var(--pms-nav-h) + 24px); }

:where(section[id], [id].pms-section, [id].section, [id].pms-ind-section,
       .section-head[id], [data-scroll-anchor]) {
  scroll-margin-top: calc(var(--pms-nav-h) + 24px);
}
@media (max-width: 768px){
  :where(section[id], [id].pms-section, [id].section, [id].pms-ind-section,
         .section-head[id], [data-scroll-anchor]) {
    scroll-margin-top: calc(var(--pms-nav-h) + 16px);
  }
}

/* ── Global reduced-motion honor (a11y P1-13) ──────────────────────────────
   WCAG 2.3.3. Every page in the app links this file, so this one block makes
   all of them (POS, admin-portal, portal, the 19 generic dashboards) respect
   the OS 'reduce motion' setting — including the ~56 inline animations in
   pos.html that had no guard. Uses near-zero durations (not display:none) so
   animations still land on their final visual state instead of breaking. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

/* =============================================================================
   MWEB dashboard shell — shared fix for all vertical dashboards (loaded by every
   /{vertical}.html). Targets the copy-pasted .topbar / .mobile-nav / FAB pattern.
   !important is required because each vertical's inline <style> and the runtime-
   injected FAB <style> come AFTER this linked file in the cascade.
   ---------------------------------------------------------------------------
   Issues fixed (2026-07 mobile QA):
   1. Header row collides at 375px (title + clock + Take Payment).
   2. 8–10 tab strip has no scroll cue and clips labels.
   3. Ask-AI + POS FABs stack awkwardly / risk overlap.
   ============================================================================= */
@media (max-width: 768px) {
  /* 1 · HEADER — drop the cosmetic clock, tighten padding, keep title from
     shoving the Take-Payment button off-screen. */
  .topbar { padding-left: 12px !important; padding-right: 12px !important; gap: 8px !important; }
  .topbar-clock { display: none !important; }
  /* Tenant badge wraps to 3 lines at 375px and duplicates the title context —
     drop it on phones; the page title already names the surface. */
  .topbar-badge, .topbar .biz-badge { display: none !important; }
  .topbar-title {
    min-width: 0; flex: 1 1 auto;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  /* Take-Payment button collapses to an icon-only ＋ on phones: font-size:0 on the
     button eats the bare "Take Payment" text node + leading space; the icon <span>
     re-declares its own size so only the glyph shows. Keeps a 40px tap target. */
  .topbar .pms-pos-btn {
    font-size: 0 !important;
    padding: 0 !important;
    width: 40px; height: 40px; min-width: 40px;
    justify-content: center; gap: 0 !important; flex: 0 0 auto;
    order: -1; /* keep it leading, before the title */
  }
  .topbar .pms-pos-btn > span { font-size: 20px !important; line-height: 1 !important; }
}

/* 2 · BOTTOM NAV — 4 primary tabs + "More" overflow (see pms-mobnav.js).
   The bottom `.mobile-nav` is MOBILE-ONLY. pms-mobnav.js keeps the first 4
   authored tabs inline and folds the rest into a "More" tab that opens a small
   bottom sheet, so labels are never truncated and every tab stays reachable. */
@media (max-width: 768px) {
  /* Kill the old scroll-cue mask (nav no longer scrolls) and distribute the
     ≤5 visible slots evenly with room for full labels. */
  .mobile-nav {
    -webkit-mask-image: none !important; mask-image: none !important;
    overflow: visible !important;
  }
  .mobile-nav-btn {
    flex: 1 1 0 !important;
    min-width: 0 !important;
    font-size: 10px !important;
    padding: 0 2px !important;
    overflow: visible !important; text-overflow: clip !important;
    white-space: nowrap !important;
  }
  /* "More" bottom sheet — sits directly above the 60px nav bar. */
  .pms-more-scrim {
    position: fixed; inset: 0; z-index: 60;
    background: rgba(15, 23, 42, 0.32);
    -webkit-backdrop-filter: blur(1px); backdrop-filter: blur(1px);
  }
  .pms-more-sheet {
    position: fixed; left: 0; right: 0;
    bottom: calc(60px + env(safe-area-inset-bottom));
    z-index: 61;
    display: grid; grid-template-columns: repeat(3, 1fr); gap: 4px;
    padding: 12px 12px calc(12px + env(safe-area-inset-bottom));
    background: var(--card, #fff);
    border-top: 1px solid var(--border, rgba(15, 23, 42, 0.1));
    border-radius: 16px 16px 0 0;
    box-shadow: 0 -10px 30px rgba(15, 23, 42, 0.18);
    transform: translateY(12px); opacity: 0;
    transition: transform .18s cubic-bezier(0.16, 1, 0.3, 1), opacity .18s;
  }
  .pms-more-sheet.open { transform: translateY(0); opacity: 1; }
  /* While the sheet is open, suppress the floating launchers (their very high
     z-index would otherwise sit over a sheet item). */
  body.pms-more-open .pms-ai-fab,
  body.pms-more-open .pmsc-btn,
  body.pms-more-open .pms-pos-btn.float { display: none !important; }
  .pms-more-sheet .pms-more-item {
    flex: none !important; min-width: 0 !important;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 5px; padding: 12px 6px !important; border-radius: 12px;
    font-size: 11px !important; font-weight: 600; min-height: 64px;
    color: var(--text, #0b1f17) !important;
    background: color-mix(in srgb, var(--accent, #059669) 6%, transparent);
  }
  .pms-more-sheet .pms-more-item svg { width: 20px; height: 20px; }
  .pms-more-sheet .pms-more-item.on {
    color: var(--accent, #059669) !important;
    background: color-mix(in srgb, var(--accent, #059669) 12%, transparent);
  }
}
@media (prefers-reduced-motion: reduce) {
  .pms-more-sheet { transition: none !important; }
}

@media (max-width: 768px) {
  /* 3 · FABs — the bottom nav owns the bottom edge, but the "← Back to home" pill
     is MANDATORY on every demo (a visitor must always be able to leave), so it is
     NOT hidden. Instead the whole bottom-right stack is LIFTED clear ABOVE the
     60px nav bar. Order from the nav upward: the back pill sits closest (lowest),
     then the "✦ Ask AI" launcher / concierge button, then the floating POS button
     — each with ≥12px clearance so no two ever touch and none sit on a nav tab.
     EVERY rule here is scoped to pages that actually have a bottom nav (:has()
     with a body.pms-has-mobnav no-:has() fallback). Pages WITHOUT a bottom nav
     (portal, queue, consulting, storefront, gate/preview pages) keep each
     widget's own JS-injected default position — same behavior as live main.
     The back-pill rule overrides the inline bottom/right that pms-back.js sets via
     data-corner="br" (those inline styles carry no !important, so these win). */
  body:has(.mobile-nav) .pms-back-pill,
  body.pms-has-mobnav .pms-back-pill {
    top: auto !important;
    left: auto !important;
    right: 16px !important;
    bottom: calc(72px + env(safe-area-inset-bottom)) !important;
  }
  body:has(.mobile-nav) .pms-ai-fab,
  body.pms-has-mobnav .pms-ai-fab {
    right: 16px !important;
    bottom: calc(124px + env(safe-area-inset-bottom)) !important;
  }
  body:has(.mobile-nav) .pmsc-btn,
  body.pms-has-mobnav .pmsc-btn {
    right: 16px !important;
    bottom: calc(124px + env(safe-area-inset-bottom)) !important;
    width: 52px !important; height: 52px !important;
  }
  body:has(.mobile-nav) .pms-pos-btn.float,
  body.pms-has-mobnav .pms-pos-btn.float {
    right: 16px !important;
    bottom: calc(180px + env(safe-area-inset-bottom)) !important;
  }
}

/* =============================================================================
   FOOTER GUARD — hide the fixed floating pills ("← Back to home" .pms-back-pill,
   "✦ Ask AI" .pms-ai-fab, concierge .pmsc-btn, floating POS .pms-pos-btn.float)
   whenever the SEO breadcrumb footer (.pms-seo-foot) is scrolled into view.
   pms-mobnav.js sets `body.pms-foot-in-view` via an IntersectionObserver. Applies
   at ALL widths (the pills overlap the dark-green footer on desktop too); the
   footer itself carries Home / Pricing / industry links so nothing is lost.
   ============================================================================= */
body.pms-foot-in-view .pms-back-pill,
body.pms-foot-in-view .pms-ai-fab,
body.pms-foot-in-view .pmsc-btn,
body.pms-foot-in-view .pms-pos-btn.float { display: none !important; }

/* =============================================================================
   4 · TABLE OVERFLOW — every dashboard table is wrapped in `.tbl-wrap`, which the
   verticals define as `overflow:hidden` (to clip the rounded corners). On phones a
   wide table (STATUS / DUE / DEPARTMENT columns) then gets CLIPPED at the viewport
   edge with no way to reach it. Turn the wrapper into a horizontal scroller and
   layer a right-edge fade cue that only shows when there is more to scroll.
   The Lea Verou "scroll shadow" trick (background-attachment: local) auto-hides
   the shadow at the ends and when the table already fits — so desktop is untouched.
   ============================================================================= */
.tbl-wrap {
  overflow-x: auto !important;
  overflow-y: hidden !important;
  -webkit-overflow-scrolling: touch;
  /* !important is required: the vertical's inline <style> loads after this file
     and sets `.tbl-wrap{background:var(--card)}`, which would erase the cue. */
  background:
    /* cover shadows sitting over the card fill */
    linear-gradient(to right, var(--card, #fff) 30%, transparent),
    linear-gradient(to right, transparent, var(--card, #fff) 70%) 100% 0,
    /* the actual right/left fade shadows */
    radial-gradient(farthest-side at 0 50%, rgba(15, 23, 42, 0.14), transparent),
    radial-gradient(farthest-side at 100% 50%, rgba(15, 23, 42, 0.14), transparent) 100% 0 !important;
  background-color: var(--card, #fff) !important;
  background-repeat: no-repeat !important;
  background-size: 36px 100%, 36px 100%, 14px 100%, 14px 100% !important;
  background-attachment: local, local, scroll, scroll !important;
}
/* Keep columns at their natural width so the wrapper scrolls instead of squashing
   the last column off-screen. */
.tbl-wrap > table { min-width: 100%; }
@media (max-width: 768px) {
  .tbl-wrap > table { width: max-content; min-width: 100%; }
}

/* =============================================================================
   TRILLION-DOLLAR DASHBOARD SHELL  —  reference layer (Linear / Stripe / Vercel bar)
   -----------------------------------------------------------------------------
   Gated behind a `.pms-shell-v2` class on <body>. Lives in the shared stylesheet
   every vertical loads, so ROLLOUT = add the body class to a dashboard; there is
   NO markup churn and NO drift (one source of truth for the primitives).
   Targets the canonical shared classes: .kpi-card/.kpi-top/.kpi-label/.kpi-val/
   .kpi-sub/.kpi-trend/.kpi-spark (from pms-sparkline.js), .tbl-wrap/table,
   .mobile-nav-btn, .empty-state, .section-hd. Uses each vertical's own theme vars
   (--card/--border/--text/--muted/--accent) so it inherits light/dark correctly.
   ============================================================================= */
.pms-shell-v2 { --shell-ease: cubic-bezier(0.16, 1, 0.3, 1); }

/* ---- Typography: tabular figures everywhere numbers live; calmer weights ---- */
.pms-shell-v2 .kpi-val,
.pms-shell-v2 .kpi-trend,
.pms-shell-v2 .kpi-sub,
.pms-shell-v2 table td,
.pms-shell-v2 table th,
.pms-shell-v2 .badge {
  font-feature-settings: 'tnum' 1, 'cv11' 1;
  font-variant-numeric: tabular-nums;
}
.pms-shell-v2 .section-hd h2 { font-weight: 600; letter-spacing: -0.02em; }

/* ---- KPI card system — the biggest "trillion-dollar" tell ------------------- */
.pms-shell-v2 .kpi-grid { gap: 16px; }
.pms-shell-v2 .kpi-card {
  padding: 16px 18px;
  border: 1px solid rgba(15, 23, 42, 0.06);
  border-radius: 12px;
  box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
  background: var(--card, #fff);
  transition: transform .18s var(--shell-ease), border-color .18s var(--shell-ease), box-shadow .18s var(--shell-ease);
}
.pms-shell-v2 .kpi-card:hover {
  transform: translateY(-1px);
  border-color: rgba(15, 23, 42, 0.12);
  box-shadow: 0 6px 18px rgba(15, 23, 42, 0.08);
}
.pms-shell-v2 .kpi-top { align-items: center; }
.pms-shell-v2 .kpi-label {
  font-size: 11px; font-weight: 600; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--muted); margin-bottom: 8px;
}
.pms-shell-v2 .kpi-val,
.pms-shell-v2 .kpi-value {
  font-size: 34px; font-weight: 600; line-height: 1.1;
  letter-spacing: -0.025em; color: var(--text);
}
/* Money values ("$1,570.00") are unbreakable tokens: a fixed 34px figure is
   wider than one track of the 2-column mobile KPI grid and pushes the whole
   page sideways. This selector outranks each vertical's own `.kpi-val` mobile
   rule (0,2,0 vs 0,1,0), so the cap has to live HERE to take effect. */
@media (max-width: 768px) {
  .pms-shell-v2 .kpi-val,
  .pms-shell-v2 .kpi-value { font-size: clamp(17px, 5vw, 26px); letter-spacing: -0.03em; }
}
.pms-shell-v2 .kpi-sub { font-size: 12.5px; color: var(--muted); margin-top: 4px; line-height: 1.4; }
/* Delta pill: green-100/green-700 up, red-100/red-700 down (was indigo-tinted). */
.pms-shell-v2 .kpi-trend { font-size: 11px; font-weight: 700; padding: 2px 8px; border-radius: 999px; }
.pms-shell-v2 .kpi-trend.up   { color: #047857; background: rgba(5, 150, 105, 0.12); }
.pms-shell-v2 .kpi-trend.down { color: #b91c1c; background: rgba(220, 38, 38, 0.10); }
.pms-shell-v2 .kpi-trend.flat { color: var(--muted); background: rgba(15, 23, 42, 0.06); }
.pms-shell-v2 .kpi-spark { height: 30px; margin-top: 12px; opacity: 0.85; }

/* ---- Tables: 44px rows, quiet uppercase headers, accent hover, tabular nums -- */
.pms-shell-v2 .tbl-wrap {
  border: 1px solid rgba(15, 23, 42, 0.06); border-radius: 12px;
  box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
}
.pms-shell-v2 table th {
  padding: 11px 14px; font-size: 11px; font-weight: 600; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--muted);
  background: var(--bg, #f7f8fa); border-bottom: 1px solid var(--border);
}
.pms-shell-v2 table td { padding: 0 14px; height: 44px; font-size: 13px; }
.pms-shell-v2 table tbody tr { transition: background .15s var(--shell-ease); }
.pms-shell-v2 table tbody tr:hover { background: color-mix(in srgb, var(--accent) 6%, transparent); }
/* opt-in numeric alignment for cells/headers that add class="num" */
.pms-shell-v2 td.num, .pms-shell-v2 th.num { text-align: right; }

/* ---- Tabs: active gets a 2px accent underline + weight bump ----------------- */
.pms-shell-v2 .mobile-nav-btn { position: relative; }
.pms-shell-v2 .mobile-nav-btn.on { font-weight: 700; }
.pms-shell-v2 .mobile-nav-btn.on::after {
  content: ''; position: absolute; left: 14px; right: 14px; bottom: 0;
  height: 2px; border-radius: 2px 2px 0 0; background: var(--accent);
}

/* ---- Empty states: larger illustration + calmer type (CTA is per-call) ------ */
.pms-shell-v2 .empty-state {
  padding: 48px 24px; border: 1px dashed var(--border); border-radius: 12px;
  color: var(--muted); font-size: 14px; line-height: 1.5;
}
.pms-shell-v2 .empty-state::before { width: 64px; height: 64px; margin-bottom: 16px; opacity: 0.5; }

/* ---- Motion: one easing, no bounce; honour reduced-motion ------------------- */
.pms-shell-v2 .btn, .pms-shell-v2 .ai-card, .pms-shell-v2 .badge {
  transition-timing-function: var(--shell-ease);
}
@media (prefers-reduced-motion: reduce) {
  .pms-shell-v2 .kpi-card, .pms-shell-v2 table tbody tr, .pms-shell-v2 .btn {
    transition-duration: 0.001ms !important;
  }
}

/* =============================================================================
   DS-1 / DS-2 — NEW TOKENS (shadow-glass, shadow-dark, border-inverse,
   space-section-tight). These extend the :root token layer above without
   touching any existing token so they are fully additive.
   ============================================================================= */
:root {
  /* -- Shadow tokens (DS-1) --------------------------------------------------
     shadow-glass: inset top-highlight + sm drop — for frosted / glass cards.
     shadow-dark:  inset top-highlight + strong elevated drop — for glass-on-dark
     cards (bapps section, ink-band). Named distinctly from the grey ramp so
     consumers can opt in to either surface without a specificity war. */
  --pms-shadow-glass: rgba(255,255,255,0.65) 0 1px 0 inset, var(--pms-shadow-sm);
  --pms-shadow-dark:  rgba(255,255,255,0.05) 0 1px 0 inset, 0 18px 44px rgba(0,0,0,0.34);

  /* -- Border token (DS-2) ---------------------------------------------------
     border-inverse: hairline for cards that sit on dark/ink surfaces. The
     standard --pms-border is near-black at 8% opacity — invisible on a dark bg.
     This variant is near-white at 12% — the minimum visible whisper on ink. */
  --pms-border-inverse: rgba(255, 255, 255, 0.12);

  /* -- Spacing token (DS-2) --------------------------------------------------
     space-section-tight: compact padding for FAQ, narrow-CTA, and dense
     sections that do not need the full 64-128px section rhythm. */
  --pms-space-section-tight: clamp(32px, 4vw, 48px);
}

/* =============================================================================
   DS-3 — THREE CANONICAL CARD PRIMITIVES
   -----------------------------------------------------------------------------
   These are the single source of truth for card appearance site-wide.
   Page agents MUST reference these class names verbatim; they must NOT
   duplicate the property values inline. Each primitive is ADDITIVE: existing
   class names (.svc-card, .bapps-card, etc.) are NOT removed — they stay live
   for parallel agents editing those pages. The three primitives can be co-applied
   with existing classes via multi-class syntax: class="svc-card pms-card".
   ============================================================================= */

/* ── Primitive 1: .pms-card — standard light-surface card ───────────────────
   Replaces the visual pattern of: .svc-card, .why-card, .ab-card, .step,
   .pr-combo-card, .pr-feature, .addon, .item, .tpl (light-mode variant).
   Use .pms-card--compact for dense/step-style grids (smaller pad, tighter radius).
   Use .pms-card--glass for frosted cards over gradient/mesh backgrounds.
   Use .pms-card--featured for the dark-inverted "hero" card in a mixed grid. */
.pms-card {
  background: var(--pms-surface-1);
  border: 1px solid var(--pms-border);
  border-radius: var(--pms-radius-xl);       /* 16px */
  padding: 24px;
  box-shadow: var(--pms-shadow-sm);
  transition: transform 180ms ease, box-shadow 180ms ease, border-color 180ms ease;
}
.pms-card:hover {
  transform: translateY(-4px);
  box-shadow: var(--pms-shadow-md);
  border-color: var(--pms-border-strong);
}
/* Compact variant — step cards, dense grids. Smaller pad, radius steps down one. */
.pms-card--compact {
  padding: 20px;
  border-radius: var(--pms-radius-lg);       /* 12px */
}
/* Glass variant — for cards over mesh/gradient backgrounds (why-card, tpl). */
.pms-card--glass {
  background: rgba(255,255,255,0.82);
  box-shadow: var(--pms-shadow-glass);
  border-color: var(--pms-border-glass);
}
/* Featured/inverted variant — dark card in a light grid (svc-card.feat pattern). */
.pms-card--featured {
  background: var(--pms-surface-ink);
  border-color: transparent;
  color: var(--pms-ink-inverse);
}

/* ── Primitive 2: .pms-card-dark — glass-on-dark for ink-band sections ───────
   Replaces the visual pattern of: .bapps-card, .bento-card (dark variants).
   Sits on --pms-surface-ink / .pms-band-ink backgrounds.
   Hover accent is a soft blue teal border — visible against the dark band
   without needing a background-color change (avoids the flash at paint). */
.pms-card-dark {
  background: rgba(255,255,255,0.04);
  border: 1px solid var(--pms-border-inverse);   /* rgba(255,255,255,0.12) */
  border-radius: var(--pms-radius-xl);            /* 16px — was 18px on .bapps-card */
  padding: 24px;
  box-shadow: var(--pms-shadow-dark);
  color: var(--pms-ink-inverse);
  transition: transform 180ms ease, border-color 180ms ease;
}
.pms-card-dark:hover {
  transform: translateY(-4px);
  border-color: rgba(59,106,160,0.45);
}

/* ── Primitive 3: .pms-plan-card — pricing and plan cards ────────────────────
   Replaces: .pr-combo-card (was 12px radius → now 16px), .ho-plan
   (was 34/30px pad → now 32px), .cm-card (was 34px pad → now 32px).
   No box-shadow by default — plan cards sit in quiet grids and let borders
   do the work; shadow-md comes in only on the featured card via --featured.
   The --featured modifier inverts to brand gradient for the "recommended" slot. */
.pms-plan-card {
  background: var(--pms-surface-1);
  border: 1px solid var(--pms-border);
  border-radius: var(--pms-radius-xl);       /* 16px */
  padding: 32px;
  box-shadow: none;
}
.pms-plan-card--featured {
  border-color: var(--pms-accent);
  background: var(--pms-gradient-brand);
  color: var(--pms-ink-inverse);
  box-shadow: var(--pms-shadow-md);
}

/* =============================================================================
   DS-4 — ORPHAN-PREVENTION GRID UTILITIES
   -----------------------------------------------------------------------------
   When a CSS grid with N columns has a number of items that does not divide
   evenly by N, the last row contains fewer items than N. Those items sit in the
   left-most columns with empty space to the right — the "orphan" pattern that
   makes grids look unfinished.

   .grid-orphan-last         — stretches the lone last item to full row width,
                               left-aligned (max-width clamp keeps card narrow).
   .grid-orphan-last--center — same stretch but centers the card (for symmetric
                               grids where centering matches the visual intent).

   CONSTRAINT: these utilities use `grid-column: 1 / -1` which does NOT introduce
   horizontal overflow — it only stretches within the existing grid context.
   The companion `max-width` on the item prevents the card from growing wider
   than a single column-width, preserving card proportions.

   Usage: add .grid-orphan-last to the grid container. The utility targets only
   the last child when it lands alone (nth-child(odd) = position 1 in a 2-col
   row, or any odd count in an N-col context when the remainder is 1). For grids
   where the last row holds 2 orphans in a 3-col grid, use justify-content:center
   on the grid directly (no utility needed — that's a CSS single-property fix). */
/* CORRECTION: `:nth-child(odd)` only identifies a lone trailing item in a
   TWO-column grid. In a 3-col grid it fires on any odd total, so 15 items
   (which divide evenly, no orphan) would be wrongly stretched, while 35 items
   (which leave TWO orphans) would get the single-orphan treatment. Because
   these grids use `auto-fit`, the column count also changes with viewport, so
   no nth-child rule is correct at every breakpoint.

   `.pms-grid-tidy` uses flexbox instead. Wrapping flex items with
   justify-content:center centre the short final row automatically, whatever the
   column count resolves to — no item-count or breakpoint assumptions. This is
   the same fix already proven on #why-us, where 5 cards in an auto-fit grid
   stranded the 5th on the left.

   Requires: items get a flex-basis approximating the old minmax() track, and
   min-width preserves the wrap points. Apply --tidy-min / --tidy-cols to tune.
   Do NOT use this on a grid containing spanning/featured items — flex has no
   equivalent of grid-column:span, so those grids keep display:grid. */
.pms-grid-tidy {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
}
.pms-grid-tidy > * {
  flex: 0 1 calc(100% / var(--tidy-cols, 3) - var(--tidy-gap, 24px));
  min-width: var(--tidy-min, 250px);
}

/* =============================================================================
   DS-5 — REDUCED-MOTION BLOCK FOR NEW PRIMITIVES
   -----------------------------------------------------------------------------
   WCAG 2.3.3 / prefers-reduced-motion: the three card primitives all carry
   CSS transitions for their hover states. Visitors who have set the OS-level
   "reduce motion" preference get no transitions and no translateY lift. The
   cards still respond to hover (border-color change is instant) so they remain
   interactive without triggering vestibular discomfort.

   This block is separate from the global `*, *::before` guard above so that
   future updates to card transitions stay co-located with the primitives. The
   global guard already covers all other animations. */
@media (prefers-reduced-motion: reduce) {
  .pms-card,
  .pms-card-dark,
  .pms-plan-card {
    transition: none;
  }
  .pms-card:hover,
  .pms-card-dark:hover {
    transform: none;
  }
}

/* ── L-091 · Visible keyboard focus ───────────────────────────────────────────
   Many pages reset `outline:none` on controls (inputs/buttons) and rely on a
   subtle border change, which fails WCAG 2.4.7 / 2.4.11. :focus-visible only
   fires for keyboard focus, so mouse/touch users see no change. :where() keeps
   specificity at zero for the selector list; !important is required to beat the
   page-level `outline:none` resets. */
:where(a[href], button, input, select, textarea, summary, [tabindex]:not([tabindex="-1"]), [role="button"]):focus-visible {
  outline: 2px solid var(--pms-focus-ring, #2f6fed) !important;
  outline-offset: 2px !important;
}
@media (forced-colors: active) {
  :where(a[href], button, input, select, textarea, summary, [tabindex]:not([tabindex="-1"]), [role="button"]):focus-visible {
    outline: 2px solid Highlight !important;
  }
}

/* =============================================================================
   SCROLL + LAYOUT SAFETY (2026-10 scroll audit; pairs with content/js/pms-scroll.js)
   Everything here is a no-op until the condition it guards actually happens.
   ============================================================================= */

/* A closed "More" sheet used to be display:grid (class rule beats the UA [hidden] rule) at opacity 0, so it
   silently swallowed taps and scroll drags over the bottom third of the page. */
.pms-more-sheet[hidden],
.pms-more-scrim[hidden] { display: none !important; }
.pms-more-sheet:not(.open) { pointer-events: none; }
.pms-more-scrim { touch-action: none; overscroll-behavior: contain; }
@media (max-width: 768px) {
  .pms-more-sheet {
    max-height: calc(100vh - 60px - 120px);
    max-height: calc(100dvh - 60px - 120px - env(safe-area-inset-bottom));
    overflow-y: auto; overscroll-behavior: contain;
  }
}

/* A grid track is `1fr` = minmax(auto, 1fr): a wide table inside a card made the track as wide as the table and
   pushed the whole panel past the viewport (reports tab, 375px). min-width:0 lets the card shrink so the
   `.tbl-wrap` scroller above takes the overflow instead of the page. */
.dash-grid > *,
.kpi-grid > *,
.settings-grid > *,
.detail-grid > *,
.dash-card { min-width: 0; }
/* Flex bar charts: `flex:1` columns keep their nowrap label as min-content, so 12 months of bars were wider than
   the card (budget tab, 768px). Let the columns shrink and clip the label instead. */
.bar-chart > *,
.bar-col { min-width: 0; }
.bar-col .bar-label { max-width: 100%; overflow: hidden; text-overflow: ellipsis; }
/* Fixed-size chart canvases (width="680" attributes) must not set the page width. */
.panel canvas,
.dash-card canvas,
.panel-inner canvas { max-width: 100%; height: auto; }

/* Dashboard icon sidebars (#app > .sidebar): 10-15 nav items are ~1000px tall, so on a short window (landscape phone,
   laptop at 768px) the last ones were cut off with no scroller to reach them. Own scroller, scrollbar hidden. */
#app > .sidebar {
  overflow-y: auto; overflow-x: hidden; min-height: 0; scrollbar-width: none; overscroll-behavior: contain;
}
#app > .sidebar::-webkit-scrollbar { display: none; }
#app > .sidebar > * { flex-shrink: 0; }

/* Document scroll: when html scrolls, body must not be a second (non-scrolling) scroller, otherwise
   position:sticky bars (topbar, tab strip) stick to the body and scroll away. pms-scroll.js sets the class. */
html.pms-one-scroller > body { overflow: visible !important; }

/* Modal scroll lock (see PMSScroll.lock): class based so it can never leave an inline overflow behind. */
html.pms-scroll-locked { overflow: hidden !important; padding-right: var(--pms-sbw, 0px); }
html.pms-scroll-locked > body { overflow: hidden !important; }
