/* ============================================================
   Listík landing — rebuild
   ============================================================ */

@font-face {
  font-family: "TikTok Sans";
  src: url("/assets/fonts/TikTokSans.ttf") format("truetype-variations");
  font-weight: 300 900;
  font-stretch: 75% 150%;
  font-style: normal;
  font-display: swap;
}

:root {
  /* ---- Colors ---- */
  --primary: #362c55;
  --brand: #9d80f3;
  --surface: #ffffff;
  --secondary: rgba(54, 44, 85, 0.56);
  --tertiary: rgba(54, 44, 85, 0.32);
  --quaternary: rgba(54, 44, 85, 0.2);
  --border: rgba(54, 44, 85, 0.08);
  --background: rgba(54, 44, 85, 0.04);
  --overlay-dark: rgba(0, 0, 0, 0.4);

  /* ---- Shadows ---- */
  --shadow-card: 0px 4px 24px rgba(0, 0, 0, 0.04);
  --shadow-block: 0px 4px 24px rgba(0, 0, 0, 0.02);
  --shadow-btn: 0px 1.5px 9px rgba(0, 0, 0, 0.04);

  /* ---- Radii ---- */
  --radius-pill: 40px;
  --radius-card: 33px;
  /* white section blocks (halves and their containers) */
  --radius-block: 4px;
  --radius-btn-sm: 30px;
  --radius-tag: 15px;

  /* ---- Layout ---- */
  /* taller than the 1440/544 from the design — a flatter ellipse reads
     as a band rather than a glow */
  --glow-ratio: 1440 / 760;

  --container-max: 1200px;
  /* centred statement copy in the full-screen sections */
  --measure-full: 720px;
  --gutter-x: 32px;
  --gutter-y: 2px;

  --font: "TikTok Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    sans-serif;
}

/* ============================================================
   Typography — matches Figma text styles (Dev Mode layer names)
   ============================================================ */
.h1 {
  margin: 0;
  font-size: 40px;
  line-height: 110%;
  font-weight: 500;
  font-stretch: 95%;
  color: var(--primary);
}
.h2 {
  margin: 0;
  font-size: 32px;
  line-height: 110%;
  font-weight: 500;
  font-stretch: 95%;
  color: var(--primary);
}
.h3 {
  margin: 0;
  font-size: 28px;
  line-height: 110%;
  font-weight: 500;
  font-stretch: 95%;
  color: var(--primary);
}
.p1 {
  margin: 0;
  font-size: 20px;
  line-height: 140%;
  font-weight: 400;
  color: var(--primary);
}
.caption {
  margin: 0;
  font-size: 14px;
  line-height: 120%;
  font-weight: 400;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--secondary);
}
/* accordion header, half-width sections (24/29, weight 500) */
.accordion-label {
  margin: 0;
  font-size: 24px;
  line-height: 120%;
  font-weight: 500;
  font-stretch: 95%;
  color: var(--primary);
}
/* accordion header, full-width single section (20/28, weight 500) */
.accordion-label-sm {
  margin: 0;
  font-size: 20px;
  line-height: 120%;
  font-weight: 500;
  font-stretch: 95%;
  color: var(--primary);
}
/* footer link list item */
.link {
  margin: 0;
  font-size: 18px;
  line-height: 120%;
  font-weight: 400;
  color: var(--primary);
}
/* footer meta (email / legal links / copyright) */
.meta {
  margin: 0;
  font-size: 16px;
  line-height: 120%;
  font-weight: 400;
  color: var(--secondary);
}
/* inline color override for a run of text within a heading/paragraph */
.text-secondary {
  color: var(--secondary);
}

/* ============================================================
   Buttons
   ============================================================ */
.btn {
  display: inline-flex;
  width: auto;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 16px 32px;
  min-height: 60px;
  border-radius: var(--radius-pill);
  border: none;
  font-size: 20px;
  line-height: 130%;
  font-weight: 400;
  cursor: pointer;
  text-decoration: none;
  transition: transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1);
}
/* pointer devices get the full lift-and-press pair */
@media (hover: hover) and (pointer: fine) {
  .btn:hover {
    transform: scale(1.04);
  }
  .btn:active {
    transform: scale(0.94);
    transition-duration: 0.1s;
  }
}
/* on touch there is no hover to lift out of, so the press itself grows the
   button — shrinking it under the finger would hide it rather than answer */
@media (hover: none) {
  .btn:active {
    transform: scale(1.04);
    transition-duration: 0.1s;
  }
}
/* stretches to fill its parent instead of shrink-wrapping content */
.btn--block {
  display: flex;
  width: 100%;
}
.btn img,
.btn svg {
  width: 24px;
  height: 24px;
  flex: none;
}
.btn--primary {
  background: var(--primary);
  color: var(--surface);
}
.btn--secondary {
  background: var(--background);
  color: var(--primary);
}
/* ghost: no fill, no border — label + optional 20px icon only.
   Label reuses the footer-link type scale (18/120%, 16 on mobile);
   the values are repeated here because .btn comes later than .link. */
