/* mixtape.works design tokens — generated from packages/ui/src/tokens.css.
   DO NOT EDIT. Run `pnpm --filter @mixtape/ui kit` after changing the source.

   This is the plain-CSS build, for products with no Tailwind and no build step. It is the
   same palette, the same type, the same radii and the same motion as every other product -
   that is the entire point of it. Add a colour to the source, never here, and never to a
   product's own stylesheet. */

:root {
  --font-sans: var(--font-geist-sans), ui-sans-serif, system-ui, sans-serif;
  --font-mono: var(--font-geist-mono), ui-monospace, "SF Mono", Menlo, monospace;
  --radius-sm: 8px;
  --radius-md: 12px;
  --radius-lg: 18px;
  --radius-xl: 26px;
  --ease-out-soft: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
  --animate-rise: rise 380ms cubic-bezier(0.22, 1, 0.36, 1) both;
  --animate-fade: fade 200ms cubic-bezier(0.22, 1, 0.36, 1) both;
  --animate-pop: pop 200ms cubic-bezier(0.34, 1.56, 0.64, 1) both;
  --animate-pulse-soft: pulse-soft 2.4s cubic-bezier(0.22, 1, 0.36, 1) infinite;
}
/* mixtape.works design tokens — one system, every product.

   Two layers on purpose:
   1. `@theme inline` maps Tailwind utilities onto CSS variables *by reference*, so
      `bg-canvas` resolves at paint time rather than being frozen at build time.
   2. The palettes below re-point those variables. Swap `data-theme` on <html> and the
      whole app re-themes with no re-render and no flash.

   Add a colour here, never in an app. If a product needs its own accent, override
   `--c-accent*` on a wrapper element; everything else inherits. */



/* ── Dark: the studio default and the brand's home ─────────────────────── */
:root,
:root[data-theme='dark'] {
  color-scheme: dark;

  --c-canvas: #0a0b0d;
  --c-surface: #101216;
  --c-surface-2: #171a21;
  --c-line: #23272f;
  --c-line-strong: #323843;

  --c-ink: #eceef1;
  --c-ink-muted: #a3abb6;
  --c-ink-subtle: #6d7583;

  --c-accent: #f2b544;
  --c-accent-strong: #ffc85c;
  --c-accent-ink: #0a0b0d;
  --c-accent-soft: #f2b5441f;

  --c-ok: #3ecf8e;
  --c-rec: #f0604d;
  --c-warn: #e9b949;
  --c-go: #6fbf5e;
  --c-go-strong: #5aa84a;
  --c-go-ink: #0a0b0d;
  --c-idea: #b892f6;
  --c-plan: #4cc4e0;
  --c-build: #5b8def;
  --c-review: #f0916d;
  --c-surface-sunk: #0c0d10;

  --select-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23a3abb6' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
}

/* ── Light: warm paper, not clinical white ─────────────────────────────── */
:root[data-theme='light'] {
  color-scheme: light;

  --c-canvas: #fbfaf8;
  --c-surface: #ffffff;
  --c-surface-2: #f4f2ee;
  --c-line: #e6e2db;
  --c-line-strong: #d3cec4;

  --c-ink: #17191c;
  --c-ink-muted: #565c66;
  --c-ink-subtle: #868d99;

  /* The brand orange, on paper. Two corrections, in order.
     First, the light accent used to be a dark brown-gold chosen so *white* text could sit on
     it; dark ink on a brighter amber - the relationship dark mode already has - reads better
     and looks like the brand. Then on 2026-09-16 Chris said the buttons and badges still
     looked brownish here, and the cause was hue rather than lightness: #c8871f is a
     low-chroma gold, and a filled patch of it reads as mustard. #d97706 is the same
     lightness at a purer orange, so it costs nothing - it is fractionally better as text
     (3.05:1 against paper, up from 2.90) while dark ink on it stays at 5.7:1. Small accent
     labels remain the deliberate cost of one token doing both jobs; splitting fill from text
     would mean touching 73 call sites to buy a tenth of a ratio. */
  --c-accent: #d97706;
  --c-accent-strong: #b45309;
  --c-accent-ink: #1c1405;
  --c-accent-soft: #d977061f;

  --c-ok: #17864f;
  --c-rec: #c3402c;
  --c-warn: #a67908;
  --c-go: #3f8f31;
  --c-go-strong: #2f7524;
  --c-go-ink: #ffffff;
  --c-idea: #7c4dcc;
  --c-plan: #0f7d99;
  --c-build: #2f5fd0;
  --c-review: #c2551f;
  --c-surface-sunk: #f1f2f4;

  --select-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23565c66' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
}

