/* Alibi landing page. Tokens first: change the palette here and everything follows. */
:root {
  --font-ui: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --font-lead: var(--font-ui);
  --font-hand: "Caveat", cursive;
  --ease-standard: cubic-bezier(.25, .46, .45, .94);
  --paper: #FBF6EE;
  --card: #FFFDF9;
  --sun: #F7B23B;
  --green: #2FA35C;
}

/* ---- minimal reset (what the page needs from Tailwind preflight) ---- */
*, ::before, ::after { box-sizing: border-box; border: 0 solid currentColor; }
html { line-height: 1.5; -webkit-text-size-adjust: 100%; font-family: var(--font-ui); }
body { margin: 0; background: var(--paper); }
h1, h2, h3, h4, h5, h6, p, hr, figure, blockquote { margin: 0; font-size: inherit; font-weight: inherit; }
ol, ul { list-style: none; margin: 0; padding: 0; }
a { color: inherit; text-decoration: inherit; }
img, svg, video, canvas { display: block; vertical-align: middle; }
img, video { max-width: 100%; height: auto; }
button, input { font: inherit; color: inherit; margin: 0; padding: 0; }
button { background: transparent; cursor: pointer; text-transform: none; -webkit-appearance: button; }
b, strong { font-weight: bolder; }
[hidden] { display: none !important; }

/* ---- the handful of utility classes the mirrored markup uses ---- */
.absolute { position: absolute; } .relative { position: relative; }
.inset-0 { inset: 0; } .block { display: block; } .flex { display: flex; } .hidden { display: none; }
.items-end { align-items: flex-end; }
.w-full { width: 100%; } .h-full { height: 100%; } .w-max { width: max-content; }
.w-\[58px\] { width: 58px; } .w-\[68px\] { width: 68px; }
.h-\[30px\] { height: 30px; } .h-\[58px\] { height: 58px; } .h-\[62px\] { height: 62px; }
.left-full { left: 100%; } .top-full { top: 100%; }
.bottom-\[34px\] { bottom: 34px; } .right-\[56px\] { right: 56px; }
.ml-1 { margin-left: .25rem; } .mr-2 { margin-right: .5rem; } .mt-1 { margin-top: .25rem; }
.-mt-1 { margin-top: -.25rem; } .-mb-1 { margin-bottom: -.25rem; } .-mr-1 { margin-right: -.25rem; }
.ml-auto { margin-left: auto; }
.object-cover { object-fit: cover; } .overflow-hidden { overflow: hidden; }
.pointer-events-none { pointer-events: none; } .select-none { user-select: none; -webkit-user-select: none; }
.rounded-\[16px\] { border-radius: 16px; }
@media (min-width: 640px) { .sm\:block { display: block; } .sm\:hidden { display: none; } }
@media (min-width: 1024px) { .lg\:hidden { display: none; } }