.btn--ghost {
  background: transparent;
  color: var(--primary);
  gap: 8px;
  padding: 14px 0;
  min-height: 50px;
  font-size: 18px;
  line-height: 120%;
}
.btn--ghost img,
.btn--ghost svg {
  width: 20px;
  height: 20px;
}

/* ============================================================
   Card — phone-mockup frame. Content is a screenshot image,
   not recreated UI.
   ============================================================ */
.card {
  position: relative;
  flex: none;
  width: 300px;
  max-width: 100%;
  aspect-ratio: 300 / 630;
  height: auto;
  background: var(--surface);
  /* the outline rides outside the box instead of a border, which
     box-sizing would otherwise carve out of the image's own width.
     A spread shadow keeps it radius-aware and out of the layout. */
  box-shadow: 0 0 0 1px var(--border), var(--shadow-card);
  border-radius: var(--radius-card);
  overflow: hidden;
}
.card img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top center;
}

/* Pre-cropped screenshot: the artwork already ends where the design cuts
   it, so the card takes its height from the image instead of being clipped
   by the block's fixed height. Bottom corners stay square — the cut is
   meant to read as the screen running off the edge. */
.card--cut {
  aspect-ratio: auto;
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
}
.card--cut img {
  height: auto;
}
.section--single > .container:has(.card--cut) {
  height: auto;
}
.section--single .half--image:has(.card--cut) {
  justify-content: flex-end;
  padding-bottom: 0;
}

/* ============================================================
   Section — Double (two half cards side by side)
   ============================================================ */
.section--double {
  display: flex;
  justify-content: center;
  align-items: flex-start;
  padding: var(--gutter-y) var(--gutter-x);
  width: 100%;
}
.section--double > .container {
  display: flex;
  flex-direction: row;
  justify-content: center;
  /* stretch so both halves match the taller one's height */
  align-items: stretch;
  gap: 4px;
  width: 100%;
  max-width: var(--container-max);
}
/* since the halves are stretched, their content sits at the top rather
   than floating in the middle of the extra space */
.section--double .half {
  justify-content: flex-start;
}
.half {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  padding: 80px 72px;
  flex: 1 1 0;
  min-width: 0;
  position: relative;
  background: var(--surface);
  border-radius: var(--radius-block);
}
/* the shadow rides on a negative-z pseudo instead of box-shadow: that
   puts every block's shadow in the section's below-content layer, so it
   never falls on top of the neighbouring block's white background */
.half::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: var(--shadow-block);
  pointer-events: none;
  z-index: -1;
}
.half > .container {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 80px;
  width: 436px;
  max-width: 100%;
}

/* ============================================================
   Section — Hero (like Double, but taller top padding, no card
   backgrounds, and the left half is a text/logo/CTA block)
   ============================================================ */
.section--hero {
  display: flex;
  justify-content: center;
  align-items: flex-start;
  padding: 80px 32px 40px;
  width: 100%;
}
.section--hero > .container {
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: stretch;
  gap: 4px;
  width: 100%;
  max-width: var(--container-max);
}
.section--hero .half {
  background: transparent;
}
.section--hero .half::before {
  display: none;
}
.hero-text-half {
  align-items: flex-start;
}
.hero-content {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  align-items: flex-start;
  gap: 80px;
  width: 436px;
  max-width: 100%;
  flex: 1 1 auto;
}
.hero-content .h1 {
  align-self: stretch;
}
.hero-content .cta-buttons {
  margin: 0;
}
.hero-logo {
  flex: none;
  height: 64px;
  color: var(--primary);
}
.hero-logo svg {
  display: block;
  height: 100%;
  width: auto;
}

/* ============================================================
   Section — Single (one shared white card: text half + image half,
   fixed height, image bleeds out and gets clipped at the bottom)
   ============================================================ */
.section--single {
  display: flex;
  justify-content: center;
  align-items: flex-start;
  padding: var(--gutter-y) var(--gutter-x);
  width: 100%;
}
.section--single > .container {
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: stretch;
  gap: 4px;
  width: 100%;
  max-width: var(--container-max);
  height: 560px;
  overflow: hidden;
  background: var(--surface);
  border-radius: var(--radius-block);
  box-shadow: var(--shadow-block);
}
.section--single .half {
  background: transparent;
  justify-content: flex-start;
}
.section--single .half::before {
  display: none;
}
/* mirrored variant: DOM order stays text-then-image (mobile keeps
   text on top), but on row layout it visually flips to image-left */
