:root {
  --background: 210 33% 98%;
  --foreground: 217 48% 20%;
  --card: 0 0% 100%;
  --card-foreground: 217 48% 20%;
  --muted: 210 20% 95%;
  --muted-foreground: 216 14% 30%;
  --primary: 217 48% 20%;
  --primary-foreground: 0 0% 100%;
  --accent: 207 55% 40%;
  --accent-foreground: 0 0% 100%;
  --secondary: 207 55% 40%;
  --secondary-foreground: 0 0% 100%;
  --border: 214 32% 91%;
  --input: 214 32% 91%;
  --destructive: 0 72% 51%;
  --success: 160 84% 26%;
  --warning: 37 92% 31%;
  --navy: 217 48% 20%;
  --blue: 207 55% 40%;
  --radius: 0.625rem;
}

.dark {
  /* Lifted from 8% → 14% so dark-mode product screenshots (which use
     their own ~6–9% dark surfaces) stand off the page rather than
     blending into it. Cards/muted/border re-tiered to keep visible
     elevation between surface layers. */
  --background: 220 18% 14%;
  --foreground: 210 33% 95%;
  --card: 220 16% 18%;
  --card-foreground: 210 33% 95%;
  --muted: 220 14% 22%;
  --muted-foreground: 216 14% 74%;
  --primary: 210 33% 95%;
  --primary-foreground: 220 20% 8%;
  /* Raised from 48% → 62% so accent-as-text clears 4.5:1 on dark card (#272b35: 5.25)
     and dark muted (#303540: 4.55). Accent-bg buttons use a darker hardcoded override below. */
  --accent: 207 55% 62%;
  --accent-foreground: 0 0% 100%;
  --secondary: 207 55% 62%;
  --secondary-foreground: 0 0% 100%;
  --border: 220 14% 28%;
  --input: 220 14% 28%;
  /* Lifted for dark-bg contrast: success/warning as text on dark cards must clear 4.5:1. */
  --success: 160 84% 50%;
  --warning: 37 92% 62%;
}

/* ── Dark-mode: accent-colored button backgrounds ──────────────────────────
   --accent is lifted to 62% for text-on-dark-surface legibility, but that
   makes it too light for white text (contrast < 4.5:1). Override the bg for
   any element that uses hsl(var(--accent)) as its background so buttons keep
   a dark-enough fill to pass 4.5:1 with white text (L=40% → 5.62:1). */
.dark .bg-\[hsl\(var\(--accent\)\)\] {
  background-color: hsl(207 55% 40%);
}

/* ── Dark-mode: solid-success-green backgrounds ─────────────────────────────
   Light-mode --success (#0b7a55 at L=26%) is fine; dark --success is lifted
   to L=50% for text legibility, but as a solid badge/button background it
   fails 4.5:1 with white text (#14eba3 on white = 1.56). Override to a darker
   teal (L=26% = #0b7a55) so white text hits 5.35:1. */
.dark .bg-\[hsl\(var\(--success\)\)\] {
  background-color: hsl(160 84% 26%);
}

* { box-sizing: border-box; }

/* <picture> as a layout-transparent wrapper.
   Both hero mechanisms (the rotating hero_photo macro and the fixed-pair
   partials/_theme_hero_image.jinja) wrap their <img> in a <picture> so the
   browser can pick ONE file from a prefers-color-scheme <source>. That
   wrapper must not participate in layout: an inline <picture> shrinks to
   content, which would break the `w-full h-full` inline use the fixed-pair
   partial documents. `display: contents` removes the box while leaving the
   resource-selection behavior — which the HTML image algorithm runs
   independently of CSS — completely intact. */
picture.as-hero-pic { display: contents; }

/* Theme-aware hero image swap.
   Stack two <img> elements absolutely on top of each other and toggle
   visibility based on the `.dark` class on <html>. Both stay in flow so the
   active one paints immediately on theme change with no reload flash.
   Use via partials/_theme_hero_image.jinja. */
.theme-hero-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: opacity 200ms ease;
}
.theme-hero-light { opacity: 1; }
.theme-hero-dark  { opacity: 0; }
.dark .theme-hero-light { opacity: 0; }
.dark .theme-hero-dark  { opacity: 1; }

/* Same theme-aware swap for inline product mocks (feature pages) */
.theme-mock-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top center;
  transition: opacity 200ms ease;
}
.theme-mock-light { opacity: 1; }
.theme-mock-dark  { opacity: 0; }
.dark .theme-mock-light { opacity: 0; }
.dark .theme-mock-dark  { opacity: 1; }

@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

