/* QueryDoctor — design system.
   Indigo/violet palette, large readable type, iOS-style springy
   buttons (transform/opacity only — GPU-friendly, buttery on phones).
   pos/warn/neg stay green/amber/red regardless of brand — those carry
   health-score/severity meaning, not brand identity. */

:root {
  --bg: #f7f7fc;
  --card: #ffffff;
  --ink: #191a2e;
  --muted: #63637d;
  --line: #e4e4f0;
  --brand: #6366f1;
  --brand-2: #8b5cf6;
  --accent: #06b6d4;
  --brand-ink: #ffffff;
  --logo-1: #10b981;
  --logo-2: #14b8a6;
  --pos: #10b981;
  --warn: #f59e0b;
  --neg: #ef4444;
  --code-bg: #14152a;
  --code-ink: #e3e3f7;
  --shadow: 0 8px 30px rgba(25, 26, 46, .08);
  --radius: 20px;
  --spring: cubic-bezier(.34, 1.56, .64, 1); /* iOS-style overshoot */
}
/* Dark palette — applied when the user picks dark, or by system preference
   when the user hasn't picked anything. */
:root[data-theme="dark"] {
  --bg: #000000;
  --card: #15162a;
  --ink: #eeeef8;
  --muted: #9a9ab8;
  --line: #26273f;
  --brand: #818cf8;
  --brand-2: #a78bfa;
  --accent: #22d3ee;
  --brand-ink: #1e1b4b;
  --logo-1: #34d399;
  --logo-2: #2dd4bf;
  --code-bg: #0e0f1f;
  --code-ink: #dadaf3;
  --shadow: 0 8px 30px rgba(0, 0, 0, .45);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #000000;
    --card: #15162a;
    --ink: #eeeef8;
    --muted: #9a9ab8;
    --line: #26273f;
    --brand: #818cf8;
    --brand-2: #a78bfa;
    --accent: #22d3ee;
    --brand-ink: #1e1b4b;
    --logo-1: #34d399;
    --logo-2: #2dd4bf;
    --code-bg: #0e0f1f;
    --code-ink: #dadaf3;
    --shadow: 0 8px 30px rgba(0, 0, 0, .45);
  }
}

/* Theme switch animation — View Transitions API: the new theme sweeps out
   as a circle from the toggle button, one GPU-composited animation instead
   of hundreds of per-element colour transitions (no text stutter). */
::view-transition-old(root), ::view-transition-new(root) { animation: none; mix-blend-mode: normal; }
::view-transition-new(root) { z-index: 2147483646; }
::view-transition-old(root) { z-index: 1; }

@font-face {
  font-family: "Space Grotesk";
  font-style: normal;
  font-weight: 500 700;
  font-display: swap;
  src: url("fonts/SpaceGrotesk-Variable.woff2") format("woff2");
}

* { box-sizing: border-box; margin: 0; }
/* Safety net: the browser's own default link blue (#0000EE-ish) is harsh,
   especially on a dark background — any <a> that doesn't get a more
   specific colour rule elsewhere lands here instead of on that default.
   Every current link already has its own brand-coloured rule, so this is
   a future-proofing floor, not a fix for a live bug. */
a { color: #38bdf8; }
[hidden] { display: none !important; }
/* overflow-x:hidden on body alone doesn't reliably stop iOS Safari from
   including off-screen bled content in its own zoom-to-fit width
   calculation — belt-and-braces alongside the real fix (clipping the
   actual offending element, see .hero-demo's comment below). */
html { -webkit-text-size-adjust: 100%; overflow-x: hidden; }
body {
  font-family: "Space Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", system-ui, sans-serif;
  font-weight: 500;
  background: var(--bg); color: var(--ink);
  font-size: 17px; line-height: 1.55; min-height: 100dvh;
  padding-bottom: env(safe-area-inset-bottom);
  overflow-x: hidden;
}
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
}

/* ── Top bar ── */
.topbar {
  display: flex; justify-content: space-between; align-items: center;
  padding: 10px clamp(16px, 4vw, 40px);
  position: sticky; top: 0; z-index: 5;
  background: color-mix(in srgb, var(--bg) 80%, transparent);
  backdrop-filter: blur(12px);
}
.brand { display: flex; align-items: center; gap: 10px; font-weight: 800; font-size: 20px; }
.brand span {
  background: linear-gradient(100deg, var(--logo-1), var(--logo-2));
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
.topbar-left { display: flex; align-items: center; gap: 8px; }
.topbar-right { display: flex; align-items: center; gap: 10px; }
.btn-history {
  width: 40px; height: 40px; border-radius: 12px; border: 0;
  background: none; color: var(--muted); cursor: pointer;
  display: grid; place-items: center;
  transition: transform .3s var(--spring), background .2s ease, color .2s ease;
}
.btn-history:hover { background: color-mix(in srgb, var(--muted) 12%, transparent); color: var(--ink); transform: scale(1.08); }
.btn-history:active { transform: scale(.88); }

/* ── History drawer ── */
.drawer {
  position: fixed; z-index: 60; top: 0; bottom: 0; left: 0;
  width: min(320px, 86vw);
  background: var(--card); box-shadow: 0 0 60px rgba(0, 0, 0, .35);
  padding: 18px 16px calc(18px + env(safe-area-inset-bottom));
  transform: translateX(-105%);
  transition: transform .45s var(--spring);
  display: flex; flex-direction: column;
}
.drawer.open { transform: translateX(0); }
.drawer-head { display: flex; justify-content: space-between; align-items: center; }
.drawer-head h2 { font-size: 18px; }
.drawer-note { color: var(--muted); font-size: 13px; margin: 8px 0 12px; }
.history-insights {
  background: color-mix(in srgb, var(--brand) 8%, transparent);
  border: 1px solid color-mix(in srgb, var(--brand) 20%, transparent);
  border-radius: 12px; padding: 12px 14px; margin-bottom: 14px; font-size: 13.5px;
}
.history-insights b { display: block; margin-bottom: 6px; font-size: 13px; }
.history-insights ul { list-style: none; padding: 0; }
.history-insights li { display: flex; justify-content: space-between; gap: 8px; padding: 3px 0; color: var(--muted); }
.history-insights li b { display: inline; margin: 0; font-weight: 600; color: var(--ink); }
.drawer-backdrop {
  position: fixed; inset: 0; z-index: 55;
  background: rgba(0, 0, 0, .45); backdrop-filter: blur(2px);
  opacity: 0; transition: opacity .3s ease;
}
.drawer-backdrop.show { opacity: 1; }
.history-list { list-style: none; overflow-y: auto; flex: 1; }
.history-list li {
  display: flex; gap: 10px; align-items: center;
  padding: 11px 10px; border-radius: 12px; cursor: pointer;
  transition: background .2s ease, transform .25s var(--spring);
}
.history-list li:hover { background: color-mix(in srgb, var(--brand) 8%, transparent); transform: translateX(3px); }
.history-list li:active { transform: scale(.97); }
.h-score {
  flex: none; width: 40px; height: 40px; border-radius: 50%;
  display: grid; place-items: center;
  font-size: 13px; font-weight: 800; font-variant-numeric: tabular-nums;
}
.h-score.good { background: color-mix(in srgb, var(--pos) 14%, transparent);  color: var(--pos); }
.h-score.mid  { background: color-mix(in srgb, var(--warn) 16%, transparent); color: var(--warn); }
.h-score.bad  { background: color-mix(in srgb, var(--neg) 14%, transparent);  color: var(--neg); }
.h-meta { min-width: 0; }
.h-sql {
  font: 500 12.5px/1.4 ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--ink);
}
.h-sub { font-size: 12px; color: var(--muted); }
.drawer-empty { color: var(--muted); font-size: 14.5px; text-align: center; margin-top: 30px; }