.section--single--reverse > .container {
  flex-direction: row-reverse;
}

/* ============================================================
   Section — Single, full (no crop/overflow-hidden bleed).
   Text half + accordion half share the left column; image spans
   both rows. Grid handles the desktop 2-col / mobile 3-block
   reflow without duplicating markup.
   ============================================================ */
.section--single--full > .container {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: auto 1fr;
  height: auto;
  overflow: visible;
}
.feature-text-half {
  grid-column: 1;
  grid-row: 1;
}
.feature-accordion-half {
  grid-column: 1;
  grid-row: 2;
}
.section--single--full .half.feature-accordion-half {
  justify-content: flex-end;
}
.feature-image-half {
  grid-column: 2;
  grid-row: 1 / span 2;
  justify-content: center;
}
.feature-accordion-half .accordion-header {
  padding: 20px 0;
}
.feature-accordion-half .accordion-panel-inner .p1 {
  padding: 0 0 20px 0;
}
.accordion-header .accordion-label {
  flex: 1 1 auto;
}

/* ============================================================
   Section — Full (full-bleed centered icon + statement)
   ============================================================ */
.section--full {
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 0 32px;
  width: 100%;
  min-height: 80vh;
}
.section--full > .container {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  padding: 200px 0;
  gap: 24px;
  width: 100%;
  max-width: var(--container-max);
}
.section--full svg {
  width: 64px;
  height: 64px;
  flex: none;
  color: var(--primary);
}
.section--full .h2 {
  text-align: center;
  max-width: var(--measure-full);
}

/* ============================================================
   Section — CTA (final full-screen call-to-action: badge, H1,
   stacked auth buttons)
   ============================================================ */
.section--cta {
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 0 32px;
  width: 100%;
  min-height: 80vh;
}
.section--cta > .container {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 200px 0;
  gap: 56px;
  width: 100%;
  max-width: 920px;
}
.cta-intro {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 56px;
  width: 100%;
}
.cta-badge {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 96px;
  height: 96px;
  flex: none;
  background: var(--surface);
  border-radius: 50%;
  box-shadow: var(--shadow-card);
}
.cta-badge svg {
  width: 80%;
  height: 80%;
  flex: none;
  color: var(--primary);
}
.section--cta .h1 {
  text-align: center;
  max-width: var(--measure-full);
}
.cta-buttons {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  width: 347px;
  max-width: 100%;
  margin: 0 auto;
}

.store-cta {
  gap: 16px;
}
.store-primary,
.store-ghosts {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
}
/* the two desktop ghosts stack flush, no gap (per the Figma frame) */
.store-ghosts {
  gap: 0;
}

/* ============================================================
   Store CTA — the same markup ships every variant; script.js puts
   data-platform on <html> and these rules pick what to show.
   With no attribute (detection not run or inconclusive) the
   fallback below shows both stores plus web, never a dead end.
   ============================================================ */
.store-cta .store-btn {
  display: none;
}
/* A label too long for the column runs on one line under a fade at the
   right edge instead of wrapping. The fade is as wide as the button's own
   side padding, so the text dissolves exactly within it.
   `safe center` keeps the label centred while it fits and falls back to
   start-alignment when it doesn't, so the icon never gets clipped. */
.store-cta .store-btn {
  white-space: nowrap;
  justify-content: safe center;
}
.store-cta .btn--primary.store-btn {
  position: relative;
  overflow: hidden;
}
.store-cta .btn--primary.store-btn::after {
  content: "";
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 32px;
  border-radius: inherit;
  background: linear-gradient(to right, rgba(54, 44, 85, 0), var(--primary));
  pointer-events: none;
}
/* fallback — unknown platform: web plus both stores, never a dead end */
html:not([data-platform]) .store-cta [data-store="web-primary"],
html:not([data-platform]) .store-cta [data-store="apple-ghost"],
html:not([data-platform]) .store-cta [data-store="google-ghost"],
/* iPhone / iPad — App Store first, Google Play as the alternative */
[data-platform="ios-phone"] .store-cta [data-store="apple"],
[data-platform="ios-phone"] .store-cta [data-store="google-ghost"],
[data-platform="ios-tablet"] .store-cta [data-store="apple"],
[data-platform="ios-tablet"] .store-cta [data-store="google-ghost"],
/* Android — the other way round */
[data-platform="android-phone"] .store-cta [data-store="google"],
[data-platform="android-phone"] .store-cta [data-store="apple-ghost"],
[data-platform="android-tablet"] .store-cta [data-store="google"],
[data-platform="android-tablet"] .store-cta [data-store="apple-ghost"],
/* desktop — web first, both stores below it */
[data-platform="desktop"] .store-cta [data-store="web-primary"],
[data-platform="desktop"] .store-cta [data-store="apple-ghost"],
[data-platform="desktop"] .store-cta [data-store="google-ghost"] {
  display: flex;
}
/* ============================================================
   Footer
   ============================================================ */
