/* mixtape.works landing layer — the "spiced up" half of the design system.

   `tokens.css` is the system every product shares: the palette, the type, the radii, the
   motion vocabulary. It is deliberately quiet, because most of what we build is an app
   somebody works in all day. A landing page has the opposite job - it has about four
   seconds to look like a company worth buying from - so this file adds the loud half:
   full-bleed bands, glass surfaces, an animated ribbon, bento cards with live vignettes,
   and DOM screencasts instead of video files.

   Two rules keep the two halves one system:
   1. **Every colour here comes from `--c-*`.** No landing page invents a palette. Swap
      `data-theme` and this re-themes with everything else, because there is nothing to
      re-theme separately.
   2. **It is plain CSS.** Our Next apps get it through `@mixtape/ui/landing.css`; a product
      that is a bare Node server with no build step gets the identical file out of
      https://mixtape.works/kit/. One file, one look, both worlds.

   Put a `.lp` class on the page root and everything below is in scope.

   Removing the ribbon: delete the `.lp-wave` block here, `landing/wave.js`, and the two
   markup blocks. Nothing else depends on it. */

/* ── Landing-only variables ──────────────────────────────────────────────
   Hairlines are alpha, never a flat grey: a line at 8% black picks up whatever sits
   behind it, which on a page made of gradients and glass is the difference between a
   border and a stripe. The veil is the one alternate band background. */
.lp {
  --lp-hair: rgba(255, 255, 255, 0.10);
  --lp-hair-strong: rgba(255, 255, 255, 0.16);
  --lp-veil: rgba(23, 26, 33, 0.55);
  --lp-glass: color-mix(in srgb, var(--c-surface) 65%, transparent);
  --lp-glass-up: color-mix(in srgb, var(--c-surface) 78%, transparent);
  --lp-glass-down: color-mix(in srgb, var(--c-surface) 48%, transparent);
  --lp-tile: color-mix(in srgb, var(--c-accent) 7%, var(--c-surface-2));
  /* The chosen half of a segmented switch (Yearly | Monthly). In dark the plain surface is the
     track's own colour, so the chosen one did not read as chosen (Chris, 2026-10-15): lifted. */
  --lp-seg-on: color-mix(in srgb, var(--c-ink) 18%, var(--c-surface));
  --lp-header-h: 3.6875rem;
  --lp-gutter: 1.5rem;
  --lp-max: var(--page-max, 90rem);
  /* Ribbon strength. Dark is the brand's home, so it is tuned there first - and the light
     value is lower rather than higher, which is the opposite of what the reference site
     needed. Our paper is #fbfaf8, much lighter than theirs, so the same saturated band
     reads twice as loud on it and starts competing with the copy instead of sitting behind
     it. Measured on the real page, not guessed. */
  --lp-wave-opacity: 0.34;
  --lp-dots: #d9c6a8;
  --lp-dots-blend: screen;
  --lp-dots-opacity: 0.10;
}

:root[data-theme='light'] .lp {
  --lp-hair: rgba(0, 0, 0, 0.08);
  --lp-hair-strong: rgba(0, 0, 0, 0.13);
  --lp-veil: rgba(255, 255, 255, 0.62);
  --lp-seg-on: var(--c-surface);
  --lp-wave-opacity: 0.3;
  --lp-dots: #7a5a24;
  --lp-dots-blend: multiply;
  --lp-dots-opacity: 0.09;
}

@media (prefers-color-scheme: light) {
  :root:not([data-theme]) .lp {
    --lp-hair: rgba(0, 0, 0, 0.08);
    --lp-hair-strong: rgba(0, 0, 0, 0.13);
    --lp-veil: rgba(255, 255, 255, 0.62);
    --lp-seg-on: var(--c-surface);
    --lp-wave-opacity: 0.3;
    --lp-dots: #7a5a24;
    --lp-dots-blend: multiply;
    --lp-dots-opacity: 0.09;
  }
}

/* ── Page frame ──────────────────────────────────────────────────────────
   Full-bleed bands are `width: 100vw` children of a centred container, so they push past
   the viewport edge by a scrollbar's width unless something clips. Clip at `html`, not at
   the container: the primary button's glow and the CTA band's shadow are *meant* to spill
   past their own box. */
html:has(.lp) { overflow-x: clip; }

.lp {
  position: relative;
  /* A stacking context, so the fixed background layers below can sit at z-index -1 inside
     the page and still paint behind the content rather than behind the whole document. */
  z-index: 0;
}

.lp-container {
  width: 100%;
  max-width: var(--lp-max);
  margin-inline: auto;
  padding-inline: var(--lp-gutter);
}

.lp-bleed {
  width: 100vw;
  margin-inline: calc(50% - 50vw);
  position: relative;
}

/* ── The three background layers ─────────────────────────────────────────
   All fixed, all inert, all behind the content. Never `background-attachment: fixed` on
   any of them: it forces Chrome onto main-thread scrolling for the whole document, which
   is how a page with no heavy content still scrolls like it does. */
.lp-bg {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  overflow: hidden;
}

.lp-bg__wash {
  position: absolute;
  inset: 0;
  background: linear-gradient(
    135deg,
    color-mix(in srgb, var(--c-idea) 14%, transparent) 0%,
    color-mix(in srgb, var(--c-review) 12%, transparent) 26%,
    color-mix(in srgb, var(--c-rec) 11%, transparent) 52%,
    color-mix(in srgb, var(--c-accent) 14%, transparent) 78%,
    color-mix(in srgb, var(--c-accent-strong) 12%, transparent) 100%
  );
}

/* 2rem grid of 1.5px dots. Blended rather than drawn flat, so it reads as texture in the
   light and as a faint star-field in the dark instead of as a grey mesh in both. */
.lp-bg__dots {
  position: absolute;
  inset: 0;
  opacity: var(--lp-dots-opacity);
  mix-blend-mode: var(--lp-dots-blend);
  background-image: radial-gradient(circle at 1.5px 1.5px, var(--lp-dots) 1.5px, transparent 0);
  background-size: 2rem 2rem;
}

/* ── The ribbon ──────────────────────────────────────────────────────────
   Strength lives on the wrapper's opacity, never on the canvas: the canvas fades itself in
   once (see `--drawn`) and a second opacity on the same element would fight that. */
.lp-wave {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  overflow: hidden;
  opacity: var(--lp-wave-opacity);
  /* The cheap fallback, and the only thing phones ever get. It paints during LCP, so it is
     two radial gradients and nothing else - no mask, no color-mix. */
  background:
    radial-gradient(48% 42% at 78% 12%, rgba(242, 181, 68, 0.30), transparent 70%),
    radial-gradient(40% 38% at 96% 34%, rgba(240, 96, 77, 0.20), transparent 72%);
}

.lp-wave__canvas {
  display: block;
  width: 100%;
  height: 100%;
  opacity: 0;
  transition: opacity 0.7s ease;
}

.lp-wave--drawn .lp-wave__canvas { opacity: 1; }

/* Frosted ends instead of a faded ribbon: two composited strips cost one blur each, where
   fading the geometry would mean a multi-tap read per pixel in the shader. */
