/* ============================================================
   SwapMatch — landing page
   Ported from Claude Design: "SwapMatch - Landing Page.dc.html"
   ============================================================ */

/* ---------- Design tokens ---------- */

:root {
  --bg:        #EFF0F2;
  --surface:   #FFFFFF;
  --surface2:  #F7F8F9;
  --ink:       #16191D;
  --ink2:      #4C5157;
  --muted:     #878C93;
  --line:      #E7E8EB;
  --brand:     #0FB981;
  --brand-hi:  #0A9E6C;
  --danger:    #E4573E;
  --glass:     rgba(255, 255, 255, .72);
  --plate1:    #EEF0F2;
  --plate2:    #E6E8EB;
  --glassbar:  rgba(255, 255, 255, .72);
  --glassline: rgba(255, 255, 255, .9);
  --phone-shadow: 0 40px 90px -36px rgba(20, 22, 26, .5);
  --phone-border: 0;

  --font-display: 'Space Grotesk', system-ui, sans-serif;
  --font-body:    'Instrument Sans', system-ui, sans-serif;
  --font-mono:    'Space Mono', ui-monospace, monospace;

  --shell:  1140px;
  --gutter: clamp(20px, 5vw, 40px);
  --ease:   cubic-bezier(.2, .7, .2, 1);

  color-scheme: light;
}

/* System dark, unless the visitor has explicitly chosen light. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg:        #101215;
    --surface:   #191C20;
    --surface2:  #14171A;
    --ink:       #F1F3F5;
    --ink2:      #AFB5BC;
    --muted:     #7C828A;
    --line:      #282D33;
    --glass:     rgba(25, 28, 32, .72);
    --plate1:    #1D2126;
    --plate2:    #22262C;
    --glassbar:  rgba(30, 34, 39, .72);
    --glassline: rgba(255, 255, 255, .08);
    /* On a dark ground the drop shadow reads as mud; a hairline holds the
       phone's edge instead. */
    --phone-shadow: none;
    --phone-border: 1px solid #333A41;
    color-scheme: dark;
  }
}

/* Explicit dark wins in either direction. */
:root[data-theme="dark"] {
  --bg:        #101215;
  --surface:   #191C20;
  --surface2:  #14171A;
  --ink:       #F1F3F5;
  --ink2:      #AFB5BC;
  --muted:     #7C828A;
  --line:      #282D33;
  --glass:     rgba(25, 28, 32, .72);
  --plate1:    #1D2126;
  --plate2:    #22262C;
  --glassbar:  rgba(30, 34, 39, .72);
  --glassline: rgba(255, 255, 255, .08);
  --phone-shadow: none;
  --phone-border: 1px solid #333A41;
  color-scheme: dark;
}

/* ---------- Base ---------- */

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

/* The swipe section is the only snap target on the site, so this is inert
   on the info pages. */
html {
  scroll-behavior: auto;
  scroll-snap-type: y proximity;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  overflow-x: clip;
  transition: background .45s ease, color .45s ease;
}

a { color: var(--brand-hi); text-decoration: none; }
a:hover { color: var(--brand); }
button { font: inherit; }
img, svg { display: block; }

::selection { background: rgba(15, 185, 129, .18); }
:focus-visible { outline: 2px solid var(--brand); outline-offset: 3px; }

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.eyebrow {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--brand);
}

/* ---------- Animations ---------- */

@keyframes sm-fade-up {
  from { opacity: 0; transform: translateY(14px) scale(.94); }
  to   { opacity: 1; transform: none; }
}

@keyframes sm-hint {
  0%, 100% { opacity: .35; transform: translateY(0); }
  50%      { opacity: .9;  transform: translateY(5px); }
}

/* The in-app toast drops in from above the phone's top edge and leaves the
   same way. 160px clears the tallest toast plus its shadow. */
@keyframes sm-toast-in {
  from { opacity: 0; transform: translateY(-160px); }
  to   { opacity: 1; transform: none; }
}
@keyframes sm-toast-out {
  from { opacity: 1; transform: none; }
  to   { opacity: 0; transform: translateY(-160px); }
}