/* ── About modal ── */
.about-modal {
  position: fixed; z-index: 70; top: 50%; left: 50%;
  width: min(640px, 92vw); max-height: min(82vh, 720px);
  background: var(--card); border-radius: var(--radius); box-shadow: 0 20px 70px rgba(0, 0, 0, .4);
  display: flex; flex-direction: column;
  opacity: 0; transform: translate(-50%, -48%) scale(.96);
  pointer-events: none;
  transition: opacity .3s ease, transform .35s var(--spring);
}
.about-modal.open { opacity: 1; transform: translate(-50%, -50%) scale(1); pointer-events: auto; }
.about-head {
  display: flex; justify-content: space-between; align-items: center;
  padding: 18px 20px 12px; border-bottom: 1px solid var(--line); flex: none;
}
.about-head h2 { font-size: 19px; }
.about-body { overflow-y: auto; padding: 4px 20px 22px; }
.about-section { margin-top: 20px; }
.about-section:first-child { margin-top: 16px; }
.about-section h3 {
  font-size: 14px; font-weight: 800; text-transform: uppercase; letter-spacing: .04em;
  color: var(--brand); margin-bottom: 10px;
}
.about-section p { color: var(--ink); font-size: 15px; line-height: 1.6; margin-bottom: 10px; }
.about-section ol, .about-section ul { padding-left: 22px; color: var(--ink); font-size: 15px; line-height: 1.6; }
.about-section li { margin-bottom: 10px; }
.about-section code { font-size: 13px; }
.about-section a { color: var(--brand); font-weight: 600; }
.privacy-tip-wrap { position: relative; display: inline-flex; }
.btn-privacy {
  width: 40px; height: 40px; border-radius: 12px; border: 1.5px solid var(--line);
  background: color-mix(in srgb, var(--pos) 10%, transparent); color: var(--pos);
  cursor: pointer; display: grid; place-items: center;
  /* NOT transitioning border-color: confirmed (reproduced in isolation on
     a plain test element) that a transitioned property whose value comes
     from a custom property freezes at its first-painted value and never
     re-resolves on a later var() change with no new rule match — exactly
     what a light/dark theme toggle is. Transitioning transform only here
     still gives the hover/press feel; border-color changes instantly
     instead of animating, which is correct rather than smoothly wrong. */
  transition: transform .3s var(--spring);
}
.btn-privacy:hover, .btn-privacy:focus-visible { transform: scale(1.08); border-color: var(--pos); }
.btn-privacy:active { transform: scale(.9); }
.privacy-tip {
  position: absolute; top: calc(100% + 10px); right: 0; z-index: 20;
  width: max-content; max-width: 220px;
  background: var(--card); color: var(--ink); border: 1.5px solid var(--line);
  border-radius: 12px; padding: 10px 14px; font-size: 13.5px; font-weight: 600;
  box-shadow: var(--shadow);
  opacity: 0; transform: translateY(-6px); pointer-events: none;
  transition: opacity .2s ease, transform .2s var(--spring);
}
.btn-privacy:hover + .privacy-tip,
.btn-privacy:focus-visible + .privacy-tip,
.privacy-tip.show {
  opacity: 1; transform: translateY(0);
}

/* Theme toggle: floating action button in the thumb zone (bottom-right),
   springy press, sun/moon swap with a little spin */
.btn-theme {
  position: fixed; z-index: 50;
  right: 18px; bottom: calc(18px + env(safe-area-inset-bottom));
  width: 54px; height: 54px; border-radius: 50%;
  border: 1.5px solid var(--line); background: var(--card); color: var(--ink);
  box-shadow: 0 10px 30px rgba(0, 0, 0, .25);
  cursor: pointer; display: grid; place-items: center;
  transition: transform .35s var(--spring), opacity .25s ease; /* not border-color — see .btn-privacy's comment above */
}
.btn-theme:hover { transform: scale(1.12); border-color: var(--brand); }
.btn-theme:active { transform: scale(.88); }
/* Both floating corner buttons are position:fixed, so they sit still while
   the page scrolls past underneath — on a long page, several near-full-
   width rows (the pricing email field, the SQL-dialect toolbar, etc.) pass
   directly through that bottom corner at different scroll positions, each
   visually colliding with the button in turn. Patching every such row
   individually is whack-a-mole; instead the buttons themselves fade down
   and stop intercepting clicks while a scroll is actively happening (see
   the scroll listener in app.js), and return to full strength the moment
   scrolling settles — so they never sit solidly on top of content that's
   mid-scroll, only when the page is at rest. */
.btn-theme.is-scrolling { opacity: .35; pointer-events: none; }
.btn-theme svg { position: absolute; transition: transform .5s var(--spring), opacity .35s ease; }
.ico-sun  { opacity: 0; transform: rotate(-90deg) scale(.4); }
.ico-moon { opacity: 1; transform: rotate(0) scale(1); }

/* Mirrors .btn-theme on the opposite corner — same size/z-index/shadow so
   the two read as a matched pair rather than one being an afterthought,
   and neither ever overlaps the other regardless of viewport width. */
.btn-feedback { left: 18px; right: auto; color: var(--brand); }
.btn-feedback:hover { border-color: var(--brand); }

