/* ==========================================================================
   empatheticbot.com — hand-written, no build step
   ========================================================================== */

/* ------------------------------ Fonts ------------------------------- */

@font-face {
  font-family: "Fraunces";
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url("assets/fonts/fraunces-latin.woff2") format("woff2");
}

@font-face {
  font-family: "Fraunces";
  font-style: italic;
  font-weight: 400 600;
  font-display: swap;
  src: url("assets/fonts/fraunces-latin-italic.woff2") format("woff2");
}

/* ------------------------------ Tokens ------------------------------ */

:root {
  color-scheme: light dark;

  --bg: #faf8f4;
  --bg-raised: #ffffff;
  --ink: #211d1a;
  --ink-soft: #625a52;
  --line: #e6dfd4;
  --accent: #c22417;
  --accent-bright: #da1212;
  --accent-soft: rgba(218, 18, 18, 0.07);
  --shadow: 0 1px 2px rgba(33, 29, 26, 0.05), 0 12px 32px -12px rgba(33, 29, 26, 0.12);
  --focus-ring: 0 0 0 2px var(--bg), 0 0 0 4px var(--accent);

  --font-display: "Fraunces", "Iowan Old Style", Georgia, serif;
  --font-body: system-ui, -apple-system, "Segoe UI", sans-serif;

  --space: clamp(4.5rem, 10vw, 8.5rem);
  --radius: 14px;
  --measure: 62ch;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #171412;
    --bg-raised: #201c19;
    --ink: #f1ede7;
    --ink-soft: #a89f94;
    --line: #332d28;
    --accent: #ff6a5e;
    --accent-bright: #ff5a4e;
    --accent-soft: rgba(255, 90, 78, 0.09);
    --shadow: 0 1px 2px rgba(0, 0, 0, 0.3), 0 12px 32px -12px rgba(0, 0, 0, 0.5);
  }

  #heart-gradient stop:first-child {
    stop-color: #ffb3ad;
  }

  #heart-gradient stop:last-child {
    stop-color: #ff453a;
  }

  #antenna-gradient stop:first-child {
    stop-color: #ff6a5e;
  }

  #antenna-gradient stop:last-child {
    stop-color: #a82b24;
    stop-opacity: 1;
  }

  #body-gradient stop:first-child {
    stop-color: #ff7a70;
  }

  #body-gradient stop:last-child {
    stop-color: #b92f28;
    stop-opacity: 1;
  }

  :where(.bot, .wordmark-bot, .wordmark-icon, .footer-bot, .thanks-bot, .error-bot) {
    filter: drop-shadow(0 2px 8px rgba(255, 90, 78, 0.18));
  }
}

/* ------------------------------ Reset ------------------------------- */

*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
}

html {
  scroll-behavior: smooth;
  scroll-padding-top: 5.5rem;
}

body {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: clamp(1.0125rem, 0.95rem + 0.3vw, 1.125rem);
  line-height: 1.7;
  -webkit-font-smoothing: antialiased;
}

img,
svg {
  display: block;
  max-width: 100%;
}

.svg-definitions {
  position: absolute;
}

::selection {
  background: var(--accent);
  color: #fff;
}

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

/* --------------------------- Typography ----------------------------- */

h1,
h2,
h3 {
  font-family: var(--font-display);
  font-weight: 500;
  line-height: 1.12;
  letter-spacing: -0.015em;
  text-wrap: balance;
}

h1 {
  font-size: clamp(2.4rem, 1.4rem + 4.5vw, 4.25rem);
  max-width: 24ch;
  margin-inline: auto;
}

h1 em,
h2 em {
  font-style: italic;
  color: var(--accent);
}

h2 {
  font-size: clamp(1.75rem, 1.2rem + 2.4vw, 2.75rem);
  max-width: 26ch;
}

h3 {
  font-size: 1.25rem;
  font-weight: 600;
}

p {
  text-wrap: pretty;
}

a {
  color: var(--accent);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
}

.kicker {
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 1rem;
}

.section-lede {
  font-size: 1.15em;
  color: var(--ink-soft);
  max-width: var(--measure);
  margin-top: 1.25rem;
}

/* ------------------------- Progress hairline ------------------------ */