@keyframes sm-swipe-l {
  0%, 100% { opacity: .45; transform: translateX(0); }
  45%      { opacity: 1;   transform: translateX(-5px); }
}
@keyframes sm-swipe-r {
  0%, 100% { opacity: .45; transform: translateX(0); }
  45%      { opacity: 1;   transform: translateX(5px); }
}

/* Runs on the top card until the first swipe or tap, as an invitation. */
@keyframes sm-nudge {
  0%, 72%, 100% { transform: translateX(0) rotate(0); }
  80%           { transform: translateX(-11px) rotate(-1.1deg); }
  88%           { transform: translateX(9px) rotate(.9deg); }
}

/* Scroll-triggered reveals. JS adds .is-in when the block enters the viewport. */
[data-reveal] {
  opacity: 0;
  transform: translateY(22px);
  transition: opacity .8s var(--ease), transform .8s var(--ease);
}
[data-reveal].is-in { opacity: 1; transform: none; }

/* ---------- Theme toggle ---------- */

.theme-toggle {
  position: fixed;
  top: 18px; right: 18px;
  z-index: 60;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 14px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--glass);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  color: var(--ink2);
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: .1em;
  text-transform: uppercase;
  cursor: pointer;
  transition: border-color .2s ease, color .2s ease;
}
.theme-toggle:hover { border-color: var(--brand); color: var(--ink); }

.theme-toggle__dot {
  width: 9px; height: 9px;
  border-radius: 99px;
  background: var(--brand);
}

/* ---------- 1 · Hero ---------- */

/* Heights are in svh — the *small* viewport, the one left when a mobile
   browser has its address bar out. vh there is the large viewport, so a
   sticky stage sized in vh is taller than what is on screen and its
   centred content sits low, drifting as the bar hides and shows. The vh
   line before each is the fallback for browsers without svh. */
.hero { height: 200vh; height: 200svh; position: relative; }

.hero__stage {
  position: sticky;
  top: 0;
  height: 100vh;
  height: 100svh;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

.logo {
  position: absolute;
  width: 180px; height: 180px;
  animation: sm-fade-up 1.1s var(--ease) both;
}
.logo__layer { position: absolute; inset: 0; }
.logo__half { will-change: transform; }

.hero__word {
  position: absolute;
  text-align: center;
  padding: 0 24px;
  opacity: 0;
}
.hero__word h1 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(46px, 11vw, 116px);
  line-height: .95;
  letter-spacing: -.045em;
}
.hero__word p {
  margin: 18px 0 0;
  font-size: clamp(16px, 2.4vw, 23px);
  line-height: 1.4;
  color: var(--ink2);
  text-wrap: pretty;
}

/* Lives at the root, not inside the hero, so it carries through both scroll
   scenes. JS drives the outer opacity; the pulse stays on the inner element so
   the two never fight over the same property. Anchored to the top, out of the
   way of the mobile browser's own bottom bar. */
.scroll-hint {
  position: fixed;
  top: max(18px, env(safe-area-inset-top)); left: 50%;
  transform: translateX(-50%);
  z-index: 30;
  opacity: 1;
  transition: opacity .3s ease;
}
.scroll-hint__pulse {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 9px;
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--muted);
  animation: sm-hint 2.4s ease-in-out infinite;
}
.scroll-hint__pulse span {
  width: 1px; height: 24px;
  background: linear-gradient(to bottom, var(--muted), transparent);
}

/* ---------- 2 · Swipe ----------
   One viewport tall, and a snap target: the page stops here, and
   scroll-snap-stop keeps it here until the deck is empty. JS clears both
   properties on the last card, and the Skip button is the way out before
   that. */

.swipe {
  height: 100vh;
  height: 100svh;
  position: relative;
  scroll-snap-align: center;
  scroll-snap-stop: always;
}

.swipe__stage {
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: clamp(12px, 2.2vh, 24px);
  padding: clamp(24px, 5vh, 56px) 16px clamp(20px, 4vh, 44px);
  overflow: hidden;
}

.swipe__cue {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: none;
  margin: 0;
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--muted);
  text-align: center;
  transition: opacity .5s ease;
}
.swipe__cue span { flex: none; width: 26px; height: 1px; background: currentColor; opacity: .5; }