.footer {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 120px 32px 40px;
  gap: 40px;
  width: 100%;
  background: var(--surface);
  /* sits outside <main>, so the .has-glow--bottom sibling rule can't
     reach it — lift it here so the CTA glow stops at the footer edge */
  position: relative;
  z-index: 1;
}
.footer > .top {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: flex-start;
  width: 100%;
  max-width: var(--container-max);
  gap: 40px;
}
.footer-logo {
  flex: none;
  height: 64px;
  color: var(--primary);
}
.footer-logo svg {
  display: block;
  height: 100%;
  width: auto;
}
.footer-cols {
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  gap: 80px;
  margin-left: auto;
}
.footer-section {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 16px;
}
.footer-list {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 12px;
}
.footer > .bottom {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: flex-start;
  width: 100%;
  max-width: var(--container-max);
  gap: 80px;
}
.footer-meta-links {
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  gap: 20px;
}
.link,
.meta {
  text-decoration: none;
}

/* ============================================================
   Legal pages (privacy / terms) — one column of running text,
   built out of the existing type scale rather than a new one
   ============================================================ */
.legal {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 64px 32px 120px;
  gap: 64px;
  width: 100%;
}
.legal-logo {
  display: block;
  height: 48px;
  color: var(--primary);
  /* the wordmark doubles as the way back to the landing page */
  text-decoration: none;
  transition: transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.legal-logo svg {
  display: block;
  height: 100%;
  width: auto;
}
@media (hover: hover) and (pointer: fine) {
  .legal-logo:hover {
    transform: scale(1.04);
  }
  .legal-logo:active {
    transform: scale(0.94);
    transition-duration: 0.1s;
  }
}
@media (hover: none) {
  .legal-logo:active {
    transform: scale(1.04);
    transition-duration: 0.1s;
  }
}

.legal-body {
  width: 100%;
  max-width: var(--measure-full);
}
/* the rhythm lives here so the markup can stay plain .h1/.h2/.p1 and keep
   matching the rest of the site */
.legal-body > * + * {
  margin-top: 20px;
}
.legal-body > .h2 {
  margin-top: 56px;
}
.legal-body > .h3 {
  margin-top: 40px;
}
.legal-body > .legal-date {
  margin-top: 12px;
}
.legal-list {
  margin: 20px 0 0;
  padding-left: 24px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.legal-list li::marker {
  color: var(--tertiary);
}
.legal-link {
  color: var(--primary);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: var(--quaternary);
  transition: text-decoration-color 0.2s ease;
}
.legal-link:hover {
  text-decoration-color: var(--primary);
}
.legal-term {
  font-weight: 500;
}
.legal-address {
  font-style: normal;
  color: var(--secondary);
}

/* the legal pages carry the footer's bottom row only — the link columns
   belong to the landing page's own navigation */
.footer--slim {
  padding: 40px 32px;
}

@media (max-width: 767px) {
  .legal {
    padding: 40px 24px 80px;
    gap: 40px;
  }
  .legal-logo {
    height: 36px;
  }
  .legal-body > .h2 {
    margin-top: 40px;
  }
  .footer--slim {
    padding: 32px 24px;
  }
}

/* ============================================================
   Section — FAQ (title + accordion)
   ============================================================ */
.section--faq {
  display: flex;
  justify-content: center;
  align-items: flex-start;
  padding: 0 32px;
  width: 100%;
  background: var(--surface);
}
.section--faq > .container {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 104px 0;
  gap: 20px;
  width: 100%;
  max-width: 920px;
}
.section--faq .h1 {
  text-align: center;
}

/* ============================================================
   Accordion — reusable disclosure component (JS-toggled `is-open`
   class + CSS grid-rows animation for a smooth expand/collapse).
   ============================================================ */
.accordion {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  width: 100%;
}
.accordion-item {
  width: 100%;
  border-bottom: 1px solid var(--border);
}
.accordion-item:last-child {
  border-bottom: none;
}
.accordion-header {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  width: 100%;
  padding: 16px 0;
  margin: 0;
  border: none;
  background: none;
  font: inherit;
  text-align: left;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
}
.accordion-header .accordion-label-sm {
  flex: 1 1 auto;
}
.accordion-icon {
  position: relative;
  width: 20px;
  height: 20px;
  flex: none;
  /* the two bars are opaque and the fade lives here, on the group: with a
     translucent fill each the crossing would stack two layers and show a
     darker square where they meet. 0.56 matches --secondary. */
  opacity: 0.56;
  transition: opacity 0.2s ease;
}
.accordion-icon::before,
.accordion-icon::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 13.33px;
  height: 1.5px;
  background: var(--primary);
  transform: translate(-50%, -50%) rotate(0deg);
  transition: transform 0.25s ease;
}
/* vertical bar: rotated 90deg by default so the pair reads as "+" */
.accordion-icon::after {
  transform: translate(-50%, -50%) rotate(90deg);
}
.accordion-item.is-open .accordion-icon {
  opacity: 1;
}
/* rotates flat onto the horizontal bar, merging "+" into "−" */
.accordion-item.is-open .accordion-icon::after {
  transform: translate(-50%, -50%) rotate(0deg);
}
.accordion-panel {
  height: 0;
  overflow: hidden;
  transition: height 0.3s ease;
}
.accordion-panel-inner .p1 {
  padding: 0 40px 24px 0;
  opacity: 0;
  transition: opacity 0.2s ease;
}
.accordion-item.is-open .accordion-panel-inner .p1 {
  opacity: 1;
  transition: opacity 0.25s ease 0.05s;
}

/* half content variant: icon + H3 (no phone card) */
.icon-row {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 40px;
  width: 436px;
  max-width: 100%;
}
.icon-row svg {
  width: 48px;
  height: 48px;
  flex: none;
  color: var(--primary);
}

* {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  font-family: var(--font);
  color: var(--primary);
  /* fixed full-page gradient: background (top) -> surface (bottom) */
  background: linear-gradient(180deg, var(--background) 0%, var(--surface) 100%);
  background-attachment: fixed;
  min-height: 100vh;
}

.page {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
  /* the glow layers overhang the page on every side — clip so they can
     never add scrollable area (a horizontal bar, or dead space under
     the footer from the CTA glow) */
  overflow: clip;
}

/* ============================================================
   Background glow
   A brand-colored ellipse centered on the section's top edge, 150%
   of the screen width so its soft left/right falloff sits off-screen
   instead of fading inside the viewport. Height is purely derived
   from the width, so the ellipse keeps the same proportions on every
   screen size.
   Drawn as a radial-gradient rather than a solid rect + blur(272px)
   — same look, no huge blur surface to rasterize on every repaint.
   ============================================================ */
.has-glow {
  position: relative;
  isolation: isolate;
}
.has-glow::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 0;
  width: 150%;
  aspect-ratio: var(--glow-ratio);
  /* translateY(-50%) resolves against the element's own height, so the
     ellipse stays centered on the edge whatever the ratio computes to */
  transform: translate(-50%, -50%);
  background: radial-gradient(
    ellipse closest-side at 50% 50%,
    rgba(157, 128, 243, 0.2),
    rgba(157, 128, 243, 0) 100%
  );
  pointer-events: none;
  /* below the section's content but above its background; `isolation`
     above keeps the negative z-index from escaping the section. Lifting
     the children instead would make them positioned, which would steal
     the containing block from absolutely positioned content inside. */
  z-index: -1;
}
/* same ellipse, centered on the bottom edge instead of the top */
.has-glow--bottom::before {
  top: auto;
  bottom: 0;
  transform: translate(-50%, 50%);
}
/* .has-glow is a stacking context and paints above the static blocks
   around it, so the neighbour the ellipse overhangs into gets lifted to
   stay on top of it — the section below a bottom glow, the section above
   a top one. */