.progress {
  position: fixed;
  inset: 0 0 auto 0;
  height: 2px;
  background: linear-gradient(90deg, var(--accent-bright), #ff6a00);
  transform-origin: 0 50%;
  transform: scaleX(0);
  z-index: 20;
}

@supports (animation-timeline: scroll()) {
  .progress {
    animation: grow linear both;
    animation-timeline: scroll(root);
  }

  @keyframes grow {
    from {
      transform: scaleX(0);
    }
    to {
      transform: scaleX(1);
    }
  }
}

/* ----------------------------- Header -------------------------------- */

.skip-link {
  position: absolute;
  top: 0.75rem;
  left: 0.75rem;
  z-index: 30;
  padding: 0.5rem 1rem;
  background: var(--bg-raised);
  border-radius: 999px;
  /* The shadow appears only on focus — on the base rule it bleeds into the
     viewport's top edge while the link is tucked away above it. */
  translate: 0 calc(-100% - 1.5rem);
  transition: translate 0.2s;
}

.skip-link:focus {
  translate: 0 0;
  box-shadow: var(--shadow);
}

.site-header {
  position: sticky;
  top: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  gap: 1.75rem;
  padding: 0.85rem clamp(1.25rem, 4vw, 2.5rem);
  background: color-mix(in srgb, var(--bg) 82%, transparent);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-bottom: 1px solid transparent;
  transition: border-color 0.3s;
}

.site-header.scrolled {
  border-bottom-color: var(--line);
}

.wordmark {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.1rem;
  color: var(--ink);
  text-decoration: none;
  margin-right: auto;
}

.wordmark-bot {
  width: 1.75rem;
  height: 1.75rem;
}

.desktop-navigation {
  display: flex;
  gap: 1.5rem;
}

.desktop-navigation a {
  font-size: 0.925rem;
  color: var(--ink-soft);
  text-decoration: none;
  background-image: linear-gradient(var(--accent), var(--accent));
  background-repeat: no-repeat;
  background-size: 0% 1.5px;
  background-position: 0 100%;
  padding-bottom: 2px;
  transition:
    background-size 0.25s,
    color 0.25s;
}

.desktop-navigation a:hover {
  color: var(--ink);
  background-size: 100% 1.5px;
}

.mobile-navigation {
  display: none;
  position: relative;
}

.mobile-navigation summary {
  list-style: none;
  padding: 0.45rem 0.8rem;
  color: var(--ink);
  background: var(--bg-raised);
  border: 1px solid var(--line);
  border-radius: 999px;
  cursor: pointer;
  font-size: 0.875rem;
  font-weight: 600;
  line-height: 1.4;
}

.mobile-navigation summary::-webkit-details-marker {
  display: none;
}

.mobile-navigation summary:focus-visible {
  outline: 2px solid transparent;
  box-shadow: var(--focus-ring);
}

.mobile-navigation summary::after {
  content: "\00a0\25be";
  color: var(--ink-soft);
}

.mobile-navigation[open] summary::after {
  content: "\00a0\25b4";
}

.mobile-navigation-panel {
  position: absolute;
  top: calc(100% + 0.65rem);
  right: 0;
  display: grid;
  gap: 0.2rem;
  width: min(13rem, calc(100vw - 2rem));
  padding: 0.6rem;
  background: var(--bg-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}

.mobile-navigation-panel a {
  padding: 0.55rem 0.7rem;
  color: var(--ink);
  border-radius: 0.55rem;
  font-size: 0.925rem;
  text-decoration: none;
}

.mobile-navigation-panel a:hover,
.mobile-navigation-panel a:focus-visible {
  background: var(--accent-soft);
}

.mobile-navigation-panel .mobile-navigation-cta {
  margin-top: 0.2rem;
  color: var(--bg);
  background: var(--ink);
  text-align: center;
}

.mobile-navigation-panel .mobile-navigation-cta:hover,
.mobile-navigation-panel .mobile-navigation-cta:focus-visible {
  background: var(--ink);
}

@media (max-width: 760px) {
  .site-header {
    gap: 0.75rem;
    padding-inline: 1rem;
  }

  .desktop-navigation,
  .button.header-cta {
    display: none;
  }

  /* Legal pages have no menu, so their header CTA fits on small screens. */
  .legal-header .button.header-cta {
    display: inline-block;
  }

  .mobile-navigation {
    display: block;
  }
}

/* ----------------------------- Buttons -------------------------------- */

.button {
  --shine-size: 8rem;
  --shine-x: 50%;
  --shine-y: 50%;

  position: relative;
  isolation: isolate;
  display: inline-block;
  padding: 0.8rem 1.6rem;
  background: var(--ink);
  color: var(--bg);
  font-weight: 600;
  font-size: 0.975rem;
  text-decoration: none;
  border: 1px solid var(--ink);
  border-radius: 999px;
  cursor: pointer;
  transition:
    transform 0.15s,
    box-shadow 0.15s,
    background-color 0.15s;
}

.button:hover {
  box-shadow: var(--shadow);
}

.button:focus-visible {
  outline: 2px solid transparent;
  box-shadow: var(--focus-ring);
}

.button:active {
  transform: scale(0.99);
}

.button-ghost {
  background: transparent;
  color: var(--ink);
  border-color: var(--line);
}

.button-small {
  padding: 0.5rem 1.1rem;
  font-size: 0.875rem;
}

.header-cta {
  white-space: nowrap;
}

.button-wide {
  width: 100%;
  text-align: center;
  font-family: var(--font-body);
}

/* ------------------------------ Hero ---------------------------------- */

.hero {
  position: relative;
  max-width: 56rem;
  margin-inline: auto;
  padding: clamp(3.5rem, 8vw, 6.5rem) 1.5rem clamp(4rem, 9vw, 7rem);
  text-align: center;
}

.hero-glow {
  position: absolute;
  inset: -20% 0 auto;
  height: 34rem;
  background: radial-gradient(closest-side, var(--accent-soft), transparent 70%);
  pointer-events: none;
  z-index: -1;
}

.bot {
  width: clamp(6.5rem, 14vw, 9rem);
  margin: 0 auto 1.75rem;
  animation: hover 2.4s cubic-bezier(0.45, 0, 0.55, 1) alternate infinite;
}

/* When you hover a call-to-action, every bot gets a little excited hop. */
.happy .bot,
.happy .wordmark-bot,
.happy .footer-bot {
  animation: bounce 0.6s cubic-bezier(0.28, 0.84, 0.42, 1) infinite;
}

.bot-heart {
  transform-origin: 72% 20%;
  animation: beat 1.5s cubic-bezier(0.21, -0.08, 0.54, 1.15) 0.4s infinite;
}

.bot-light-1 {
  transform-origin: 72% 28%;
  animation: light 2s cubic-bezier(0.21, -0.08, 0.54, 1.15) infinite;
}

.bot-light-2 {
  transform-origin: 72% 28%;
  animation: light 2s cubic-bezier(0.21, -0.08, 0.54, 1.15) 0.2s infinite;
}

/* Each eye is one stroked path: a hair-length line with round caps and an
   8-unit stroke renders as the classic round eye. JS animates the gaze
   (transform on .bot-eyes) and the happy morph (d/stroke-width attributes)
   — Safari has no CSS `d` property and root-level CSS variables restyle
   the whole document there, so neither effect is driven from CSS. */
.bot-eye {
  transform-box: view-box;
  transform-origin: 11px 18px;
}

.bot-eye-right {
  transform-origin: 21px 18px;
}

/* An occasional blink (class scheduled by JS on <body>). */
.blink .bot-eye {
  animation: blink 0.22s ease-in-out;
}

@keyframes blink {
  50% {
    transform: scaleY(0.08);
  }
}

@keyframes hover {
  from {
    transform: translateY(4px);
  }
  to {
    transform: translateY(-8px);
  }
}

@keyframes bounce {
  0%,
  100% {
    transform: translateY(0);
  }
  45% {
    /* Percentage is relative to each bot's own height, so the hop stays
       proportional whether it's the big hero or a tiny header bot. */
    transform: translateY(-12%);
  }
}

@keyframes light {
  from {
    opacity: 1;
    transform: none;
  }
  to {
    opacity: 0;
    transform: scale(1.5);
  }
}

@keyframes beat {
  0%,
  25%,
  50%,
  85%,
  100% {
    opacity: 1;
    transform: none;
  }
  35% {
    opacity: 0.7;
    transform: scale(1.06);
  }
  65% {
    opacity: 0.8;
    transform: scale(1.03);
  }
}

.eyebrow {
  font-size: 0.875rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: 1.25rem;
}

.lede {
  font-size: clamp(1.1rem, 1rem + 0.5vw, 1.3rem);
  color: var(--ink-soft);
  max-width: 46ch;
  margin: 1.5rem auto 0;
}

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.85rem;
  margin-top: 2.25rem;
}

.hero-terms {
  margin-top: 1.5rem;
  font-size: 0.875rem;
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
}

/* ---------------------------- Sections -------------------------------- */

.section {
  max-width: 62rem;
  margin-inline: auto;
  padding: var(--space) 1.5rem 0;
}

.section:last-of-type {
  padding-bottom: var(--space);
}

/* Content is visible by default. JavaScript opts into the hidden starting
   state only after IntersectionObserver is ready. */
.reveal-enabled .reveal {
  opacity: 0;
  translate: 0 18px;
  transition:
    opacity 0.6s ease,
    translate 0.6s ease;
}

.reveal-enabled .reveal.in {
  opacity: 1;
  translate: 0 0;
}

/* ------------------------- Reasons (01–04) ---------------------------- */

.reasons {
  list-style: none;
  padding: 0;
  margin-top: 3rem;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 24rem), 1fr));
  gap: 2.5rem 3rem;
}

