/* =====================================================================
   TOKENS
   ===================================================================== */
:root {
  --navy-950: #0a0e1a;
  --navy-900: #0b1220;
  --navy-800: #131b2e;
  --ink: #0f172a;
  --muted: #64748b;
  --muted-light: #94a3b8;
  --border: #e2e8f0;
  --bg: #f8fafc;
  --bg-alt: #eef2f7;
  --card: #ffffff;

  --cyan: #17c3d9;
  --cyan-light: #e6fbfd;
  /* Same hue as --cyan, darkened for use as *text* on a light
     background (--bg/--bg-alt/--card) — --cyan itself measures only
     ~1.9:1 contrast there (WCAG AA needs 4.5:1 for normal text), fine
     for icons/borders/accents but not for text a user has to read.
     4.77:1 at this value. Added 2026-09-16 for the Hero readout line;
     reach for this instead of --cyan wherever cyan text sits on a
     light surface. */
  --cyan-700: #0e7490;
  --red: #ef4444;

  --radius-sm: 8px;
  --radius-md: 14px;
  --radius-lg: 20px;
  --shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.06);
  --shadow-md: 0 8px 24px rgba(15, 23, 42, 0.08);
  --shadow-lg: 0 20px 48px rgba(15, 23, 42, 0.12);

  --container: 1160px;
  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

/* =====================================================================
   RESET
   ===================================================================== */
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  margin: 0;
  font-family: var(--font);
  color: var(--ink);
  background: var(--bg);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}
h1, h2, h3, p, ul { margin: 0; }
ul { padding: 0; list-style: none; }
a { color: inherit; text-decoration: none; }
img { display: block; max-width: 100%; }
button { font: inherit; background: none; border: none; cursor: pointer; color: inherit; }
svg.icon { width: 20px; height: 20px; flex-shrink: 0; }
svg.icon--sm { width: 16px; height: 16px; }

.container {
  max-width: var(--container);
  margin: 0 auto;
  padding: 0 24px;
}
.container--narrow { max-width: 820px; }

.section { padding: 96px 0; }
.section--alt { background: var(--bg-alt); }
.section__title {
  font-size: clamp(28px, 3.4vw, 40px);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.2;
  max-width: 720px;
  text-wrap: balance;
}
.section__lead {
  margin-top: 14px;
  color: var(--muted);
  font-size: 17px;
  max-width: 620px;
}
.center { margin-left: auto; margin-right: auto; text-align: center; }

/* --cyan-700, not --cyan: this label sits on a light section background
   (--bg/--bg-alt) everywhere except #tools (see the override just below
   #tools's own rules) — --cyan measures ~2:1 there, under the 4.5:1 AA
   minimum for this 13px/700 text (too small to count as "large text").
   Same fix, same reasoning as the Hero readout line and card icons. */
.eyebrow {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--cyan-700);
  margin: 0 0 12px;
}

.check-list li {
  position: relative;
  padding-left: 26px;
  color: var(--ink);
  font-size: 15px;
  margin-bottom: 10px;
}
/* --cyan-700, not --cyan: this checkmark is a meaningful graphic (it's
   what actually says "included"), so it falls under WCAG 1.4.11's 3:1
   non-text contrast minimum, not just a decorative accent. On the
   light card backgrounds this class is normally used against (Personal/
   Team price cards, the Multi-monitor checklist), --cyan measures only
   ~2.1:1. The Pro card is the one place this class sits on a dark
   background instead — see the .price-card--featured override below,
   which restores the brighter --cyan there since it already clears 3:1
   by a wide margin (~8.8:1) and reads better against navy. */
.check-list li::before {
  content: "";
  position: absolute;
  left: 0; top: 4px;
  width: 16px; height: 16px;
  border-radius: 50%;
  background: var(--cyan-light);
  box-shadow: inset 0 0 0 1.5px var(--cyan-700);
}
.check-list li::after {
  content: "";
  position: absolute;
  left: 4.5px; top: 7.5px;
  width: 7px; height: 4px;
  border-left: 2px solid var(--cyan-700);
  border-bottom: 2px solid var(--cyan-700);
  transform: rotate(-45deg);
}
.check-list--spaced li { margin-bottom: 16px; font-size: 15.5px; }

/* Buttons -------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 12px 20px;
  border-radius: var(--radius-sm);
  font-weight: 600;
  font-size: 14.5px;
  white-space: nowrap;
  transition: transform .15s ease, box-shadow .15s ease, background .15s ease;
}
.btn:hover { transform: translateY(-1px); }
.btn--sm { padding: 9px 16px; font-size: 13.5px; }
.btn--lg { padding: 15px 26px; font-size: 15.5px; }
.btn--block { width: 100%; margin: 20px 0; }
.btn--dark { background: var(--navy-900); color: #fff; }
.btn--dark:hover { background: #000; }
/* Brand-cyan primary button, for CTAs on a dark background. Dark navy
   text, not white — white-on-cyan measures ~2.1:1 (fails WCAG AA),
   the same contrast issue already fixed on the Final CTA band. */
.btn--cyan { background: var(--cyan); color: var(--navy-950); }
.btn--cyan:hover { background: #14a6b8; box-shadow: 0 8px 20px rgba(23,195,217,.4); }
.btn--ghost-dark { background: transparent; color: #fff; border: 1px solid rgba(255,255,255,.25); }
/* Official Microsoft Store badge, self-hosted (no get.microsoft.com
   script, so nothing reaches Microsoft before a click). Badge rules:
   proportional resizing only, no recolor/shadow/hover effect, never
   under 40px tall. */
.store-badge { display: inline-flex; align-items: center; line-height: 0; }
.store-badge img { height: 56px; width: auto; }
.store-badge:focus-visible { outline: 2px solid currentColor; outline-offset: 4px; border-radius: 6px; }
.btn--ghost-dark:hover { background: rgba(255,255,255,.08); }
.btn--outline { background: #fff; color: var(--ink); border: 1px solid var(--border); }
.btn--outline:hover { border-color: var(--navy-900); box-shadow: var(--shadow-sm); }

.pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 7px 14px;
  border-radius: 999px;
  background: var(--cyan-light);
  color: #0e8a9a;
  font-size: 13px;
  font-weight: 600;
  margin-bottom: 22px;
}

/* Hero badge: an ambient shimmer sweep so a non-interactive pill above
   the headline still gets noticed passively (most visitors have no
   reason to hover a plain badge), plus a stronger glow for anyone who
   does hover it. Scoped to this one pill via .hero__badge, not .pill
   itself, so the pricing section's "Billed annually" pill is
   unaffected. */
.hero__badge {
  position: relative;
  overflow: hidden;
  border: 1px solid transparent;
  transition: box-shadow .25s ease, border-color .25s ease, transform .2s ease;
}
.hero__badge::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(120deg, transparent 30%, rgba(255,255,255,.75) 50%, transparent 70%);
  transform: translateX(-120%);
  pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
  .hero__badge::after { animation: pill-shimmer 3.2s ease-in-out infinite; }
}
@keyframes pill-shimmer {
  0% { transform: translateX(-120%); }
  35%, 100% { transform: translateX(120%); }
}
.hero__badge:hover {
  border-color: rgba(23,195,217,.5);
  box-shadow: 0 0 20px rgba(23,195,217,.5);
  transform: translateY(-1px);
}

.link-arrow {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-weight: 600;
  color: var(--cyan-700);
  margin-top: 8px;
}
.link-arrow span { transition: transform .15s ease; }
.link-arrow:hover span { transform: translateX(4px); }

.card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  padding: 20px 22px;
}
.card__label {
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--muted-light);
  margin-bottom: 12px;
}

/* =====================================================================
   MODULE: Nav bar
   ===================================================================== */
.nav {
  position: sticky;
  top: 0;
  z-index: 50;
  background: rgba(248, 250, 252, 0.85);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--border);
}
.nav__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 68px;
}
.nav__brand {
  display: flex;
  align-items: center;
  gap: 10px;
}
.nav__logo { width: 30px; height: 30px; border-radius: 8px; }
/* The wordmark alone — kept on its own class rather than styling
   .nav__brand directly, so this only ever touches the text, never the
   icon next to it (.nav__logo is an <img>, unaffected either way, but
   scoping it explicitly keeps that guarantee obvious). Sora replaces
   the plain system-font stack used everywhere else on the site,
   specifically for this wordmark — tighter tracking (-0.02em) and a
   heavier 700 weight is what makes it read as a designed logotype
   instead of bold body text next to an icon. */
.nav__wordmark {
  font-family: "Sora", var(--font);
  font-weight: 700;
  font-size: 19px;
  letter-spacing: -0.02em;
}
.nav__links {
  display: flex;
  align-items: center;
  gap: 32px;
  font-size: 14.5px;
  font-weight: 500;
  color: var(--muted);
}
.nav__links > a:hover { color: var(--ink); }
.nav__actions { display: flex; align-items: center; gap: 12px; }
.nav__toggle {
  display: none;
  flex-direction: column;
  gap: 4px;
  padding: 6px;
}
.nav__toggle span {
  width: 20px; height: 2px;
  background: var(--ink);
  border-radius: 2px;
}