.lp-wave__frost {
  position: absolute;
  top: 0;
  bottom: 0;
  -webkit-backdrop-filter: blur(2.125rem);
  backdrop-filter: blur(34px);
  pointer-events: none;
}
.lp-wave__frost--left {
  left: 0;
  width: 42vw;
  -webkit-mask-image: linear-gradient(90deg, #000 0%, rgba(0, 0, 0, 0.82) 42%, transparent 100%);
  mask-image: linear-gradient(90deg, #000 0%, rgba(0, 0, 0, 0.82) 42%, transparent 100%);
}
.lp-wave__frost--right {
  right: 0;
  width: 22vw;
  -webkit-mask-image: linear-gradient(270deg, #000 0%, rgba(0, 0, 0, 0.82) 42%, transparent 100%);
  mask-image: linear-gradient(270deg, #000 0%, rgba(0, 0, 0, 0.82) 42%, transparent 100%);
}

/* ── Bands ───────────────────────────────────────────────────────────────
   Exactly two section backgrounds, strictly alternating down the page: transparent, then
   the veil. Add or remove a section and fix the alternation - it is the page's rhythm, and
   two tinted bands in a row read as a mistake nobody can name. */
.lp-band {
  width: 100vw;
  margin-inline: calc(50% - 50vw);
  padding-block: 4.5rem;
  position: relative;
}
.lp-band--tint { background: var(--lp-veil); }
.lp-band--tight { padding-block: 3rem; }
/* The hero sets its own vertical rhythm, including the clearance for the fixed header, so
   the band around it contributes none. Without this the top of the page carries the band's
   4.5rem *and* the hero's 4rem *and* the header's height, and the headline sits a third of
   the way down the screen. */
/* Two classes, so the tablet/phone rule `.lp-band { padding-block: 3rem }` further down
   cannot win on source order: it did until 2026-09-21, and every phone got 3rem of empty
   band above the hero's own header clearance - a gap under the bar Chris spotted at once. */
.lp-band.lp-band--hero { padding-block: 0; }

/* ── Boxed tinted bands (Chris 2026-10-21: "lets keep it (for now)") ─────
   On a tablet and a desktop a tinted band no longer fills the page's width: its content sits in
   one big rounded box, the content's width, wearing the veil the band used to. A phone keeps the
   full-width band. Everything about it is this one block.
   TO REVERT to full-width tinted bands: delete this block, rebuild the kit, re-vendor
   landing.css into every product and publish them (DESIGN.md "Bands"). Nothing else depends on it.
   Two classes, so the tablet/phone `.lp-band { padding-block: 3rem }` further down cannot win. */
@media (min-width: 576px) {
  .lp-band.lp-band--tint { background: none; padding-block: 0.75rem; }
  .lp-band--tint > .lp-container {
    width: min(var(--lp-max), calc(100% - 2 * var(--lp-gutter)));
    padding-block: 3rem;
    border-radius: 24px;
    background: var(--lp-veil);
  }
  .lp-band--tint.lp-band--tight > .lp-container { padding-block: 2rem; }
  /* A closing call to action that IS the band's container (<div class="lp-container lp-final">)
     gets the box's room too: `.lp-final:first-child { padding-block: 0 }` below leaves the band
     to pad it, and a boxed band does not - its heading sat on the box's top edge (2026-10-21). */
  .lp-band--tint > .lp-container.lp-final { padding-block: 3rem; }
}
@media (min-width: 992px) {
  .lp-band.lp-band--tint { padding-block: 1rem; }
  .lp-band--tint > .lp-container { padding-block: 4rem; border-radius: 28px; }
  .lp-band--tint.lp-band--tight > .lp-container { padding-block: 2.5rem; }
  .lp-band--tint > .lp-container.lp-final { padding-block: 4rem; }
}

/* A document page - privacy, terms, refunds, help - in the landing frame: the fixed header's
   clearance, a reading measure, text from the left. Every product used to invent its own
   (Dayrule `section.page`, Storefront `.sn-doc`), and RentKeeper's did it by padding the kit's
   own .lp-container, which also pushed its landing hero 130px down on phones (2026-10-05).
   <section class="lp-doc"><div class="lp-container"> ... </div></section> */
.lp-doc { padding-block: calc(var(--lp-header-h) + 3rem) 4rem; }
.lp-doc > .lp-container { max-width: 46rem; }
/* A document reads as paragraphs and blocks with room between them; the first card or grid after the
   text gets a little more, so it does not sit on the line above it (RentKeeper's privacy page). */
.lp-doc > .lp-container > * + * { margin-top: 1rem; }
.lp-doc > .lp-container > * + :is(.lp-bento, .lp-grid, .lp-faq, .lp-card, .mx-panel) { margin-top: 2rem; }
@media (max-width: 575.98px) { .lp-doc { padding-block: calc(var(--lp-header-h) + 2rem) 3rem; } }

/* ── Type ────────────────────────────────────────────────────────────────
   One display treatment shared by the hero and every section title, so the page has a
   single voice. Geist is the system font - we do not add a second family for landing
   pages; the display weight and the negative tracking are what make it read as display. */
.lp-eyebrow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  /* min-height, not height. A two-word English eyebrow fits on one line at any width; a
     Hungarian one does not, and a fixed-height pill puts its second line outside its own
     background. The padding is what centres the text now, and every pill on a page still
     matches because they all start from the same minimum. */
  min-height: 1.8125rem;
  padding: 0.3125rem 0.8125rem 0.25rem;
  box-sizing: border-box;
  line-height: 1.25;
  text-align: center;
  text-wrap: balance;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--c-accent);
  background: var(--c-accent-soft);
  border: 1px solid color-mix(in srgb, var(--c-accent) 22%, transparent);
  border-radius: 999px;
  align-self: flex-start;
  /* As wide as its words, never as wide as its container: a grid or a stretched flex column
     would otherwise give it the whole row. Once it wraps (a Hungarian eyebrow on a phone) the
     browser still sizes the box to the full row however short the balanced lines are - the kit
     script measures the lines and narrows it (kit/pills.ts). */
  width: fit-content;
  max-width: 100%;
  justify-self: start;
  /* Its own composited layer. Sections above have fractional heights, so pills land on
     sub-pixel offsets and rasterise differently from one another - which reads as pills of
     visibly different heights. In a layer each one rasterises in integer local coordinates
     and Chrome pixel-aligns it when compositing. */
  transform: translateZ(0);
}

.lp-eyebrow__dot {
  width: 0.375rem;
  height: 0.375rem;
  border-radius: 50%;
  background: var(--c-accent);
  animation: lp-dot 2.4s ease-in-out infinite;
}

.lp-h1 {
  margin: 0;
  font-size: clamp(2.5rem, 6.2vw, 4.5rem);
  font-weight: 800;
  line-height: 1.04;
  letter-spacing: -0.035em;
  text-wrap: balance;
  color: var(--c-ink);
}
/* The second line goes on its own row on purpose, rather than being left to wrap. */
.lp-h1 span { display: block; }

.lp-h2 {
  margin: 0.875rem 0 0;
  font-size: clamp(2rem, 2.5vw, 2.5rem);
  font-weight: 700;
  line-height: 1.08;
  letter-spacing: -0.035em;
  text-wrap: balance;
  color: var(--c-ink);
}

.lp-lead {
  margin: 0.875rem 0 0;
  font-size: 1.125rem;
  line-height: 1.55;
  color: var(--c-ink-muted);
  text-wrap: pretty;
}

.lp-sub {
  margin: 1.5rem 0 0;
  max-width: 720px;
  font-size: 1.125rem;
  line-height: 1.55;
  color: color-mix(in srgb, var(--c-ink) 85%, transparent);
  text-wrap: pretty;
}

.lp-trust {
  margin: 1.5rem 0 0;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--c-ink) 52%, transparent);
}

.lp-bullets {
  margin: 1.5rem 0 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
}
.lp-bullets li {
  position: relative;
  display: grid;
  grid-template-columns: 1.25rem 1fr;
  gap: 0.7rem;
  align-items: start;
  font-size: 1.125rem;
  line-height: 1.5;
  color: var(--c-ink-muted);
  text-wrap: pretty;
}
.lp-bullets li::before {
  content: "";
  margin-top: 0.28em;
  width: 1.25rem;
  height: 1.25rem;
  border-radius: 50%;
  background: var(--c-accent-soft);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--c-accent) 25%, transparent);
}
/* The tick itself is a mask rather than an icon font or an <svg> per bullet: one data URI,
   no network, and it takes its colour from the token like everything else. */
.lp-bullets li::after {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  margin-top: 0.28em;
  width: 1.25rem;
  height: 1.25rem;
  background: var(--c-accent);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 12.5 4 4 8-8.5'/%3E%3C/svg%3E") center / 0.8125rem 0.8125rem no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 12.5 4 4 8-8.5'/%3E%3C/svg%3E") center / 0.8125rem 0.8125rem no-repeat;
}

/* ── Buttons ─────────────────────────────────────────────────────────────
   Primary and secondary share exactly one geometry. The secondary subtracts its border
   from its padding, so the two are the same height to the pixel when they sit side by
   side - which they almost always do. */
.lp-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.55rem;
  padding: 1.15rem 1.75rem;
  border-radius: 12px;
  font-size: 16px;
  font-weight: 700;
  line-height: 1;
  text-decoration: none;
  cursor: pointer;
  border: none;
}