/* The phone keeps its 372x764 internal layout and is scaled to fit; this box
   is sized by JS to the scaled result so the column below it sits right. */
.swipe__fit {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 372px;
  height: 764px;
}

.swipe__actions { display: flex; align-items: center; gap: 12px; flex: none; }

/* Only reachable once the deck has run out; JS raises it. */
.swipe__reset {
  appearance: none;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--ink2);
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: .18em;
  text-transform: uppercase;
  padding: 11px 20px;
  border-radius: 999px;
  cursor: pointer;
  opacity: 0;
  pointer-events: none;
  transition: opacity .4s ease, border-color .3s ease, color .3s ease;
}
.swipe__reset:hover { border-color: var(--brand); color: var(--brand); }

.swipe__skip {
  appearance: none;
  border: 0;
  background: none;
  color: var(--muted);
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: .18em;
  text-transform: uppercase;
  padding: 11px 8px;
  cursor: pointer;
  transition: color .3s ease;
}
.swipe__skip:hover { color: var(--brand); }

/* In-app toasts. One per card, raised when that card is answered and pulled
   back up 3.4s later. Their palette is the app's, not the page's, so they
   read the same in both themes. */

.toasts {
  position: absolute;
  left: 16px; right: 16px; top: 72px;
  z-index: 200;
  pointer-events: none;
}
.toast {
  position: absolute;
  left: 0; right: 0; top: 0;
  margin: 0;
  opacity: 0;
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 14px 15px;
  border-radius: 22px;
  corner-shape: squircle;
  background: linear-gradient(160deg, #12332A 0%, #0F241F 100%);
  border: 1px solid rgba(15, 185, 129, .42);
  box-shadow: 0 18px 36px -18px rgba(8, 20, 16, .66);
}
.toast__icon {
  flex: none;
  width: 30px; height: 30px;
  border-radius: 9px;
  corner-shape: squircle;
  background: var(--brand);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.toast__text {
  flex: 1;
  min-width: 0;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 14.5px;
  line-height: 1.38;
  letter-spacing: -.01em;
  color: #fff;
  text-wrap: pretty;
}

/* Phone shell */

.phone {
  position: relative;
  width: 372px; height: 764px;
  flex: none;
  padding: 12px;
  border-radius: 66px;
  corner-shape: squircle;
  background: #16181B;
  box-shadow: var(--phone-shadow);
  border: var(--phone-border);
  transform-origin: center center;
}
.phone__screen {
  position: relative;
  width: 100%; height: 100%;
  border-radius: 56px;
  corner-shape: squircle;
  background: var(--surface2);
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

.status {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 28px 0;
  height: 44px;
  flex: none;
}
.status__time { font-weight: 600; font-size: 14.5px; color: var(--ink); }
.status__right { display: flex; align-items: center; gap: 6px; }
.status__bars { display: flex; align-items: flex-end; gap: 2px; height: 11px; }
.status__bars i { width: 3px; background: var(--ink); border-radius: 1px; }
.status__bars i:nth-child(1) { height: 5px; }
.status__bars i:nth-child(2) { height: 7px; }
.status__bars i:nth-child(3) { height: 9px; }
.status__bars i:nth-child(4) { height: 11px; }
.status__net { font-size: 10.5px; font-weight: 700; color: var(--ink); }
.status__battery {
  width: 24px; height: 12px;
  border: 1.5px solid var(--ink);
  border-radius: 3px;
  padding: 1.5px;
  display: flex;
}
.status__battery i { flex: 1; background: var(--ink); border-radius: 1px; }

.appbar {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 8px 22px 10px;
  flex: none;
}
.appbar__brand { display: flex; align-items: center; gap: 9px; }
.appbar__name {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 20px;
  letter-spacing: -.03em;
  color: var(--ink);
}

/* Deck */

/* touch-action and cursor are left to JS: it sets touch-action: none while
   there are cards left, so a gesture starting on the deck goes to the drag
   handler rather than the page, and relaxes it to pan-y once the last card
   is gone. Declaring `none` here instead would leave the deck dead to touch
   on a page whose script never ran. */
.deck {
  position: relative;
  flex: 1;
  margin: 6px 18px 0;
  min-height: 0;
  overscroll-behavior: contain;
  user-select: none;
  -webkit-user-select: none;
}

.deck__plate {
  position: absolute;
  left: 0; right: 0; top: 0;
  height: 100%;
  border-radius: 28px;
  corner-shape: squircle;
}
.deck__plate--back  { background: var(--plate2); transform: translateY(20px) scale(.9); }
.deck__plate--front { background: var(--plate1); transform: translateY(10px) scale(.95); }

.deck__empty {
  position: absolute;
  left: 0; right: 0; top: 0;
  height: 100%;
  border-radius: 28px;
  corner-shape: squircle;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 34px;
  opacity: 0;
  transform: translateY(10px);
  transition: opacity .5s ease, transform .5s var(--ease);
}
.deck__empty-inner { display: flex; flex-direction: column; align-items: center; gap: 16px; }
.deck__empty svg { color: var(--muted); animation: sm-hint 1.9s ease-in-out infinite; }
.deck__empty span {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 22px;
  line-height: 1.28;
  letter-spacing: -.02em;
  color: var(--ink2);
  text-align: center;
  text-wrap: pretty;
}

.card {
  position: absolute;
  left: 0; right: 0; top: 0;
  height: 100%;
  border-radius: 28px;
  corner-shape: squircle;
  overflow: hidden;
  background: #17181B;
  box-shadow: 0 24px 48px -20px rgba(20, 22, 26, .55);
  will-change: transform;
  /* The throw and the snap back both ride this; JS clears it for the frames
     a finger is actually on the card. */
  transition: transform .44s cubic-bezier(.3, .85, .25, 1), opacity .44s ease;
}

/* Photo slot. The gradient sits behind the image, so a card whose photo has
   not been dropped in yet still reads as a card rather than a broken box.
   --card-tint is set per card and is invisible once a real photo loads. */
.card__media {
  position: absolute;
  inset: 0;
  --card-tint: 200;
  background:
    radial-gradient(120% 90% at 70% 10%,
      hsl(var(--card-tint) 14% 22%) 0%,
      hsl(var(--card-tint) 12% 14%) 55%,
      hsl(var(--card-tint) 10% 8%) 100%);
}
.card__media img {
  width: 100%; height: 100%;
  object-fit: cover;
}
.card__media img[hidden] { display: none; }

.card__scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg,
    rgba(10, 16, 10, .34)   0%,
    rgba(10, 16, 10, 0)    26%,
    rgba(10, 16, 10, 0)    46%,
    rgba(9, 14, 9, .64)    78%,
    rgba(8, 12, 8, .93)   100%);
}

.card__stamp {
  position: absolute;
  top: 84px;
  padding: 8px 16px;
  border-radius: 12px;
  color: #fff;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 23px;
  letter-spacing: .04em;
  text-shadow: 0 2px 9px rgba(6, 10, 8, .55);
  box-shadow: 0 9px 22px -7px rgba(6, 10, 8, .55);
  opacity: 0;
  pointer-events: none;
}
.card__stamp--swap { left: 18px;  background: var(--brand);  transform: rotate(-14deg); }
.card__stamp--nope { right: 18px; background: var(--danger); transform: rotate(14deg); }

/* Sits over the top card until the first swipe or tap. */
.deck__hint {
  position: absolute;
  inset: 0;
  z-index: 80;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  transition: opacity .45s ease;
}
.deck__hint span {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 9px 15px;
  border-radius: 999px;
  background: rgba(12, 16, 14, .62);
  backdrop-filter: blur(9px);
  -webkit-backdrop-filter: blur(9px);
  border: 1px solid rgba(255, 255, 255, .16);
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: #F2F5F3;
}
.deck__hint-arrow--l { animation: sm-swipe-l 1.7s ease-in-out infinite; }
.deck__hint-arrow--r { animation: sm-swipe-r 1.7s ease-in-out infinite; }

.card__counter {
  position: absolute;
  top: 13px; left: 13px; right: 13px;
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.card__dots {
  position: absolute;
  top: 50px; left: 13px; right: 13px;
  display: flex;
  gap: 4px;
}
.card__dots i {
  flex: 1;
  height: 3px;
  border-radius: 2px;
  background: rgba(255, 255, 255, .38);
}
.card__dots i.is-on { background: #fff; }

.card__body {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  padding: 0 17px 17px;
}
.card__title {
  font-family: var(--font-display);
  font-weight: 600;
  color: #fff;
  font-size: 24px;
  line-height: 1.06;
  letter-spacing: -.025em;
  margin: 0 0 9px;
  text-wrap: pretty;
}
.card__tags { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }

.pill {
  background: rgba(255, 255, 255, .18);
  border: 1px solid rgba(255, 255, 255, .28);
  color: #fff;
  font-size: 11.5px;
  font-weight: 600;
  padding: 5px 11px;
  border-radius: 999px;
}
.pill--count { border-color: rgba(255, 255, 255, .25); padding: 4px 11px; }

/* Swipe controls */

.controls {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 24px;
  padding: 16px 0 10px;
  flex: none;
}
.controls > * {
  appearance: none;
  border: 0;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--surface);
}
.controls button { cursor: pointer; }
.controls__pass {
  width: 58px; height: 58px;
  box-shadow: 0 8px 18px -8px rgba(20, 22, 26, .28);
  color: var(--danger);
}
.controls__info {
  width: 44px; height: 44px;
  box-shadow: 0 5px 12px -6px rgba(20, 22, 26, .25);
  color: var(--muted);
}
.controls__swap {
  width: 62px; height: 62px;
  box-shadow: 0 10px 22px -8px rgba(20, 22, 26, .32);
  color: var(--brand);
}

/* Tab bar */

.tabbar-wrap { padding: 0 18px 8px; flex: none; }
.tabbar {
  display: flex;
  align-items: center;
  justify-content: space-around;
  padding: 11px 10px;
  border-radius: 22px;
  corner-shape: squircle;
  background: var(--glassbar);
  border: 1px solid var(--glassline);
  box-shadow: 0 10px 26px -12px rgba(20, 22, 26, .3);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
}
.tabbar span { display: inline-flex; color: var(--muted); }
.tabbar span.is-active { color: var(--brand); }
/* Qualified the same way .is-active is: a bare .tabbar__fab loses to
   .tabbar span and the plus comes out muted grey on the green. */
.tabbar span.tabbar__fab {
  width: 42px; height: 42px;
  border-radius: 14px;
  corner-shape: squircle;
  background: var(--brand);
  color: #fff;
  align-items: center;
  justify-content: center;
  box-shadow: 0 8px 18px -6px rgba(15, 185, 129, .6);
}

.home-indicator { display: flex; justify-content: center; padding: 2px 0 8px; flex: none; }
.home-indicator span {
  width: 128px; height: 5px;
  border-radius: 3px;
  background: var(--ink);
  opacity: .75;
}

/* ---------- 3 · Mission, vision, goal ----------
   Three rows of equal weight, separated by their own top rules rather than
   by gaps, so they read as one list. */

.mission__inner {
  max-width: 1000px;
  margin: 0 auto;
  padding: clamp(60px, 11vh, 120px) var(--gutter) clamp(48px, 8vh, 88px);
}

.mission__row {
  display: grid;
  grid-template-columns: minmax(0, 96px) minmax(0, 1fr);
  gap: clamp(14px, 3vw, 36px);
  align-items: baseline;
  padding: clamp(22px, 3vh, 32px) 0;
  border-top: 1px solid var(--line);
}

.mission__lead {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(18px, 2.4vw, 29px);
  line-height: 1.28;
  letter-spacing: -.028em;
  text-wrap: pretty;
}

/* The 96px label column leaves too little for the sentence on a phone. */
@media (max-width: 560px) {
  .mission__row { grid-template-columns: minmax(0, 1fr); gap: 10px; }
}

/* ---------- 4 · Purpose ---------- */

.purpose { border-top: 1px solid var(--line); }

.purpose__inner {
  max-width: var(--shell);
  margin: 0 auto;
  padding: clamp(56px, 10vh, 110px) var(--gutter);
  display: grid;
  grid-template-columns: minmax(0, 180px) minmax(0, 1fr);
  gap: clamp(16px, 4vw, 48px);
  align-items: start;
}
.purpose__inner .eyebrow { padding-top: 7px; }
.purpose__stack { display: flex; flex-direction: column; gap: 18px; max-width: 720px; }
.purpose__body {
  margin: 0;
  font-size: clamp(16px, 2vw, 20px);
  line-height: 1.6;
  color: var(--ink2);
  text-wrap: pretty;
}
.purpose__kicker {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(17px, 2.1vw, 22px);
  line-height: 1.45;
  letter-spacing: -.02em;
  text-wrap: pretty;
}

@media (max-width: 640px) {
  .purpose__inner { grid-template-columns: minmax(0, 1fr); }
  .purpose__inner .eyebrow { padding-top: 0; }
}

/* ---------- 5 · Download ---------- */

.cta {
  border-top: 1px solid var(--line);
  background: var(--surface);
  transition: background .45s ease;
}
.cta__inner {
  max-width: var(--shell);
  margin: 0 auto;
  padding: clamp(64px, 11vh, 132px) var(--gutter);
  display: flex;
  flex-wrap: wrap;
  gap: clamp(28px, 5vw, 64px);
  align-items: center;
  justify-content: space-between;
}
.cta__lockup { display: flex; align-items: center; gap: 20px; }
.cta__copy { display: flex; flex-direction: column; gap: 7px; }
.cta__title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(24px, 3.4vw, 34px);
  letter-spacing: -.03em;
}
.cta__sub { font-size: 15px; color: var(--ink2); }
.cta__buttons { display: flex; gap: 12px; flex-wrap: wrap; }

.store {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 14px 24px;
  border-radius: 18px;
  corner-shape: squircle;
  transition: opacity .2s ease, border-color .2s ease;
}
.store__label {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: .14em;
  text-transform: uppercase;
}
.store__name {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 17px;
  letter-spacing: -.02em;
}
.store--primary {
  background: var(--ink);
  color: var(--surface);
  border: 1px solid var(--ink);
}
.store--primary:hover { opacity: .88; color: var(--surface); }
.store--primary .store__label { opacity: .6; }
.store--ghost {
  background: transparent;
  color: var(--ink);
  border: 1px solid var(--line);
}
.store--ghost:hover { border-color: var(--brand); color: var(--ink); }
.store--ghost .store__label { color: var(--muted); }

/* ---------- 6 · Footer ---------- */

.footer { border-top: 1px solid var(--line); }
.footer__inner {
  max-width: var(--shell);
  margin: 0 auto;
  padding: clamp(40px, 7vh, 72px) var(--gutter) clamp(32px, 5vh, 56px);
  display: flex;
  flex-direction: column;
  gap: 34px;
}
.footer__cols {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 26px;
}
.footer__col { display: flex; flex-direction: column; gap: 11px; }
.footer__head {
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--muted);
}
.footer__col a { font-size: 14.5px; color: var(--ink2); }
.footer__col a:hover { color: var(--brand); }