body {
  font-family: 'Plus Jakarta Sans', system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
  background: hsl(var(--background));
  color: hsl(var(--foreground));
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  margin: 0;
}

.text-gradient {
  background: linear-gradient(135deg, hsl(var(--navy)) 0%, hsl(var(--blue)) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.dark .text-gradient {
  background: linear-gradient(135deg, hsl(210 33% 95%) 0%, hsl(207 55% 65%) 100%);
  -webkit-background-clip: text;
  background-clip: text;
}

.bg-gradient-accent {
  background: linear-gradient(135deg, hsl(var(--navy)) 0%, hsl(var(--blue)) 100%);
}

/* In dark mode the default gradient (navy → blue) blends into the dark
   page background, making CTAs look washed out. Lift both stops to keep
   the button visually punchy on a dark surface while staying on-brand. */
.dark .bg-gradient-accent {
  background: linear-gradient(135deg, hsl(214 60% 32%) 0%, hsl(199 70% 45%) 100%);
}

.bg-gradient-accent-light {
  background: linear-gradient(135deg, hsla(var(--navy) / 0.05) 0%, hsla(var(--blue) / 0.05) 100%);
}

.dark .bg-gradient-accent-light {
  background: linear-gradient(135deg, hsla(var(--blue) / 0.08) 0%, hsla(var(--navy) / 0.08) 100%);
}

/* Accessible focus ring */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid hsl(var(--accent));
  outline-offset: 2px;
  border-radius: 4px;
}

/* Skip link */
/* WCAG-standard visually-hidden pattern. The older left: -9999px trick
   could cause horizontal overflow on some mobile browsers and surface
   the link as a stray underlined "Skip to main content" line. */
.skip-link {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
.skip-link:focus-visible {
  position: fixed;
  top: 12px;
  left: 12px;
  width: auto;
  height: auto;
  padding: 8px 16px;
  background: hsl(var(--accent));
  color: white;
  border-radius: 4px;
  font-weight: 600;
  z-index: 9999;
}

/* Scrollbar */
::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}
::-webkit-scrollbar-track {
  background: hsl(var(--muted));
}
::-webkit-scrollbar-thumb {
  background: hsl(var(--muted-foreground) / 0.4);
  border-radius: 5px;
}
::-webkit-scrollbar-thumb:hover {
  background: hsl(var(--muted-foreground) / 0.6);
}

/* Accessible checkmark icon — meets WCAG 1.4.11 non-text contrast ≥3:1 on both
   light (--card: white) and dark card surfaces. #16a34a fails light cards;
   hsl(160 84% 29%) is the --success token and passes 3:1 on white. */
.check-icon { color: hsl(var(--success)); }
.dark .check-icon { color: hsl(160 70% 52%); }

/* Form error state — dark-mode-aware color for WCAG 1.4.3 (≥4.5:1) */
.form-error { color: hsl(var(--destructive)); }
.dark .form-error { color: hsl(0 72% 68%); }

/* Card helper */
.as-card {
  background: hsl(var(--card));
  color: hsl(var(--card-foreground));
  border: 1px solid hsl(var(--border));
  border-radius: var(--radius);
}

/* Course carousel — hide scrollbar on webkit + firefox */
.course-carousel { scrollbar-width: thin; }
.course-carousel::-webkit-scrollbar { height: 8px; }
.course-carousel::-webkit-scrollbar-thumb { background: hsl(var(--border)); border-radius: 9999px; }
.course-carousel::-webkit-scrollbar-track { background: transparent; }

/* Lock body scroll while the mobile menu is open. The class is toggled by
   main.js when the hamburger is tapped. */
html.mobile-menu-open,
body.mobile-menu-open {
  overflow: hidden;
  touch-action: none;
}

/* ── Accessible nav dropdowns (JS-driven disclosure) ─────────────────────────
   _header.jinja's four desktop dropdowns (Features, Products, per-item
   submenus, language picker) are `<div class="relative group">` wrapping a
   `<button aria-haspopup="true" aria-expanded aria-controls>` and a sibling
   `.dropdown-panel`. Pointer users keep the plain CSS reveal below
   (group-hover, untouched). Keyboard and click users are driven by
   main.js's wireDropdowns(), which toggles a `data-open="true"` attribute on
   the `.group` wrapper in lockstep with `aria-expanded` — this rule is what
   CSS honors for that state.

   Deliberately NOT keyed off `:focus-within`: an element with
   `visibility: hidden` cannot receive focus, Safari does not focus a
   <button> on click (so `:focus-within` never fires there at all), and a
   panel that reveals itself as a side-effect of focus merely arriving —
   rather than a deliberate click/Enter/Space/ArrowDown — leaves
   `aria-expanded` out of sync with what's actually on screen. JS owns both
   in lockstep instead. Specificity (0,3,0) beats the plain `.opacity-0` /
   `.invisible` utility classes (0,1,0) regardless of stylesheet order, so no
   `!important` is needed. */
.group[data-open="true"] > .dropdown-panel {
  opacity: 1;
  visibility: visible;
}

/* ── Responsive comparison table (partials/_comparison_table.jinja) ─────────
   <640px: stacked cards, one per column (plan/competitor) — the PRIMARY
   view, since most marketing-site traffic is mobile and a stacked card
   reads better on a phone than any horizontally-scrolled table.
   >=640px: the familiar table, wrapped for accessible horizontal scroll
   (role=region + aria-label + tabindex=0, WAI-ARIA APG "scrollable region")
   with a sticky first column and a visible scroll cue. All colors ride the
   existing hsl(var(--...)) custom properties, so both themes are handled by
   the same rules with no `.dark` overrides needed here. */

/* -- mobile stacked cards -- */
/* `sm:hidden` on the markup is NOT sufficient here and never was: style.css
   loads AFTER tailwind-compiled.css, and `.as-compare__cards{display:flex}`
   and `.sm\:hidden{display:none}` are both single-class selectors, so the
   later sheet wins and the cards stayed `display:flex` at every width. The
   result was BOTH renders on screen at once — the stacked cards and then the
   full table underneath them — on every comparison table in the fleet.
   The breakpoint therefore lives here, next to the rule it has to beat. */
.as-compare__cards {
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}
@media (min-width: 640px) {
  .as-compare__cards { display: none; }
}
.as-compare__card {
  background: hsl(var(--card));
  color: hsl(var(--card-foreground));
  border: 1px solid hsl(var(--border));
  border-radius: var(--radius);
  overflow: hidden;
}
.as-compare__card--highlight {
  border: 2px solid hsl(var(--accent));
}
.as-compare__card-head {
  padding: 0.875rem 1.125rem;
  font-weight: 700;
  font-size: 1rem;
  background: hsl(var(--muted) / 0.5);
  border-bottom: 1px solid hsl(var(--border));
}
.as-compare__card--highlight .as-compare__card-head {
  background: hsl(var(--accent) / 0.12);
  color: hsl(var(--accent));
}
.as-compare__card-body {
  margin: 0;
  padding: 0.25rem 1.125rem 0.5rem;
}
.as-compare__card-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.625rem 0;
  border-bottom: 1px solid hsl(var(--border) / 0.6);
  font-size: 0.875rem;
}
.as-compare__card-row:last-child { border-bottom: 0; }
.as-compare__card-row dt {
  margin: 0;
  color: hsl(var(--muted-foreground));
  font-weight: 500;
}
.as-compare__card-row dd {
  margin: 0;
  display: flex;
  align-items: center;
  gap: 0.375rem;
  font-weight: 600;
  color: hsl(var(--foreground));
  text-align: right;
}