.lp-btn--primary {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  color: var(--c-accent-ink);
  /* A → B → C → B → A, so the two ends of the gradient match and the loop has no seam.
     The middle stop is `--c-accent-shine`, a neighbour of the accent's own hue. Unset, it is
     mixtape's amber mixed toward red, which stays warm. Mixing ANY accent with red is not safe:
     Dayrule's teal went brown-olive (Chris, 2026-09-30, "the brown... im not a fan of"), as
     `--c-go` green once did against warm paper. A product sets its shine next to its accent;
     a design's accent gets one from kit/design.ts shineFor(). Check a gradient in BOTH themes,
     because the one that fails is never the one you designed it in.
     app.css `.mx-cta` is this same button for an app screen (Studio's Continue): change both. */
  background: linear-gradient(
    120deg,
    var(--c-accent-strong) 0%,
    var(--c-accent) 25%,
    var(--c-accent-shine, color-mix(in srgb, var(--c-accent) 62%, var(--c-rec))) 50%,
    var(--c-accent) 75%,
    var(--c-accent-strong) 100%
  );
  background-size: 200% 100%;
  animation: lp-flow 9s linear infinite, lp-breathe 3.6s ease-in-out infinite;
  box-shadow: 0 0 24px color-mix(in srgb, var(--c-accent) 35%, transparent);
  transition: transform 0.2s ease, box-shadow 0.3s ease;
}
.lp-btn--primary::after {
  content: "";
  position: absolute;
  inset: 0;
  /* Above the gradient, below the label. A positioned pseudo-element at z-index 0 paints
     after inline text, so the label has to be a span with a layer of its own - a bare text
     node cannot be raised above this and the sweep would wash over the words. */
  z-index: 0;
  background: linear-gradient(115deg, transparent 38%, rgba(255, 255, 255, 0.18) 50%, transparent 62%);
  transform: translateX(-130%);
  animation: lp-sheen 5.5s ease-in-out infinite;
  pointer-events: none;
}
.lp-btn--primary:hover,
.lp-btn--primary:focus-visible {
  transform: translateY(-2px);
  /* The glow animation is dropped so the stronger static shadow can win - a running
     box-shadow animation outranks a hover rule for as long as it is running. */
  animation: lp-flow 9s linear infinite;
  box-shadow:
    0 0 44px color-mix(in srgb, var(--c-accent) 60%, transparent),
    0 0 0 0.375rem color-mix(in srgb, var(--c-accent) 18%, transparent);
}
.lp-btn--primary:active { transform: translateY(0); }

.lp-btn__label {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
}
.lp-btn__arrow { animation: lp-nudge 4.5s ease-in-out infinite; }
.lp-btn--primary:hover .lp-btn__arrow,
.lp-btn--primary:focus-visible .lp-btn__arrow {
  animation: none;
  transform: translateX(3px);
  transition: transform 0.2s ease;
}

.lp-btn--ghost {
  padding: calc(1.15rem - 1px) calc(1.75rem - 1px);
  color: var(--c-ink);
  background: var(--lp-glass);
  border: 1px solid var(--lp-hair-strong);
  -webkit-backdrop-filter: blur(0.75rem);
  backdrop-filter: blur(12px);
  transition: background 0.2s ease, border-color 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease;
}
.lp-btn--ghost:hover,
.lp-btn--ghost:focus-visible {
  background: var(--lp-glass-up);
  border-color: color-mix(in srgb, var(--c-accent) 45%, transparent);
  transform: translateY(-2px);
  box-shadow: 0 10px 30px -18px var(--c-accent);
}

/* A pill, for the "reveal" affordances and the sticky heads. */
.lp-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.6rem 1.05rem;
  border-radius: 999px;
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--c-ink);
  background: var(--lp-glass);
  border: 1px solid var(--lp-hair);
  -webkit-backdrop-filter: blur(0.75rem);
  backdrop-filter: blur(12px);
  cursor: pointer;
  transition: border-color 0.2s ease, background 0.2s ease;
}
.lp-pill:hover { border-color: color-mix(in srgb, var(--c-accent) 40%, transparent); }
.lp-pill__chev { transition: transform 0.25s var(--ease-out-soft); }
.lp-pill[aria-expanded='true'] .lp-pill__chev { transform: rotate(180deg); }

/* ── Header ──────────────────────────────────────────────────────────────
   Transparent until the page moves, so the ribbon runs unbroken behind it on first paint.
   A tiny scroll watcher flips `.is-scrolled` on the element; everything else is CSS. */
.lp-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 50;
  height: var(--lp-header-h);
  display: flex;
  align-items: center;
  background: transparent;
  border-bottom: 1px solid transparent;
  transition: background 0.25s ease, border-color 0.25s ease, backdrop-filter 0.25s ease;
}
.lp-header.is-scrolled {
  background: var(--lp-glass);
  border-bottom-color: var(--lp-hair);
  -webkit-backdrop-filter: blur(0.75rem);
  backdrop-filter: blur(12px);
}
.lp-header__inner {
  position: relative;
  width: 100%;
  max-width: var(--lp-max);
  margin-inline: auto;
  padding-inline: var(--lp-gutter);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}
/* Absolutely centred on the page rather than flex-centred between the two clusters: the
   left lockup and the right actions are never the same width, so flex centring puts the
   nav visibly off the page's true centre line. */
.lp-header__nav {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 1.75rem;
}
.lp-header__nav a {
  font-size: 0.9062rem;
  font-weight: 600;
  color: var(--c-ink);
  opacity: 0.72;
  text-decoration: none;
  transition: color 0.2s ease, opacity 0.2s ease;
}
.lp-header__nav a:hover { opacity: 1; color: var(--c-accent); }
.lp-header__right { display: flex; align-items: center; gap: 0.75rem; }
/* Studio pulls the cog flush with the header edge; here something follows it. */
.lp-header__right mixtape-settings { --mx-cog-edge: 0; }
/* Two round 44px targets side by side read further apart than two glyphs should: 4px closer. */
.lp-header__right mixtape-settings + mixtape-profile { margin-inline-start: -0.25rem; }

/* Anything a header link scrolls to needs room to land under the fixed bar. */
.lp [id] { scroll-margin-top: calc(var(--lp-header-h) + 1.25rem); }

/* ── Hero ────────────────────────────────────────────────────────────────
   The gaps above and below the button row are equal on purpose; an unequal pair is the
   most common way a centred hero looks subtly wrong. */
.lp-hero {
  padding-block: calc(var(--lp-header-h) + 4rem) 4rem;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
}
.lp-hero .lp-eyebrow { align-self: center; justify-self: center; margin-bottom: 1.5rem; }
/* THE PILL IS THE HERO'S (Chris, 2026-09-28: "only the hero cta can have a pill, all other
   section's headings dont need a pill"). A section's title stands on its own; an eyebrow above
   every one of them is a row of identical badges saying nothing the title does not. Kept out
   here rather than trusted to every product's markup, so a page written before the rule - or
   by a Builder that missed it - still shows one pill, at the top. */
.lp-eyebrow:not(.lp-hero .lp-eyebrow) { display: none; }
.lp-hero__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.75rem;
  margin-top: 1.5rem;
}

/* The demo directly under the hero reads as part of it, so the gap above is small and the
   container gutter is cancelled - it sits flush with the content edge. */
.lp-hero-demo { padding-block: 1.25rem calc(3.5rem + 1.25rem); }

/* ── Split spotlight ─────────────────────────────────────────────────────
   7/12 demo, 5/12 copy, alternating sides down the page. */
.lp-spotlight {
  display: grid;
  grid-template-columns: 7fr 5fr;
  gap: 3rem;
  align-items: center;
}
.lp-spotlight--flip { grid-template-columns: 5fr 7fr; }
.lp-spotlight--flip .lp-spotlight__copy { order: -1; }
.lp-spotlight__demo { max-width: 75%; }
.lp-spotlight:not(.lp-spotlight--flip) .lp-spotlight__demo { margin-inline-end: auto; }
.lp-spotlight--flip .lp-spotlight__demo { margin-inline-start: auto; }

/* ── Sticky-head bento ───────────────────────────────────────────────────*/
.lp-grid {
  display: grid;
  grid-template-columns: 360px 1fr;
  gap: 44px;
  align-items: start;
}
.lp-grid--flip { grid-template-columns: 1fr 360px; }
.lp-grid--flip .lp-grid__head { order: 2; }
.lp-grid__head {
  position: sticky;
  top: calc(var(--lp-header-h) + 1.5rem);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}