/* Nav dropdowns (Account / Help) ---------------------------------- */
.nav__dropdown { position: relative; }
.nav__dropdown-trigger {
  display: flex;
  align-items: center;
  gap: 5px;
  font: inherit;
  color: var(--muted);
}
.nav__dropdown-trigger:hover { color: var(--ink); }
.nav__chevron { width: 14px; height: 14px; transition: transform .15s ease; }
.nav__dropdown-trigger[aria-expanded="true"] .nav__chevron { transform: rotate(180deg); }
.nav__dropdown-menu {
  position: absolute;
  top: calc(100% + 14px);
  left: 50%;
  transform: translateX(-50%);
  min-width: 200px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  padding: 8px;
  display: none;
  flex-direction: column;
  z-index: 60;
}
.nav__dropdown-menu.is-open { display: flex; }
.nav__dropdown-menu a {
  padding: 9px 12px;
  border-radius: var(--radius-sm);
  font-size: 14px;
  font-weight: 500;
  color: var(--ink);
}
.nav__dropdown-menu a:hover { background: var(--bg-alt); }

/* =====================================================================
   MODULE: Hero (photo-style — illustrated product scene, see
   PROJECT_STATE.md for why this isn't a real screenshot yet)
   ===================================================================== */
.hero {
  position: relative;
  min-height: 620px;
  display: flex;
  align-items: center;
  padding: 120px 0 100px;
  color: #fff;
}

.hero__bg { position: absolute; inset: 0; overflow: hidden; background: var(--navy-950); }

/* Base: a soft navy radial glow from the top-left corner, faded out
   toward the bottom-right by a mask (not a second gradient layer —
   matches the source component's own approach: one gradient for
   color, one mask for the fade shape, since they don't move together
   the same way a single gradient's own stops would). Recolored
   2026-09-16 from the source's literal generic charcoal/black
   (#2e2e2e/#000) to the site's own --navy-800/--navy-950 tokens — the
   user flagged the background didn't read as matched to the brand's
   cyan accent, and a generic gray base (lifted verbatim from the
   supplied snippet, not from this site's palette) was the real cause,
   not the cyan streaks themselves. Needs the -webkit-mask fallback for
   older Safari; the source React component didn't include one,
   presumably targeting a Chromium preview only. */