/* -- desktop horizontal-scroll table -- */
.as-compare__scroll-wrap { position: relative; }
.as-compare__scroll {
  overflow-x: auto;
  border: 1px solid hsl(var(--border));
  border-radius: var(--radius);
  -webkit-overflow-scrolling: touch;
}
.as-compare__scroll:focus-visible {
  outline: 2px solid hsl(var(--accent));
  outline-offset: 2px;
}
.as-compare__table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font-size: 0.875rem;
}
/* Cells WRAP. `white-space: nowrap` here was what forced the horizontal
   scroll: a cell like "A separate purchase; often priced near a full CPA
   course" became one unbreakable line, so a 3-column table needed 979px
   inside an 830px container. Wrapping keeps every word and removes the
   scroll; `balance` stops a 2-line cell ending in one orphaned word. */
.as-compare__table th,
.as-compare__table td {
  padding: 0.875rem 1rem;
  text-align: center;
  white-space: normal;
  text-wrap: balance;
  border-bottom: 1px solid hsl(var(--border));
}
.as-compare__table tbody tr:last-child th,
.as-compare__table tbody tr:last-child td {
  border-bottom: 0;
}
.as-compare__table thead th {
  font-weight: 700;
  color: hsl(var(--foreground));
  background: hsl(var(--muted) / 0.5);
}
.as-compare__table tbody th {
  text-align: left;
  font-weight: 500;
  color: hsl(var(--muted-foreground));
  white-space: normal;
}
.as-compare__col--highlight {
  background: hsl(var(--accent) / 0.06);
  color: hsl(var(--accent));
  font-weight: 700;
}
.as-compare__table td.as-compare__col--highlight {
  color: hsl(var(--foreground));
}
/* Sticky first column: stays in place while the row scrolls under it. Needs
   its own opaque background (matching whichever row band it's in) so
   scrolled content doesn't show through underneath it. */