.footer__base {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  align-items: center;
  justify-content: space-between;
  padding-top: 22px;
  border-top: 1px solid var(--line);
}
.footer__brand {
  display: flex;
  align-items: center;
  gap: 10px;
  /* A span on the landing, a link home on the info pages. */
  color: var(--ink);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 15px;
  letter-spacing: -.03em;
}
a.footer__brand:hover { color: var(--ink); }
.footer__legal {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--muted);
  letter-spacing: .04em;
}

/* ---------- 6 · Info pages ----------
   Everything below dresses the documents generated by tools/build_pages.py
   from content/pages/. Ported from "SwapMatch - Info Page.dc.html". */

.page-head {
  position: sticky;
  top: 0;
  z-index: 40;
  background: var(--glass);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--line);
}
.page-head__inner {
  max-width: var(--shell);
  margin: 0 auto;
  padding: 13px var(--gutter);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
.page-head__brand {
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--ink);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 18px;
  letter-spacing: -.03em;
}
.page-head__brand:hover { color: var(--ink); }

/* The landing pins its toggle to the corner; a document has a header to put
   it in. */
.theme-toggle--inline {
  position: static;
  background: transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  padding: 7px 13px;
  font-size: 10.5px;
}

.doc { display: block; }
.doc__inner {
  max-width: var(--shell);
  margin: 0 auto;
  padding: clamp(40px, 7vw, 84px) var(--gutter) clamp(56px, 9vw, 110px);
}