/* Follow the OS when the user hasn't chosen. `data-theme` always wins. */
@media (prefers-color-scheme: light) {
  :root:not([data-theme]) {
    color-scheme: light;

    --c-canvas: #fbfaf8;
    --c-surface: #ffffff;
    --c-surface-2: #f4f2ee;
    --c-line: #e6e2db;
    --c-line-strong: #d3cec4;

    --c-ink: #17191c;
    --c-ink-muted: #565c66;
    --c-ink-subtle: #868d99;

    --c-accent: #d97706;
    --c-accent-strong: #b45309;
    --c-accent-ink: #1c1405;
    --c-accent-soft: #d977061f;

    --c-ok: #17864f;
    --c-rec: #c3402c;
    --c-warn: #a67908;
    --c-go: #3f8f31;
    --c-go-strong: #2f7524;
    --c-go-ink: #ffffff;
    --c-idea: #7c4dcc;
    --c-plan: #0f7d99;
    --c-build: #2f5fd0;
    --c-review: #c2551f;
    --c-surface-sunk: #f1f2f4;

    --select-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23565c66' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  }
}

@keyframes rise {
  from {
    opacity: 0;
    transform: translateY(8px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
@keyframes fade {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}
@keyframes pop {
  from {
    opacity: 0;
    transform: scale(0.94);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
@keyframes pulse-soft {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.45;
  }
}

@layer base {
  html {
    -webkit-tap-highlight-color: transparent;
    /* The root element, not just body: any moment where the page is between renders paints
       this, and without it the browser falls back to `color-scheme` - which is dark by
       default here, so light mode flashed dark. */
    background-color: var(--c-canvas);
  }

  /* Interface scale. The middle step is the size the UI is designed at, so it sets no
     attribute at all; the other two nudge it either way. A phone gets a smaller spread
     because its layout already runs close to the screen edges.

     The root's font size, not `zoom` on the body - and the difference is not cosmetic.
     **CSS `zoom` breaks `position: fixed` on iOS**: a bar with `bottom: 0` under a zoomed
     body resolves against a scaled coordinate space and lands short of the bottom edge by
     the difference, so at 0.94 on a 932pt screen the tab bar floated about 55pt up the
     screen. It looked correct on any page long enough to scroll, because scrolling drags a
     mis-positioned fixed element back into place, which is what made it look like a
     content-height problem for weeks. The docked chat card is fixed too, and had the same
     flaw waiting.

     Every size and every spacing step in this system is already in `rem` - `--spacing` is
     .25rem, the type scale is rem throughout - so moving the root font size scales the whole
     interface with no zoom anywhere and no coordinate space to get wrong. Values written in
     px deliberately stay put: a 16px field has to stay 16px or iOS zooms the page on focus,
     and a touch target should not shrink because somebody likes small text. */
  :root[data-scale='s'] {
    font-size: 15px;
  }
  :root[data-scale='l'] {
    font-size: 17.25px;
  }

  @media (min-width: 768px) {
    :root[data-scale='s'] {
      font-size: 14.4px;
    }
    :root[data-scale='l'] {
      font-size: 18.4px;
    }
  }

  /* Colour changes are animated so the theme toggle dissolves rather than jump-cuts.
     Scoped to colour only, so transforms and animations stay crisp. */
  body,
  header,
  main,
  aside,
  nav,
  section,
  article,
  li,
  input,
  textarea,
  select,
  button {
    transition:
      background-color 380ms cubic-bezier(0.22, 1, 0.36, 1),
      border-color 380ms cubic-bezier(0.22, 1, 0.36, 1),
      color 380ms cubic-bezier(0.22, 1, 0.36, 1);
  }

  ::selection {
    background: var(--color-accent);
    color: var(--color-accent-ink);
  }

  /* Anything focusable gets the same visible ring — keyboard and switch users included. */
  :focus-visible {
    outline: 2px solid var(--color-accent);
    outline-offset: 2px;
    border-radius: var(--radius-sm);
  }

  /* Honour the OS setting. Motion is decoration in an app; it is never load-bearing.
     A marketing page opts out with `data-motion="always"` on <html>, because there the
     motion IS the product - and because older Windows machines have animations disabled
     globally, so honouring the setting there shows a stranger a dead page. Owner's standing
     decision. Scoping it here rather than trying to undo it further down is deliberate:
     `animation-duration: revert` rolls back to the user-agent value, which is 0s, so an
     "un-disable" rule would disable the animations just as thoroughly. */
  @media (prefers-reduced-motion: reduce) {
    :root:not([data-motion='always']) *,
    :root:not([data-motion='always']) *::before,
    :root:not([data-motion='always']) *::after {
      animation-duration: 0.01ms !important;
      animation-iteration-count: 1 !important;
      transition-duration: 0.01ms !important;
      scroll-behavior: auto !important;
    }
  }
}

/* The shared settings element (elements/settings.ts) paints nothing until its script has
   defined it. Hold its exact footprint meanwhile - 44px square, pulled flush with the edge -
   so the header does not shift sideways the moment the cog appears. */
mixtape-settings:not(:defined) {
  display: inline-block;
  width: 44px;
  height: 44px;
  margin-right: var(--mx-cog-edge, -0.5rem);
}

/* Native <select> anchors its arrow to the padding box, so adding padding-right only
   squeezes the label — it never moves the arrow off the border. Suppress the native
   control and draw our own chevron at a fixed inset instead. Colour comes from a
   per-theme variable because a data: URI can't read currentColor. */


/* Minimum comfortable touch target — 44px is the Apple/WCAG floor. */


/* Physical feedback on tap. Cheap, and it makes the whole app feel alive. */


/* Children rise in sequence; set --i on each child for the stagger. */


/* iOS safe areas. `viewport-fit=cover` deliberately extends the page under the notch and
   the home indicator so backgrounds reach the screen edges - these keep *content* out of
   them. Applied to the sticky header and the fixed tab bar, never to <html>, because
   padding there breaks sticky positioning. */




/* The tab bar's own version: enough to clear a home indicator, not a whole band of it.
   Four items with labels already stand well clear, and the full inset left a strip of empty
   canvas under them that read as the bar floating rather than sitting on the edge. */
/* While the keyboard is up, everything pinned to the bottom stands aside. There is nothing
   to gain from a tab bar and a usage dock squeezed between the text you are writing and the
   keyboard you are writing it with. */
:root[data-kb] [data-overlay-bar] {
  display: none;
}

/* ...and the thing you are typing into comes to the keyboard, rather than the page being
   scrolled until it happens to be visible.

   Four rounds went into scrolling that card into the right place: correcting the browser,
   then racing it, then giving up and trusting it - and it still left the field under the
   keyboard on a phone. Docking removes the question. `bottom: var(--kb)` is the top of the
   keyboard on both platforms: zero on Android, where the layout viewport already shrank,
   and the keyboard's height on iOS, where it did not.

   `--kb-bar` is the editor toolbar Android floats over the page - arrows and a Done tick -
   which no measurement includes and no attribute refuses. It is reserved *inside* the card
   rather than by lifting the card, so the strip it covers is the card's own background
   instead of the conversation showing through underneath. Zero on iOS, which draws no such
   bar for a `contenteditable`. See Keyboard.tsx. */
:root[data-kb] [data-docks-to-keyboard] {
  position: fixed;
  left: 0;
  right: 0;
  bottom: var(--kb, 0px);
  padding-bottom: var(--kb-bar, 0px);
  z-index: 45;
  border-radius: 0;
  border-left: 0;
  border-right: 0;
  max-height: 100%;
}

/* A field you can tap without the page jumping.

   iOS zooms the whole page when you focus a control whose text is under 16px, and every
   measurement taken afterwards is in a scaled viewport - which is how the chat card once
   ended up somewhere else entirely. 16px on a phone is the guard.

   The trigger is the *device*, not the window width, and tying it to a `sm:` breakpoint got
   that wrong: a phone in landscape and a tablet are both wider than any breakpoint and both
   still zoom. So ask what is pointing at the screen. A mouse gets the smaller, tidier text;
   anything you touch gets 16px, at any width. */
.tap-text {
  font-size: 13px;
}
@media (pointer: coarse) {
  .tap-text {
    font-size: 16px;
  }

  /* No compensation for the interface scale here any more. It used to be a `zoom` on the
     body, which rendered this 16px at 15.04 and let iOS zoom the page anyway; the scale is
     the root font size now and this is px, so 16 stays 16 at every setting. */
}

/* A strip that scrolls sideways in step with another one, so only one of them should show
   a scrollbar. The board's headings mirror the columns below them; two bars would be two
   controls for one axis. */
[data-no-scrollbar] {
  scrollbar-width: none;
}
[data-no-scrollbar]::-webkit-scrollbar {
  display: none;
}

/* Chrome that gets out of the way.

   Scrolling down is the one time the two bottom bars are worth less than the rows they are
   covering, so they leave - and come back the moment you scroll up, which is when you are
   looking for them. Only past a threshold, so a short nudge does not flap them. Never while
   the keyboard is up: those bars are already hidden then, and a transform on top of that is
   a second answer to a settled question.

   The header goes too, but only on a phone: on a desktop it carries the nav and there is
   screen to spare. It is `sticky`, so by the time it leaves - a hundred and twenty pixels
   down - the place it holds in the flow is already scrolled past, and nothing opens a gap
   where it was. The board's sticky column headings move up to take its place, or they would
   hang below a bar that is no longer there. */
[data-chrome-bar] {
  transition: transform 220ms var(--ease-out-soft);
}
:root[data-chrome-away] [data-chrome-bar='bottom'] {
  transform: translateY(100%);
}
@media (max-width: 767px) {
  :root[data-chrome-away] header[data-chrome-bar] {
    transform: translateY(-100%);
  }
  /* Up into the space the header left - all the way, and filling the notch itself rather
     than starting under it. Stopping at the inset leaves a band of status bar with the
     board scrolling behind it, which is what the header had been covering. */
  :root[data-chrome-away] [data-board-headings] {
    top: 0;
    padding-top: env(safe-area-inset-top);
  }
}
@media (prefers-reduced-motion: reduce) {
  [data-chrome-bar] {
    transition: none;
  }
}



/* Landscape on a notched phone puts the cutout on one side. */


/* The studio floor. Two-frame sprite animation done the pixel-art way: cross-fade nothing,
   just swap which frame is visible on a step timing function, so it reads as real frames
   rather than a smooth tween. */
@keyframes bob {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-1.5%); }
}
@keyframes frame-a {
  0%, 49.9% { opacity: 1; }
  50%, 100% { opacity: 0; }
}
@keyframes frame-b {
  0%, 49.9% { opacity: 0; }
  50%, 100% { opacity: 1; }
}
.frame-a { animation: frame-a 0.44s steps(1, end) infinite; }
.frame-b { animation: frame-b 0.44s steps(1, end) infinite; }

/* The two frames are sibling <g> elements at the same coordinates inside one SVG, so they
   already overlap - opacity alone swaps which one you see. CSS positioning does not apply
   to SVG groups, so do not reach for it here. */

/* Somebody who does not want motion still gets the colour and the bubble, which carry the
   same information - the animation is emphasis, never the only signal. */
@media (prefers-reduced-motion: reduce) {
  .frame-a, .frame-b { animation: none; }
  .frame-b { opacity: 0; }
}

/* ── No zoom-lurch when a field is focused ──────────────────────────────────────────────
 *
 * iOS Safari zooms the whole page in when you tap into a control whose text is smaller
 * than 16px, and zooms back out when you leave it. Every text field in this app is 14px,
 * so typing anywhere meant the page jumping in and out.
 *
 * The usual fix - `maximum-scale=1, user-scalable=no` in the viewport meta - takes
 * pinch-zoom away from everybody permanently to solve a typing annoyance. Giving the
 * controls the 16px Safari is asking for costs nothing and leaves zoom alone.
 *
 * Deliberately unlayered: Tailwind's utilities live in a cascade layer, and anything
 * layered loses to unlayered rules regardless of order - which is the only way to beat the
 * `text-sm` that every control here carries.
 */
@media (pointer: coarse) {
  input:not([type='checkbox']):not([type='radio']):not([type='color']):not([type='range']),
  select,
  textarea {
    font-size: 16px;
  }

  /* The pre-divided 17.05px that used to sit here is gone with the `zoom` it compensated
     for. 16px is 16px at every interface scale now. */
}

/* 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));
  --lp-header-h: 59px;
  --lp-gutter: 1.5rem;
  --lp-max: 1200px;
  /* 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-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-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%
  );
}

/* 32px 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: 32px 32px;
}

/* ── 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(34px);
  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. */
.lp-band--hero { padding-block: 0; }

/* ── 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: 29px;
  padding: 5px 13px 4px;
  box-sizing: border-box;
  line-height: 1.25;
  text-align: center;
  text-wrap: balance;
  font-size: 12px;
  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;
  /* 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: 6px;
  height: 6px;
  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: 14px 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: 14px 0 0;
  font-size: 18px;
  line-height: 1.55;
  color: var(--c-ink-muted);
  text-wrap: pretty;
}

.lp-sub {
  margin: 1.5rem 0 0;
  max-width: 720px;
  font-size: 18px;
  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: 12px;
  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: 20px 1fr;
  gap: 0.7rem;
  align-items: start;
  font-size: 18px;
  line-height: 1.5;
  color: var(--c-ink-muted);
  text-wrap: pretty;
}
.lp-bullets li::before {
  content: "";
  margin-top: 0.28em;
  width: 20px;
  height: 20px;
  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;
  left: 0;
  margin-top: 0.28em;
  width: 20px;
  height: 20px;
  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 / 13px 13px 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 / 13px 13px 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 stays inside the warm family. It was `--c-go` (green), which reads well
     enough on the dark palette and turns to olive mud against warm paper - a mixed-hue
     gradient has to be checked in BOTH themes, because the one that fails is never the one
     you designed it in. */
  background: linear-gradient(
    120deg,
    var(--c-accent-strong) 0%,
    var(--c-accent) 25%,
    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 6px 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(12px);
  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: 14px;
  font-weight: 600;
  color: var(--c-ink);
  background: var(--lp-glass);
  border: 1px solid var(--lp-hair);
  -webkit-backdrop-filter: blur(12px);
  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(12px);
  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: 14.5px;
  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; }

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

/* ── 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; margin-bottom: 1.5rem; }
.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: 20px calc(3.5rem + 20px); }

/* ── 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-right: auto; }
.lp-spotlight--flip .lp-spotlight__demo { margin-left: 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) + 24px);
  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: 12px; }
.lp-card {
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-width: 0;
  padding: 18px;
  border-radius: 16px;
  background: var(--lp-glass);
  border: 1px solid var(--lp-hair);
  -webkit-backdrop-filter: blur(12px);
  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: 16px; padding: 18px 20px; }
.lp-card__title { font-size: 17.5px; font-weight: 700; color: var(--c-ink); }
.lp-card__text { margin-top: 2px; font-size: 15.5px; 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: 100px;
  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: 22px; }
.lp-reveal.is-open { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; }
.lp-reveal__item {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 4px 10px;
  align-items: center;
  padding: 14px;
  border-radius: 14px;
  background: var(--lp-glass);
  border: 1px solid var(--lp-hair);
}
.lp-reveal__name { font-size: 14.5px; font-weight: 700; color: var(--c-ink); }
.lp-reveal__text { grid-column: 1 / -1; font-size: 13px; 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 36px; 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; }
.lp-fine {
  margin: 14px 0 0;
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: 13px;
  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; }

/* ── Three-card comparison ───────────────────────────────────────────────*/
.lp-compare {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 14px;
  max-width: 980px;
  margin-inline: auto;
}
.lp-compare--two { 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: 14px;
  right: 14px;
  padding: 3px 9px;
  border-radius: 999px;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--c-accent-ink);
  background: var(--c-accent);
}
.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: 14px;
  font-weight: 600;
  cursor: pointer;
}
.lp-seg button[aria-pressed='true'] {
  background: var(--c-surface);
  color: var(--c-ink);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
}
.lp-seg__badge {
  padding: 2px 7px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 700;
  color: var(--c-go-ink);
  background: var(--c-go);
}
.lp-was { margin-right: 0.4rem; color: var(--c-ink-subtle); text-decoration: line-through; }
.lp-plan__list { margin: 16px 0 0; padding: 16px 0 0; list-style: none; border-top: 1px solid var(--lp-hair); }
.lp-plan__fine { margin-top: 12px; font-family: var(--font-mono, ui-monospace, monospace); font-size: 12px; 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: 10px; }
.lp-faq details {
  padding: 0 18px;
  border-radius: 14px;
  background: var(--lp-glass);
  border: 1px solid var(--lp-hair);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
}
.lp-faq summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 18px 0;
  font-size: 18px;
  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: 22px;
  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 18px; font-size: 16.5px; line-height: 1.6; color: var(--c-ink-muted); text-wrap: pretty; }