.hero__bg-base {
  position: absolute; inset: 0;
  background: radial-gradient(100% 100% at 0% 0%, var(--navy-800) 0%, var(--navy-950) 100%);
  -webkit-mask: radial-gradient(125% 100% at 0% 0%, #000 0%, rgba(0,0,0,.224) 88.2883%, rgba(0,0,0,0) 100%);
          mask: radial-gradient(125% 100% at 0% 0%, #000 0%, rgba(0,0,0,.224) 88.2883%, rgba(0,0,0,0) 100%);
}

/* Five skewed, fading brand-cyan streaks — each is the same base
   gradient (cyan fading to transparent top-to-bottom), with a
   different horizontal mask so each streak occupies its own band once
   skewed 45°. Numbers copied as-is from the supplied component; only
   the color was swapped to var(--cyan). */
.hero__streaks { position: absolute; inset: 0; }
.hero__streak {
  position: absolute; inset: 0;
  opacity: .2;
  background: linear-gradient(var(--cyan) 0%, rgba(23,195,217,0) 100%);
  transform: skewX(45deg);
}
.hero__streak--1 {
  -webkit-mask: linear-gradient(90deg, rgba(0,0,0,0) 0%, rgb(0,0,0) 20%, rgba(0,0,0,0) 36%, rgb(0,0,0) 55%, rgba(0,0,0,.13) 67%, rgb(0,0,0) 78%, rgba(0,0,0,0) 97%);
          mask: linear-gradient(90deg, rgba(0,0,0,0) 0%, rgb(0,0,0) 20%, rgba(0,0,0,0) 36%, rgb(0,0,0) 55%, rgba(0,0,0,.13) 67%, rgb(0,0,0) 78%, rgba(0,0,0,0) 97%);
}
.hero__streak--2 {
  -webkit-mask: linear-gradient(90deg, rgba(0,0,0,0) 11%, rgb(0,0,0) 25%, rgba(0,0,0,.55) 41%, rgba(0,0,0,.13) 67%, rgb(0,0,0) 78%, rgba(0,0,0,0) 97%);
          mask: linear-gradient(90deg, rgba(0,0,0,0) 11%, rgb(0,0,0) 25%, rgba(0,0,0,.55) 41%, rgba(0,0,0,.13) 67%, rgb(0,0,0) 78%, rgba(0,0,0,0) 97%);
}
.hero__streak--3 {
  -webkit-mask: linear-gradient(90deg, rgba(0,0,0,0) 9%, rgb(0,0,0) 20%, rgba(0,0,0,.55) 28%, rgba(0,0,0,.424) 40%, rgb(0,0,0) 48%, rgba(0,0,0,.267) 54%, rgba(0,0,0,.13) 78%, rgb(0,0,0) 88%, rgba(0,0,0,0) 97%);
          mask: linear-gradient(90deg, rgba(0,0,0,0) 9%, rgb(0,0,0) 20%, rgba(0,0,0,.55) 28%, rgba(0,0,0,.424) 40%, rgb(0,0,0) 48%, rgba(0,0,0,.267) 54%, rgba(0,0,0,.13) 78%, rgb(0,0,0) 88%, rgba(0,0,0,0) 97%);
}
.hero__streak--4 {
  -webkit-mask: linear-gradient(90deg, rgba(0,0,0,0) 0%, rgb(0,0,0) 17%, rgba(0,0,0,.55) 26%, rgb(0,0,0) 35%, rgba(0,0,0,0) 47%, rgba(0,0,0,.13) 69%, rgb(0,0,0) 79%, rgba(0,0,0,0) 97%);
          mask: linear-gradient(90deg, rgba(0,0,0,0) 0%, rgb(0,0,0) 17%, rgba(0,0,0,.55) 26%, rgb(0,0,0) 35%, rgba(0,0,0,0) 47%, rgba(0,0,0,.13) 69%, rgb(0,0,0) 79%, rgba(0,0,0,0) 97%);
}
.hero__streak--5 {
  -webkit-mask: linear-gradient(90deg, rgba(0,0,0,0) 0%, rgb(0,0,0) 20%, rgba(0,0,0,.55) 27%, rgb(0,0,0) 42%, rgba(0,0,0,0) 48%, rgba(0,0,0,.13) 67%, rgb(0,0,0) 74%, rgb(0,0,0) 82%, rgba(0,0,0,.47) 88%, rgba(0,0,0,0) 97%);
          mask: linear-gradient(90deg, rgba(0,0,0,0) 0%, rgb(0,0,0) 20%, rgba(0,0,0,.55) 27%, rgb(0,0,0) 42%, rgba(0,0,0,0) 48%, rgba(0,0,0,.13) 67%, rgb(0,0,0) 74%, rgb(0,0,0) 82%, rgba(0,0,0,.47) 88%, rgba(0,0,0,0) 97%);
}

/* Subtle dot-grid texture (kept from the source component as-is) — the
   source also had a 5%-opacity repeating noise-photo texture from a
   third-party CDN (cdn.21st.dev); deliberately dropped rather than
   copied, since a marketing homepage shouldn't carry a hard dependency
   on a random external host for a barely-visible grain effect — see
   PROJECT_STATE.md.
   Radial fade mask added 2026-09-16, translated from the separately
   supplied `DotPattern` component's own
   `[mask-image:radial-gradient(400px_circle_at_center,white,transparent)]`
   — a fixed 400px circle reads fine in that component's isolated demo
   box but would leave the dots visible only in a small disc floating
   in the middle of this much wider hero, so sized as a percentage
   ellipse instead and anchored to match `.hero__bg-base`'s own
   top-left origin, so the two masks fade in the same direction rather
   than fighting each other. */
.hero__dots {
  position: absolute; inset: 0;
  opacity: .2;
  background-image: radial-gradient(circle at 1px 1px, rgba(255,255,255,.5) 1px, transparent 0);
  background-size: 20px 20px;
  -webkit-mask: radial-gradient(85% 85% at 15% 15%, #000 0%, transparent 75%);
          mask: radial-gradient(85% 85% at 15% 15%, #000 0%, transparent 75%);
}

/* Soft center-top glow — plain-CSS equivalent of the source's
   `bg-gradient-radial from-slate-800/20 via-transparent to-transparent`
   (a Tailwind radial-gradient utility, not a default one — approximated
   directly rather than pulled in as a dependency). */
.hero__glow {
  position: absolute; inset: 0;
  background: radial-gradient(60% 60% at 50% 0%, rgba(30,41,59,.35), transparent 70%);
}

.hero__scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(6,9,16,.15), rgba(6,9,16,.35));
}

/* The hero's actual content lives inside the same 1160px container the
   nav uses, in two columns — so the copy lines up with the logo on the
   left and the visual lines up with the Download button on the right.
   (Previously the text sat in its own narrower max-width re-centered
   in the viewport, and the illustration was a full-bleed background
   with no container at all — neither aligned with the nav.) */
.hero__grid {
  position: relative;
  z-index: 2;
  display: grid;
  /* Was 1.15fr/0.85fr favoring copy when the right side was decorative
     art; 1fr/1.05fr once it held a real information-bearing
     screenshot. Widened again 2026-09-15 (v2) — the abstract motif
     that replaced the screenshot read as too small/sparse in that
     narrower column. */
  grid-template-columns: 0.92fr 1.18fr;
  gap: 48px;
  align-items: center;
}

.hero__title {
  font-size: clamp(34px, 4.6vw, 54px);
  font-weight: 800;
  letter-spacing: -0.03em;
  line-height: 1.12;
  color: #fff;
  text-wrap: balance;
}
.hero__title .accent { color: var(--cyan); }
.hero__lead {
  margin-top: 18px;
  font-size: 18px;
  color: rgba(255,255,255,.75);
  max-width: 480px;
}
.hero__ctas { display: flex; gap: 14px; margin-top: 30px; flex-wrap: wrap; container-type: inline-size; }
/* When the two buttons can't sit side by side (narrow hero column, or
   the longer German labels), stack them at equal full width instead of
   letting the second one wrap at its own width. 460px is the widest
   pair (German) plus the gap. */
@container (max-width: 460px) {
  .hero__ctas .btn { flex: 1 1 100%; justify-content: center; }
  /* The Store badge keeps its own proportions; centre it between the
     full-width buttons rather than stretching it. */
  .hero__ctas .store-badge { margin-inline: auto; }
}
.hero__trust {
  display: flex;
  gap: 22px;
  margin-top: 26px;
  font-size: 13.5px;
  color: rgba(255,255,255,.65);
  flex-wrap: wrap;
}
.hero__trust li { display: flex; align-items: center; gap: 7px; }
.hero__trust svg { color: var(--cyan); }

.hero__visual { position: relative; }

/* Generic content skeleton for the Hero's window-mockup — deliberately
   not a real app (no logo, no real text), so it reads as "any window"
   rather than implying a specific product. See index.html's comment on
   this slot for why it replaced a real screenshot 2026-09-15. Sized up
   2026-09-15 (v2) — the first pass read as too sparse/small inside the
   frame. */
.window-mockup__body--motif { position: relative; padding: 32px 30px 36px; }
.content-skeleton__topbar { display: flex; align-items: center; gap: 12px; margin-bottom: 22px; }
.content-skeleton__dot { width: 12px; height: 12px; border-radius: 50%; background: var(--cyan); flex: none; }
.content-skeleton__bar { display: block; height: 10px; border-radius: 5px; background: var(--bg-alt); }
.content-skeleton__heading { height: 26px; width: 62%; border-radius: 7px; background: var(--border); margin-bottom: 20px; }
.content-skeleton__lines { display: flex; flex-direction: column; gap: 11px; margin-bottom: 24px; }
.content-skeleton__block {
  position: relative;
  height: 88px;
  border-radius: 12px;
  background: var(--bg-alt);
  border: 1px dashed var(--border);
  display: flex;
  align-items: center;
  padding: 0 16px;
  overflow: hidden;
}

/* A small "technical readout" line inside the block — flavor text, not
   a literal spec sheet: purely to give the abstract motif a bit of
   personality rather than an empty gray box. Describes the real
   mechanism (a hardware-composited layered window — see
   app/system/win32_utils.py's WS_EX_LAYERED usage in the app repo),
   worded generically rather than naming a specific Windows API
   (DirectComposition) Penmarka doesn't actually call — see
   PROJECT_STATE.md for why. Reuses the ui-monospace stack already
   established elsewhere on this site (.usecase-card code, keyboard
   shortcuts) rather than loading a new font. */
.tech-readout {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12.5px;
  letter-spacing: .02em;
  /* --cyan itself (the fill used everywhere else this token appears,
     all on dark surfaces) measures ~1.9:1 here against the block's
     light --bg-alt background — user flagged it as hard to read.
     --cyan-700 keeps the same hue at 4.77:1; the glow keeps using the
     brighter --cyan as a halo (a text-shadow blur, not the readable
     fill), so it still reads as "glowing cyan" rather than losing the
     effect. */
  color: var(--cyan-700);
  white-space: nowrap;
  text-shadow: 0 0 6px rgba(23,195,217,.55), 0 0 14px rgba(23,195,217,.25);
}
.tech-readout::before { content: "› "; opacity: .7; }
.tech-readout__cursor { display: inline-block; margin-left: 1px; }
@media (prefers-reduced-motion: no-preference) {
  .tech-readout { animation: tech-glow 2.6s ease-in-out infinite; }
  .tech-readout__cursor { animation: tech-cursor 1s steps(1) infinite; }
}
@keyframes tech-glow {
  0%, 100% { text-shadow: 0 0 6px rgba(23,195,217,.55), 0 0 14px rgba(23,195,217,.25); }
  50% { text-shadow: 0 0 9px rgba(23,195,217,.85), 0 0 20px rgba(23,195,217,.45); }
}
@keyframes tech-cursor { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }

/* Cyan "pen stroke" overlays — a marker-style annotation drawn over the
   skeleton content on scroll-into-view, standing in for the real
   drawing motion without exposing the real toolbar. Hooks into the
   *existing* reveal-init/is-visible classes js/main.js already puts on
   [data-reveal] elements (.hero__visual and .split__art both have
   data-reveal) — no new JS. --len is each shape's own approximate real
   path length (see index.html's comment on why not pathLength="…").
   top/left/width/height are all fixed px now, deliberately, and each
   SVG's viewBox is set to exactly match its own width×height 1:1 — two
   real bugs from 2026-09-15 v1 this fixes: (1) height:auto on an SVG
   derives rendered height from width × the viewBox's own aspect ratio,
   so the circle grew far taller than intended as soon as the frame got
   wider than assumed; (2) a percentage width meant the traveling
   .pen-tip dot below (which shares these same coordinates for its
   motion path) would only trace the visible stroke correctly at one
   specific frame width. Fixed 1:1 px coordinates make the dot's path
   and the stroke's own shape provably the same shape, always. */
.pen-stroke {
  position: absolute;
  fill: none;
  stroke: var(--cyan);
  stroke-width: 4;
  stroke-linecap: round;
  filter: drop-shadow(0 1px 3px rgba(23,195,217,.4));
}
.pen-stroke, .pen-stroke__seg {
  stroke-dasharray: var(--len, 1000);
  stroke-dashoffset: 0; /* default: fully drawn — safe for no-JS and prefers-reduced-motion, same "never stuck invisible" contract as the rest of the reveal system */
}
.hero__visual.reveal-init .pen-stroke,
.split__art.reveal-init .pen-stroke,
.split__art.reveal-init .pen-stroke__seg {
  stroke-dashoffset: var(--len, 1000);
  transition: stroke-dashoffset 1.1s ease;
}
.hero__visual.reveal-init .pen-stroke--underline { transition-delay: .35s; }
.split__art.reveal-init .pen-stroke__seg--b { transition-delay: .35s; }
.split__art.reveal-init .pen-stroke__seg--c { transition-delay: .7s; }
.hero__visual.is-visible .pen-stroke,
.split__art.is-visible .pen-stroke,
.split__art.is-visible .pen-stroke__seg {
  stroke-dashoffset: 0;
}
/* Subtle ambient glow pulse once a stroke has finished drawing — same
   "gate infinite motion behind prefers-reduced-motion" pattern as the
   hero badge's own shimmer above, not a new convention. */
@media (prefers-reduced-motion: no-preference) {
  .hero__visual.is-visible .pen-stroke,
  .split__art.is-visible .pen-stroke__seg {
    animation: pen-glow-pulse 2.8s ease-in-out .4s infinite;
  }
}
@keyframes pen-glow-pulse {
  0%, 100% { filter: drop-shadow(0 1px 3px rgba(23,195,217,.4)); }
  50% { filter: drop-shadow(0 2px 7px rgba(23,195,217,.75)); }
}

/* A small traveling "pen tip" dot per Hero stroke, animating along that
   stroke's own path while it draws, then fading — offset-path is
   well-supported (Chrome/Edge/Firefox/Safari 16+); no fallback needed
   since it's a pure embellishment on top of a stroke animation that
   already works without it, gated behind the same reduced-motion query
   as every other looping/decorative motion on this page. Hero-only for
   now, not added to the Multi-monitor bridge (3 segments would need 3
   dots with more path math) — a reasonable fast-follow once this is
   confirmed to look right live, not scoped in this pass. */
.pen-tip {
  position: absolute;
  top: 0; left: 0;
  width: 9px; height: 9px;
  margin: -4.5px 0 0 -4.5px; /* offset-anchor centers the dot on its own path point; this centers the dot itself on that anchor */
  border-radius: 50%;
  background: var(--cyan);
  box-shadow: 0 0 8px 2px rgba(23,195,217,.7);
  opacity: 0;
  offset-anchor: 50% 50%;
  offset-rotate: 0deg;
}
@media (prefers-reduced-motion: no-preference) {
  .hero__visual.is-visible .pen-tip { animation: pen-tip-travel 1.1s ease forwards; }
  .hero__visual.is-visible .pen-tip--underline { animation-delay: .35s; }
}
@keyframes pen-tip-travel {
  0% { opacity: 1; offset-distance: 0%; }
  85% { opacity: 1; }
  100% { opacity: 0; offset-distance: 100%; }
}

/* Circle brackets the topbar+heading only (ends right where the first
   paragraph line starts); underline sits directly under that first
   line. Both the stroke SVG's viewBox and the matching .pen-tip's
   offset-path use these exact same numbers (the tip's path is in the
   shared container's coordinate space, so it's the SVG's own top/left
   added to its internal ellipse/path coordinates) — see the .pen-stroke
   comment above for why that match matters. */
.pen-stroke--circle { top: 48px; left: 22px; width: 260px; height: 64px; }
.pen-stroke--underline { top: 118px; left: 26px; width: 220px; height: 18px; }
.pen-tip--circle { offset-path: path("M152 54 A122 26 0 1 1 151.9 54"); }
.pen-tip--underline { offset-path: path("M30 129 Q136 136 242 124"); }

/* =====================================================================
   MODULE: Presenter Toolkit
   ===================================================================== */
.feature-grid {
  margin-top: 48px;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 20px;
}
.feature-card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 26px 22px;
  /* opacity is here purely for the scroll-reveal effect (see
     js/main.js's initScrollReveal) — since `transition` is a shorthand,
     a separate rule declaring it for .reveal-init would just get
     clobbered by this more specific one, silently killing the fade. */
  transition: opacity .15s ease, box-shadow .15s ease, transform .15s ease;
}
.feature-card:hover { box-shadow: var(--shadow-md); transform: translateY(-2px); }
/* Unified to one brand-cyan chip 2026-09-16 — was 5 colors
   (cyan/blue/red/slate/purple) spread across the 8 cards with no
   actual grouping logic behind the assignment (e.g. cyan on both
   Spotlight and Magnifier, blue on both Timer and Annotation Shapes),
   which read as decorative confetti rather than a system. One
   consistent tint reads as deliberate instead. Icon color is
   --cyan-700, not --cyan: --cyan itself on --cyan-light measures
   ~1.99:1, under WCAG's 3:1 minimum for meaningful graphics (SC
   1.4.11) — same class of issue as the Hero readout line fixed
   earlier today. --cyan-700 measures 5:1 here. */
.feature-card__icon {
  width: 42px; height: 42px;
  border-radius: 12px;
  display: flex; align-items: center; justify-content: center;
  margin-bottom: 16px;
  background: var(--cyan-light);
  color: var(--cyan-700);
}
.feature-card h3 { font-size: 16.5px; font-weight: 700; margin-bottom: 8px; }
.feature-card p { font-size: 14px; color: var(--muted); }

/* =====================================================================
   MODULE: Multi-monitor
   ===================================================================== */
.split {
  display: grid;
  /* Was 1fr/1fr, then 0.8fr/1.2fr (2026-09-15 v2) — widened again the
     same day (v3, "bigger and cleaner") since the checklist copy still
     had noticeably more headroom than the frames were using. */
  grid-template-columns: 0.68fr 1.32fr;
  gap: 48px;
  align-items: center;
}
.split__art { display: flex; justify-content: center; }
/* Real product screenshot (see PROJECT_STATE.md) presented in a
   minimal window-chrome frame — gives it breathing room from the
   card's own edges and reads as "a real window on a real desktop"
   rather than a raw cropped image. Windows-style controls (not the
   more common macOS traffic-light dots), since Penmarka is
   Windows-only — no invented window title, just the chrome. */
.window-mockup {
  width: 100%;
  /* Was 540px — widened 2026-09-15 (v2), the abstract motif that
     replaced the real screenshot read as too small for the frame at
     the old cap. */
  max-width: 620px;
  background: #fff;
  border-radius: var(--radius-lg);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}
.window-mockup__bar {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 16px;
  padding: 12px 18px;
  background: var(--bg-alt);
  border-bottom: 1px solid var(--border);
}
.window-mockup__control { width: 13px; height: 13px; color: var(--muted); }
.window-mockup__body { padding: 18px; }

/* Two frames side by side instead of one real screenshot — see
   index.html's comment on this slot for why (replaced 2026-09-15).
   Also a genuinely better fit for "dual, triple & ultrawide displays"
   than a single window ever was: it can show a stroke crossing a
   monitor boundary. Widened 2026-09-15 (v2, then v3 — "bigger and
   cleaner": the real lever both times was .split's column ratio above,
   not this max-width, which the narrower columns never let the frames
   reach anyway). v3 also dropped the borders on the plain cells (kept
   only on the marked/highlighted one) for a flatter, less
   wireframe-y look, and bumped the corner radii to match the bigger
   cells. */
.dual-monitor { position: relative; display: flex; gap: 28px; width: 100%; max-width: 880px; }
.window-mockup--sm { max-width: none; flex: 1 1 0; }
.window-mockup__body--grid { padding: 28px; }
.content-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
.content-grid__cell { height: 78px; border-radius: 12px; background: var(--bg-alt); }
.content-grid__cell--marked { background: var(--cyan-light); box-shadow: inset 0 0 0 1.5px var(--cyan); }
.pen-stroke--bridge { position: absolute; inset: 0; width: 100%; height: 100%; }
/* Phones: keep the two frames side by side (scaled down) instead of
   stacking them, so the stroke can still cross from one to the other —
   that crossing is the whole point of this illustration. Until
   2026-10-02 this breakpoint stacked the frames and hid the stroke,
   leaving two empty windows on mobile. Sizes keep the pair's aspect
   ratio close to desktop's, so the stretched stroke SVG
   (preserveAspectRatio="none") stays round rather than squashed. */
@media (max-width: 720px) {
  .dual-monitor { gap: 14px; }
  .window-mockup--sm { border-radius: var(--radius-md); }
  .window-mockup--sm .window-mockup__bar { gap: 8px; padding: 7px 10px; }
  .window-mockup--sm .window-mockup__control { width: 9px; height: 9px; }
  .window-mockup__body--grid { padding: 12px; }
  .content-grid { gap: 8px; }
  .content-grid__cell { height: 36px; border-radius: 7px; }
  /* The SVG scales down ~2x here, so a thicker stroke in viewBox units
     keeps the drawn line about as bold as the hero's. */
  .pen-stroke--bridge { stroke-width: 6; }
}

/* =====================================================================
   MODULE: Recorder
   ===================================================================== */
/* Mirror of the multi-monitor split (art left, copy right) so the two
   neighbouring split sections don't read as the same layout twice.
   Desktop only: the DOM keeps copy first, so the stacked mobile layout
   stays content-first like every other section (see the 960px rules). */
@media (min-width: 961px) {
  .split--reverse { grid-template-columns: 1.1fr 0.9fr; }
  .split--reverse .split__art { order: -1; }
}
.split__copy code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; background: var(--bg-alt); color: var(--ink); padding: 1px 5px; border-radius: 4px; font-size: 13px; }
.split__copy .section__lead + .check-list { margin-top: 24px; }
.split__fine { margin-top: 18px; font-size: 13.5px; color: var(--muted); }