.lp-grid__head .lp-pill { margin-top: 1.5rem; }

.lp-bento { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0.75rem; }
/* Three across on a desktop, for a set of exactly three - two to a row would leave the third
   alone on its own line. Below the breakpoint the bento's own rules apply. */
@media (min-width: 992px) {
  .lp-bento.lp-bento--3 { grid-template-columns: repeat(3, 1fr); }
}
/* Four across on a wide screen, for a list that grows (mixtape.works' products, Chris 2026-10-05):
   a last card on its own stays one column wide - never stretched across the row. */
@media (min-width: 1200px) {
  .lp-bento.lp-bento--4 { grid-template-columns: repeat(4, 1fr); }
}
.lp-card {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  min-width: 0;
  padding: 1.125rem;
  border-radius: 16px;
  background: var(--lp-glass);
  border: 1px solid var(--lp-hair);
  -webkit-backdrop-filter: blur(0.75rem);
  backdrop-filter: blur(12px);
}
.lp-card--wide { grid-column: span 2; }
.lp-card--up { background: var(--lp-glass-up); }
.lp-card--down { background: var(--lp-glass-down); }
.lp-card--row { flex-direction: row; align-items: center; gap: 1rem; padding: 1.125rem 1.25rem; }
/* The margins are set, not left to the element: a product that marks its card titles up as the
   headings they are (<h3>, which search engines read) got the browser's 1em above and below,
   and Storefront Ninja's bento had a gap under every title that Dayrule's <div>s did not
   (Chris, 2026-10-01). Same look whatever the element. */
.lp-card__title { margin: 0; font-size: 1.0938rem; font-weight: 700; color: var(--c-ink); }
.lp-card__text { margin: 2px 0 0; font-size: 0.9688rem; line-height: 1.5; color: var(--c-ink-muted); text-wrap: pretty; }

/* The tile a vignette lives in. Fixed height, clipped, tinted with the accent so the row
   of them reads as one material. */
/* Everything inside a tile sizes border-box, whatever the host page's reset says: these
   vignettes ship to products that have their own CSS and some of them do not set it. */
.lp-viz,
.lp-viz * { box-sizing: border-box; }

.lp-viz {
  height: 6.25rem;
  border-radius: 11px;
  background: var(--lp-tile);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  position: relative;
}

/* ── Reveal grid ─────────────────────────────────────────────────────────*/
.lp-reveal { display: none; margin-top: 1.375rem; }
.lp-reveal.is-open { display: grid; grid-template-columns: repeat(4, 1fr); gap: 0.75rem; }
.lp-reveal__item {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.25rem 0.625rem;
  align-items: center;
  padding: 0.875rem;
  border-radius: 14px;
  background: var(--lp-glass);
  border: 1px solid var(--lp-hair);
}
.lp-reveal__name { font-size: 0.9062rem; font-weight: 700; color: var(--c-ink); }
.lp-reveal__text { grid-column: 1 / -1; font-size: 0.8125rem; line-height: 1.45; color: var(--c-ink-muted); }

/* ── Centred section head, and the fineprint under an offer ──────────────*/
.lp-head-centre { max-width: 760px; margin: 0 auto 2.25rem; text-align: center; }
.lp-head-centre .lp-eyebrow { align-self: center; }
.lp-head-centre .lp-h2 { margin-top: 0; }
.lp-head-centre .lp-lead { margin-inline: auto; }
/* A section title written straight into its band's container, without the .lp-head-centre block
   (Hotel, 2026-10-06 - Chris: "gap issues below headings": "What it does" and "How it works" sat
   on their cards, flush left over a centred page). It reads as that block does: centred, its
   paragraph centred under it, and the block's gap before what follows. The closing CTA
   (.lp-final) and the sticky-head grid (.lp-grid__head) keep their own. */
/* A document page's title and lead too (.lp-doc): its body stays left, but its heading reads like every
   other heading on the site (Storefront Ninja mirrored this rule for its own document pages, 2026-10-09 -
   one rule for every product's privacy, terms and help pages instead). */
:is(.lp-band, .lp-doc) > .lp-container:not(.lp-final) > .lp-h2 {
  max-width: 760px;
  margin: 0 auto 2.25rem;
  text-align: center;
}
:is(.lp-band, .lp-doc) > .lp-container:not(.lp-final) > .lp-h2:has(+ :is(.lp-lead, .lp-sub)) { margin-bottom: 0; }
:is(.lp-band, .lp-doc) > .lp-container:not(.lp-final) > .lp-h2 + :is(.lp-lead, .lp-sub) {
  max-width: 760px;
  margin: 0.875rem auto 2.25rem;
  text-align: center;
}
/* A section that centres its heading centres what follows it. A list flush to the left margin
   under a centred heading reads as a stray block that lost its own heading - the list is
   centred as a block, its items still left-aligned against each other so the ticks line up. */
.lp-container:has(> .lp-head-centre) > .lp-bullets {
  width: fit-content;
  max-width: 100%;
  margin-inline: auto;
}
/* And they stand further off than a hero's do. A hero's buttons follow straight on from the
   sentence that set them up; a closing offer has said its piece - the eyebrow, the heading,
   the lead and usually the small print - and the button is the answer to all of it, so it
   gets air above it rather than reading as one more line of the paragraph (Chris, 2026-09-22). */
.lp-container:has(> .lp-head-centre) > .lp-hero__actions,
.lp-container:has(> .lp-head-centre) > .lp-actions {
  justify-content: center;
  margin-top: 3.25rem;
}
.lp-container:has(> .lp-head-centre) > .lp-fine { text-align: center; }
.lp-fine {
  margin: 0.875rem 0 0;
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: 0.8125rem;
  line-height: 1.55;
  color: var(--c-ink-muted);
}

/* A whole card that is a link. Colour and underline come off; the lift stays. */
a.lp-card {
  text-decoration: none;
  color: inherit;
  transition: border-color 0.2s ease, transform 0.2s ease;
}
a.lp-card:hover {
  border-color: color-mix(in srgb, var(--c-accent) 55%, transparent);
  transform: translateY(-2px);
}
a.lp-card .lp-card__cta { align-self: flex-start; width: auto; }

/* ── A product, in its own colour ──────────────────────────────────────────
   Where the company lists what it makes (mixtape.works, the account's product list), each
   card wears the product's accent instead of mixtape's orange: the mark, a wash in the
   corner, the hover edge and the button all say which product this is before the name does.
   The page sets --p-dark and --p-light on the card (the product's own two accents); this
   re-points --c-accent inside the card, so the kit's own rules paint with it. Same selector
   list as tokens.css, so an explicit theme choice and the OS both win the way they do there. */
.lp-product { --c-accent: var(--p-dark); }
:root[data-theme='light'] .lp-product { --c-accent: var(--p-light); }
@media (prefers-color-scheme: light) {
  :root:not([data-theme]) .lp-product { --c-accent: var(--p-light); }
}
.lp-product {
  background-image: radial-gradient(150% 130% at 0% 0%, color-mix(in srgb, var(--c-accent) 16%, transparent), transparent 72%);
}
.lp-product__head { display: flex; align-items: center; gap: 0.75rem; }
/* The bare mark in the product's accent, never on a tile - see .mx-brand svg in app.css. */
.lp-product__icon { width: 2rem; height: 2rem; flex: none; color: var(--c-accent); }
.lp-product .lp-card__title { font-size: 1.375rem; letter-spacing: -0.01em; }
/* What the product gets done, in one bold line above how it works, and what its free plan
   holds, in the product's own colour just above the button (mixtape.works, 2026-10-14). */
.lp-product__headline { margin: 0; font-size: 1.0625rem; font-weight: 600; line-height: 1.35; color: var(--c-ink); text-wrap: balance; }
.lp-product__free { margin: 0; font-size: 0.8125rem; font-weight: 600; color: var(--c-accent); }

/* The button fills with the product's colour from the left, and the arrow steps right as the
   fill reaches it (Chris, 2026-09-21). The fill is the product's LIGHT-theme accent in both
   themes: it is the deeper of the two, so the white label reads on it either way (Voks
   5.7:1, Dayrule 5.5:1), where the bright dark-theme accents would put white on pastel.
   The whole card is the link, so hovering or focusing the card is what starts it. */