.as-compare__sticky-col {
  position: sticky;
  left: 0;
  z-index: 1;
  background: hsl(var(--card));
  box-shadow: 1px 0 0 hsl(var(--border));
  text-align: left;
}
.as-compare__table thead .as-compare__sticky-col {
  background: hsl(var(--muted));
  z-index: 2;
}
/* Accessible check/x glyphs: green check reuses --success (already tuned to
   pass 3:1 non-text contrast in both themes elsewhere in this file); the
   "not included" mark uses muted-foreground rather than red — this is an
   absence, not an error. */
.as-compare__icon { display: inline-block; height: 1rem; width: 1rem; vertical-align: -2px; }
.as-compare__icon--yes { color: hsl(var(--success)); }
.as-compare__icon--no { color: hsl(var(--muted-foreground)); }

/* Visible scroll cue: a fading edge + chevron hinting there's more table off
   to the right. Purely decorative (aria-hidden), absolutely positioned on
   the non-scrolling wrapper so it stays fixed at the visible right edge
   regardless of scroll position (a static affordance, not a
   scroll-position-aware one — deliberately simple, no JS dependency). */
/* Shown only when the table really does overflow. Until 2026-08-21 this was
   a static, always-on affordance ("deliberately simple, no JS dependency") —
   which was defensible while every cell was nowrap and the table therefore
   ALWAYS scrolled. Now that cells wrap it usually does not, and a permanent
   "there's more →" arrow pointing at nothing is worse than the JS. The
   toggle is in main.js; with JS off the cue simply never appears, which is
   the safe direction for a decorative hint. */
.as-compare__scroll-cue { display: none; }
.as-compare__scroll-wrap--scrollable .as-compare__scroll-cue {
  position: absolute;
  top: 1px;
  right: 1px;
  bottom: 1px;
  width: 2.75rem;
  pointer-events: none;
  border-radius: 0 var(--radius) var(--radius) 0;
  background: linear-gradient(to right, transparent, hsl(var(--card)) 65%);
  display: flex;
  align-items: center;
  justify-content: flex-end;
  padding-right: 0.5rem;
}
.as-compare__scroll-cue svg {
  width: 1.1rem;
  height: 1.1rem;
  color: hsl(var(--accent));
  opacity: 0.85;
}

/* ── Sticky mobile CTA bar (partials/_sticky_cta.jinja) ──────────────────────
   Fixed to the bottom of the viewport below `md` (768px) only — desktop
   already shows the persistent header CTA. Respects the iOS home-indicator
   safe area via env(safe-area-inset-bottom) so the button never sits under
   it. `.as-sticky-cta-spacer` is the matching bottom-padding helper pages
   must add to their last content block — see the partial's own doc comment
   for why (short pages would otherwise have the bar cover real content). */
.as-sticky-cta {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 40;
  background: hsl(var(--card));
  border-top: 1px solid hsl(var(--border));
  padding-bottom: env(safe-area-inset-bottom, 0px);
  box-shadow: 0 -4px 16px rgba(0, 0, 0, 0.08);
}
.dark .as-sticky-cta {
  box-shadow: 0 -4px 16px rgba(0, 0, 0, 0.35);
}
.as-sticky-cta__inner {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.75rem;
  max-width: 640px;
  margin: 0 auto;
  padding: 0.625rem 1rem;
}
.as-sticky-cta__secondary {
  font-size: 0.75rem;
  line-height: 1.3;
  color: #5A6577; /* Renkara Slate — light-mode muted text, AA on white */
  flex: 1 1 auto;
  min-width: 0;
}
.dark .as-sticky-cta__secondary {
  color: #94A3B8; /* slate-400 — dark-mode muted text, AA on dark surfaces */
}
.as-sticky-cta__button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px; /* WCAG 2.5.5 target size */
  padding: 0 1.25rem;
  border-radius: 9999px;
  font-size: 0.875rem;
  font-weight: 600;
  white-space: nowrap;
  flex-shrink: 0;
}
/* Bottom-padding helper for page content sharing the viewport with the bar.
   Collapses to zero at the same breakpoint the bar itself disappears at, so
   it is always safe to leave the class on regardless of viewport width. */
.as-sticky-cta-spacer {
  padding-bottom: calc(72px + env(safe-area-inset-bottom, 0px));
}
@media (min-width: 768px) {
  .as-sticky-cta-spacer { padding-bottom: 0; }
}