.doc__intro {
  display: flex;
  flex-direction: column;
  gap: 14px;
  max-width: 760px;
  margin-bottom: clamp(34px, 5vw, 56px);
}
.doc__kicker {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--muted);
}
.doc__dash { width: 16px; height: 1px; background: var(--line); }
.doc__title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(34px, 6vw, 58px);
  line-height: 1.03;
  letter-spacing: -.035em;
}
.doc__lede {
  margin: 0;
  font-size: clamp(16px, 2vw, 19px);
  line-height: 1.6;
  color: var(--ink2);
  text-wrap: pretty;
}

.doc__grid {
  display: grid;
  grid-template-columns: minmax(0, 200px) minmax(0, 1fr);
  gap: clamp(24px, 5vw, 72px);
  align-items: start;
}

.doc__toc {
  position: sticky;
  top: 96px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding-right: 8px;
}
.doc__toc-head {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 2px;
}
.doc__toc a { font-size: 13.5px; line-height: 1.35; color: var(--ink2); }
.doc__toc a:hover { color: var(--brand); }

.doc__body {
  max-width: 720px;
  display: flex;
  flex-direction: column;
  gap: clamp(28px, 3.5vw, 42px);
}
.doc__section {
  display: flex;
  flex-direction: column;
  gap: 14px;
  scroll-margin-top: 88px;
}
.doc__section h2 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(22px, 2.6vw, 27px);
  letter-spacing: -.025em;
  line-height: 1.2;
}
.doc__section p {
  margin: 0;
  font-size: 16.5px;
  line-height: 1.68;
  color: var(--ink2);
  text-wrap: pretty;
}
.doc__list {
  margin: 0;
  padding-left: 20px;
  display: flex;
  flex-direction: column;
  gap: 9px;
  font-size: 16.5px;
  line-height: 1.6;
  color: var(--ink2);
}
.doc__list strong { color: var(--ink); font-weight: 600; }

