:root {
  color-scheme: light;
  --ep-font-sans: "Avenir Next", "Segoe UI Variable", "Segoe UI", Arial, sans-serif;
  --ep-font-mono: "SFMono-Regular", Consolas, "Liberation Mono", monospace;
  /*
   * The 2026 palette: near-white, iOS system colours, no warm paper and no
   * hard offset shadows.
   *
   * The previous language -- cream ground, two-pixel outlines, a 7px solid
   * shadow under everything -- belonged to a product about making things by
   * hand. This one is about reading numbers, where the interface should be
   * quiet enough that a green figure is the loudest thing on screen.
   *
   * Colour now carries meaning rather than personality: blue is action, green
   * is a number that moved the right way, red is one that did not, and the
   * rest exist only to tell categories apart.
   */
  --ep-ink: #1d1d1f;
  --ep-ink-rgb: 29, 29, 31;
  --ep-paper: #ffffff;
  --ep-surface: #ffffff;
  --ep-surface-2: #f5f5f7;
  --ep-muted: #6e6e73;
  --ep-line: #e5e5ea;

  /*
   * Measured, not assumed. iOS systemBlue #007aff is only 4.02:1 against
   * white -- it fails AA for normal text, which is why Apple's own website
   * uses this deeper #0071e3 (4.70:1) for anything with white text on it.
   * The positive green is chosen the same way: #15803d carries white at
   * 5.02:1 and reads as text on white at the same ratio, where iOS
   * systemGreen manages neither. The bright system hues still exist below
   * for strokes and fills that carry no text.
   */
  --ep-accent: #0071e3;
  --ep-accent-contrast: #ffffff;
  --ep-accent-deep: #0060df;
  --ep-positive: #15803d;
  --ep-danger: #d70015;

  /* iOS system hues, with tints for surfaces that need to read as a category. */
  --ep-yellow: #ff9500;
  --ep-blue: #007aff;
  --ep-green: #34c759;
  --ep-purple: #af52de;
  --ep-yellow-soft: #fff3e0;
  --ep-blue-soft: #e8f1ff;
  --ep-green-soft: #e6f7ec;
  --ep-purple-soft: #f4ebfd;

  --ep-radius-sm: 12px;
  --ep-radius-md: 18px;
  --ep-radius-lg: 28px;
  --ep-border-width: 1px;

  /*
   * Both shadows are soft now. --ep-shadow-flat keeps its name because it is
   * referenced across three apps; what it means has changed, and changing the
   * value in one place was the point of it being a token.
   */
  --ep-shadow-flat: 0 1px 2px rgba(var(--ep-ink-rgb), .04), 0 12px 32px -12px rgba(var(--ep-ink-rgb), .14);
  --ep-shadow-soft: 0 2px 6px rgba(var(--ep-ink-rgb), .05), 0 32px 64px -24px rgba(var(--ep-ink-rgb), .18);
  --ep-content: 1240px;
  --ep-ease: cubic-bezier(.2, .8, .2, 1);
  font-family: var(--ep-font-sans);
  font-synthesis: none;
}

/*
 * Dark follows the same system: near-black iOS surfaces rather than the old
 * sepia. epoyo.com sets this attribute on its own root element and ships with
 * it already set to dark; the account portal does not set it at all, so the
 * portal stays light and these values reach only what opts in.
 */
:root[data-appearance="dark"] {
  color-scheme: dark;
  --ep-ink: #f5f5f7;
  --ep-ink-rgb: 245, 245, 247;
  --ep-paper: #0b0b0d;
  --ep-surface: #17181b;
  --ep-surface-2: #2c2c2e;
  --ep-muted: #98989d;
  --ep-line: #38383a;
  --ep-accent-contrast: #ffffff;
  --ep-shadow-soft: 0 28px 90px rgba(0, 0, 0, .5);

  /*
   * The soft washes, inverted rather than kept.
   *
   * Each is a tint of its hue mixed into white -- a quiet background for dark
   * text on a white page. Left alone on a dark theme they stay near-white while
   * everything written on them turns near-white too, which is how the
   * recommended plan card ended up as cream with invisible copy on it. The dark
   * set mixes the same hues into the ground instead, so a tinted surface is
   * still quietly that colour and still has the page ink on it.
   */
  --ep-yellow-soft: #3a2a0f;
  --ep-blue-soft: #12233d;
  --ep-green-soft: #0f2e1c;
  --ep-purple-soft: #291a3c;
}

