/* ─────────────────────────────────────────────────────────────────────────
   Pepstack — waitlist site.

   PURE BLACK. Every surface is #000. There are no card fills, no tinted
   panels, no raised greys, no coloured glows, no gradient buttons and no
   coloured shadows anywhere in this file. Separation is hairlines and
   whitespace only. If something needs to read as raised it gets a 1px
   hairline, never a fill. Please keep it that way.

   Accent (#7B5CFA) is allowed in exactly four places: the second half of
   the wordmark, the primary button fill, the active demo card's hairline,
   and small numeric labels. Nowhere else.

   Type: Geist 500 for display (self-hosted, OFL), system stack for body.
   Two weights across the whole site — 400 body, 500 display and UI.
   ───────────────────────────────────────────────────────────────────── */

/* Self-hosted so there is no third-party round trip and nothing to disclose. */
@font-face{
  font-family:"Geist";
  src:url("fonts/geist-500-latin.woff2") format("woff2");
  font-weight:500; font-style:normal; font-display:swap;
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,
    U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,
    U+FEFF,U+FFFD;
}
@font-face{
  font-family:"Geist";
  src:url("fonts/geist-500-latin-ext.woff2") format("woff2");
  font-weight:500; font-style:normal; font-display:swap;
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,
    U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,
    U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}
/* 700 is here only for the hero title. Real bold, not a synthesised one —
   faux-bolding Geist 500 smears the stems at display size. */
@font-face{
  font-family:"Geist";
  src:url("fonts/geist-700-latin.woff2") format("woff2");
  font-weight:700; font-style:normal; font-display:swap;
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,
    U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,
    U+FEFF,U+FFFD;
}
@font-face{
  font-family:"Geist";
  src:url("fonts/geist-700-latin-ext.woff2") format("woff2");
  font-weight:700; font-style:normal; font-display:swap;
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,
    U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,
    U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}

:root{
  --hair:rgba(255,255,255,.09);
  --hair-soft:rgba(255,255,255,.06);
  --t1:#fff;
  --t2:rgba(255,255,255,.60);
  --t3:rgba(255,255,255,.38);
  --t-hero2:rgba(255,255,255,.42);
  --accent:#7B5CFA;

  --pad:80px;   /* section side padding, desktop */
  --bleed:40px; /* hero media inset from the viewport edge */
  --maxw:1440px;

  --display:"Geist",-apple-system,BlinkMacSystemFont,"SF Pro Display",system-ui,sans-serif;
  --body:-apple-system,BlinkMacSystemFont,"SF Pro Text",system-ui,sans-serif;
}

*,*::before,*::after{margin:0;padding:0;box-sizing:border-box}
[hidden]{display:none !important}