.has-glow--bottom + * {
  position: relative;
  z-index: 1;
}
*:has(+ .has-glow:not(.has-glow--bottom)) {
  position: relative;
  z-index: 1;
}

/* ============================================================
   RESPONSIVE — tablet, 768–1200px
   ============================================================ */
@media (min-width: 768px) and (max-width: 1199px) {
  .h2 {
    font-size: 28px;
  }

  .section--double {
    padding: 2px 32px;
  }
  .section--double > .container {
    gap: 4px;
  }
  .half {
    padding: 64px 56px;
  }
  .half > .container {
    width: 410px;
  }
}

/* ============================================================
   RESPONSIVE — below 768px, mobile layout kicks in
   (Figma "mobile" frame, 375–400 wide)
   ============================================================ */
@media (max-width: 767px) {
  .h2 {
    font-size: 22px;
    line-height: 110%;
  }
  .h3 {
    font-size: 20px;
    line-height: 110%;
  }
  .p1 {
    font-size: 18px;
  }
  .caption {
    font-size: 12px;
  }

  .section--faq {
    padding: 0 20px;
  }
  .section--faq > .container {
    padding: 56px 0;
  }

  .section--double {
    padding: 2px 16px;
  }
  .section--double > .container {
    flex-direction: column;
    gap: 4px;
    max-width: 480px;
  }
  .half {
    width: 100%;
    padding: 40px 24px;
  }
  .half > .container {
    gap: 40px;
    width: 100%;
    max-width: 320px;
  }
  .card {
    width: 280px;
    border-radius: 30.8px;
  }
  .icon-row {
    flex-direction: row-reverse;
    align-items: flex-start;
    gap: 20px;
    width: 100%;
    max-width: 320px;
  }
  .icon-row svg {
    width: 32px;
    height: 32px;
  }

  .section--hero {
    padding: 40px 16px 48px;
  }
  .section--hero > .container {
    flex-direction: column;
    gap: 20px;
  }
  .hero-text-half {
    align-items: center;
  }
  .hero-content {
    flex: none;
    width: 100%;
    /* same cap the other sections use on this breakpoint */
    max-width: 480px;
    justify-content: flex-start;
    align-items: center;
    text-align: center;
    gap: 48px;
  }
  .hero-logo {
    height: 56px;
  }
  .hero-image-half {
    padding: 0 24px;
  }
  .section--hero .cta-buttons {
    width: 320px;
  }

  .section--single {
    padding: 2px 16px;
  }
  .section--single > .container {
    flex-direction: column;
    justify-content: flex-start;
    gap: 0;
    height: 576px;
    /* same cap as the double sections, so no white block stretches past
       the others once the viewport grows towards the breakpoint */
    max-width: 480px;
  }
  .section--single .half {
    width: 100%;
    padding: 40px 24px;
  }
  .section--single .half--image {
    padding: 0 24px 40px;
  }
  .section--single .card {
    width: 280px;
    border-radius: 32.9px;
  }
  /* the two rules above reset the radius wholesale, so the cut cards and
     the block they sit in need their square bottom put back */
  .card--cut,
  .section--single .card--cut {
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
  }
  .section--single .half--image:has(.card--cut) {
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
  }

  .section--single--full {
    padding: 2px 16px;
  }
  .section--single--full > .container {
    height: auto;
    overflow: visible;
    grid-template-columns: 1fr;
    grid-template-rows: auto auto auto;
    max-width: 480px;
  }
  .feature-text-half,
  .feature-accordion-half,
  .feature-image-half {
    grid-column: auto;
    grid-row: auto;
  }
  /* needs to outrank `.section--single .half`, which would otherwise put
     40px back on the top and bottom */
  .section--single--full .feature-image-half {
    padding: 0 24px;
  }
  .accordion-label {
    font-size: 20px;
  }

  .section--full {
    padding: 0 40px;
  }
  .section--full > .container {
    padding: 160px 0;
    gap: 20px;
    max-width: 480px;
  }
  .section--full svg {
    width: 48px;
    height: 48px;
  }

  .btn {
    font-size: 18px;
    padding: 16px 24px;
    min-height: 56px;
  }
  /* fade matches the narrower mobile side padding */
  .store-cta .btn--primary.store-btn::after {
    width: 24px;
  }
  .store-cta {
    gap: 12px;
  }
  .btn--ghost {
    font-size: 16px;
    padding: 12px 0;
    min-height: 46px;
  }
  .btn img,
  .btn svg {
    width: 20px;
    height: 20px;
  }
  .btn--secondary {
    padding: 16px 24px;
  }

  .h1 {
    font-size: 32px;
    line-height: 110%;
  }
  .section--cta {
    padding: 0 32px;
  }
  .section--cta > .container {
    padding: 120px 0;
    gap: 40px;
    max-width: 480px;
  }
  .cta-intro {
    gap: 40px;
  }
  .cta-badge {
    width: 80px;
    height: 80px;
  }
  .cta-buttons {
    width: 306px;
  }

  .footer {
    padding: 40px 16px 24px;
    gap: 24px;
  }
  .footer > .top {
    flex-direction: column;
    align-items: flex-start;
    gap: 32px;
  }
  .footer-logo {
    height: 56px;
  }
  .footer-cols {
    flex-direction: column;
    align-items: flex-start;
    gap: 24px;
    margin: 0;
  }
  .footer-section {
    gap: 12px;
  }
  .footer .link {
    font-size: 16px;
  }
  .footer > .bottom {
    flex-direction: row;
    justify-content: space-between;
    align-items: flex-end;
    gap: 24px;
  }
  .footer-meta-links {
    flex-direction: column;
    align-items: flex-start;
    gap: 12px;
  }
}