.lp-product .lp-card__cta {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  color: var(--c-accent);
  border-color: color-mix(in srgb, var(--c-accent) 40%, transparent);
  transition: color 0.3s ease 0.08s, border-color 0.45s ease;
}
.lp-product .lp-card__cta::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--p-light);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 0.45s cubic-bezier(0.65, 0, 0.35, 1);
}
.lp-product__arrow { display: inline-block; transition: transform 0.2s ease; }
.lp-product:hover .lp-card__cta,
.lp-product:focus-visible .lp-card__cta { color: #fff; border-color: var(--p-light); }
.lp-product:hover .lp-card__cta::before,
.lp-product:focus-visible .lp-card__cta::before { transform: scaleX(1); }
/* The fill crosses the arrow about 0.3s in; the arrow waits for it, then steps. */
.lp-product:hover .lp-product__arrow,
.lp-product:focus-visible .lp-product__arrow { transform: translateX(0.25rem); transition: transform 0.22s ease 0.3s; }
@media (prefers-reduced-motion: reduce) {
  .lp-product .lp-card__cta,
  .lp-product .lp-card__cta::before,
  .lp-product__arrow { transition: none; }
}

/* ── Three-card comparison ───────────────────────────────────────────────*/
.lp-compare {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.875rem;
  max-width: 980px;
  margin-inline: auto;
}
.lp-compare--two { grid-template-columns: repeat(2, 1fr); max-width: 820px; }
/* Free plus three paid plans (2026-10-13: every product is free forever, with paid plans by size). Four in a row
   on a wide screen, two by two in between, one column on a phone like the others. */
.lp-compare--four { grid-template-columns: repeat(4, 1fr); max-width: 1200px; }
@media (max-width: 1180px) { .lp-compare--four { grid-template-columns: repeat(2, 1fr); max-width: 820px; } }
.lp-compare .lp-card { position: relative; height: 100%; }
/* The emphasised one. `margin-top: auto` on the CTA is what aligns the buttons across all
   three when the copy above them is different lengths. */
.lp-compare .lp-card--pick {
  background: var(--lp-glass-up);
  border-color: color-mix(in srgb, var(--c-accent) 55%, transparent);
  box-shadow: 0 10px 30px -18px var(--c-accent);
}
.lp-card__badge {
  position: absolute;
  top: 0.875rem;
  inset-inline-end: 0.875rem;
  /* The letter-spacing also follows the LAST letter, so the end side gives that much back and the
     word sits in the middle of its pill. */
  padding-block: 3px;
  padding-inline: 0.5625rem calc(0.5625rem - 0.07em);
  border-radius: 999px;
  font-size: 0.6562rem;
  font-weight: 700;
  line-height: 1.4;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--c-accent-ink);
  background: var(--c-accent);
}
/* Capitals have no descenders, so a line box centres them high. Where the browser can, the box is cut to
   the capitals and the pill pads them evenly. */
@supports (text-box: trim-both cap alphabetic) {
  .lp-card__badge { text-box: trim-both cap alphabetic; padding-block: 0.375rem; }
}
.lp-card__cta { margin-top: auto; width: 100%; }

/* ── Plan cards + segmented toggle ───────────────────────────────────────*/
.lp-seg {
  display: inline-flex;
  padding: 3px;
  border-radius: 999px;
  background: var(--lp-glass-down);
  border: 1px solid var(--lp-hair);
}
.lp-seg button {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 1.1rem;
  border: none;
  border-radius: 999px;
  background: transparent;
  color: var(--c-ink-muted);
  font-size: 0.875rem;
  font-weight: 600;
  cursor: pointer;
}
.lp-seg button[aria-pressed='true'] {
  background: var(--lp-seg-on, var(--c-surface));
  color: var(--c-ink);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
}
.lp-seg__badge {
  padding: 2px 0.4375rem;
  border-radius: 999px;
  font-size: 0.6875rem;
  font-weight: 700;
  color: var(--c-go-ink);
  background: var(--c-go);
}
.lp-was { margin-inline-end: 0.4rem; color: var(--c-ink-subtle); text-decoration: line-through; }
.lp-plan__list { margin: 1rem 0 0; padding: 1rem 0 0; list-style: none; border-top: 1px solid var(--lp-hair); }
.lp-plan__fine { margin-top: 0.75rem; font-family: var(--font-mono, ui-monospace, monospace); font-size: 0.75rem; color: var(--c-ink-subtle); }
/* One card shows one variant at a time; the wrapper's data attribute picks it. */
[data-mode='a'] .lp-var--b, [data-mode='b'] .lp-var--a { display: none; }

/* ── FAQ ─────────────────────────────────────────────────────────────────
   Native <details>. No JavaScript, keyboard and screen-reader correct for free. */
.lp-faq { max-width: 760px; margin-inline: auto; display: flex; flex-direction: column; gap: 0.625rem; }
/* The questions sit in a centred column, so their heading does too - whether a product wraps it
   in .lp-head-centre or writes it straight into the container (Chris, 2026-09-28: "the heading
   above the q&a sections should always be in the middle"). */
.lp-container:has(> .lp-faq) > :is(.lp-h2, .lp-lead, .lp-eyebrow) { text-align: center; margin-inline: auto; }
.lp-container:has(> .lp-faq) > .lp-h2 { margin-bottom: 2.25rem; }
.lp-faq details {
  padding: 0 1.125rem;
  border-radius: 14px;
  background: var(--lp-glass);
  border: 1px solid var(--lp-hair);
  -webkit-backdrop-filter: blur(0.75rem);
  backdrop-filter: blur(12px);
}
.lp-faq summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 1.125rem 0;
  font-size: 1.125rem;
  font-weight: 600;
  color: var(--c-ink);
  cursor: pointer;
  list-style: none;
}
.lp-faq summary::-webkit-details-marker { display: none; }
.lp-faq summary::after {
  content: "+";
  flex: 0 0 auto;
  font-size: 1.375rem;
  font-weight: 400;
  line-height: 1;
  color: var(--c-ink-subtle);
  transition: transform 0.25s var(--ease-out-soft);
}
.lp-faq details[open] summary::after { transform: rotate(45deg); }
.lp-faq p { margin: 0 0 1.125rem; font-size: 1.0312rem; line-height: 1.6; color: var(--c-ink-muted); text-wrap: pretty; }

/* ── Final CTA ───────────────────────────────────────────────────────────*/
.lp-final { padding-block: 3.5rem; text-align: center; }
/* The divider and the extra padding set a closing call-to-action apart from the section it
   SHARES a band with - so only when something comes before it. One that opens its own band is
   set apart by the band already; with them it showed a hairline floating a band's padding below
   the edge. That was fixed once for one markup (.lp-container > .lp-final, mixtape.works
   2026-09-21) and came straight back on Storefront Ninja, which writes the two classes on one
   element (2026-09-28) - so the rule now asks what comes before, not how it is nested. */
.lp-final:not(:first-child) { border-top: 1px solid var(--lp-hair); }
.lp-final:first-child { padding-block: 0; }
.lp-final .lp-h2 { margin-top: 0; }
/* Its sentence centred under the heading, whichever text style it wears: .lp-sub has a max-width
   and no auto margins, so in a centred closing section it hung at the left (Hotel, 2026-10-06). */
.lp-final :is(.lp-lead, .lp-sub) { max-width: 720px; margin-inline: auto; }

/* ── Footer ──────────────────────────────────────────────────────────────
   `position: relative; z-index: 2`, and `.lp` is z-index 0 with its own stacking context.
   Without the footer's own layer the fixed ribbon inside the page paints straight over it. */
.lp-footer {
  position: relative;
  z-index: 2;
  padding-block: 3rem;
  background: var(--lp-glass);
  /* No rule on top: the glass and the band above it meet without a line (Chris, 2026-10-21). */
  -webkit-backdrop-filter: blur(0.75rem);
  backdrop-filter: blur(12px);
}

/* LEGACY - the one-row footer every landing page drew before the kit's own (below). Kept only until each product
   and mixtape.works has moved to landingFooter(); then this block and .lp-footer__links can go.
   The studio line on one side, the page links on the other, wrapping on a phone. Every link a 44px target on
   touch - they were 21px tall on a 390px phone (Hotel, 2026-10-07). */