/* ---- pieces that lived in the original's Tailwind bundle ---- */
.ink-note { font-family: var(--font-hand); line-height: 1.3; letter-spacing: .01em; }
.vanish-mock-tap { color: #fff; background: var(--accent); border: 0; cursor: pointer; }
.vanish-mock-tap[data-on="true"] { background: #B03A2E; }
.vanish-mock-tap:after { content: ""; position: absolute; top: -14px; right: 0; bottom: -14px; left: 0; }
.vanish-mock-hint { position: absolute; inset: 0; border-radius: inherit; border: 2px solid rgba(255,255,255,.9); pointer-events: none;
  animation: vanishMockTap 2.1s var(--ease-standard) infinite; }
.vanish-mock-tap[data-on="true"] .vanish-mock-hint, .vanish-mock-tap[data-tapped="true"] .vanish-mock-hint { display: none; }
@keyframes vanishMockTap { 0% { transform: scale(1); opacity: .9 } 70%, to { transform: scale(1.07); opacity: 0 } }
@media (prefers-reduced-motion: reduce) { .vanish-mock-hint { animation: none; opacity: .5; transform: none; } }

/* ---- footer status pill ---- */
.v2-statuspill { display: inline-flex; align-items: center; gap: 8px; padding: 7px 13px 7px 11px; border-radius: 999px;
  background: var(--panel); border: 1px solid var(--line); font-family: var(--font-ui); font-size: 13px; font-weight: 500;
  letter-spacing: -0.005em; color: var(--muted); text-decoration: none; transition: color .18s ease, border-color .18s ease; }
.v2-statuspill:hover { color: var(--ink); border-color: #D3C3A8; }
.v2-statuspill__dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; }
.v2-statuspill__dot--operational { background: var(--green); box-shadow: 0 0 0 3px rgba(47,163,92,0.18); }

/* ---- toast for the pretend checkout ---- */
.toast { position: fixed; left: 50%; bottom: 28px; transform: translateX(-50%) translateY(12px); z-index: 300;
  padding: 13px 20px; border-radius: 14px; background: #16120E; color: #fff; font-size: 15px; font-weight: 600;
  border: 1px solid rgba(255,255,255,0.14);
  letter-spacing: -0.01em; box-shadow: 0 18px 40px -14px rgba(30,26,22,0.6); opacity: 0;
  transition: opacity .25s ease, transform .25s cubic-bezier(.22,1,.36,1); white-space: nowrap; max-width: 92vw; }
.toast[data-show="true"] { opacity: 1; transform: translateX(-50%) translateY(0); }


/* ===== design system (adapted) ===== */

.v2-root {
  --ink: #1E1A16;
  --ink-2: #2B2620;
  --ink-3: #4E453C;
  --ink-4: #6B6157;
  --muted: #7A6E62;
  --muted-2: #A2968A;
  --line: #E8DCC8;
  --line-2: #F1E8D9;
  --panel: #F4ECDF;
  --stage: #1E1A16;
  --accent: #F2622E;
  --accent-deep: #C9481B;

  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-ui);
  font-size: 16px;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}
.v2-root ::selection { background: rgba(242,98,46,0.22); }
.v2-root :focus-visible { outline: 2px solid var(--accent-deep); outline-offset: 3px; border-radius: 6px; }

/* ── Rhythm ─────────────────────────────────────────────────────────────── */
.v2-wrap { width: 100%; max-width: 1152px; margin: 0 auto; padding-inline: 20px; }
@media (min-width: 768px) { .v2-wrap { padding-inline: 32px; } }
.v2-section { padding-block: 44px; }
@media (min-width: 768px) { .v2-section { padding-block: 56px; } }
@media (min-width: 1024px) { .v2-section { padding-block: 68px; } }
.v2-rule { height: 1px; background: var(--line); border: 0; margin: 0; }

/* ── Hero ───────────────────────────────────────────────────────────────────
   Three layers. The aerial map sits underneath; a teal sky is poured over the
   top of it and fades out by two-thirds, which is what gives the headline a
   dark enough ground to be white on; a white fade at the foot hands off to the
   page. The very top is nearly the old Vanish near-black, so the header still
   reads as stealth before it warms into the map. */
.v2-hero { position: relative; overflow: hidden; background: #4A2416; }
.v2-hero__photo {
  position: absolute; inset: 0;
  background: url('../img/hero-map.webp') center 74% / cover no-repeat;
  filter: sepia(0.55) saturate(1.35) hue-rotate(-28deg) brightness(1.02);
}
/* Sits ABOVE the sky and multiplies into it — see the note on the photo. */
.v2-hero__photo--over {
  mix-blend-mode: multiply;
  -webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 26%, #000 84%, transparent 97%);
  mask-image: linear-gradient(180deg, transparent 0%, #000 26%, #000 84%, transparent 97%);
}
.v2-hero__sky {
  position: absolute; inset: 0;
  /* Stops sampled down the middle of cluely.com's hero and shifted ~10deg
     toward cyan. The shape is the point: it STARTS mid-blue and gets lighter
     all the way down to a near-white horizon. The previous near-black top was
     reading as murk — a sky does not get darker as it rises. */
  background: linear-gradient(180deg,
    #D2532A 0%,
    #E66A38 18%,
    #F29057 36%,
    rgba(247,178,110,0.82) 52%,
    rgba(250,222,190,0.46) 68%,
    rgba(251,246,238,0) 86%);
}
/* Cluely's edges lighten rather than blanking to white — measured at the sky
   band they run #97C6ED at the rim against #3A9DE1 in the core. Matched. */
.v2-hero__sides {
  position: absolute; inset: 0;
  background: linear-gradient(90deg,
    rgba(251,246,238,0.80) 0%, rgba(251,246,238,0.40) 11%, rgba(251,246,238,0) 32%,
    rgba(251,246,238,0) 68%, rgba(251,246,238,0.40) 89%, rgba(251,246,238,0.80) 100%);
}
.v2-hero__inner { position: relative; text-align: center; padding-top: 120px; padding-bottom: 96px; }
@media (min-width: 768px) { .v2-hero__inner { padding-top: 148px; padding-bottom: 132px; } }
.v2-hero__floor {
  position: absolute; left: 0; right: 0; bottom: 0; height: 42%;
  /* Long and eased. At 17% the wash still had colour in it when the section
     ended, so the hero met the page on a hard line. */
  background: linear-gradient(180deg,
    rgba(251,246,238,0) 0%, rgba(251,246,238,0.30) 30%,
    rgba(251,246,238,0.72) 56%, rgba(251,246,238,0.95) 68%, var(--paper) 80%);
}

/* ── Type ───────────────────────────────────────────────────────────────── */
.v2-h1 {
  font-family: 'EB Garamond', Iowan Old Style, Georgia, serif;
  font-weight: 200;
  font-size: clamp(40px, 8.4vw, 86px);
  /* 0.96 put the descender of "your" into the ascender of "anywhere" on the
     line below. Garamond's extenders are long; 1.06 is the tightest setting
     that clears them at every size in the clamp. Do not take it back down. */
  line-height: 1.06;
  letter-spacing: -0.012em;
  margin: 0;
}
.v2-h2 {
  font-weight: 500;
  font-size: clamp(29px, 4.6vw, 54px);
  line-height: 1.22;
  letter-spacing: -0.028em;
  margin: 0;
  background: linear-gradient(90deg, var(--ink-2) 0%, #7A6355 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.v2-h2--tight { background-image: linear-gradient(90deg, var(--ink-2) 0%, #3E322B 100%); }
.v2-h3 { font-size: 19px; font-weight: 550; letter-spacing: -0.02em; line-height: 1.35; margin: 0; color: var(--ink); }
.v2-lede { font-size: 17px; line-height: 1.55; letter-spacing: -0.011em; color: var(--muted); margin: 14px 0 0; }
.v2-body { font-size: 15.5px; line-height: 1.6; letter-spacing: -0.008em; color: var(--muted); margin: 8px 0 0; }
.v2-eyebrow { font-size: 12px; font-weight: 550; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted-2); margin: 0 0 14px; }

/* ── Buttons ────────────────────────────────────────────────────────────────
   The primary is ink, not blue. A blue gradient CTA is Cluely's signature and
   borrowing it made the page read as theirs; black with a cyan tell on hover is
   both stealthier and already in Vanish's own button system. */
.v2-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  height: 46px; padding: 0 22px; border-radius: 12px; border: 0;
  font-family: inherit; font-size: 15.5px; font-weight: 550; letter-spacing: -0.013em;
  text-decoration: none; cursor: pointer; white-space: nowrap;
  transition: transform 0.16s ease-out, box-shadow 0.22s ease, background-color 0.16s ease;
}
.v2-btn:active { transform: scale(0.975); }

.v2-btn--primary {
  color: #fff;
  background: linear-gradient(180deg, #FF7A45 0%, #E4531F 100%);
  box-shadow:
    inset 0 1px 0 0 rgba(255,255,255,0.28),
    inset 0 0 0 1px rgba(160,50,15,1),
    0 10px 26px -12px rgba(201,72,27,0.7);
}
.v2-btn--primary:hover {
  transform: translateY(-1px);
  box-shadow:
    inset 0 1px 0 0 rgba(255,255,255,0.36),
    inset 0 0 0 1px rgba(160,50,15,1),
    0 14px 34px -12px rgba(201,72,27,0.6),
    0 0 26px -6px rgba(242,98,46,0.5);
}
.v2-btn--onDark {
  color: var(--ink); background: #fff;
  box-shadow: 0 10px 26px -12px rgba(0,0,0,0.8);
}
.v2-btn--onDark:hover { transform: translateY(-1px); box-shadow: 0 14px 34px -12px rgba(0,0,0,0.8), 0 0 26px -6px rgba(242,98,46,0.55); }
.v2-btn--sm { height: 38px; padding: 0 16px; font-size: 14px; border-radius: 10px; }

/* ── Gloss sweep ────────────────────────────────────────────────────────────
   An occasional highlight running left to right across the button. Opt-in, and
   deliberately only on the download / send-link controls — putting it on the
   checkout button too would make the page twinkle and the effect would stop
   meaning "this is the thing to press".

   The pass takes ~1s of a 6s cycle, so it reads as a glint rather than a
   loading shimmer. Transform-only, so it composites and never lays out. */
.v2-btn--sheen { position: relative; overflow: hidden; }
.v2-btn--sheen::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(105deg,
    transparent 36%, var(--sheen, rgba(255,255,255,0.42)) 50%, transparent 64%);
  transform: translateX(-130%);
  animation: v2Sheen 6s cubic-bezier(0.3, 0.5, 0.35, 1) 2s infinite;
}
/* On the white button a white sweep is invisible, so it glints brand cyan. */
.v2-btn--onDark { --sheen: rgba(242,98,46,0.30); }
@keyframes v2Sheen {
  0%   { transform: translateX(-130%); }
  17%  { transform: translateX(130%); }
  100% { transform: translateX(130%); }
}


/* ── Header ─────────────────────────────────────────────────────────────── */
.v2-header { position: fixed; inset: 0 0 auto; z-index: 60; height: 62px; display: flex; align-items: center;
  transition: background-color 0.25s ease, box-shadow 0.25s ease; }
.v2-header--stuck {
  background: rgba(251,246,238,0.84);
  backdrop-filter: saturate(180%) blur(16px); -webkit-backdrop-filter: saturate(180%) blur(16px);
  box-shadow: 0 1px 0 var(--line);
}
.v2-header__inner { display: flex; align-items: center; gap: 8px; width: 100%; }
/* App icon plus the name set beside it, the way Cluely sets theirs. The mark
   already carries the brand, so the name is the UI sans rather than a second
   display face — one characterful face on the page (the Garamond headline) is
   the whole point of the pairing. */
.v2-brand { display: flex; align-items: center; gap: 4px; margin-right: 28px; text-decoration: none; }
/* The outlined mark is a white square: perfect over the hero, invisible once
   the header goes white. Both variants sit stacked and cross-fade with the bar,
   the scrolled one being the same 3D mark the email sheet uses. */
.v2-brand__icon { position: relative; width: 27px; height: 27px; flex: none; }
.v2-brand__icon img { position: absolute; inset: 0; display: block; width: 100%; height: 100%; transition: opacity 0.25s ease; }
.v2-brand__icon .on-color { opacity: 1; }
.v2-brand__icon .on-white { opacity: 0; }
.v2-header--stuck .v2-brand__icon .on-color { opacity: 0; }
.v2-header--stuck .v2-brand__icon .on-white { opacity: 1; }
.v2-brand span {
  font-size: 19.5px; font-weight: 600; letter-spacing: -0.028em; color: #fff;
  transition: color 0.25s ease;
}
.v2-header--stuck .v2-brand span { color: var(--ink); }
.v2-nav { display: none; align-items: center; gap: 4px; }
@media (min-width: 860px) { .v2-nav { display: flex; } }
.v2-nav a {
  padding: 8px 13px; font-size: 14.5px; font-weight: 500; letter-spacing: -0.012em;
  text-decoration: none; color: rgba(255,255,255,0.80); border-radius: 8px;
  transition: color 0.18s ease, background-color 0.18s ease;
}
.v2-nav a:hover { color: #fff; background: rgba(255,255,255,0.10); }
.v2-header--stuck .v2-nav a { color: var(--ink-3); }
.v2-header--stuck .v2-nav a:hover { color: var(--ink); background: rgba(30,26,22,0.06); }

/* The download control at top-right. Hidden while the hero — which already
   carries the same action — is on screen, so the two never sit up at once. */
.v2-header__cta {
  display: flex; align-items: center; gap: 12px;
  opacity: 0; visibility: hidden;
  transition: opacity 0.3s ease, visibility 0.3s ease;
}
.v2-header__cta[data-shown='true'] { opacity: 1; visibility: visible; }
/* Holds the right-hand group together so Manage Billing keeps its place beside
   the download button instead of drifting when the button fades out. */
.v2-header__right { margin-left: auto; display: flex; align-items: center; gap: 16px; }
/* Existing customers only, so it stays quieter than the nav and much quieter
   than the CTA — it must be findable without competing with the one action the
   page is asking a new visitor to take. Unlike the CTA it is always on: someone
   arriving to fix a card has no reason to scroll first. */
.v2-header__billing {
  font-size: 13.5px; font-weight: 500; letter-spacing: -0.01em; white-space: nowrap;
  text-decoration: none; color: rgba(255,255,255,0.62);
  transition: color 0.18s ease;
}
.v2-header__billing:hover { color: #fff; }
.v2-header--stuck .v2-header__billing { color: var(--muted-2); }
.v2-header--stuck .v2-header__billing:hover { color: var(--ink); }
/* On a phone the CTA alone already fills the bar. */
@media (max-width: 599px) { .v2-header__billing { display: none; } }

/* ── Cards ──────────────────────────────────────────────────────────────── */
.v2-card { border-radius: 20px; background: var(--card); padding: 26px; box-shadow: 0 0 0 1px var(--line), 0 1px 2px rgba(16,18,32,0.04); }
.v2-card--fill { background: var(--panel); box-shadow: 0 0 0 1px var(--line-2); }
.v2-grid { display: grid; gap: 16px; }
.v2-grid--2, .v2-grid--3, .v2-grid--4, .v2-grid--split { grid-template-columns: 1fr; }
@media (min-width: 620px) { .v2-grid--4 { grid-template-columns: repeat(2, 1fr); gap: 22px; } }
@media (min-width: 880px) {
  .v2-grid--2 { grid-template-columns: repeat(2, 1fr); gap: 24px; }
  .v2-grid--3 { grid-template-columns: repeat(3, 1fr); gap: 24px; }
  .v2-grid--4 { grid-template-columns: repeat(4, 1fr); gap: 24px; }
  .v2-grid--split { grid-template-columns: 0.9fr 1.1fr; gap: 64px; align-items: center; }
}

/* ── Hero phones ────────────────────────────────────────────────────────────
   Two devices, as v1 had them: the rotating shots on the left, the playable
   mock on the right. The shells are the v1 ones re-lit for a white page —
   a hairline instead of a white border, and a softer shadow. */
/* perspective + translateZ, which is how cluely.com does its demo entrance:
   the element starts pushed toward the viewer, so it enters oversized and
   recedes into place rather than just fading up. At 800px of perspective a
   350px push renders it 800/(800-350) = 1.78x, which is the whole effect. */
.v2-phones { display: flex; align-items: flex-start; justify-content: center; gap: 26px; }
.v2-flyin { animation: v2FlyIn 0.95s cubic-bezier(0.22, 1, 0.36, 1) var(--flyin-delay, 0s) both; }
@keyframes v2FlyIn {
  from { opacity: 0; transform: translateY(28px) scale(0.96); }
  to { opacity: 1; transform: none; }
}
.v2-phone {
  position: relative; aspect-ratio: 9 / 19.5; border-radius: 30px; overflow: hidden;
  background: #0A0C10; flex: none;
  box-shadow: 0 0 0 1px rgba(10,12,16,0.10), 0 40px 80px -36px rgba(10,12,16,0.55);
}

/* HandwrittenNote is drawn in cream for the v1 dark page. Its ink colour lives
   on a class and its arrow colour on a presentation attribute, and a plain CSS
   rule outranks both — so it can be re-inked from out here without touching a
   component the /legacy page still renders. */
.v2-note .ink-note { color: var(--ink-2); }
.v2-note svg path { stroke: var(--ink-3); opacity: 1; }

/* ── Section heading group ──────────────────────────────────────────────────
   Centred name, one quiet line under it. The left-ranged three-line gradient
   heading this replaces was doing all the shouting in a page whose whole idea
   is restraint. */
.v2-hgroup { text-align: center; max-width: 640px; margin: 0 auto 40px; }
/* --font-lead (SF Pro Display) is applied to every rule below whose SMALLEST
   size stays comfortably above ~24px. Anything that drops under that on a
   handset — .v2-num__label falls to 19px, the /status headline sits at 21 —
   deliberately keeps Text, because Display at those sizes is the cramped look
   this split exists to avoid. */
.v2-hgroup h2 { font-family: var(--font-lead); font-size: clamp(28px, 3.6vw, 40px); font-weight: 550; letter-spacing: -0.032em; line-height: 1.18; margin: 0; color: var(--ink); }
.v2-hgroup p { margin: 12px 0 0; font-size: 16px; line-height: 1.5; letter-spacing: -0.01em; color: var(--muted); }

/* Feature card: visual on top, one sentence under it that leads with the claim
   in ink and finishes in grey. Cluely's "Undetectable in every way" shape. */
.v2-fcard p { margin: 16px 0 0; font-size: 15px; line-height: 1.55; letter-spacing: -0.009em; color: var(--muted); }
.v2-fcard b { font-weight: 600; color: var(--ink); }

/* v1's note, re-inked to match "tap this". Absolutely placed with a negative
   top so it lives in the gap that was already there between the phones and the
   bar — it costs the layout no height. Hidden on narrow screens, where that gap
   is smaller and the note would land on the icons. */
.v2-anywhere { position: absolute; left: 24%; top: -92px; width: max-content; z-index: 2; pointer-events: none; }
/* Below this the hero's gap is gone, so the note joins the flow: centred,
   and without the arrow, which has nothing to point at once it is not beside
   the bar. The svg is the arrow — the words are a span. */
@media (max-width: 1000px) {
  .v2-anywhere { position: static; width: auto; margin: 0 auto 4px; text-align: center; }
  /* The arrow's wrapper carries a fixed height class, so hiding just the <svg>
     inside it leaves the box — and the gap. Hide the wrapper. */
  .v2-anywhere > div > div { display: none; }
}

/* Bottom-aligned so the laptop base and the phone sit on one line, as v1 had
   them. Both screens share a height, which is what makes the pair read as one
   photograph rather than two pasted images. */
.v2-island {
  position: absolute; top: 1.3%; left: 50%; transform: translateX(-50%);
  width: 31%; aspect-ratio: 3.4 / 1; border-radius: 999px; background: #000; z-index: 3;
  /* A real island is a hole, not a shape, so it carries no outline. Over dark
     map footage that leaves it invisible, so it gets the faintest rim — enough
     to read as a cutout, not enough to read as drawn. */
  box-shadow: inset 0 0 0 0.5px rgba(255,255,255,0.07);
}

/* No pad behind it — the phone stands on the page. Sized to run nearly the
   full height of the numbers column beside it. */
.v2-numshot { --phone-h: clamp(340px, 47vw, 660px); display: grid; place-items: center; }
.v2-numshot__screen { position: relative; height: var(--phone-h); aspect-ratio: 9 / 19.5; overflow: hidden; }
.v2-numshot__screen img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }

.v2-devices { --phone-h: clamp(150px, 25vw, 360px); display: flex; align-items: flex-end; justify-content: center; gap: 4%; }
.v2-devices__mac { height: var(--phone-h); flex: none; }
.v2-devices__phone { position: relative; height: var(--phone-h); aspect-ratio: 9 / 19.5; overflow: hidden; }

/* ── App strip ──────────────────────────────────────────────────────────── */
.v2-strip { position: relative; overflow: hidden; width: 100%; padding-block: 20px; }
.v2-strip::after { content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(90deg, var(--paper) 0%, rgba(251,246,238,0) 16%, rgba(251,246,238,0) 84%, var(--paper) 100%); }
.v2-strip__track { display: flex; width: max-content; animation: v2Marquee 46s linear infinite; will-change: transform; }
/* The trailing gap belongs to the set, not the track, so one set plus its gap
   is exactly 1/COPIES of the track width — which is what lets the shift below
   be a clean percentage and land on an identical frame. */
.v2-strip__set { display: flex; gap: 14px; padding-right: 14px; flex: none; }
.v2-strip__icon { width: 52px; height: 52px; border-radius: 13px; overflow: hidden; flex: none;
  box-shadow: 0 0 0 1px rgba(16,18,32,0.06), 0 4px 12px -4px rgba(16,18,32,0.16); }
.v2-strip__icon img { display: block; width: 100%; height: 100%; object-fit: cover; }
/* Runs left-to-right: the track starts one set to the left and slides back to
   zero. Two copies left a third of a wide screen empty at the start of the
   cycle — the track has to be at least one set WIDER than the viewport for the
   offset frame to still reach the right edge, hence COPIES below.
   Both copies of every icon share one src, so each file is fetched once at
   load and reused; the loop never re-requests anything. */
@keyframes v2Marquee {
  from { transform: translateX(calc(-1 * var(--marquee-shift, 20%))); }
  to { transform: translateX(0); }
}

/* ── Numbers ────────────────────────────────────────────────────────────── */
.v2-num { display: grid; grid-template-columns: 1fr; gap: 4px; padding: 30px 0; border-top: 1px solid var(--line); }
@media (min-width: 620px) { .v2-num { grid-template-columns: 128px 1fr; gap: 28px; align-items: start; } }
.v2-num:first-child { border-top: 0; padding-top: 0; }
.v2-num:last-child { padding-bottom: 0; }
/* Taken out of the flow: inline, the dot pushed the 7 right and broke the
   column that 4, 1 and 0.32 all sit on. Absolute at -19px keeps exactly the
   spacing it had (9px dot + its old 10px margin) with the digit back in line. */
.v2-num__dot {
  position: absolute; left: -19px; top: 50%; transform: translateY(-50%);
  width: 9px; height: 9px; border-radius: 999px; background: #22C55E;
  animation: v2Pulse 2.1s ease-in-out infinite;
}
/* Single-column rows have no gutter to hang it in, so it goes back inline. */
@media (max-width: 619px) {
  .v2-num__dot { position: static; transform: none; display: inline-block; margin-right: 10px; vertical-align: 0.34em; }
}
@keyframes v2Pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(34,197,94,0.55); }
  60%      { box-shadow: 0 0 0 7px rgba(34,197,94,0); }
}
.v2-num__value { position: relative; font-family: var(--font-lead); font-size: clamp(38px, 4.6vw, 50px); font-weight: 500; letter-spacing: -0.05em; line-height: 0.96; color: var(--ink); font-variant-numeric: tabular-nums; }
.v2-num__unit { font-size: 0.5em; letter-spacing: -0.02em; color: var(--muted-2); margin-left: 1px; }
.v2-num__label { font-size: clamp(21px, 2.4vw, 28px); font-weight: 500; letter-spacing: -0.03em; line-height: 1.2; color: var(--ink); }
.v2-num__body { margin: 9px 0 0; font-size: 15.5px; line-height: 1.55; letter-spacing: -0.008em; color: var(--muted); max-width: 46ch; }
/* One column costs every row a value, a label AND a body stacked, where the
   two-column desktop row spends the value and the text side by side. The 30px
   rhythm that reads as air next to the phone therefore compounds into ~200px a
   row, and four of those ran the section past three screens on a handset. Same
   content and the same hierarchy, on a tighter gutter.
   The phone deliberately keeps its full height. Shrinking it bought ~135px and
   cost the section the one thing in it that is the product — at 200px it read
   as a thumbnail of a screenshot rather than a phone. The rows are where the
   height was actually going. */
@media (max-width: 619px) {
  .v2-num { padding: 16px 0; }
  .v2-num__value { font-size: 34px; }
  .v2-num__label { font-size: 19px; }
  .v2-num__body { margin-top: 6px; font-size: 14.5px; line-height: 1.5; }
}

/* ── Pricing ────────────────────────────────────────────────────────────── */
.v2-tabs { display: flex; padding: 4px; border-radius: 13px; background: var(--panel); box-shadow: inset 0 0 0 1px var(--line-2); }
.v2-tab { flex: 1; height: 36px; border: 0; border-radius: 10px; background: transparent; cursor: pointer;
  font-family: inherit; font-size: 13.5px; font-weight: 550; letter-spacing: -0.012em; color: var(--muted);
  transition: color 0.18s ease, background-color 0.18s ease, box-shadow 0.18s ease; }
.v2-tab:hover { color: var(--ink); }
.v2-tab[aria-selected='true'] { color: var(--ink); }
.v2-tab__pill { position: absolute; inset: 0; border-radius: 10px; background: var(--card);
  box-shadow: 0 0 0 1px var(--line), 0 1px 2px rgba(16,18,32,0.06); }
.v2-price { display: flex; align-items: flex-end; gap: 8px; }
.v2-price__amount { font-family: var(--font-lead); font-size: 60px; font-weight: 500; letter-spacing: -0.045em; line-height: 1; font-variant-numeric: tabular-nums; }
.v2-price__suffix { font-size: 16px; color: var(--muted-2); padding-bottom: 9px; }
.v2-badge { display: inline-flex; align-items: center; height: 22px; padding: 0 9px; border-radius: 999px; font-size: 11.5px; font-weight: 600; }
.v2-feat { display: flex; align-items: center; gap: 9px; font-size: 14.5px; letter-spacing: -0.01em; color: var(--ink-3); }

/* ── Pricing board ──────────────────────────────────────────────────────────
   Structurally v1's PricingToggle: free strip on top, the Vanish Mobile switch
   docked as the card's header, four period tabs over a tweened price, one CTA,
   and the feature row underneath. Re-lit for white; the behaviour is v1's. */
.v2-free {
  display: flex; flex-direction: column; gap: 12px; justify-content: space-between;
  padding: 16px 20px; border-radius: 16px; margin-bottom: 12px;
  background: var(--card); box-shadow: 0 0 0 1px var(--line), 0 1px 2px rgba(60,40,20,0.05);
}
@media (min-width: 560px) { .v2-free { flex-direction: row; align-items: center; gap: 16px; } }
.v2-free__t { margin: 0; font-size: 14px; font-weight: 600; letter-spacing: -0.012em; color: var(--ink); }
.v2-free__t span { color: var(--muted-2); font-weight: 500; }
.v2-free__s { margin: 2px 0 0; font-size: 12.5px; color: var(--muted-2); letter-spacing: -0.006em; }

/* position + isolation are for the connector line below, not for the card. The
   line is z-index: -1, and without a stacking context here it would sink behind
   this element's own white background and vanish entirely. */
.v2-board { border-radius: 20px; overflow: hidden; background: var(--card);
  position: relative; isolation: isolate;
  box-shadow: 0 0 0 1px var(--line), 0 8px 24px -16px rgba(16,18,32,0.22); }

/* Ties the iPhone-app row to the switch that governs it. Sits behind every
   in-flow element in the board, so the middle of its run is swallowed by the
   tab pill's opaque panel and the Best value badge, and only the two ends
   survive — which is the whole effect. It stays readable through .v2-mob's
   band because that fill is a translucent tint, not a solid.
   Endpoints are measured, not hard-coded: the row's right edge moves with the
   font and the switch moves with the board width. */
.v2-incl__link { position: absolute; inset: 0; z-index: -1; pointer-events: none; overflow: visible; }
.v2-incl__link path { fill: none; stroke: var(--accent-deep); stroke-width: 1.5; stroke-linecap: round; opacity: 0.45; transition: stroke 160ms ease; }
.v2-incl__link--off path { stroke: #B03A2E; }

/* The switch row is the one upsell on the board, so it gets a tinted band and
   the whole row is the control — a 51px target at the right edge is the hardest
   thing here to hit on a phone, and the row is what people actually tap. */
.v2-mob {
  display: flex; align-items: center; gap: 14px; width: 100%; text-align: left;
  padding: 15px 20px; border: 0; cursor: pointer;
  background: linear-gradient(180deg, rgba(242,98,46,0.11) 0%, rgba(242,98,46,0.05) 100%);
  box-shadow: inset 0 -1px 0 0 var(--line);
  transition: background 0.3s ease;
}
.v2-mob[aria-disabled='true'] { cursor: default; }
/* drop-shadow, not box-shadow: the icon is a transparent PNG with a squircle in
   it, and box-shadow would draw a hard square behind the rounded corners. Kept
   calm — the band it sits on is already a tint, and anything heavier turns a
   34px icon into a floating chip. */
.v2-mob__glyph { width: 34px; height: 34px; flex: none; display: block;
  filter: drop-shadow(0 2px 5px rgba(16,18,32,0.18));
  transition: opacity 0.25s ease, filter 0.25s ease; }
.v2-mob__glyph--off { opacity: 0.55; filter: grayscale(1) drop-shadow(0 2px 5px rgba(16,18,32,0.18)); }
.v2-mob__t { margin: 0; font-size: 14px; font-weight: 600; letter-spacing: -0.012em; color: var(--ink); }
.v2-mob__s { margin: 2px 0 0; font-size: 12.5px; color: var(--muted); letter-spacing: -0.006em; }
.v2-mob__note { margin-left: auto; margin-right: 12px; font-size: 13px; font-weight: 600; color: var(--accent-deep); white-space: nowrap; }
/* Drawn, not interactive: a button inside a button is invalid and double-fires. */
.v2-switch { width: 46px; height: 28px; border-radius: 999px; flex: none; padding: 3px;
  background: #DCD0BC; transition: background 0.25s ease; display: flex; }
.v2-switch__knob { width: 22px; height: 22px; border-radius: 999px; background: #fff;
  box-shadow: 0 1px 3px rgba(16,18,32,0.28); transition: transform 0.25s cubic-bezier(0.4,0,0.2,1); }
.v2-mob[aria-checked='true'] .v2-switch { background: var(--accent); }
.v2-mob[aria-checked='true'] .v2-switch__knob { transform: translateX(18px); }
/* Under ~480px the flex row runs out of middle. The glyph, the price and the
   switch are all fixed width, and with the padding they claim ~240px of it — so
   on a 350px board the title and the sentence were being asked to share 110px.
   "Include Vanish / Mobile" broke in half and a one-line sentence stacked four
   deep, which is the whole ugliness: nothing here is too long, the container
   was too narrow for the shape it was in.
   Same pieces, two rows instead of one. The title gets the width it needs and
   the switch stays on its line, because those two are the question being asked.
   The price sits directly under the title, where it reads as that title's own
   number rather than as a third column. The sentence spans the full width below
   both, where it costs two lines instead of four. Nothing is smaller — the row
   is shorter because the words stopped wrapping.
   display: contents dissolves the <span> that groups the title and the sentence
   so this grid can place them individually; the alternative is forking the JSX
   for one breakpoint. */
@media (max-width: 479px) {
  .v2-mob {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    grid-template-areas:
      "icon title  switch"
      "icon price  switch"
      "icon body   body";
    column-gap: 13px; align-items: center;
    /* Top-heavy on purpose. Matched to the bottom it read as cramped: the
       sentence ends the band on a short second line, so the space under it
       is bounded by one word and reads wider than it measures, while the
       title runs the full width and needs real air above it to clear the
       band's edge. Measure this one by eye, not by making the numbers match. */
    padding: 14px 18px 10px;
    /* The one number that sets the spacing between title, price and sentence.
       row-gap applies to both gaps identically, so they cannot drift apart the
       way three hand-tuned margins do. The tight line-heights below are what
       make it the real gap: on the inherited leading the boxes already sat ~20px
       apart before any margin was added, which is why the margins that used to
       be here were 1px and -5px and still looked uneven. */
    row-gap: 9px;
  }
  .v2-mob__text { display: contents; }
  .v2-mob__glyph { grid-area: icon; }
  /* No top margin: the band's own padding is now the only space above the
     title, which is what lets it match the space under the sentence. The icon
     and the switch re-centre on their own across the rows they span. */
  .v2-mob__t { grid-area: title; margin: 0; line-height: 1.15; }
  /* margin-left: auto was what pushed it to the far edge of the flex row; on the
     grid the area places it and the auto would fight the column. */
  .v2-mob__note { grid-area: price; margin: 0; line-height: 1.15; }
  /* -2px is leading compensation, not spacing. row-gap puts an identical 9px
     between all three boxes, but this one wraps to two lines so it runs at
     line-height 1.4 against the 1.15 above it — and half of that extra leading
     sits above its first line, which made the gap under the price read ~2px
     wider than the gap over it. Taking the 2px back centres the price between
     its neighbours and is the 2px the band's top padding gives up as well. */
  .v2-mob__s { grid-area: body; margin: -2px 0 0; line-height: 1.4; }
  /* Centred across the title and price rows, then nudged: dead centre it read
     as belonging to the title alone rather than to the pair. */
  .v2-switch { grid-area: switch; margin-top: 5px; }
}

.v2-board__body { padding: 26px; }
.v2-plan-note { margin: 10px 0 0; font-size: 13.5px; color: var(--muted-2); letter-spacing: -0.008em; }
/* What the licence includes, inside the board and above the CTA: below the
   button it was read after the decision rather than before it. Two columns so
   six lines cost four rows of height instead of six.
   The mobile line is a full-width row above the grid rather than a cell in it,
   so it reads as the thing the switch governs and not as one more feature. */
.v2-incl { margin-top: 22px; padding-top: 18px; border-top: 1px solid var(--line); }
.v2-incl__grid { display: grid; grid-template-columns: 1fr 1fr; gap: 9px 18px; }
.v2-incl .v2-feat { font-size: 12.5px; gap: 7px; color: var(--ink-4); }
/* A flex container so the row inside shrinks to its text. .v2-feat is
   display: flex and would otherwise stretch the full width of the board, and
   the connector line anchors to its right edge — which has to be the last
   glyph of "App", not the far side of the card. */
.v2-incl__mob { display: flex; padding-bottom: 9px; }
/* Weight stays 600 in both states: off is the case worth noticing, so the line
   must not read as disabled text. Only the colour and the glyph move. */
.v2-incl__mob .v2-feat { font-weight: 600; color: var(--accent-deep); transition: color 160ms ease; }
.v2-incl__mob--off .v2-feat { color: #B03A2E; }
@media (max-width: 460px) { .v2-incl__grid { grid-template-columns: 1fr; } }

/* ── FAQ ────────────────────────────────────────────────────────────────── */
.v2-faq { border-top: 1px solid var(--line); }
.v2-faq__row { border-bottom: 1px solid var(--line); }
.v2-faq__q { display: flex; align-items: center; justify-content: space-between; gap: 20px; width: 100%;
  padding: 22px 0; border: 0; background: none; cursor: pointer; text-align: left;
  font-family: inherit; font-size: 17px; font-weight: 500; letter-spacing: -0.018em; color: var(--ink); }
.v2-faq__sign { flex: none; width: 20px; height: 20px; color: var(--muted-2); transition: transform 0.28s cubic-bezier(0.22,1,0.36,1); }
.v2-faq__q[aria-expanded='true'] .v2-faq__sign { transform: rotate(45deg); }
.v2-faq__a { overflow: hidden; display: grid; grid-template-rows: 0fr; transition: grid-template-rows 0.32s cubic-bezier(0.22,1,0.36,1); }
.v2-faq__a[data-open='true'] { grid-template-rows: 1fr; }
.v2-faq__a > div { overflow: hidden; }
.v2-faq__a p { margin: 0; padding: 0 44px 24px 0; font-size: 15.5px; line-height: 1.62; letter-spacing: -0.008em; color: var(--muted); }

/* ── Closing stage ──────────────────────────────────────────────────────── */
.v2-stage { position: relative; overflow: hidden; background: var(--stage); border-radius: 30px; }
.v2-stage::before { content: ''; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(ellipse 66% 78% at 50% 42%, rgba(242,98,46,0.42) 0%, rgba(242,98,46,0.14) 42%, transparent 72%);
}
.v2-stage__inner { position: relative; z-index: 1; }

/* ── Footer ─────────────────────────────────────────────────────────────── */
/* The page does not end on flat white. A cool blue-grey wash builds from
   nothing at the top of the footer to its full value at the bottom edge, so the
   document closes rather than just stopping.

   Straight onto .v2-footer, not a pseudo-element: the footer is already the
   full-bleed element and .v2-wrap is the capped column inside it, so the
   gradient spans the viewport for free. The alpha ramp rather than solid stops
   means whatever a section above happens to paint still shows through the top
   of the fade instead of being cut off by a hard edge.

   The last two stops are the same value so the bottom of the page settles into
   a flat field, instead of a ramp still visibly climbing when it runs out. */
.v2-footer {
  padding-block: 60px 40px;
  background: linear-gradient(180deg,
    rgba(239,227,208,0) 0%,
    rgba(239,227,208,0.42) 38%,
    rgba(239,227,208,0.82) 70%,
    #EFE3D0 92%,
    #EFE3D0 100%);
}
.v2-footer__cols { display: grid; grid-template-columns: repeat(2, 1fr); gap: 32px 20px; }
/* Brand block plus four link columns. Was repeat(3) when there were three;
   a fourth added without widening this wrapped Compare onto its own row. */
@media (min-width: 760px) { .v2-footer__cols { grid-template-columns: 1.5fr repeat(4, 1fr); gap: 36px; } }
.v2-footer h4 { font-size: 12px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted-2); margin: 0 0 16px; }
.v2-footer ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 11px; }
.v2-footer a { font-size: 14.5px; letter-spacing: -0.01em; color: var(--muted); text-decoration: none; }
.v2-footer a:hover { color: var(--ink); }
/* Copyright and the status badge share the closing rule. They stack on a narrow
   screen rather than shrinking the badge, whose label is a whole sentence. */
.v2-footer__bottom { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; }

/* ── Reveal ─────────────────────────────────────────────────────────────── */
.v2-reveal { opacity: 0; transform: translateY(20px); transition: opacity 0.6s cubic-bezier(0.22,1,0.36,1), transform 0.6s cubic-bezier(0.22,1,0.36,1); }
.v2-reveal[data-shown='true'] { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .v2-root *, .v2-root *::before, .v2-root *::after {
    /* Delay has to go too, not just duration: the fly-in fills both ways, so a
       0.45s delay would hold a reduced-motion visitor on the invisible first
       frame and then pop the phones in — worse than the animation it replaces. */
    animation-duration: 0.001ms !important; animation-delay: 0s !important;
    animation-iteration-count: 1 !important; transition-duration: 0.001ms !important;
  }
  .v2-reveal { opacity: 1; transform: none; }
}