.rec-mockup .window-mockup__body--motif { padding-bottom: 84px; }
.rec-mockup__track { gap: 14px; }
.rec-mockup__mic {
  flex: none;
  display: grid;
  place-items: center;
  width: 34px; height: 34px;
  border-radius: 50%;
  background: var(--cyan-light);
  color: var(--cyan-700);
}
.rec-mockup__levels { flex: 1; display: flex; align-items: center; gap: 5px; height: 48px; }
.rec-mockup__levels i {
  display: block;
  flex: 1;
  min-height: 6px;
  height: calc(var(--h) * 100%);
  border-radius: 3px;
  background: var(--cyan);
  opacity: .8;
  transform-origin: center;
}
@media (prefers-reduced-motion: no-preference) {
  .rec-mockup__levels i { animation: rec-level 1.4s ease-in-out infinite alternate; }
  .rec-mockup__levels i:nth-child(3n)   { animation-delay: -.5s; }
  .rec-mockup__levels i:nth-child(3n+1) { animation-delay: -.9s; }
  .rec-mockup__levels i:nth-child(4n)   { animation-duration: 1.1s; }
  .rec-hud__dot { animation: rec-blink 1.6s ease-in-out infinite; }
}
@keyframes rec-level { from { transform: scaleY(1); } to { transform: scaleY(.6); } }
@keyframes rec-blink { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }

/* Stand-in for the app's real recording HUD: red dot, elapsed time,
   Pause and Stop. */
.rec-hud {
  position: absolute;
  right: 24px;
  bottom: 22px;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px 8px 14px;
  border-radius: 999px;
  background: var(--navy-900);
  color: #fff;
  box-shadow: var(--shadow-md);
  font-size: 13px;
}
.rec-hud__dot { width: 10px; height: 10px; border-radius: 50%; background: var(--red); }
.rec-hud__time { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: .03em; margin-right: 4px; }
.rec-hud__btn { padding: 4px 11px; border-radius: 999px; background: rgba(255,255,255,.12); font-size: 12.5px; }

/* =====================================================================
   MODULE: Use cases
   ===================================================================== */
.usecase-grid {
  margin-top: 48px;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 22px;
}
.usecase-grid--4 { grid-template-columns: repeat(4, 1fr); }
.usecase-card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 26px;
  box-shadow: var(--shadow-sm);
}
/* Sized/radiused to match .feature-card__icon (Presenter Toolkit, just
   above this section) rather than its own slightly-off 40px/10px —
   one consistent icon-chip size reads as more deliberate once both
   sections also share the same cyan color (see below). */
.usecase-card__icon {
  width: 42px; height: 42px;
  border-radius: 12px;
  display: flex; align-items: center; justify-content: center;
  margin-bottom: 18px;
  background: var(--cyan-light);
  color: var(--cyan-700);
}
.usecase-card p { font-size: 14px; color: var(--muted); }
.usecase-card code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; background: var(--bg-alt); color: var(--ink); padding: 1px 5px; border-radius: 4px; font-size: 12.5px; }

/* Hover, added 2026-09-16 — the base .usecase-card had no :hover at
   all. Scoped to #use-cases specifically rather than the shared base
   .usecase-card class: that class is reused by #tools's dark Tool
   deep-dive cards below, which have their own deliberately
   transform-free hover (see that rule's comment — a tilt/rotate hover
   was tried there and explicitly rejected as "should not be bending").
   Scoping by ID keeps the two sections' hover behavior fully
   independent, so a lift here can never leak into a section that
   explicitly opted out of movement. Lift-and-shadow, no rotation —
   matches .feature-card's already-established, already-accepted hover
   just above this section. transition includes opacity/transform (not
   just box-shadow) for the same shorthand-clobbering reason documented
   on .feature-card: a partial transition here would silently fight the
   shared .reveal-init scroll-reveal transition on the same property. */