.lp-footer__row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem 1.5rem;
  font-size: 0.8125rem;
  color: var(--c-ink-subtle);
}
.lp-footer__row p { margin: 0; }
.lp-footer__links { display: flex; flex-wrap: wrap; gap: 0 1.25rem; }
.lp-footer a { color: inherit; text-underline-offset: 3px; }
.lp-footer a:hover { color: var(--c-ink); }
@media (pointer: coarse) {
  .lp-footer a { display: inline-flex; align-items: center; min-height: 44px; }
}

/* ── Landing footer ──────────────────────────────────────────────────────
   The kit's own (Chris, 2026-10-21): server/mixtape-footer.mjs landingFooter(), React LandingFooter.
   Four columns on a desktop - the product with its one-liner and "A product of mixtape.works", the header's
   links, help and legal, the newsletter - two by two on a tablet, one on a phone; below them the copyright and
   four placeholder social icons.
   Its ground follows the section above it: a fill (the tinted bands' veil) after a plain section, none right
   after a tinted band, so two tints never touch and the page never ends on two plain grounds. `lp-footer--plain`
   / `lp-footer--fill` (afterTint) override what the page says. */
.lp-footer--kit {
  padding-block: 3.5rem 2rem;
  background: var(--lp-veil);
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  font-size: 0.875rem;
  line-height: 1.5;
  color: var(--c-ink-muted);
}
:is(main:has(> .lp-band--tint:last-of-type), .lp-band--tint) + .lp-footer--kit:not(.lp-footer--fill),
.lp-footer--kit.lp-footer--plain { background: transparent; }
.lp-footer__grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: 2.25rem; }
@media (min-width: 576px) {
  .lp-footer__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 2.5rem 2rem; }
}
@media (min-width: 992px) {
  .lp-footer__grid { grid-template-columns: minmax(0, 1.45fr) minmax(0, 0.85fr) minmax(0, 0.95fr) minmax(0, 1.55fr); gap: 2.5rem; }
  .lp-footer__grid--three { grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr) minmax(0, 1fr); }
}
/* Zero specificity, so every class below sets its own margins over it. */
:where(.lp-footer--kit) p { margin: 0; }
/* The product: its mark beside its name and one-liner; the studio line under the two. */
.lp-footer__id { display: flex; align-items: center; font-size: 1rem; line-height: normal; }
/* The mark and name are the header's own lock-up (.mx-brand / .mx-logo, app.css), at the header's 1rem, not a footer copy
   of their own (Chris, 2026-10-26). The one-liner under them, from the column's own edge (Chris, 2026-10-21). */
.lp-footer__line { max-width: 22rem; margin-top: 0.75rem; }
.lp-footer__of { margin-top: 1rem; color: var(--c-ink-subtle); font-size: 0.8125rem; }
.lp-footer__of a { text-decoration: underline; }
.lp-footer__head {
  margin-bottom: 0.875rem;
  color: var(--c-ink-subtle);
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: 0.6875rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
/* Arabic is joined script: the monospace face draws it letter by letter, and spacing breaks it apart - so its
   headings take the page's own face, unspaced. */
.lp-footer--kit:lang(ar) .lp-footer__head { font-family: inherit; letter-spacing: 0; text-transform: none; }
.lp-footer__list { display: grid; gap: 0.5rem; margin: 0; padding: 0; list-style: none; }
/* On a touch screen the old footer's 44px link rows (above) spread the lists far apart (Chris, 2026-10-21: "a big gap
   between all the links"): here the rows are 2rem, touching, which keeps a fair target and a tight list. */
@media (pointer: coarse) {
  .lp-footer--kit .lp-footer__list { gap: 0; }
  .lp-footer--kit .lp-footer__list a { min-height: 2rem; }
}
.lp-footer--kit a { color: var(--c-ink-muted); text-decoration-color: color-mix(in srgb, currentColor 35%, transparent); }
.lp-footer__list a { text-decoration: none; }
.lp-footer--kit a:hover { color: var(--c-ink); }
/* The newsletter: the address and its button on one row, the mixtape box and the small print under it. */
.lp-footer__lead { margin-bottom: 0.875rem; }
.lp-footer__news { position: relative; margin: 0; }
.lp-footer__label { display: block; margin-bottom: 0.375rem; color: var(--c-ink-muted); font-size: 0.8125rem; }
.lp-footer__field { display: flex; gap: 0.5rem; }
.lp-footer__input {
  flex: 1;
  min-width: 0;
  height: 2.75rem;
  padding: 0 0.875rem;
  border: 1px solid var(--c-line-strong);
  border-radius: 10px;
  background: var(--c-surface);
  color: var(--c-ink);
  font: inherit;
  font-size: 0.9375rem;
}
.lp-footer__input:focus-visible { outline: 2px solid var(--c-accent); outline-offset: 1px; }
/* The button the field's own height, so the two read as one control. */
.lp-btn.lp-footer__go { flex: none; height: 2.75rem; min-height: 0; padding-block: 0; padding-inline: 1.125rem; font-size: 0.875rem; }
.lp-footer__check { display: flex; align-items: flex-start; gap: 0.5rem; margin-top: 0.75rem; font-size: 0.8125rem; line-height: 1.4; cursor: pointer; }
.lp-footer__check input { flex: none; width: 1rem; height: 1rem; margin: 0.1rem 0 0; accent-color: var(--c-accent); }
/* The honeypot: a field no person sees or reaches, which a form-filling bot does. */
.lp-footer__hp { position: absolute; inset-inline-start: -9999px; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.lp-footer__fine { margin-top: 0.625rem; color: var(--c-ink-subtle); font-size: 0.75rem; }
.lp-footer__bottom {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-top: 3rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--lp-hair);
  color: var(--c-ink-subtle);
  font-size: 0.8125rem;
}
/* Placeholders until the accounts exist: no links, nothing to press. */
.lp-footer__social { display: flex; gap: 1rem; color: var(--c-ink-subtle); }
.lp-footer__icon { display: inline-flex; width: 1rem; height: 1rem; }
.lp-footer__icon svg { width: 100%; height: 100%; }

/* ── Demo frame ──────────────────────────────────────────────────────────
   The scale-to-fit scaffolding every DOM screencast uses. Three elements, and which one
   carries the transform is not a matter of taste: iOS Safari fails to render a transformed
   element clipped by its own box, so the transform goes on the CHILD and a plain parent
   does the clipping.

   Never put `will-change: transform` on the stage - it rasterises at natural size and every
   glyph blurs the moment the scale goes above 1.

   The frame draws no box of its own, and a stage paints no ground: the phone, the browser
   window, whatever the demo shows, stand on the page itself (Chris, 2026-10-20, FleetFault:
   "remove that area around the two screens"). The frame still clips, so a stage keeps what it
   draws clear of its edges by more than its shadow reaches: --lp-demo-shadow, the one shadow
   for a demo's devices, fades out within about 40px at the sides and bottom (the page's
   --shadow-lift, a 60px blur, would end in a hard line there). */
.lp-demo {
  --lp-demo-shadow: 0 12px 32px rgb(0 0 0 / 0.24);
  width: 100%;
}
.lp-demo__fit {
  position: relative;
  width: 100%;
  overflow: hidden;
}
.lp-demo__stage {
  position: absolute;
  top: 0;
  left: 0;
  transform-origin: top left;
}
/* The fake cursor: an empty `<span class="lp-cursor" hidden></span>` in the stage, which demo.js
   fills with the kit's two shapes - the arrow, and a pointing finger wherever a finger is what
   would really touch the screen: over a demo's phone (demo.js sets .is-touch while the cursor's
   point is on a .lp-phone), and everywhere on a touch screen, since nobody watching on a phone
   has an arrow (Chris, 2026-10-20). Either one's point is where moveTo() puts it, and the click
   ring opens round that point. Inside a stage, so px. */
.lp-cursor {
  position: absolute;
  top: 0;
  left: 0;
  width: 24px;
  height: 24px;
  z-index: 90;
  pointer-events: none;
  filter: drop-shadow(0 3px 5px rgba(0, 0, 0, 0.42));
  transition: transform 0.45s cubic-bezier(0.5, 0.05, 0.2, 1);
}
.lp-cursor svg { position: absolute; inset: 0; width: 24px; height: 24px; overflow: visible; }
/* The theme's ink with a canvas edge, so it reads on either ground. */
.lp-cursor path { fill: var(--c-ink); stroke: var(--c-canvas); stroke-width: 1.4; stroke-linejoin: round; stroke-linecap: round; }
.lp-cursor .lp-cursor__knuckles { fill: none; }
.lp-cursor__hand { display: none; }
.lp-cursor.is-click::after {
  content: "";
  position: absolute;
  left: -8.5px;
  top: -8.5px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  border: 2px solid var(--demo-accent, var(--c-accent));
  /* Held at its faded last frame: demo.js only restarts the class, so without it the ring
     came back round the cursor after every click (FleetFault, 2026-10-20). */
  animation: lp-ripple 0.55s ease-out forwards;
}
.lp-cursor.is-touch .lp-cursor__arrow { display: none; }
.lp-cursor.is-touch .lp-cursor__hand { display: block; }
/* The fingertip (8, 2 in its box) on the point, not the box's corner. */
.lp-cursor.is-touch { margin: -2px 0 0 -8px; }
.lp-cursor.is-touch.is-click::after { left: -2px; top: -8px; }
@media (pointer: coarse) {
  .lp-cursor .lp-cursor__arrow { display: none; }
  .lp-cursor .lp-cursor__hand { display: block; }
  .lp-cursor { margin: -2px 0 0 -8px; }
  .lp-cursor.is-click::after { left: -2px; top: -8px; }
}

/* ── Demo phone ──────────────────────────────────────────────────────────
   The phone a demo shows: `<div class="lp-phone"><div class="lp-phone__screen">...`. The kit
   draws it - a thin bezel (4px, Chris 2026-10-20: "lower the border of the phone by more than
   half"), the front camera over whatever the screen shows, the demo shadow; the product places
   and sizes it (position, left, top, width, height) and fills the screen. */
.lp-phone {
  position: absolute;
  padding: 4px;
  border-radius: 44px;
  background: color-mix(in srgb, var(--c-ink) 16%, var(--c-surface));
  box-shadow: inset 0 0 0 1px var(--lp-hair-strong, var(--c-line-strong)), var(--lp-demo-shadow);
}
.lp-phone__screen {
  position: relative;
  height: 100%;
  overflow: hidden;
  border-radius: 40px;
  background: var(--c-canvas);
}
.lp-phone__screen::before {
  content: "";
  position: absolute;
  z-index: 30;
  top: 10px;
  left: 50%;
  width: 84px;
  height: 22px;
  margin-left: -42px;
  border-radius: 12px;
  background: var(--c-scrim, #050505);
}
.lp-pressed { filter: brightness(0.92); }

/* ── Vignette recipes ────────────────────────────────────────────────────
   Abstract, never screenshots: a few spans, pure CSS, looping forever. Siblings are
   staggered with NEGATIVE delays so the whole row is already mid-motion on first paint -
   a vignette that starts from zero when you scroll to it looks like a page still loading. */
@keyframes lp-dot {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--c-accent) 40%, transparent); }
  50% { box-shadow: 0 0 0 0.375rem color-mix(in srgb, var(--c-accent) 0%, transparent); }
}
@keyframes lp-flow { 0% { background-position: 200% 50%; } 100% { background-position: 0% 50%; } }
@keyframes lp-breathe {
  0%, 100% { box-shadow: 0 0 1.5rem color-mix(in srgb, var(--c-accent) 35%, transparent); }
  50% { box-shadow: 0 0 2.625rem 0.25rem color-mix(in srgb, var(--c-accent) 55%, transparent); }
}
@keyframes lp-sheen {
  0%, 62% { transform: translateX(-130%); }
  78%, 100% { transform: translateX(130%); }
}
@keyframes lp-nudge {
  0%, 78%, 100% { transform: translateX(0); }
  84% { transform: translateX(0.25rem); }
  90% { transform: translateX(0); }
  95% { transform: translateX(2px); }
}
@keyframes lp-ripple {
  from { transform: scale(0.3); opacity: 1; }
  to { transform: scale(1.6); opacity: 0; }
}
@keyframes lp-grow { from { transform: scaleY(0.25); } to { transform: scaleY(1); } }
@keyframes lp-shimmer { 0% { transform: translateX(-130%); } 60%, 100% { transform: translateX(260%); } }
@keyframes lp-slide-in {
  0%, 12% { opacity: 0; transform: translateY(0.75rem); }
  22%, 78% { opacity: 1; transform: translateY(0); }
  90%, 100% { opacity: 0; transform: translateY(-0.25rem); }
}
@keyframes lp-walk {
  0%, 13% { background: var(--c-accent-soft); color: var(--c-accent); }
  19%, 100% { background: transparent; color: var(--c-ink-subtle); }
}
@keyframes lp-cycle {
  0%, 26% { opacity: 1; transform: translateY(0); }
  33%, 93% { opacity: 0; transform: translateY(-0.5rem); }
  100% { opacity: 1; transform: translateY(0); }
}
@keyframes lp-blip {
  0%, 100% { transform: scale(1); opacity: 1; }
  50% { transform: scale(1.7); opacity: 0.45; }
}
@keyframes lp-marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }
@keyframes lp-hop {
  0%, 80%, 100% { transform: translate(0, 0); }
  85% { transform: translate(0.375rem, -0.3125rem); }
}
@keyframes lp-type { from { transform: scaleX(0.86); } to { transform: scaleX(1); } }
@keyframes lp-confirm {
  0%, 38% { opacity: 0; transform: scale(0.6); }
  55%, 96% { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(0.6); }
}