.feedback-type-row { display: flex; gap: 8px; margin: 14px 0 10px; }
.feedback-type {
  flex: 1; font: inherit; font-size: 13.5px; font-weight: 700;
  padding: 9px 6px; border-radius: 10px; border: 1.5px solid var(--line);
  background: var(--bg); color: var(--muted); cursor: pointer;
  transition: color .15s ease, border-color .15s ease, background .15s ease;
}
.feedback-type:hover { color: var(--ink); }
.feedback-type.is-active { color: var(--brand); border-color: var(--brand); background: color-mix(in srgb, var(--brand) 10%, transparent); }
.feedback-textarea {
  width: 100%; min-height: 100px; font: inherit; font-size: 16px;
  padding: 12px 14px; border: 1.5px solid var(--line); border-radius: 12px;
  background: var(--bg); color: var(--ink); resize: vertical;
  margin-bottom: 12px;
}
.feedback-textarea:focus { border-color: var(--brand); outline: none; }
.feedback-shake-note {
  font-size: 13px; color: var(--brand); background: color-mix(in srgb, var(--brand) 10%, transparent);
  border-radius: 10px; padding: 9px 12px; margin: -4px 0 12px;
}
/* Opt-in row for shake-to-report. Only rendered on iOS, and only while the
   permission hasn't been granted — see renderShakeOptin() in app.js. */
.shake-optin {
  display: flex; align-items: center; gap: 12px;
  margin: -4px 0 12px; padding: 11px 13px;
  border: 1px solid var(--line); border-radius: 12px;
  background: color-mix(in srgb, var(--card) 70%, transparent);
}
.shake-optin b { display: block; font-size: 13.5px; color: var(--ink); }
.shake-optin small { display: block; margin-top: 3px; font-size: 12px; line-height: 1.45; color: var(--muted); }
.btn-mini-action {
  flex: 0 0 auto; font: inherit; font-size: 13px; font-weight: 700;
  color: #fff; background: linear-gradient(120deg, var(--pos), var(--logo-2));
  border: 0; border-radius: 999px; padding: 9px 16px; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  transition: transform .15s ease, opacity .15s ease;
}
.btn-mini-action:hover { transform: translateY(-1px); }
.btn-mini-action:active { transform: scale(.95); }
.btn-mini-action:disabled { opacity: .6; cursor: wait; transform: none; }

.feedback-direct { text-align: center; font-size: 13.5px; color: var(--muted); margin-top: 12px; }
.feedback-direct a { color: var(--brand); font-weight: 700; }
html[data-theme="light"] .ico-sun  { opacity: 1; transform: rotate(0) scale(1); }
html[data-theme="light"] .ico-moon { opacity: 0; transform: rotate(90deg) scale(.4); }
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .ico-sun  { opacity: 1; transform: rotate(0) scale(1); }
  :root:not([data-theme="dark"]) .ico-moon { opacity: 0; transform: rotate(90deg) scale(.4); }
}

.screen { max-width: 1240px; margin: 0 auto; padding: 8px clamp(16px, 4vw, 40px) 48px; }

/* ── Hero ── */
.hero { text-align: center; padding: clamp(12px, 3vw, 28px) 0 24px; animation: rise .6s ease both; }
.hero h1 { font-size: clamp(36px, 7.5vw, 64px); font-weight: 700; letter-spacing: -.03em; line-height: 1.1; }
.hero em {
  font-style: normal;
  background: linear-gradient(90deg, #2dd4bf, #38bdf8, #a855f7, #ef4444, #fb923c, #2dd4bf);
  background-size: 300% 100%;
  -webkit-background-clip: text; background-clip: text; color: transparent;
  animation: hero-hue 8s ease-in-out infinite;
}
@keyframes hero-hue {
  0%, 100% { background-position: 0% 50%; }
  50% { background-position: 100% 50%; }
}
/* Same treatment as .hero em, colour stops reversed (teal→orange→red→
   purple→blue→teal instead of teal→blue→purple→red→orange→teal) — and
   the exact same animation name/duration/easing, not just a matching one,
   so the two words are guaranteed to stay in lockstep rather than drifting
   out of sync from separately-timed animations. */
.hero-sql {
  background: linear-gradient(90deg, #2dd4bf, #fb923c, #ef4444, #a855f7, #38bdf8, #2dd4bf);
  background-size: 300% 100%;
  -webkit-background-clip: text; background-clip: text; color: transparent;
  animation: hero-hue 8s ease-in-out infinite;
}
.hero .sub { max-width: 620px; margin: 14px auto 0; color: var(--muted); font-size: 18px; }
/* The full worked-example clause (DELETE/LEFT JOIN/JOIN ON) is useful detail
   on desktop but reads as a wall of text on a phone screen, right under a
   huge gradient headline — hidden below 620px, leaving a short one-line
   pitch. Kept in the DOM (not removed) so it's still there for anyone who
   resizes up, and for search engines / screen readers on desktop. */
@media (max-width: 620px) { .sub-detail { display: none; } }

/* ── Hero demo — a self-playing, clearly-labelled loop showing Diagnose /
   Auto-fix / Optimize, sitting BELOW the real hero copy in its own bordered
   "window" (never blended behind the headline) so it can't be mistaken for
   the real app. Purely decorative: aria-hidden, no pointer events on its
   fake controls, gated by the same reduced-motion kill switch as the rest
   of the app (see the global `* { animation: none !important }` rule). */
/* overflow:hidden is load-bearing, not cosmetic: .hero-demo-glow bleeds
   40px past this box on each side via negative inset, and it animates
   (transform: translate/scale) continuously. Left unclipped, that bleed
   was wider than the mobile viewport and kept changing every frame — on
   iOS Safari specifically, that makes the browser's own zoom-to-fit
   recompute in real time, which reads as the whole page pulsing in and
   out and locks pinch-zoom at whatever shrunk scale it last settled on.
   Clipping it here costs nothing visually (the gradients already fade to
   transparent well inside the box) and makes the page's layout width
   genuinely constant regardless of the glow's animation. */
/* NOT overflow-x:hidden on .hero-demo itself — browsers compute a
   "visible" overflow-y as auto (not truly visible) the moment overflow-x
   is non-visible, so that still clipped the "preview" badge's negative
   top offset (verified: its rendered top sat above .hero-demo's own box).
   Instead the glow gets its own clipping wrapper, sized to exactly match
   .hero-demo's box, so only the glow's bleed is clipped — .hero-demo
   itself stays overflow:visible and the badge is never touched. */
.hero-demo { position: relative; max-width: 560px; margin: 40px auto 0; }
.hero-demo-glow-wrap { position: absolute; inset: 0; overflow: hidden; pointer-events: none; z-index: 0; }
.hero-demo-glow {
  position: absolute; inset: -80px -40px auto -40px; height: 320px;
  background:
    radial-gradient(circle at 30% 30%, color-mix(in srgb, var(--brand) 30%, transparent), transparent 60%),
    radial-gradient(circle at 70% 60%, color-mix(in srgb, var(--accent) 22%, transparent), transparent 60%);
  filter: blur(36px); animation: hero-demo-drift 10s ease-in-out infinite alternate;
}
@keyframes hero-demo-drift { 0% { transform: translate(0, 0) scale(1); } 100% { transform: translate(16px, -12px) scale(1.06); } }

.hero-demo-window {
  position: relative; border-radius: 16px; padding: 1.5px;
  background: linear-gradient(120deg,
    color-mix(in srgb, var(--brand) 55%, transparent),
    color-mix(in srgb, var(--accent) 55%, transparent),
    color-mix(in srgb, var(--brand-2) 55%, transparent));
  background-size: 300% 300%; animation: hero-demo-border 6s ease infinite;
  box-shadow: var(--shadow);
}
@keyframes hero-demo-border { 0%, 100% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } }

.hero-demo-inner { position: relative; background: var(--code-bg); border-radius: 14.5px; }
.hero-demo-badge {
  position: absolute; top: -13px; right: 14px; z-index: 1; background: var(--card); border: 1px solid var(--line);
  color: var(--muted); font-size: 11px; padding: 3px 10px; border-radius: 999px; letter-spacing: .02em;
}
.hero-demo-titlebar {
  display: flex; align-items: center; gap: 8px; padding: 11px 14px; background: color-mix(in srgb, var(--card) 90%, transparent);
  border-bottom: 1px solid var(--line); border-radius: 14.5px 14.5px 0 0; overflow: hidden;
}
.hero-demo-titledot { width: 10px; height: 10px; border-radius: 50%; }

.hero-demo-tabs { display: flex; gap: 4px; margin-left: 8px; }
.hero-demo-tab {
  font: inherit; font-size: 12px; font-weight: 700; color: var(--muted); background: transparent; border: 0;
  padding: 5px 10px; border-radius: 8px; cursor: pointer; transition: color .2s ease, background .2s ease;
}
.hero-demo-tab:hover { color: var(--ink); background: color-mix(in srgb, var(--brand) 10%, transparent); }
.hero-demo-tab.active { color: var(--brand); background: color-mix(in srgb, var(--brand) 16%, transparent); }

/* Fixed-height stage — every scene renders inside the same box size, so the
   card never grows/shrinks/jumps as content changes between scenes. */
.hero-demo-body { position: relative; height: 176px; overflow: hidden; }
.hero-demo-stage {
  position: absolute; inset: 0; display: flex; align-items: center; padding: 0 18px;
  opacity: 1; transform: translateY(0); transition: opacity .45s ease, transform .45s ease;
}
.hero-demo-stage.out { opacity: 0; transform: translateY(-4px); }

.hero-demo-pane { flex: 1 1 0; min-width: 0; }
.hero-demo-pane-before { position: relative; padding-right: 16px; }
.hero-demo-pane-after { padding-left: 16px; min-height: 60px; display: flex; align-items: center; }
.hero-demo-kicker { font-size: 10px; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); font-weight: 800; margin-bottom: 8px; }
.hero-demo-sql { font: 13px/1.65 ui-monospace, "SF Mono", monospace; color: var(--code-ink); white-space: pre-wrap; margin: 0; }
.hero-demo-sql .bad { color: var(--code-ink); transition: color .3s ease; }
.hero-demo-sql .bad.flag { color: var(--neg); }