/* ============================================================
   Checkbox — reusable 24×24 control (20×20 visual box) with
   bounce-in check animation.
   ============================================================ */
.checkbox {
  position: relative;
  width: 24px;
  height: 24px;
  flex: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: transform 0.15s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.checkbox:hover {
  transform: scale(1.04);
}
.checkbox:active {
  transform: scale(0.96);
  transition-duration: 0.08s;
}
.checkbox__input {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
  pointer-events: none;
}
.checkbox__box {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 20px;
  height: 20px;
  border-radius: 6px;
  background: var(--surface);
  border: 1px solid var(--quaternary);
  transition: background 0.18s ease, border-color 0.18s ease;
}
.checkbox:has(.checkbox__input:checked) .checkbox__box {
  background: var(--brand);
  border-color: var(--brand);
}
/* the mark rides the same squish as the coloured box — animating only the
   box makes the checkmark float at a fixed size while the box moves under it */
.checkbox__input:checked ~ .checkbox__box,
.checkbox__input:checked ~ .checkbox__mark {
  animation: cb-check 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
}
@keyframes cb-check {
  0% {
    transform: scale(1);
  }
  40% {
    transform: scale(0.82);
  }
  100% {
    transform: scale(1);
  }
}
.checkbox__mark {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 20px;
  height: 20px;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}
.checkbox__mark svg {
  width: 12px;
  height: 9px;
  opacity: 0;
  transform: scale(0.5);
  transition: opacity 0.15s ease, transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.checkbox:has(.checkbox__input:checked) .checkbox__mark svg {
  opacity: 1;
  transform: scale(1);
}

/* ============================================================
   Section — Plans change (a to-do row you can actually swipe
   away; the next one takes its place, five of them on a loop)
   ============================================================ */
.section--swipe {
  position: relative;
}

/* the row flies well past the text before it is fully faded, so the stage
   clips it rather than letting it overlap the section's neighbours */
.swipe-stage {
  display: flex;
  justify-content: center;
  width: 100%;
  overflow: hidden;
  /* room for the checkbox's hover/active scale, which would otherwise clip */
  padding: 6px 0;
}

.swipe-row {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 15px;
  cursor: grab;
  user-select: none;
  -webkit-user-select: none;
  -webkit-tap-highlight-color: transparent;
  /* claim horizontal gestures, leave vertical ones to the page scroll */
  touch-action: pan-y;
}
.swipe-row.is-dragging {
  cursor: grabbing;
}
/* the settle back to centre and the flight out both animate; the drag itself
   must not, or the row lags a frame behind the finger */
.swipe-row.is-animating {
  transition: transform 0.34s cubic-bezier(0.32, 0.72, 0.35, 1), opacity 0.34s ease;
}
/* the replacement arrives on its own gentler curve */
.swipe-row.is-entering {
  transition: transform 0.32s cubic-bezier(0.22, 0.9, 0.3, 1), opacity 0.32s ease;
}

/* only the first to-do rocks — once the visitor has swiped, the hint has
   done its job and repeating it would just be noise */
.swipe-row.is-hinting {
  animation: swipe-hint 2.8s ease-in-out infinite;
}
@keyframes swipe-hint {
  0%,
  62%,
  100% {
    transform: translateX(0);
  }
  70% {
    transform: translateX(-9px);
  }
  80% {
    transform: translateX(9px);
  }
  88% {
    transform: translateX(-4px);
  }
  94% {
    transform: translateX(2px);
  }
}

.swipe-label {
  font-size: 27px;
  line-height: 133%;
  font-weight: 400;
  color: var(--primary);
  white-space: nowrap;
}

.swipe-caption {
  position: absolute;
  left: 50%;
  bottom: 20px;
  transform: translateX(-50%);
  margin: 0;
  white-space: nowrap;
}

@media (max-width: 767px) {
  .swipe-row {
    gap: 10px;
  }
  .swipe-label {
    font-size: 18px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .swipe-row.is-hinting {
    animation: none;
  }
}

/* ============================================================
   Section — Celebrate (interactive checkbox demo living inside
   the existing "Celebrate when the last one's done." full section)
   ============================================================ */
.section--celebrate {
  position: relative;
}

.celebrate-stage {
  display: flex;
  flex-direction: column;
  align-items: center;
  /* no gap: the todo and the heading are never visible at the same time, and
     any gap would offset whichever one is showing from the section's centre */
  gap: 0;
  width: 100%;
}

.celebrate-todo {
  position: relative;
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 15px;
  transition: opacity 0.3s ease, max-height 0.18s ease, margin 0.18s ease,
    padding 0.18s ease;
}
/* on the way back only the fade should animate — letting max-height,
   margin and padding transition too makes the row inflate and shove the
   label sideways as the checkbox grows into place */
.celebrate-todo.is-restoring,
.celebrate-todo.is-restoring .celebrate-checkbox {
  transition: opacity 0.3s ease;
}
.celebrate-todo.is-collapsing {
  opacity: 0;
  overflow: hidden;
  /* !important beats the inline max-height the script measures before the
     collapse — without it the row keeps its height and pushes the heading
     below the centre of the section */
  max-height: 0 !important;
  margin: 0 !important;
  padding: 0 !important;
}

.celebrate-ripple {
  position: absolute;
  width: 160px;
  height: 160px;
  left: -62px;
  top: 50%;
  border-radius: 50%;
  /* a soft radial falloff instead of a flat disc: a solid circle keeps a hard
     rim all the way through the scale, which is what reads as a "border" */
  background: radial-gradient(
    circle,
    var(--brand) 0%,
    var(--brand) 42%,
    color-mix(in srgb, var(--brand) 0%, transparent) 100%
  );
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s ease;
  animation: celebrate-ripple-pulse 2.6s ease-out infinite;
}
.celebrate-ripple.is-hidden {
  animation: none;
  opacity: 0;
}
/* opacity ramps up from 0 and is fully back at 0 before the scale ends, so
   neither the loop restart nor the outer edge ever pops */
@keyframes celebrate-ripple-pulse {
  0% {
    transform: translateY(-50%) scale(0.7);
    opacity: 0;
  }
  18% {
    opacity: 0.16;
  }
  55% {
    opacity: 0.07;
  }
  85% {
    opacity: 0;
  }
  100% {
    transform: translateY(-50%) scale(1.3);
    opacity: 0;
  }
}
@media (prefers-reduced-motion: reduce) {
  .celebrate-ripple {
    animation: none;
    transform: translateY(-50%);
    opacity: 0.1;
  }
}

.celebrate-checkbox {
  transition: opacity 0.3s ease;
}

.celebrate-label {
  font-size: 27px;
  line-height: 133%;
  font-weight: 400;
  color: var(--primary);
}

.celebrate-caption {
  position: absolute;
  left: 50%;
  bottom: 20px;
  transform: translateX(-50%);
  margin: 0;
  white-space: nowrap;
  transition: opacity 0.3s ease;
}
.celebrate-caption.is-collapsing {
  opacity: 0;
}
/* takes the caption's slot once the animation has played */
.celebrate-reset {
  border: none;
  background: none;
  padding: 0;
  cursor: pointer;
  font-family: inherit;
}
.celebrate-reset[hidden] {
  display: none;
}

.celebrate-heading {
  max-height: 0;
  opacity: 0;
  overflow: hidden;
  transition: opacity 0.4s ease 0.1s, max-height 0.4s ease;
}
.celebrate-heading.is-visible {
  max-height: 240px;
  opacity: 1;
}
/* on the way back the heading fades on the spot, keeping its height so
   nothing below it moves */
.celebrate-heading.is-fading {
  opacity: 0;
  transition: opacity 0.3s ease;
}
/* the height is only given up once the heading is already invisible — with
   the transition off, so the collapse can't be seen or felt as a shove */
.celebrate-heading.is-instant {
  transition: none;
}

/* purely decorative here — the swipe is the interaction, so the checkbox
   must not advertise itself as tappable */
.swipe-checkbox {
  cursor: inherit;
}
.swipe-checkbox:hover,
.swipe-checkbox:active {
  transform: none;
}

@media (min-width: 768px) {
  .celebrate-checkbox,
  .swipe-checkbox {
    width: 36px;
    height: 36px;
  }
  .celebrate-checkbox .checkbox__box,
  .swipe-checkbox .checkbox__box {
    width: 30px;
    height: 30px;
    top: 3px;
    left: 3px;
    border-radius: 9px;
    border-width: 1.5px;
  }
  .celebrate-checkbox .checkbox__mark {
    width: 30px;
    height: 30px;
    top: 3px;
    left: 3px;
  }
  .celebrate-checkbox .checkbox__mark svg {
    width: 14px;
    height: 11px;
  }
}

@media (max-width: 767px) {
  .celebrate-todo {
    gap: 10px;
  }
  .celebrate-ripple {
    width: 106px;
    height: 106px;
    left: -41px;
  }
  .celebrate-label {
    font-size: 18px;
  }
}

/* ============================================================
   Letter-scatter completion effect (used by the Celebrate demo)
   ============================================================ */
@keyframes sparkFly {
  0% {
    opacity: 1;
    transform: translate(0, 0);
  }
  100% {
    opacity: 0;
    transform: translate(var(--sx), var(--sy));
  }
}
@keyframes letterFly {
  0% {
    opacity: 1;
    transform: translate(0, 0) rotate(0deg) scale(1);
  }
  15% {
    opacity: 1;
    transform: translate(var(--x1), var(--y1)) rotate(var(--r1)) scale(1.2);
  }
  100% {
    opacity: 0;
    transform: translate(var(--x2), var(--y2)) rotate(var(--r2)) scale(0.15);
  }
}

/* ============================================================
   Confetti — full-viewport canvas overlay
   ============================================================ */
.celebrate-confetti {
  position: fixed;
  inset: 0;
  width: 100vw;
  height: 100vh;
  pointer-events: none;
  z-index: 999;
}