html{background:#000;scroll-behavior:smooth;-webkit-text-size-adjust:100%}
body{
  background:#000;
  color:var(--t1);
  font-family:var(--body);
  font-size:17px;
  font-weight:400;
  line-height:1.6;
  letter-spacing:0;
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
}
/* No overflow-x:hidden anywhere. It would mask a real overflow bug rather
   than fix it, and it would make the width audit lie. */

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

:focus-visible{outline:2px solid #fff;outline-offset:3px;border-radius:2px}
::selection{background:var(--accent);color:#fff}

.wrap{width:100%;max-width:var(--maxw);margin-inline:auto;padding-inline:var(--pad)}
/* Full-bleed rail. Uses % of the body box, never vw — vw includes the
   scrollbar and that is what produces a phantom horizontal scroll. */
.bleed{width:100%;padding-inline:var(--bleed)}

/* ── waitlist entry point ─────────────────────────────────────────────
   Desktop fills the form in place. Under 760px the form is hidden and this
   link takes over, sending people to waitlist.html. Only one of the two is
   ever on screen. */
/* .btn sets display:inline-flex further down the file, so this needs two
   classes to win regardless of source order. */
.btn.to-waitlist{display:none;text-decoration:none;margin-top:32px}

/* ── swipe position dots (mobile demo) ───────────────────────────────── */
.dots{display:none;justify-content:center;gap:9px;margin-top:22px}
.dot{
  width:7px;height:7px;border-radius:999px;
  background:transparent;
  border:1px solid rgba(255,255,255,.28);
  transition:background-color .2s ease,border-color .2s ease;
}
/* Accent, not white — it marks the same active state the selected card's
   accent hairline does, and keeps the palette to the four allowed uses. */
.dot[data-on]{background:var(--accent);border-color:var(--accent)}

h1,h2,h3,.mark,.stat-value{font-family:var(--display);font-weight:500}

h1{
  font-weight:700;
  color:var(--t1);
  font-size:clamp(38px,5vw,72px);
  line-height:1.05;
  letter-spacing:-.038em;
  text-wrap:balance;
}
/* The tagline is a genuine second level, not a second line of the same size —
   it is 37 characters and would wrap badly at title size. */
h1 .dim{
  display:block;
  margin-top:14px;
  font-weight:500;
  font-size:clamp(17px,1.95vw,29px);
  letter-spacing:-.02em;
  line-height:1.25;
  color:var(--t-hero2);
}

h2{
  font-size:clamp(30px,3.7vw,52px);
  line-height:1.08;
  letter-spacing:-.034em;
  text-wrap:balance;
}

/* One rhythm for the whole page. Every gap between two sections is twice this
   value, and the hero carries a matching bottom pad so the hero→demo gap is
   not half the size of every other one. */
.section{padding-block:clamp(60px,6.4vw,96px)}

/* ── nav ─────────────────────────────────────────────────────────────── */
/* Frosted glass. Black at half alpha so the bar still reads as #000 rather
   than a grey panel, with the blur doing the work — content passing under it
   stays legible as a refraction instead of being hidden.

   backdrop-filter dies silently if ANY ancestor has filter, transform,
   perspective, will-change, opacity < 1, isolation or contain:paint. The nav's
   only ancestors are <body> and <html>; keep them clean or this goes flat. */
.nav{
  position:sticky;top:0;z-index:50;
  /* Thin veil, not a frost. The tint is low so content actually shows through
     and you can see it bending; the darkening that keeps the wordmark legible
     is done with brightness() on the backdrop itself, which dims what is
     behind while keeping its structure, rather than a flat black sheet that
     would hide it. */
  background:rgba(0,0,0,.06);
  -webkit-backdrop-filter:blur(6px) saturate(200%) brightness(.66);
          backdrop-filter:blur(6px) saturate(200%) brightness(.66);
  border-bottom:1px solid var(--hair);
}
/* Without backdrop-filter the bar would be half-transparent with text running
   under it. Fall back to solid. */
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .nav{background:#000}
}
.nav .wrap{display:flex;align-items:center;height:70px}
.mark{font-size:20px;letter-spacing:-.04em;color:var(--t1);line-height:1}
.mark span{color:var(--accent)}
.spacer{flex:1}

.btn{
  display:inline-flex;align-items:center;justify-content:center;
  font-family:var(--body);font-size:15px;font-weight:500;line-height:1;
  letter-spacing:0;
  padding:13px 22px;
  border:1px solid transparent;border-radius:999px;
  background:var(--accent);color:#fff;
  cursor:pointer;white-space:nowrap;
  transition:opacity .2s ease;
}
.btn:hover{opacity:.86}
.btn:disabled{opacity:.42;cursor:not-allowed}

/* ── hero ────────────────────────────────────────────────────────────── */
.hero{padding-top:clamp(56px,7vw,104px);padding-bottom:clamp(60px,6.4vw,96px)}
/* Copy sits left, the phone render takes the right column. */
.hero-grid{
  display:grid;
  /* The render scales with the viewport rather than stepping at a breakpoint,
     so the text column never jumps width and re-wraps the headline. */
  grid-template-columns:minmax(0,1fr) minmax(0,clamp(300px,33vw,500px));
  gap:clamp(36px,4.5vw,72px);
  align-items:center;
}
.hero-copy{min-width:0}
.hero-shot{min-width:0}
.hero-shot img{display:block;width:100%;height:auto;aspect-ratio:1080/1920}
.hero-sub{margin:24px 0 0;max-width:520px;font-size:17px;line-height:1.6;color:var(--t2)}

.form{display:flex;gap:10px;justify-content:center;margin-top:32px}
.form input{
  flex:0 1 340px;min-width:0;
  background:transparent;
  border:1px solid var(--hair);border-radius:14px;
  padding:14px 18px;
  color:#fff;font-family:var(--body);font-size:16px;line-height:1.25;
  transition:border-color .2s ease;
}
.form input::placeholder{color:var(--t3)}
.form input:focus{border-color:rgba(255,255,255,.26)}
.form input:disabled{color:var(--t3);cursor:not-allowed}
.form .btn{flex:none}
/* Chrome paints autofilled inputs pale blue. Inset shadow is the only way
   to override it, and it keeps the field black. */
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus{
  -webkit-text-fill-color:#fff;
  -webkit-box-shadow:0 0 0 1000px #000 inset;
  caret-color:#fff;
}
.note{margin-top:16px;font-size:13px;line-height:1.5;color:var(--t3)}
.note[data-state="error"]{color:var(--t1)}

/* ── the three-part waitlist form in the hero ─────────────────────────── */
.signup{margin-top:34px;max-width:520px;display:flex;flex-direction:column;gap:22px}
.field{border:0;padding:0;margin:0;min-width:0} /* also resets <fieldset> */
.field-label{
  display:block;padding:0;margin-bottom:10px;
  font-size:12px;font-weight:500;letter-spacing:.12em;text-transform:uppercase;
  color:var(--t3);
}
.signup input[type="email"],
.signup textarea{
  display:block;width:100%;
  background:transparent;
  border:1px solid var(--hair);border-radius:14px;
  padding:14px 16px;
  color:#fff;font-family:var(--body);font-size:16px;line-height:1.45;
  transition:border-color .2s ease;
}
.signup input[type="email"]{line-height:1.25}
.signup input::placeholder,.signup textarea::placeholder{color:var(--t3)}
.signup input:focus,.signup textarea:focus{border-color:rgba(255,255,255,.28)}
.signup textarea{min-height:104px;resize:vertical}

/* Radio pills. The real <input> stays in the DOM for keyboard and form data;
   the pill is the label around it. Selected reads as a bright hairline plus
   white text — the same language as the demo cards, without spending the
   accent on it. */
.choices{display:flex;flex-wrap:wrap;gap:10px}
.choice{
  position:relative;display:inline-flex;align-items:center;
  padding:11px 20px;
  border:1px solid var(--hair-soft);border-radius:999px;
  color:var(--t2);font-size:15px;line-height:1;cursor:pointer;
  transition:border-color .2s ease,color .2s ease;
}
.choice input{
  position:absolute;width:1px;height:1px;margin:0;padding:0;
  opacity:0;appearance:none;-webkit-appearance:none;background:transparent;border:0;
}
.choice:hover{border-color:rgba(255,255,255,.20)}
.choice:has(input:checked){border-color:rgba(255,255,255,.62);color:var(--t1)}
.choice:has(input:focus-visible){outline:2px solid #fff;outline-offset:3px}
.signup .btn{align-self:flex-start}

/* ── media slots ─────────────────────────────────────────────────────── */
/* Every slot carries an explicit aspect-ratio so the box is the right size
   before a single byte of video arrives. Nothing reflows on load. */
/* No frame. The mockups already contain their own device edge, so a hairline
   around them reads as a second, competing border — they sit straight on the
   background instead. */
.media{
  position:relative;width:100%;
  background:transparent;
  border:0;
  overflow:hidden;
}
/* Matches the crop of the two screen mockups exactly, so neither panel
   letterboxes and the swap lands in perfect register. */
.media--phone{aspect-ratio:1288/2380}
.media img{width:100%;height:100%;object-fit:contain;background:#000}

/* ── demo / tabs ─────────────────────────────────────────────────────── */
.demo-grid{
  display:grid;
  grid-template-columns:minmax(0,clamp(320px,34vw,480px)) minmax(0,1fr);
  gap:clamp(40px,5vw,80px);
  align-items:center;
}
.tabs{display:flex;flex-direction:column;gap:14px}
.tab{
  display:block;width:100%;text-align:left;
  background:transparent;
  border:1px solid var(--hair-soft);
  border-radius:20px;
  padding:32px 34px 34px;
  font-family:var(--body);
  color:var(--t2);
  cursor:pointer;
  /* Border and text colour only. Nothing else moves. */
  transition:border-color .2s ease,color .2s ease;
}
.tab[aria-selected="true"]{border-color:var(--accent);color:var(--t1)}
.tab-label{
  display:block;
  font-family:var(--display);font-weight:500;
  font-size:clamp(20px,1.8vw,25px);letter-spacing:-.025em;line-height:1.2;
  color:inherit;
}
.tab-sub{display:block;margin-top:8px;font-size:15px;line-height:1.5;color:var(--t3)}
.tab-copy{display:block;margin-top:18px;font-size:15px;line-height:1.6;color:var(--t2)}
/* Spans, not a <ul>: a button may only contain phrasing content, and an
   invalid list inside it breaks the tab's accessible name. */
.tab-points{display:block;margin-top:20px}
.tab-point{
  position:relative;display:block;
  margin-top:10px;padding-left:20px;
  font-size:14.5px;line-height:1.5;color:var(--t2);
}
.tab-point::before{content:"–";position:absolute;left:0;top:0;color:var(--t3)}

.panel{
  position:absolute;inset:0;
  opacity:0;visibility:hidden;
  transition:opacity .28s ease,visibility .28s ease;
}
.panel[data-active="true"]{opacity:1;visibility:visible}

/* ── research ────────────────────────────────────────────────────────── */
.lede{max-width:60ch;font-size:17px;line-height:1.6;color:var(--t2);margin-top:24px}
/* Two stats, centred as a pair rather than stretched across the full measure —
   at 1440 a half-width column would leave each illustration marooned. */
.stats{
  display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:clamp(24px,3vw,56px);
  margin-top:clamp(56px,6vw,88px);
  max-width:940px;margin-inline:auto;
}
/* A single rule between the two, vertical on desktop and horizontal once they
   stack. Drawn on the second item so there is one line, not two. */
.stat{padding-top:30px;min-width:0;text-align:center}
.stat + .stat{
  border-left:1px solid var(--hair);
  padding-left:clamp(24px,3vw,56px);
}
/* One shared box height so the three captions sit on the same line, with a
   per-illustration scale inside it. The two artworks are drawn at different
   scales natively, so matching the box is not enough — --art has to bring the
   individual tiles to the same visual size. Height is on the wrapper, so the
   row is reserved before the images decode and nothing shifts. */
.stat-art{
  height:clamp(190px,23vw,330px);
  display:flex;align-items:center;justify-content:center;
  margin-bottom:26px;
}
/* The goals artwork is a wide composition; at a tile size matching the stacks
   it is fractionally wider than a third of the row. Everything behind it is
   #000, so letting it bleed into the grid gutter is invisible — and it is the
   only way to get both illustrations to the same tile size at this scale.
   Only above the 1-column breakpoint, where there is 80px of side padding to
   absorb it. */
@media (min-width:821px){
  .stat-art{margin-inline:-22px}
}
.stat-art img{
  height:calc(100% * var(--art,1));
  width:auto;max-width:100%;object-fit:contain;
}
/* --art equalises the *tiles*, not the image boxes: the two artworks are drawn
   at different native scales, so matching box heights would leave the goal
   tiles about 30% small. Measured off the render, not guessed. */
.stat--stacks{--art:1}    /* stat 2 — reference */
.stat--goals{--art:.79}   /* stat 3 — matched tile-for-tile */
.stat-cap{font-size:15px;line-height:1.5;color:var(--t2);max-width:32ch;margin-inline:auto}

/* ── comparison ──────────────────────────────────────────────────────── */
.compare{
  display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);
  margin-top:clamp(48px,5vw,72px);
}
.compare-col{padding-right:clamp(28px,4vw,72px)}
.compare-col + .compare-col{
  border-left:1px solid var(--hair);
  padding-left:clamp(28px,4vw,72px);
  padding-right:0;
}
.compare-head{
  font-size:12px;font-weight:500;letter-spacing:.14em;text-transform:uppercase;
  color:var(--t3);margin-bottom:28px;
}
.compare-col ul{list-style:none;display:flex;flex-direction:column;gap:18px}
.compare-col li{font-size:17px;line-height:1.5;color:var(--t2)}
.compare-col + .compare-col li{color:var(--t1)}

/* ── faq ─────────────────────────────────────────────────────────────── */
.faq{margin-top:clamp(40px,4vw,56px)}
.faq details{border-top:1px solid var(--hair)}
.faq details:last-of-type{border-bottom:1px solid var(--hair)}
.faq summary{
  list-style:none;cursor:pointer;
  display:flex;align-items:flex-start;justify-content:space-between;gap:28px;
  padding:26px 0;
  font-size:clamp(17px,1.4vw,19px);font-weight:500;line-height:1.45;color:var(--t1);
}
.faq summary::-webkit-details-marker{display:none}
.faq summary::after{
  content:"+";
  flex:none;display:inline-block;
  font-family:var(--body);font-size:24px;font-weight:400;line-height:1.1;
  color:var(--t3);
  transition:transform .25s ease,color .25s ease;
}
.faq details[open] summary::after{transform:rotate(45deg);color:var(--t1)}
.faq .answer{padding:0 0 28px;max-width:72ch;font-size:16px;line-height:1.65;color:var(--t2)}

/* ── waitlist.html ───────────────────────────────────────────────────── */
.page{padding-block:clamp(56px,7vw,96px) clamp(80px,8vw,124px)}
.page h1{max-width:18ch}
.page .signup{margin-top:clamp(32px,4vw,48px)}
.back-link{
  color:var(--t2);font-size:15px;line-height:1;text-decoration:none;
  transition:color .2s ease;
}
.back-link:hover{color:var(--t1)}

/* ── closing + footer ────────────────────────────────────────────────── */
/* Top matches the shared rhythm; only the run-out to the footer is longer. */
.closing{padding-block:clamp(60px,6.4vw,96px) clamp(80px,8vw,124px);text-align:center;scroll-margin-top:70px}
footer{border-top:1px solid var(--hair);padding-block:36px 48px}
footer .wrap{display:flex;align-items:center;gap:26px;flex-wrap:wrap}
footer nav{display:flex;gap:26px;flex-wrap:wrap}
footer a{color:var(--t3);font-size:15px;text-decoration:none;transition:color .2s ease}
footer a:hover{color:var(--t1)}

/* ── responsive ──────────────────────────────────────────────────────── */
/* Hero stacks earlier than the demo: the headline needs the full measure
   back before the columns get tight enough to wrap it. */
@media (max-width:1150px){
  .hero-grid{grid-template-columns:minmax(0,1fr);gap:44px}
  .hero-shot{max-width:360px;margin-inline:auto}
}
@media (max-width:900px){
  .demo-grid{grid-template-columns:minmax(0,1fr);gap:40px}
  /* Swipeable below this width, so show the position dots. */
  .dots{display:flex}
  .media--phone{touch-action:pan-y}
  /* Single column, but the phone is capped. At 834px an uncapped 1288:2380
     box would stand 1450px tall, which is not what "full width" means. */
  .demo-media{max-width:420px;margin-inline:auto;width:100%}
}
@media (max-width:820px){
  .stats{grid-template-columns:minmax(0,1fr);gap:0}
  /* Same single rule, turned horizontal now the pair is stacked. */
  .stat + .stat{
    border-left:0;padding-left:0;
    border-top:1px solid var(--hair);
    margin-top:44px;padding-top:44px;
  }
}
@media (max-width:760px){
  /* Mobile does not fill a three-part form inline. Both inline forms give way
     to a single button that opens waitlist.html. */
  /* Scoped to the hero and closing blocks — waitlist.html reuses .signup and
     must keep showing it. */
  .hero .signup,.closing .form{display:none}
  .btn.to-waitlist{display:inline-flex}
  .closing .btn.to-waitlist{margin-top:26px}

  .form{flex-direction:column;align-items:stretch}
  .form input,.form .btn{flex:1 1 auto;width:100%}
  .compare{grid-template-columns:minmax(0,1fr)}
  .compare-col{padding-right:0}
  .compare-col + .compare-col{
    border-left:0;border-top:1px solid var(--hair);
    padding-left:0;margin-top:40px;padding-top:40px;
  }
}
@media (max-width:640px){
  :root{--pad:24px;--bleed:24px}
  .btn{padding:12px 18px}
  .media{border-radius:20px}
}

@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{
    animation-duration:.001ms !important;animation-iteration-count:1 !important;
    transition-duration:.001ms !important;
  }
}