/* ── Legal document body (partials/_legal_privacy_body.jinja,
   _legal_terms_body.jinja) ──────────────────────────────────────────────────
   Both partials render long-form HTML (h2/h3/p/ul/ol/table) inside
   `.legal-body`, and the privacy body additionally wraps its data tables in
   `.table-wrapper` — neither class had a CSS definition anywhere in this
   overlay. Consequence: Tailwind's Preflight reset strips all default
   heading/list/paragraph/table styling, so every element rendered
   identically (same size, no spacing, no bullets, no borders), and the
   wide multi-column tables had no horizontal-scroll handling at all on
   narrow viewports. The template HTML itself is untouched here — these
   classes already existed on every element that needs them. */

/* -- prose typography -- */
.legal-body h2 {
  font-size: 1.5rem;
  font-weight: 800;
  color: hsl(var(--foreground));
  margin: 0 0 0.75rem;
  line-height: 1.3;
}
.legal-body h3 {
  font-size: 1.125rem;
  font-weight: 700;
  color: hsl(var(--foreground));
  margin: 1.5rem 0 0.5rem;
  line-height: 1.4;
}
.legal-body p { margin: 0 0 1rem; }
.legal-body ul,
.legal-body ol {
  margin: 0 0 1rem;
  padding-left: 1.5rem;
}
.legal-body ul { list-style: disc; }
.legal-body ol { list-style: decimal; }
.legal-body li { margin: 0 0 0.5rem; }
.legal-body li > ul,
.legal-body li > ol { margin: 0.5rem 0 0; }
.legal-body strong { color: hsl(var(--foreground)); font-weight: 700; }
.legal-body em { font-style: italic; }
.legal-body a {
  color: hsl(var(--accent));
  text-decoration: underline;
  text-underline-offset: 2px;
}
.legal-body a:hover { opacity: 0.85; }
.legal-body hr {
  border: 0;
  border-top: 1px solid hsl(var(--border));
  margin: 2rem 0;
}

/* -- data tables: accessible horizontal scroll (same technique as
   .as-compare__scroll — role=region + aria-label + tabindex=0 are set in
   the template on each .table-wrapper; this is the matching CSS half) -- */
.legal-body .table-wrapper {
  overflow-x: auto;
  margin: 0 0 1.5rem;
  border: 1px solid hsl(var(--border));
  border-radius: var(--radius);
  -webkit-overflow-scrolling: touch;
}
.legal-body .table-wrapper:focus-visible {
  outline: 2px solid hsl(var(--accent));
  outline-offset: 2px;
}
.legal-body table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font-size: 0.8125rem;
}
.legal-body th,
.legal-body td {
  padding: 0.75rem 1rem;
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid hsl(var(--border));
}
.legal-body tbody tr:last-child th,
.legal-body tbody tr:last-child td {
  border-bottom: 0;
}
.legal-body thead th {
  font-weight: 700;
  color: hsl(var(--foreground));
  background: hsl(var(--muted) / 0.5);
  white-space: nowrap;
}
.legal-body tbody td:first-child { white-space: nowrap; }

/* Horizontal pill / tab strips (FAQ section tabs, page jump navs).
   Centered when the pills fit, scrolling from the start when they do not.
   `safe center` is the load-bearing half: plain `center` in a scroll
   container pushes the leading pills into negative space where they cannot
   be scrolled to. The plain declaration above it is the fallback for
   browsers without `safe` (they get today's left alignment, not a broken
   strip). Owner directive 2026-08-21: FAQ tabs are centered. */
.as-strip-center { justify-content: center; justify-content: safe center; }

/* ── Header dropdown panels ──────────────────────────────────────────────────
   OPEN fades; CLOSE is instant.

   The panels used to carry Tailwind's `transition-all duration-150`, which
   includes `visibility` — and `visibility` is a discrete-animated property, so
   the switch to `hidden` is deferred to the END of the transition. A panel
   therefore stayed fully visible for 150ms after the cursor left it. Sweeping
   across the nav bar left two, then three, panels stacked on screen at once
   (measured: 2 panels visible across four consecutive 40ms samples). Only
   `opacity` is transitioned now, so leaving a menu hides it on the same frame.

   `:focus-within` is here because `group-hover` alone made these menus
   mouse-only — a keyboard user tabbing to the trigger could never open one.
   (The triggers' `aria-expanded` is still hard-coded "false"; making it
   truthful needs JS and is tracked separately.) */
.dropdown-panel { transition: opacity 150ms ease; }
.group:focus-within > .dropdown-panel { opacity: 1; visibility: visible; }