/* ── Final CTA ───────────────────────────────────────────────────────────*/
.lp-final { padding-block: 56px; text-align: center; border-top: 1px solid var(--lp-hair); }
.lp-final .lp-h2 { margin-top: 0; }

/* ── 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);
  border-top: 1px solid var(--lp-hair);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
}

/* ── 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. */
.lp-demo { width: 100%; }
.lp-demo__fit {
  position: relative;
  width: 100%;
  overflow: hidden;
  border-radius: 10px;
  border: 1px solid var(--lp-hair);
  background: var(--c-surface);
}
.lp-demo__stage {
  position: absolute;
  top: 0;
  left: 0;
  transform-origin: top left;
}
/* The fake cursor. One SVG, one ripple, nothing else. */
.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.is-click { transform-origin: 4px 4px; }
.lp-cursor.is-click::after {
  content: "";
  position: absolute;
  inset: -8px;
  border-radius: 50%;
  border: 2px solid var(--demo-accent, var(--c-accent));
  animation: lp-ripple 0.55s ease-out;
}
.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 6px 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 24px color-mix(in srgb, var(--c-accent) 35%, transparent); }
  50% { box-shadow: 0 0 42px 4px 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(4px); }
  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(12px); }
  22%, 78% { opacity: 1; transform: translateY(0); }
  90%, 100% { opacity: 0; transform: translateY(-4px); }
}
@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(-8px); }
  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(6px, -5px); }
}
@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: 8px; height: 62px; 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: 6px; justify-content: center; padding: 0 12px; }
.lp-viz-chips > span {
  padding: 4px 9px;
  border-radius: 999px;
  font-size: 11.5px;
  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: 26px;
  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: 5px; width: 100%; padding-inline: 14px; }