/* A light beam sweeps left-to-right across the query — reads as "actively
   scanning," not a static typo waiting to be hovered. */
.hero-demo-scan {
  position: absolute; top: -6px; bottom: -6px; left: -40%; width: 40%; pointer-events: none;
  background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--accent) 30%, transparent), transparent);
}
.hero-demo-scan.run { animation: hero-demo-sweep 1s cubic-bezier(.4,0,.2,1) both; }
@keyframes hero-demo-sweep {
  0% { left: -40%; opacity: 0; }
  12% { opacity: 1; }
  85% { opacity: 1; }
  100% { left: 100%; opacity: 0; }
}

/* Three chevrons chase each other to signal "becomes" — symmetric,
   continuous, unlike a single static → glyph. */
.hero-demo-arrow { display: flex; gap: 1px; font-size: 15px; font-weight: 800; color: var(--brand); line-height: 1; }
.hero-demo-arrow span { opacity: .2; animation: hero-demo-chase 1.4s ease-in-out infinite; }
.hero-demo-arrow span:nth-child(2) { animation-delay: .16s; }
.hero-demo-arrow span:nth-child(3) { animation-delay: .32s; }
@keyframes hero-demo-chase { 0%, 100% { opacity: .2; } 50% { opacity: 1; } }

.hero-demo-result { display: flex; align-items: flex-start; gap: 8px; font-size: 12.5px; line-height: 1.5;
  opacity: 0; transform: translateX(10px); transition: opacity .45s ease, transform .45s ease; }
.hero-demo-result.show { opacity: 1; transform: translateX(0); }
.hero-demo-result b { font-weight: 800; }
.hero-demo-result.neg { color: var(--neg); }
.hero-demo-result.pos { color: var(--pos); }
.hero-demo-result.accent { color: var(--accent); }

/* Progress rail — one hairline track per scene, filling smoothly across
   its hold time so the auto-advance reads as deliberate pacing, not a
   random jump cut. */
.hero-demo-dots { display: flex; justify-content: center; gap: 8px; padding: 0 0 16px; }
.hero-demo-dot-track { width: 30px; height: 3px; border-radius: 999px; background: var(--line); overflow: hidden; }
.hero-demo-dot-fill { display: block; height: 100%; width: 0%; border-radius: 999px; background: linear-gradient(120deg, var(--brand), var(--accent)); }
.hero-demo-dot-fill.run { animation: hero-demo-fill 2.9s linear forwards; }
.hero-demo-dot-fill.done { width: 100%; }
@keyframes hero-demo-fill { from { width: 0%; } to { width: 100%; } }
/* Trust ticker — the same 5 credibility stats that used to take two full
   rows (~120px), now a single 40px-tall strip that scrolls itself, like a
   stock ticker or a conference sponsor bar. Reads as "alive" (a static
   trust bar reads as decoration; a moving one reads as data), and every
   stat is still shown in full — none dropped, none behind a click. The
   track is the 5 pills duplicated once so the loop point is invisible;
   edges fade via mask so pills don't visibly clip at the container edge.
   Pauses on hover/focus so a reader can actually read one without racing
   it, and the site's global `prefers-reduced-motion` kill switch already
   freezes the animation for anyone who needs that — it just holds on the
   first pass (pills 1–5), never a half-scrolled mid-loop frame, since the
   animation starts at translateX(0). */