.reason-number {
  display: block;
  font-family: var(--font-display);
  font-size: 1rem;
  font-style: italic;
  color: var(--accent);
  margin-bottom: 0.6rem;
}

.reason h3 {
  margin-bottom: 0.5rem;
}

.reason p {
  color: var(--ink-soft);
}

/* --------------------------- Compare cards ---------------------------- */

.compare {
  margin-top: 3rem;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 24rem), 1fr));
  gap: 1.25rem;
}

.compare-card {
  --shine-size: 15rem;
  --shine-x: 50%;
  --shine-y: 50%;

  position: relative;
  isolation: isolate;
  background: var(--bg-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.75rem;
  transition:
    transform 0.25s ease,
    box-shadow 0.25s ease,
    opacity 0.6s ease,
    translate 0.6s ease;
}

.compare-card:hover {
  box-shadow: var(--shadow);
}

.compare-card h3 {
  margin-bottom: 0.6rem;
}

.compare-card p {
  color: var(--ink-soft);
  font-size: 0.975rem;
}

/* ----------------------- Founder & selected work ---------------------- */

.founder {
  margin-top: 3rem;
  display: grid;
  grid-template-columns: minmax(9rem, 12rem) 1fr;
  align-items: start;
  gap: 2rem;
  padding: 2rem;
  background: var(--bg-raised);
  border: 1px solid var(--line);
  border-radius: calc(var(--radius) + 4px);
  box-shadow: var(--shadow);
}

.founder-portrait-frame {
  width: 100%;
  aspect-ratio: 1;
  border: 3px solid transparent;
  border-radius: 50%;
  background:
    linear-gradient(var(--bg-raised), var(--bg-raised)) padding-box,
    linear-gradient(var(--border-angle), var(--accent-bright), var(--line) 38% 62%, #ff6a00)
    border-box;
  box-shadow: var(--shadow);
  animation: border-spin 18s linear infinite;
}

.founder-portrait {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 50%;
}

.founder-copy h3 {
  font-size: 1.5rem;
}

.founder-copy p {
  margin-top: 0.75rem;
  color: var(--ink-soft);
}

.selected-work {
  margin-top: 3.5rem;
}

.selected-work > h3 {
  margin-bottom: 1.25rem;
  font-size: 1.5rem;
}

.work-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
  gap: 1.25rem;
}

.work-card {
  --shine-size: 15rem;
  --shine-x: 50%;
  --shine-y: 50%;

  position: relative;
  isolation: isolate;
  display: grid;
  grid-template-rows: auto auto 1fr auto;
  gap: 0.75rem;
  height: 100%;
  padding: 1.5rem;
  color: var(--ink-soft);
  background: var(--bg-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  text-decoration: none;
  transition:
    transform 0.25s ease,
    box-shadow 0.25s ease,
    border-color 0.25s ease,
    opacity 0.6s ease,
    translate 0.6s ease;
}

.work-card:hover {
  box-shadow: var(--shadow);
}

:where(.button, .compare-card, .work-card)::before {
  content: "";
  position: absolute;
  inset: -1px;
  z-index: 1;
  padding: 1.5px;
  border-radius: inherit;
  pointer-events: none;
  background: radial-gradient(
    var(--shine-size) circle at var(--shine-x) var(--shine-y),
    var(--accent-bright),
    #ff6a00 35%,
    transparent 72%
  );
  opacity: 0;
  transition: opacity 0.2s ease;
  -webkit-mask:
    linear-gradient(#fff 0 0) content-box,
    linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
  mask:
    linear-gradient(#fff 0 0) content-box,
    linear-gradient(#fff 0 0);
  mask-composite: exclude;
}

:where(.button, .compare-card, .work-card):is(:hover, :focus-visible)::before {
  opacity: 1;
}

@media (hover: hover) and (pointer: fine) {
  .button:hover {
    transform: scale(1.012);
  }
}

.work-card strong {
  color: var(--ink);
  font-family: var(--font-display);
  font-size: 1.25rem;
}

.work-mark {
  font-size: 1.75rem;
  line-height: 1;
}

.work-link {
  margin-top: auto;
  color: var(--accent);
  font-size: 0.9rem;
  font-weight: 600;
}

@media (max-width: 640px) {
  .founder {
    grid-template-columns: 1fr;
    text-align: center;
  }

  .founder-portrait-frame {
    width: 9rem;
    justify-self: center;
  }
}

/* -------------------------- Included list ----------------------------- */

.included-list {
  list-style: none;
  padding: 0;
  margin-top: 2.5rem;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
  gap: 0.9rem 2rem;
}

.included-list li {
  position: relative;
  padding-left: 2rem;
  color: var(--ink-soft);
}

.included-list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.42em;
  width: 1.1em;
  height: 1.1em;
  border-radius: 50%;
  background: var(--accent-soft);
}

.included-list li::after {
  content: "";
  position: absolute;
  left: 0.28em;
  top: 0.72em;
  width: 0.55em;
  height: 0.3em;
  border-left: 2px solid var(--accent);
  border-bottom: 2px solid var(--accent);
  transform: rotate(-45deg);
}

/* ------------------------------ Pricing ------------------------------- */

#pricing {
  text-align: center;
}

#pricing h2,
#pricing .section-lede {
  margin-inline: auto;
}

@property --border-angle {
  syntax: "<angle>";
  inherits: false;
  initial-value: 135deg;
}

.price-card {
  max-width: 26rem;
  margin: 3rem auto 0;
  padding: 2.5rem 2.25rem 2.25rem;
  border: 1.5px solid transparent;
  border-radius: calc(var(--radius) + 4px);
  box-shadow: var(--shadow);
  background:
    linear-gradient(var(--bg-raised), var(--bg-raised)) padding-box,
    linear-gradient(var(--border-angle), var(--accent-bright), var(--line) 38% 62%, #ff6a00)
    border-box;
  animation: border-spin 18s linear infinite;
}

@keyframes border-spin {
  to {
    --border-angle: 495deg;
  }
}

.price {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 0.5rem;
}

.price-amount {
  font-family: var(--font-display);
  font-size: 4.25rem;
  font-weight: 500;
  letter-spacing: -0.03em;
  line-height: 1;
  font-variant-numeric: lining-nums;
}

.price-period {
  color: var(--ink-soft);
}

.price-terms {
  margin-top: 0.9rem;
  font-size: 0.9rem;
  color: var(--ink-soft);
}

.price-list {
  list-style: none;
  padding: 0;
  margin: 1.75rem 0 2rem;
  text-align: left;
  display: grid;
  gap: 0.7rem;
  font-size: 0.95rem;
}

.price-list li {
  padding-left: 1.5rem;
  position: relative;
  color: var(--ink-soft);
}

.price-list li::before {
  content: "♥";
  position: absolute;
  left: 0;
  color: var(--accent);
  font-size: 0.8em;
  top: 0.25em;
}

.price-disclosure {
  max-width: 40rem;
  margin: 1.5rem auto 0;
  color: var(--ink-soft);
  font-size: 0.925rem;
}

.price-summary-link {
  margin-top: 0.65rem;
  font-size: 0.925rem;
}

.why-twelve {
  max-width: var(--measure);
  margin: 3.5rem auto 0;
  padding: 1.75rem 2rem;
  text-align: left;
  border-left: 3px solid var(--accent);
  background: var(--accent-soft);
  border-radius: 0 var(--radius) var(--radius) 0;
}

.why-twelve h3 {
  margin-bottom: 0.5rem;
}

.why-twelve p {
  color: var(--ink-soft);
  font-size: 0.975rem;
}

/* -------------------------------- FAQ --------------------------------- */

.faq-list {
  margin-top: 2.5rem;
  max-width: 46rem;
  display: grid;
  gap: 0.75rem;
}

.faq-list details {
  background: var(--bg-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 0 1.5rem;
  transition:
    opacity 0.6s ease,
    translate 0.6s ease,
    border-color 0.2s ease,
    box-shadow 0.2s ease;
  interpolate-size: allow-keywords;
}

.faq-list details:focus-within {
  border-color: var(--accent);
  box-shadow: var(--focus-ring);
}

.faq-list summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 1.1rem 0;
  font-weight: 600;
}

.faq-list summary:focus-visible {
  outline: 2px solid transparent;
}

.faq-list summary::-webkit-details-marker {
  display: none;
}

.faq-list summary::after {
  content: "";
  flex-shrink: 0;
  width: 0.6em;
  height: 0.6em;
  border-right: 2px solid var(--accent);
  border-bottom: 2px solid var(--accent);
  transform: rotate(45deg);
  transition: transform 0.3s;
  margin-top: -0.2em;
}

.faq-list details[open] summary::after {
  transform: rotate(225deg);
  margin-top: 0.2em;
}

.faq-list details::details-content {
  block-size: 0;
  overflow: hidden;
  transition:
    block-size 0.35s ease,
    content-visibility 0.35s allow-discrete;
}

.faq-list details[open]::details-content {
  block-size: auto;
}

.faq-list details p {
  padding-bottom: 1.25rem;
  color: var(--ink-soft);
  max-width: 58ch;
}

/* ----------------------------- Contact -------------------------------- */

.section-contact {
  max-width: 46rem;
}

.contact-form {
  margin-top: 2.75rem;
  display: grid;
  gap: 1.5rem;
}

.contact-fallback {
  margin-top: 1rem;
  color: var(--ink-soft);
  font-size: 0.95rem;
}

.turnstile-wrap {
  display: grid;
  gap: 0.5rem;
  justify-items: start;
}

/* The interaction-only widget stays out of sight AND out of the tab order
   (visibility, unlike opacity, removes it from keyboard focus). JS lifts
   this class the moment Turnstile says a visible challenge is required. */
.turnstile-pending {
  visibility: hidden;
}

.form-no-script {
  color: var(--ink-soft);
  font-size: 0.875rem;
}

.form-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
  gap: 1.5rem;
}

.field {
  display: grid;
  gap: 0.45rem;
  border: 0;
  padding: 0;
}

.field label {
  font-weight: 600;
  font-size: 0.95rem;
}

input,
select,
textarea {
  font: inherit;
  color: var(--ink);
  background: var(--bg-raised);
  border: 1px solid var(--line);
  border-radius: 1rem;
  padding: 0.7rem 0.9rem;
  width: 100%;
  transition:
    border-color 0.2s,
    box-shadow 0.2s;
}

input::placeholder,
textarea::placeholder {
  color: color-mix(in srgb, var(--ink-soft) 72%, transparent);
}

input:focus,
select:focus,
textarea:focus {
  outline: 2px solid transparent;
  outline-offset: 2px;
  border-color: var(--accent);
  box-shadow: var(--focus-ring);
}

textarea {
  resize: vertical;
}

.form-note {
  font-size: 0.925rem;
  color: var(--ink-soft);
  min-height: 1.5em;
  text-align: center;
}

.form-privacy {
  color: var(--ink-soft);
  font-size: 0.85rem;
  text-align: center;
}

@media (forced-colors: active) {
  .mobile-navigation summary:focus-visible,
  .button:focus-visible,
  .faq-list details:focus-within,
  input:focus,
  select:focus,
  textarea:focus {
    outline: 2px solid Highlight;
    outline-offset: 2px;
    box-shadow: none;
  }

  .faq-list summary:focus-visible {
    outline: none;
  }
}

.form-note.success {
  color: var(--accent);
  font-weight: 600;
}

/* ------------------------------ Footer -------------------------------- */

.site-footer {
  border-top: 1px solid var(--line);
  margin-top: var(--space);
  padding: 3.5rem 1.5rem 4rem;
  text-align: center;
  color: var(--ink-soft);
  display: grid;
  gap: 0.5rem;
  justify-items: center;
}

/* biome-ignore lint/style/noDescendingSpecificity: the .happy hop rule
   only sets animation, so it never actually competes with these base sizes. */
.footer-bot {
  width: 2.5rem;
  margin-bottom: 0.75rem;
}

.footer-small {
  font-size: 0.875rem;
}

/* ------------------------- Privacy and errors ------------------------- */

.legal-header {
  position: static;
}

.wordmark-icon {
  width: 1.75rem;
  height: 1.75rem;
}

.legal-main {
  width: min(100% - 3rem, 46rem);
  margin-inline: auto;
  padding-block: clamp(2.75rem, 7vw, 5rem) clamp(4rem, 10vw, 7rem);
}

.legal-content {
  display: grid;
  gap: 1.5rem;
}

.legal-content > .kicker {
  margin-bottom: -0.5rem;
}

.legal-content h1 {
  max-width: none;
  margin-inline: 0;
  font-size: clamp(2.25rem, 1.5rem + 3vw, 3.5rem);
}

.legal-content h2 {
  margin-top: 1rem;
  font-size: 1.4rem;
}

.legal-content p,
.legal-content li {
  color: var(--ink-soft);
}

.legal-content ul {
  display: grid;
  gap: 0.5rem;
  padding-left: 1.25rem;
}

.legal-meta {
  font-size: 0.925rem;
}

.agreement-note {
  margin-top: 1rem;
  padding: 1.5rem 1.75rem;
  background: var(--accent-soft);
  border-left: 3px solid var(--accent);
  border-radius: 0 var(--radius) var(--radius) 0;
}

.agreement-note h2 {
  margin-top: 0;
  margin-bottom: 0.75rem;
}

/* -------------------------- Thank-you page --------------------------- */

.thanks-main {
  width: min(100% - 3rem, 48rem);
  min-height: calc(100svh - 5rem);
  margin-inline: auto;
  padding-block: clamp(4rem, 10vw, 7rem);
  display: grid;
  align-content: center;
}

.thanks-content {
  display: grid;
  justify-items: center;
  gap: 1.4rem;
  text-align: center;
}

.thanks-bot {
  width: clamp(7.5rem, 18vw, 10rem);
  margin-bottom: 0.35rem;
  animation: hover 2.4s cubic-bezier(0.45, 0, 0.55, 1) alternate infinite;
}

.thanks-content .kicker {
  margin-bottom: -0.65rem;
}

.thanks-content h1 {
  max-width: 18ch;
}

.thanks-intro,
.thanks-reassurance {
  max-width: 40rem;
  color: var(--ink-soft);
}

.thanks-intro {
  font-size: 1.1em;
}

.thanks-next {
  width: 100%;
  max-width: 40rem;
  margin-block: 0.8rem 0.25rem;
  padding: clamp(1.5rem, 5vw, 2.25rem);
  text-align: left;
  background: var(--bg-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}

.thanks-next h2 {
  margin-bottom: 1rem;
  font-size: 1.45rem;
}

.thanks-next ol {
  display: grid;
  gap: 0.8rem;
  padding-left: 1.5rem;
  color: var(--ink-soft);
}

.thanks-next li::marker {
  color: var(--accent);
  font-weight: 700;
}

.thanks-content > .button {
  margin-top: 0.35rem;
}

/* Declared on both roots: the fixed fall stage sits outside .error-page, and
   the staged copy of the 404 sizes itself from these same tokens. */
.error-page,
.fall-stage {
  --bot-size: clamp(6.5rem, 14vw, 9rem);
  --code-size: clamp(4rem, 12vw, 7rem);
  --stack-gap: 1.25rem;
}

.error-page {
  /* Roughly a screenful, less the header above it. Nothing about the animation
     depends on this being exact any more — the falling 404 is clipped by the
     fixed stage, which is the viewport itself. */
  min-height: calc(100svh - 4rem);
  display: grid;
  align-content: center;
  justify-items: center;
  gap: var(--stack-gap);
  text-align: center;
}

.error-page .button {
  margin-top: 0.75rem;
}

/* The 404 pratfall, one shared 5s timeline in four synchronized tracks:
   the bot falls in (eyes open, watching the ground), slams into the 404 —
   which shakes — and winces; the 404 gives out and keeps falling off the
   bottom of the screen, returns from the top a beat later, and smacks him on
   the way down; his eyes cross into dizzy spirals and he does the classic
   video-game defeat spin. Runs once; the global reduced-motion rule collapses
   everything to the final resting pose (404 in place, bot knocked out).

   The falling 404 needs to leave the screen without its travel turning into
   scrollable overflow. Clipping it here is the no-JS answer, but the clip edge
   is this box's bottom edge, which is never quite the bottom of the screen —
   the 404 gets cut off in mid-air above it. So when JavaScript is available,
   main.js moves the fall into a fixed, viewport-sized stage and this clip is
   switched off: a fixed subtree adds no scrollable overflow, so there is
   nothing left to hold back. */
.error-page {
  overflow: clip;
}

/* The stage is exactly the viewport, so the edge the 404 vanishes at is the
   edge of the screen — at any window size, at any scroll offset, with no
   measuring. It sits under the sticky header and must never swallow a click.
   (The rules that hand the fall over to it live below, after the plain
   .error-code rules, so specificity climbs in source order.) */
.fall-stage {
  position: fixed;
  inset: 0;
  z-index: 1;
  overflow: clip;
  pointer-events: none;
}

/* The arc and the spin ride separate transform properties (translate/scale vs
   rotate) so each keeps its own easing: the flight is ballistic, the spin is
   dead linear like a defeated game sprite. Individual properties compose as
   translate → rotate → scale, so the squash still lands on his own axis. */
.error-bot {
  width: var(--bot-size);
  transform-origin: 50% 60%;
  animation:
    bot-defeat 5s 0.3s both,
    bot-spin 5s 0.3s both;
}

/* How far the 404 has to rise from its resting spot for its lowest ink to meet
   the top of the bot's head. This calc is only the no-JS approximation, built
   from the same two clamps that size the pair; main.js overwrites --bonk with
   the measured ink geometry, which lands about 8px lower because the digits
   barely descend past their baseline. */
.error-code {
  --bonk: calc(-0.92 * var(--bot-size) - 0.8 * var(--code-size) - var(--stack-gap));
  --bonk-rebound: calc(var(--bonk) - 0.35 * var(--code-size));
  animation: code-crash 5s 0.3s both;
}

.error-bot-eyes-open,
.error-bot-eyes-spiral {
  transform-box: view-box;
  transform-origin: 16px 18px;
}

.error-bot-eyes-open {
  animation: eyes-open 5s 0.3s both;
}

/* Pupil size rides stroke-width rather than a group scale, so the eyes bug
   out in place instead of drifting apart from each other. */
.error-bot-eyes-open path {
  animation: eyes-dilate 5s 0.3s both;
}

/* Knocked out: the spirals are hidden until the bonk, fade in on the hit, and
   then keep spinning for good — the classic dizzy-eyed daze. Each spins about
   its own eye centre, so the bot's 45deg resting tilt is irrelevant to them
   (which is also why spirals sidestep the X-reads-as-plus problem). */
.error-bot-eyes-spiral {
  animation: spiral-in 5s 0.3s both;
}

.error-bot-spiral {
  transform-box: view-box;
  animation: spiral-spin 1.3s linear infinite;
}

.error-bot-spiral-left {
  transform-origin: 11px 18px;
}

.error-bot-spiral-right {
  transform-origin: 21px 18px;
}

@keyframes bot-defeat {
  0% {
    translate: 0 -70vh;
    scale: 1 1;
    animation-timing-function: cubic-bezier(0.5, 0, 1, 0.6);
  }
  /* Falls the whole way at full height — the squash starts on contact, not
     before it, so keep an un-scaled keyframe at the exact touchdown frame. */
  9% {
    translate: 0 40%;
    scale: 1 1;
    animation-timing-function: ease-out;
  }
  /* Slams into the 404 below, squashing on contact… */
  10.5% {
    translate: 0 55%;
    scale: 1.16 0.74;
    animation-timing-function: ease-out;
  }
  /* …then bounces back up to his spot. */
  15% {
    translate: 0 0;
    scale: 0.96 1.03;
  }
  /* Standing pause while the 404 leaves and returns — held until the falling
     404 actually reaches his head. */
  21%,
  66.7% {
    translate: 0 0;
    scale: 1 1;
    animation-timing-function: ease-out;
  }
  /* Clonked. The squash is the launch — no beat of standing around between
     the hit and the spin, which is what made the old version feel staged.
     The upward offset compensates for scaling around the 60% origin, keeping
     the rounded crown pinned beneath the 404 while the shell compresses. */
  68.2% {
    translate: 0 -7.7%;
    scale: 1.18 0.76;
    animation-timing-function: cubic-bezier(0.2, 0.9, 0.35, 1);
  }
  /* Stretches as he's punted off the ground. */
  70% {
    translate: 0 -14%;
    scale: 0.84 1.22;
    animation-timing-function: cubic-bezier(0.3, 0, 0.5, 1);
  }
  /* Apex of the pop-up. */
  75% {
    translate: 0 -48%;
    scale: 1 1;
    animation-timing-function: cubic-bezier(0.5, 0, 0.9, 0.7);
  }
  /* Hits the deck on his corner. */
  85% {
    translate: 0 0;
    scale: 1 1;
    animation-timing-function: ease-out;
  }
  86.5% {
    translate: 0 0;
    scale: 1.12 0.88;
  }
  89% {
    translate: 0 0;
    scale: 0.98 1.02;
  }
  92%,
  100% {
    translate: 0 0;
    scale: 1 1;
  }
}

/* Three flat rotations at a constant rate, stopping 45deg over — the sprite
   that spins off and settles on its corner. */
@keyframes bot-spin {
  0%,
  68.2% {
    rotate: 0turn;
    animation-timing-function: linear;
  }
  /* Overshoots the tilt on landing, then rocks back into it. */
  85% {
    rotate: 3.15turn;
    animation-timing-function: cubic-bezier(0.3, 0.8, 0.4, 1);
  }
  89%,
  100% {
    rotate: 3.125turn;
  }
}

@keyframes code-crash {
  /* Rattled by the bot's landing — still until he actually touches down. */
  0%,
  9% {
    transform: translate(0, 0);
  }
  10.5% {
    transform: translateX(-5px);
  }
  12.5% {
    transform: translateX(5px);
  }
  14.5% {
    transform: translateX(-3px);
  }
  16.5% {
    transform: translateX(2px);
  }
  /* Gives out and falls off the bottom of the screen… */
  20%,
  36% {
    transform: translate(0, 0);
    animation-timing-function: cubic-bezier(0.4, 0, 0.85, 0.5);
  }
  /* …and keeps accelerating straight past the bottom edge rather than parking.
     It's clear of the tallest viewport by ~46% (105vh always overshoots the
     window, since the digits rest well below its top) and simply keeps
     travelling, so it reads as falling off the page instead of blinking out
     at a fixed spot. The tail keyframe only has to stay gone; it's the motion
     across the visible edge that matters. */
  46% {
    transform: translateY(105vh);
    animation-timing-function: cubic-bezier(0.5, 0, 1, 0.7);
  }
  59.9% {
    transform: translateY(255vh);
  }
  /* …then returns from the top a beat later, straight onto the bot's head.
     Offsets here are percentages of the 404's own height, which tracks its
     font-size, so the contact point holds as the type scales. */
  60% {
    transform: translateY(-85vh);
    animation-timing-function: cubic-bezier(0.4, 0, 1, 0.7);
  }
  /* Lands on his head. Hold through the squash for 75ms so a display cannot
     skip the single mathematical contact frame, then rebound off the shell. */
  66.7% {
    transform: translateY(var(--bonk));
  }
  68.2% {
    transform: translateY(var(--bonk));
    animation-timing-function: cubic-bezier(0.15, 0.75, 0.4, 1);
  }
  70.5% {
    transform: translateY(var(--bonk-rebound));
    animation-timing-function: cubic-bezier(0.5, 0, 1, 0.6);
  }
  /* Drops into place while he's still spinning overhead. */
  78% {
    transform: translateY(0);
    animation-timing-function: cubic-bezier(0.2, 0.7, 0.4, 1);
  }
  80% {
    transform: translateY(-14%);
    animation-timing-function: cubic-bezier(0.5, 0, 1, 0.6);
  }
  83%,
  100% {
    transform: translate(0, 0);
  }
}

/* Every keyframe carries the same translate+scale pair so the browser
   interpolates component-wise instead of falling back to matrix maths. */
@keyframes eyes-open {
  /* Watching the ground rush up. */
  0%,
  6% {
    transform: translateY(1.2px) scale(1, 1);
    opacity: 1;
  }
  /* Braced, eyes saucering, an instant before touchdown. */
  9% {
    transform: translateY(1.2px) scale(1.08, 1.08);
    opacity: 1;
  }
  /* Clamped shut on impact… */
  10.5%,
  14% {
    transform: translateY(2px) scale(1, 0.06);
    opacity: 1;
  }
  /* …then a pained flutter — cracks open, screws shut again, and only
     gingerly reopens. One flat squint read as bored rather than hurt. */
  16.5% {
    transform: translateY(2px) scale(1, 0.34);
    opacity: 1;
  }
  18.5% {
    transform: translateY(2px) scale(1, 0.09);
    opacity: 1;
  }
  22% {
    transform: translateY(1.5px) scale(1, 0.42);
    opacity: 1;
  }
  26%,
  38% {
    transform: translateY(0) scale(1, 1);
    opacity: 1;
  }
  /* Watching the 404 disappear below… */
  44%,
  58% {
    transform: translateY(1.2px) scale(1, 1);
    opacity: 1;
  }
  /* …then spotting it coming back down and shrinking away from it. */
  62% {
    transform: translateY(-1.2px) scale(1, 1);
    opacity: 1;
  }
  66.6% {
    transform: translateY(-1.6px) scale(1.22, 1.22);
    opacity: 1;
  }
  66.7%,
  100% {
    transform: translateY(-1.6px) scale(1.22, 1.22);
    opacity: 0;
  }
}

/* The alarm beat: pupils blow out as the 404 bears down on him, and again
   for a beat before the first landing. The group scale in `eyes-open` does
   most of the work — this only adds pupil on top of it, and has to stay
   under the 10-unit gap between the eyes or the two merge into one blob. */
@keyframes eyes-dilate {
  0%,
  6% {
    stroke-width: 8;
  }
  9%,
  10.5% {
    stroke-width: 8.6;
  }
  14%,
  62% {
    stroke-width: 8;
  }
  66.6%,
  100% {
    stroke-width: 8.8;
  }
}

@keyframes spiral-in {
  0%,
  66.6% {
    opacity: 0;
  }
  66.7%,
  100% {
    opacity: 1;
  }
}

/* One steady turn, looped forever — the spin never resolves, so he stays
   woozy. Runs from load beneath the spiral-in fade; invisible until the bonk. */
@keyframes spiral-spin {
  to {
    transform: rotate(1turn);
  }
}

.error-code {
  color: var(--accent);
  font-family: var(--font-display);
  font-size: var(--code-size);
  /* The --bonk maths above assumes the em box is the line box. */
  line-height: 1;
}

.error-page p {
  max-width: 36rem;
}

/* Placed over the slot the real 404 occupies, in viewport coordinates. */
.fall-stage .error-code {
  position: absolute;
  margin: 0;
  visibility: hidden;
  animation: none;
}

/* JavaScript first resets every track, finishes the font and collision
   measurements, then adds is-playing to the page and stage in one frame. This
   starts the whole scene from a shared CSS state without animation timestamps. */
.error-page.has-fall-stage:not(.is-playing) .error-bot,
.error-page.has-fall-stage:not(.is-playing) .error-bot-eyes-open,
.error-page.has-fall-stage:not(.is-playing) .error-bot-eyes-open path,
.error-page.has-fall-stage:not(.is-playing) .error-bot-eyes-spiral,
.error-page.has-fall-stage:not(.is-playing) .error-bot-spiral {
  animation: none;
}

.error-page.has-fall-stage:not(.is-playing) .error-bot {
  visibility: hidden;
}

.fall-stage.is-playing .error-code {
  visibility: visible;
  animation: code-crash 5s 0.3s both;
}

/* Once the stage has taken the fall, the page no longer needs to clip it. */
.error-page.has-fall-stage {
  overflow: visible;
}

/* The real 404 keeps its slot in the grid but hands the performance over — the
   animation has to come off too, or it would still be travelling 255vh
   invisibly and dragging the scrollbar out with it. */
.error-page.has-fall-stage .error-code {
  visibility: hidden;
  animation: none;
}

/* After the offscreen travel finishes, the fixed stage is removed and the
   ordinary in-page digits take over in the same final pose. */
.error-page.fall-complete .error-code {
  visibility: visible;
  animation: none;
}

/* --------------------------- Reduced motion --------------------------- */

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  /* biome-ignore-start lint/complexity/noImportantStyles: honouring a motion
     preference has to beat every other rule, however specific. */
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  /* biome-ignore-end lint/complexity/noImportantStyles: reduced-motion reset */

  .reveal-enabled .reveal {
    opacity: 1;
    translate: none;
  }
}