.lp-viz-rows > span {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  padding: 4px 10px;
  border-radius: 7px;
  font-size: 11.5px;
  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: 10px; 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-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,
  .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: 14px; }
  .lp-grid__head > .lp-pill { order: 1; margin-top: 22px; 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,
  .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) {
  .lp { --lp-gutter: 28px; }
  .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: left; }
  .lp-demo { margin-inline: calc(var(--lp-gutter) * -1); width: calc(100% + var(--lp-gutter) * 2); }
}


@font-face{font-family:"Geist";src:url("/s/a369fcf5628ea2aa.woff2") format("woff2-variations");
  font-weight:100 900;font-style:normal;font-display:swap}
@font-face{font-family:"Geist Mono";src:url("/s/fba8f577f38a2bbc.woff2") format("woff2-variations");
  font-weight:100 900;font-style:normal;font-display:swap}

:root{
  --font-geist-sans:"Geist";
  --font-geist-mono:"Geist Mono";
  /* this product's names for the platform tokens */
  --ink:var(--c-ink);
  --ink-2:var(--c-ink-muted);
  --muted:var(--c-ink-subtle);
  --line:var(--c-line);
  --line-2:var(--c-line-strong);
  --bg:var(--c-canvas);
  --card:var(--c-surface);
  --sunk:var(--c-surface-sunk);
  --link:var(--c-accent);
  --tap:44px;
  --r:14px;
  --r-sm:10px;
  --sh-1:0 1px 2px rgba(0,0,0,.05),0 1px 3px rgba(0,0,0,.04);
  --sh-2:0 2px 4px rgba(0,0,0,.04),0 10px 26px rgba(0,0,0,.07);
  /* Neutral by default; a zone page overrides all four in <head>. Neutral has to mean
     *invisible* here, because this is what paints the 4px line under the top bar on every
     page that has no zone - and colour means zone, nothing else. */
  --accent:var(--c-line-strong);
  --accent-ink:var(--c-ink);
  --accent-soft:var(--c-surface-2);
  --accent-line:var(--c-line-strong);
  /* The top bar and anything else painted as a solid slab. It cannot be --ink: in dark
     mode --ink is near-white, and a white bar with white text is how a straight token
     swap breaks a header. It is the dark slab in the light theme and a lifted surface in
     the dark one, which is the same *idea* in both. */
  --chrome:var(--c-surface-2);
  --chrome-ink:var(--c-ink);
  --chrome-sub:var(--c-ink-muted);
}
/* Light. Keyed exactly the way tokens.css keys its palettes: an explicit choice in the
   settings menu (data-theme, written by the boot script) wins, and the OS setting only
   decides when there is none. Keyed on the media query alone, picking Light in the menu on
   a dark-mode laptop repaints the page light and leaves these dark - which is how a page
   ends up half in each theme. */