/* The parts the vignettes are built from. */
.lp-viz-bar { border-radius: 3px; background: color-mix(in srgb, var(--c-ink) 22%, transparent); }
/* `width` is not optional. The bars are `flex: 1` with no basis, so inside a tile that
   centres its child on the main axis the row collapses to zero and the tile renders empty -
   which looks exactly like a vignette nobody wrote, not like a bug. */
.lp-viz-bars { display: flex; align-items: flex-end; gap: 0.5rem; height: 3.875rem; width: 100%; max-width: 260px; }
.lp-viz-bars > i {
  flex: 1;
  border-radius: 5px 5px 2px 2px;
  transform-origin: bottom;
  background: color-mix(in srgb, var(--c-accent) 45%, var(--c-surface-2));
  animation: lp-grow 2.4s ease infinite alternate;
}
.lp-viz-bars > i:nth-child(1) { height: 44%; animation-delay: -0.2s; }
.lp-viz-bars > i:nth-child(2) { height: 62%; animation-delay: -0.7s; }
.lp-viz-bars > i:nth-child(3) { height: 50%; animation-delay: -1.1s; }
.lp-viz-bars > i:nth-child(4) { height: 78%; animation-delay: -1.5s; }
.lp-viz-bars > i:nth-child(5) { height: 96%; background: var(--c-accent); animation-delay: -1.9s; }

.lp-viz-chips { display: flex; flex-wrap: wrap; gap: 0.375rem; justify-content: center; padding: 0 0.75rem; }
.lp-viz-chips > span {
  padding: 0.25rem 0.5625rem;
  border-radius: 999px;
  font-size: 0.7188rem;
  font-weight: 600;
  color: var(--c-ink-subtle);
  animation: lp-walk 6s linear infinite;
}
.lp-viz-chips > span:nth-child(2) { animation-delay: -1s; }
.lp-viz-chips > span:nth-child(3) { animation-delay: -2s; }
.lp-viz-chips > span:nth-child(4) { animation-delay: -3s; }
.lp-viz-chips > span:nth-child(5) { animation-delay: -4s; }
.lp-viz-chips > span:nth-child(6) { animation-delay: -5s; }

.lp-viz-cycle { position: relative; width: 100%; height: 100%; }
.lp-viz-cycle > span {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.625rem;
  font-weight: 700;
  color: var(--c-ink);
  animation: lp-cycle 6s ease infinite;
}
.lp-viz-cycle > span:nth-child(2) { animation-delay: -4s; }
.lp-viz-cycle > span:nth-child(3) { animation-delay: -2s; }

.lp-viz-rows { display: flex; flex-direction: column; gap: 0.3125rem; width: 100%; padding-inline: 0.875rem; }
.lp-viz-rows > span {
  display: flex;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.25rem 0.625rem;
  border-radius: 7px;
  font-size: 0.7188rem;
  font-weight: 600;
  white-space: nowrap;
  color: var(--c-ink-muted);
  background: var(--c-surface);
  border: 1px solid var(--lp-hair);
}
.lp-viz-rows > span.is-new { animation: lp-slide-in 4.5s ease infinite; }