.usecase-card h3 { font-size: 17px; font-weight: 700; margin-bottom: 8px; }
#use-cases .usecase-card {
  transition: opacity .15s ease, box-shadow .15s ease, transform .15s ease;
}
#use-cases .usecase-card:hover {
  box-shadow: var(--shadow-md);
  transform: translateY(-2px);
}

/* Small category tag above a usecase-card's heading — used by the Tool
   deep-dive section's cards, which have no icon tile of their own.
   --cyan-700, not --cyan: unlike its parent #tools section, the card
   itself is a light FloralWhite surface, where --cyan measures ~2:1. */
.tool-detail-label {
  display: block;
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--cyan-700);
  margin-bottom: 8px;
}
.kbd-inline {
  display: inline-block;
  padding: 1px 6px;
  border-radius: 5px;
  background: var(--bg-alt);
  color: var(--ink);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px;
  font-weight: 600;
}

/* =====================================================================
   MODULE: Tool deep-dive (dark variant)
   Same dark navy scene as the hero's own background, reusing
   .usecase-card but recolored for a dark surface — the same treatment
   .price-card--featured already gives a card sitting on a light page.
   ===================================================================== */
#tools {
  position: relative;
  overflow: hidden;
  background:
    radial-gradient(ellipse 900px 600px at 78% 30%, rgba(23,195,217,.10), transparent 60%),
    linear-gradient(135deg, #111a2c 0%, var(--navy-950) 60%, #05070c 100%);
}
/* Animated glow drifting up behind the section content — vanilla
   Canvas 2D (see js/main.js's initBeamsBackground), not a library.
   Sits behind everything (z-index 0); the container is lifted above it
   so text/cards stay unaffected, and pointer-events are off since it's
   purely decorative. */
#tools .beams-bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  /* The blur used to be redone in software every frame inside the
     canvas itself (ctx.filter in js/main.js) — a well-known expensive
     Canvas 2D anti-pattern, and the reason scrolling into this section
     specifically felt heavier than the rest of the page. A CSS filter
     on the element is one GPU-composited operation over the already-
     rendered frame instead, at a fraction of the cost. */
  filter: blur(22px);
}
#tools .container { position: relative; z-index: 1; }
#tools .section__title { color: #fff; }
#tools .section__lead { color: var(--muted-light); }
/* This is the one .eyebrow instance sitting on a dark surface (this
   section's own navy/beams background, not a light card) — restore the
   brighter --cyan here instead of the light-background --cyan-700
   default set above: --cyan measures ~8.8:1 against this section's
   background, --cyan-700 would only measure ~3.5:1, under AA. */
#tools .eyebrow { color: var(--cyan); }

/* MODULE: Product demo video (after Presenter toolkit). Same static
   navy gradient as #tools, without the animated canvas. Video left,
   text right on desktop; text first when stacked. The play button sits
   over the empty middle of the window drawn in the poster
   (assets/images/demo-poster*.jpg, window centre at 76% / 51%). */
.demo {
  position: relative;
  overflow: hidden;
  color: #fff;
  background:
    radial-gradient(ellipse 900px 600px at 25% 45%, rgba(23,195,217,.10), transparent 60%),
    linear-gradient(135deg, #111a2c 0%, var(--navy-950) 60%, #05070c 100%);
}
.demo .eyebrow { color: var(--cyan); }
.demo .section__title { color: #fff; }
.demo .section__lead { color: var(--muted-light); }
.demo__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
  gap: 56px;
  align-items: center;
}
.demo__player {
  order: -1;
  position: relative;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  background: #000;
  border: 1px solid rgba(255,255,255,.08);
  border-radius: var(--radius-lg);
  box-shadow: 0 30px 80px rgba(0,0,0,.45);
}
.demo__video { display: block; width: 100%; height: 100%; }
.demo__play {
  position: absolute;
  left: 76%;
  top: 51%;
  transform: translate(-50%, -50%);
  display: grid;
  place-items: center;
  width: 76px;
  height: 76px;
  color: var(--navy-950);
  background: var(--cyan);
  border: 0;
  border-radius: 50%;
  box-shadow: 0 10px 30px rgba(23,195,217,.45);
  cursor: pointer;
  transition: transform .15s ease, box-shadow .15s ease;
}
.demo__play[hidden] { display: none; }
.demo__play svg { width: 32px; height: 32px; margin-left: 4px; }
.demo__play:hover { transform: translate(-50%, -54%); box-shadow: 0 14px 36px rgba(23,195,217,.55); }
.demo__play:focus-visible { outline: 3px solid #fff; outline-offset: 4px; }
.demo__note { margin-top: 18px; font-size: 14px; color: var(--muted-light); }
@media (max-width: 880px) {
  .demo__grid { grid-template-columns: minmax(0, 1fr); gap: 32px; }
  .demo__player { order: 0; }
  .demo__play { width: 60px; height: 60px; }
  .demo__play svg { width: 26px; height: 26px; margin-left: 3px; }
}
/* Cards are FloralWhite — a light card naturally stands out against
   the dark beams background on its own, without needing hover
   animation (an earlier hover-tilt/glow attempt was reverted per
   explicit "should not be bending" feedback: static cards, no
   transform). No h3/p/code color overrides needed below this — the
   base .usecase-card rules already use dark ink/muted text tuned for a
   light card, same as the light-themed Use Cases section above. */
#tools .usecase-card {
  background: #FFFAF0;
  border-color: transparent;
  /* opacity/transform added for the scroll-reveal effect (see the
     .feature-card note) — this rule doesn't use transform for its own
     hover (static, no bending, per earlier decision), so it's free. */
  transition: opacity .6s ease, transform .6s ease, background-color .2s ease, box-shadow .2s ease;
}
/* Static — no transform/rotate (see note above) — just a subtle shift
   to a slightly richer cream plus a deeper shadow for lift on hover. */
#tools .usecase-card:hover {
  background: #FFF3DF;
  box-shadow: 0 10px 28px rgba(0,0,0,.28);
}

/* =====================================================================
   MODULE: Pricing
   ===================================================================== */