:root[data-surface="plain"] { --ep-grid-opacity: 0; }
:root[data-surface="tinted"] { --ep-paper: color-mix(in srgb, var(--ep-accent) 6%, #ffffff); }
:root[data-appearance="dark"][data-surface="tinted"] { --ep-paper: color-mix(in srgb, var(--ep-accent) 8%, #17150f); }
:root[data-depth="soft"] { --ep-border-width: 1px; --ep-shadow-flat: var(--ep-shadow-soft); }
:root[data-depth="none"] { --ep-border-width: 1px; --ep-shadow-flat: none; }

*, *::before, *::after { box-sizing: border-box; }
html { min-width: 320px; min-height: 100%; scroll-behavior: smooth; background: var(--ep-paper); }
body { min-height: 100vh; margin: 0; color: var(--ep-ink); background: var(--ep-paper); font-family: var(--ep-font-sans); text-rendering: optimizeLegibility; }
button, input, select, textarea { font: inherit; }
button, select { color: inherit; }
button, a, input, select { -webkit-tap-highlight-color: transparent; }
a { color: inherit; }
img { max-width: 100%; }
[hidden] { display: none !important; }
::selection { background: var(--ep-accent); color: var(--ep-accent-contrast); }
:focus-visible { outline: 3px solid var(--ep-accent); outline-offset: 4px; }

.skip-link {
  position: fixed;
  top: -5rem;
  left: 1rem;
  z-index: 1000;
  padding: .75rem 1rem;
  border-radius: var(--ep-radius-sm);
  background: var(--ep-ink);
  color: var(--ep-paper);
  font-weight: 800;
}
.skip-link:focus { top: 1rem; }
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.ep-button,
.button,
.primary-button,
.secondary-button,
.quiet-button {
  min-height: 46px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .65rem;
  padding: .72rem 1.05rem;
  border: var(--ep-border-width) solid var(--ep-line);
  border-radius: 999px;
  background: var(--ep-surface);
  color: var(--ep-ink);
  font-weight: 780;
  line-height: 1;
  text-decoration: none;
  cursor: pointer;
  transition: transform 180ms var(--ep-ease), box-shadow 180ms var(--ep-ease), background-color 180ms ease, color 180ms ease;
}
.ep-button:hover,
.button:hover,
.primary-button:hover,
.secondary-button:hover { transform: translateY(-2px); }
.ep-button:active,
.button:active,
.primary-button:active,
.secondary-button:active { transform: translateY(0); }
.ep-button--accent,
.button-accent,
.primary-button { background: var(--ep-accent); color: var(--ep-accent-contrast); box-shadow: var(--ep-shadow-flat); }
.ep-button--ink,
.button-ink { background: var(--ep-ink); color: var(--ep-paper); }
.ep-button--quiet,
.quiet-button { min-height: 40px; border-color: transparent; background: transparent; color: var(--ep-muted); }
.ep-button--quiet:hover,
.quiet-button:hover { background: var(--ep-surface-2); color: var(--ep-ink); }
button:disabled { cursor: wait; opacity: .58; transform: none !important; }

.ep-panel,
.panel {
  border: var(--ep-border-width) solid var(--ep-line);
  border-radius: var(--ep-radius-lg);
  background: var(--ep-surface);
  box-shadow: var(--ep-shadow-flat);
}

.ep-input,
input:not([type="checkbox"]):not([type="radio"]),
select,
textarea {
  width: 100%;
  min-height: 48px;
  padding: .8rem .95rem;
  border: var(--ep-border-width) solid color-mix(in srgb, var(--ep-line) 64%, transparent);
  border-radius: var(--ep-radius-sm);
  background: var(--ep-surface);
  color: var(--ep-ink);
  transition: border-color 160ms ease, box-shadow 160ms ease, background-color 160ms ease;
}
.ep-input:hover,
input:not([type="checkbox"]):not([type="radio"]):hover,
select:hover,
textarea:hover { border-color: var(--ep-line); }
.ep-input:focus,
input:not([type="checkbox"]):not([type="radio"]):focus,
select:focus,
textarea:focus { border-color: var(--ep-accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--ep-accent) 22%, transparent); outline: 0; }

.wordmark {
  display: inline-flex;
  align-items: center;
  gap: .65rem;
  color: inherit;
  font-size: 1.23rem;
  font-weight: 900;
  letter-spacing: -.055em;
  text-decoration: none;
}
.wordmark-mark { position: relative; width: 38px; height: 38px; flex: 0 0 38px; display: grid; place-items: center; overflow: hidden; border-radius: 50%; background: var(--ep-ink); }
.wordmark-mark img { width: 76%; height: 76%; object-fit: contain; }
.wordmark-glyph { filter: none; }

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


/*
 * Epoyo website foundation.
 *
 * Loaded between the shared design system and the page styles. Holds the
 * typographic scale, the flat-toon surface treatments (dot grids, dithered
 * seams, grain), and the layout rhythm every section is built from. Page
 * specific rules belong in styles.css.
 */

:root {
  /* Display face. The build swaps --ep-font-display when a self-hosted
     woff2 is present in public/fonts; otherwise the stack below carries it. */
  --ep-font-display: var(--ep-font-sans);
  --ep-display-tracking: -.035em;

  /* The 2026 site's own face, set on .landing and on the search-cluster
     headings so a /connect page and the homepage are visibly one site. It is a
     token rather than a literal in two files because that is exactly how the
     clusters came to be set in Avenir at weight 900 while the hero next to
     them was Inter at 700. */
  --ep-font-hero: "Inter", "SF Pro Text", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;

  /* Fluid type scale. Steps are deliberately far apart so hierarchy survives
     at a glance instead of relying on weight alone. */
  --ep-step-hero: clamp(2.85rem, 1.6rem + 5.4vw, 6.5rem);
  --ep-step-section: clamp(2.1rem, 1.35rem + 3.1vw, 4rem);
  --ep-step-card: clamp(1.28rem, 1.1rem + .7vw, 1.72rem);
  --ep-step-lede: clamp(1.06rem, .98rem + .5vw, 1.4rem);
  --ep-step-body: clamp(1rem, .96rem + .2vw, 1.11rem);
  --ep-step-small: .82rem;
  --ep-step-eyebrow: .74rem;

  /* Vertical rhythm. Sections share one spacing language. */
  --ep-section-y: clamp(4.5rem, 3rem + 7vw, 9.5rem);
  --ep-gap-lg: clamp(2rem, 1.2rem + 2.6vw, 3.75rem);
  --ep-gap-md: clamp(1.25rem, .9rem + 1.2vw, 2rem);
  --ep-gap-sm: .85rem;
  --ep-gutter: max(5vw, 22px);

  --ep-dot-size: 22px;
  --ep-dot-radius: 1.15px;
  --ep-dot-color: color-mix(in srgb, var(--ep-ink) 26%, transparent);
  --ep-grain-opacity: .035;

  /*
   * Live accent recolouring.
   *
   * --ep-hue rotates the whole accent family continuously, so the slider in
   * the scroll story repaints buttons, fills, particles and widget accents
   * together without ever touching the paper or the ink. Keeping the ground
   * and the type fixed is what stops the page turning into a muddy dark theme
   * halfway through the scroll.
   */
  /*
   * The accent is the design system's now. This file used to redefine it as
   * an orange for the old scroll story, and because it loads after the shared
   * stylesheet it quietly won every primary button on the site -- including
   * the header action, which read orange while the token said blue.
   */
  --ep-accent-soft: #e2ecff;

  /* Widget corner roundness, driven by the second demo slider. */
  --ep-widget-radius: 14px;
}

/* ---------------------------------------------------------------- layout */

.ep-wrap { width: min(var(--ep-content), calc(100% - (var(--ep-gutter) * 2))); margin-inline: auto; }
.ep-wrap--narrow { --ep-content: 880px; }
.ep-wrap--wide { --ep-content: 1420px; }

.ep-section { position: relative; padding-block: var(--ep-section-y); }
.ep-section > .ep-wrap { position: relative; z-index: 1; }

/* Alternating bands keep long scrolls legible without boxing every idea. */
/* Muted text on the ink band is mixed against the band itself, not toward
   transparency, so its contrast ratio stays predictable. */

.ep-stack { display: grid; gap: var(--ep-gap-md); }
.ep-stack--lg { gap: var(--ep-gap-lg); }

/* ------------------------------------------------------------ typography */

.ep-display,
.ep-title,
.ep-hero-title {
  font-family: var(--ep-font-display);
  font-weight: 900;
  letter-spacing: var(--ep-display-tracking);
  line-height: .95;
  text-wrap: balance;
  margin: 0;
}
.ep-hero-title { font-size: var(--ep-step-hero); line-height: .92; }
.ep-title { font-size: var(--ep-step-section); }
.ep-card-title { margin: 0; font-family: var(--ep-font-display); font-size: var(--ep-step-card); font-weight: 850; letter-spacing: -.02em; line-height: 1.12; }

.ep-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  margin: 0;
  font-family: var(--ep-font-mono);
  font-size: var(--ep-step-eyebrow);
  font-weight: 700;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--ep-muted);
}
.ep-eyebrow::before { content: ""; width: 10px; height: 10px; border: 1px solid currentColor; border-radius: 50%; }

.ep-lede { margin: 0; max-width: 46ch; font-size: var(--ep-step-lede); line-height: 1.5; color: var(--ep-muted); text-wrap: pretty; }
.ep-body { margin: 0; max-width: 62ch; font-size: var(--ep-step-body); line-height: 1.62; text-wrap: pretty; }
.ep-body strong { font-weight: 800; }

/* Highlighted words in headings get a hand-drawn marker, not a gradient. */
.ep-mark { position: relative; white-space: nowrap; }
.ep-mark::after {
  content: "";
  position: absolute;
  z-index: -1;
  inset: auto -.14em .06em;
  height: .34em;
  background: var(--ep-accent);
  border-radius: 2px;
}

/* --------------------------------------------------------------- surface */

/* Dot lattice. The shape field snaps to this grid, so the size is shared. */
.ep-dots {
  background-image: radial-gradient(circle at center, var(--ep-dot-color) var(--ep-dot-radius), transparent calc(var(--ep-dot-radius) + .6px));
  background-size: var(--ep-dot-size) var(--ep-dot-size);
  background-position: center;
}

/* Dithered seam between bands: a density ramp of the same dot lattice,
   which reads as a halftone gradient without any blur. */
.ep-dither {
  position: relative;
}
.ep-dither::before,
.ep-dither::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  height: clamp(48px, 7vw, 96px);
  pointer-events: none;
  background-image: radial-gradient(circle at center, var(--ep-dither-color, var(--ep-dot-color)) 1.4px, transparent 2px);
  background-size: 10px 10px;
}
.ep-dither::before { top: 0; -webkit-mask-image: linear-gradient(to bottom, #000, transparent); mask-image: linear-gradient(to bottom, #000, transparent); }
.ep-dither::after { bottom: 0; -webkit-mask-image: linear-gradient(to top, #000, transparent); mask-image: linear-gradient(to top, #000, transparent); }
.ep-dither--top-only::after { content: none; }
.ep-dither--bottom-only::before { content: none; }

/* Film grain. Inlined so the strict CSP needs no extra source. */
.ep-grain::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: var(--ep-grain-opacity);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23g)'/%3E%3C/svg%3E");
}

/* ------------------------------------------------------------ components */

/* Hard-shadow panel. The offset shadow is the house signature. */
.ep-card {
  position: relative;
  padding: clamp(1.3rem, 1rem + 1vw, 2rem);
  border: var(--ep-border-width) solid var(--ep-line);
  border-radius: var(--ep-radius-md);
  background: var(--ep-surface);
  box-shadow: var(--ep-shadow-flat);
}
.ep-card--flush { padding: 0; overflow: hidden; }
.ep-card--tint-yellow { background: var(--ep-yellow-soft); }
.ep-card--tint-blue { background: var(--ep-blue-soft); }
.ep-card--tint-green { background: var(--ep-green-soft); }
.ep-card--tint-purple { background: var(--ep-purple-soft); }

/* Primary action. One colour, one weight, used sparingly. */
.ep-button--accent { box-shadow: var(--ep-shadow-flat); font-weight: 850; }
.ep-button--accent:hover { transform: translateY(-1px); box-shadow: var(--ep-shadow-flat); }
.ep-button--lg { min-height: 58px; padding: 1rem 1.6rem; font-size: 1.02rem; }

.ep-button--accent,
.ep-button { transition: transform 180ms var(--ep-ease), box-shadow 180ms var(--ep-ease), background-color 180ms ease, color 180ms ease; }

/* Screenshot frame. Anchors the corner behaviour of the shape field. */
.ep-shot {
  position: relative;
  border: var(--ep-border-width) solid var(--ep-line);
  border-radius: var(--ep-radius-md);
  overflow: hidden;
  background: var(--ep-surface);
  box-shadow: var(--ep-shadow-flat);
  line-height: 0;
}
.ep-shot img { width: 100%; height: auto; display: block; }

/*
 * One continuous workspace grid.
 *
 * A single fixed layer rather than a background on each section, so the
 * lattice never restarts at a section seam. It scrolls with the page through
 * a transform on its own compositor layer, and it inherits the interpolated
 * ink colour so it tracks the theme without ever competing with text.
 */
/*
 * Stacking, in order: html paints the paper, the grid sits on top of it, the
 * particle canvas above that, and all content above both. The grid must not
 * use a negative z-index, that would put it behind the root background and
 * make it invisible, which is exactly what happened the first time.
 */
.ep-grid-field {
  position: fixed;
  inset: -50vh 0;
  z-index: 0;
  pointer-events: none;
  background-image: radial-gradient(circle at center, var(--ep-dot-color) var(--ep-dot-radius), transparent calc(var(--ep-dot-radius) + .7px));
  background-size: var(--ep-dot-size) var(--ep-dot-size);
  background-position: center top;
  opacity: .85;
  will-change: transform;
  transform: translate3d(0, calc(var(--ep-grid-shift, 0) * 1px), 0);
}
/* The paper lives on html so nothing paints over the grid. */
html { background: var(--ep-paper); }
body, .home-page { background: transparent; }

/* Bands stay translucent so the one grid reads straight through them. */
.ep-section--band { background: color-mix(in oklab, var(--ep-surface-2) 62%, transparent); }
.ep-section--ink { background: var(--ep-ink); color: var(--ep-paper); --ep-line: var(--ep-paper); --ep-muted: color-mix(in srgb, var(--ep-paper) 76%, var(--ep-ink)); }

/* Particles sit above the grid and below every piece of content, so they
   never obscure text or sit on top of a button. */
.shape-field {
  position: fixed;
  inset: 0;
  z-index: 1;
  width: 100%;
  height: 100%;
  pointer-events: none;
}
.site-shell { position: relative; z-index: 2; }

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

/*
 * Scroll reveal.
 *
 * Sections opt in with .reveal; the observer adds .is-visible on entry and
 * removes it on exit, so content eases back out as it leaves rather than
 * snapping. .has-shown gates the exit animation so nothing plays it on load.
 * The overshoot is what makes the arrival feel like weight landing rather
 * than a fade.
 */
@keyframes ep-reveal-in {
  0% { opacity: 0; transform: translate3d(0, 26px, 0) scale(.968); }
  56% { opacity: 1; transform: translate3d(0, -5px, 0) scale(1.012); }
  78% { transform: translate3d(0, 1px, 0) scale(.998); }
  100% { opacity: 1; transform: none; }
}
@keyframes ep-reveal-out {
  0% { opacity: 1; transform: none; }
  100% { opacity: 0; transform: translate3d(0, 16px, 0) scale(.985); }
}

.reveal { opacity: 0; }
.reveal.is-visible { animation: ep-reveal-in .66s var(--ep-ease) both; }
.reveal.has-shown:not(.is-visible) { animation: ep-reveal-out .34s var(--ep-ease) both; }
.reveal-2 { animation-delay: 90ms; }
.reveal-3 { animation-delay: 180ms; }
.reveal-4 { animation-delay: 270ms; }

/*
 * Grids stagger their own children so a row of cards arrives one after
 * another. The grid itself carries .reveal; its children ride off it.
 */
.reveal.is-visible > .ep-card,
.reveal.is-visible > li,
.reveal.is-visible > figure,
.reveal.is-visible > article { animation: ep-reveal-in .6s var(--ep-ease) both; }
.reveal.is-visible > :nth-child(2) { animation-delay: 70ms; }
.reveal.is-visible > :nth-child(3) { animation-delay: 140ms; }
.reveal.is-visible > :nth-child(4) { animation-delay: 210ms; }
.reveal.is-visible > :nth-child(5) { animation-delay: 280ms; }
.reveal.is-visible > :nth-child(6) { animation-delay: 350ms; }

/* Layered parallax. site.mjs writes --ep-scroll on the section; depth is
   declared per element so one scroll pass drives every plane. */
.ep-parallax { will-change: transform; transform: translate3d(0, calc(var(--ep-scroll, 0) * var(--ep-depth, 0) * 1px), 0); }

.js .reveal { will-change: opacity, transform; }
.js .reveal.is-visible { will-change: auto; }

@media (prefers-reduced-motion: reduce) {
  /* Everything is simply present: no arrival, no exit, and crucially nothing
     that could leave content stuck at opacity 0 if the observer misbehaves. */
  .reveal,
  .reveal.is-visible,
  .reveal.has-shown:not(.is-visible),
  .reveal.is-visible > * { opacity: 1; transform: none; animation: none; transition: none; }
  .ep-parallax { transform: none; }
  .ep-grain::before { display: none; }
}


@font-face {
  font-family: "Epoyo Display";
  src: url("/fonts/epoyo-display.woff2") format("woff2");
  font-weight: 400 900;
  font-style: normal;
  font-display: swap;
}
:root { --ep-font-display: "Epoyo Display", var(--ep-font-sans); }

:root {
  color-scheme: light;
  --ink: #17131f;
  --paper: #f8f2df;
  --paper-2: #fffaf0;
  --night: #09142f;
  --night-soft: #102453;
  --blue: #7487ff;
  --berry: #ed5f9a;
  --nectar: #ffb443;
  --lime: #b8eb52;
  --opal: #78d7e8;
  --launcher: #788bff;
  --line: #17131f;
  --muted: #635e69;
  --radius: 22px;
  --shadow-size: 10px;
  font-family: ui-rounded, "Arial Rounded MT Bold", "Avenir Next", Arial, Helvetica, sans-serif;
  font-synthesis: none;
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }
html { min-width: 320px; background: var(--ep-paper); scroll-behavior: smooth; }
body { margin: 0; color: var(--ink); background-color: var(--paper); background-image: radial-gradient(circle, rgba(23,19,31,.13) 1.35px, transparent 1.5px); background-size: 24px 24px; }
a { color: inherit; }
button,
input { font: inherit; }
img { display: block; max-width: 100%; }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
.skip-link { position: fixed; left: 1rem; top: -6rem; z-index: 100; padding: .8rem 1rem; background: var(--paper-2); color: var(--ink); border: 1px solid var(--ink); border-radius: 12px; font-weight: 800; }
.skip-link:focus { top: 1rem; }
:focus-visible { outline: 3px solid var(--lime); outline-offset: 4px; }

.site-shell { min-height: 100svh; display: grid; grid-template-rows: auto 1fr auto; }
.site-header,
.site-footer { position: relative; z-index: 20; width: min(1320px, calc(100% - 40px)); margin-inline: auto; display: flex; align-items: center; justify-content: space-between; gap: 2rem; }
.site-header { min-height: 82px; border-bottom: 1px solid color-mix(in srgb, var(--ink) 18%, transparent); }
.home-page .site-header { position: absolute; inset: 0 0 auto; color: var(--paper-2); border-color: rgba(255,255,255,.22); }
.wordmark { display: inline-flex; align-items: center; gap: .85rem; text-decoration: none; font-size: 1.55rem; font-weight: 900; letter-spacing: -.055em; }
/*
 * Optically centred against the mark, which `align-items: center` cannot do.
 *
 * That centres the inline *box*, and the box reserves 27px of ascent at this
 * size while "epoyo" -- all lowercase, no ascenders -- puts its ink between
 * the x-height and the descenders of p and y. So the box centre sat 7px above
 * the ink, and the word rode that much low beside a mark whose own ink is
 * dead centre in its box (measured: 0.4997).
 *
 * 0.17em is the ink's *mass* centroid, not its bounding box. The geometric
 * centre would ask for 6.5px, but the descenders are two thin strokes against
 * a solid band of x-height, so weighting by ink lands at 4.3px -- which is
 * what the eye is actually averaging. In em so it survives any font-size
 * change; the collapsed header hides this span rather than resizing it.
 */
.wordmark > span:last-child { transform: translateY(-0.17em); }
.wordmark-mark { position: relative; display: block; width: 44px; height: 44px; overflow: visible; isolation: isolate; border-radius: 0; background: transparent; }
.wordmark-mark img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; }
.wordmark-glyph { z-index: 1; }
.wordmark-glyph-white { display: none; }
.home-page .wordmark-glyph-black { display: block; }
.home-page .wordmark-glyph-white { display: none; }
.account-session-bridge { position: fixed; width: 1px; height: 1px; border: 0; opacity: 0; pointer-events: none; }
/*
 * The signed-in account button. It used to be the old product's yellow disc
 * with a hard offset shadow, which is the one piece of retired styling left
 * in the bar. It is the same ink circle as the action beside it now, a size
 * down, so the pair reads as one control group rather than two eras.
 */
.header-profile {
  width: 32px;
  height: 32px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: var(--ep-ink);
  color: #ffffff;
  font-size: .78rem;
  font-weight: 650;
  letter-spacing: .01em;
  text-decoration: none;
  transition: transform 160ms var(--ep-ease), opacity 160ms var(--ep-ease);
}
.header-profile:hover { transform: translateY(-1px); opacity: .88; }
.header-profile[hidden],
.header-actions[hidden] { display: none; }
.site-nav { display: flex; align-items: center; gap: .35rem; font-size: .8rem; font-weight: 800; text-transform: uppercase; letter-spacing: .08em; }
.site-nav a { padding: .65rem .75rem; text-decoration: none; border-radius: 10px; }
.site-nav a:hover,
.site-nav a[aria-current="page"] { background: color-mix(in srgb, var(--blue) 17%, transparent); }
.home-page .nav-account { background: rgba(255,255,255,.08); }
.menu-toggle > span:not(.sr-only) { display: block; width: 100%; height: 2px; margin: 4px 0; background: currentColor; }

main { min-width: 0; }
.site-footer { min-height: 86px; padding-block: 1.4rem; border-top: 1px solid color-mix(in srgb, var(--ink) 18%, transparent); color: var(--muted); font-size: .74rem; line-height: 1.5; }
.home-page .site-footer { display: none; }
.footer-links { display: flex; gap: 1rem; font-weight: 700; }
.product-stage.is-changing .product-backdrop { opacity: 0; }
.product-stage:not(.is-changing) .product-backdrop img { transform: scale(1); }


.product-stage.is-changing .stage-copy { opacity: .35; transform: translateY(8px); }
.eyebrow { font-family: "Courier New", monospace; font-size: .72rem; font-weight: 900; line-height: 1.4; letter-spacing: .13em; text-transform: uppercase; }
.product-stage.is-changing .stage-product-icon { opacity: .35; transform: scale(.86) rotate(-4deg); }
.button { position: relative; display: inline-flex; align-items: center; justify-content: center; gap: 1rem; min-height: 54px; padding: .95rem 1.25rem; border: 1px solid var(--ink); border-radius: 17px; text-decoration: none; font-size: .79rem; font-weight: 900; text-transform: uppercase; letter-spacing: .07em; box-shadow: 7px 7px 0 var(--ink); transition: transform .2s cubic-bezier(.2,.8,.2,1), box-shadow .2s ease, filter .2s ease; }
.button:hover { transform: translateY(-1px); box-shadow: 4px 4px 0 var(--ink); }

/* Shared content pages */
.page-wrap { width: min(1240px, calc(100% - 40px)); margin-inline: auto; padding: clamp(4rem, 8vw, 7rem) 0 7rem; }
.page-intro { max-width: 1000px; margin-bottom: clamp(3rem, 7vw, 6rem); }
.eyebrow { margin: 0 0 1rem; color: #5b49c9; }
.page-intro h1 { margin: 0; font-size: clamp(3.8rem, 9vw, 8.5rem); line-height: .86; letter-spacing: -.075em; font-weight: 900; }
.lede { max-width: 760px; margin: 1.5rem 0 0; color: var(--muted); font-size: clamp(1.05rem, 2.2vw, 1.45rem); line-height: 1.55; letter-spacing: -.02em; }
.status-pill { display: inline-flex; padding: .35rem .55rem; border: 1.5px solid var(--ink); border-radius: 999px; background: var(--paper-2); font-family: "Courier New", monospace; font-size: .61rem; font-weight: 900; letter-spacing: .08em; text-transform: uppercase; white-space: nowrap; }
.text-link { display: inline-flex; gap: .5rem; align-items: center; margin-top: auto; padding-top: 1.4rem; font-weight: 900; text-decoration-thickness: 2px; text-underline-offset: 4px; }

/* Funnel pages */
.product-page { --accent: var(--berry); }
.product-nectar { --accent: var(--nectar); }
.product-lime { --accent: var(--lime); }
.funnel-copy .eyebrow { color: color-mix(in srgb, var(--accent) 70%, var(--ink)); }

.local-first-callout .eyebrow { color: var(--ink); }
.final-cta .eyebrow { color: var(--accent); }
.final-cta .button { margin-top: 2.5rem; }

.support-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1.5rem; }
.support-card { min-height: 300px; display: flex; flex-direction: column; padding: 1.5rem; border: 1px solid var(--ink); border-radius: var(--radius); background: var(--paper-2); box-shadow: 8px 8px 0 var(--ink); text-decoration: none; }
.support-card > span { font-family: "Courier New", monospace; font-size: .72rem; font-weight: 900; text-transform: uppercase; letter-spacing: .1em; }
.support-card h2 { margin: auto 0 .7rem; font-size: clamp(2rem, 5vw, 3.8rem); line-height: .92; letter-spacing: -.055em; }
.support-card p { margin: 0; color: var(--muted); line-height: 1.6; }
.notice-box { margin-top: 3rem; padding: 1.4rem; border: 1px solid var(--ink); border-radius: 18px; background: var(--nectar); box-shadow: 8px 8px 0 var(--ink); }
.notice-box b { font-size: 1.25rem; }
.notice-box p { max-width: 760px; line-height: 1.6; }
.docs-stack,
.issue-list { display: grid; gap: 1.3rem; }
.docs-stack article { display: grid; grid-template-columns: 64px 1fr; gap: 1.4rem; padding: 1.5rem; border: 1px solid var(--ink); border-radius: var(--radius); background: var(--paper-2); box-shadow: 7px 7px 0 var(--ink); }
.docs-stack article > span { font-family: "Courier New", monospace; font-size: .75rem; font-weight: 900; letter-spacing: .08em; }
.docs-stack h2,
.issue-list h2 { margin: 0 0 .65rem; font-size: clamp(1.7rem, 4vw, 3rem); line-height: .95; letter-spacing: -.05em; }
.docs-stack p,
.issue-list p { max-width: 850px; margin: 0; color: var(--muted); line-height: 1.65; }
.issue-list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.issue-list article { min-height: 300px; display: flex; flex-direction: column; align-items: flex-start; padding: 1.5rem; border: 1px solid var(--ink); border-radius: var(--radius); background: var(--paper-2); box-shadow: 7px 7px 0 var(--ink); }
.issue-list h2 { margin-top: auto; padding-top: 2.5rem; }
.issue-list p + p { margin-top: .7rem; }
.legal-copy { max-width: 900px; }
.legal-copy h2 { margin: 2.8rem 0 .8rem; font-size: 2rem; letter-spacing: -.04em; }
.legal-copy p { color: var(--muted); font-size: 1.02rem; line-height: 1.75; }
.legal-copy .legal-meta { margin: 0 0 3rem; padding: 1.3rem 1.4rem; border: 1px solid var(--ink); border-radius: 16px; background: var(--paper-2); box-shadow: 6px 6px 0 var(--ink); color: var(--ink); }

@media (max-width: 760px) {
  .site-header,
.site-footer { width: min(100% - 28px, 1320px); }
  .site-nav { display: none; position: absolute; top: 68px; right: 0; width: min(280px, calc(100vw - 28px)); padding: .8rem; flex-direction: column; align-items: stretch; border: 1px solid currentColor; border-radius: 16px; background: var(--paper-2); color: var(--ink); box-shadow: 7px 7px 0 currentColor; }
  .site-nav[data-open] { display: flex; }
  .support-grid,
.issue-list { grid-template-columns: 1fr; }
  .docs-stack article { grid-template-columns: 1fr; }
  .page-wrap { width: calc(100% - 28px); padding-top: 3.5rem; }
  .page-intro h1 { font-size: clamp(3.6rem, 18vw, 6rem); }
  .site-footer { align-items: flex-start; flex-direction: column; gap: 1rem; }
}

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

/* Brand tokens now come from packages/design-system/src/web.css so the site,
the account portal and the application stay on one palette. */
:root {
  --ep-primary-hue: 42;
}
.home-page main > section {
  min-height: calc(100svh - 76px);
}
.hero-copy { margin-inline: auto; display: flex; flex-direction: column; align-items: center; }
.hero-copy h1 { max-width: 1080px; margin-inline: auto; }
.hero-actions { justify-content: center; }
.app-detail-actions .ep-button { border-color: #09080b; background: #f8f4e9; color: #17151c; box-shadow: 4px 4px 0 #09080b; }
.app-detail-actions .ep-button--accent { background: var(--app-accent, #fbd05b); }



.theme-section,
.closing-section { display: flex; flex-direction: column; justify-content: center; }
:root[data-appearance="dark"] { --ep-line: #38383a; }



.closing-section { margin-bottom: 0; }

@media (max-width: 760px) {
  html { scroll-padding-top: 68px; }
  .home-page main > section { min-height: calc(100svh - 68px); }
}

/* Keep product heroes on the homepage's warm paper palette. */
.product-page,
.product-page .site-shell,
.product-page .product-hero {
  background-color: var(--ep-paper);
}
.product-page .product-hero {
  background-image: radial-gradient(circle, color-mix(in srgb, var(--ep-ink) 12%, transparent) 1.2px, transparent 1.35px);
  background-size: 24px 24px;
  border-bottom-color: var(--ep-line);
}
.product-page .product-hero h1,
.product-page .product-hero-identity b { color: var(--ep-ink); }
.product-page .product-hero-description,
.product-page .product-hero-note,
.product-page .product-hero-identity small { color: var(--ep-muted); }
.product-page .site-footer { color: var(--ep-ink); border-color: color-mix(in srgb, var(--ep-line) 18%, transparent); }
.product-page .site-footer a { color: inherit; }

/* Product routes share the centered,
interactive hero language of epoyo.com. */
.product-page { background: #18161d; }
.product-page .site-footer { color: #f8f4e9; border-color: rgba(255,255,255,.16); }
.product-page .site-footer a { color: inherit; }
.product-hero { min-height: 100svh; padding: 118px 24px 74px; display: grid; place-items: center; overflow: hidden; }
.product-hero::after { content: ""; position: absolute; inset: auto 8vw 6vh auto; width: clamp(180px, 22vw, 360px); aspect-ratio: 1; border: 1px solid rgba(255,255,255,.1); border-radius: 42%; rotate: 14deg; }
.product-hero-glow { position: absolute; left: 50%; top: 50%; width: min(72vw, 850px); aspect-ratio: 1; border-radius: 46%; background: color-mix(in srgb, var(--accent) 22%, transparent); filter: blur(70px); translate: -50% -50%; rotate: -8deg; pointer-events: none; }
.product-hero-inner { position: relative; z-index: 2; width: min(1040px, 100%); display: flex; flex-direction: column; align-items: center; text-align: center; }
.product-hero-identity { display: grid; justify-items: center; gap: .35rem; }
.product-hero-identity > span { width: 96px; height: 96px; padding: 8px; display: grid; place-items: center; border: 1px solid #09080b; border-radius: 27px; background: color-mix(in srgb, var(--accent) 72%, #f8f4e9); box-shadow: 7px 7px 0 #09080b; rotate: -3deg; transition: translate 180ms var(--ep-ease), rotate 180ms var(--ep-ease); }
.product-hero:hover .product-hero-identity > span { translate: 0 -6px; rotate: 2deg; }
.product-hero-identity img { width: 100%; height: 100%; object-fit: contain; }
.product-hero-identity b { margin-top: .55rem; color: #f8f4e9; font-size: 1.15rem; }
.product-hero-identity small,
.product-hero-note { font-family: var(--ep-font-mono); }
.product-hero-identity small { color: #aaa3b0; font-size: .64rem; letter-spacing: .1em; text-transform: uppercase; }
.product-hero .eyebrow { margin: 1.5rem 0 .8rem; color: var(--accent); }
.product-hero h1 { max-width: 1000px; margin: 0; color: #f8f4e9; font-size: clamp(4rem, 8.2vw, 8.4rem); letter-spacing: -.08em; line-height: .86; }
.product-hero-description { max-width: 680px; margin: 1.5rem 0 0; color: #c4bdca; font-size: clamp(1rem, 1.6vw, 1.2rem); line-height: 1.65; }
.product-hero-actions { margin-top: 2rem; display: flex; flex-wrap: wrap; justify-content: center; gap: .8rem; }
.product-page .product-hero-actions .ep-button { border-color: #09080b; background: #f8f4e9; color: #17151c; box-shadow: 5px 5px 0 #09080b; }
.product-page .product-hero-actions .ep-button--accent { background: var(--accent); }
.product-hero-note { margin-top: 1.15rem; color: #aaa3b0; font-size: .66rem; line-height: 1.5; }

.waitlist-section { padding: clamp(4rem, 8vw, 7rem) 24px; color: #17151c; background: var(--ep-paper); }
.waitlist-card { width: min(1120px, 100%); margin: 0 auto; display: grid; grid-template-columns: minmax(0, .9fr) minmax(360px, 1.1fr); overflow: hidden; border: var(--ep-border-width) solid var(--ep-line); border-radius: 32px; background: var(--ep-surface); box-shadow: var(--ep-shadow-flat); }
.waitlist-copy { padding: clamp(2rem, 4.5vw, 4rem); background: color-mix(in srgb, var(--accent) 18%, var(--ep-surface)); }
.waitlist-copy .eyebrow { margin: 0 0 .9rem; color: color-mix(in srgb, var(--accent) 62%, var(--ep-ink)); }
.waitlist-copy h2 { margin: 0; font-size: clamp(2.6rem, 5vw, 5rem); line-height: .92; letter-spacing: -.065em; }
.waitlist-copy > p:not(.eyebrow) { margin: 1.25rem 0 0; color: var(--ep-muted); font-size: 1rem; line-height: 1.65; }
.waitlist-copy ul { margin: 1.6rem 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: .55rem; list-style: none; }
.waitlist-copy li { padding: .45rem .65rem; border: 1.5px solid var(--ep-line); border-radius: 999px; background: var(--ep-surface); font-family: var(--ep-font-mono); font-size: .62rem; font-weight: 900; text-transform: uppercase; letter-spacing: .06em; }
.waitlist-form { padding: clamp(2rem, 4.5vw, 4rem); display: grid; align-content: center; gap: 1rem; border-left: var(--ep-border-width) solid var(--ep-line); }
.waitlist-field { display: grid; gap: .5rem; font-weight: 800; }
.waitlist-field span { font-size: .76rem; text-transform: uppercase; letter-spacing: .07em; }
.waitlist-field input { width: 100%; min-height: 54px; padding: .8rem 1rem; border: 1px solid var(--ep-line); border-radius: 14px; background: var(--ep-paper); color: var(--ep-ink); box-shadow: var(--ep-shadow-flat); }
.waitlist-field input:focus-visible { outline: 3px solid color-mix(in srgb, var(--accent) 70%, white); outline-offset: 3px; }
.waitlist-choice { display: grid; grid-template-columns: 21px 1fr; align-items: start; gap: .7rem; color: var(--ep-muted); font-size: .84rem; line-height: 1.45; cursor: pointer; }
.waitlist-choice input { width: 19px; height: 19px; margin: .05rem 0 0; accent-color: var(--accent); }
.waitlist-form .ep-button { width: 100%; border-color: var(--ep-line); background: var(--accent); color: var(--ep-ink); box-shadow: var(--ep-shadow-flat); }
.waitlist-form .ep-button:disabled { cursor: not-allowed; opacity: .52; box-shadow: none; }
.waitlist-status,
.preferences-status,
.waitlist-setup-note { margin: 0; min-height: 1.4em; font-size: .78rem; line-height: 1.5; }
.waitlist-status[data-state="success"],
.preferences-status[data-state="success"] { color: #276019; font-weight: 800; }
.waitlist-status[data-state="error"],
.preferences-status[data-state="error"],
.waitlist-setup-note { color: #973329; font-weight: 800; }
.waitlist-form > small { color: var(--ep-muted); font-size: .68rem; line-height: 1.5; }
.waitlist-form a,
.preferences-card a { color: inherit; }
.preferences-card { max-width: 760px; padding: clamp(1.5rem, 4vw, 3rem); border: var(--ep-border-width) solid var(--ep-line); border-radius: 26px; background: var(--ep-surface); box-shadow: var(--ep-shadow-flat); }
.preferences-card > p:first-child { margin-top: 0; color: var(--ep-muted); line-height: 1.65; }
.preferences-actions { display: grid; gap: .8rem; margin: 1.5rem 0; }
.preferences-actions .ep-button { width: 100%; border-color: var(--ep-line); color: var(--ep-ink); background: var(--ep-paper); box-shadow: var(--ep-shadow-flat); }
.preferences-actions .ep-button:disabled { opacity: .65; box-shadow: none; }

@media (max-width: 760px) {
  .product-hero { min-height: 100svh; padding: 102px 16px 58px; }
  .product-hero h1 { font-size: clamp(3.7rem, 18vw, 5.8rem); }
  .product-hero-description { font-size: .98rem; }
  .product-hero-actions { width: 100%; display: grid; }
  .product-hero-actions .ep-button { width: 100%; }
  .product-hero::after { display: none; }
  .waitlist-section { padding: 3rem 14px 4rem; }
  .waitlist-card { grid-template-columns: 1fr; border-radius: 25px; box-shadow: var(--ep-shadow-flat); }
  .waitlist-form { border-top: var(--ep-border-width) solid var(--ep-line); border-left: 0; }
}

@media (min-width: 1041px) and (min-height: 680px) {
  .home-page .home-section { padding-block: clamp(2rem, 4vh, 3rem); }
  .theme-copy { margin-bottom: 1.4rem; }
  .theme-copy h2 { font-size: clamp(3.4rem, 5vw, 5rem); }
}

/* 2026 one-page public experience. Shared primitives and tokens live in
   packages/design-system/src/web.css; this file owns website composition. */
:root { --ep-grid-opacity: .12; }
body { background: var(--ep-paper); color: var(--ep-ink); font-family: var(--ep-font-sans); }
.site-shell { min-height: 100vh; overflow: clip; background: var(--ep-paper); }
/*
 * The floating pill bar. It sits inside the page rather than across it, with
 * air on every side -- the reference frame is a capsule, not a strip -- and
 * stays glassy while scrolled content passes underneath.
 */
/*
 * ------------------------------------------------------------ liquid glass
 *
 * One recipe, used by every floating surface, so they read as the same
 * material rather than as several things that happen to be translucent.
 *
 * Four parts, and all four matter:
 *
 *   saturate   what separates glass from frosted plastic. Blur alone greys
 *              out whatever is behind it; boosting saturation lets the
 *              colour beneath bleed through the way real glass carries it.
 *   fill       a light tint, thin enough that the page still shows through.
 *   rim        a bright inset line along the top edge and a fainter one
 *              along the bottom: the specular highlight where a pane catches
 *              the light. This is the detail that makes it read as a
 *              physical sheet rather than a rectangle of blur.
 *   shadow     a close contact shadow plus a wide soft one, so the surface
 *              sits above the page instead of being printed on it.
 */
:root {
  --glass-blur: saturate(180%) blur(20px);
  --glass-fill: rgba(255, 255, 255, .58);
  --glass-fill-solid: rgba(255, 255, 255, .82);
  --glass-edge: rgba(255, 255, 255, .7);
  --glass-rim:
    inset 0 1px 0 rgba(255, 255, 255, .92),
    inset 0 -1px 0 rgba(255, 255, 255, .32);
  --glass-shadow:
    0 1px 2px rgba(29, 29, 31, .05),
    0 10px 30px -12px rgba(29, 29, 31, .2);
}

.ep-glass {
  border: 1px solid var(--glass-edge);
  background: var(--glass-fill);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--glass-rim), var(--glass-shadow);
}

/*
 * The navigation bar. Compact by default and more compact once scrolled --
 * a header is a way back, not a piece of furniture, and every pixel it does
 * not take is a pixel the page gets.
 */
.site-header {
  position: sticky;
  top: 10px;
  z-index: 80;
  width: min(1080px, calc(100% - 28px));
  min-height: 52px;
  margin: 10px auto 0;
  padding: 0 8px 0 16px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .75rem;
  border: 1px solid var(--glass-edge);
  border-radius: 999px;
  background: var(--glass-fill);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--glass-rim), var(--glass-shadow);
  transition:
    width 420ms cubic-bezier(.22, .8, .24, 1),
    min-height 420ms cubic-bezier(.22, .8, .24, 1),
    padding 420ms cubic-bezier(.22, .8, .24, 1),
    box-shadow 260ms ease,
    background-color 260ms ease;
}
/*
 * Scrolled, the bar collapses to a compact capsule: much narrower, shorter,
 * wordmark text gone so only the logo remains, and the links folded into a
 * menu. The one thing that survives at full strength is the action, shortened
 * to "Analyze →" -- a header that shrinks should lose chrome, not its point.
 *
 * Every property here is on the transition list above, so the change is a
 * single smooth move rather than a jump.
 */
.site-header[data-scrolled] {
  width: min(420px, calc(100% - 28px));
  min-height: 44px;
  padding: 0 6px 0 12px;
  /* Denser glass once content is passing underneath: the same material,
     turned up so the bar stays legible over anything. */
  background: var(--glass-fill-solid);
  box-shadow: var(--glass-rim), var(--ep-shadow-soft);
}

.site-header[data-scrolled] .wordmark > span:last-child,
.site-header[data-scrolled] .site-nav,
.site-header[data-scrolled] .header-sign-in { display: none; }

.site-header[data-scrolled] .wordmark-mark { width: 24px; height: 24px; flex-basis: 24px; }
.site-header[data-scrolled] .header-create { min-height: 30px; padding-inline: .7rem; font-size: .76rem; }
.site-header[data-scrolled] .header-burger { display: inline-flex; }

/* One label is always hidden; which one is the only thing scrolling changes. */
.header-create-short { display: none; }
.site-header[data-scrolled] .header-create-full { display: none; }
.site-header[data-scrolled] .header-create-short { display: inline; }

/*
 * ------------------------------------------------ the field in the bar
 *
 * The compact bar carries the hero's input. Once the hero has scrolled past
 * there is nowhere left to paste a URL, and a bar that has just given up its
 * links and its wordmark has the room to hold the one control the whole site
 * exists to funnel people into.
 *
 * It is not a second field so much as the same one, moved: same detector,
 * same platform icon, same button. Only the chrome is smaller.
 */
/*
 * The extra width is claimed only when the field is actually there.
 *
 * Signed in, the whole signed-out block is hidden and the bar is a wordmark
 * and an avatar -- widening it for a field nobody can see leaves a long empty
 * capsule. Where :has is unsupported the bar stays at its old width and the
 * field is merely cramped, which is the right way round to fail.
 */
.site-header[data-scrolled]:has([data-signed-out-actions]:not([hidden])) {
  width: min(560px, calc(100% - 28px));
}

.header-source {
  position: relative;
  display: flex;
  align-items: center;
  min-width: 0;
}

.header-source-field {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

/*
 * Hidden by width rather than by `display`, so it opens on the same curve and
 * over the same 420ms as the bar it lives in -- the two read as one movement
 * instead of a field appearing inside a box that is still resizing.
 *
 * `visibility` is what actually takes it out of the tab order and off the
 * accessibility tree while it is closed; a zero-width input is still a
 * focusable one, and tabbing into something invisible is disorienting.
 *
 * The selectors are deliberately over-qualified. The icon shares a class with
 * the hero's, which sets a width of its own, and the design system sizes every
 * bare input on the site through a selector worth more than a single class --
 * both quietly won this rule, and shipped the closed bar with an open field
 * sitting in it.
 */
.site-header .header-source-icon,
.site-header .header-source-field .header-source-input {
  width: 0;
  min-width: 0;
  opacity: 0;
  visibility: hidden;
  transition:
    width 420ms cubic-bezier(.22, .8, .24, 1),
    opacity 240ms ease,
    visibility 420ms;
}

.site-header .header-source-icon { flex: none; overflow: hidden; }
.site-header .header-source-icon svg { width: 15px; height: 15px; }

/* The mark is always TikTok; only its colour reacts to a recognised handle.
   A globe used to be swapped in here, from when the field took a website too. */
.header-source-field[data-kind="tiktok"] .header-source-icon { color: var(--ink); }

.site-header .header-source-field .header-source-input {
  /* Explicit width rather than flex growth: a flex item with a zero basis
     ignores width, and width is the thing that has to animate here. */
  flex: none;
  height: 26px;
  min-height: 0;
  padding: 0;
  border: 0;
  background: none;
  color: var(--ink);
  font: inherit;
  font-size: .82rem;
  outline: none;
}

.site-header .header-source-input::placeholder { color: color-mix(in srgb, var(--ink) 42%, transparent); }

.site-header[data-scrolled] .header-source-icon { width: 17px; height: 17px; opacity: 1; visibility: visible; }
.site-header[data-scrolled] .header-source-input { width: 310px; opacity: 1; visibility: visible; }

/*
 * The design system styles every bare input on the site, and its selector
 * outranks a single class -- without this the field grows a grey border and
 * an accent ring inside a bar that is already a frame.
 */
.site-header .header-source-field .header-source-input,
.site-header .header-source-field .header-source-input:hover,
.site-header .header-source-field .header-source-input:focus,
.site-header .header-source-field .header-source-input:focus-visible {
  border: 0;
  outline: none;
  box-shadow: none;
  background: none;
}

/*
 * The same rainbow the hero's button wears, on the same terms: lit only when
 * what has been typed can actually be analysed, because the glow is the
 * affordance. The keyframes and the animatable angle are declared once, in
 * landing.css, and every page ships one stylesheet.
 */
.header-source-field[data-ready="true"] .l-source-cta::before {
  opacity: .85;
  animation: l-glow-hue 6s linear infinite;
}

.header-source-field[data-ready="true"] .l-source-cta:hover::before { opacity: 1; filter: blur(7px); }
.header-source-field:not([data-ready="true"]) .l-source-cta::before { opacity: 0; animation: none; }

/* Said only when what was typed cannot be read, so it is never nagging. It
   hangs below the bar rather than widening it. */
.header-source-note {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  margin: 0;
  padding: 6px 12px;
  border-radius: 999px;
  background: var(--paper-2);
  box-shadow: var(--glass-rim), var(--ep-shadow-soft);
  color: var(--ep-danger, #e0002f);
  font-size: .76rem;
  white-space: nowrap;
}

.header-source-note:empty { display: none; }

.header-burger {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 4px;
  width: 36px;
  height: 36px;
  padding: 0 8px;
  border: 0;
  border-radius: 999px;
  background: none;
  cursor: pointer;
}

.header-burger span {
  display: block;
  height: 1.6px;
  border-radius: 2px;
  background: var(--ep-ink);
  transition: transform 220ms cubic-bezier(.2, .7, .2, 1), opacity 160ms ease;
}

/* The bars become an X, so the control says what pressing it again does. */
.header-burger[aria-expanded="true"] span:nth-child(1) { transform: translateY(5.6px) rotate(45deg); }
.header-burger[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.header-burger[aria-expanded="true"] span:nth-child(3) { transform: translateY(-5.6px) rotate(-45deg); }

.header-menu {
  position: absolute;
  top: calc(100% + 10px);
  right: 8px;
  z-index: 90;
  display: grid;
  gap: 2px;
  min-width: 190px;
  padding: 8px;
  border: 1px solid var(--ep-line);
  border-radius: 18px;
  background: var(--glass-fill-solid);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--glass-rim), var(--ep-shadow-soft);
  animation: header-menu-in 200ms cubic-bezier(.2, .7, .2, 1);
}

.header-menu[hidden] { display: none; }

.header-menu a {
  padding: 10px 12px;
  border-radius: 12px;
  color: var(--ep-ink);
  font-size: .92rem;
  font-weight: 560;
  text-decoration: none;
}

.header-menu a:hover { background: var(--ep-surface-2); }

@keyframes header-menu-in {
  from { opacity: 0; transform: translateY(-6px) scale(.98); }
  to { opacity: 1; transform: none; }
}
.site-header .wordmark { color: var(--ep-ink); }
.site-header .wordmark-glyph { filter: none; mix-blend-mode: normal; }
.header-actions { display: flex; align-items: center; gap: .6rem; }

/*
 * The theme switch.
 *
 * Both icons are in the markup and this shows the one for the theme you are
 * not in, so the button always pictures where pressing it goes: a moon on a
 * light page, a sun on a dark one. Showing the current theme instead is the
 * more common mistake and it reads as a status light rather than a control.
 */
.header-theme {
  width: 32px;
  height: 32px;
  padding: 0;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--ep-muted);
  cursor: pointer;
  transition: color 160ms var(--ep-ease), background 160ms var(--ep-ease);
}

.header-theme:hover { color: var(--ep-ink); background: var(--ep-surface-2); }
.header-theme:focus-visible { outline: 2px solid var(--ep-accent); outline-offset: 2px; }
/*
 * No display here, deliberately.
 *
 * `.header-theme svg` is a class and a type, which outranks the single class
 * on each icon below -- so setting display on it showed both of them, stacked,
 * whatever the theme said. Sizing belongs here; which one is on does not.
 */
.header-theme svg { width: 17px; height: 17px; }

.header-theme-moon { display: block; }
.header-theme-sun { display: none; }
:root[data-appearance="dark"] .header-theme-sun { display: block; }
:root[data-appearance="dark"] .header-theme-moon { display: none; }

/*
 * The mark is a black glyph and a white one, and dark needs the other one.
 * Higher specificity than the per-page rules below it on purpose: a page that
 * asked for the black mark asked while there was only one theme.
 */
:root[data-appearance="dark"] .wordmark-glyph-black { display: none; }
:root[data-appearance="dark"] .wordmark-glyph-white { display: block; }

/*
 * Surfaces the rest of this stylesheet paints with a literal.
 *
 * Most of the site reads its colour from the design system's tokens and needs
 * nothing here. These are the few places that were written as a fixed value
 * back when there was one theme to write for.
 */
/*
 * The glass, on a dark page.
 *
 * All four values, not just the fill. The header is a pane of this and it was
 * still filled with 58% white over a near-black page, which is how a dark site
 * ends up with a light grey bar across the top of it. The rim is what a pane
 * catches along its edges; on dark that is a trace of light rather than the
 * near-white highlight it is on paper.
 */
:root[data-appearance="dark"] {
  --glass-fill: rgba(28, 29, 33, .62);
  --glass-fill-solid: rgba(28, 29, 33, .86);
  --glass-edge: rgba(255, 255, 255, .1);
  --glass-rim:
    inset 0 1px 0 rgba(255, 255, 255, .07),
    inset 0 -1px 0 rgba(0, 0, 0, .3);
}
.header-sign-in { padding: .4rem .25rem; color: var(--ep-muted); font-size: .8rem; font-weight: 620; text-underline-offset: 4px; text-decoration-thickness: 1px; }
.header-sign-in:hover { color: var(--ep-ink); }
/*
 * Black, not the accent. The bar is a pale glass capsule and the action has
 * to be the darkest thing in it; a coloured fill competes with the page
 * behind the glass instead of sitting on top of it.
 */
.header-create {
  min-height: 34px;
  padding-inline: .85rem;
  font-size: .78rem;
  background: var(--ep-ink);
  color: var(--ep-paper);
}
/*
 * Hover pushes the fill further from the page rather than toward black. On a
 * dark theme the ink is already the light colour, and darkening it there took
 * the button toward the page it sits on instead of away from it.
 */
.header-create:hover { background: color-mix(in srgb, var(--ep-ink) 86%, var(--ep-accent)); }
.home-page main { display: block; }
.dotted-field {
  background-color: var(--ep-paper);
  background-image: radial-gradient(circle, color-mix(in srgb, var(--ep-ink) calc(var(--ep-grid-opacity) * 100%), transparent) 1.2px, transparent 1.35px);
  background-size: 24px 24px;
}
.eyebrow {
  margin: 0 0 1rem;
  color: var(--ep-muted);
  font-family: var(--ep-font-mono);
  font-size: .69rem;
  font-weight: 800;
  letter-spacing: .13em;
  text-transform: uppercase;
}
.hero { min-height: calc(100svh - 76px); display: grid; align-items: center; border-bottom: var(--ep-border-width) solid var(--ep-line); }
.hero-copy h1 { max-width: 760px; margin: 0; color: var(--ep-ink); font-size: clamp(4.75rem, 8.4vw, 8.7rem); font-weight: 900; letter-spacing: -.078em; line-height: .82; }
.hero-copy h1 span { color: var(--ep-accent); -webkit-text-stroke: 2px var(--ep-line); paint-order: stroke fill; }
.hero-actions { margin-top: 2rem; display: flex; flex-wrap: wrap; gap: .8rem; }
.hero-actions .ep-button { min-height: 54px; padding-inline: 1.35rem; }


.section-heading .eyebrow { grid-column: 1 / -1; margin-bottom: -.5rem; }
.theme-copy h2,
/* Tracking follows the shared display token rather than a tighter one-off, so
   the characters in this heading sit apart as much as every other display. */
.closing-section h2 { margin: 0; font-size: clamp(3rem, 6vw, 6rem); font-weight: 900; letter-spacing: var(--ep-display-tracking); line-height: .92; }
.theme-copy > p:last-child { margin: 0; color: var(--ep-muted); line-height: 1.7; }
.app-detail.is-changing { opacity: .35; transform: translateY(8px); }
[data-accent="berry"] { --app-accent: #f05c93; } [data-accent="nectar"] { --app-accent: #ffca48; } [data-accent="lime"] { --app-accent: #a9d957; } [data-accent="opal"] { --app-accent: #78d7e8; }

.theme-section { display: grid; grid-template-columns: minmax(260px, .55fr) minmax(0, 1fr); align-items: center; gap: clamp(3rem, 8vw, 7rem); border-top: 1px solid color-mix(in srgb, var(--ep-line) 16%, transparent); }
.theme-copy h2 { max-width: 620px; }
.theme-copy > p:last-child { max-width: 540px; margin-top: 1.5rem; }
.theme-demo > p:not(.eyebrow) { max-width: 400px; margin: 1.2rem 0 1.6rem; color: var(--ep-muted); line-height: 1.6; }
.section-heading-light .eyebrow { color: #aaa3b0; }

.closing-section { margin-bottom: clamp(4rem, 8vw, 7rem); padding: clamp(3rem, 7vw, 6rem); border: var(--ep-border-width) solid var(--ep-line); border-radius: var(--ep-radius-lg); background: var(--ep-accent); color: var(--ep-accent-contrast); box-shadow: var(--ep-shadow-flat); text-align: center; }
.closing-section h2 { max-width: 920px; margin-inline: auto; }.closing-section > div { margin-top: 2rem; display: flex; flex-wrap: wrap; justify-content: center; gap: .8rem; }.closing-section .eyebrow { color: currentColor; opacity: .7; }
/* The ink fill dated from when this section had an orange ground of its own.
   The redesign puts it on paper, so the call to action takes the accent like
   every other primary button and keeps its ink outline and hard shadow. */

.site-footer { width: min(var(--ep-content), calc(100% - 48px)); min-height: 112px; margin: 0 auto; padding: 2rem 0; display: flex; align-items: center; justify-content: space-between; gap: 2rem; border-top: 1px solid color-mix(in srgb, var(--ep-line) 18%, transparent); color: var(--ep-muted); font-size: .76rem; }
.footer-links { display: flex; flex-wrap: wrap; gap: 1.2rem; }.site-footer a { text-underline-offset: 4px; }
.footer-privacy-choice { padding: 0; border: 0; color: inherit; background: transparent; font: inherit; text-decoration: underline; text-underline-offset: 4px; cursor: pointer; }

.reveal { opacity: 1; transform: none; }
.js .reveal { opacity: 0; transform: translateY(24px); transition: opacity 700ms var(--ep-ease), transform 700ms var(--ep-ease); }
.js .reveal.is-visible { opacity: 1; transform: translateY(0); }.reveal-1 { transition-delay: 80ms; }.reveal-2 { transition-delay: 210ms; }

/* The remaining support,
legal,
download,
beta,
and product funnels keep their
   routes and content while inheriting the calmer type and token system. */
.content-page,
.product-page { background: var(--ep-paper); color: var(--ep-ink); }
.page-wrap { width: min(var(--ep-content), calc(100% - 48px)); margin: 0 auto; }
.page-intro { padding-top: clamp(5rem, 10vw, 9rem); }.page-intro h1 { color: var(--ep-ink); }.page-intro .lede,
.funnel .lede { color: var(--ep-muted); }
.support-card,
.notice-box { border-color: var(--ep-line); background: var(--ep-surface); color: var(--ep-ink); box-shadow: var(--ep-shadow-flat); }

@media (max-width: 1040px) {.hero-copy { max-width: 820px; }
  .theme-section { grid-template-columns: 1fr; }.theme-copy { max-width: 760px; }
}

@media (max-width: 760px) {
  /*
   * No field in the bar on a phone. There is not the width for one beside a
   * logo and a button, and the keyboard would cover most of the page anyway
   * -- the hero's own field is a scroll away and is the better place to type.
   */
  .site-header[data-scrolled] .header-source-icon,
  .site-header[data-scrolled] .header-source-input { width: 0; opacity: 0; visibility: hidden; }
  /* Same selector as the rule that widens the bar, so this one wins on order
     rather than by accident of specificity. */
  .site-header[data-scrolled]:has([data-signed-out-actions]:not([hidden])) {
    width: min(420px, calc(100% - 28px));
  }

  .site-header { min-height: 68px; padding-inline: 16px; }.wordmark-mark { width: 34px; height: 34px; flex-basis: 34px; }.header-actions { gap: .65rem; }.header-create { min-height: 38px; padding-inline: .75rem; }.header-sign-in { font-size: .78rem; }
  .hero { min-height: auto; }.page-wrap,
.site-footer { width: min(100% - 28px, var(--ep-content)); }.hero-copy h1 { font-size: clamp(4rem, 21vw, 6.4rem); }.hero-copy h1 span { -webkit-text-stroke-width: 1.5px; }.hero-actions { display: grid; }.hero-actions .ep-button { width: 100%; }.section-heading .eyebrow { grid-column: auto; }.theme-copy h2,
.closing-section h2 { font-size: clamp(2.8rem, 14vw, 4.6rem); }.closing-section { padding: 3rem 1rem; }.closing-section > div { display: grid; }.site-footer { align-items: flex-start; flex-direction: column; }
}

@media (max-width: 480px) {
  .wordmark > span:last-child { display: none; }.hero-copy h1 { font-size: clamp(3.55rem, 22vw, 5rem); }.footer-links { gap: .8rem; }
}

/* Interactive one-page refinement. */
.hero {
  position: relative;
  isolation: isolate;
  min-height: calc(100svh - 76px);
  overflow: hidden;
}
.hero.dotted-field { background-image: none; }
.hero-playfield { position: absolute; z-index: -1; inset: 0; width: 100%; height: 100%; pointer-events: none; }
.hero-copy { width: min(1120px, 100%); }
.hero-copy h1 {
  max-width: 1120px;
  font-size: clamp(4.4rem, 9.25vw, 9.8rem);
  line-height: .88;
  text-wrap: balance;
}
.hero-copy h1 span { display: inline-block; }
.hero-actions { margin-top: clamp(2rem, 4vw, 3.2rem); }



.theme-section { display: block; }
.theme-copy { max-width: 1000px; margin-bottom: clamp(2.5rem, 5vw, 4rem); }
.theme-copy h2 { max-width: 980px; }






@media (max-width: 760px) {
  .hero-copy h1 { font-size: clamp(3.6rem, 17vw, 6.2rem); line-height: .91; }
  .hero-copy .eyebrow { max-width: 270px; line-height: 1.6; }
}

@media (max-width: 480px) {
  .hero-copy h1 { font-size: clamp(3.2rem, 16.5vw, 4.9rem); }
}
.download-page .site-shell { min-height: 100vh; background-color: #18161d; background-image: linear-gradient(rgba(255,255,255,.045) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,.045) 1px, transparent 1px); background-size: 34px 34px; }
.download-page .site-header { color: #f8f4e9; border-color: rgba(255,255,255,.16); background: color-mix(in srgb, #18161d 90%, transparent); }
.download-page .site-header[data-scrolled] { border-color: rgba(255,255,255,.16); background: color-mix(in srgb, #18161d 97%, transparent); }
.download-page .site-header .wordmark,
.download-page .header-sign-in { color: #f8f4e9; }
.download-page .wordmark-glyph-black { display: none; }
.download-page .wordmark-glyph-white { display: block; }
.download-page .site-footer { color: #aaa4b0; border-color: rgba(255,255,255,.12); }
.download-page .site-footer a { color: #f8f4e9; }
.launcher-download-copy .eyebrow { margin-bottom: 1rem; color: #fbd05b; }

/* Final homepage overrides: kept last so legacy funnel styles cannot win. */
.home-page main > section { min-height: calc(100svh - 76px); }
.hero-copy { margin-inline: auto; display: flex; flex-direction: column; align-items: center; }
.hero-copy h1 { max-width: 1080px; margin-inline: auto; }
.hero-actions { justify-content: center; }
.app-detail-actions .ep-button { border-color: #09080b; background: #f8f4e9; color: #17151c; box-shadow: 4px 4px 0 #09080b; }
.app-detail-actions .ep-button--accent { background: var(--app-accent, #fbd05b); }

.theme-section,
.closing-section { display: flex; flex-direction: column; justify-content: center; }
:root[data-appearance="dark"] { --ep-line: #38383a; }
.closing-section { margin-bottom: 0; }

@media (min-width: 1041px) and (min-height: 680px) {
  .home-page .home-section { padding-block: clamp(2rem, 4vh, 3rem); }
  .theme-copy h2 { font-size: clamp(3.4rem, 5vw, 5rem); }
  .theme-section { gap: 0; }
  .theme-copy { margin-bottom: clamp(2.75rem, 4vw, 3.5rem); }
}

/* Final layout tuning: roomy app browser { padding-block: clamp(5rem, 9vw, 8rem); }
.theme-section { position: relative; isolation: isolate; }
.theme-section > :not(.theme-dot-field) { position: relative; z-index: 1; }
.home-page main > .closing-section { min-height: min(560px, calc(100svh - 76px)); margin-bottom: clamp(3rem, 7vw, 6rem); padding-block: clamp(3rem, 6vw, 4.5rem); }
.closing-section { position: relative; isolation: isolate; overflow: hidden; }
.closing-section > :not(.closing-dot-field) { position: relative; z-index: 1; }

/* Compact registry-driven product funnels. */
.product-page { background: var(--ep-paper); }
.product-short-copy > .eyebrow { margin: clamp(2rem, 3.5vw, 3rem) 0 .8rem; color: color-mix(in srgb, var(--accent) 72%, var(--ep-ink)); }
.product-page .product-short-actions .ep-button { border-color: var(--ep-line); box-shadow: var(--ep-shadow-flat); }
.product-page .product-short-actions .ep-button--accent { background: var(--accent); color: var(--ep-ink); }

@media (max-width: 760px) {
  .home-page main > .closing-section { min-height: 520px; }
  .product-short-actions .ep-button { width: 100%; }
}

/* Search-focused comparison and migration library. */
.compare-page {
  --compare-grid: color-mix(in srgb, var(--ep-line) 9%, transparent);
  background:
    radial-gradient(circle, var(--compare-grid) 1.25px, transparent 1.4px) 0 0 / 30px 30px,
    var(--ep-paper);
}
.compare-page .site-header { background: color-mix(in srgb, var(--ep-paper) 88%, transparent); }
.compare-shell { width: min(1320px, calc(100% - 48px)); margin: 0 auto; padding: clamp(2.5rem, 5vw, 5rem) 0 7rem; }
.breadcrumbs { margin-bottom: clamp(2.5rem, 5vw, 4.5rem); display: flex; flex-wrap: wrap; gap: .65rem; color: var(--ep-muted); font-family: var(--ep-font-mono); font-size: .72rem; letter-spacing: .04em; }
.breadcrumbs a { text-underline-offset: 4px; }
.compare-hero,
.comparison-hero,
.migration-hero {
  position: relative;
  overflow: hidden;
  border: var(--ep-border-width) solid var(--ep-line);
  border-radius: clamp(28px, 4vw, 52px);
  background: var(--ep-surface);
  box-shadow: var(--ep-shadow-flat);
}
.compare-hero {
  min-height: 660px;
  padding: clamp(2rem, 6vw, 6rem);
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(320px, .85fr);
  align-items: center;
  gap: clamp(2rem, 5vw, 5rem);
}
.compare-hero::before,
.comparison-hero::before,
.migration-hero::before {
  content: "";
  position: absolute;
  width: 420px;
  aspect-ratio: 1;
  right: -170px;
  top: -210px;
  border: 56px solid color-mix(in srgb, var(--ep-accent) 34%, transparent);
  border-radius: 50%;
  pointer-events: none;
}
.compare-hero > div:first-child { position: relative; z-index: 1; }
.compare-hero h1,
.comparison-hero h1,
.migration-hero h1 {
  margin: .55rem 0 1.5rem;
  max-width: 980px;
  font-size: clamp(4.1rem, 8.5vw, 8.8rem);
  line-height: .87;
  letter-spacing: -.075em;
  text-wrap: balance;
}
.compare-hero .lede,
.comparison-hero .lede,
.migration-hero .lede { max-width: 760px; margin: 0; color: var(--ep-muted); font-size: clamp(1.05rem, 1.7vw, 1.35rem); line-height: 1.6; }
.orbit-stage { position: relative; min-height: 440px; display: grid; place-items: center; }
.orbit-stage::before,
.orbit-stage::after { content: ""; position: absolute; inset: 18%; border: 2px dashed color-mix(in srgb, var(--ep-line) 28%, transparent); border-radius: 50%; animation: compare-orbit-turn 18s linear infinite; }
.orbit-stage::after { inset: 4%; animation-duration: 26s; animation-direction: reverse; }
.orbit-core { position: relative; z-index: 2; width: 154px; height: 154px; display: grid; place-items: center; border: 1px solid var(--ep-line); border-radius: 42px; background: var(--ep-accent); box-shadow: var(--ep-shadow-flat); transform: rotate(-5deg); animation: compare-core-float 4.8s var(--ep-ease) infinite alternate; }
.orbit-core img { width: 112px; height: 112px; object-fit: contain; }
.orbit-stage i { position: absolute; z-index: 1; width: 20px; height: 20px; border: 1px solid var(--ep-line); background: var(--ep-accent); }
.orbit-stage i:nth-of-type(1) { left: 7%; top: 25%; border-radius: 50%; animation: compare-drift 3.8s var(--ep-ease) infinite alternate; }
.orbit-stage i:nth-of-type(2) { right: 8%; top: 18%; border-radius: 4px; rotate: 18deg; animation: compare-drift 4.6s var(--ep-ease) -1s infinite alternate-reverse; }
.orbit-stage i:nth-of-type(3) { right: 18%; bottom: 11%; clip-path: polygon(50% 0, 100% 100%, 0 100%); animation: compare-drift 4.1s var(--ep-ease) -.5s infinite alternate; }
.orbit-stage b { position: absolute; z-index: 2; padding: .65rem .9rem; border: 1px solid var(--ep-line); border-radius: 999px; background: var(--ep-surface); box-shadow: var(--ep-shadow-flat); font-family: var(--ep-font-mono); font-size: .68rem; text-transform: uppercase; letter-spacing: .08em; }
.orbit-stage b:nth-of-type(1) { left: 5%; bottom: 19%; rotate: -7deg; }
.orbit-stage b:nth-of-type(2) { right: 2%; top: 38%; rotate: 6deg; }
.orbit-stage b:nth-of-type(3) { left: 34%; top: 2%; rotate: 3deg; }
@keyframes compare-orbit-turn { to { transform: rotate(360deg); } }
@keyframes compare-core-float { to { transform: translateY(-10px) rotate(3deg); } }
@keyframes compare-drift { to { translate: 8px -12px; rotate: 24deg; } }

.compare-section-heading { max-width: 880px; }
.compare-section-heading h2,
.migration-bridge h2,
.source-section h2,
.planner-controls h2,
.migration-check h2 { margin: .45rem 0 1rem; font-size: clamp(2.7rem, 6vw, 5.6rem); line-height: .92; letter-spacing: -.06em; text-wrap: balance; }
.compare-section-heading > p:not(.eyebrow) { color: var(--ep-muted); line-height: 1.65; }
.compare-chooser,
.comparison-link-grid,
.fit-grid,
.matrix-section,
.source-section,
.export-section,
.migration-planner,
.migration-check { padding-top: clamp(6rem, 11vw, 10rem); }
.workflow-options { margin-top: 2.5rem; display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; }
.workflow-options button {
  min-height: 230px;
  padding: 1.4rem;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: .85rem;
  border: 1px solid var(--ep-line);
  border-radius: var(--ep-radius-md);
  background: var(--ep-surface);
  color: var(--ep-ink);
  box-shadow: var(--ep-shadow-flat);
  text-align: left;
  cursor: pointer;
  transition: transform 220ms var(--ep-ease), background-color 220ms ease, box-shadow 220ms var(--ep-ease);
}
.workflow-options button:hover { transform: translateY(-5px) rotate(-.5deg); box-shadow: var(--ep-shadow-flat); }
.workflow-options button[aria-pressed="true"] { background: var(--ep-accent); transform: translateY(-7px); }
.workflow-options button span { font-family: var(--ep-font-mono); font-size: .72rem; }
.workflow-options button b { margin-top: auto; font-size: clamp(1.35rem, 2.3vw, 2rem); line-height: 1; }
.workflow-options button small { color: color-mix(in srgb, currentColor 70%, transparent); line-height: 1.5; }
.workflow-result { margin-top: 1.4rem; min-height: 170px; padding: clamp(1.5rem, 4vw, 2.5rem); display: grid; grid-template-columns: minmax(140px, .35fr) minmax(180px, .35fr) minmax(0, 1.3fr) auto; align-items: center; gap: 1.5rem; border: 1px solid var(--ep-line); border-radius: var(--ep-radius-md); background: #18161d; color: #f7f3ec; box-shadow: var(--ep-shadow-flat); }
.workflow-result.is-changing { animation: result-swap 360ms var(--ep-ease); }
.workflow-result span { color: #fbd05b; font-family: var(--ep-font-mono); font-size: .7rem; font-weight: 800; text-transform: uppercase; letter-spacing: .09em; }
.workflow-result h3 { margin: 0; font-size: clamp(2rem, 4vw, 3.6rem); letter-spacing: -.055em; }
.workflow-result p { margin: 0; color: #c9c4cf; line-height: 1.55; }
@keyframes result-swap { 0% { opacity: .55; transform: scale(.985); } 70% { transform: scale(1.008); } }
.seo-card-grid { margin-top: 2.5rem; display: grid; grid-template-columns: repeat(2, 1fr); gap: 1.4rem; }
.seo-card { position: relative; min-height: 390px; overflow: hidden; padding: clamp(1.5rem, 4vw, 2.6rem); display: flex; flex-direction: column; border: 1px solid var(--ep-line); border-radius: var(--ep-radius-lg); background: var(--ep-surface); box-shadow: var(--ep-shadow-flat); text-decoration: none; transition: transform 240ms var(--ep-ease), box-shadow 240ms var(--ep-ease); }
.seo-card::after { content: ""; position: absolute; right: -45px; bottom: -60px; width: 180px; aspect-ratio: 1; border: 30px solid var(--ep-accent); border-radius: 50%; transition: transform 450ms var(--ep-ease); }
.seo-card:hover { transform: translateY(-7px); box-shadow: var(--ep-shadow-flat); }
.seo-card:hover::after { transform: translateY(-1px) rotate(20deg); }
.seo-card > span { font-family: var(--ep-font-mono); font-size: .7rem; font-weight: 900; letter-spacing: .1em; text-transform: uppercase; }
.seo-card h3 { max-width: 520px; margin: auto 0 1rem; font-size: clamp(2.7rem, 5vw, 5rem); line-height: .9; letter-spacing: -.065em; }
.seo-card p { position: relative; z-index: 1; max-width: 500px; margin: 0 0 2rem; color: var(--ep-muted); line-height: 1.6; }
.seo-card i { font-style: normal; font-weight: 850; }
.comparison-method { margin-top: clamp(6rem, 10vw, 9rem); padding: 2rem; display: grid; grid-template-columns: .6fr 1.8fr auto; gap: 2rem; align-items: center; border-top: 1px solid var(--ep-line); border-bottom: 1px solid var(--ep-line); }
.comparison-method span,
.comparison-method b { font-family: var(--ep-font-mono); font-size: .7rem; text-transform: uppercase; letter-spacing: .08em; }
.comparison-method p { margin: 0; color: var(--ep-muted); line-height: 1.6; }

.comparison-hero { padding: clamp(2.2rem, 6vw, 6rem); }
.comparison-hero h1 span { color: var(--ep-accent); -webkit-text-stroke: 2px var(--ep-line); paint-order: stroke fill; }
.comparison-hero-actions { margin-top: 2rem; display: flex; flex-wrap: wrap; gap: .8rem; }
.comparison-disclosure { position: relative; z-index: 1; margin-top: clamp(3rem, 7vw, 6rem); max-width: 860px; padding-top: 1.2rem; display: grid; grid-template-columns: 110px 1fr auto; gap: 1.5rem; border-top: 1px solid color-mix(in srgb, var(--ep-line) 22%, transparent); color: var(--ep-muted); font-size: .82rem; }
.comparison-disclosure p { margin: 0; line-height: 1.55; }
.comparison-disclosure span { font-family: var(--ep-font-mono); white-space: nowrap; }
.fit-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1.4rem; }
.fit-grid .compare-section-heading { grid-column: 1 / -1; margin-bottom: 1rem; }
.fit-grid article { min-height: 360px; padding: clamp(1.5rem, 4vw, 2.8rem); display: flex; flex-direction: column; border: 1px solid var(--ep-line); border-radius: var(--ep-radius-lg); background: var(--ep-surface); box-shadow: var(--ep-shadow-flat); }
.fit-grid article:nth-of-type(1) { background: color-mix(in srgb, var(--ep-accent) 24%, var(--ep-surface)); }
.fit-grid article > span { font-family: var(--ep-font-mono); font-size: .73rem; font-weight: 900; text-transform: uppercase; letter-spacing: .08em; }
.fit-grid ul { margin: auto 0 0; padding: 0; display: grid; gap: 1.15rem; list-style: none; }
.fit-grid li { padding-left: 1.6rem; position: relative; font-size: clamp(1rem, 1.5vw, 1.2rem); line-height: 1.5; }
.fit-grid li::before { content: ""; position: absolute; left: 0; top: .48em; width: 10px; height: 10px; border: 1px solid var(--ep-line); border-radius: 50%; background: var(--ep-accent); }
.matrix-section { scroll-margin-top: 100px; }
.matrix-filters { margin: 2rem 0 1.2rem; display: flex; flex-wrap: wrap; gap: .65rem; }
.matrix-filters button { min-height: 42px; padding: .55rem .9rem; border: 1px solid var(--ep-line); border-radius: 999px; background: var(--ep-surface); color: var(--ep-ink); font-weight: 800; cursor: pointer; transition: transform 160ms var(--ep-ease), background-color 160ms ease; }
.matrix-filters button:hover { transform: translateY(-2px); }
.matrix-filters button[aria-pressed="true"] { background: var(--ep-accent); box-shadow: var(--ep-shadow-flat); }
.comparison-table-wrap { overflow: hidden; border: 1px solid var(--ep-line); border-radius: var(--ep-radius-lg); background: var(--ep-surface); box-shadow: var(--ep-shadow-flat); }
.comparison-table { width: 100%; border-collapse: collapse; }
.comparison-table th,
.comparison-table td { padding: clamp(1rem, 2.6vw, 2rem); border-bottom: 1px solid color-mix(in srgb, var(--ep-line) 20%, transparent); text-align: left; vertical-align: top; line-height: 1.58; }
.comparison-table thead th { background: #18161d; color: #f7f3ec; font-family: var(--ep-font-mono); font-size: .7rem; letter-spacing: .08em; text-transform: uppercase; }
.comparison-table tbody th { width: 19%; font-size: clamp(1.05rem, 2vw, 1.4rem); }
.comparison-table tbody td { width: 40.5%; color: var(--ep-muted); }
.comparison-table tbody td span { display: none; }
.comparison-table tbody tr:last-child > * { border-bottom: 0; }
.comparison-table tbody tr.is-filtered-out { display: none; }
.comparison-table tbody tr.is-focused { animation: row-focus 360ms var(--ep-ease); }
@keyframes row-focus { 0% { background: color-mix(in srgb, var(--ep-accent) 34%, var(--ep-surface)); } }
.migration-bridge { margin-top: clamp(6rem, 11vw, 10rem); padding: clamp(2rem, 5vw, 4.5rem); display: grid; grid-template-columns: 1fr auto; align-items: end; gap: 2rem; border: 1px solid var(--ep-line); border-radius: var(--ep-radius-lg); background: var(--ep-accent); box-shadow: var(--ep-shadow-flat); }
.migration-bridge p { max-width: 760px; margin-bottom: 0; line-height: 1.6; }
.source-section { display: grid; grid-template-columns: minmax(0, .8fr) minmax(0, 1.2fr); gap: clamp(2rem, 6vw, 6rem); }
.source-section > div > p:not(.eyebrow) { color: var(--ep-muted); line-height: 1.6; }
.source-section ul { margin: 0; padding: 0; display: grid; list-style: none; border-top: 1px solid var(--ep-line); }
.source-section li { border-bottom: 1px solid color-mix(in srgb, var(--ep-line) 24%, transparent); }
.source-section a { min-height: 64px; padding: .8rem .2rem; display: flex; align-items: center; justify-content: space-between; gap: 1rem; text-decoration: none; font-weight: 780; }
.source-section a:hover { color: color-mix(in srgb, var(--ep-ink) 58%, var(--ep-accent)); }
.trademark-note { margin: 5rem 0 0; color: var(--ep-muted); font-size: .75rem; text-align: center; }

.migration-hero { min-height: 610px; padding: clamp(2rem, 6vw, 6rem); display: grid; grid-template-columns: minmax(0, 1.2fr) minmax(280px, .8fr); align-items: center; gap: 3rem; }
.migration-hero > div:first-child { position: relative; z-index: 1; }
.migration-hero h1 { font-size: clamp(3.8rem, 7.4vw, 7.6rem); }
.migration-stack { position: relative; z-index: 1; display: grid; justify-items: center; gap: 1rem; }
.migration-stack span { min-width: 230px; min-height: 120px; padding: 1rem; display: flex; align-items: center; justify-content: center; gap: .8rem; border: 1px solid var(--ep-line); border-radius: 30px; background: var(--ep-surface); box-shadow: var(--ep-shadow-flat); font-size: 1.45rem; font-weight: 900; }
.migration-stack span:first-child { rotate: -4deg; }
.migration-stack span:last-child { background: var(--ep-accent); rotate: 3deg; }
.migration-stack img { width: 62px; height: 62px; object-fit: contain; }
.migration-stack i { font-size: 2rem; font-style: normal; animation: migration-arrow 1.8s var(--ep-ease) infinite; }
@keyframes migration-arrow { 50% { transform: translateY(7px); } }
.migration-warning { margin-top: 2rem; padding: 1.4rem 1.6rem; display: grid; grid-template-columns: 210px 1fr; gap: 2rem; border: 1px solid var(--ep-line); border-radius: var(--ep-radius-md); background: #18161d; color: #f7f3ec; box-shadow: var(--ep-shadow-flat); }
.migration-warning b { color: #fbd05b; font-size: 1.1rem; }
.migration-warning p { margin: 0; color: #c9c4cf; line-height: 1.6; }
.export-section { display: grid; grid-template-columns: minmax(0, .85fr) minmax(0, 1.15fr); gap: clamp(2rem, 6vw, 6rem); }
.export-steps { margin: 0; padding: 0; display: grid; list-style: none; border-top: 1px solid var(--ep-line); }
.export-steps li { min-height: 100px; padding: 1.2rem .2rem; display: grid; grid-template-columns: 52px 1fr; gap: 1rem; align-items: start; border-bottom: 1px solid color-mix(in srgb, var(--ep-line) 24%, transparent); }
.export-steps span { color: color-mix(in srgb, var(--ep-accent) 70%, var(--ep-ink)); font-family: var(--ep-font-mono); font-size: .73rem; font-weight: 900; }
.export-steps p { margin: 0; line-height: 1.6; }
.migration-planner { scroll-margin-top: 90px; display: grid; grid-template-columns: minmax(0, .9fr) minmax(380px, 1.1fr); gap: 1.4rem; }
.planner-controls,
.planner-output { padding: clamp(1.4rem, 4vw, 3rem); border: 1px solid var(--ep-line); border-radius: var(--ep-radius-lg); box-shadow: var(--ep-shadow-flat); }
.planner-controls { background: var(--ep-surface); }
.planner-controls > p:not(.eyebrow) { color: var(--ep-muted); line-height: 1.6; }
.migration-items { margin-top: 2rem; display: grid; gap: .75rem; }
.migration-item { cursor: pointer; }
.migration-item input { position: absolute; opacity: 0; pointer-events: none; }
.migration-item > span { min-height: 98px; padding: 1rem; display: grid; gap: .4rem; border: 1px solid color-mix(in srgb, var(--ep-line) 42%, transparent); border-radius: 18px; background: var(--ep-paper); transition: transform 180ms var(--ep-ease), border-color 180ms ease, background-color 180ms ease; }
.migration-item:hover > span { transform: translateX(4px); border-color: var(--ep-line); }
.migration-item input:checked + span { border-color: var(--ep-line); background: color-mix(in srgb, var(--ep-accent) 26%, var(--ep-surface)); box-shadow: var(--ep-shadow-flat); transform: translateY(-1px); }
.migration-item input:focus-visible + span { outline: 3px solid var(--ep-accent); outline-offset: 4px; }
.migration-item small { color: var(--ep-muted); line-height: 1.48; }
.planner-actions { margin-top: 1.5rem; display: flex; flex-wrap: wrap; gap: .6rem; }
.planner-copy-status { min-height: 1.5em; color: var(--ep-muted); font-size: .8rem; }
.planner-output { position: sticky; top: 100px; min-height: 660px; align-self: start; overflow: hidden; background: #18161d; color: #f7f3ec; }
.planner-output::before { content: ""; position: absolute; right: -110px; bottom: -130px; width: 310px; aspect-ratio: 1; border: 44px solid color-mix(in srgb, #fbd05b 36%, transparent); border-radius: 50%; }
.planner-progress { --progress: 0deg; position: relative; z-index: 1; width: 126px; aspect-ratio: 1; margin-bottom: 3rem; display: grid; place-items: center; border-radius: 50%; background: conic-gradient(#fbd05b var(--progress), #34313b 0); transition: background 450ms var(--ep-ease); }
.planner-progress::after { content: ""; position: absolute; inset: 13px; border-radius: 50%; background: #18161d; }
.planner-progress span { position: relative; z-index: 1; font-family: var(--ep-font-mono); font-size: .8rem; font-weight: 900; }
.planner-output .eyebrow { color: #fbd05b; }
.planner-output h3 { margin: .35rem 0 1.6rem; font-size: clamp(2rem, 4vw, 3.4rem); line-height: .94; letter-spacing: -.05em; }
.planner-output ol { position: relative; z-index: 1; margin: 0; padding-left: 1.4rem; display: grid; gap: .9rem; color: #d3ced8; line-height: 1.5; }
.planner-output li::marker { color: #fbd05b; font-family: var(--ep-font-mono); font-weight: 900; }
.planner-note { position: relative; z-index: 1; margin: 2rem 0 0; padding-top: 1.2rem; border-top: 1px solid #403c48; color: #aaa4b1; font-size: .82rem; line-height: 1.55; }
.migration-check > div:first-child { margin-bottom: 2rem; }
.check-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; }
.check-grid article { min-height: 280px; padding: 1.5rem; display: flex; flex-direction: column; border: 1px solid var(--ep-line); border-radius: var(--ep-radius-md); background: var(--ep-surface); box-shadow: var(--ep-shadow-flat); }
.check-grid span { font-family: var(--ep-font-mono); font-size: .72rem; }
.check-grid h3 { margin: auto 0 .8rem; font-size: clamp(1.7rem, 3vw, 2.6rem); line-height: .95; letter-spacing: -.045em; }
.check-grid p { margin: 0; color: var(--ep-muted); line-height: 1.55; }

@media (max-width: 980px) {
  .compare-hero,
.migration-hero { grid-template-columns: 1fr; }
  .orbit-stage { min-height: 340px; }
  .migration-stack { grid-template-columns: 1fr auto 1fr; }
  .migration-stack i { rotate: -90deg; }
  .workflow-result { grid-template-columns: 1fr 1fr; }
  .fit-grid,
.source-section,
.export-section,
.migration-planner { grid-template-columns: 1fr; }
  .planner-output { position: relative; top: auto; min-height: 0; }
}
@media (max-width: 720px) {
  .compare-shell { width: min(100% - 28px, 1320px); padding-top: 1.7rem; }
  .compare-hero,
.comparison-hero,
.migration-hero { border-radius: 28px; box-shadow: var(--ep-shadow-flat); }
  .compare-hero { min-height: 0; padding: 2rem 1.25rem; }
  .compare-hero h1,
.comparison-hero h1,
.migration-hero h1 { font-size: clamp(3.45rem, 17vw, 5.5rem); }
  .orbit-stage { min-height: 300px; }
  .orbit-core { width: 120px; height: 120px; border-radius: 32px; }
  .orbit-core img { width: 86px; height: 86px; }
  .workflow-options,
.seo-card-grid,
.check-grid { grid-template-columns: 1fr; }
  .workflow-options button { min-height: 170px; }
  .workflow-result { grid-template-columns: 1fr; }
  .seo-card { min-height: 330px; }
  .comparison-method { grid-template-columns: 1fr; gap: .8rem; }
  .comparison-disclosure { grid-template-columns: 1fr; gap: .6rem; }
  .fit-grid { grid-template-columns: 1fr; }
  .fit-grid .compare-section-heading { grid-column: auto; }
  .comparison-table-wrap { overflow: visible; border: 0; border-radius: 0; background: transparent; box-shadow: none; }
  .comparison-table,
.comparison-table tbody { display: grid; gap: .8rem; }
  .comparison-table thead { display: none; }
  .comparison-table tr { padding: 1.1rem; display: grid; gap: 1rem; border: 1px solid var(--ep-line); border-radius: 18px; background: var(--ep-surface); box-shadow: var(--ep-shadow-flat); }
  .comparison-table th,
.comparison-table td { width: auto !important; padding: 0; border: 0; }
  .comparison-table tbody td span { display: block; margin-bottom: .35rem; color: var(--ep-ink); font-family: var(--ep-font-mono); font-size: .65rem; font-weight: 900; text-transform: uppercase; letter-spacing: .08em; }
  .migration-bridge { grid-template-columns: 1fr; align-items: start; }
  .migration-stack { grid-template-columns: 1fr; }
  .migration-stack i { rotate: 0deg; }
  .migration-warning { grid-template-columns: 1fr; gap: .5rem; }
  .planner-controls,
.planner-output { padding: 1.3rem; border-radius: 24px; box-shadow: var(--ep-shadow-flat); }
  .planner-output h3 { font-size: 2.3rem; }
}
@media (prefers-reduced-motion: reduce) {
  .orbit-stage::before,
.orbit-stage::after,
.orbit-core,
.orbit-stage i,
.migration-stack i { animation: none !important; }
  .workflow-result.is-changing,
.comparison-table tbody tr.is-focused { animation: none !important; }
  .workflow-options button,
.seo-card,
.seo-card::after,
.migration-item > span,
.planner-progress { transition: none !important; }
}

/*
 * Everything the visitor came for stacks above whatever is behind it.
 *
 * The header is exempt. It is a direct child of the shell, so this rule used
 * to hand it `position: relative` at a higher specificity than its own
 * `position: sticky` -- which is why the collapsing capsule collapsed on cue
 * and then scrolled away with the page instead of staying put. It already
 * declares a stacking position of its own, so it needs nothing from here.
 */
.site-shell > *:not(.site-header) { position: relative; z-index: 1; }
.site-header { z-index: 80; }




/*
 * Homepage composition.
 *
 * Loaded last so it wins over the legacy rules in styles.css that still serve
 * the product, compare, support and legal pages. Those pages are untouched by
 * this file; everything here is scoped to .home-page or to classes only the
 * new homepage uses.
 */

/* ------------------------------------------- neutralise the legacy homepage */

body.home-page { background: transparent; color: var(--ep-ink); background-image: none; }
.home-page main > section { min-height: 0; }
.home-page .site-header { position: relative; inset: auto; color: var(--ep-ink); border-color: color-mix(in srgb, var(--ep-ink) 16%, transparent); }
.home-page .site-footer { display: flex; }
.home-page main { display: block; }
.home-page main > section { margin: 0; border-radius: 0; }
.home-page .hero { min-height: 0; border-bottom: 0; display: block; }
/* Stays a grid so the column's own gap spaces the headline, the sentence and
   the call to action. As a block the gap was inert and the headline sat
   directly against the paragraph. */
.home-page .hero-copy { display: grid; gap: 2.4rem; justify-items: start; margin-inline: 0; text-align: left; }
.home-page .hero-copy h1 { max-width: none; margin: 0; color: inherit; font-size: var(--ep-step-hero); letter-spacing: var(--ep-display-tracking); line-height: .92; }
.home-page .hero-copy h1 span { color: inherit; -webkit-text-stroke: 0; }
.home-page .hero-actions { justify-content: flex-start; }
.home-page .theme-section,
.home-page .closing-section { display: block; grid-template-columns: none; gap: 0; border: 0; border-radius: 0; background: none; color: inherit; box-shadow: none; text-align: left; margin: 0; padding-block: var(--ep-section-y); }

/* ------------------------------------------------------------------ header */

.site-header { gap: 1.25rem; }
.site-nav { display: flex; align-items: center; gap: .25rem; font-family: var(--ep-font-sans); font-size: .92rem; font-weight: 750; letter-spacing: 0; text-transform: none; }
/* Centred on the bar itself rather than in the gap between the wordmark and
   the account actions, which are different widths. Scoped above the mobile
   breakpoint so it never fights the narrow-screen dropdown, which positions
   the same element itself. */
@media (min-width: 761px) {
  .site-nav { position: absolute; left: 50%; transform: translateX(-50%); margin: 0; }
}
.site-nav a { padding: .5rem .8rem; border-radius: 999px; text-decoration: none; transition: background-color 160ms ease; }
.site-nav a:hover { background: color-mix(in srgb, var(--ep-ink) 8%, transparent); }
.header-sign-in { font-weight: 750; text-decoration: none; padding: .5rem .4rem; }
.header-sign-in:hover { text-decoration: underline; text-underline-offset: 3px; }

/* -------------------------------------------------------------------- hero */

.hero-grid { display: grid; grid-template-columns: minmax(0, .92fr) minmax(0, 1.08fr); align-items: center; gap: var(--ep-gap-lg); }
/* The hero column is deliberately airy: the headline, the sentence under it
   and the call to action each get room rather than reading as one block. */
.hero-copy { display: grid; gap: 2.4rem; justify-items: start; }
.hero-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 1rem 1.25rem; margin-top: .6rem; }
/* The note sits beside the button and wraps under it only when the row runs
   out of width, so it stays on one line on a phone rather than breaking up. */
.hero-note { color: var(--ep-muted); font-size: var(--ep-step-small); line-height: 1.45; }
.hero-note b { display: inline; color: var(--ep-ink); font-weight: 800; }
.hero-shot { min-width: 0; }

/* ------------------------------------------------------------- what it is */

.anchor-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(255px, 1fr)); gap: var(--ep-gap-md); margin: 0; padding: 0; list-style: none; }
.anchor-grid .ep-card { display: grid; gap: .6rem; align-content: start; }

/* ------------------------------------------------------------------ canvas */

.canvas-grid { display: grid; grid-template-columns: minmax(0, .85fr) minmax(0, 1.15fr); align-items: center; gap: var(--ep-gap-lg); }
.canvas-copy { display: grid; gap: 1.15rem; justify-items: start; }
.widget-groups { display: grid; gap: 1rem; margin: .75rem 0 0; }
.widget-group dt { margin-bottom: .5rem; font-family: var(--ep-font-mono); font-size: var(--ep-step-eyebrow); font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: var(--ep-muted); }
.widget-group dd { display: flex; flex-wrap: wrap; gap: .4rem; margin: 0; }
.widget-group dd span { padding: .3rem .68rem; border: 1.5px solid color-mix(in srgb, var(--ep-line) 40%, transparent); border-radius: 999px; background: var(--ep-surface); font-size: var(--ep-step-small); font-weight: 650; }
.canvas-figure { margin: 0; display: grid; gap: .8rem; }
.canvas-figure figcaption,
.workspace-pair figcaption { color: var(--ep-muted); font-size: var(--ep-step-small); }

/* -------------------------------------------------------------- workspaces */

.section-head { display: grid; gap: .9rem; justify-items: start; max-width: 62ch; margin-bottom: var(--ep-gap-lg); }
.workspace-pair { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: var(--ep-gap-md); }
.workspace-pair figure { margin: 0; display: grid; gap: .7rem; }
.workspace-pair figure:first-child .ep-shot { transform: rotate(-.9deg); }
.workspace-pair figure:last-child .ep-shot { transform: rotate(.9deg); }

/* ------------------------------------------------------------------ themes */

.theme-grid { display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, .9fr); align-items: center; gap: var(--ep-gap-lg); }
.theme-gallery { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--ep-gap-md); }
.theme-gallery figure { margin: 0; display: grid; gap: .5rem; }
.theme-gallery figcaption { font-family: var(--ep-font-mono); font-size: var(--ep-step-eyebrow); font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: var(--ep-muted); }
.theme-gallery .ep-shot { box-shadow: var(--ep-shadow-flat); }
.theme-copy { display: grid; gap: 1.1rem; justify-items: start; }
.theme-list { display: grid; gap: 1.15rem; margin: 0; padding: 0; list-style: none; max-width: 52ch; }
.theme-list li { padding-left: 1.6rem; position: relative; line-height: 1.5; }
.theme-list li::before { content: ""; position: absolute; left: 0; top: .48em; width: 9px; height: 9px; border: 1px solid var(--ep-line); background: var(--ep-accent); }
.theme-list b { font-weight: 850; }
.theme-community { color: var(--ep-muted); }

/* ----------------------------------------------------------- collaboration */

.collab-section { color: var(--ep-paper); }
.collab-inner .section-head { margin-bottom: var(--ep-gap-lg); }
.collab-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(255px, 1fr)); gap: var(--ep-gap-md); margin: 0; padding: 0; list-style: none; }
.collab-grid li { display: grid; gap: .55rem; align-content: start; padding: 1.4rem; border: 1px solid color-mix(in srgb, var(--ep-paper) 30%, transparent); border-radius: var(--ep-radius-md); }
.collab-grid .ep-body { color: color-mix(in srgb, var(--ep-paper) 78%, var(--ep-ink)); }

/* ----------------------------------------------------------------- devices */

.devices-inner { display: grid; gap: 1rem; justify-items: start; }

/* ------------------------------------------------------------------- plans */

.plan-mini-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: var(--ep-gap-md); }
.plan-mini { position: relative; display: grid; gap: .55rem; align-content: start; padding: 1.5rem; border: var(--ep-border-width) solid var(--ep-line); border-radius: var(--ep-radius-md); background: var(--ep-surface); }
.plan-mini.is-recommended { background: var(--ep-yellow-soft); box-shadow: var(--ep-shadow-flat); }
.plan-mini-flag { position: absolute; top: -13px; left: 1.4rem; padding: .22rem .6rem; border: 1px solid var(--ep-line); border-radius: 999px; background: var(--ep-accent); color: var(--ep-accent-contrast); font-size: .68rem; font-weight: 850; letter-spacing: .04em; text-transform: uppercase; }
.plan-mini-price { display: grid; gap: .15rem; margin: 0; }
.plan-mini-price strong { font-family: var(--ep-font-display); font-size: 1.5rem; font-weight: 900; letter-spacing: -.02em; }
.plan-mini-price small { color: var(--ep-muted); font-size: var(--ep-step-small); }
.plan-mini-tagline { color: var(--ep-muted); }
.plans-more { margin: var(--ep-gap-md) 0 0; }

/* ----------------------------------------------------------------- closing */

.closing-inner { display: grid; gap: 1.3rem; justify-items: center; text-align: center; }
.closing-inner .ep-lede { max-width: 44ch; }
.closing-signin { margin: .4rem 0 0; color: var(--ep-muted); font-size: var(--ep-step-small); }
.closing-signin a { font-weight: 750; }

/* ---------------------------------------------------------------- features */

.features-intro .ep-wrap { display: grid; gap: 1.3rem; justify-items: start; }

/* Two prose sections that stacked on default margins and read as one dense
   block. As grids they get an explicit, generous rhythm between the eyebrow,
   the heading and whatever follows. */
#data > .ep-wrap,
#elements > .ep-wrap { display: grid; gap: 1.7rem; justify-items: start; }
#data > .ep-wrap > .ep-eyebrow,
#elements > .ep-wrap > .ep-eyebrow { margin-bottom: -.5rem; }

.feature-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: start; gap: var(--ep-gap-lg) var(--ep-gap-lg); }
.feature-copy { display: grid; gap: .9rem; justify-items: start; }
.feature-figure { grid-row: span 2; margin: 0; align-self: center; }
.feature-block.is-flipped .feature-figure { order: -1; }
.feature-list { display: grid; gap: var(--ep-gap-sm); margin: 0; padding: 0; list-style: none; }
.feature-list li { display: grid; gap: .35rem; padding: 1.1rem 1.2rem; border: var(--ep-border-width) solid var(--ep-line); border-radius: var(--ep-radius-md); background: var(--ep-surface); }

.widget-catalogue { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: var(--ep-gap-md); margin: 0; }
.widget-catalogue dt { margin-bottom: .6rem; font-family: var(--ep-font-mono); font-size: var(--ep-step-eyebrow); font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: var(--ep-muted); }
.widget-catalogue dd { display: flex; flex-wrap: wrap; gap: .4rem; margin: 0; }
.widget-catalogue dd span { padding: .32rem .7rem; border: 1.5px solid color-mix(in srgb, var(--ep-line) 40%, transparent); border-radius: 999px; background: var(--ep-surface); font-size: var(--ep-step-small); font-weight: 650; }
.widget-note { margin-top: var(--ep-gap-md); max-width: 68ch; color: var(--ep-muted); }

/* ------------------------------------------------------------------- plans */

/* The page opens on the plans themselves: one title, the interval switch, then
   the grid, all on a shared centre line. */
.plans-title { margin: 0 0 var(--ep-gap-md); text-align: center; }

.plan-interval { display: flex; width: fit-content; margin-inline: auto; gap: .3rem; padding: .3rem; margin-bottom: var(--ep-gap-lg); border: var(--ep-border-width) solid var(--ep-line); border-radius: 999px; background: var(--ep-surface); }
.plan-interval button { display: inline-flex; align-items: center; gap: .45rem; min-height: 42px; padding: .5rem 1.1rem; border: 0; border-radius: 999px; background: transparent; font-weight: 800; cursor: pointer; transition: background-color 160ms ease, color 160ms ease; }
.plan-interval button[aria-pressed="true"] { background: var(--ep-accent); color: var(--ep-accent-contrast); }
.plan-interval small { font-size: .7rem; font-weight: 700; opacity: .8; }

.plan-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(270px, 1fr)); gap: var(--ep-gap-md); align-items: start; }
.plan-card { position: relative; display: grid; gap: .75rem; align-content: start; padding: 1.6rem; border: var(--ep-border-width) solid var(--ep-line); border-radius: var(--ep-radius-md); background: var(--ep-surface); }
.plan-card.is-recommended { background: var(--ep-yellow-soft); box-shadow: var(--ep-shadow-flat); }
.plan-card-tagline { color: var(--ep-muted); }
/* Name and price are the card's masthead and sit centred above the tagline,
   feature list and note, which stay left-aligned for reading. */
.plan-card .ep-card-title { text-align: center; }
.plan-price { display: grid; gap: .15rem; margin: 0; justify-items: center; text-align: center; }
.plan-price strong { font-family: var(--ep-font-display); font-size: 1.85rem; font-weight: 900; letter-spacing: -.025em; }
.plan-price small { color: var(--ep-muted); font-size: var(--ep-step-small); }
/* One price is rendered per interval; the container decides which shows, so
   the page still reads correctly before the toggle script runs. */
[data-plan-interval][data-interval="yearly"] [data-interval-monthly],
[data-plan-interval][data-interval="monthly"] [data-interval-yearly] { display: none; }
.plan-card-cta { width: 100%; }
.plan-card ul { display: grid; gap: .45rem; margin: .3rem 0 0; padding: 0; list-style: none; font-size: var(--ep-step-small); }
.plan-card li { display: grid; grid-template-columns: 1.1rem 1fr; gap: .45rem; align-items: start; line-height: 1.45; }
.plan-card li span { font-weight: 900; color: var(--ep-accent-deep); }
.plan-card-note { margin: .3rem 0 0; color: var(--ep-muted); font-size: var(--ep-step-small); line-height: 1.45; }
.plans-checkout-status { margin: var(--ep-gap-md) auto 0; max-width: 62ch; padding: .85rem 1rem; border: var(--ep-border-width) solid var(--ep-line); border-radius: var(--ep-radius-sm); background: var(--ep-yellow-soft); font-weight: 750; text-align: center; }
.plans-checkout-note { margin: var(--ep-gap-md) auto 0; max-width: 62ch; color: var(--ep-muted); font-size: var(--ep-step-small); text-align: center; }

.plan-faq { display: grid; gap: .6rem; margin-top: var(--ep-gap-md); }
.plan-faq details { border: var(--ep-border-width) solid var(--ep-line); border-radius: var(--ep-radius-md); background: var(--ep-surface); }
.plan-faq summary { padding: 1rem 1.2rem; font-weight: 800; cursor: pointer; list-style: none; }
.plan-faq summary::-webkit-details-marker { display: none; }
.plan-faq summary::after { content: "+"; float: right; font-weight: 900; }
.plan-faq details[open] summary::after { content: "−"; }
.plan-faq details > p { margin: 0; padding: 0 1.2rem 1.1rem; }

/* ------------------------------------------------------------------ narrow */

@media (max-width: 940px) {
  .hero-grid,
  .canvas-grid,
  .feature-grid,
  .theme-grid { grid-template-columns: minmax(0, 1fr); }
  .feature-figure { grid-row: auto; }
  .feature-block.is-flipped .feature-figure { order: 0; }
  .hero-shot { order: -1; }
  .theme-gallery { order: 1; }
}

@media (max-width: 640px) {
  .site-nav { display: none; }
  .hero-note { max-width: none; }
  .hero-actions { align-items: flex-start; flex-direction: column; }
}


/* ------------------------------------------------- mid-page colour pass */

/*
 * The middle of the page was cream on cream. These give each section its own
 * tint and let the widget-board language carry through: tinted cards, hard
 * shadows, and accents that pick up whatever the story slider left behind.
 */

/* Anchor cards get a shadow so they read as widgets, not panels. */
.anchor-grid .ep-card { box-shadow: var(--ep-shadow-flat); transition: transform .22s var(--ep-ease), box-shadow .22s var(--ep-ease); }
.anchor-grid .ep-card:hover { transform: translateY(-1px); box-shadow: var(--ep-shadow-flat); }

/* Widget-type chips take the colour of the group they belong to. */
.widget-group:nth-of-type(1) dd span { background: var(--ep-blue-soft); }
.widget-group:nth-of-type(2) dd span { background: var(--ep-green-soft); }
.widget-group:nth-of-type(3) dd span { background: var(--ep-yellow-soft); }
.widget-group:nth-of-type(4) dd span { background: var(--ep-purple-soft); }
.widget-group dd span { border-color: var(--ep-line); transition: transform .16s var(--ep-ease); }
.widget-group dd span:hover { transform: translateY(-2px); }

/* Screenshots sit on a tinted plate so they stop floating on bare paper. */
.canvas-figure .ep-shot,
.workspace-pair .ep-shot,
.theme-gallery .ep-shot { background: var(--ep-surface); }
/* Section tints are applied as an opaque wash on a backdrop layer rather than
   a translucent section background: stacking translucency over the grid made
   the muted text measure as low as 1.17:1 against what actually rendered. */
.workspaces-section, .theme-section, .devices-section, .library-section { position: relative; }
.workspaces-section::after, .theme-section::after, .devices-section::after, .library-section::after {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
}
.workspaces-section::after { background: color-mix(in oklab, var(--ep-blue-soft) 30%, var(--ep-paper)); }
.theme-section::after { background: color-mix(in oklab, var(--ep-purple-soft) 26%, var(--ep-paper)); }
.devices-section::after { background: color-mix(in oklab, var(--ep-green-soft) 26%, var(--ep-paper)); }
.library-section::after { background: color-mix(in oklab, var(--ep-yellow-soft) 24%, var(--ep-paper)); }

/* Collaboration cards get their own tints inside the ink band. */
.collab-grid li { transition: transform .22s var(--ep-ease), border-color .22s var(--ep-ease); }
.collab-grid li:hover { transform: translateY(-3px); border-color: var(--ep-accent); }
.collab-grid li:nth-child(1) { background: color-mix(in oklab, var(--ep-blue) 16%, transparent); }
.collab-grid li:nth-child(2) { background: color-mix(in oklab, var(--ep-green) 16%, transparent); }
.collab-grid li:nth-child(3) { background: color-mix(in oklab, var(--ep-yellow) 14%, transparent); }
.collab-grid li:nth-child(4) { background: color-mix(in oklab, var(--ep-purple) 18%, transparent); }

/* Plan cards: the recommended one keeps the accent, the others take a tint. */
.plan-mini { box-shadow: var(--ep-shadow-flat); transition: transform .22s var(--ep-ease), box-shadow .22s var(--ep-ease); }
.plan-mini:hover { transform: translateY(-1px); box-shadow: var(--ep-shadow-flat); }
.plan-mini:nth-child(1) { background: var(--ep-blue-soft); }
.plan-mini:nth-child(3) { background: var(--ep-green-soft); }

/* Theme-list markers cycle the palette rather than repeating one accent. */
.theme-list li:nth-child(1)::before { background: var(--ep-accent); }
.theme-list li:nth-child(2)::before { background: var(--ep-blue); }
.theme-list li:nth-child(3)::before { background: var(--ep-green); }
.theme-list li:nth-child(4)::before { background: var(--ep-purple); }

/* Library cards lift like the hero widgets do. */
.library-card { box-shadow: var(--ep-shadow-flat); }
.library-card:nth-child(odd) { background: var(--ep-surface); }
.library-card:nth-child(even) { background: color-mix(in oklab, var(--ep-surface-2) 60%, var(--ep-surface)); }

@media (prefers-reduced-motion: reduce) {
  .anchor-grid .ep-card, .plan-mini, .collab-grid li, .widget-group dd span { transition: none; }
  .anchor-grid .ep-card:hover, .plan-mini:hover, .collab-grid li:hover, .widget-group dd span:hover { transform: none; }
}


/*
 * Scroll story.
 *
 * The track supplies scroll length; the stage pins inside it. Every animated
 * value arrives as a custom property written by story.mjs, so the browser only
 * ever composites transforms and opacity. Nothing here traps scrolling: the
 * stage is a plain sticky element and the page scrolls normally past it.
 */

.story {
  position: relative;
  /* Phase count drives the length. Roughly one viewport per phase. */
  --story-phases: 4.5;
  --story-progress: 0;
}
.story-track { height: calc(var(--story-phases) * 100vh); }
/* The board is the subject, so it takes the centre and the beats sit beside
   it as a narrow rail rather than an equal column. */
.story-stage {
  position: sticky;
  top: 0;
  height: 100vh;
  display: grid;
  place-items: center;
  width: min(1180px, calc(100% - (var(--ep-gutter) * 2)));
  margin-inline: auto;
  padding-block: clamp(2rem, 6vh, 4rem);
}

/* ---------------------------------------------------------------- stage */

/* Square stage so the 3x3 board reads as a board. */
.story-visual { position: relative; z-index: 2; width: min(100%, 78vh); aspect-ratio: 1; max-height: 74vh; }

/* The board is the coordinate space every widget positions itself inside. */
.story-board {
  position: absolute;
  inset: 0;
  --cols: 3;
  --rows: 3;
}

.story-widget {
  position: absolute;
  left: 0;
  top: 0;
  width: calc(100% / var(--cols) - 2.2%);
  height: calc(100% / var(--rows) - 3.4%);
  display: grid;
  align-content: space-between;
  gap: .4rem;
  padding: .7rem .8rem;
  border: 1px solid var(--ep-line);
  border-radius: var(--ep-widget-radius);
  background: var(--ep-surface);
  box-shadow: var(--story-shadow, var(--ep-shadow-flat));
  /*
   * story.mjs writes --tx/--ty as pixels measured against the board. A
   * percentage here would resolve against the widget's own box instead, which
   * silently scales every board coordinate by the widget's width.
   */
  transform:
    translate3d(calc(var(--tx, 0) * 1px), calc(var(--ty, 0) * 1px), 0)
    rotate(calc(var(--rot, 0) * 1deg))
    scale(var(--scale, 1));
  transform-origin: 50% 50%;
  will-change: transform;
}
.story-widget--yellow { background: var(--ep-yellow-soft); }
.story-widget--blue { background: var(--ep-blue-soft); }
.story-widget--green { background: var(--ep-green-soft); }
.story-widget--purple { background: var(--ep-purple-soft); }
.story-widget--paper { background: var(--ep-surface); }

.story-widget-glyph { font-size: 1.1rem; line-height: 1; opacity: .75; }
.story-widget-label {
  font-family: var(--ep-font-mono);
  font-size: .62rem;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  /* Darker than --ep-muted: these sit on tinted widgets, where the muted
     token only reaches about 4.3:1. */
  color: color-mix(in srgb, var(--ep-ink) 76%, transparent);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ------------------------------------------------------- customisation */

.story-panel {
  position: absolute;
  right: 0;
  top: 50%;
  z-index: 5;
  width: min(230px, 40%);
  display: grid;
  gap: .9rem;
  padding: 1rem;
  border: 1px solid var(--ep-line);
  border-radius: var(--ep-radius-md);
  background: var(--ep-surface);
  box-shadow: var(--ep-shadow-flat);
  transform: translate3d(calc(var(--panel-x, 120) * 1%), -50%, 0);
  opacity: var(--panel-opacity, 0);
  transition: opacity .3s var(--ep-ease);
  will-change: transform, opacity;
}
.story-panel-title {
  margin: 0;
  font-family: var(--ep-font-mono);
  font-size: .62rem;
  font-weight: 800;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ep-muted);
}
.story-slider { display: grid; gap: .45rem; font-size: .72rem; font-weight: 700; }
.story-slider-track {
  position: relative;
  display: block;
  height: 10px;
  border: 1px solid var(--ep-line);
  border-radius: 999px;
  background: var(--ep-surface-2);
}
.story-slider-fill {
  position: absolute;
  inset: 0 auto 0 0;
  width: calc(var(--value, 0) * 100%);
  border-radius: 999px;
  background: var(--ep-accent);
}
.story-slider-knob {
  position: absolute;
  top: 50%;
  left: calc(var(--value, 0) * 100%);
  width: 16px;
  height: 16px;
  border: 1px solid var(--ep-line);
  border-radius: 50%;
  background: var(--ep-surface);
  box-shadow: var(--ep-shadow-flat);
  transform: translate(-50%, -50%) scale(var(--knob-scale, 1));
}

/* -------------------------------------------------------------- cursor */

/* Deliberately unlike a real pointer: solid black, outlined, and it carries a
   press state so clicks read as intent rather than teleporting. */
.story-cursor {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 6;
  color: var(--ep-ink);
  opacity: var(--cursor-opacity, 0);
  /* Pixels, for the same reason the widgets use pixels: a percentage would
     resolve against the cursor glyph rather than the board. */
  transform: translate3d(calc(var(--cx, 0) * 1px), calc(var(--cy, 0) * 1px), 0) scale(var(--cursor-scale, 1));
  transition: opacity .25s var(--ep-ease);
  will-change: transform, opacity;
  filter: drop-shadow(2px 3px 0 color-mix(in srgb, var(--ep-ink) 22%, transparent));
}

/* ------------------------------------------------------- theme library */

.library-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: var(--ep-gap-md); margin: 0; padding: 0; list-style: none; }
.library-card {
  display: grid;
  gap: .8rem;
  align-content: start;
  padding: .8rem;
  border: var(--ep-border-width) solid var(--ep-line);
  border-radius: var(--ep-radius-md);
  background: var(--ep-surface);
  box-shadow: var(--ep-shadow-flat);
  transition: transform .22s var(--ep-ease), box-shadow .22s var(--ep-ease);
}
.library-card:hover { transform: translateY(-1px); box-shadow: var(--ep-shadow-flat); }
.library-shot { display: block; overflow: hidden; border: 1px solid var(--ep-line); border-radius: calc(var(--ep-radius-md) - 6px); line-height: 0; }
.library-shot img { width: 100%; height: auto; display: block; }
.library-meta { display: grid; gap: .3rem; }
.library-meta .ep-body { color: var(--ep-muted); font-size: var(--ep-step-small); }
.library-open { justify-self: start; }

/* ------------------------------------------------------- button juice */

/*
 * Press compression. The shadow shortens as the button sinks so the movement
 * reads as physical rather than as a colour change, and the travel is small
 * enough that the hit area never slides out from under a pointer mid-click.
 * Particle attraction toward the button is handled in shapes.mjs.
 */
[data-shape-hover] { transition: transform .16s var(--ep-ease), box-shadow .16s var(--ep-ease); }
[data-shape-hover]:hover { transform: translateY(-1px); }
[data-shape-hover]:active { transform: translateY(-1px) scale(.985); box-shadow: var(--ep-shadow-flat); }

@media (prefers-reduced-motion: reduce) {
  [data-shape-hover] { transition: none; }
  [data-shape-hover]:hover,
  [data-shape-hover]:active { transform: none; }
}

/* -------------------------------------------------------------- narrow */

@media (max-width: 940px) {
  /* Not a shrunk desktop: the stage stacks under the copy and loses a row of
     widgets so the board stays legible at this size. */
  .story-stage { padding-block: clamp(1.5rem, 4vh, 3rem); }
  .story-panel { width: min(190px, 52%); padding: .8rem; }
}

@media (max-width: 620px) {
  .story-stage { padding-block: 1.5rem; }
  .story-widget { padding: .5rem .55rem; }
  .story-widget-glyph { font-size: .9rem; }
  .story-widget-label { font-size: .55rem; }
}

/* ------------------------------------------------------ reduced motion */

/*
 * The still version is designed, not merely frozen: the board is shown
 * organised, the panel is open with both sliders part-way, and every beat of
 * the story is legible at once instead of one at a time.
 */
@media (prefers-reduced-motion: reduce) {
  .story-track { height: auto; }
  .story-stage { position: static; height: auto; overflow: visible; }
  .story-visual { width: min(100%, 60vh); }
  .story-widget { transition: none; }
  .story-cursor { display: none; }
  .story-panel { --panel-x: 0; --panel-opacity: 1; position: static; width: auto; margin-top: 1.25rem; transform: none; }
  .library-card:hover { transform: none; }
}


/*
 * Hero widget cluster.
 *
 * A staged board built from markup rather than a screenshot, so it themes with
 * the page, stays crisp at any size, and can animate in card by card. Cards
 * share one shell (.hw) and differ only in their contents.
 */

.hero { padding-block: clamp(2.5rem, 5vw, 5rem) var(--ep-section-y); }
.hero-grid { align-items: center; }

/* --------------------------------------------------------- the copy card */

/* The offset dotted outline is the mockup's signature: a second border sitting
   behind the card rather than a blurred shadow. */
.hero-copy {
  position: relative;
  padding: clamp(1.6rem, 1rem + 2.2vw, 3rem);
  border: var(--ep-border-width) solid var(--ep-line);
  border-radius: var(--ep-radius-lg);
  background: var(--ep-surface);
}
.hero-copy::before {
  content: "";
  position: absolute;
  inset: 10px -12px -14px 10px;
  z-index: -1;
  border: 2px dotted color-mix(in srgb, var(--ep-ink) 45%, transparent);
  border-radius: var(--ep-radius-lg);
  pointer-events: none;
}
.hero-copy .ep-lede { color: var(--ep-muted); }

.hero-note {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  max-width: none;
  padding: .8rem 1.1rem;
  border: var(--ep-border-width) solid var(--ep-line);
  border-radius: 999px;
  background: var(--ep-surface);
  color: var(--ep-ink);
  font-size: .92rem;
  font-weight: 750;
}
.hero-note b { display: inline; font-weight: 750; }
.hero-note::before { content: "✦"; color: var(--ep-accent); font-size: 1rem; }

/* -------------------------------------------------------- the product shot */

.hero-shot { margin: 0; }
/* The drift lives on the image so the figure keeps its own entrance
   animation; two animations on one element would replace each other. */
.hero-shot img {
  display: block;
  width: 100%;
  height: auto;
  animation: hero-shot-float 7s ease-in-out 1.2s infinite;
}

/* A drift small enough to read as the shot breathing rather than moving. */
@keyframes hero-shot-float {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-10px); }
}

/* ----------------------------------------------------------- the marquee */

/*
 * One track holding the run twice, sliding by half its own width. At the end
 * of the cycle the second copy sits exactly where the first started, so the
 * jump back to zero is invisible and the strip reads as endless.
 */
.hero-marquee {
  margin-top: var(--ep-gap-lg);
  overflow: hidden;
  /* The cards fade out at both edges instead of being cut by a hard line. */
  mask-image: linear-gradient(90deg, transparent, #000 5rem, #000 calc(100% - 5rem), transparent);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 5rem, #000 calc(100% - 5rem), transparent);
}
.hero-marquee-track {
  display: flex;
  width: max-content;
  animation: hero-marquee-slide 46s linear infinite;
}
.hero-marquee:hover .hero-marquee-track { animation-play-state: paused; }
@keyframes hero-marquee-slide {
  from { transform: translate3d(0, 0, 0); }
  to { transform: translate3d(-50%, 0, 0); }
}

.hero-marquee-run {
  display: flex;
  gap: clamp(.6rem, 1vw, 1rem);
  align-items: stretch;
  /* Half the track's gap, so the seam between the two runs is spaced like
     every other join in the row. */
  padding-right: clamp(.6rem, 1vw, 1rem);
}
.hero-marquee-stack {
  display: grid;
  gap: clamp(.6rem, 1vw, 1rem);
  align-content: stretch;
  grid-template-rows: 1fr 1fr;
}

/* The cards were grid items sized by their column; in a row they carry their
   own width, set to what each one's contents need. */
.hero-marquee .hw { flex: 0 0 auto; }
.hero-marquee .hw--today { width: 15rem; }
.hero-marquee .hw--board { width: 24rem; }
.hero-marquee .hw--focus { width: 14rem; }
.hero-marquee .hw--files { width: 16rem; }
.hero-marquee-stack .hw { width: 15rem; }

/* Shared card shell. */
.hw {
  display: grid;
  gap: .6rem;
  align-content: start;
  /* Grid items default to min-width:auto, which refuses to shrink below their
     content and pushes it out of the card. */
  min-width: 0;
  padding: .8rem .85rem;
  border: var(--ep-border-width) solid var(--ep-line);
  border-radius: var(--ep-radius-md);
  background: var(--ep-surface);
  box-shadow: var(--ep-shadow-flat);
  /* Cards lift toward the pointer, staggered by depth. */
  transition: transform .28s var(--ep-ease), box-shadow .28s var(--ep-ease);
}
.hw:hover { transform: translateY(-1px); box-shadow: var(--ep-shadow-flat); z-index: 2; }

.hw-head { display: flex; align-items: center; gap: .45rem; min-width: 0; font-size: .78rem; }
.hw-head b { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-family: var(--ep-font-mono); font-weight: 800; letter-spacing: .1em; text-transform: uppercase; }
.hw-icon { flex: 0 0 auto; opacity: .85; }
.hw-chev { margin-left: -.2rem; opacity: .6; }
.hw-dots { margin-left: auto; display: inline-flex; gap: 2px; }
.hw-dots i { width: 3px; height: 3px; border-radius: 50%; background: color-mix(in srgb, var(--ep-ink) 45%, transparent); }

/* Today ------------------------------------------------------------------ */

.hw--today { --tint: var(--ep-surface); }
.hw-cal { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px 0; text-align: center; }
.hw-cal-day { font-size: .6rem; font-weight: 800; color: var(--ep-muted); }
.hw-cal-date { padding: .14rem 0; font-size: .64rem; font-weight: 650; border-radius: 999px; }
.hw-cal-date.is-muted { opacity: .34; }
.hw-cal-date.is-today { background: var(--ep-accent); color: var(--ep-accent-contrast); font-weight: 850; }
.hw-event {
  display: flex; align-items: center; gap: .4rem; margin: 0;
  padding: .38rem .5rem; border-radius: calc(var(--ep-radius-sm) - 2px);
  background: var(--ep-accent-soft); font-size: .66rem; font-weight: 700;
}
.hw-event span { margin-left: auto; color: var(--ep-muted); }
.hw-pip { width: 6px; height: 6px; border-radius: 50%; background: var(--ep-accent); flex: 0 0 auto; }
.hw-pip--live { background: var(--ep-green); }

/* Focus ------------------------------------------------------------------ */

.hw--focus { background: var(--ep-green-soft); justify-items: center; }
.hw--focus .hw-head { width: 100%; }
.hw-ring { position: relative; display: grid; place-items: center; color: var(--ep-green); }
.hw-ring svg { display: block; width: clamp(64px, 7vw, 92px); height: auto; }
.hw-ring-arc { transition: stroke-dashoffset .6s var(--ep-ease); }
.hw:hover .hw-ring-arc { stroke-dashoffset: 30; }
.hw-ring-value { position: absolute; display: grid; justify-items: center; color: var(--ep-ink); line-height: 1; }
.hw-ring-value b { font-family: var(--ep-font-display); font-size: 1.5rem; font-weight: 900; }
.hw-ring-value small { font-size: .62rem; color: var(--ep-muted); }
.hw-pause { display: inline-flex; gap: 3px; padding: .3rem .55rem; border: 1px solid var(--ep-line); border-radius: 999px; background: var(--ep-surface); }
.hw-pause i { width: 3px; height: 11px; border-radius: 1px; background: var(--ep-ink); }

/* Files ------------------------------------------------------------------ */

.hw-file { display: flex; align-items: center; gap: .5rem; padding: .4rem .45rem; border: 1px solid var(--ep-line); border-radius: calc(var(--ep-radius-sm) - 2px); background: var(--ep-surface); }
.hw-file span { display: grid; min-width: 0; }
.hw-file b { font-size: .72rem; font-weight: 800; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.hw-file small { font-size: .6rem; color: var(--ep-muted); }
.hw-folder { flex: 0 0 auto; width: 22px; height: 17px; border: 1px solid var(--ep-line); border-radius: 3px 5px 5px 5px; position: relative; }
.hw-folder::before { content: ""; position: absolute; top: -6px; left: -2px; width: 10px; height: 6px; border: 1px solid var(--ep-line); border-bottom: 0; border-radius: 3px 3px 0 0; background: inherit; }
.hw-folder--blue { background: var(--ep-blue); }
.hw-folder--yellow { background: var(--ep-yellow); }

/* Brand refresh ---------------------------------------------------------- */

.hw-minis { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: .5rem; align-items: stretch; }
.hw-mini {
  position: relative; display: grid; gap: .4rem; align-content: start;
  padding: .5rem; border: 1px solid var(--ep-line);
  border-radius: calc(var(--ep-radius-sm) - 2px);
  min-height: clamp(88px, 11vw, 132px);
}
.hw-mini small { font-size: .6rem; font-weight: 750; }
.hw-mini--blue { background: var(--ep-blue-soft); }
.hw-mini--yellow { background: var(--ep-yellow-soft); }
.hw-mini--purple { background: var(--ep-purple-soft); }
.hw-mini--add {
  display: grid; place-items: center; border-style: dashed;
  background: transparent; color: color-mix(in srgb, var(--ep-ink) 45%, transparent);
  font-size: 1.3rem; font-weight: 800;
  transition: background-color .2s var(--ep-ease), color .2s var(--ep-ease);
}
.hw--board:hover .hw-mini--add { background: color-mix(in srgb, var(--ep-accent) 12%, transparent); color: var(--ep-accent-deep); }
.hw-thumb { display: block; height: 34px; border: 1px solid var(--ep-line); border-radius: 4px; background: var(--ep-surface); }
.hw-aa { display: grid; place-items: center; height: 34px; font-family: var(--ep-font-display); font-size: 1.25rem; font-weight: 900; font-style: normal; }
.hw-swatches { display: flex; gap: 3px; align-items: center; height: 34px; }
.hw-swatches b { width: 13px; height: 13px; border: 1px solid var(--ep-line); border-radius: 50%; }
.hw-swatches b:nth-child(1) { background: var(--ep-accent); }
.hw-swatches b:nth-child(2) { background: var(--ep-green); }
.hw-swatches b:nth-child(3) { background: var(--ep-blue); }
.hw-mini .hw-avatar { position: absolute; left: .45rem; bottom: .45rem; }

/* Shared ----------------------------------------------------------------- */

.hw-avatars { display: flex; }
.hw-avatars .hw-avatar { margin-left: -8px; }
.hw-avatars .hw-avatar:first-child { margin-left: 0; }
.hw-avatar {
  display: grid; place-items: center; width: 26px; height: 26px; flex: 0 0 auto;
  border: 1px solid var(--ep-line); border-radius: 50%; overflow: hidden;
  background: var(--ep-surface);
}
.hw-avatar svg { width: 100%; height: 100%; }
.hw-avatar--peach { color: #e8a07a; }
.hw-avatar--clay { color: #b8705a; }
.hw-avatar--lilac { color: #a48bd4; }
.hw-avatar--sand { color: #d8b06a; }
.hw-live { display: flex; align-items: center; gap: .4rem; margin: 0; font-size: .66rem; font-weight: 700; color: var(--ep-muted); }

/* Themes ----------------------------------------------------------------- */

/*
 * The swatch row wraps rather than overflowing. Four fixed-width swatches plus
 * the nowrap "View all" label need roughly 195px, so on a narrow viewport they
 * used to spill past the card edge; letting the row wrap drops the label onto
 * its own line instead, where its auto margin still keeps it right-aligned.
 */
.hw-themes { display: flex; flex-wrap: wrap; align-items: center; gap: .35rem; min-width: 0; }
.hw-theme {
  position: relative; display: flex; width: 30px; height: 24px; flex: 0 1 auto; min-width: 0;
  border: 1px solid var(--ep-line); border-radius: 5px; overflow: hidden;
  background: var(--a);
  transition: transform .2s var(--ep-ease);
}
.hw-theme i { flex: 1; background: var(--a); }
.hw-theme b { width: 10px; flex: 0 0 auto; background: var(--b); }
.hw-theme:first-child { outline: 2px solid var(--ep-accent); outline-offset: 2px; }
.hw--themes:hover .hw-theme { transform: translateY(calc(var(--i) * -1px - 1px)); }
.hw-viewall { margin-left: auto; font-size: .62rem; font-weight: 750; color: var(--ep-muted); white-space: nowrap; }

/* ------------------------------------------------------- feature strip */

.hero-features {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--ep-gap-md);
  margin: var(--ep-gap-lg) auto 0;
  padding: 0;
  list-style: none;
}
.hero-feature {
  display: flex; gap: .9rem; align-items: flex-start;
  padding: 1.1rem 1.2rem;
  border: var(--ep-border-width) solid var(--ep-line);
  border-radius: var(--ep-radius-md);
  background: var(--ep-surface);
  transition: transform .22s var(--ep-ease), box-shadow .22s var(--ep-ease);
}
.hero-feature:hover { transform: translateY(-1px); box-shadow: var(--ep-shadow-flat); }
.hero-feature .ep-body { margin-top: .2rem; color: var(--ep-muted); font-size: var(--ep-step-small); }
.hero-feature-icon {
  flex: 0 0 auto; display: grid; place-items: center;
  width: 38px; height: 38px;
  border: 1px solid var(--ep-line); border-radius: 10px;
}
.hero-feature--blue .hero-feature-icon { background: var(--ep-blue-soft); color: #2f6ea8; }
.hero-feature--green .hero-feature-icon { background: var(--ep-green-soft); color: #4f7a1e; }
.hero-feature--purple .hero-feature-icon { background: var(--ep-purple-soft); color: #6b4a9e; }

/* ------------------------------------------------------------ appearance */

/* Cards arrive one after another with a small overshoot, rather than all
   fading together. --d is the card's place in the order. */
@keyframes hw-in {
  0% { opacity: 0; transform: translateY(16px) scale(.96); }
  62% { opacity: 1; transform: translateY(-3px) scale(1.012); }
  100% { opacity: 1; transform: none; }
}
.js .hero-shot { opacity: 0; }
.js .hero-shot.is-visible { animation: hw-in .7s var(--ep-ease) both; }
.js .hero-features .hero-feature { opacity: 0; }
.js .hero-features.is-visible .hero-feature { animation: hw-in .6s var(--ep-ease) both; }
.js .hero-features.is-visible .hero-feature:nth-child(2) { animation-delay: 90ms; }
.js .hero-features.is-visible .hero-feature:nth-child(3) { animation-delay: 180ms; }

/* ---------------------------------------------------------------- narrow */

@media (max-width: 940px) {
  .hero-features { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 560px) {
  /* The cards shrink with the strip rather than staying at desktop width,
     which would leave barely one card on screen at a time. */
  .hero-marquee .hw--today,
  .hero-marquee .hw--focus,
  .hero-marquee-stack .hw { width: 12rem; }
  .hero-marquee .hw--files { width: 13rem; }
  .hero-marquee .hw--board { width: 18rem; }
  .hw-minis { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (prefers-reduced-motion: reduce) {
  .js .hero-shot,
  .js .hero-features .hero-feature { opacity: 1; animation: none; }
  .hero-shot img { animation: none; }
  /* The strip stops rather than crawling, and shows the run it starts on. */
  .hero-marquee-track { animation: none; }
  .hw, .hero-feature, .hw-theme, .hw-ring-arc { transition: none; }
  .hw:hover, .hero-feature:hover { transform: none; }
}

/* ------------------------------------------------- hero and closing fixes */

/*
 * The copy sits directly on the page. The card and its offset dotted outline
 * were competing with the widget cluster beside it, and the panel edge was
 * clipping the headline.
 */
.hero-copy {
  padding: 0;
  border: 0;
  background: transparent;
}
.hero-copy::before { content: none; }

/*
 * The headline is sized against its own column rather than the viewport. At
 * the shared hero step, "everywhere" was wider than the column and ran past
 * the edge of the section.
 */
.hero .ep-hero-title,
.home-page .hero .hero-copy h1 {
  font-size: clamp(2.4rem, 1rem + 4vw, 4.9rem);
  overflow-wrap: break-word;
}

/* The note sits beside the button, not as a second bordered control. */
.hero-note {
  padding: 0;
  border: 0;
  background: transparent;
  font-size: var(--ep-step-small);
  font-weight: 650;
  color: var(--ep-muted);
}
.hero-note b { color: var(--ep-ink); font-weight: 800; }

/* ---------------------------------------------------------- closing block */

/*
 * A centred column: heading, one line of copy, the button, then the quiet
 * sign-in link. The legacy `.closing-section > div` flex rule out-specifies
 * the class on its own, so this is scoped to beat it.
 */
.home-page .closing-section .closing-inner {
  display: grid;
  justify-items: center;
  justify-content: center;
  gap: 1.5rem;
  text-align: center;
}
.home-page .closing-section .closing-inner .ep-lede {
  max-width: 40ch;
  margin-inline: auto;
}
.home-page .closing-section .closing-inner .ep-button {
  margin-top: .25rem;
}
.home-page .closing-section .closing-signin {
  margin: 0;
  color: var(--ep-muted);
  font-size: var(--ep-step-small);
}
.home-page .closing-section .closing-signin a {
  font-weight: 800;
  text-underline-offset: 3px;
}


/* ---------------------------------------------------------- support hero */

.support-hero-inner {
  display: grid;
  justify-items: center;
  gap: 1.6rem;
  text-align: center;
}
.support-hero-inner .ep-lede { max-width: 46ch; margin-inline: auto; }

.support-search {
  display: grid;
  width: min(620px, 100%);
  gap: .6rem;
}
.support-search-field {
  position: relative;
  display: flex;
  align-items: center;
}
.support-search-field svg,
.support-search-glyph {
  position: absolute;
  left: 1.15rem;
  width: 20px;
  height: 20px;
  opacity: .5;
  pointer-events: none;
}
.support-search input {
  width: 100%;
  box-sizing: border-box;
  border: var(--ep-border-width) solid var(--ep-ink);
  border-radius: var(--ep-radius-lg);
  padding: 1.15rem 3.4rem;
  color: var(--ep-ink);
  background: var(--ep-paper);
  box-shadow: 4px 4px 0 var(--ep-ink);
  font: inherit;
  font-size: 1.05rem;
  transition: box-shadow .22s var(--ep-ease), transform .22s var(--ep-ease);
}
.support-search input::placeholder { color: color-mix(in srgb, var(--ep-muted) 82%, transparent); }
.support-search input:hover { transform: translateY(-1px); box-shadow: 6px 6px 0 var(--ep-ink); }
.support-search input:focus-visible {
  outline: none;
  transform: translateY(-1px);
  box-shadow: 7px 7px 0 var(--ep-accent);
}
.support-search-clear {
  position: absolute;
  right: .85rem;
  display: grid;
  width: 30px;
  height: 30px;
  place-items: center;
  border: 0;
  border-radius: 50%;
  color: var(--ep-muted);
  background: transparent;
  cursor: pointer;
  font-size: 1.1rem;
  line-height: 1;
  transition: background-color .2s ease, color .2s ease;
}
.support-search-clear:hover { color: var(--ep-ink); background: var(--ep-surface-2); }
.support-search-clear[hidden] { display: none; }
.support-search-status {
  min-height: 1.2em;
  margin: 0;
  color: var(--ep-muted);
  font-size: var(--ep-step-small);
}

/* ----------------------------------------------------------- faq listing */

.support-faq-list {
  display: grid;
  gap: .7rem;
  margin: 0;
  padding: 0;
  list-style: none;
}
.support-faq-item {
  border: var(--ep-border-width) solid var(--ep-ink);
  border-radius: var(--ep-radius-md);
  background: var(--ep-surface);
  box-shadow: 3px 3px 0 color-mix(in srgb, var(--ep-ink) 22%, transparent);
  transition: transform .24s var(--ep-ease), box-shadow .24s var(--ep-ease), background-color .24s ease;
}
.support-faq-item:hover {
  transform: translateY(-1px);
  background: var(--ep-surface-2);
  box-shadow: 6px 6px 0 color-mix(in srgb, var(--ep-ink) 30%, transparent);
}
.support-faq-item[open] {
  background: var(--ep-surface-2);
  box-shadow: 5px 5px 0 var(--ep-accent-soft);
}
.support-faq-item summary {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 1rem;
  padding: 1.05rem 1.25rem;
  cursor: pointer;
  font-weight: 800;
  list-style: none;
}
.support-faq-item summary::-webkit-details-marker { display: none; }
.support-faq-item summary:focus-visible { outline: 3px solid var(--ep-accent); outline-offset: 3px; border-radius: var(--ep-radius-md); }
.support-faq-mark {
  display: grid;
  width: 26px;
  height: 26px;
  flex: none;
  place-items: center;
  border-radius: 50%;
  background: var(--ep-accent-soft);
  transition: transform .26s var(--ep-ease), background-color .2s ease;
  font-size: .95rem;
  line-height: 1;
}
.support-faq-item[open] .support-faq-mark { transform: rotate(45deg); background: var(--ep-accent); }
.support-faq-body {
  display: grid;
  gap: .75rem;
  padding: 0 1.25rem 1.15rem;
  color: var(--ep-muted);
  animation: support-faq-open .28s var(--ep-ease) both;
}
.support-faq-body p { margin: 0; max-width: 62ch; }
@keyframes support-faq-open {
  from { opacity: 0; transform: translateY(-5px); }
  to { opacity: 1; transform: none; }
}
.support-faq-more {
  justify-self: start;
  border-bottom: 1px solid var(--ep-accent);
  color: var(--ep-ink);
  font-size: var(--ep-step-small);
  font-weight: 800;
  text-decoration: none;
}
.support-faq-more span { display: inline-block; transition: transform .24s var(--ep-ease); }
.support-faq-more:hover span { transform: translateX(4px); }
.support-faq-item.is-hidden { display: none; }
.support-faq-empty {
  display: none;
  border: var(--ep-border-width) dashed color-mix(in srgb, var(--ep-ink) 45%, transparent);
  border-radius: var(--ep-radius-md);
  padding: 1.6rem;
  color: var(--ep-muted);
  text-align: center;
}
.support-faq-empty p { margin: 0 0 .5rem; }
.support-faq-list.is-empty + .support-faq-empty { display: block; }

/* ------------------------------------------------------------ guide grid */

.support-guide-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: var(--ep-gap-md);
}
.support-guide-card {
  display: grid;
  align-content: start;
  gap: .5rem;
  border: var(--ep-border-width) solid var(--ep-ink);
  border-radius: var(--ep-radius-md);
  padding: 1.35rem;
  color: inherit;
  background: var(--ep-surface);
  box-shadow: 4px 4px 0 var(--ep-ink);
  text-decoration: none;
  transition: transform .26s var(--ep-ease), box-shadow .26s var(--ep-ease), background-color .26s ease;
}
.support-guide-card:hover {
  transform: translateY(-1px);
  background: var(--ep-surface-2);
  box-shadow: 8px 8px 0 var(--ep-ink);
}
.support-guide-card h3 { margin: 0; font-size: 1.02rem; }
.support-guide-card p { margin: 0; color: var(--ep-muted); font-size: var(--ep-step-small); line-height: 1.5; }
.support-guide-glyph {
  display: grid;
  width: 34px;
  height: 34px;
  place-items: center;
  border-radius: var(--ep-radius-sm);
  background: var(--ep-accent-soft);
  font-size: 1.05rem;
  transition: transform .26s var(--ep-ease);
}
.support-guide-card:hover .support-guide-glyph { transform: rotate(-8deg) scale(1.08); }

/* --------------------------------------------------------- answer detail */

.support-answer { display: grid; gap: 1.15rem; }
.support-answer h2 { margin: 1.4rem 0 0; font-size: 1.12rem; }
.support-answer p { margin: 0; max-width: 66ch; color: var(--ep-muted); line-height: 1.65; }
.support-answer ol,
.support-answer ul { display: grid; gap: .55rem; max-width: 66ch; margin: 0; padding-left: 1.15rem; color: var(--ep-muted); }
.support-answer li::marker { color: var(--ep-accent); font-weight: 800; }
.support-answer-note {
  border-left: 1px solid var(--ep-accent);
  border-radius: 0 var(--ep-radius-sm) var(--ep-radius-sm) 0;
  padding: .85rem 1.1rem;
  background: var(--ep-surface-2);
}
.support-answer-note p { color: var(--ep-ink); }
.support-answer-nav {
  display: flex;
  flex-wrap: wrap;
  gap: .6rem;
  margin-top: 1.6rem;
}

/* ---------------------------------------------------------- contact form */

.support-contact-inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
  align-items: start;
  gap: var(--ep-gap-lg);
}
.support-contact-form { display: grid; gap: .85rem; }
.support-field { display: grid; gap: .35rem; }
.support-field span { font-size: var(--ep-step-small); font-weight: 800; }
.support-field input,
.support-field textarea {
  box-sizing: border-box;
  width: 100%;
  border: var(--ep-border-width) solid var(--ep-ink);
  border-radius: var(--ep-radius-sm);
  padding: .8rem .95rem;
  color: var(--ep-ink);
  background: var(--ep-paper);
  font: inherit;
  font-size: .95rem;
  transition: box-shadow .2s var(--ep-ease), transform .2s var(--ep-ease);
}
.support-field textarea { min-height: 148px; resize: vertical; }
.support-field input:focus-visible,
.support-field textarea:focus-visible {
  outline: none;
  transform: translateY(-1px);
  box-shadow: 4px 4px 0 var(--ep-accent);
}
.support-contact-actions { display: flex; flex-wrap: wrap; align-items: center; gap: .8rem; }
.support-contact-status { margin: 0; color: var(--ep-muted); font-size: var(--ep-step-small); }
.support-contact-status[data-state="error"] { color: #b3402c; }
.support-contact-aside { display: grid; align-content: start; gap: .9rem; }
.support-contact-aside p { margin: 0; max-width: 40ch; color: var(--ep-muted); line-height: 1.6; }
.support-contact-points { display: grid; gap: .6rem; margin: 0; padding: 0; list-style: none; }
.support-contact-points li {
  display: grid;
  grid-template-columns: 20px minmax(0, 1fr);
  gap: .6rem;
  color: var(--ep-muted);
  font-size: var(--ep-step-small);
  line-height: 1.5;
}
.support-contact-points b { color: var(--ep-ink); }

@media (max-width: 860px) {
  .support-contact-inner { grid-template-columns: 1fr; }
  .support-search input { padding: 1rem 3rem; font-size: 1rem; }
}

@media (prefers-reduced-motion: reduce) {
  .support-faq-item,
  .support-guide-card,
  .support-search input,
  .support-field input,
  .support-field textarea { transition: none; }
  .support-faq-body { animation: none; }
  .support-faq-item:hover,
  .support-guide-card:hover,
  .support-search input:hover { transform: none; }
}


/*
 * Micro-interactions.
 *
 * Loaded last so it applies across every page. Nothing here changes layout or
 * hit areas: every effect is a transform, a shadow, or a colour, so a control
 * never moves out from under a pointer mid-click. All of it is switched off
 * under prefers-reduced-motion at the end of the file.
 */

/* ------------------------------------------------------------ pointer tilt */

/*
 * site.mjs writes --tilt-x/--tilt-y (roughly -1..1) from the pointer position.
 * The rotation is small on purpose: enough to catch the eye when the pointer
 * crosses a screenshot, not enough to make the image hard to read.
 */
[data-tilt] {
  transform:
    perspective(900px)
    rotateX(calc(var(--tilt-y, 0) * -3.2deg))
    rotateY(calc(var(--tilt-x, 0) * 3.2deg))
    translateZ(0);
  transition: transform .34s var(--ep-ease), box-shadow .34s var(--ep-ease);
  will-change: transform;
}
[data-tilt]:hover { box-shadow: var(--ep-shadow-flat); }

/* --------------------------------------------------------------- wordmark */

.wordmark-mark { transition: transform .32s var(--ep-ease); }
.wordmark:hover .wordmark-mark { transform: rotate(-8deg) scale(1.07); }
.wordmark:active .wordmark-mark { transform: rotate(4deg) scale(.96); }

/* ------------------------------------------------------------------- nav */

.site-nav a { position: relative; }
.site-nav a::after {
  content: "";
  position: absolute;
  left: .8rem;
  right: .8rem;
  bottom: .28rem;
  height: 2px;
  border-radius: 2px;
  background: var(--ep-accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform .26s var(--ep-ease);
}
.site-nav a:hover::after,
.site-nav a:focus-visible::after { transform: scaleX(1); }

/* The header sign-in gets the same treatment rather than a browser underline. */
.header-sign-in { position: relative; text-decoration: none; }
.header-sign-in::after {
  content: "";
  position: absolute; left: .4rem; right: .4rem; bottom: .2rem; height: 2px;
  border-radius: 2px; background: currentColor;
  transform: scaleX(0); transform-origin: left;
  transition: transform .24s var(--ep-ease);
}
.header-sign-in:hover { text-decoration: none; }
.header-sign-in:hover::after,
.header-sign-in:focus-visible::after { transform: scaleX(1); }

/* ---------------------------------------------------------------- accents */

/* The marker behind a highlighted word grows when the heading is hovered. */
.ep-mark::after { transition: height .3s var(--ep-ease), transform .3s var(--ep-ease); transform-origin: left; }
h1:hover .ep-mark::after,
h2:hover .ep-mark::after { height: .52em; }

/* The eyebrow's ring fills on section hover, a small sign of life. */
.ep-eyebrow::before { transition: background-color .3s var(--ep-ease), transform .3s var(--ep-ease); }
section:hover > .ep-wrap .ep-eyebrow::before,
section:hover .section-head .ep-eyebrow::before { background: var(--ep-accent); transform: scale(1.15); }

/* --------------------------------------------------------------- content */

/* Widget chips pick up their group colour more strongly on hover. */
.widget-group dd span:hover,
.widget-catalogue dd span:hover { border-color: var(--ep-line); box-shadow: var(--ep-shadow-flat); }

/* Feature list rows nudge toward the reader. */
.feature-list li,
.theme-list li { transition: transform .2s var(--ep-ease); }
.feature-list li:hover { transform: translateX(4px); box-shadow: var(--ep-shadow-flat); }
.theme-list li:hover { transform: translateX(4px); }
.theme-list li::before { transition: transform .2s var(--ep-ease); }
.theme-list li:hover::before { transform: rotate(45deg) scale(1.15); }

/* Figures lift with their captions. */
.theme-gallery figure,
.workspace-pair figure { transition: transform .28s var(--ep-ease); }
.theme-gallery figure:hover,
.workspace-pair figure:hover { transform: translateY(-4px); }

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

.plan-faq details { transition: border-color .2s var(--ep-ease), box-shadow .2s var(--ep-ease); }
.plan-faq details:hover { border-color: var(--ep-accent); }
.plan-faq details[open] { box-shadow: var(--ep-shadow-flat); }
.plan-faq summary::after { display: inline-block; transition: transform .26s var(--ep-ease); }
.plan-faq details[open] summary::after { transform: rotate(180deg); }
.plan-faq details[open] > p { animation: ep-reveal-in .4s var(--ep-ease) both; }

/* Billing toggle knob reacts to the press. */
.plan-interval button { transition: background-color .2s var(--ep-ease), color .2s var(--ep-ease), transform .16s var(--ep-ease); }
.plan-interval button:active { transform: scale(.95); }

/* ----------------------------------------------------------------- footer */

.footer-links a,
.footer-privacy-choice { position: relative; transition: color .2s var(--ep-ease); }
.footer-links a:hover,
.footer-privacy-choice:hover { color: var(--ep-accent-deep); }

/* ------------------------------------------------------------- reduced */

@media (prefers-reduced-motion: reduce) {
  [data-tilt],
  .wordmark-mark,
  .feature-list li,
  .theme-list li,
  .theme-gallery figure,
  .workspace-pair figure,
  .plan-interval button,
  .plan-faq summary::after { transform: none !important; transition: none; animation: none; }
  .site-nav a::after,
  .header-sign-in::after { transition: none; }
  .plan-faq details[open] > p { animation: none; }
}


/*
 * The 2026 epoyo.com landing page.
 *
 * Layout and components for the landing page. The palette itself now lives in
 * the design system, where the whole site shares it: the visual-workspace
 * pages that would have clashed with it have been retired, so there is no
 * longer a second look to keep in step with.
 *
 * The direction: near-white, generous space, compact centred column, floating
 * cards with soft corners and shadows that suggest depth rather than announce
 * it. Colour is reserved for meaning. Blue is action, green says a number went
 * the right way, red says it did not, and nothing else earns a hue.
 *
 * The --l-* tokens repeat a few design-system values rather than referencing
 * them. That is deliberate for the two-weight colours below, which the shared
 * tokens do not model, and for the rest it keeps this file readable as one
 * page's design rather than a diff against another file.
 */

.landing {
  --l-ink: #1d1d1f;
  --l-ink-2: #424245;
  --l-muted: #6e6e73;
  --l-faint: #a1a1a6;
  --l-page: #ffffff;
  /* The same colour as channels, for the few places that need it at an alpha:
     a gradient cannot fade a hex to nothing without knowing its parts. */
  --l-page-rgb: 255, 255, 255;
  --l-raised: #ffffff;
  --l-sunken: #f5f5f7;
  --l-line: #e5e5ea;
  --l-line-strong: #d1d1d6;

  /*
   * iOS system colours, in two weights each.
   *
   * The bright ones are for strokes, dots and figures set large -- systemGreen
   * on white is about 2.2:1, which is fine for a chart line and unreadable as
   * body text. The deep ones carry anything small, where AA applies. Using one
   * value for both is the mistake that makes a palette look right in a mockup
   * and fail the moment real sentences appear in it.
   */
  --l-up: #00875a;
  --l-up-bright: #00e676;
  --l-up-soft: #d8fbe8;
  --l-down: #e0002f;
  --l-down-bright: #ff2d55;
  --l-down-soft: #ffe4ea;
  --l-note: #0057ff;
  --l-note-soft: #e2ecff;
  --l-warm: #c2410c;
  --l-warm-bright: #ff9f0a;
  --l-warm-soft: #fff0d6;

  --l-r-sm: 12px;
  --l-r-md: 18px;
  --l-r-lg: 28px;
  --l-r-pill: 999px;

  --l-shadow-card: 0 1px 2px rgba(11, 11, 12, 0.04), 0 12px 32px -12px rgba(11, 11, 12, 0.14);
  --l-shadow-float: 0 2px 6px rgba(11, 11, 12, 0.05), 0 32px 64px -24px rgba(11, 11, 12, 0.22);

  --l-gap: clamp(72px, 11vw, 152px);

  /*
   * The mark behind the hero, and the one dark band on the page.
   *
   * The green is the brand's, not the chart palette's: this figure is the logo
   * drawn as a trend line rather than a series, so it takes the logo's colour.
   * The dark values are the application's own ground and card, so the
   * analytics wall reads as a piece of the product rather than a mood.
   */
  --l-mark: #34c759;
  --l-dark: #111214;
  --l-dark-card: #1c1d20;
  --l-dark-line: #2a2b2f;

  background: var(--l-page);
  color: var(--l-ink);
  font-family: var(--ep-font-hero);
  -webkit-font-smoothing: antialiased;
}

/* The old page painted a grid and a canvas of drifting shapes behind
   everything. Both belong to the previous product; here they are noise. */
.landing .ep-grid-field,
.landing .shape-field { display: none; }

.landing main { display: block; }

.l-wrap {
  width: min(1080px, calc(100vw - 48px));
  margin-inline: auto;
}

.l-wrap--tight { width: min(760px, calc(100vw - 48px)); }

.l-section {
  padding-block: var(--l-gap);
}

/* The video sits directly under the hero and shares its breathing room. */
.l-section--flush { padding-top: 0; }

.l-eyebrow {
  display: block;
  margin: 0 0 14px;
  color: var(--l-muted);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.02em;
}

.l-h2 {
  margin: 0;
  font-size: clamp(30px, 4.4vw, 46px);
  font-weight: 680;
  line-height: 1.08;
  letter-spacing: -0.035em;
}

.l-lede {
  margin: 16px 0 0;
  max-width: 58ch;
  color: var(--l-ink-2);
  font-size: clamp(16px, 1.5vw, 18px);
  line-height: 1.55;
}

.l-center { text-align: center; }
.l-center .l-lede { margin-inline: auto; }

/* ------------------------------------------------------------------ hero */

.l-hero {
  position: relative;
  padding-block: clamp(76px, 9vw, 136px) clamp(104px, 13vw, 184px);
  text-align: center;
  overflow: clip;
}

.l-hero-inner { position: relative; z-index: 2; }

/*
 * The live line, behind the headline.
 *
 * It runs the width of the hero and rises across it, which means it passes
 * behind the words. That is the point -- the claim sits on the trend it is
 * claiming -- and it is also the risk, so the stroke is held at an opacity
 * where the arrow is unmistakable and the text over it is untouched. The
 * element takes no pointer events: it lies over the input, and a decoration
 * must never take a click meant for the product's front door.
 */
.l-heroline {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  /*
   * Faded in from the left.
   *
   * The line has no beginning: it is a window onto a series that scrolls, and
   * the left edge is simply where readings are old enough to leave. Ending it
   * on the side of the screen made that a cut -- a thirty-pixel stroke stopping
   * dead against the page. Fading it means the line arrives from somewhere
   * rather than starting nowhere.
   */
  mask-image: linear-gradient(to right, transparent 0, #000 16%);
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 16%);
}

.l-heroline svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
}

/*
 * ------------------------------------------------------------- the scene
 *
 * The product's story, told as motion behind the headline: short-form videos
 * fly in from the left, converge on a point, and leave to the right as sales.
 *
 * On a pointer device that point is the cursor. The shorts aim at it, the
 * analytics line bends through it, and each sale is emitted just to its right
 * before travelling off screen -- so moving the mouse is moving the place
 * where content becomes revenue, which is the entire pitch in one gesture.
 * With no pointer, the same machinery follows a slow idle drift, so the scene
 * is never still and never demands interaction to make sense.
 *
 * Placement comes from custom properties written by one rAF loop in
 * landing.mjs. The defaults below are a composed still, so the scene looks
 * deliberate before the script runs and if it never runs at all.
 */
.l-scene {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}

/*
 * The line, drawn the way the logo is.
 *
 * It was a two-pixel stroke with a dashed twin drifting along it and a filled
 * swell underneath -- a data visualisation. It is the brand mark now: one flat
 * green stroke, corners and ends rounded, with an arrowhead the loop builds at
 * its head. The dash and the swell are gone rather than restyled, because a
 * thick rounded stroke with a second dashed stroke inside it reads as a
 * mistake, and a fill under a flat line is a shape nobody asked for.
 */
.l-scene-flow { opacity: 0.42; }

/*
 * --boost is written by the loop each frame: 1 the instant the cursor crosses
 * the line, decaying to 0 over about a second. Reading it here keeps the
 * reaction in the stylesheet, where its look can be tuned without touching the
 * animation, and costs the loop a single property write.
 *
 * The head is styled with the line and never separately: they are one figure,
 * and a barb a different weight from the stroke it sits on is the tell that an
 * arrow was added to a chart rather than drawn as one thing.
 */
.l-scene-flow-line,
.l-scene-flow-head {
  fill: none;
  stroke: var(--l-mark);
  /* Three pixels of swell rather than five. The value easing into it is what
     made the reaction readable; it does not also need to be large. */
  stroke-width: calc(30px + var(--boost, 0) * 3px);
  stroke-linecap: round;
  stroke-linejoin: round;
}

/*
 * The head is filled, not stroked.
 *
 * The thin stroke over the fill is not an outline: it is the corner radius.
 * SVG has no way to round the corners of a polygon, and a triangle with three
 * needle points is sharper than anything in the mark -- so the shape is drawn
 * eight pixels wider in its own colour with round joins, which softens each
 * corner by four pixels and leaves the arrow otherwise flat.
 */
.l-scene-flow-head {
  fill: var(--l-mark);
  stroke: var(--l-mark);
  stroke-width: 8px;
  stroke-linejoin: round;
  stroke-linecap: round;
}

/*
 * Short-form videos. Phone-shaped cards carrying real frames of short-form
 * content, travelling toward whatever the loop names as the convergence
 * point and shrinking as they approach it.
 */
.l-short {
  position: absolute;
  top: 0;
  left: 0;
  /*
   * Half again as large as they were: 62x106 read as thumbnails of something
   * rather than as the content itself, and the frames inside them are the
   * whole reason the scene is there. The radius goes up with them, because a
   * 16px corner on a card this size reads as a sharper rectangle than the
   * same corner did on a smaller one.
   */
  width: 94px;
  height: 160px;
  border-radius: 22px;
  border: 1px solid rgba(255, 255, 255, 0.85);
  box-shadow: 0 10px 28px -10px rgba(29, 29, 31, 0.22);
  /* The still is a square-cornered bitmap; without this it overhangs the
     radius and the cards read as rectangles with a drawn-on outline. */
  overflow: hidden;
  /*
   * No backdrop blur here, and there never should have been one: every one of
   * these cards is filled with a fully opaque colour, so the blur had nothing
   * to show through it. What it did have was a cost -- four elements that move
   * every frame, each making the compositor snapshot and blur the page behind
   * a card you cannot see through.
   */
  transform:
    translate3d(var(--x, -80px), var(--y, 30%), 0)
    rotate(var(--r, -4deg))
    scale(var(--s, 1));
  opacity: var(--o, 0.9);
  will-change: transform, opacity;
}

/*
 * The still itself. `cover` rather than `contain` because the cards are not
 * quite 9:16 and the frames are: contain would letterbox each one against the
 * card's fallback colour, which is exactly the look this replaced.
 */
.l-short-media {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.l-short-play {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  /*
   * A pool of shade under the glyph.
   *
   * The triangle was white on a flat saturated fill, where it could not fail
   * to be seen. Over photographs it can land on anything -- a blown-out sky,
   * a white t-shirt -- so it brings its own contrast rather than depending on
   * whichever frame is underneath.
   */
  background: radial-gradient(circle at 50% 50%, rgba(29, 29, 31, 0.34) 0, rgba(29, 29, 31, 0.16) 38%, rgba(29, 29, 31, 0) 62%);
}

.l-short-play::before {
  content: "";
  width: 0;
  height: 0;
  border-style: solid;
  /* Grown with the cards; a 7px glyph on a 160px card reads as a speck. */
  border-width: 10px 0 10px 16px;
  border-color: transparent transparent transparent rgba(255, 255, 255, 0.95);
  /*
   * Heavier than the shadow this carried over a flat fill, and doing a
   * different job. The scrim above handles the average frame; this handles
   * the individual blown-out pixel -- a phone screen, a white shirt, a sky --
   * which the scrim lifts to a contrast of about 1.5 against a white glyph.
   * A shadow tight to the edge separates the two without dimming the picture
   * the way more scrim would.
   */
  filter: drop-shadow(0 1px 3px rgba(29, 29, 31, 0.55));
}

/*
 * What sits under the still.
 *
 * These were four saturated brand colours, back when the colour was the whole
 * card. Now it shows only in the instant before a frame decodes, so it is a
 * quiet grey instead: a card that flashed blue and then became a photograph
 * read as a loading fault, where this reads as the card arriving.
 */
.l-short { background: #e8e9ed; }
/* The two smaller cards keep their proportion to the base as it grew. */
.l-short--2 { width: 82px; height: 139px; }
.l-short--4 { width: 76px; height: 130px; }

/* Defaults for the no-script still: a staggered approach from the left. */
.l-short--1 { --x: 4%; --y: 24%; --s: .92; }
.l-short--2 { --x: 13%; --y: 46%; --s: .8; --o: .8; }
.l-short--3 { --x: 9%; --y: 64%; --s: .86; --o: .74; }
.l-short--4 { --x: 19%; --y: 34%; --s: .66; --o: .6; }

/*
 * Sales: glass receipts emitted beside the convergence point, travelling
 * right. Positioned by the loop; the still below shows one, at rest.
 */
.l-sale {
  position: absolute;
  top: 0;
  left: 0;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  border-radius: 16px;
  border: 1px solid rgba(255, 255, 255, 0.9);
  /*
   * The solid glass fill rather than the translucent one, and no blur.
   *
   * These receipts travel across the section every frame, and a moving element
   * with a backdrop filter makes the compositor re-blur the page underneath it
   * on each of those frames. What is underneath is a near-white page, so the
   * blur was buying a difference nobody can point at, at a price that showed up
   * in the frame rate.
   */
  background: var(--glass-fill-solid);
  box-shadow: var(--glass-rim), 0 14px 34px -14px rgba(29, 29, 31, 0.28);
  white-space: nowrap;
  transform: translate3d(var(--x, 76%), var(--y, 26%), 0) scale(var(--s, 1));
  opacity: var(--o, 0);
  will-change: transform, opacity;
}

.l-sale--1 { --o: 1; }

.l-sale-icon {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: 10px;
  background: var(--l-up-soft);
  color: var(--l-up);
}

.l-sale-icon svg, .l-sale-check svg { width: 18px; height: 18px; }

.l-sale-text { display: grid; text-align: left; line-height: 1.25; }
.l-sale-text i { font-style: normal; font-size: 11.5px; color: var(--l-muted); }
.l-sale-text b { font-size: 15px; font-weight: 700; color: var(--l-up); font-variant-numeric: tabular-nums; }

.l-sale-check {
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--l-up-bright);
  color: #fff;
}

.l-hero h1 {
  margin: 0;
  font-size: clamp(40px, 7.2vw, 82px);
  font-weight: 700;
  line-height: 1.02;
  letter-spacing: -0.045em;
}

.l-hero p {
  margin: 22px auto 0;
  max-width: 46ch;
  color: var(--l-ink-2);
  font-size: clamp(16px, 1.7vw, 19px);
  line-height: 1.5;
}

/* The one input the whole site funnels through. */
.l-source {
  display: flex;
  align-items: center;
  gap: 8px;
  width: min(560px, 100%);
  margin: 34px auto 0;
  padding: 8px 8px 8px 17px;
  border: 1px solid var(--glass-edge);
  border-radius: var(--l-r-pill);
  background: var(--glass-fill-solid);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--glass-rim), var(--l-shadow-card);
  transition: box-shadow 220ms ease;
}

/*
 * No border, at rest or focused. The pill is defined by its shadow instead,
 * which deepens on focus -- a keyboard user still gets an unmistakable
 * signal, it simply is not drawn as a line.
 */
.l-source:focus-within {
  box-shadow: var(--glass-rim), var(--l-shadow-float);
}

/*
 * The design system styles every bare input on the site, and
 * `input:not([type="checkbox"]):not([type="radio"])` outranks `.l-source
 * input` -- so without this the field kept a grey border at rest and grew an
 * accent ring on focus, inside a pill that already frames it. Naming
 * .landing clears that specificity without reaching for !important.
 */
.landing .l-source input,
.landing .l-source input:hover,
.landing .l-source input:focus,
.landing .l-source input:focus-visible {
  border: 0;
  outline: none;
  box-shadow: none;
  background: none;
}

.l-source-icon {
  display: grid;
  flex: none;
  place-items: center;
  width: 22px;
  height: 22px;
  color: var(--l-muted);
}

.l-source-icon svg { width: 18px; height: 18px; display: block; }
/* Only one of the two marks is ever shown; the detector decides which. */
/*
 * The mark is always TikTok now, and only its colour reacts.
 *
 * There used to be a globe here too, swapped for the TikTok mark as soon as a
 * handle was recognised, because the field took either. It takes only a TikTok
 * account, so a globe on an empty field would be telling somebody to paste the
 * one thing it refuses.
 */
.l-source[data-kind="tiktok"] .l-source-icon { color: var(--l-ink); }

.l-source input {
  flex: 1;
  min-width: 0;
  border: 0;
  background: none;
  color: var(--l-ink);
  font: inherit;
  font-size: 16px;
  outline: none;
}

.l-source input::placeholder { color: var(--l-faint); }

.l-source button {
  flex: none;
  padding: 11px 18px;
  border: 0;
  border-radius: var(--l-r-pill);
  background: var(--l-ink);
  /* The page, not white: this sits on the ink, and on a dark page the ink is
     the light colour. */
  color: var(--l-page);
  font: inherit;
  font-size: 15px;
  font-weight: 560;
  cursor: pointer;
  transition: transform 140ms ease, opacity 140ms ease;
}

/*
 * The wrapper exists only to own the glow. It is the positioned box the
 * pseudo-element is measured against; the button inside it keeps its own
 * stacking so the fill and the label both paint above the light.
 */
.l-source-cta {
  position: relative;
  display: inline-flex;
  flex: none;
  border-radius: var(--l-r-pill);
}

.l-source button { position: relative; z-index: 1; }
.l-source button:hover { transform: translateY(-1px); }

/*
 * The rainbow glow, on hover and only while the button can actually be
 * pressed. It is a blurred copy of the button sitting behind it, so the
 * colour reads as light thrown onto the page rather than a second border.
 *
 * The :not(:disabled) is the point: an empty field means the button does
 * nothing, and a control that lights up invitingly when pressing it will not
 * work is a small lie. The glow is the affordance, so it appears exactly
 * when the affordance is real.
 */
/*
 * The rainbow glow, present whenever the button can actually be pressed.
 *
 * The colours cycle; the element does not move. Animating transform on a
 * blurred rectangle sweeps the whole blob around the button and smears it
 * well past its edges -- which is exactly what the first version did.
 * Animating the conic gradient's own start angle keeps the glow where it is
 * and turns only the hues through it, which is the effect that was wanted.
 *
 * The angle needs @property to animate at all: a plain custom property is an
 * unparsed string to the animation engine and jumps between values rather
 * than interpolating. Where @property is unsupported the angle stays put and
 * the glow is a still rainbow, which is a perfectly good resting state.
 */
@property --l-glow-angle {
  syntax: "<angle>";
  initial-value: 0deg;
  inherits: false;
}

.l-source-cta::before {
  content: "";
  position: absolute;
  /* Tight to the button: a rim of light, not a cloud behind it. */
  inset: -2.5px;
  z-index: -1;
  border-radius: inherit;
  background: conic-gradient(from var(--l-glow-angle),
    #ff2d55, #ff9f0a, #ffe600, #00e676, #00d4ff, #0057ff, #a855f7, #ff2d55);
  opacity: 0;
  filter: blur(5px);
  transition: opacity 300ms ease, filter 300ms ease;
}

/*
 * Lit as soon as the field holds something analysable, because the glow is
 * the affordance: it says the button is live. Hiding it until hover makes
 * the visitor go looking for that answer.
 */
.l-source[data-ready="true"] .l-source-cta::before {
  opacity: 0.85;
  animation: l-glow-hue 6s linear infinite;
}

.l-source[data-ready="true"] .l-source-cta:hover::before { opacity: 1; filter: blur(7px); }

@keyframes l-glow-hue { to { --l-glow-angle: 360deg; } }

.l-source button:disabled { opacity: 0.38; cursor: default; transform: none; }

/*
 * Stated outright rather than left to `:not(:disabled)` failing to match.
 * The negation alone did not put the glow out: an empty field left a fully
 * lit button, which is the exact lie this affordance exists to avoid. An
 * explicit rule for the disabled case is also simply easier to read than
 * inferring the off state from the absence of the on state.
 */
.l-source:not([data-ready="true"]) .l-source-cta::before { opacity: 0; animation: none; }


.l-reassure {
  margin: 14px 0 0;
  color: var(--l-faint);
  font-size: 13.5px;
}

/* Said only when the typed value cannot be read, so it is never nagging. */
.l-source-note {
  min-height: 20px;
  margin: 10px 0 0;
  color: var(--l-down);
  font-size: 13.5px;
}

/* Quiet, but present. The chips it disclaims are illustrations, and a reader
   who takes them for measurements has been misled by the page. */
.l-final {
  margin-top: var(--l-gap);
  padding: clamp(48px, 7vw, 88px) 24px;
  border-radius: var(--l-r-lg);
  background: var(--l-sunken);
  text-align: center;
}

.l-final h2 { margin: 0; font-size: clamp(28px, 4vw, 42px); font-weight: 680; letter-spacing: -0.035em; }
.l-final .l-source { margin-top: 28px; }

/* ----------------------------------------------------------------- footer */

.landing .site-footer {
  margin-top: var(--l-gap);
  padding-block: 32px;
  border-top: 1px solid var(--l-line);
  color: var(--l-muted);
  font-size: 13px;
}

/* ------------------------------------------------------------- entrances */

/*
 * One shared entrance. Elements start slightly low and fade up as they reach
 * the viewport; the observer sets data-seen once and never again, so nothing
 * re-animates on the way back up.
 */
/* Same rule as the chart: only hide what JS has committed to revealing. */
.js .l-rise:not([data-seen="true"]) {
  opacity: 0;
  transform: translateY(26px) scale(0.985);
}

/*
 * Snappy rather than slow: a short, firmly eased move with a touch of
 * overshoot, so a section arrives and settles instead of gliding in. The
 * cubic below ends above 1 on purpose -- that tiny rebound is what makes it
 * read as a physical thing landing.
 */
.l-rise {
  transition:
    opacity 480ms cubic-bezier(0.22, 0.9, 0.24, 1),
    transform 620ms cubic-bezier(0.18, 1.1, 0.3, 1);
}

/*
 * Variation, so a long page never feels like one repeated effect.
 *
 * Children of a group stagger by position, and the alternating rule gives
 * neighbouring cards slightly different distances and directions to travel.
 * All of it is opacity and transform only, so every one of these stays on
 * the compositor.
 */
.js .l-metrics .l-rise:not([data-seen="true"]) { transform: translateY(30px) scale(0.98); }

.l-metrics .l-rise:nth-child(2) { transition-delay: 60ms; }
.l-metrics .l-rise:nth-child(3) { transition-delay: 120ms; }
.l-metrics .l-rise:nth-child(4) { transition-delay: 180ms; }
.l-metrics .l-rise:nth-child(5) { transition-delay: 240ms; }
.l-metrics .l-rise:nth-child(6) { transition-delay: 300ms; }
.l-metrics .l-rise:nth-child(7) { transition-delay: 360ms; }



@media (prefers-reduced-motion: reduce) {
  .landing * {
    animation-duration: 1ms !important;
    animation-delay: 0ms !important;
    transition-duration: 1ms !important;
    transition-delay: 0ms !important;
  }
  .l-rise { opacity: 1; transform: none; }

  /*
   * The scene becomes a still. The loop in landing.mjs never starts under this
   * preference, so the elements keep the composed positions their custom
   * properties already carry, and the line keeps the shape it ships with --
   * there is nothing to park, nothing to un-hide, and no CSS loop left to
   * stop since the drifting dash was removed with the old look.
   */
}

/*
 * There was a rule here hiding the cards, the receipts and the line below
 * 900px. Two of those three are wanted on a phone now -- the cards as a still
 * under the claim, the line behind the hero's headline -- and the third is
 * handled where the still life is composed, further down. A blanket hide that
 * three later rules have to undo is not a rule, it is a trap.
 */

/* -------------------------------------------------------------- responsive */

@media (max-width: 720px) {
  .l-wrap, .l-wrap--tight { width: calc(100vw - 32px); }
  .l-source {
    flex-wrap: wrap;
    border-radius: var(--l-r-lg);
    padding: 12px;
  }
  .l-source input { flex-basis: calc(100% - 34px); padding: 4px 0; }
  .l-source button { width: 100%; margin-top: 8px; }
  .l-metric { padding: 16px 14px 0; }
}

/*
 * The travelling swell: the line thickened where a new reading is passing
 * through. A filled outline rather than a thicker stroke, because SVG stroke
 * width is uniform along a path and the whole point here is that it is not.
 *
 * No stroke of its own, and the same green as the line, so the two read as
 * one shape rather than a band laid over a wire.
 */


/*
 * -------------------------------------------------- interior page shells
 *
 * /features, /plans and /support share the landing body class so they
 * inherit its type, tokens and glass from one place. These are the few
 * pieces the homepage does not already provide: a page heading smaller than
 * a hero, and the old pages' own class names mapped onto the new system so
 * their existing markup stops carrying the retired look.
 */
.l-page-head {
  padding-block: clamp(56px, 7vw, 96px) clamp(24px, 3vw, 40px);
  text-align: center;
}

.l-page-head h1 {
  margin: 0;
  font-size: clamp(32px, 5vw, 54px);
  font-weight: 700;
  line-height: 1.05;
  letter-spacing: -0.04em;
}

.l-page-head .l-lede { margin-inline: auto; }
.l-page-head .l-source { margin-top: 28px; }

/*
 * The interior pages keep their own section and card class names from the
 * previous design. Rather than rewrite every template, the names are pointed
 * at the current tokens -- same result, far less surface to get wrong.
 */
.plans-page .ep-section,
.support-page .ep-section { padding-block: clamp(48px, 7vw, 96px); }

.plans-page h1, .support-page h1,
.plans-page h2, .support-page h2 {
  font-weight: 700;
  letter-spacing: -0.035em;
  line-height: 1.08;
}

.plans-page .ep-panel,
.support-page .ep-panel,
.support-guide-card,
.support-answer {
  border: 1px solid var(--l-line);
  border-radius: var(--l-r-lg);
  background: var(--l-raised);
  box-shadow: var(--l-shadow-card);
}

.support-guide-card {
  display: block;
  padding: 20px;
  color: inherit;
  text-decoration: none;
  transition: transform 160ms ease, border-color 160ms ease;
}

.support-guide-card:hover { transform: translateY(-2px); border-color: var(--l-line-strong); }
.support-guide-card h3 { margin: 10px 0 6px; font-size: 17px; font-weight: 650; letter-spacing: -0.02em; }
.support-guide-card p { margin: 0; color: var(--l-muted); font-size: 14.5px; line-height: 1.55; }
.support-guide-glyph { color: var(--l-note); font-size: 20px; }

/* The old pages painted the retired product's fields behind their sections. */
.plans-page .ep-grain,
.support-page .ep-grain,
.features-page .ep-grain { background-image: none; }

/*
 * =====================================================================
 * The 2026 rebuild: hero mark, app panel, flow band, analytics wall
 * =====================================================================
 *
 * Five sections, one idea each, and a lot of air between them. The page is
 * white and stays white until the analytics wall, which is the one dark band
 * on the site: the seven cards are the product's own screen, and on white they
 * looked like a widget gallery rather than the thing being sold.
 */

/* ------------------------------------------------------------- the hero */

.l-hero-lede {
  margin: 22px auto 0;
  max-width: 40ch;
}

/* ----------------------------------------------------- the flow section */

/*
 * The cards behind the claim they illustrate.
 *
 * Written above them the two read as a caption and a picture; behind, the
 * shorts falling into sales are the sentence happening. The section carries
 * the room for both -- the copy sets its height and the scene is laid over
 * that, centred, so neither pushes the other around.
 */
.l-flowsection {
  position: relative;
  padding-block: clamp(104px, 12vw, 168px);
}

.l-flowsection .l-flowclip {
  position: absolute;
  inset: 0;
  display: grid;
  align-content: center;
}

.l-flowsection .l-flowband { margin-block: 0; }

/*
 * A clear centre for the words.
 *
 * The copy already paints above the scene -- it hit-tests on top and the
 * glyphs are drawn over it -- but the cards are opaque photographs, and grey
 * body text over somebody's face is text nobody reads. This fades the scene
 * out across the middle of the section, so a card passing behind the sentence
 * dissolves rather than sitting behind the gaps between letters.
 *
 * On the clip rather than the section, so it covers the scene and nothing
 * else, and after the band so it paints over it.
 */
.l-flowsection .l-flowclip::after {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse 48% 42% at 50% 44%, rgba(var(--l-page-rgb), 0.95) 36%, rgba(var(--l-page-rgb), 0) 76%);
  pointer-events: none;
}

/* Above the scene, and legible over it. */
.l-flowcopy { position: relative; z-index: 1; }

/* -------------------------------------------------------- the flow band */

/*
 * The scene has its own section now.
 *
 * Behind the headline it was decoration nobody read; the claim it illustrates
 * is written above it here, so the shorts falling into sales are the picture
 * of a sentence somebody just read rather than movement under one.
 */
/*
 * The window a sale leaves through.
 *
 * Full width and clipping, so a sale flying off to the right disappears at the
 * edge of the screen the way it did behind the hero, rather than being cut off
 * in the middle of the page or pushing a horizontal scrollbar onto it.
 */
.l-flowclip { overflow: clip; }

/*
 * The stage the scene is measured against, which is the content column rather
 * than the window. The cards' positions are percentages of this box, and
 * against the full viewport they began half off the left of the screen.
 */
.l-flowband {
  position: relative;
  width: min(1080px, calc(100vw - 48px));
  height: clamp(280px, 34vw, 400px);
  margin: clamp(28px, 4vw, 48px) auto 0;
}

/* -------------------------------------------------- the analytics wall */

.l-analytics {
  margin-top: clamp(48px, 7vw, 96px);
  padding-block: clamp(64px, 9vw, 128px);
  background: var(--l-dark);
  color: #f5f5f7;
}

.l-analytics .l-h2 { color: #fff; }
.l-analytics .l-lede { color: #a1a1a6; }

.l-metrics {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 14px;
  margin-top: clamp(40px, 5vw, 64px);
}

/*
 * The tone per metric, and the one card that is worth two columns.
 *
 * Copied from `apps/berry/src/shell/metricSeries.ts`, which is where the
 * application's palette is decided: a metric keeps its colour wherever it is
 * shown, so somebody who has learned that revenue is blue in the product does
 * not have to learn it again here.
 *
 * Rules rather than an inline custom property, because the site's CSP has no
 * `'unsafe-inline'` in `style-src` and the style attribute is dropped with it.
 */
.l-metric[data-metric="revenue"] { --tone: #0ea5e9; grid-column: span 2; }
.l-metric[data-metric="conversion"] { --tone: #db2777; }
.l-metric[data-metric="new-users"] { --tone: #f59e0b; }
.l-metric[data-metric="profit"] { --tone: #14b8a6; }
.l-metric[data-metric="views"] { --tone: #22c55e; }
.l-metric[data-metric="likes"] { --tone: #e8813a; }
.l-metric[data-metric="comments"] { --tone: #8b5cf6; }

.l-metric {
  grid-column: span 1;
  display: grid;
  align-content: start;
  padding: 18px 18px 0;
  border: 1px solid var(--l-dark-line);
  border-radius: var(--l-r-md);
  background: var(--l-dark-card);
  overflow: hidden;
  transition: border-color 200ms ease, background 200ms ease;
}

.l-metric:hover,
.l-metric[data-reading="true"] { border-color: color-mix(in srgb, var(--tone) 42%, transparent); }
.l-metric:focus-visible { outline: 2px solid var(--tone); outline-offset: 2px; }

.l-metric-name {
  margin: 0;
  color: var(--tone);
  font-size: 14px;
  font-weight: 650;
  letter-spacing: -0.01em;
}

.l-metric-figures {
  display: flex;
  align-items: baseline;
  gap: 12px;
  margin-top: 10px;
}

.l-metric-figures strong {
  font-size: clamp(26px, 3vw, 34px);
  font-weight: 700;
  letter-spacing: -0.035em;
  font-variant-numeric: tabular-nums;
}

.l-metric-figures span { margin-left: auto; color: #86868b; font-size: 13px; font-variant-numeric: tabular-nums; }

.l-metric-period { margin-top: 6px; color: #86868b; font-size: 13px; }

.l-metric-plot {
  height: 128px;
  margin: 22px -18px 0;
  cursor: crosshair;
}

.l-metric-plot svg { display: block; width: 100%; height: 100%; overflow: visible; }

.l-metric-area { fill: var(--tone); opacity: 0.16; }

.l-metric-line {
  fill: none;
  stroke: var(--tone);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}

.l-metric-rule {
  stroke: #fff;
  stroke-width: 1;
  opacity: 0;
  vector-effect: non-scaling-stroke;
}

.l-metric[data-reading="true"] .l-metric-rule { opacity: 0.22; }

.l-metric-dot {
  fill: var(--tone);
  stroke: var(--l-dark-card);
  stroke-width: 2.5;
  opacity: 0;
  transition: opacity 160ms ease;
}

.l-metric:hover .l-metric-dot,
.l-metric[data-reading="true"] .l-metric-dot { opacity: 1; }

/* ------------------------------------------------------------ the notes */

.l-note {
  margin: 26px 0 0;
  color: #86868b;
  font-size: 13px;
  text-align: center;
}

.l-quiet { margin: 26px 0 0; text-align: center; font-size: 15px; }

.l-quiet a {
  color: var(--l-note);
  font-weight: 550;
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: border-color 180ms ease;
}

.l-quiet a:hover { border-bottom-color: currentColor; }
.l-analytics .l-quiet a { color: var(--l-up-bright); }

.l-section--last { padding-bottom: clamp(88px, 12vw, 168px); }

/* --------------------------------------------------------- narrow views */

@media (max-width: 1080px) {
  .l-metrics { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .l-metric { grid-column: span 1; }
  .l-metric[data-metric="revenue"] { grid-column: span 2; }
}

/*
 * The flow band on a phone.
 *
 * The animation does not run below 901px -- the loop is off and the cards it
 * moves were hidden with it -- which left this section as a heading over 240
 * pixels of nothing. It says the same sentence held still instead: two frames,
 * and the sale they turned into.
 *
 * Two rather than four, and that is the download as much as the composition.
 * These are photographs, and the pair says what the four said.
 */
@media (max-width: 900px) {
  /* Nothing flies out of it any more, so nothing needs clipping. */
  .l-flowclip { overflow: visible; }

  /*
   * The line gets a band rather than the whole hero.
   *
   * Its box is 1000 by 600 and it is stretched, not fitted -- which is what
   * lets the stroke stay an even weight at any width. Stretched into a phone
   * hero, though, the box is taller than it is wide and the gentle rise turns
   * into a near vertical slash across the headline and the input. Held to a
   * band roughly the shape of the box, the drawing keeps its proportions and
   * sits where it is meant to: behind the words.
   */
  .l-heroline {
    top: 21%;
    bottom: auto;
    height: clamp(150px, 44vw, 230px);
  }

  /*
   * Below the words on a phone, not behind them.
   *
   * Two cards and a receipt behind a centred headline in a 340 pixel column is
   * neither readable nor a picture. The section becomes a column and the order
   * is set here rather than in the markup, because on a wide screen the scene
   * has to come first in the DOM to sit underneath.
   */
  .l-flowsection {
    display: flex;
    flex-direction: column;
    padding-block: var(--l-gap);
  }

  .l-flowsection .l-flowclip {
    position: static;
    order: 2;
    display: block;
  }

  .l-flowcopy { order: 1; }

  .l-flowband {
    height: auto;
    margin-top: clamp(24px, 6vw, 44px);
  }

  /*
   * Wrapping, because the three of them do not fit a phone on one line: two
   * frames and a receipt come to about 400 pixels and the column is 312 on a
   * small Android, so on one row both ends were cut off by the viewport.
   * Wrapped, the receipt sits under the posts it came from, which is the
   * order the sentence above reads in anyway.
   */
  .l-scene {
    position: static;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: clamp(14px, 4vw, 22px);
  }

  /*
   * Relative, not static. The frame inside each card is absolutely positioned
   * against it, so a static card hands that image to the nearest positioned
   * ancestor instead -- which stretched one photograph across the whole band.
   *
   * They do not flex, because these are photographs at a chosen size, and a
   * flex item allowed to shrink is a photograph at whatever size is left over.
   */
  .l-short {
    display: block;
    position: relative;
    flex: none;
    width: 108px;
    height: 184px;
    transform: none;
    opacity: 1;
    will-change: auto;
  }

  .l-short--2 { width: 108px; height: 184px; }
  .l-short--3,
  .l-short--4 { display: none; }

  .l-sale {
    display: flex;
    position: relative;
    flex: none;
    transform: none;
    opacity: 1;
    will-change: auto;
  }

  .l-sale--2,
  .l-sale--3 { display: none; }
}

@media (max-width: 640px) {
  .l-metrics { grid-template-columns: minmax(0, 1fr); }
  .l-metric,
  .l-metric[data-metric="revenue"] { grid-column: span 1; }
}

/*
 * =====================================================================
 * The landing page in dark
 * =====================================================================
 *
 * The page keeps its own palette rather than reading the design system's,
 * because it models things the shared tokens do not: a colour at two weights,
 * one for strokes set large and one for anything small enough that AA applies.
 * Dark inverts that relationship rather than the values. On white the deep
 * tone carries the small text and the bright one draws the lines; on near
 * black the bright tone is the only one that can be read at all, so the pairs
 * swap roles and the "soft" backgrounds become tints of the ink instead of
 * washes of the paper.
 *
 * Scoped to `.landing` because these tokens are the landing page's. The rest
 * of the site is on `--ep-*` and is already handled.
 */
:root[data-appearance="dark"] .landing {
  --l-ink: #f5f5f7;
  --l-ink-2: #c9c9ce;
  --l-muted: #9a9aa0;
  --l-faint: #68686e;
  --l-page: #0b0b0d;
  --l-page-rgb: 11, 11, 13;
  --l-raised: #17181b;
  --l-sunken: #131418;
  --l-line: #26272b;
  --l-line-strong: #35363b;

  /*
   * The bright value now carries the small text as well as the strokes.
   *
   * systemGreen on near-black is about 8:1, where the deep green the light
   * theme uses for the same job is under 2:1 and unreadable. The soft washes
   * become low-alpha tints of the same hue, which is the only way a "quiet
   * background in this colour" exists on a dark ground.
   */
  --l-up: #3ddc84;
  --l-up-bright: #34d17a;
  --l-up-soft: rgba(61, 220, 132, .14);
  --l-down: #ff6b81;
  --l-down-bright: #ff5470;
  --l-down-soft: rgba(255, 107, 129, .16);
  --l-note: #5ea3ff;
  --l-note-soft: rgba(94, 163, 255, .16);
  --l-warm: #ffb340;
  --l-warm-bright: #ffa726;
  --l-warm-soft: rgba(255, 179, 64, .16);

  /*
   * A shadow is light falling short of a surface, and on a near-black page
   * there is none to fall. Depth comes from the border instead, so these go
   * almost to nothing rather than being darkened further.
   */
  --l-shadow-card: 0 1px 2px rgba(0, 0, 0, .5), 0 12px 32px -12px rgba(0, 0, 0, .7);
  --l-shadow-float: 0 2px 6px rgba(0, 0, 0, .55), 0 32px 64px -24px rgba(0, 0, 0, .8);

  /*
   * The analytics band is the one section that was always dark. On a dark page
   * it has to stop being the contrast and start being a surface, so it lifts
   * off the page rather than sinking into it.
   */
  --l-dark: #141519;
  --l-dark-card: #1e1f24;
  --l-dark-line: #303138;
}

/*
 * The app panel's chart fill, which is a wash of the same green as its line.
 * Heavier on dark: a twelve per cent white-ground wash disappears entirely.
 */


/*
 * The photo cards and the receipts keep their white rims.
 *
 * They are pieces of glass over photographs, and a white rim is what makes an
 * edge read on one. Against the dark page they are quieter than they were, so
 * this only takes the hardest of them down.
 */
:root[data-appearance="dark"] .l-short { border-color: rgba(255, 255, 255, .28); }
:root[data-appearance="dark"] .l-sale { border-color: rgba(255, 255, 255, .16); }

/*
 * The mark, and how loudly it is drawn.
 *
 * systemGreen at 42% over white lands on a soft mint. The same pair over a
 * near-black page lands on a dark forest green that reads as a smudge, because
 * the ground it is being mixed with is now dark rather than light. The brighter
 * green and the higher opacity put it back where it was: clearly the same
 * figure, clearly behind the words.
 */
:root[data-appearance="dark"] .landing { --l-mark: #3ddc84; }
:root[data-appearance="dark"] .l-scene-flow { opacity: 0.6; }

/*
 * =====================================================================
 * /features
 * =====================================================================
 *
 * Five rows, copy on one side and its picture on the other, alternating. The
 * page was nine cards of prose in three grids; it is the same product with
 * four fewer claims and something to look at against each of the five left.
 *
 * Everything reads its colour from the landing page's tokens, so the page
 * follows the theme with the rest of the site and nothing here needs a dark
 * counterpart of its own.
 */

.f-row { padding-block: clamp(56px, 8vw, 104px); }

.f-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
  align-items: center;
  gap: clamp(32px, 6vw, 88px);
}

/*
 * The flip is a row-direction change, not a source order change: the copy
 * stays first in the document on every row, so the page reads in one order
 * however it is laid out.
 */
.f-row--flip .f-copy { order: 2; }

.f-eyebrow {
  margin: 0 0 12px;
  color: var(--l-muted);
  font-size: 12.5px;
  font-weight: 650;
  letter-spacing: .06em;
  text-transform: uppercase;
}

.f-copy .l-lede { margin-top: 14px; }

.f-points {
  margin: 26px 0 0;
  padding: 0;
  display: grid;
  gap: 12px;
  list-style: none;
}

.f-points li {
  position: relative;
  padding-left: 26px;
  color: var(--l-ink-2);
  font-size: 15px;
  line-height: 1.55;
}

/* A tick drawn in CSS rather than an svg per bullet: two borders and a
   rotation, which costs nothing and cannot fall out of sync with the text. */
.f-points li::before {
  content: "";
  position: absolute;
  left: 4px;
  top: 6px;
  width: 5px;
  height: 9px;
  border-right: 2px solid var(--l-up);
  border-bottom: 2px solid var(--l-up);
  rotate: 45deg;
}

/* ------------------------------------------------------------ the panels */

.f-panel {
  padding: 18px;
  border: 1px solid var(--l-line);
  border-radius: var(--l-r-lg);
  background: var(--l-raised);
  box-shadow: var(--l-shadow-card);
}

.f-panel-head {
  display: flex;
  align-items: baseline;
  gap: 12px;
  margin-bottom: 16px;
  font-size: 13px;
  font-weight: 620;
}

.f-panel-note { margin-left: auto; color: var(--l-faint); font-weight: 500; }

/* ----------------------------------------------------------- the sources */

.f-sources { margin: 0; padding: 0; display: grid; gap: 2px; list-style: none; }

.f-source {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 11px 12px;
  border-radius: var(--l-r-sm);
  background: var(--l-sunken);
  font-size: 14.5px;
}

.f-source-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--l-faint); }
.f-source[data-source="tiktok"] .f-source-dot { background: #ff2d55; }
.f-source[data-source="instagram"] .f-source-dot { background: #c13584; }
.f-source[data-source="youtube"] .f-source-dot { background: #ff0000; }
.f-source[data-source="stripe"] .f-source-dot { background: #635bff; }
.f-source[data-source="shopify"] .f-source-dot { background: #5e8e3e; }

.f-source b { font-weight: 560; }
.f-source-kind { margin-left: auto; color: var(--l-faint); font-size: 12.5px; }

/* ------------------------------------------------------------ the chart */

.f-chart { height: 150px; }
.f-chart svg { display: block; width: 100%; height: 100%; overflow: visible; }

.f-line {
  fill: none;
  stroke-width: 2.4;
  stroke-linecap: round;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}

.f-line--reach { stroke: var(--l-faint); }
.f-line--revenue { stroke: var(--l-up); }

.f-legend {
  margin: 16px 0 0;
  padding: 0;
  display: flex;
  gap: 18px;
  list-style: none;
  font-size: 12.5px;
  color: var(--l-muted);
}

.f-key { display: flex; align-items: center; gap: 7px; }

.f-key::before {
  content: "";
  width: 14px;
  height: 2.5px;
  border-radius: 2px;
  background: currentColor;
}

.f-key--reach { color: var(--l-muted); }
.f-key--revenue { color: var(--l-up); }

/* ----------------------------------------------------------- the funnel */

.f-funnel { margin: 0; padding: 0; display: grid; gap: 14px; list-style: none; }

.f-step {
  display: grid;
  grid-template-columns: 118px minmax(0, 1fr) 42px;
  align-items: center;
  gap: 12px;
  font-size: 14px;
}

.f-step-label { color: var(--l-ink-2); }

.f-step-bar {
  height: 10px;
  border-radius: var(--l-r-pill);
  background: var(--l-sunken);
  overflow: hidden;
}

.f-step-fill {
  display: block;
  height: 100%;
  border-radius: var(--l-r-pill);
  background: var(--l-up);
}

/*
 * The widths as classes rather than an inline custom property.
 *
 * The site's CSP is `style-src 'self'` with no `'unsafe-inline'`, which drops
 * the style attribute along with inline blocks -- so a width passed on the
 * element never arrives. Four steps is a closed set and four rules is the
 * honest way to say it.
 */
.f-step-fill--100 { width: 100%; }
.f-step-fill--38 { width: 38%; }
.f-step-fill--31 { width: 31%; }
.f-step-fill--9 { width: 9%; }

.f-step-share { color: var(--l-muted); font-variant-numeric: tabular-nums; text-align: right; }

/* The step that loses the most is the one worth looking at. */
.f-step[data-drop="true"] .f-step-fill { background: var(--l-down); }
.f-step[data-drop="true"] .f-step-label,
.f-step[data-drop="true"] .f-step-share { color: var(--l-down); font-weight: 620; }

/* ----------------------------------------------------------- the grades */

.f-grades { margin: 0; padding: 0; display: grid; gap: 8px; list-style: none; }

.f-grade {
  display: grid;
  gap: 3px;
  padding: 12px 14px;
  border: 1px solid var(--l-line);
  border-radius: var(--l-r-sm);
  font-size: 14px;
}

.f-grade b { font-family: var(--ep-font-mono); font-size: 13px; font-weight: 600; color: var(--l-muted); }
.f-grade span { color: var(--l-faint); font-size: 13px; }

/* Only the top grade is filled: the point of the list is that the others are
   not it. */
.f-grade[data-top="true"] { border-color: var(--l-up); background: var(--l-up-soft); }
.f-grade[data-top="true"] b { color: var(--l-up); }
.f-grade[data-top="true"] span { color: var(--l-ink-2); }

/* ------------------------------------------------------------ the brief */

.f-brief { margin: 0; display: grid; gap: 0; }
.f-brief dt { color: var(--l-muted); font-size: 12.5px; font-weight: 620; }
.f-brief dd { margin: 4px 0 18px; color: var(--l-ink); font-size: 15.5px; line-height: 1.5; }
.f-brief dd:last-of-type { margin-bottom: 0; }

/* -------------------------------------------------------- narrow views */

@media (max-width: 860px) {
  /* One column, picture under its copy. The flip has nothing left to flip. */
  .f-grid { grid-template-columns: minmax(0, 1fr); gap: 28px; }
  .f-row--flip .f-copy { order: 0; }
  .f-step { grid-template-columns: 96px minmax(0, 1fr) 38px; font-size: 13px; }
}


/*
 * Page furniture for the /answers, /connect, /compare and /blog clusters.
 *
 * These pages render inside `content-page`, not `landing`, so they use the
 * shared --ep-* tokens. The --l-* tokens are scoped to `.landing` and resolve
 * to nothing here, which is why landing.css's own .support-answer card rule
 * quietly does not apply -- worth knowing before "fixing" it.
 *
 * Every table is wrapped rather than styled to fit. A comparison table has
 * three columns of prose and a connection matrix has five; at 380px neither
 * can be made to fit without becoming unreadable, so they scroll inside their
 * own box and the page itself never scrolls sideways.
 */

/* ------------------------------------------------------ heading and rhythm */

/*
 * The hole above every breadcrumbed page.
 *
 * `.page-intro` carries a top padding of up to 9rem because it was written for
 * pages where it is the first thing on the page. Put a breadcrumb trail above
 * it -- which every page in these clusters and every support answer does -- and
 * that padding stacks on the trail's own 4rem margin, leaving ~190px of empty
 * white between the trail and the first word. It reads as a broken image.
 *
 * Keyed off the adjacency rather than a page class so it cannot be missed on a
 * page added later: an intro that follows a trail does not need to push itself
 * down the page, wherever it appears.
 */
.breadcrumbs + .page-intro { padding-top: 0; }

/*
 * The same signal decides the heading.
 *
 * A breadcrumbed intro is an article-shaped page -- the /answers, /connect,
 * /compare and /blog clusters and the support answers, and nothing else: the
 * legal pages, /account-deletion and /404 render their intro with no trail
 * above it and keep the older display style.
 *
 * Those headings were set in Avenir at weight 900 and up to 8.5rem, beside a
 * hero one click away in Inter at 700 and 82px. Keyed off the structure rather
 * than a body class because the class had to be remembered on every new page,
 * and was already missing from the support answers.
 */
.breadcrumbs + .page-intro h1 {
  font-family: var(--ep-font-hero);
  font-size: clamp(40px, 7.2vw, 82px);
  font-weight: 700;
  line-height: 1.02;
  letter-spacing: -0.045em;
}

.breadcrumbs + .page-intro .lede { max-width: 68ch; }

.answer-summary {
  margin: 0 0 2.4rem;
  padding: 1.1rem 1.3rem;
  border-left: 3px solid var(--ep-accent);
  border-radius: 0 var(--ep-radius-sm) var(--ep-radius-sm) 0;
  background: var(--ep-surface-2);
}

.answer-summary p { margin: 0; max-width: 70ch; color: var(--ep-ink); line-height: 1.6; }

/* ------------------------------------------------------------- questions */

.faq-block { margin-top: 3.4rem; }
.faq-block > h2 { margin: 0 0 1.4rem; font-size: clamp(1.5rem, 3vw, 2.1rem); letter-spacing: -0.03em; }
.faq-list { display: grid; gap: 1.4rem; margin: 0; }
.faq-entry {
  padding: 1.2rem 1.3rem;
  border: 1px solid var(--ep-line);
  border-radius: var(--ep-radius-md);
  background: var(--ep-surface);
}
.faq-entry dt { margin: 0 0 .5rem; font-weight: 850; letter-spacing: -0.02em; }
.faq-entry dd { margin: 0; max-width: 70ch; color: var(--ep-muted); line-height: 1.65; }

/* ---------------------------------------------------------------- tables */

.answer-table-wrap {
  /* The one rule that keeps the page body from scrolling horizontally. */
  overflow-x: auto;
  margin: 1.6rem 0;
  border: 1px solid var(--ep-line);
  border-radius: var(--ep-radius-md);
  background: var(--ep-surface);
}

.answer-table,
.compare-table,
.connect-matrix {
  width: 100%;
  min-width: 520px;
  border-collapse: collapse;
  font-size: .95rem;
}

.answer-table th,
.answer-table td,
.compare-table th,
.compare-table td,
.connect-matrix th,
.connect-matrix td {
  padding: .85rem 1rem;
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--ep-line);
  line-height: 1.55;
}

.answer-table thead th,
.compare-table thead th,
.connect-matrix thead th {
  font-family: var(--ep-font-mono);
  font-size: .72rem;
  font-weight: 900;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ep-ink);
  background: var(--ep-surface-2);
}

.answer-table tbody tr:last-child td,
.compare-table tbody tr:last-child td,
.compare-table tbody tr:last-child th,
.connect-matrix tbody tr:last-child td,
.connect-matrix tbody tr:last-child th { border-bottom: 0; }

.answer-table td { color: var(--ep-muted); }
.compare-table tbody th { font-weight: 850; width: 26%; }
.compare-table td { color: var(--ep-muted); }
/* The last column is ours, and is the one a reader is scanning for. */
.compare-table td:last-child { color: var(--ep-ink); font-weight: 650; }
.connect-matrix tbody th { font-weight: 850; }
.connect-matrix td a { font-weight: 700; text-underline-offset: 4px; }

/* ------------------------------------------------------------- comparison */

.comparison-meta {
  margin: -2rem 0 2.6rem;
  font-family: var(--ep-font-mono);
  font-size: .74rem;
  letter-spacing: .04em;
  color: var(--ep-muted);
}

.compare-choose {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 1.4rem;
  margin: 2.4rem 0;
}

.compare-choose-card {
  padding: 1.4rem 1.5rem;
  border: 1px solid var(--ep-line);
  border-radius: var(--ep-radius-md);
  background: var(--ep-surface);
}

.compare-choose-card--ours { border-color: var(--ep-accent); background: var(--ep-surface-2); }
.compare-choose-card h3 { margin: 0 0 .9rem; font-size: 1.1rem; letter-spacing: -0.02em; }
.compare-choose-card ul { display: grid; gap: .6rem; margin: 0; padding-left: 1.15rem; color: var(--ep-muted); line-height: 1.6; }
.compare-choose-card li::marker { color: var(--ep-accent); font-weight: 800; }

/* --------------------------------------------------------------- connect */

.connect-chain {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .6rem;
  margin: 0 0 2.6rem;
}

.connect-node {
  padding: .5rem .85rem;
  border: 1px solid var(--ep-line);
  border-radius: var(--ep-radius-pill, 999px);
  background: var(--ep-surface);
  font-family: var(--ep-font-mono);
  font-size: .74rem;
  letter-spacing: .03em;
}

.connect-node--end { border-color: var(--ep-accent); background: var(--ep-surface-2); font-weight: 850; }
.connect-arrow { color: var(--ep-muted); }
.connect-steps { counter-reset: connect-step; }
.connect-steps li { line-height: 1.65; }

/* ------------------------------------------------------------------ blog */

.post-meta {
  margin: -2rem 0 2.6rem;
  font-family: var(--ep-font-mono);
  font-size: .74rem;
  letter-spacing: .04em;
  color: var(--ep-muted);
}

.post-list { display: grid; gap: 1.4rem; margin: 0; padding: 0; list-style: none; }

.post-card {
  display: block;
  padding: 1.8rem;
  border: 1px solid var(--ep-line);
  border-radius: var(--ep-radius-lg);
  background: var(--ep-surface);
  color: inherit;
  text-decoration: none;
  transition: transform 200ms var(--ep-ease), border-color 200ms var(--ep-ease);
}

.post-card:hover { transform: translateY(-3px); border-color: var(--ep-accent); }
.post-card-meta {
  font-family: var(--ep-font-mono);
  font-size: .7rem;
  font-weight: 900;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ep-muted);
}
.post-card h2 { margin: .7rem 0 .6rem; font-size: clamp(1.5rem, 3.4vw, 2.3rem); line-height: 1.05; letter-spacing: -0.04em; }
.post-card p { margin: 0 0 1rem; max-width: 70ch; color: var(--ep-muted); line-height: 1.6; }
.post-card i { font-style: normal; font-weight: 850; }

/* ------------------------------------------------------------------- cta */

.seo-cta {
  margin-top: 4rem;
  padding: clamp(1.8rem, 4vw, 2.8rem);
  border: 1px solid var(--ep-line);
  border-radius: var(--ep-radius-lg);
  background: var(--ep-surface-2);
  text-align: center;
}

.seo-cta h2 { margin: 0 0 .7rem; font-size: clamp(1.6rem, 3.6vw, 2.4rem); line-height: 1.05; letter-spacing: -0.04em; }
.seo-cta > p { margin: 0 auto 1.6rem; max-width: 54ch; color: var(--ep-muted); line-height: 1.6; }
.seo-cta-note {
  margin: 1.1rem 0 0;
  font-family: var(--ep-font-mono);
  font-size: .72rem;
  letter-spacing: .03em;
  color: var(--ep-muted);
}

/* Inline code appears throughout the answers; without this it inherits the
   body face and stops reading as a parameter name. */
.support-answer code {
  padding: .12em .4em;
  border-radius: var(--ep-radius-sm);
  background: var(--ep-surface-2);
  font-family: var(--ep-font-mono);
  font-size: .88em;
}

@media (max-width: 720px) {
  .comparison-meta,
  .post-meta { margin-top: -1rem; }
  .seo-cta { margin-top: 3rem; }
}


/*
 * The redesigned landing page.
 *
 * White page, one centred column, everything left and right of it empty.
 * Colour is rationed to the violet accent, one nectar, one green. The only
 * dark element is the floating nav pill.
 *
 * Every rule is scoped to `body.nl` and every class carries the `nl-` prefix,
 * because this stylesheet ships in the same bundle as the rest of the site
 * and generic names like .hero or .btn would fight pages that still wear the
 * old design. The homepage renders bare (no site shell), so the shell's own
 * rules never apply here; the scope is for the other direction.
 *
 * No inline style attributes anywhere: the CSP (style-src 'self') drops them,
 * custom properties included, so the funnel widths, bar heights and the gauge
 * are utility classes instead.
 */

body.nl {
  color-scheme: light;
  --nl-ink: #17131f;
  --nl-ink-2: #4a4653;
  --nl-muted: #8a8691;
  --nl-surface: #f6f6f8;
  --nl-line: rgba(23, 19, 31, .08);
  --nl-line-2: rgba(23, 19, 31, .15);
  --nl-violet: #7487ff;
  --nl-violet-d: #5a6ef5;
  --nl-violet-t: #eceefe;
  --nl-nectar: #ffb443;
  --nl-green: #14cf95;
  --nl-dark: #101321;
  --nl-wrap: 880px;
  --nl-text: 620px;
  --nl-gutter: clamp(20px, 5vw, 48px);
  --nl-pad-y: clamp(110px, 14vw, 190px);
  --nl-sans: -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

  margin: 0;
  background: #fff;
  color: var(--nl-ink);
  font-family: var(--nl-sans);
  font-size: 16.5px;
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
}

body.nl img { max-width: 100%; display: block; }
body.nl a { color: inherit; text-decoration: none; }
body.nl :where(a, button, input):focus-visible {
  outline: 2px solid var(--nl-violet);
  outline-offset: 3px;
  border-radius: 6px;
}

/* ─────────────────────────────────────────────────────  nav  ── */
.nl-navwrap {
  position: sticky; top: 0; z-index: 100;
  display: flex; justify-content: center;
  padding: 16px var(--nl-gutter) 0;
  transition: transform .26s ease;
}
.nl-navwrap[data-hidden="true"] { transform: translateY(-150%); }

.nl-nav {
  display: flex; align-items: center; gap: 6px;
  background: var(--nl-dark); color: #fff;
  border-radius: 999px;
  padding: 6px 6px 6px 14px;
  box-shadow: 0 8px 26px rgba(16, 19, 33, .16);
}
.nl-brand { display: grid; place-items: center; padding-right: 4px; }
.nl-brand img { width: 22px; height: 22px; border-radius: 6px; }

.nl-links { display: flex; gap: 2px; list-style: none; margin: 0; padding: 0; }
.nl-links a {
  display: block; padding: 7px 12px; border-radius: 999px;
  font-size: 14px; color: rgba(255, 255, 255, .75);
  transition: color .16s ease, background .16s ease;
}
.nl-links a:hover { color: #fff; background: rgba(255, 255, 255, .09); }

.nl-actions { display: flex; align-items: center; gap: 2px; margin-left: 4px; }
.nl-login {
  padding: 7px 12px; border-radius: 999px;
  font-size: 14px; color: rgba(255, 255, 255, .75);
}
.nl-login:hover { color: #fff; background: rgba(255, 255, 255, .09); }

@media (max-width: 780px) { .nl-links { display: none; } }

/* ─────────────────────────────────────────────────  buttons  ── */
.nl-btn {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 13px 24px; border-radius: 999px;
  font-family: var(--nl-sans); font-size: 15px; font-weight: 500;
  border: 1px solid transparent; cursor: pointer; white-space: nowrap;
  transition: background .16s ease, color .16s ease, transform .16s ease;
}
.nl-btn:active { transform: translateY(1px); }
.nl-btn-primary { background: var(--nl-violet); color: #fff; }
.nl-btn-primary:hover { background: var(--nl-violet-d); }
.nl-btn-primary:disabled { opacity: .55; cursor: default; }
.nl-btn-ghost { background: var(--nl-surface); color: var(--nl-ink); }
.nl-btn-ghost:hover { background: #ededf0; }
.nl-btn-sm { padding: 8px 16px; font-size: 14px; }

/* ─────────────────────────────────────────────────────  hero  ── */
.nl-hero {
  max-width: var(--nl-text);
  margin: 0 auto;
  padding: clamp(84px, 12vw, 150px) var(--nl-gutter) clamp(48px, 6vw, 72px);
  text-align: center;
}
.nl-hero h1 {
  margin: 0;
  font-family: var(--ep-font-display, var(--nl-sans));
  font-weight: 600;
  font-size: clamp(2.5rem, 6vw, 4.1rem);
  line-height: 1.05;
  letter-spacing: -.033em;
  text-wrap: balance;
}
.nl-hero-actions {
  display: flex; flex-wrap: wrap; gap: 10px; justify-content: center;
  margin-top: 32px;
}

/* ─────────────────────────────────────────  hero rotator  ── */
.nl-rotator { white-space: nowrap; }

/* A fresh letter lands violet, passes through nectar, settles to ink. */
.nl-ch { animation: nl-ch-in .45s both; }
.nl-ch-settled { animation: none; }
@keyframes nl-ch-in {
  0%   { color: var(--nl-violet); }
  55%  { color: var(--nl-nectar); }
  100% { color: inherit; }
}

.nl-caret {
  display: inline-block; width: 3px; height: .74em;
  margin-left: 3px; border-radius: 2px;
  background: var(--nl-violet);
  vertical-align: -.02em;
  animation: nl-caret-blink 1.05s steps(1) infinite;
}
/* Solid while deleting or typing, blinking only at rest. */
.nl-rotator[data-busy="true"] .nl-caret { animation: none; opacity: 1; }
@keyframes nl-caret-blink {
  0%, 55% { opacity: 1; }
  56%, 100% { opacity: 0; }
}

/* ─────────────────────────────────────────────────  showcase  ── */
.nl-showcase { padding: clamp(20px, 4vw, 44px) var(--nl-gutter) var(--nl-pad-y); }

.nl-panel {
  max-width: var(--nl-wrap); margin: 0 auto;
  background: #fff;
  border: 1px solid var(--nl-line);
  border-radius: 22px;
  box-shadow: 0 1px 2px rgba(16, 19, 33, .04), 0 28px 60px -34px rgba(16, 19, 33, .18);
  overflow: hidden;
}
.nl-panel-bar {
  display: flex; align-items: center; gap: 10px;
  padding: 13px 18px; border-bottom: 1px solid var(--nl-line);
}
.nl-panel-mark img { width: 20px; height: 20px; border-radius: 5px; }
.nl-panel-workspace {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 14px; font-weight: 500; color: var(--nl-ink);
}
.nl-panel-workspace svg { color: var(--nl-muted); }
.nl-panel-avatar {
  margin-left: auto; width: 26px; height: 26px; border-radius: 50%;
  background: var(--nl-violet-t); color: var(--nl-violet-d);
  display: grid; place-items: center; font-size: 12px; font-weight: 600;
}
.nl-panel-body { padding: clamp(18px, 3vw, 28px); }
.nl-panel-range {
  display: inline-flex; align-items: center; gap: 6px;
  border: 1px solid var(--nl-line); border-radius: 9px;
  padding: 6px 11px; font-size: 13px; color: var(--nl-ink-2);
}

.nl-metrics {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 8px; margin: 22px 0 0; padding: 0;
}
.nl-metric dt { font-size: 12.5px; color: var(--nl-muted); }
.nl-metric dd {
  margin: 3px 0 0;
  font-family: var(--ep-font-display, var(--nl-sans));
  font-size: clamp(1.35rem, 2.4vw, 1.8rem);
  font-weight: 600; letter-spacing: -.03em; line-height: 1.1;
}
.nl-delta { font-size: 12px; font-weight: 500; }
.nl-delta-up { color: var(--nl-green); }
.nl-delta-flat { color: var(--nl-muted); }

.nl-bigchart { width: 100%; height: clamp(140px, 20vw, 210px); margin-top: 14px; display: block; }
.nl-grid { stroke: var(--nl-line); stroke-width: 1; fill: none; }
.nl-line-a { fill: none; stroke: var(--nl-violet); stroke-width: 2.4; stroke-linecap: round; }
.nl-line-b { fill: none; stroke: var(--nl-line-2); stroke-width: 2; stroke-linecap: round; }

/* The site already carries this disclosure on illustrated figures. */
.nl-mock-note {
  max-width: var(--nl-wrap);
  margin: 14px auto 0;
  font-size: 12.5px; color: var(--nl-muted);
  text-align: center;
}

/* ──────────────────────────────────────────────────  sections  ── */
.nl-section {
  max-width: var(--nl-wrap); margin: 0 auto;
  padding: 0 var(--nl-gutter) var(--nl-pad-y);
  text-align: center;
}
.nl-display {
  margin: 0;
  font-family: var(--ep-font-display, var(--nl-sans));
  font-weight: 600;
  font-size: clamp(1.8rem, 3.8vw, 2.7rem);
  line-height: 1.08;
  letter-spacing: -.028em;
  text-wrap: balance;
}
.nl-ssub {
  max-width: 30em;
  margin: 16px auto 0;
  color: var(--nl-muted);
  font-size: 15.5px;
}

/* ─────────────────────────────────────────────────  features  ── */
.nl-fgrid {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px;
  margin-top: clamp(40px, 6vw, 64px);
  text-align: left;
}
.nl-fcard {
  background: var(--nl-surface);
  border-radius: 22px;
  padding: 24px 24px 20px;
  display: flex; flex-direction: column;
}
.nl-fcard h3 {
  margin: 0 0 6px;
  font-family: var(--ep-font-display, var(--nl-sans));
  font-size: 1.05rem; font-weight: 600; letter-spacing: -.015em;
}
.nl-fcard-sub { margin: 0 0 18px; font-size: 14px; color: var(--nl-ink-2); }

/* Pinned to the card's bottom so the four shots align however long each
 * card's sentence runs. */
.nl-fshot {
  margin-top: auto;
  background: #fff; border-radius: 16px;
  border: 1px solid var(--nl-line);
  padding: 14px 16px;
}

.nl-funnel { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.nl-funnel li {
  display: grid; grid-template-columns: 8.5em 1fr auto; align-items: center; gap: 10px;
  font-size: 12.5px; color: var(--nl-ink-2);
}
.nl-funnel i {
  display: block; height: 7px; border-radius: 999px;
  background: var(--nl-violet); opacity: .5;
}
.nl-funnel .nl-leak i { background: var(--nl-green); opacity: 1; }
.nl-funnel b { font-size: 12px; font-weight: 600; color: var(--nl-ink); }

/* Widths as classes, because the CSP drops style attributes. */
.nl-fw100 { width: 100%; }
.nl-fw64 { width: 64%; }
.nl-fw31 { width: 31%; }
.nl-fw9 { width: 9%; }
.nl-fw6 { width: 6%; }

.nl-mini-note {
  display: flex; align-items: center; gap: 7px;
  margin: 12px 0 0; font-size: 12px; color: var(--nl-muted);
}
.nl-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--nl-violet); flex: none; }
.nl-dot-mint { background: var(--nl-green); }

.nl-legend { display: flex; gap: 13px; list-style: none; margin: 0 0 8px; padding: 0; flex-wrap: wrap; }
.nl-legend li { display: flex; align-items: center; gap: 6px; font-size: 11.5px; color: var(--nl-muted); }
.nl-sw { width: 8px; height: 8px; border-radius: 2px; flex: none; }
.nl-sw-violet { background: var(--nl-violet); }
.nl-sw-mint { background: var(--nl-green); }
.nl-sw-ink { background: var(--nl-ink); }

.nl-smallchart { width: 100%; height: 96px; display: block; }
.nl-s-violet { fill: none; stroke: var(--nl-violet); stroke-width: 2.2; stroke-linecap: round; }
.nl-s-mint { fill: none; stroke: var(--nl-green); stroke-width: 2.2; stroke-linecap: round; }
.nl-s-ink { fill: none; stroke: var(--nl-ink); stroke-width: 2.2; stroke-linecap: round; opacity: .4; }

.nl-bars { display: flex; align-items: flex-end; gap: 7px; height: 92px; }
.nl-bars i {
  flex: 1; border-radius: 5px 5px 0 0;
  background: var(--nl-violet); opacity: .4;
}
.nl-bars .nl-hot { background: var(--nl-green); opacity: 1; }

/* Heights as classes, same CSP reason as the widths. */
.nl-bh34 { height: 34%; }
.nl-bh58 { height: 58%; }
.nl-bh100 { height: 100%; }
.nl-bh41 { height: 41%; }
.nl-bh26 { height: 26%; }
.nl-bh63 { height: 63%; }

.nl-shot-row { display: flex; align-items: center; gap: 16px; }
.nl-gauge { position: relative; width: 72px; flex: none; }
.nl-gauge svg { width: 72px; height: 72px; transform: rotate(-90deg); }
.nl-gtrack { fill: none; stroke: #ebebee; stroke-width: 8; }
/* 78% of the r=28 circumference (176), fixed because the figure is fixed. */
.nl-gfill {
  fill: none; stroke: var(--nl-violet); stroke-width: 8; stroke-linecap: round;
  stroke-dasharray: 137.2 176;
}
.nl-gauge b {
  position: absolute; inset: 0; display: grid; place-items: center;
  font-size: 14px; font-weight: 600;
}
.nl-kv { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; flex: 1; }
.nl-kv li { display: flex; justify-content: space-between; font-size: 12.5px; color: var(--nl-muted); }
.nl-kv b { color: var(--nl-ink); }

/* ────────────────────────────────────────────────  globe cta  ── */
.nl-globe {
  position: relative;
  padding: 0 var(--nl-gutter) var(--nl-pad-y);
  display: grid; place-items: center;
  min-height: clamp(520px, 70vw, 720px);
  overflow: hidden;
}
.nl-globe-svg {
  position: absolute; top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  width: min(720px, 130vw); height: auto;
  pointer-events: none;
}
.nl-glo { fill: none; stroke: #e7e7ec; stroke-width: 1.5; }
.nl-land { fill: #ededf1; }

.nl-globe-card {
  position: relative; z-index: 1;
  width: 100%; max-width: 480px;
  background: #fff;
  border: 1px solid var(--nl-line);
  border-radius: 22px;
  box-shadow: 0 1px 2px rgba(16, 19, 33, .04), 0 30px 60px -30px rgba(16, 19, 33, .2);
  padding: 34px 32px 30px;
  text-align: center;
}
.nl-globe-dot {
  display: block; width: 44px; height: 44px; margin: 0 auto 18px;
  border-radius: 50%; background: var(--nl-violet-t);
  position: relative;
}
.nl-globe-dot::after {
  content: ""; position: absolute; inset: 0; margin: auto;
  width: 14px; height: 14px; border-radius: 50%; background: var(--nl-violet);
}
.nl-globe-card h2 {
  margin: 0;
  font-family: var(--ep-font-display, var(--nl-sans));
  font-weight: 600;
  font-size: clamp(1.4rem, 2.6vw, 1.75rem); line-height: 1.15; letter-spacing: -.025em;
}
.nl-globe-card > p { margin: 12px 0 0; font-size: 14.5px; color: var(--nl-ink-2); }

.nl-cta-form { margin: 22px 0 0; }
.nl-field {
  display: flex; gap: 8px; justify-content: center; flex-wrap: wrap;
}
.nl-field input {
  flex: 1 1 180px; min-width: 0;
  padding: 12px 18px; border-radius: 999px;
  border: 1px solid var(--nl-line-2); background: #fff; color: var(--nl-ink);
  font: inherit; font-size: 15px;
}
.nl-field input::placeholder { color: #adaab3; }
.nl-field input:focus {
  outline: none; border-color: var(--nl-violet);
  box-shadow: 0 0 0 3px rgba(116, 135, 255, .16);
}
/* landing.mjs publishes what it detected; the ring answers it. */
.nl-field[data-kind="tiktok"] input { border-color: var(--nl-green); }
.nl-field-note { margin: 10px 0 0; min-height: 1.2em; font-size: 13px; color: #c04545; }
.nl-reassure { margin: 14px 0 0; font-size: 13px; color: var(--nl-muted); }

/* ────────────────────────────────────────────────────  foot  ── */
.nl-foot { border-top: 1px solid var(--nl-line); }
.nl-foot-in {
  max-width: var(--nl-wrap); margin: 0 auto;
  padding: 30px var(--nl-gutter);
  display: flex; flex-direction: column; align-items: center; gap: 14px;
  font-size: 13px; color: var(--nl-muted);
  text-align: center;
}
.nl-foot-brand {
  display: flex; align-items: center; gap: 8px;
  font-weight: 600; font-size: 14.5px; color: var(--nl-ink);
}
.nl-foot-brand img { width: 20px; height: 20px; border-radius: 5px; }
body.nl .footer-links {
  display: flex; gap: 14px 18px; flex-wrap: wrap; justify-content: center;
}
body.nl .footer-links a { color: var(--nl-muted); }
body.nl .footer-links a:hover { color: var(--nl-ink); }
body.nl .footer-privacy-choice {
  border: 0; padding: 0; background: none; font: inherit;
  color: var(--nl-muted); cursor: pointer;
}
body.nl .footer-privacy-choice:hover { color: var(--nl-ink); }

/* ──────────────────────────────────────────────  responsive  ── */
@media (max-width: 720px) {
  .nl-fgrid { grid-template-columns: 1fr; }
  .nl-metrics { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
}
@media (max-width: 560px) {
  body.nl { font-size: 16px; }
  .nl-funnel li { grid-template-columns: 7em 1fr auto; }
  .nl-globe-card { padding: 28px 20px 24px; }
}

@media (prefers-reduced-motion: reduce) {
  .nl-navwrap { transition: none; }
  .nl-ch { animation: none; }
  .nl-caret { display: none; }
}

/* ═══════════════════════════════════════════════════  nl-sub  ══ */
/*
 * The shelled subpages, brought into the homepage's language without
 * rewriting their markup. Two token families drive most of the old look, so
 * flipping them on body.nl-sub does most of the work: cream turns white,
 * heavy borders turn hairline, offset shadows collapse, and the accent
 * becomes the violet. What the tokens cannot reach is overridden by name
 * below, and the list is kept short on purpose: anything that needs more
 * than this deserves a real redesign of its own page.
 */
body.nl-sub {
  /* the legacy family */
  --paper: #ffffff;
  --paper-2: #fbfbfc;
  --ink: #17131f;
  --night: #101321;
  --night-soft: #1a2033;
  --blue: #7487ff;
  --line: rgba(23, 19, 31, .12);
  --muted: #8a8691;
  --radius: 16px;
  --shadow-size: 0px;

  /* the ep family */
  --ep-paper: #ffffff;
  --ep-ink: #17131f;
  --ep-line: rgba(23, 19, 31, .12);
  --ep-muted: #8a8691;
  --ep-surface: #f6f6f8;
  --ep-accent: #7487ff;
  --ep-content: 880px;
  --ep-shadow-flat: 0 1px 2px rgba(16, 19, 33, .05);
  --ep-shadow-soft: 0 24px 50px -30px rgba(16, 19, 33, .18);
  --ep-border-width: 1px;
  --ep-grid-opacity: 0;
  --ep-grain-opacity: 0;
  --ep-dither-color: transparent;
  --ep-font-sans: -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

  color-scheme: light;
  background: #fff;
  color: var(--nl-ink);
  font-family: var(--nl-sans);
  -webkit-font-smoothing: antialiased;
}

/* Breathing room: the content column, centred, with real whitespace. */
.nl-sub .nl-content { display: block; }
.nl-sub .nl-content > * { background: transparent; }
.nl-sub :is(.ep-section, .content-page section, .page-hero) {
  padding-block: clamp(56px, 8vw, 110px);
}
.nl-sub :is(.ep-wrap, .l-wrap) {
  max-width: var(--nl-wrap);
  margin-inline: auto;
  padding-inline: var(--nl-gutter);
}
.nl-sub .ep-wrap--wide { max-width: var(--nl-wrap); }
.nl-sub :is(.ep-wrap--tight, .l-wrap--tight) { max-width: var(--nl-text); }

/* Type: headings in the display face, tight and calm. */
.nl-sub :is(h1, h2, .ep-title, .l-h2) {
  font-family: var(--ep-font-display, var(--nl-sans));
  font-weight: 600;
  letter-spacing: -.028em;
  line-height: 1.08;
  text-wrap: balance;
}
.nl-sub h1, .nl-sub .ep-title { font-size: clamp(2rem, 4.4vw, 3rem); }
.nl-sub :is(h2, .l-h2) { font-size: clamp(1.5rem, 3vw, 2.1rem); }
.nl-sub :is(.ep-eyebrow) {
  color: var(--nl-violet);
  font-size: 12.5px; font-weight: 600; letter-spacing: .06em;
}
.nl-sub :is(.ep-lede, .l-lede) { color: var(--nl-ink-2); }
.nl-sub :is(.ep-body, p) { color: var(--nl-ink-2); }
.nl-sub :is(h1, h2, h3, h4, dt, th, strong, b) { color: var(--nl-ink); }

/* Buttons: everything becomes the pill. */
.nl-sub .ep-button {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 12px 22px; border-radius: 999px;
  font-size: 15px; font-weight: 500;
  background: var(--nl-surface); color: var(--nl-ink);
  border: 1px solid transparent; box-shadow: none;
  transition: background .16s ease, color .16s ease;
}
.nl-sub .ep-button:hover { background: #ededf0; }
.nl-sub .ep-button--accent { background: var(--nl-violet); color: #fff; }
.nl-sub .ep-button--accent:hover { background: var(--nl-violet-d); }

/* The five literal offset shadows the tokens cannot reach. */
.nl-sub :is(.button, .support-card, .notice-box, .docs-stack article, .issue-list article) {
  box-shadow: 0 1px 2px rgba(16, 19, 33, .05);
  border: 1px solid var(--nl-line);
  border-radius: 16px;
}
.nl-sub .button {
  border-radius: 999px;
  border-color: transparent;
  background: var(--nl-surface);
}

/* Cards and panels: hairline, soft radius, white or near-white. */
.nl-sub :is(.plan-card, .l-card, .ep-shot, .feature-list li) {
  background: var(--nl-surface);
  border: 1px solid var(--nl-line);
  border-radius: 20px;
  box-shadow: none;
}
.nl-sub .plan-card.is-recommended { border-color: var(--nl-violet); }

/* Prose links show themselves quietly. */
.nl-sub main a:not([class]) {
  color: var(--nl-violet-d);
  text-decoration: underline;
  text-decoration-color: rgba(116, 135, 255, .4);
  text-underline-offset: 2px;
}
.nl-sub main a:not([class]):hover { text-decoration-color: currentColor; }

/* The old entrance reveals never settle now that their driving tokens are
 * flipped, and a page this calm does not want an entrance dance anyway:
 * content is simply there. */
.nl-sub :is(.reveal, .l-rise) { opacity: 1 !important; transform: none !important; animation: none !important; }

/* The shared source form, out of its dark slab and into the pill. */
.nl-sub .l-source-form { max-width: 560px; margin-inline: auto; }
.nl-sub .l-source {
  display: flex; align-items: center; gap: 8px;
  background: #fff;
  border: 1px solid var(--nl-line-2);
  border-radius: 999px;
  padding: 6px 6px 6px 16px;
  box-shadow: 0 1px 2px rgba(16, 19, 33, .05);
}
.nl-sub .l-source:focus-within {
  border-color: var(--nl-violet);
  box-shadow: 0 0 0 3px rgba(116, 135, 255, .16);
}
.nl-sub .l-source-icon { display: inline-flex; color: var(--nl-muted); }
.nl-sub .l-source-icon svg { width: 18px; height: 18px; }
.nl-sub .l-source[data-kind="tiktok"] .l-source-icon { color: var(--nl-green); }
.nl-sub .l-source input {
  flex: 1; min-width: 0; border: 0; background: transparent;
  font: inherit; font-size: 15px; color: var(--nl-ink);
  padding: 8px 4px;
}
.nl-sub .l-source input::placeholder { color: #adaab3; }
.nl-sub .l-source input:focus { outline: none; }
.nl-sub .l-source-cta button {
  border: 0; cursor: pointer;
  padding: 11px 20px; border-radius: 999px;
  font: inherit; font-size: 14.5px; font-weight: 500;
  background: var(--nl-violet); color: #fff;
  transition: background .16s ease;
  white-space: nowrap;
}
.nl-sub .l-source-cta button:hover { background: var(--nl-violet-d); }
.nl-sub .l-source-cta button:disabled { opacity: .55; cursor: default; }
.nl-sub .l-source-note { min-height: 1.2em; margin: 10px 0 0; font-size: 13px; color: #c04545; text-align: center; }
.nl-sub .l-reassure { color: var(--nl-muted); font-size: 13px; }