.trust-ticker {
  position: relative; max-width: 620px; margin: 26px auto 0; overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 10%, #000 90%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 10%, #000 90%, transparent);
}
.trust-ticker-track { display: flex; gap: 10px; width: max-content; animation: trust-scroll 24s linear infinite; }
.trust-ticker:hover .trust-ticker-track, .trust-ticker:focus-within .trust-ticker-track { animation-play-state: paused; }
@keyframes trust-scroll { from { transform: translateX(0); } to { transform: translateX(-50%); } }

.trust-pill {
  flex: none; display: inline-flex; align-items: center; gap: 6px;
  font-size: 12.5px; font-weight: 700; color: var(--muted); white-space: nowrap;
  background: var(--card); border: 1px solid var(--line); border-radius: 999px; padding: 8px 14px;
}
.trust-pill b {
  font-size: 14px; font-weight: 800;
  background: linear-gradient(100deg, var(--logo-1), var(--logo-2));
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
.trust-pill.ok b { background: none; -webkit-text-fill-color: unset; color: var(--pos); }

/* ── Cards ── */
.card {
  background: var(--card); border-radius: var(--radius); box-shadow: var(--shadow);
  padding: clamp(16px, 3vw, 24px); margin-top: 14px; min-width: 0;
  max-width: 900px; margin-left: auto; margin-right: auto;
  animation: rise .5s ease both;
}
/* Every card heading gets a translucent pill so it reads as a label at a
   glance, not just slightly-bigger body text. */
.card h2 {
  display: inline-block; font-size: 17px; margin-bottom: 10px;
  padding: 4px 12px; border-radius: 999px;
  background: color-mix(in srgb, var(--ink) 6%, transparent);
}

/* Cleaned-up/Optimizer results: these are answers, not neutral info — a
   green/teal gradient panel (not flat colour) sets them apart, and the
   grid goes wide + side-by-side instead of capping to .card's 900px, so
   large queries actually get to use the empty space either side on wide
   screens. Wraps to one column below ~760px combined width. */
.answer-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(360px, 1fr));
  align-items: start; /* default `stretch` forces both cards to the height
    of the taller one, leaving dead empty space in the shorter card instead
    of each sizing to its own content. */
  gap: 14px; margin-top: 14px; max-width: none;
}
/* margin-left/right:auto from .card (used to center non-grid cards in the
   widened .screen) makes a grid item shrink to fit-content instead of
   stretching to fill its column — reset both explicitly, not just
   max-width, or the shorter card's shorter text produces a narrower box. */
.answer-grid > .card { margin: 0; max-width: none; width: auto; }
.answer-card {
  background: linear-gradient(135deg,
    color-mix(in srgb, var(--pos) 12%, var(--card)),
    color-mix(in srgb, #14b8a6 16%, var(--card)));
  border: 1px solid color-mix(in srgb, var(--pos) 28%, transparent);
}
.answer-card h2 { background: color-mix(in srgb, var(--pos) 16%, transparent); color: var(--pos); }
/* A single answer card spanning the full wide grid looks huge/hollow —
   cap it to the normal card width and center it, same as every other card. */
.answer-grid.single { grid-template-columns: minmax(0, 900px); justify-content: center; }

/* Translated output gets its own distinct-but-cohesive theme (cyan/blue,
   the app's --accent) — a third kind of "answer" alongside the green
   cleaned-up/optimizer pair, not neutral info. */
#translated-card {
  max-width: none;
  background: linear-gradient(135deg,
    color-mix(in srgb, var(--accent) 12%, var(--card)),
    color-mix(in srgb, var(--brand) 12%, var(--card)));
  border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
}
#translated-card h2 { background: color-mix(in srgb, var(--accent) 18%, transparent); color: var(--accent); }
/* Shown instead of a byte-identical copy of the cleaned-up SQL — see the
   `identical` branch in app.js. Deliberately a plain sentence, not a code
   block, so the card collapses to one line rather than repeating ~200px of
   text the reader has already just read directly above. */
.translated-same { margin: 0; font-size: 14.5px; }

/* ── Mode toggle (Diagnose / Compare) ── */
.mode-toggle {
  display: flex; gap: 6px; width: max-content; margin: 0 auto 14px;
  background: color-mix(in srgb, var(--ink) 5%, transparent);
  border-radius: 999px; padding: 5px;
}
.mode-btn {
  font: inherit; font-size: 15px; font-weight: 700; color: var(--muted);
  background: none; border: 0; border-radius: 999px; padding: 9px 20px; cursor: pointer;
  transition: background .25s ease, color .2s ease, transform .3s var(--spring);
}
.mode-btn:hover { color: var(--ink); }
.mode-btn.active { background: var(--card); color: var(--ink); box-shadow: var(--shadow); }
.mode-btn:active { transform: scale(.95); }

/* ── Compare card ── */
.compare-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 14px; }
.compare-col textarea { min-height: 220px; }

/* ── Input card ── */
.input-card { animation-delay: .1s; }
.toolbar { display: flex; gap: 12px; flex-wrap: wrap; align-items: center; margin-bottom: 12px; }
.tool { display: flex; align-items: center; gap: 8px; font-size: 15px; color: var(--muted); font-weight: 600; }
select {
  font: inherit; font-weight: 600; color: var(--ink);
  background: var(--bg); border: 1.5px solid var(--line); border-radius: 12px;
  padding: 8px 12px; cursor: pointer;
  transition: transform .2s var(--spring); /* not border-color — see .btn-privacy's comment above */
}
select:hover { transform: scale(1.03); }
select:focus { border-color: var(--brand); outline: none; }

textarea {
  width: 100%; min-height: 190px; resize: vertical;
  font: 500 16px/1.6 ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  color: var(--ink); background: var(--bg);
  border: 1.5px solid var(--line); border-radius: 14px; padding: 14px;
  transition: box-shadow .2s ease; /* not border-color — see .btn-privacy's comment above */
}
textarea:focus {
  border-color: var(--brand); outline: none;
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--brand) 18%, transparent);
}

/* ── CodeMirror: reskinned to match the plain-textarea look above, rather
   than using a prebuilt CodeMirror theme (keeps light/dark in lockstep
   with the rest of the app's --bg/--ink/--line variables instead of a
   second, separately-maintained colour scheme). */
