: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-coloured 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; }

/* 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 -- */
.as-compare__cards {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}
.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;
}
.as-compare__table th,
.as-compare__table td {
  padding: 0.875rem 1.25rem;
  text-align: center;
  white-space: nowrap;
  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). */
.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; }