:root[data-theme="light"]{--chrome:#12151a;--chrome-ink:#fff;--chrome-sub:#aeb6c2}
@media (prefers-color-scheme:light){
  :root:not([data-theme]){--chrome:#12151a;--chrome-ink:#fff;--chrome-sub:#aeb6c2}
}

/* THIS PRODUCT'S ACCENT. Orange belongs to mixtape itself; every product carries its own
   (studio rule, 2026-09-16). Qr voks is violet: it sits with the two zone colours - green
   Ötletláda, blue Vezetői Üzenőfal - where the amber fought them, and it is nobody else's.
   Four tokens on :root and the whole kit follows: buttons, pills, ticks, focus rings, the
   settings menu. Measured, not picked by eye: as text 7.2:1 on the dark canvas and 5.5:1 on
   paper; the ink on the fill 6.7:1 and 5.7:1 - both past the 4.5:1 a button label needs. */
/* The dark rule repeats tokens.css's own selector list on purpose: a bare :root loses to its
   :root[data-theme="dark"], which left this page on mixtape's orange in dark mode. */
:root,:root[data-theme="dark"]{--c-accent:#a78bfa;--c-accent-strong:#c4b5fd;--c-accent-ink:#17102e;
  --c-accent-soft:#a78bfa1f}
:root[data-theme="light"]{--c-accent:#7c3aed;--c-accent-strong:#6d28d9;--c-accent-ink:#fff;
  --c-accent-soft:#7c3aed1f}
@media (prefers-color-scheme:light){
  :root:not([data-theme]){--c-accent:#7c3aed;--c-accent-strong:#6d28d9;--c-accent-ink:#fff;
    --c-accent-soft:#7c3aed1f}
}

/* THE TWO ZONE COLOURS IN THE DARK.
   ZONE_META carries the light values and injects them inline in <head> - they are literal
   hexes there because the poster, the theme-color meta and layout.test.mjs all need a real
   colour, not a var(). Dark needs its own four, and they cannot go in that inline block
   because it has no media query. So the zone is named on <html> and matched here, at a
   higher specificity than the inline :root rule, which is what lets it win. */
:root[data-theme="dark"][data-zone="otletlada"]{--accent:#3ecf8e;--accent-ink:#7fe3b6;
  --accent-soft:#10231b;--accent-line:#1f4a37}
:root[data-theme="dark"][data-zone="vezetoi-uzenofal"]{--accent:#6ea8f0;--accent-ink:#9cc4f6;
  --accent-soft:#141d2b;--accent-line:#2a3f5c}
@media (prefers-color-scheme:dark){
  :root:not([data-theme])[data-zone="otletlada"]{--accent:#3ecf8e;--accent-ink:#7fe3b6;
    --accent-soft:#10231b;--accent-line:#1f4a37}
  :root:not([data-theme])[data-zone="vezetoi-uzenofal"]{--accent:#6ea8f0;--accent-ink:#9cc4f6;
    --accent-soft:#141d2b;--accent-line:#2a3f5c}
}

*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth}
body{margin:0;background:var(--bg);color:var(--ink);
  font:16px/1.6 var(--font-sans,"Geist",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif);
  -webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}
a{color:var(--link)}
img,svg{max-width:100%}

/* ---------------------------------------------------------------- structure */
.wrap{width:100%;max-width:720px;margin:0 auto;padding:0 16px}
main.wrap{padding-bottom:64px}

header.top{background:var(--chrome);color:var(--chrome-ink);border-bottom:4px solid var(--accent);
  position:sticky;top:0;z-index:5}
header.top .wrap{display:flex;align-items:center;gap:10px;min-height:56px}
header.top a{color:var(--chrome-ink);text-decoration:none}
header.top .brand{display:inline-flex;align-items:center;gap:9px;font-weight:700;
  letter-spacing:-.01em;font-size:16px;min-height:var(--tap)}
header.top .brand .mark{display:block;width:20px;height:20px;flex:0 0 20px}
/* The shared settings menu. The top bar is a slab in its own colour, so the cog takes the
   slab's ink; the menu it opens keeps the page's palette. */
header.top{--mx-cog-color:var(--chrome-sub);--mx-cog-hover:var(--chrome-ink)}
header.top mixtape-settings{margin-left:auto}
header.top .who + mixtape-settings{margin-left:8px}
header.top .who{margin-left:auto;text-align:right;font-size:13px;line-height:1.3;
  color:var(--chrome-sub);display:inline-flex;align-items:center;gap:8px;min-height:var(--tap)}
header.top .who b{color:var(--chrome-ink);font-weight:600}
header.top .who a{text-decoration:underline;text-underline-offset:2px;color:var(--chrome-ink);
  display:inline-flex;align-items:center;min-height:var(--tap)}

/* the coloured band that puts the zone's identity on the zone's own page */
.hero{background:linear-gradient(180deg,var(--accent-soft),var(--card));
  border-bottom:1px solid var(--accent-line);padding:18px 0 20px;margin-bottom:22px}
.hero .back{display:inline-flex;align-items:center;min-height:var(--tap);
  font-size:14px;font-weight:600;color:var(--accent-ink);text-decoration:none}
.hero .back:hover{text-decoration:underline}
.hero h1{color:var(--accent-ink)}
.hero .where{font-size:13px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;
  color:var(--muted);margin:0 0 4px}

/* ------------------------------------------------------------------- type */
h1{font-size:27px;line-height:1.18;letter-spacing:-.02em;font-weight:750;margin:0 0 8px}
h2{font-size:19px;line-height:1.3;letter-spacing:-.01em;font-weight:700;margin:0 0 8px}
h3{font-size:16px;line-height:1.35;font-weight:700;margin:0 0 6px}
.lead{color:var(--ink-2);margin:0 0 18px;font-size:16px}
.sectionhead{display:flex;align-items:baseline;gap:10px;margin:30px 0 12px}
.sectionhead h2{margin:0}
.sectionhead .n{font-size:13px;color:var(--muted)}
.hint{font-size:13.5px;line-height:1.5;color:var(--muted);margin:8px 0 0}
.micro{font-size:12px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--muted)}
.empty{color:var(--muted);background:var(--card);border:1px dashed var(--line-2);
  border-radius:var(--r);padding:18px;margin:0 0 16px;font-size:15px}

/* ------------------------------------------------------------------ blocks */
.card{background:var(--card);border:1px solid var(--line);border-radius:var(--r);
  box-shadow:var(--sh-1);padding:16px;margin:0 0 12px}
.panel{background:var(--card);border:1px solid var(--line);border-radius:var(--r);
  box-shadow:var(--sh-1);padding:18px 16px;margin:0 0 18px}

.zones{display:grid;gap:12px;margin:0 0 24px}
.zone{display:block;background:var(--card);border:1px solid var(--line);
  border-left:6px solid var(--accent);border-radius:var(--r);box-shadow:var(--sh-1);
  padding:16px 16px 14px;text-decoration:none;color:inherit}
.zone:hover{box-shadow:var(--sh-2);border-color:var(--line-2);border-left-color:var(--accent)}
.zone h2{color:var(--accent-ink);display:flex;align-items:center;gap:8px}
.zone h2 .arrow{margin-left:auto;color:var(--muted);font-weight:400}
.zone p{margin:0;color:var(--ink-2);font-size:15px}
.zone .count{display:flex;flex-wrap:wrap;gap:6px;margin-top:10px}

.chip{display:inline-block;font-size:12.5px;font-weight:600;line-height:1.5;
  padding:3px 9px;border-radius:999px;background:var(--accent-soft);
  color:var(--accent-ink);border:1px solid var(--accent-line)}
.chip.plain{background:var(--c-surface-2);color:var(--ink-2);border-color:var(--line-2)}
.chip.strong{background:var(--accent);color:var(--c-canvas);border-color:var(--accent)}

/* Warn, reject and ok are platform tokens, so these three read correctly in both themes
   without a second set of hexes to keep in step. */
.notice{background:color-mix(in srgb,var(--c-warn) 12%,var(--card));
  border:1px solid color-mix(in srgb,var(--c-warn) 34%,transparent);border-left:4px solid var(--c-warn);
  color:var(--ink-2);border-radius:var(--r);padding:14px 15px;margin:0 0 18px;font-size:14.5px;line-height:1.55}
.notice strong{display:block;margin-bottom:4px;font-size:15px;color:var(--c-warn)}
.err{background:color-mix(in srgb,var(--c-rec) 12%,var(--card));
  border:1px solid color-mix(in srgb,var(--c-rec) 34%,transparent);border-left:4px solid var(--c-rec);
  color:var(--ink);border-radius:var(--r);padding:13px 15px;margin:0 0 16px;font-size:14.5px}
.ok{background:color-mix(in srgb,var(--c-ok) 12%,var(--card));
  border:1px solid color-mix(in srgb,var(--c-ok) 34%,transparent);border-left:4px solid var(--c-ok);
  color:var(--ink);border-radius:var(--r);padding:13px 15px;margin:0 0 16px;font-size:14.5px}
.err strong,.ok strong{display:block;margin-bottom:2px}
/* read-only, not broken: a locked state is information, not a warning */
.locked{background:var(--card);border:1px solid var(--line);border-left:4px solid var(--line-2);
  border-radius:var(--r);box-shadow:var(--sh-1);padding:14px 15px;margin:0 0 18px;
  font-size:14px;line-height:1.55;color:var(--ink-2)}
.locked strong{display:block;margin-bottom:3px;font-size:15px;color:var(--ink)}

/* -------------------------------------------------------------------- post */
.post .meta{display:flex;flex-wrap:wrap;align-items:center;gap:8px;font-size:13px;
  color:var(--muted);margin:0 0 8px}
.post .meta .who{font-weight:650;color:var(--ink-2)}
.post .meta .anon{background:var(--accent-soft);color:var(--accent-ink);
  border:1px solid var(--accent-line);border-radius:999px;padding:2px 9px;font-weight:650}
.post .body p{margin:0 0 10px;font-size:16px;line-height:1.6}
.post .body p:last-child{margin-bottom:0}

/* ------------------------------------------------------------------- forms */
form.compose{background:var(--card);border:1px solid var(--line);border-radius:var(--r);
  box-shadow:var(--sh-1);padding:16px;margin:0 0 22px}
label{display:block;font-weight:650;font-size:14px;margin:0 0 6px;color:var(--ink)}
textarea,input[type=text],input[type=password],input[type=email],select{
  width:100%;font:inherit;font-size:16px;min-height:var(--tap);padding:11px 12px;
  border:1px solid var(--line-2);border-radius:var(--r-sm);background:var(--card);color:var(--ink)}
textarea{min-height:120px;resize:vertical;line-height:1.55}
textarea:focus,input:focus,select:focus{outline:3px solid var(--accent-line);
  outline-offset:1px;border-color:var(--accent)}
/* The primary action is the brand amber, in every mixtape product. The zone colour is
   for button.accent, which is the vote button - colour still means zone. */
button,.btn{font:inherit;font-size:16px;font-weight:650;background:var(--c-accent);color:var(--c-accent-ink);
  border:1px solid var(--c-accent);border-radius:var(--r-sm);min-height:var(--tap);
  padding:11px 18px;cursor:pointer;text-decoration:none;display:inline-flex;
  align-items:center;justify-content:center;gap:8px;margin-top:12px}
button:hover,.btn:hover{background:var(--c-accent-strong);border-color:var(--c-accent-strong)}
button.ghost,.btn.ghost{background:var(--card);color:var(--ink);border-color:var(--line-2);font-weight:600}
button.ghost:hover,.btn.ghost:hover{background:var(--c-surface-2);border-color:var(--line-2)}
button.accent{background:var(--accent);border-color:var(--accent);color:var(--c-canvas)}
button.accent:hover{background:var(--accent-ink);border-color:var(--accent-ink)}
button.wide,.btn.wide{width:100%}

/* ------------------------------------------------------------- Ötletláda */
.budget{background:var(--card);border:1px solid var(--line);border-radius:var(--r);
  box-shadow:var(--sh-1);padding:14px 15px;margin:0 0 18px}
.budget .row{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap}
.budget .big{font-size:22px;font-weight:750;letter-spacing:-.02em;color:var(--accent-ink)}
.budget .of{font-size:13.5px;color:var(--muted)}
.budget .bar{display:block;height:8px;border-radius:999px;background:var(--c-surface-2);
  margin-top:10px;overflow:hidden}
.budget .bar i{display:block;height:100%;background:var(--accent);border-radius:999px}
.budget .note{font-size:13.5px;color:var(--muted);margin:8px 0 0}

.idea{padding:14px 16px 16px}
.idea .head{display:flex;align-items:center;gap:12px;margin-bottom:10px}
.idea .score{flex:0 0 auto;display:flex;align-items:baseline;gap:6px;
  background:var(--accent-soft);border:1px solid var(--accent-line);
  border-radius:var(--r-sm);padding:6px 11px}
.idea .score b{font-size:21px;line-height:1.1;font-weight:750;letter-spacing:-.02em;
  color:var(--accent-ink)}
.idea .score span{font-size:12.5px;color:var(--accent-ink);opacity:.8}
.idea .byline{font-size:13px;color:var(--muted);min-width:0}
.idea .byline b{display:block;font-weight:650;color:var(--ink-2);font-size:14px}
.pair{margin:0}
.pair dt{font-size:11.5px;font-weight:750;letter-spacing:.09em;text-transform:uppercase;
  color:var(--accent-ink);margin:14px 0 3px}
.pair dt:first-child{margin-top:0}
.pair dd{margin:0}
.pair dd p{margin:0 0 8px;line-height:1.6}
.pair dd p:last-child{margin-bottom:0}
.votebar{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-top:14px;
  padding-top:13px;border-top:1px solid var(--line)}
.votebar form{margin:0}
.votebar button{margin:0}
.votebar .mineinfo{font-size:13px;color:var(--muted)}
/* the logged-out / no-budget state: a sentence that says why and where from, never a
   greyed-out button that reads as a broken app */
.votebar .why{font-size:13.5px;line-height:1.5;color:var(--ink-2);margin:0}
.votebar .why b{color:var(--ink)}

/* ----------------------------------------------------------------- landing */
/*
 * THE SALES PAGE IS THE ONE PAGE HERE THAT IS NOT THE APP, and it is built on the
 * platform's landing layer (static/landing.css) rather than on the phone-first reading
 * column every other page uses. Bands, glass cards, the bento, the animated call to
 * action and the looping vignettes all come from there.
 *
 * WHAT IT DELIBERATELY DOES NOT TAKE is the half of that layer that needs JavaScript: the
 * WebGL ribbon, the cog menu and the scripted product demo. This app ships no client-side
 * JavaScript, says so on this very page in the feature list, and sells its anonymity on
 * being checkable rather than promised - so a page that quietly loaded four scripts to
 * look livelier would be making its own copy false. The kit's own first rule is that
 * everything on the page must be true, and this is what that rule costs here:
 *   - no ribbon; the gradient wash and the dot grid stay, which is exactly what the kit
 *     falls back to on a phone anyway,
 *   - no cog; the palette follows the operating system through prefers-color-scheme,
 *   - no scripted demo; the page opens a real workspace with a real database instead,
 *     which was always the stronger argument and is the one the copy already makes.
 * If the no-JavaScript line ever leaves the feature list, the ribbon and the cog can come
 * back in an afternoon - everything they need is already downloaded in static/.
 */
.lp{--lp-max:980px}
.lp-main{display:block}

/* THE CHROME IS PART OF THE HERO UNTIL YOU SCROLL.
   On every other page the top bar is a solid slab, sticky, in the flow. Here it is fixed
   and fully transparent while the page is at the top, so the ribbon and the hero run
   unbroken behind it; past 8px the kit's landing.js adds .is-scrolled and it becomes the
   same frosted glass every mixtape header uses. The transition is what makes it read as a
   fade rather than a jump-cut. */
.lp header.top{position:fixed;left:0;right:0;background:transparent;color:var(--c-ink);
  border-bottom:1px solid transparent;
  transition:background .25s ease,border-color .25s ease,
    -webkit-backdrop-filter .25s ease,backdrop-filter .25s ease}
.lp header.top a{color:var(--c-ink)}
.lp header.top.is-scrolled{background:var(--lp-glass);border-bottom-color:var(--lp-hair);
  -webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px)}
.lp header.top .wrap{gap:12px}
.lp /* The shared settings menu. The top bar is a slab in its own colour, so the cog takes the
   slab's ink; the menu it opens keeps the page's palette. */
header.top{--mx-cog-color:var(--chrome-sub);--mx-cog-hover:var(--chrome-ink)}
header.top mixtape-settings{margin-left:auto}
/* A touch device gets no blur, so the surface has to stop being translucent in the same
   breath - otherwise the page scrolls visibly through the header. */
@media (hover:none) and (pointer:coarse){
  .lp header.top.is-scrolled{-webkit-backdrop-filter:none;backdrop-filter:none;
    background:var(--c-surface)}
}
/* The chrome has to line up with the bands. Everywhere else the reading column is 820px
   and the wordmark sits on its left edge; on the sales page the bands are wider, and a
   wordmark 70px inside the content edge is the kind of thing you see before you read a
   word of the page. */
.lp header.top .wrap{max-width:var(--lp-max)}
/* Hungarian headlines are long. The kit tops out at 4.5rem, which puts this one on three
   lines with an em dash opening the last of them; 3.25rem keeps it to two. */
.lp .lp-h1{font-size:clamp(2rem,4.6vw,3.25rem)}
/* The sales-page header is fixed and out of the flow, so the kit's own clearance is
   exactly right and there is nothing to override. */
.lp .foot{width:100%;max-width:var(--lp-max);margin:0 auto;border-top:0;
  padding:22px var(--lp-gutter) 44px}

/* The two zone colours as a local accent override, which is how the design system says a
   product carries its own colour: set --c-accent* on a wrapper and inherit everything
   else. Dark first, then the light override, in the order tokens.css uses. */
.z-idea{--c-accent:#3ecf8e;--c-accent-strong:#6fe0ac;--c-accent-ink:#08120d;
  --c-accent-soft:#3ecf8e1f}
.z-manager{--c-accent:#6ea8f0;--c-accent-strong:#96c1f6;--c-accent-ink:#08101c;
  --c-accent-soft:#6ea8f01f}
:root[data-theme="light"] .z-idea{--c-accent:#157a55;--c-accent-strong:#0e5b3e;--c-accent-ink:#fff;
  --c-accent-soft:#157a551f}
:root[data-theme="light"] .z-manager{--c-accent:#2f5d9e;--c-accent-strong:#23497d;--c-accent-ink:#fff;
  --c-accent-soft:#2f5d9e1f}
@media (prefers-color-scheme:light){
  :root:not([data-theme]) .z-idea{--c-accent:#157a55;--c-accent-strong:#0e5b3e;--c-accent-ink:#fff;
    --c-accent-soft:#157a551f}
  :root:not([data-theme]) .z-manager{--c-accent:#2f5d9e;--c-accent-strong:#23497d;--c-accent-ink:#fff;
    --c-accent-soft:#2f5d9e1f}
}

/* Inside a vignette: the score column of the wall, which is the one number this product
   actually produces. Tabular figures so the rows do not jiggle as it animates. */
.viz-score{font-variant-numeric:tabular-nums;font-weight:750;color:var(--c-accent)}
.viz-row-label{overflow:hidden;text-overflow:ellipsis}

.ticks{list-style:none;margin:0;padding:0;display:grid;gap:10px}
.ticks li{position:relative;padding-left:26px;font-size:15.5px;line-height:1.55;color:var(--ink-2)}
.ticks li::before{content:"✓";position:absolute;left:0;top:0;color:var(--c-accent);font-weight:750}
.ticks li b{color:var(--ink)}
.legal{background:var(--sunk);border:1px solid var(--line-2);border-radius:var(--r);
  padding:18px;margin:22px 0 0;font-size:14.5px;line-height:1.6;color:var(--ink-2)}
.legal strong{display:block;margin-bottom:6px;color:var(--ink);font-size:15px}
.legal p{margin:0 0 10px}
.legal p:last-child{margin-bottom:0}
/* The honeypot. Off-screen rather than display:none, because some bots skip anything
   that is display:none — and hidden from assistive tech and from the tab order, so a
   person filling the form in with a screen reader never meets it. */
.hp{position:absolute!important;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);white-space:nowrap;border:0}

/* ------------------------------------------------------------------- admin */
.adminnav{display:flex;flex-wrap:wrap;gap:8px;margin:0 0 20px}
.adminnav a{display:inline-flex;align-items:center;min-height:var(--tap);padding:0 14px;
  border-radius:999px;border:1px solid var(--line-2);background:var(--card);
  color:var(--ink-2);text-decoration:none;font-size:14px;font-weight:600}
.adminnav a.on{background:var(--c-accent);border-color:var(--c-accent);color:var(--c-accent-ink)}
.rows{display:grid;gap:10px;margin:0 0 16px}
.row-card{background:var(--card);border:1px solid var(--line);border-radius:var(--r);
  box-shadow:var(--sh-1);padding:14px 15px}
.row-card .name{font-weight:650;font-size:16px}
.row-card .sub{font-size:13px;color:var(--muted);word-break:break-word}
.row-card .acts{display:flex;flex-wrap:wrap;gap:10px;align-items:flex-end;margin-top:10px}
.row-card .acts form{margin:0;display:flex;gap:8px;align-items:flex-end;flex-wrap:wrap}
.row-card .acts label{margin:0 0 4px;font-size:12px;color:var(--muted)}
.row-card .acts select{width:auto;min-width:150px}
.row-card .acts button{margin:0}
.linkbox{margin-top:10px;background:var(--sunk);border:1px solid var(--line);
  border-radius:var(--r-sm);padding:10px 11px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  font-size:12.5px;word-break:break-all;color:var(--ink-2)}
.linklist{width:100%;min-height:160px;font-family:var(--font-mono,ui-monospace,SFMono-Regular,Menlo,Consolas,monospace);
  font-size:12.5px;line-height:1.5}
code,.mono{font-family:var(--font-mono,ui-monospace,SFMono-Regular,Menlo,Consolas,monospace);font-size:13px;word-break:break-all}

footer.foot{color:var(--muted);font-size:13px;line-height:1.55;border-top:1px solid var(--line);
  padding-top:16px;margin-top:36px}
footer.foot b{color:var(--ink-2)}

/* ------------------------------------------------------------------ plakát */
/* Black on white only: this sheet has to come out of the cheapest office laser
   printer in the building. No accent colour, no grey backgrounds, no icons. */
.sheet{background:#fff;border:1px solid var(--line);border-radius:var(--r);
  box-shadow:var(--sh-1);padding:24px 16px 26px;margin:0 0 18px;text-align:center;color:#000}
.sheet h1{font-size:30px;line-height:1.1;margin:0 0 4px;color:#000}
.sheet .ws{font-size:17px;font-weight:650;line-height:1.3;margin:0 0 20px;color:#000}
.sheet .qr{display:block;width:100%;max-width:300px;height:auto;margin:0 auto 18px}
.sheet .say{font-size:16px;line-height:1.45;font-weight:600;margin:0 0 12px;color:#000}
.sheet .url{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:13px;
  word-break:break-all;margin:0;color:#000}
.sheet .zonerow{font-size:13px;line-height:1.5;margin:16px 0 0;color:#000}
.sheet .zonerow b{font-weight:650}

/* ------------------------------------------------------------------ wider */
@media (min-width:560px){
  .idea .head{align-items:flex-start}
  .row-card .acts{flex-wrap:nowrap}
}

@media (min-width:768px){
  body{font-size:17px}
  .wrap{padding:0 24px;max-width:760px}
  h1{font-size:34px}
  h2{font-size:22px}
  .lead{font-size:17.5px}
  .hero{padding:26px 0 28px;margin-bottom:28px}
  .card,.panel,form.compose{padding:20px 22px}
  .idea{padding:18px 22px 20px}
  .zone{padding:18px 20px 16px}
  .zones{gap:14px}
}
@media (min-width:1024px){
  .wrap{max-width:820px}
}

/* ---------------------------------------------------------------- nyomtatás */
/* THE POSTER IS THE PRODUCT'S FRONT DOOR, so this block is not decoration.
   A4 portrait with a 14 mm margin leaves 182 x 269 mm of paper. The QR is set to
   150 mm across, which is the number that decides whether it works on a wall: the
   rule of thumb for a phone camera is that it reads a code from about ten times its
   width, so 150 mm is ~1.5 m, and a modern phone does better than that. Everything is
   sized in mm or pt here for the same reason — px on paper is whatever the driver
   feels like. Note what CSS cannot do: the browser's own header and footer (the URL
   and the date) are a setting in the print dialog, not something a page can remove. */
@page{size:A4 portrait;margin:14mm}
@media print{
  header.top,footer.foot,form.compose,.votebar,.hero .back,.noprint{display:none!important}
  body{background:#fff;font-size:12pt}
  .card,.panel{box-shadow:none;break-inside:avoid}
  .hero{margin-bottom:12px}
  main.wrap{max-width:none;padding:0 0 0;margin:0}
  .sheet{border:0;box-shadow:none;border-radius:0;margin:0;padding:0;
    break-inside:avoid;page-break-inside:avoid}
  .sheet h1{font-size:32pt;margin-bottom:2mm}
  .sheet .ws{font-size:16pt;margin-bottom:12mm}
  .sheet .qr{width:150mm;height:150mm;max-width:none;margin:0 auto 12mm}
  .sheet .say{font-size:15pt;margin-bottom:6mm}
  .sheet .url{font-size:11pt}
  .sheet .zonerow{font-size:10pt;margin-top:8mm}
}