.CodeMirror {
  height: auto; min-height: 190px;
  font: 500 16px/1.6 ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  color: var(--ink); background: var(--bg);
  border: 1.5px solid var(--line); border-radius: 14px; padding: 14px 14px 14px 6px;
  transition: box-shadow .2s ease; /* not border-color — see .btn-privacy's comment above */
}
.CodeMirror-focused { border-color: var(--brand); box-shadow: 0 0 0 4px color-mix(in srgb, var(--brand) 18%, transparent); }
.CodeMirror-gutters { background: transparent; border-right: 1px solid var(--line); }
.CodeMirror-linenumber { color: var(--muted); }
.CodeMirror-cursor { border-left-color: var(--ink); }
.CodeMirror-selected { background: color-mix(in srgb, var(--brand) 25%, transparent); }
.cm-s-default .cm-keyword { color: var(--brand); font-weight: 700; }
.cm-s-default .cm-string { color: var(--pos); }
.cm-s-default .cm-number { color: var(--warn); }
.cm-s-default .cm-comment { color: var(--muted); font-style: italic; }
.cm-s-default .cm-operator { color: var(--accent); }
.compare-col .CodeMirror { min-height: 220px; }

/* ── Buttons: iOS-springy ── */
.actions { display: flex; gap: 12px; align-items: center; margin-top: 14px; flex-wrap: wrap; }
.btn-hero {
  /* justify-content matters only when something forces the button wider than
     its content (the full-width feedback send button, .price-tier's buttons);
     at natural width it's a no-op. Without it the label hugs the left edge
     and the button reads as broken. */
  display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  font: inherit; font-size: 18px; font-weight: 800; color: var(--brand-ink);
  background: linear-gradient(120deg, var(--brand), var(--brand-2));
  border: 0; border-radius: 16px; padding: 15px 28px; cursor: pointer;
  box-shadow: 0 10px 26px -8px color-mix(in srgb, var(--brand) 70%, transparent);
  transition: transform .35s var(--spring), box-shadow .25s ease, filter .2s ease;
}
.btn-hero:hover { transform: translateY(-2px) scale(1.04); filter: brightness(1.05); }
.btn-hero:active { transform: scale(.94); box-shadow: 0 4px 12px -6px color-mix(in srgb, var(--brand) 70%, transparent); }
.btn-hero:disabled { opacity: .6; transform: none; cursor: wait; }
/* Same size/shadow/spring treatment as .btn-hero, but forced green +
   white text regardless of theme (unlike --brand-ink, which flips to a
   dark ink in light mode) — used for the "Apply safe fixes" action,
   which should read at the same visual weight as "Get API key". */
.btn-hero-green {
  color: #fff !important;
  background: linear-gradient(120deg, var(--pos), var(--logo-2));
  box-shadow: 0 10px 26px -8px color-mix(in srgb, var(--pos) 70%, transparent);
}
.btn-hero-green:hover { box-shadow: 0 10px 26px -8px color-mix(in srgb, var(--pos) 70%, transparent); }
.btn-hero-green:active { box-shadow: 0 4px 12px -6px color-mix(in srgb, var(--pos) 70%, transparent); }
.pulse-dot {
  width: 10px; height: 10px; border-radius: 50%; background: currentColor;
  animation: pulse 1.6s ease-in-out infinite;
}
.btn-ghost {
  font: inherit; font-weight: 600; color: var(--muted);
  background: none; border: 0; cursor: pointer; padding: 10px 14px; border-radius: 12px;
  transition: transform .3s var(--spring), background .2s ease, color .2s ease;
}
.btn-ghost:hover { background: color-mix(in srgb, var(--muted) 10%, transparent); color: var(--ink); transform: scale(1.05); }
.btn-ghost:active { transform: scale(.92); }
.btn-mini {
  font: inherit; font-size: 14px; font-weight: 700; color: var(--brand);
  background: color-mix(in srgb, var(--brand) 12%, transparent);
  border: 0; border-radius: 999px; padding: 8px 16px; cursor: pointer;
  transition: transform .3s var(--spring), background .2s ease;
}
.btn-mini:hover { transform: scale(1.07); background: color-mix(in srgb, var(--brand) 20%, transparent); }
.btn-mini:active { transform: scale(.9); }

/* Copy-checkmark morph: label and check occupy the same cell (grid, not
   flex — flex would leave a gap where the shrunk-out label used to be). */