.doc__callout {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 18px 20px;
  border-radius: 16px;
  corner-shape: squircle;
  background: var(--surface);
  border: 1px solid var(--line);
  font-size: 15.5px;
  line-height: 1.55;
  color: var(--ink2);
}
.doc__callout-label {
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--muted);
}

/* The document footer opens with a compact app row; the landing has a whole
   download section above the footer and does without it. */
.footer__apps {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  padding-bottom: 26px;
  border-bottom: 1px solid var(--line);
}
.footer__apps-copy {
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 15px;
  line-height: 1.4;
  color: var(--ink2);
}
.footer__apps-copy svg { flex: none; }
.footer__apps-buttons { display: flex; gap: 9px; flex-wrap: wrap; }

.chip {
  display: inline-flex;
  align-items: center;
  padding: 9px 16px;
  border-radius: 12px;
  corner-shape: squircle;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 13.5px;
  letter-spacing: -.01em;
  transition: opacity .2s ease, border-color .2s ease;
}
.chip--primary { background: var(--ink); color: var(--surface); border: 1px solid var(--ink); }
.chip--primary:hover { opacity: .88; color: var(--surface); }
.chip--ghost { border: 1px solid var(--line); color: var(--ink); }
.chip--ghost:hover { border-color: var(--brand); color: var(--ink); }

@media (max-width: 960px) {
  .doc__grid { grid-template-columns: minmax(0, 1fr); }
  .doc__toc { display: none; }
}

/* ---------- Reduced motion ----------
   The hero collapses to one viewport and JS paints its finished state, so
   the story still reads without scroll-driven movement. The swipe deck is
   driven by the pointer rather than by scroll, so it stays — only its
   idling animations and the forced snap stop go. */

@media (prefers-reduced-motion: reduce) {
  html { scroll-snap-type: none; }
  .hero { height: 100vh; height: 100svh; }
  .logo { animation: none; }
  .scroll-hint { display: none; }
  [data-reveal] { transition: none; }
  .deck__empty svg,
  .deck__hint-arrow--l,
  .deck__hint-arrow--r { animation: none; }
}