.lp-viz-shimmer { position: relative; overflow: hidden; }
.lp-viz-shimmer::after {
  content: "";
  position: absolute;
  inset: 0;
  width: 40%;
  background: linear-gradient(100deg, transparent, color-mix(in srgb, var(--c-accent) 30%, transparent), transparent);
  animation: lp-shimmer 3.2s ease-in-out infinite;
}

.lp-viz-marquee { width: max-content; display: flex; gap: 0.625rem; animation: lp-marquee 32s linear infinite; }
.lp-viz-marquee--slow { animation-duration: 40s; animation-direction: reverse; }
.lp-viz:hover .lp-viz-marquee { animation-play-state: paused; }

.lp-blip { width: 8px; height: 8px; border-radius: 50%; background: var(--c-accent); flex: 0 0 auto; animation: lp-blip 2.4s ease infinite; }

/* ── Responsive ──────────────────────────────────────────────────────────
   One real breakpoint. Below it: every split stacks, sticky heads go static, and the three
   expensive effects - the ribbon, backdrop blur and header translucency - all switch off.
   Blur is not a taste call on a phone: it breaks fixed positioning during touch scroll and
   costs GPU the device does not have. */
@media (max-width: 991.98px) {
  .lp-wave { display: none; }
  .lp-band { padding-block: 3rem; }
  .lp-spotlight,
  .lp-spotlight--flip,
  .lp-grid,
  .lp-grid--flip { grid-template-columns: 1fr; gap: 2rem; }
  .lp-spotlight__demo,
  .lp-spotlight--flip .lp-spotlight__demo { max-width: 100%; margin-inline: 0; }
  .lp-spotlight--flip .lp-spotlight__copy { order: 0; }
  .lp-grid__head { position: static; }
  .lp-header__nav { display: none; }
  .lp-compare,
  .lp-compare--two,
  .lp-compare--four,
  .lp-reveal.is-open { grid-template-columns: 1fr; max-width: 460px; margin-inline: auto; }

  /* Kill every glass surface at once. */
  .lp-header.is-scrolled,
  .lp-card,
  .lp-reveal__item,
  .lp-faq details,
  .lp-btn--ghost,
  .lp-pill,
  .lp-footer:not(.lp-footer--kit),
  .lp-seg { -webkit-backdrop-filter: none; backdrop-filter: none; background: var(--c-surface); }
  .lp-card--down { background: var(--c-surface); }
  .lp-card--up { background: var(--c-surface-2); }

  /* When the bento grid stacks, a button inside the sticky head would land ABOVE the cards
     it is meant to follow. `display: contents` promotes the head's children to grid items,
     so the button can be ordered below the bento on its own. The head's gutters move onto
     the children, because a `display: contents` box has none. */
  .lp-grid__head { display: contents; }
  .lp-grid { row-gap: 0; }
  .lp-grid__head > * { margin-bottom: 0.875rem; }
  .lp-grid__head > .lp-pill { order: 1; margin-top: 1.375rem; margin-bottom: 0; align-self: start; }
  .lp-bento { margin-bottom: 0; }
}

/* A touch device of any width. Dropping the blur is not enough on its own: a 65%
   translucent surface with nothing blurring behind it is a window, and the page scrolls
   visibly through the header. Whatever turns the blur off has to make the surface opaque
   in the same breath. */
@media (hover: none) and (pointer: coarse) {
  .lp-card,
  .lp-reveal__item,
  .lp-faq details,
  .lp-btn--ghost,
  .lp-pill,
  .lp-seg,
  .lp-footer:not(.lp-footer--kit),
  .lp-header.is-scrolled {
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    background: var(--c-surface);
  }
  .lp-card--down { background: var(--c-surface); }
  .lp-card--up { background: var(--c-surface-2); }
}

@media (max-width: 575.98px) {
  /* A phone needs the header's clearance and a breath, not a desktop's 4rem of air. */
  .lp-hero { padding-block: calc(var(--lp-header-h) + 2.25rem) 3rem; }
  .lp { --lp-gutter: 1.75rem; }
  .lp-bento { grid-template-columns: 1fr; }
  .lp-card--wide { grid-column: auto; }
  .lp-grid__head,
  .lp-spotlight__copy { text-align: center; }
  .lp-grid__head .lp-eyebrow,
  .lp-spotlight__copy .lp-eyebrow { align-self: center; }
  /* The list centres as a block; the lines inside it stay left-aligned, because centred
     multi-line body copy is much harder to read than people expect. */
  .lp-bullets { display: inline-flex; text-align: start; }
  .lp-demo { margin-inline: calc(var(--lp-gutter) * -1); width: calc(100% + var(--lp-gutter) * 2); }
}

/* ── Right to left ────────────────────────────────────────────────────────────────────────── */
/* Arrows point the way the page reads, and step that way on hover. */
/* `scale`, not `transform`: it composes with the arrows' own nudge, and turns the nudge the same
   way - an arrow that points left steps left. */
[dir='rtl'] :is(.lp-product__arrow, .lp-btn__arrow) { display: inline-block; scale: -1 1; }
/* The scripted demos are a picture of a screen, with a cursor at fixed points: drawn as they are. */
[dir='rtl'] .lp-demo__stage { direction: ltr; }


/* ── "Made with mixtape.works" ────────────────────────────────────────────
   The one line every page a FREE plan shows the public carries (a shop, a board, a guest page, a report form),
   gone on any paid plan (2026-10-13). Quiet on purpose: it is a thank-you, not an ad. */
.mx-made-with {
  display: block;
  margin: 1.5rem auto 0.75rem;
  text-align: center;
  font-size: 0.75rem;
  color: var(--c-ink-subtle);
}
.mx-made-with a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
.mx-made-with a:hover { color: var(--c-ink); }

/* ── The pricing band drawn by server/mixtape-pricing.mjs (2026-10-13) ───────────────────────────────────────
   Free plus paid plans by size, yearly first. Every card is a column, so the buttons line up at the foot. */
.mx-plan__seg { display: flex; justify-content: center; margin-bottom: 1.5rem; }
.mx-plan { display: flex; flex-direction: column; min-width: 0; }
.mx-plan__size { margin: 0.25rem 0 0; color: var(--c-ink-muted); font-size: 0.875rem; }
.mx-plan__price { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 6px; margin: 0.625rem 0 0; color: var(--c-ink); line-height: 1.1; font-variant-numeric: tabular-nums; }
.mx-plan__price strong { font-size: 2.125rem; font-weight: 800; letter-spacing: -0.03em; }
.mx-plan__price .lp-was { margin-inline-end: 0; font-size: 1.25rem; font-weight: 600; }
.mx-plan__per { font-size: 0.9375rem; font-weight: 500; color: var(--c-ink-muted); }
/* Close under the price it explains (Chris, 2026-10-21: the gap was too big, then "even closer, like 5px up"). Free has no billing to explain, but
   keeps the line's height (--none), so every card's list starts at the same height. */
.mx-plan__billed { display: flex; flex-wrap: wrap; align-items: center; gap: 0.375rem; margin: calc(0.125rem - 5px) 0 0; color: var(--c-ink-muted); font-size: 0.875rem; }
.mx-plan__billed--none { visibility: hidden; }
.mx-plan .lp-plan__list { display: grid; gap: 0.5rem; margin-bottom: 1.25rem; }
.mx-plan .lp-plan__list li { position: relative; padding-inline-start: 1.375rem; color: var(--c-ink); font-size: 0.9375rem; }
.mx-plan .lp-plan__list li::before { content: '✓'; position: absolute; inset-inline-start: 0; color: var(--c-accent); font-weight: 700; }
.mx-plan .lp-card__cta { margin-top: auto; }
.mx-plan .lp-card__cta:disabled { opacity: 0.55; cursor: not-allowed; animation: none; box-shadow: none; }
.mx-plan__fine { max-width: 46rem; margin-inline: auto; text-align: center; }
/* The "Launch deal" badge has its own row above the title on every card (empty on Free, hidden while Monthly is
   chosen), so the titles and prices line up across the cards whichever is shown. */
/* The row keeps its height on the Free card too (no badge), so the titles line up; the badge is
   centred in it at its own height, never stretched to fill it (a stretched pill put its word at the top). */
.mx-plan__flag { display: flex; align-items: center; min-height: 1.5rem; margin-bottom: 0.5rem; }
.mx-plan__flag .lp-card__badge { position: static; }