.pricing-note { display: flex; justify-content: center; margin-top: 32px; }
.pill--muted { background: #fff; color: var(--muted); border: 1px solid var(--border); }
.pill--muted svg { color: var(--cyan); }

.pricing-grid {
  margin-top: 40px;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 22px;
  align-items: stretch;
}
.price-card {
  position: relative;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 30px 26px;
  /* opacity added for the scroll-reveal effect — see the note on
     .feature-card's transition for why it has to live here, not on
     a separate .reveal-init rule. */
  transition: opacity .3s ease, transform .3s ease, box-shadow .3s ease, border-color .3s ease;
}
.price-card h3 { font-size: 15px; font-weight: 700; color: var(--muted); text-transform: uppercase; letter-spacing: .05em; }
.price-card__price { margin-top: 14px; display: flex; align-items: baseline; gap: 6px; }
.price-card__amount { font-size: 40px; font-weight: 800; letter-spacing: -0.02em; }
.price-card__period { color: var(--muted); font-size: 14px; }
.price-card__desc { margin-top: 10px; color: var(--muted); font-size: 13.5px; min-height: 36px; }
/* Pro card only: note about the 14-day trial (app v1.8.0+). Sits under
   the button, not above it, so all three cards' buttons stay in one row. */
.price-card__trial { margin: -8px 0 16px; color: var(--muted); font-size: 13px; line-height: 1.5; text-align: center; }
.price-card .check-list { margin-top: 4px; }
/* Personal / Team: flat cards get real hover feedback for the first
   time — a lift, a deeper shadow, and the border picking up the brand
   cyan. No rotation, matching the restraint used on the Tool cards. */
/* --cyan-700, not --cyan: a hover border is a UI state indicator, so it
   falls under the same 3:1 non-text contrast minimum as the checkmarks
   above — against this card's white background --cyan measures ~2.1:1. */
.price-card:not(.price-card--featured):hover {
  transform: translateY(-6px);
  box-shadow: var(--shadow-lg);
  border-color: var(--cyan-700);
}
.price-card--featured {
  /* Same navy-800 → navy-950 radial gradient as .hero__bg-base, instead
     of a flat navy-900 fill — reuses the site's one dark-surface
     treatment rather than introducing a second, so the card reads as
     premium/rich instead of a plain dark rectangle. */
  background: radial-gradient(100% 100% at 0% 0%, var(--navy-800) 0%, var(--navy-950) 100%);
  border-color: var(--navy-950);
  color: #fff;
  transform: scale(1.03);
  /* A permanent soft cyan halo behind the featured card, on top of the
     normal drop shadow — reads as premium even before it's touched. */
  box-shadow: var(--shadow-lg), 0 0 60px rgba(23,195,217,.18);
}
.price-card--featured:hover {
  transform: scale(1.045) translateY(-6px);
  box-shadow: var(--shadow-lg), 0 0 90px rgba(23,195,217,.32);
}
/* The featured card already owns `transform` for its permanent 1.03
   scale — .reveal-init's translateY would otherwise win that property
   outright (higher specificity) and silently flatten it back to scale
   1 once revealed. Pin the scale through both reveal phases instead,
   so this card only ever fades in (no rise) rather than losing its
   distinguishing size. */
.price-card--featured.reveal-init,
.price-card--featured.reveal-init.is-visible { transform: scale(1.03); }
.price-card--featured h3 { color: var(--cyan); }
.price-card--featured .price-card__desc { color: var(--muted-light); }
.price-card--featured .price-card__trial { color: var(--muted-light); }
.price-card--featured .check-list li { color: #fff; }
.price-card--featured .check-list li::before {
  background: rgba(23,195,217,.18);
  box-shadow: inset 0 0 0 1.5px var(--cyan);
}
.price-card--featured .check-list li::after {
  border-left-color: var(--cyan);
  border-bottom-color: var(--cyan);
}
.price-card__badge {
  position: absolute;
  top: -13px; left: 50%;
  transform: translateX(-50%);
  background: var(--cyan);
  color: var(--navy-950);
  font-size: 11.5px;
  font-weight: 700;
  padding: 5px 14px;
  border-radius: 999px;
  white-space: nowrap;
  box-shadow: 0 0 18px rgba(23,195,217,.55);
}

/* Plan-by-plan capability matrix — sits under the 3 cards, inside the
   same #pricing section (not a separate section), reusing the
   .compare-table component built for the Epic Pen comparison rather
   than introducing a third table style. */
.plan-matrix { margin-top: 64px; }
.plan-matrix__title { text-align: center; font-size: 22px; font-weight: 700; margin-top: 4px; margin-bottom: 32px; }
/* Highlight the Pro column (3rd of 4) to echo .price-card--featured
   above it, without needing a class on every one of its cells. */
.plan-matrix .compare-table th:nth-child(3),
.plan-matrix .compare-table td:nth-child(3) { background: rgba(23,195,217,.07); }
/* --cyan-700, not --cyan: this header sits on a near-white background
   (a 7% cyan tint over --bg-alt, barely darker) — --cyan still measures
   under 2:1 there. */
.plan-matrix .compare-table thead th:nth-child(3) { color: var(--cyan-700); }

/* =====================================================================
   MODULE: FAQ (accordion)
   ===================================================================== */
.accordion { margin-top: 40px; border-top: 1px solid var(--border); }
.accordion__item { border-bottom: 1px solid var(--border); }
.accordion__trigger {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 22px 4px;
  font-size: 16px;
  font-weight: 600;
  text-align: left;
}
.accordion__chevron { color: var(--muted); transition: transform .2s ease, color .15s ease; }
.accordion__trigger[aria-expanded="true"] .accordion__chevron { transform: rotate(180deg); }
/* Added 2026-09-18 — every other interactive element on the page (nav
   links/dropdowns, buttons, every card type) has a hover state; this
   trigger, arguably the most clickable thing in the FAQ section, had
   none. Color shift only, no lift/transform — matches the nav's own
   .nav__dropdown-trigger:hover treatment rather than inventing a new
   hover style. */
.accordion__trigger:hover,
.accordion__trigger:hover .accordion__chevron { color: var(--cyan-700); }
.accordion__panel {
  max-height: 0;
  overflow: hidden;
  transition: max-height .25s ease;
}
.accordion__panel p {
  padding: 0 4px 22px;
  color: var(--muted);
  font-size: 15px;
  max-width: 680px;
}

/* =====================================================================
   MODULE: Comparison table
   ===================================================================== */
.compare-table-wrap {
  margin-top: 40px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
  overflow-x: auto;
}
.compare-table { width: 100%; border-collapse: collapse; min-width: 480px; }
.compare-table th, .compare-table td {
  padding: 16px 20px;
  text-align: center;
  font-size: 14.5px;
  border-bottom: 1px solid var(--border);
}
.compare-table thead th {
  background: var(--bg-alt);
  font-size: 12.5px;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--muted);
}
.compare-table thead th:first-child,
.compare-table th[scope="row"] {
  text-align: left;
  font-weight: 600;
  color: var(--ink);
}
.compare-table tbody tr:last-child td,
.compare-table tbody tr:last-child th { border-bottom: none; }
.compare-table-note {
  margin-top: 14px;
  text-align: center;
  font-size: 12.5px;
  color: var(--muted);
}
.mark { font-weight: 700; font-size: 17px; }
.mark--yes { color: var(--cyan-700); } /* same 3:1 non-text contrast reasoning as .check-list */
.mark--no { color: var(--red); }
.mark--dash { color: var(--muted-light); }
.mark-note {
  display: block;
  margin-top: 2px;
  font-size: 11px;
  font-weight: 600;
  color: var(--muted-light);
}

/* =====================================================================
   MODULE: Final CTA
   ===================================================================== */
/* Logo-cyan band instead of dark navy. White text measures ~2.1:1
   against this cyan (fails WCAG AA even for large text) so the text
   color flips to dark navy here instead, which measures ~9:1. */
.cta-band {
  background: radial-gradient(circle at 20% 20%, #5dd6e4, var(--cyan));
  color: var(--navy-950);
  padding: 90px 0;
}
.cta-band__inner { text-align: center; max-width: 620px; margin: 0 auto; }
.cta-band__inner h2 { font-size: clamp(26px, 3.2vw, 34px); font-weight: 700; letter-spacing: -0.02em; text-wrap: balance; }
.cta-band__inner p { margin-top: 14px; color: rgba(10,14,26,.72); font-size: 16px; }
.cta-band__actions { display: flex; justify-content: center; gap: 14px; margin-top: 30px; flex-wrap: wrap; }
.cta-band__fine { margin-top: 18px; font-size: 13px; color: rgba(10,14,26,.6); }
/* .btn--ghost-dark is tuned for a dark background (white text/border) —
   override just within this section rather than changing the shared
   class, since the hero still uses it on its own dark background. */
.cta-band .btn--ghost-dark { color: var(--navy-950); border-color: rgba(10,14,26,.35); }
.cta-band .btn--ghost-dark:hover { background: rgba(10,14,26,.08); }

/* =====================================================================
   MODULE: Footer
   ===================================================================== */
.footer { background: var(--navy-950); color: var(--muted-light); padding: 64px 0 0; }
.footer__grid {
  display: grid;
  grid-template-columns: 1.6fr 1fr 1fr 1fr;
  gap: 40px;
  padding-bottom: 48px;
}
.footer .nav__brand { color: #fff; }
.footer__social { display: flex; gap: 12px; margin-top: 20px; }
.footer__social a {
  width: 34px; height: 34px;
  border-radius: 50%;
  border: 1px solid rgba(255,255,255,.15);
  display: flex; align-items: center; justify-content: center;
}
.footer__social a:hover { border-color: var(--cyan); color: var(--cyan); }
.footer__heading { color: #fff; font-weight: 700; font-size: 13.5px; margin-bottom: 16px; }
.footer__col { display: flex; flex-direction: column; gap: 7px; font-size: 14px; }
.footer__col a:hover { color: #fff; }
/* § 356a BGB: the withdrawal link must be easy to find on every page. */
.footer__col a.footer__withdraw { color: #fff; font-weight: 600; }
.footer__bottom {
  display: flex;
  justify-content: space-between;
  padding: 22px 24px;
  font-size: 13px;
  position: relative;
}
/* Divider drawn here rather than as .footer__grid's border-bottom: the
   grid is a .container, so its border ran through the 24px side padding
   and stuck out past the content edge the columns and copyright share. */
.footer__bottom::before {
  content: "";
  position: absolute;
  top: 0; left: 24px; right: 24px;
  height: 1px;
  background: rgba(255,255,255,.08);
}
.footer__meta { display: flex; align-items: center; gap: 20px; }

/* Footer language switch — <details> + plain links (no JS needed to
   switch). The menu opens upward because it sits at the page bottom. */
.lang-switch { position: relative; }
.lang-switch__trigger {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 6px 12px;
  border: 1px solid rgba(255,255,255,.15);
  border-radius: var(--radius-sm);
  color: #fff;
  cursor: pointer;
  list-style: none;
  user-select: none;
}
.lang-switch__trigger::-webkit-details-marker { display: none; }
.lang-switch__trigger:hover { border-color: var(--cyan); }
.lang-switch__trigger:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }
.lang-switch__chevron { transition: transform .2s ease; }
.lang-switch[open] .lang-switch__chevron { transform: rotate(180deg); }
.lang-switch__menu {
  position: absolute;
  bottom: calc(100% + 8px);
  right: 0;
  min-width: 160px;
  margin: 0;
  padding: 6px;
  list-style: none;
  background: var(--navy-800);
  border: 1px solid rgba(255,255,255,.12);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-lg);
  z-index: 20;
}
.lang-switch__menu a {
  display: block;
  padding: 8px 12px;
  border-radius: 6px;
  color: var(--muted-light);
}
.lang-switch__menu a:hover,
.lang-switch__menu a:focus-visible { background: rgba(255,255,255,.06); color: #fff; }
.lang-switch__menu a[aria-current="page"] { color: #fff; font-weight: 600; }
.lang-switch__menu a[aria-current="page"]::after { content: " \2713"; color: var(--cyan); }
/* German pages: marks links to pages that only exist in English. */
.lang-tag {
  display: inline-block;
  margin-left: 4px;
  padding: 0 4px;
  border: 1px solid currentColor;
  border-radius: 4px;
  font-size: 9.5px;
  font-weight: 600;
  line-height: 1.5;
  letter-spacing: .04em;
  text-decoration: none;
  vertical-align: 1px;
  opacity: .7;
}

/* =====================================================================
   MODULE: Content pages (User Guide / FAQ / Version History / Contact /
   Refund Policy) — shared prose layout for pages/*.html
   ===================================================================== */
.page-hero {
  background: var(--navy-950);
  color: #fff;
  padding: 64px 0 48px;
}
.page-hero .eyebrow { color: var(--cyan); }
.page-hero h1 { font-size: clamp(28px, 3.6vw, 40px); font-weight: 800; letter-spacing: -0.02em; }
.page-hero p { margin-top: 12px; color: rgba(255,255,255,.7); font-size: 16px; max-width: 620px; }

.page-content { padding: 56px 0 96px; }
.page-content h2 {
  font-size: 22px;
  font-weight: 700;
  margin: 40px 0 14px;
  letter-spacing: -0.01em;
}
.page-content h2:first-child { margin-top: 0; }
.page-content p { color: var(--ink); font-size: 15.5px; margin-bottom: 14px; max-width: 720px; }
.page-content p.muted { color: var(--muted); font-size: 14px; }
.page-content ul.plain { list-style: disc; padding-left: 22px; margin-bottom: 16px; }
.page-content ul.plain li { margin-bottom: 8px; font-size: 15.5px; }
/* --cyan-700 like every other text link (cookie banner, docs nav): the
   brand accent darkened enough for 4.5:1 link text on light surfaces. */
a.inline-link { color: var(--cyan-700); font-weight: 600; text-decoration: underline; text-underline-offset: 2px; }
a.inline-link:hover { text-decoration-thickness: 2px; }

/* Withdrawal form (de/widerruf.html, pages/withdraw.html; § 356a BGB).
   Plain HTML form posting to /api/withdraw, no JS. Input borders use
   --muted (4.76:1 on white) to clear the 3:1 non-text contrast minimum. */
.withdraw-form {
  display: grid;
  gap: 18px;
  max-width: 560px;
  margin: 24px 0 28px;
  padding: 24px;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
.withdraw-form__field label { display: block; margin-bottom: 6px; font-size: 14.5px; font-weight: 600; color: var(--ink); }
.withdraw-form__field input {
  width: 100%;
  padding: 11px 12px;
  font: inherit;
  font-size: 15.5px;
  color: var(--ink);
  background: #fff;
  border: 1px solid var(--muted);
  border-radius: var(--radius-sm);
}
.withdraw-form__field input:focus-visible { outline: 2px solid var(--cyan-700); outline-offset: 1px; border-color: var(--cyan-700); }
.page-content .withdraw-form__hint { margin: 6px 0 0; font-size: 13px; color: var(--muted); }
.page-content .withdraw-form__note { margin: 0; font-size: 14px; color: var(--muted); }
.withdraw-form .btn { justify-self: start; }
/* Honeypot for bots: off-screen, never shown to people or screen readers. */
.withdraw-form__trap { position: absolute; left: -10000px; width: 1px; height: 1px; overflow: hidden; }
/* Error messages appear only when /api/withdraw redirects back to #fehler-…/#error-… */
.page-content .form-alert {
  display: none;
  padding: 12px 14px;
  color: #991b1b;
  background: #fef2f2;
  border: 1px solid #fecaca;
  border-radius: var(--radius-sm);
}
.page-content .form-alert:target { display: block; }

.callout {
  background: var(--cyan-light);
  border: 1px solid rgba(23,195,217,.3);
  border-radius: var(--radius-md);
  padding: 16px 20px;
  font-size: 14.5px;
  color: #0e6570;
  margin: 20px 0;
}
.callout--todo {
  background: #fff7ed;
  border-color: #fdba74;
  color: #9a3412;
}

.shortcut-table-wrap { margin: 20px 0 32px; overflow-x: auto; }
.shortcut-table { width: 100%; border-collapse: collapse; min-width: 420px; }
.shortcut-table th, .shortcut-table td {
  text-align: left;
  padding: 11px 16px;
  border-bottom: 1px solid var(--border);
  font-size: 14.5px;
}
.shortcut-table th { background: var(--bg-alt); font-size: 12.5px; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); }
.kbd {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 6px;
  background: var(--navy-950);
  color: #fff;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 13px;
}

.changelog-entry { padding: 22px 0; border-bottom: 1px solid var(--border); }
.changelog-entry:first-child { padding-top: 0; }
.changelog-entry__version { font-size: 17px; font-weight: 700; }
.changelog-entry__date { color: var(--muted-light); font-size: 13px; margin-left: 8px; font-weight: 500; }
.changelog-entry ul.plain { margin-top: 10px; margin-bottom: 0; }
.changelog-entry__headline { font-weight: 600; font-size: 14.5px; margin-top: 8px; }

.contact-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 20px;
  margin-top: 24px;
}
.contact-card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 22px;
}
.contact-card h3 { font-size: 15.5px; margin-bottom: 8px; }
.contact-card p { font-size: 14px; color: var(--muted); margin-bottom: 12px; }

.page-content code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  background: var(--bg-alt);
  color: var(--ink);
  padding: 1px 5px;
  border-radius: 4px;
  font-size: 13px;
}

/* =====================================================================
   MODULE: Documentation nav (pages/user-guide.html only)
   Two-column layout: sticky in-page nav + content, for a doc long
   enough (13 top-level sections) that a flat scroll would lose people.
   The mobile collapse reuses .accordion__trigger/.accordion__panel
   verbatim (see initAccordion in js/main.js) instead of a second
   toggle implementation — same behavior, no new JS wiring needed for
   open/close, only for the scroll-spy active-link highlighting
   (initDocsNav). Desktop and mobile render two separate <ul> copies of
   the same links (toggled via the breakpoint below) rather than one
   list fighting the accordion's max-height:0 default at all widths —
   simpler than reconciling "collapsed by default" with "always open on
   desktop" in one element. */
.docs-intro { max-width: 720px; }
.docs-layout {
  display: grid;
  grid-template-columns: 220px 1fr;
  gap: 48px;
  align-items: start;
  margin-top: 32px;
}
.docs-nav {
  position: sticky;
  top: 88px;
  max-height: calc(100vh - 108px);
  overflow-y: auto;
}
.docs-nav__list { list-style: none; }
.docs-nav__list li { margin-bottom: 2px; }
.docs-nav__list a {
  display: block;
  padding: 7px 12px;
  border-radius: var(--radius-sm);
  font-size: 14px;
  color: var(--muted);
  border-left: 2px solid transparent;
  transition: color .15s ease, background .15s ease, border-color .15s ease;
}
.docs-nav__list a:hover { color: var(--ink); background: var(--bg-alt); }
.docs-nav__list a.is-active {
  color: var(--cyan-700);
  border-left-color: var(--cyan);
  background: var(--cyan-light);
  font-weight: 600;
}
.docs-nav__toggle.accordion__trigger { display: none; }
.docs-nav__panel.accordion__panel { display: none; }

.docs-content { max-width: 760px; min-width: 0; }
/* The docs sections have no top padding of their own, so a jump to one
   (sidebar links, the homepage's "See how multi-monitor works") would land
   with its heading under the 68px sticky nav. Stop at the same 88px the
   sticky .docs-nav uses. Homepage sections don't need this: their 64-96px
   top padding already clears the nav. */
.docs-content section[id] { scroll-margin-top: 88px; }
.docs-content h3 { font-size: 17px; font-weight: 700; margin: 26px 0 10px; }
.docs-content section + section { margin-top: 40px; }
.docs-footnote { margin-top: 48px; padding-top: 20px; border-top: 1px solid var(--border); }

@media (max-width: 960px) {
  .docs-layout { display: block; margin-top: 24px; }
  .docs-nav { position: static; max-height: none; margin-bottom: 28px; }
  .docs-nav__list--desktop { display: none; }
  .docs-nav__toggle.accordion__trigger {
    display: flex;
    width: 100%;
    padding: 12px 16px;
    background: var(--bg-alt);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    font-size: 14px;
  }
  .docs-nav__panel.accordion__panel { display: block; }
  .docs-nav__panel .docs-nav__list { padding: 10px 2px 2px; }
}

/* =====================================================================
   MODULE: Scroll reveal
   Applied only via JS (see js/main.js's initScrollReveal) — elements
   are visible by default; JS adds .reveal-init only when motion is
   allowed, so a JS failure or prefers-reduced-motion never leaves
   anything permanently hidden.
   ===================================================================== */
.reveal-init {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity .6s ease, transform .6s ease;
}
.reveal-init.is-visible { opacity: 1; transform: translateY(0); }

/* =====================================================================
   RESPONSIVE
   ===================================================================== */
@media (max-width: 960px) {
  .hero__grid { grid-template-columns: 1fr; }
  /* No order:-1 here (removed 2026-09-18) — the visual used to render
     ABOVE the headline on mobile. That inverted the standard content-
     first mobile hero pattern (headline/subhead/CTA, then supporting
     art), which matters more than usual here because this particular
     visual is a deliberately generic, abstract placeholder (see its
     comment in index.html) that doesn't communicate anything on its
     own — leading with it pushed the actual value proposition below
     the first screen. Removing the override falls back to the DOM's
     natural order (.hero__copy first, .hero__visual second), matching
     how the desktop layout already reads left-to-right. */
  .hero__visual { max-width: 480px; margin: 20px auto 0; }
  .split { grid-template-columns: 1fr; }
  /* No order:-1 here (removed 2026-09-18) — same content-first fix as
     .hero__visual just above: this section's copy (headline/checklist/
     link) should read before its supporting dual-monitor illustration
     on mobile, not after. Falls back to natural DOM order
     (.split__copy first, .split__art second). */
  .feature-grid { grid-template-columns: repeat(2, 1fr); }
  .usecase-grid { grid-template-columns: repeat(2, 1fr); }
  .usecase-grid--4 { grid-template-columns: repeat(2, 1fr); }
  .pricing-grid { grid-template-columns: 1fr; max-width: 380px; margin-left: auto; margin-right: auto; }
  .price-card--featured { transform: none; }
  .footer__grid { grid-template-columns: 1fr 1fr; }
}

/* Nav-collapse breakpoint — its own media query, deliberately NOT
   720px like the rest of this section. Found 2026-09-18 testing real
   device widths: the nav's content (wordmark + Features/Pricing/
   Account/Help + Download button) needs ~735px minimum before
   anything has to give; below that, with no flex-shrink protection on
   any of .nav__brand/.nav__links/.nav__actions, the "Features" link's
   text visually overlapped the wordmark rather than wrapping (text
   doesn't wrap in a shrunk flex item — it just overflows past its own
   box into a sibling's space). 720px sat *inside* that danger zone
   (721-734px overlapped, only cleared right at ~735px), so tightening
   the threshold to the exact minimum would have been fragile — any
   future copy change (e.g. a longer translated label) could reopen
   the same gap. 880px gives real margin instead. */
@media (max-width: 880px) {
  .nav__links {
    position: absolute;
    top: 68px; left: 0; right: 0;
    background: var(--bg);
    border-bottom: 1px solid var(--border);
    flex-direction: column;
    gap: 0;
    padding: 8px 24px 16px;
    display: none;
  }
  .nav__links.is-open { display: flex; }
  .nav__links > a { padding: 10px 0; border-bottom: 1px solid var(--border); }
  .nav__dropdown { border-bottom: 1px solid var(--border); }
  /* The collapsed menu inherits the desktop .nav__links align-items:
     center, so every row is centred at its text width. Keep the Account/
     Help trigger and its submenu centred too, rather than stretching the
     trigger (label left, chevron right) once the open submenu widens the
     row. */
  .nav__dropdown-trigger { margin: 0 auto; padding: 10px 0; }
  .nav__dropdown-menu {
    position: static;
    transform: none;
    box-shadow: none;
    border: none;
    min-width: 0;
    align-items: center;
    padding: 0 0 8px;
  }
  .nav__dropdown-menu a { padding: 8px 0; }
  .nav__toggle { display: flex; }
  .nav__actions .btn--sm span { display: none; }
}

@media (max-width: 720px) {
  .usecase-grid { grid-template-columns: 1fr; }
  .usecase-grid--4 { grid-template-columns: 1fr; }
  .feature-grid { grid-template-columns: 1fr; }
  .footer__grid { grid-template-columns: 1fr; }
  .footer__bottom { flex-direction: column; gap: 6px; text-align: center; }
  .footer__meta { flex-direction: column; gap: 14px; padding-top: 8px; }
  .lang-switch__menu { right: auto; left: 50%; transform: translateX(-50%); text-align: left; }
  .section { padding: 64px 0; }
  .cta-band { padding: 64px 0; }
  /* The hero kept its desktop 120px/100px padding on phones, so the
     badge sat ~120px below the nav — nearly double every other
     section's mobile padding. Brought in line with the 64px rhythm
     (a touch less on top, since the sticky nav already adds space). */
  .hero { padding: 48px 0 64px; min-height: 0; }
  /* Tables fit the phone width instead of scrolling sideways — at the
     desktop min-width the plan matrix showed only Capability + Personal,
     with Pro/Team off-screen and no hint they existed. */
  .compare-table { min-width: 0; }
  .compare-table th, .compare-table td { padding: 12px 6px; font-size: 13.5px; }
  .compare-table th[scope="row"], .compare-table thead th:first-child { padding-left: 14px; }
  .compare-table thead th { font-size: 10.5px; letter-spacing: .02em; }
}

/* The hero illustration is fixed-pixel artwork (260px pen circle,
   nowrap readout, pen-tip offset-paths in px), ~315px wide at its
   natural size. Below a ~363px viewport that no longer fits the 24px
   gutters and pushed the whole page sideways (363px layout on a 320px
   phone). Scale the mockup as a unit rather than squeezing it, so the
   strokes and pen tips stay registered; zoom (unlike transform) also
   shrinks the layout box. min-width: 0 stops any grid item from
   forcing the page wider again. */
.hero__grid > * { min-width: 0; }
@media (max-width: 362px) {
  .hero__visual .window-mockup { zoom: .93; }
}
@media (max-width: 340px) {
  .hero__visual .window-mockup { zoom: .86; }
}

/* =====================================================================
   MODULE: Cookie consent
   Bottom banner (Accept / Reject / Settings, equal visual weight — no
   dark-pattern sizing) plus a settings modal with per-category toggles.
   See js/main.js's initCookieConsent for the localStorage-backed state
   machine. Visibility is controlled entirely via the `hidden` attribute
   (JS-gated, same "never stuck" contract as .reveal-init below) — the
   slide-up/fade-in is decorative only, added under prefers-reduced-
   motion: no-preference so a JS failure never leaves anything invisible.
   ===================================================================== */
.cookie-consent {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 200;
  padding: 16px;
  padding-bottom: max(16px, env(safe-area-inset-bottom, 0px));
}
.cookie-consent[hidden] { display: none; }
@media (prefers-reduced-motion: no-preference) {
  .cookie-consent:not([hidden]) {
    transform: translateY(100%);
    animation: cookie-slide-up .45s cubic-bezier(.16,1,.3,1) forwards;
  }
}
@keyframes cookie-slide-up { to { transform: translateY(0); } }

.cookie-consent__inner {
  max-width: var(--container);
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 28px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  padding: 22px 28px;
}
.cookie-consent__text { flex: 1; min-width: 0; }
.cookie-consent__title { font-weight: 700; font-size: 15.5px; margin: 0 0 4px; }
.cookie-consent__desc { color: var(--muted); font-size: 13.5px; max-width: 640px; }
.cookie-consent__desc a { color: var(--cyan-700); text-decoration: underline; text-underline-offset: 2px; }
.cookie-consent__actions { display: flex; gap: 10px; flex: none; }

@media (max-width: 760px) {
  .cookie-consent { padding: 10px; }
  .cookie-consent__inner { flex-direction: column; align-items: stretch; padding: 20px; gap: 16px; }
  .cookie-consent__actions { flex-direction: column; }
  .cookie-consent__actions .btn { width: 100%; }
}

/* Preferences modal ----------------------------------------------------- */
.cookie-modal {
  position: fixed;
  inset: 0;
  z-index: 210;
  display: flex;
  align-items: flex-end;
  justify-content: center;
}
.cookie-modal[hidden] { display: none; }
.cookie-modal__backdrop { position: absolute; inset: 0; background: rgba(10, 14, 26, .55); }
.cookie-modal__panel {
  position: relative;
  width: 100%;
  max-width: 560px;
  max-height: min(640px, 88vh);
  overflow-y: auto;
  margin: 0 0 0 0;
  background: var(--card);
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  box-shadow: var(--shadow-lg);
  padding: 28px;
}
@media (min-width: 640px) {
  .cookie-modal { align-items: center; }
  .cookie-modal__panel { margin: 24px; border-radius: var(--radius-lg); }
}
@media (prefers-reduced-motion: no-preference) {
  .cookie-modal:not([hidden]) .cookie-modal__backdrop { animation: cookie-fade .2s ease forwards; }
  .cookie-modal:not([hidden]) .cookie-modal__panel { animation: cookie-modal-in .3s cubic-bezier(.16,1,.3,1) forwards; }
}
@keyframes cookie-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes cookie-modal-in { from { opacity: 0; transform: translateY(24px); } to { opacity: 1; transform: translateY(0); } }

.cookie-modal__header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
.cookie-modal__header h2 { font-size: 20px; font-weight: 700; letter-spacing: -0.01em; }
.cookie-modal__close {
  display: flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; border-radius: 50%; flex: none;
  color: var(--muted);
  transition: background .15s ease, color .15s ease;
}
.cookie-modal__close:hover { background: var(--bg-alt); color: var(--ink); }
.cookie-modal__intro { color: var(--muted); font-size: 14px; margin-bottom: 22px; }
.cookie-modal__intro a { color: var(--cyan-700); text-decoration: underline; text-underline-offset: 2px; }

.cookie-modal__list { display: flex; flex-direction: column; }
.cookie-pref { border-top: 1px solid var(--border); padding: 16px 0; }
.cookie-pref:first-child { border-top: none; padding-top: 0; }
.cookie-pref__row { display: flex; align-items: flex-start; justify-content: space-between; gap: 20px; }
.cookie-pref__label { flex: 1; min-width: 0; }
.cookie-pref__name { font-weight: 600; font-size: 14.5px; margin: 0 0 4px; display: block; }
.cookie-pref__desc { color: var(--muted); font-size: 13px; margin: 0; }
/* --cyan-700, not --cyan: this badge sits on the modal's white surface —
   same reasoning as .eyebrow above, --cyan itself measures ~2:1 there. */
.cookie-pref__locked {
  flex: none;
  display: inline-flex; align-items: center; gap: 5px;
  padding: 5px 10px;
  margin-top: 1px;
  border-radius: 999px;
  background: var(--cyan-light);
  color: var(--cyan-700);
  font-size: 12px;
  font-weight: 700;
  white-space: nowrap;
}

.cookie-toggle {
  position: relative;
  flex: none;
  margin-top: 1px;
  width: 42px; height: 24px;
  border-radius: 999px;
  background: var(--border);
  transition: background .15s ease;
}
.cookie-toggle__thumb {
  position: absolute;
  top: 3px; left: 3px;
  width: 18px; height: 18px;
  border-radius: 50%;
  background: #fff;
  box-shadow: var(--shadow-sm);
  transition: transform .15s ease;
}
.cookie-toggle[aria-checked="true"] { background: var(--cyan); }
.cookie-toggle[aria-checked="true"] .cookie-toggle__thumb { transform: translateX(18px); }

.cookie-modal__footer {
  display: flex;
  gap: 10px;
  margin-top: 22px;
  padding-top: 20px;
  border-top: 1px solid var(--border);
}
.cookie-modal__footer .btn { flex: 1; }

@media (max-width: 480px) {
  .cookie-modal__footer { flex-direction: column-reverse; }
}