.btn-copy { display: inline-grid; }
.btn-copy .copy-label, .btn-copy .copy-check {
  grid-area: 1 / 1;
  transition: opacity .22s cubic-bezier(.16,1,.3,1), transform .22s cubic-bezier(.16,1,.3,1);
}
.btn-copy .copy-check { opacity: 0; transform: scale(.4); justify-self: center; }
.btn-copy.copied { background: color-mix(in srgb, var(--pos) 18%, transparent); color: var(--pos); }
.btn-copy.copied .copy-label { opacity: 0; transform: scale(.5); }
.btn-copy.copied .copy-check { opacity: 1; animation: copy-check-bounce .34s cubic-bezier(.34,1.56,.64,1) both; }
@keyframes copy-check-bounce {
  0% { transform: scale(.4); }
  60% { transform: scale(1.2); }
  100% { transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .btn-copy .copy-label, .btn-copy .copy-check { transition: opacity .01s linear; animation: none !important; transform: none !important; }
}
/* Same compact .btn-mini footprint, bright gradient + forced white text
   like .btn-hero-green — Load/Delete need to read as distinct, deliberate
   actions in a row of otherwise-neutral buttons. Sky blue here is
   deliberately its own color (#0ea5e9/#38bdf8), not var(--brand) — the
   app's indigo/violet brand color reads too close to the Diagnose button
   at this size, so Load needs a genuinely different hue to look distinct. */
.btn-mini-blue, .btn-mini-red {
  color: #fff !important;
}
.btn-mini-blue {
  background: linear-gradient(120deg, #0ea5e9, #38bdf8);
  box-shadow: 0 6px 16px -6px rgba(14, 165, 233, .6);
}
.btn-mini-blue:hover { background: linear-gradient(120deg, #0ea5e9, #38bdf8); box-shadow: 0 6px 16px -6px rgba(14, 165, 233, .7); }
.btn-mini-red {
  background: linear-gradient(120deg, var(--neg), #b91c1c);
  box-shadow: 0 6px 16px -6px color-mix(in srgb, var(--neg) 60%, transparent);
}
.btn-mini-red:hover { background: linear-gradient(120deg, var(--neg), #b91c1c); box-shadow: 0 6px 16px -6px color-mix(in srgb, var(--neg) 70%, transparent); }

/* "Try a sample" — same FLAT layer as the default .btn-mini (translucent
   tint, no gradient), just green instead of brand-purple. */
.btn-mini-green-flat {
  color: var(--pos);
  background: color-mix(in srgb, var(--pos) 12%, transparent);
}
.btn-mini-green-flat:hover { background: color-mix(in srgb, var(--pos) 20%, transparent); }

.error {
  margin-top: 12px; color: var(--neg); font-weight: 600;
  background: color-mix(in srgb, var(--neg) 10%, transparent);
  border-radius: 12px; padding: 12px 16px;
  animation: shake .4s ease;
}

/* ── Verdict / score ring ── */
/* The sticky topbar (60px) sits on top of the viewport, so scrollIntoView's
   block:"start" lands the score ring half-hidden underneath it unless we
   tell the browser to leave room for it. */
#results { scroll-margin-top: 76px; }
.verdict-card { display: flex; align-items: center; gap: clamp(16px, 4vw, 30px); }
.score-ring-wrap { position: relative; width: 120px; height: 120px; flex: none; }
.score-ring { width: 120px; height: 120px; transform: rotate(-90deg); }
.ring-bg, .ring-val { fill: none; stroke-width: 11; stroke-linecap: round; }
.ring-bg { stroke: var(--line); }
.ring-val {
  stroke: var(--brand);
  stroke-dasharray: 326.7; stroke-dashoffset: 326.7;
  transition: stroke-dashoffset 1.1s cubic-bezier(.22, 1, .36, 1), stroke .4s ease;
}
.score-num {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 1px; line-height: 1;
}
.score-num b { font-size: 34px; font-weight: 800; font-variant-numeric: tabular-nums; line-height: 1; }
.score-num small { color: var(--muted); font-weight: 600; font-size: 12.5px; line-height: 1; }
.verdict-text h2 { font-size: clamp(20px, 3.4vw, 26px); }

/* ── Findings ── */
.findings { list-style: none; }
.diff-list { margin-top: 10px; }
.diff-list li { padding: 8px 4px; font-size: 14px; }
.findings li {
  display: flex; gap: 12px; align-items: flex-start;
  padding: 13px 4px; border-bottom: 1px solid var(--line);
  animation: rise .45s ease both;
  border-radius: 10px;
  transition: transform .3s cubic-bezier(.16,1,.3,1), background .2s ease, box-shadow .3s cubic-bezier(.16,1,.3,1);
}
.findings li:hover {
  transform: translateY(-2px);
  background: color-mix(in srgb, var(--ink) 3%, transparent);
  box-shadow: 0 6px 16px rgba(0,0,0,.08);
}
.findings li:last-child { border-bottom: 0; }
.sev {
  flex: none; font-size: 12px; font-weight: 800; letter-spacing: .04em;
  padding: 4px 11px; border-radius: 999px; margin-top: 2px; text-transform: uppercase;
}
.sev.high   { background: color-mix(in srgb, var(--neg) 14%, transparent);  color: var(--neg); }
.sev.medium { background: color-mix(in srgb, var(--warn) 16%, transparent); color: var(--warn); }
.sev.low    { background: color-mix(in srgb, var(--accent) 14%, transparent); color: var(--accent); }
.findings b { display: block; margin-bottom: 2px; }
.findings p { color: var(--muted); font-size: 15.5px; }

.noqa-note { margin-top: 12px; font-size: 14.5px; }
.autofix-banner {
  display: flex; justify-content: space-between; align-items: center; gap: 14px; flex-wrap: wrap;
  margin-top: 16px; padding: 14px 16px; border-radius: 14px;
  background: color-mix(in srgb, var(--pos) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--pos) 24%, transparent);
}
.autofix-banner p { font-size: 15px; }
.autofix-banner .muted { font-size: 14px; }

/* ── Schema input (optional) ── */
.schema-section { margin: -2px 0 14px; animation: rise .3s ease both; }
.schema-label { display: block; font-size: 14.5px; color: var(--muted); font-weight: 600; margin-bottom: 8px; }
.schema-label code { font-size: 13px; }
.schema-textarea { min-height: 90px; font-size: 16px; }
.schema-warning { margin-top: 8px; color: var(--warn); }
.schema-profile-row { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin-top: 10px; }
.schema-profile-row select, .schema-profile-row input {
  font: inherit; font-size: 16px; color: var(--ink); background: var(--bg);
  border: 1.5px solid var(--line); border-radius: 10px; padding: 6px 10px;
}
.schema-profile-row input { flex: 1; min-width: 140px; }
.noqa-hint { margin-top: 8px; font-size: 13.5px; }
.noqa-hint code { font-size: 12.5px; }

/* ── Code blocks ── */
.code-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; }
.code-head h2 { margin: 0; }
.code {
  background: var(--code-bg); color: var(--code-ink);
  font: 500 14px/1.65 ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  border-radius: 14px; padding: 16px; overflow-x: auto; white-space: pre;
}
.syntax-msg { color: var(--neg); font-weight: 600; margin-bottom: 10px; }

/* ── Trust strip ── */
.why-card { margin-top: 26px; }
.why-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(255px, 1fr)); gap: 20px 26px; margin-top: 14px; }
.why-item b { display: block; margin-bottom: 5px; font-size: 15.5px; }
.why-item p { color: var(--muted); font-size: 14.5px; line-height: 1.55; margin: 0; }
.why-item em { font-style: normal; font-weight: 600; color: var(--ink); }

.trust { display: flex; justify-content: center; gap: clamp(16px, 4vw, 44px); flex-wrap: wrap; margin-top: 40px; animation: rise .6s .2s ease both; }
.trust-item { display: flex; gap: 10px; align-items: center; }
.trust-item span { font-size: 26px; width: 34px; text-align: center; flex: none; }
.trust-item small { display: block; color: var(--muted); }
@media (max-width: 620px) {
  .trust { flex-direction: column; align-items: center; gap: 16px; }
  .trust-item { width: 265px; }

  /* Shrink the hero demo on phones: it was tall enough (title bar + 176px
     stage + dot rail, plus the full sub-copy above it) to push the mode
     toggle down to exactly where the fixed feedback/theme buttons float,
     so the two visually collided on first paint. Stack the before/after
     panes instead of side-by-side (there's no room for two columns at
     this width) and drop the fixed stage height accordingly. */
  .hero-demo { margin-top: 24px; }
  .hero-demo-tabs { gap: 2px; }
  .hero-demo-tab { font-size: 11px; padding: 5px 7px; }
  .hero-demo-body { height: 220px; }
  .hero-demo-stage { flex-direction: column; align-items: stretch; gap: 10px; padding: 14px 16px; }
  .hero-demo-pane-before { padding-right: 0; }
  .hero-demo-pane-after { padding-left: 0; min-height: 0; }
  .hero-demo-arrow { transform: rotate(90deg); align-self: center; }
  .hero-demo-sql { font-size: 12px; }

  /* Stack the feedback button ABOVE the theme toggle instead of pinning it
     to the opposite corner. On a narrow screen the card's controls wrap onto
     their own rows and the last one ("+ Add schema") lands bottom-LEFT —
     directly under this button, which then ate the left ~30px of it (the
     button stayed usable via its middle/right, but the target was clipped).
     Page content is left-aligned, so the right gutter is the reliably empty
     one; both buttons live there on mobile and neither covers content.
     18 + 54 + 12 = one button height plus a 12px gap. */
  /* The email field is the one full-width row whose right edge sits close
     enough to the screen edge to pass directly under the fixed feedback/
     theme buttons as the page scrolls (they're position:fixed, so they
     never move — the input does). `max-width` alone was a no-op here since
     the input was already narrower than that cap via its container's own
     padding; force an explicit `width` so it's actually narrowed. */
  /* Specificity bump (not just source order) — the base .email-input rule
     with its own width:100% lives further down this file, after this media
     block, so an equal-specificity selector here would lose the cascade
     even while its media query matches. */
  .email-field .email-input { width: calc(100% - 64px); }

  .btn-feedback {
    left: auto; right: 18px;
    bottom: calc(84px + env(safe-area-inset-bottom));
  }
}

.muted { color: var(--muted); font-size: 15px; }
.foot {
  text-align: center; color: var(--muted); font-size: 14px;
  margin: 20px auto 6px; padding: 12px 20px; max-width: fit-content;
  background: color-mix(in srgb, var(--card) 60%, transparent);
  border: 1px solid var(--line); border-radius: 999px;
  backdrop-filter: blur(8px);
}

/* Cross-links to my other tools. Sits OUTSIDE the .foot pill on purpose:
   .foot is max-width:fit-content, so putting these long anchors inside it
   would stretch the pill across the page and wreck its shape. Kept the
   quietest element on the page — muted and smaller than the pill's own
   text — because a prominent row of outbound links reads as an ad on a
   trust-first tool. flex-wrap so the descriptive anchors (descriptive on
   purpose: that text is what search engines read) wrap on a phone rather
   than forcing a horizontal scroll. */
.foot-tools {
  text-align: center; margin: 0 auto 14px; padding: 0 16px;
  font-size: 12.5px; line-height: 1.6; color: var(--muted);
  display: flex; flex-wrap: wrap; justify-content: center;
  align-items: baseline; gap: 2px 6px;
}
.foot-tools a {
  color: var(--muted); font-weight: 600; text-decoration: underline;
  text-underline-offset: 2px; text-decoration-thickness: 1px;
  padding: 2px 4px; border-radius: 6px;
  transition: color .2s ease, background-color .2s ease;
}
.foot-tools a:hover { color: var(--brand); background: color-mix(in srgb, var(--brand) 10%, transparent); }

/* ── Pricing / API key purchase ── */
.pricing-card { animation-delay: .3s; }
.pricing-card p.muted { margin-bottom: 18px; font-size: 14.5px; }
.email-field { display: block; margin-bottom: 18px; font-size: 14.5px; font-weight: 600; }
.email-field > span:first-child { display: block; margin-bottom: 8px; color: var(--ink); }
.email-field .muted { font-weight: 500; }
.email-input {
  display: block; width: 100%; max-width: 340px;
  font: 500 16px/1.5 inherit; color: var(--ink); background: var(--bg);
  border: 1.5px solid var(--line); border-radius: 12px; padding: 11px 14px;
  transition: box-shadow .2s ease; /* not border-color — see .btn-privacy's comment earlier in this file */
}
.email-input::placeholder { color: var(--muted); }
.email-input:focus {
  border-color: var(--brand); outline: none;
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--brand) 18%, transparent);
}
.pricing-footnote { margin-top: 14px; margin-bottom: 0; font-size: 13px; }
.pricing-card a { color: var(--brand); font-weight: 700; text-decoration: none; }
.pricing-card a:hover { text-decoration: underline; }
.pricing-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; }
.price-tier {
  border: 1.5px solid var(--line); border-radius: 14px; padding: 20px 18px;
  background: var(--bg);
  text-align: center; display: flex; flex-direction: column; gap: 4px; align-items: center;
  transition: transform .3s var(--spring); /* not border-color — see .btn-privacy's comment above */
}
.price-tier:hover { transform: translateY(-3px); border-color: var(--brand); }
.price-tier h3 { font-size: 14px; font-weight: 800; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); }
.price-amount { font-size: 30px; font-weight: 800; }
.price-duration {
  font-size: 14px; font-weight: 700; color: var(--brand);
  background: color-mix(in srgb, var(--brand) 14%, transparent);
  border-radius: 999px; padding: 4px 12px; margin: 8px 0 12px;
}
.price-tier .btn-hero { font-size: 15px; padding: 10px 20px; width: 100%; justify-content: center; margin-top: 4px; }
.key-result {
  margin-top: 16px; padding: 16px 18px; border-radius: 12px;
  background: color-mix(in srgb, var(--warn) 14%, transparent);
  border: 2px solid var(--warn);
  animation: key-pulse 1.8s ease-in-out 2;
}
.key-warning {
  font-size: 16px; color: var(--ink); margin-bottom: 10px;
}
.key-warning b { color: var(--warn); }
.key-line {
  display: flex; align-items: center; gap: 10px; margin: 8px 0;
  background: var(--card); border: 1.5px solid var(--warn); border-radius: 10px; padding: 10px 12px;
}
.key-line code { flex: 1; overflow-x: auto; white-space: nowrap; font: 700 15px/1.5 ui-monospace, "SF Mono", Menlo, Consolas, monospace; }
.btn-copy-key-action { background: var(--warn); color: #1a1200; font-weight: 800; }
.btn-copy-key-action:hover { background: color-mix(in srgb, var(--warn) 85%, white); }
@keyframes key-pulse {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--warn) 45%, transparent); }
  50% { box-shadow: 0 0 0 8px color-mix(in srgb, var(--warn) 0%, transparent); }
}

/* ── Motion ── */
@keyframes rise { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: none; } }
@keyframes pulse { 0%, 100% { transform: scale(1); opacity: 1; } 50% { transform: scale(.55); opacity: .55; } }
@keyframes shake { 0%,100% { transform: none; } 25% { transform: translateX(-6px); } 75% { transform: translateX(6px); } }
