/*
 * Imaginando — new marketing site stylesheet (refs #343).
 *
 * Single file for the POC; would be split per-section once we know which
 * tokens and components survive the migration. Combines the homepage's
 * page-specific styles with the shared site-theme tokens from the design
 * handoff.    
 */

/* ---------- Tokens (dark default) ----------   #c8ff3a */
:root,
:root[data-theme="dark"] {
  --bg: #0a0a0c;
  --bg-2: #111116;
  --fg: #f4f1ea;
  --fg-dim: rgba(244, 241, 234, 0.62);
  --fg-faint: rgba(244, 241, 234, 0.38);
  --line: rgba(244, 241, 234, 0.12);
  --line-strong: rgba(244, 241, 234, 0.22);
  --accent: #c7ff3b;
  --accent-ink: #0a0a0c;
  --card: #0e0e12;
  --card-2: #161620;
  --hatch: rgba(255, 255, 255, 0.04);
  --hatch-strong: rgba(255, 255, 255, 0.06);
  /* Track ring behind the VS knob/dial value arc (shaders, LFO, modulation).
     Near-white on the dark UI; overridden dark in light mode so it stays
     visible on the white knob panel. Painted via .sh-dtrack in vs.css. */
  --dial-track: rgba(244, 241, 234, 0.15);

  --display: "Space Grotesk", ui-sans-serif, system-ui, sans-serif;
  --body: "Inter Tight", ui-sans-serif, system-ui, sans-serif;
  --mono: "JetBrains Mono", ui-monospace, monospace;
  --serif: "Instrument Serif", "Times New Roman", serif;

  /* -------- Tunable card-image shadows --------
     `--product-shadow`  → box-shadow on .product-card .visual .ph-img
                           (every product card on /products)
     `--bundle-shadow`   → filter: drop-shadow() on the FRMS bundle's
                           .ph-img on /bundles (the only bundle
                           image that ships without a baked-in shadow)
     Length values use cqw, which resolves against the .ph container
     so the shadow scales with the card's actual width. The light-mode
     overrides are at the bottom of this block. */
  --product-shadow: 0 10cqw 5cqw -7cqw rgba(0, 0, 0, 0.74);
  --bundle-shadow:  0;

  /* -------- Tunable hover glow --------
     The accent "sweep" that paints through the glow-background.png
     mask when you hover a card.
       `--product-glow-opacity` / `--product-glow-color`  → /products
       `--bundle-glow-opacity`  / `--bundle-glow-color`   → /bundles
     Opacity controls intensity; color controls the tint. Default
     tint is `var(--accent)` so the glow tracks whatever accent the
     active theme defines — set to a literal colour (or another var)
     to break that link. Light-mode overrides at the bottom of this
     block. */
  --product-glow-opacity: 0.2;
  --bundle-glow-opacity:  0.2;
  --product-glow-color: var(--accent);
  --bundle-glow-color:  var(--accent);

  /* -------- Tunable at-rest ("normal") glow --------
     The glow-background.png shape painted on every `.ph` at rest,
     before any hover. Implemented as a masked colour layer on
     `.ph::before` — `--*-glow-base-color` is the paint, and
     `--*-glow-base-opacity` is its native CSS opacity (0–1, no
     gymnastics). Picking a colour with contrast against
     `--*-glow-bg` is what makes the glow legible in light mode.
       `--product-glow-base-opacity` / `--product-glow-base-color` → /products
       `--bundle-glow-base-opacity`  / `--bundle-glow-base-color`  → /bundles */
  --product-glow-base-opacity: 0.1;
  --bundle-glow-base-opacity:  0.1;
  --product-glow-base-color: #ffffff;
  --bundle-glow-base-color:  #ffffff;

  /* -------- Tunable at-rest background tint --------
     The solid colour painted *under* the masked texture on every
     `.ph`. The at-rest texture and the hover sweep both paint over
     this — picking a faint off-white / dark-grey here is what gives
     them contrast to read against.
       `--product-glow-bg` → /products card visual background
       `--bundle-glow-bg`  → /bundles card visual background */
  --product-glow-bg: #000;
  --bundle-glow-bg:  #000;
}

html[data-theme="light"],
:root[data-theme="light"] {
  --bg: #f4f1ea !important;
  --bg-2: #ebe7dd !important;
  --fg: #0a0a0c !important;
  --fg-dim: rgba(10, 10, 12, 0.66) !important;
  /* 0.42 gave only 2.83:1 on the light bg — below WCAG AA (4.5) and even the
     3:1 UI floor, so the many small mono labels (eyebrows, prices, tags, spec
     keys) read as washed out in light mode. 0.58 → 4.71:1 on bg / 4.88:1 on
     card (AA-pass for small text) while staying below --fg-dim (6.24:1) so the
     dim/faint hierarchy holds. Dark mode is unchanged. */
  --fg-faint: rgba(10, 10, 12, 0.58) !important;
  --line: rgba(10, 10, 12, 0.12) !important;
  --line-strong: rgba(10, 10, 12, 0.22) !important;
  --accent: #7fb800 !important;
  --accent-ink: #0a0a0c !important;
  --card: #ffffff !important;
  --card-2: #ebe7dd !important;
  --hatch: rgba(0, 0, 0, 0.04) !important;
  --hatch-strong: rgba(0, 0, 0, 0.06) !important;
  --dial-track: rgba(10, 10, 12, 0.2) !important;

  /* Light-mode shadow overrides — lower opacity so the shadow doesn't
     punch through the cream/white background. Tweak the rgba alpha (or
     the length values) to taste. */
  --product-shadow: 0 10cqw 5cqw -7cqw rgba(0, 0, 0, 0.45) !important;
  --bundle-shadow:  0 ;

  /* Light-mode glow overrides — the masked sweep is layered on
     ::after over a near-white base, so it needs to composite to a
     near-solid colour. 1 is usually right; drop it to taste. The
     colour tokens inherit `var(--accent)`, which is already swapped
     to the light-mode accent — override the variables below to
     break that link in light mode specifically. */
  --product-glow-opacity: 0.2 !important;
  --bundle-glow-opacity:  0.2 !important;
  --product-glow-color: var(--accent) !important;
  --bundle-glow-color:  var(--accent) !important;

  /* Light-mode at-rest glow — the texture pixels are now painted in
     the colour you pick below (default: accent), so the glow stays
     visible against the cream/white card surface even at full
     opacity. Drop the opacity or swap to a faint grey if the
     accent tint feels too strong. */
  --product-glow-base-opacity: 0.7 !important;
  --bundle-glow-base-opacity:  0.7 !important;
  --product-glow-base-color: #fff !important;
  --bundle-glow-base-color:  white !important;

  /* Light-mode background tint — defaults to plain white / cream so
     the cards sit on the page surface. Pick a faint off-white tint
     (e.g. #f6f4ef, or color-mix(in srgb, var(--accent) 4%, #fff)) to
     give the masked sweep more contrast on hover. */
  --product-glow-bg: #DCD8CE !important;
  --bundle-glow-bg:  #DCD8CE !important;
}

* {
  box-sizing: border-box;
}
html,
body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  transition: background-color 0.25s ease, color 0.25s ease;
}
/* NOTE: the horizontal-overflow guard intentionally lives on `main` (below),
   NOT on `body`. The sticky `.nav` + `.product-nav` are direct children of
   `body` (siblings of `main`); any `overflow` on `body` makes it their
   scroll/clip context and can stop them sticking (this is what hid the nav on
   scroll on /news and /products/tv3). Keeping `body` overflow-visible
   guarantees the shared nav sticks consistently on every page. The only
   full-bleed (100vw / -50vw) elements live inside `main`, so clipping `main`
   contains them just as well. */
a {
  color: inherit;
  text-decoration: none;
}
button {
  font: inherit;
  color: inherit;
}
::selection {
  background: var(--accent);
  color: var(--accent-ink);
}

.mono {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--fg-dim);
}

/* ---------- Layout shell ---------- */
.wrap {
  max-width: 1440px;
  margin: 0 auto;
  padding: 0 32px;
}
@media (max-width: 720px) {
  .wrap {
    padding: 0 20px;
  }
}

/* ---------- Placeholder hatched panel ---------- */
.ph {
  position: relative;
  width: 100%;
  height: 100%;
  background:"";
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--fg-faint);
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  overflow: hidden;
}
.ph[data-tone="accent"] {
  background:"";
  color: rgba(0, 0, 0, 0.55);
}
:root[data-theme="light"] .ph,
:root[data-theme="light"] .pcard .visual .ph {
  background:"";
  color: rgba(0, 0, 0, 0.45) !important;
}

/* ---------- Nav ---------- */
.nav {
  position: sticky;
  top: 0;
  z-index: 50;
  background: color-mix(in oklab, var(--bg) 80%, transparent);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  border-bottom: 0.5px solid var(--line);
}
.nav-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 64px;
}
.logo {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--display);
  font-weight: 700;
  letter-spacing: -0.01em;
  font-size: 18px;
}
.logo .brand-wordmark {
  display: inline-block;
  width: 128px;
  height: 30px;
  background: currentColor;
  -webkit-mask: url("/imaginando.svg") no-repeat left center / contain;
  mask: url("/imaginando.svg") no-repeat left center / contain;
  vertical-align: middle;
}
@media (max-width: 520px) {
  .logo .brand-wordmark {
    width: 110px;
    height: 26px;
  }
}
.nav-links {
  display: flex;
  gap: 28px;
  font-size: 13.5px;
  color: var(--fg-dim);
}
.nav-links a:hover,
.nav-links a.on {
  color: var(--fg);
}
.nav-actions {
  display: flex;
  align-items: center;
  gap: 12px;
}

/* ---------- Announcement banner (#457) ----------
   Admin-managed marketing banner, rendered above the sticky nav (it sits in
   normal flow at the very top of <body>, so it scrolls away and the nav sticks
   to top:0 underneath — no JS offset juggling). Two brand variants keep
   contrast guaranteed in both themes: `default` is a subtle tinted panel;
   `accent` is the lime accent with its ink colour. All colours come from the
   theme tokens, so light/dark are handled for free. Markup: _announcement.pug. */
.anno {
  position: relative;
  z-index: 40;
  background: color-mix(in oklab, var(--fg) 7%, var(--bg));
  color: var(--fg);
  border-bottom: 0.5px solid var(--line);
  font-size: 13px;
}
.anno[hidden] {
  display: none;
}
.anno--accent {
  background: var(--accent);
  color: var(--accent-ink);
  border-bottom-color: transparent;
}
.anno-row {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 8px 16px;
  min-height: 44px;
  /* Extra right padding leaves room for the absolutely-positioned dismiss. */
  padding: 8px 52px;
  text-align: center;
}
.anno-text {
  margin: 0;
  font-weight: 500;
  letter-spacing: 0.01em;
  line-height: 1.35;
}
.anno-cta {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-weight: 600;
  white-space: nowrap;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.anno-cta-arr {
  transition: transform 0.15s ease;
}
.anno-cta:hover .anno-cta-arr {
  transform: translateX(3px);
}
.anno-dismiss {
  position: absolute;
  top: 50%;
  right: 12px;
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  background: transparent;
  color: inherit;
  border: 0;
  border-radius: 8px;
  cursor: pointer;
  opacity: 0.55;
  transition: opacity 0.15s ease;
}
.anno-dismiss:hover {
  opacity: 1;
}
@media (max-width: 720px) {
  .anno-row {
    gap: 6px 12px;
    padding: 8px 44px;
    font-size: 12.5px;
  }
}

.btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 24px;
  padding: 20px 16px;
  border-radius: 999px;
  border: 0.5px solid var(--line-strong);
  background: transparent;
  color: var(--fg);
  font-size: 13px;
  cursor: pointer;
  white-space: nowrap;
  transition: transform 0.25s ease, background 0.25s ease, border-color 0.25s ease;
}
.btn:hover {
  border-color: var(--fg);
}
.btn.primary {
  background: var(--accent);
  color: var(--accent-ink);
  border-color: var(--accent);
}
.btn.primary:hover {
  background: color-mix(in oklab, var(--accent) 88%, white);
  border-color: var(--accent);
}
.btn.ghost {
  border: none;
  padding: 0 8px;
}
/* Accent-outlined button — recurs on every product-detail page (buy/learn-more
 * CTAs, section "see all" links). Hoisted from the per-page copies (#443). The
 * `.sblock.warm .btn.outline` override below re-tints it on warm cards. */
.btn.outline { border-color: var(--accent); color: var(--accent); }
.btn.outline:hover { background: var(--accent); color: var(--accent-ink); }
.btn .arrow {
  transition: transform 0.25s ease;
}
.btn:hover .arrow {
  transform: translateX(3px);
}
/* Main nav collapses to the hamburger at 1080px — just above the row's
 * content width (~980px) so the links never overflow the bar (the old 900px
 * sat below that width), while still keeping the full links on mid-size
 * screens (1080–1280px) rather than the product sub-nav's 1280px. */
@media (max-width: 1080px) {
  .nav-links {
    display: none;
  }
}

/* Product sub-nav (.product-nav) — base + responsive behaviour (refs #441).
 * The base .pn-* rules below are the single shared definition (they used to be
 * hand-copied into every per-page sheet); the .product-nav-scoped overrides
 * further down win regardless of load order. Every product page uses these
 * base rules verbatim now — there are no per-page .pn-* overrides. The
 * single-row nowrap layout and the `.sq` glyph fallback (for logo-less pages
 * like product-template) are part of the base below.
 *
 * IMPORTANT: `.pn-row` uses `padding-top`/`padding-bottom` longhand, never the
 * `padding: 12px 0` shorthand. The element is `class="wrap pn-row"`; both are
 * single-specificity, so a `padding` shorthand here would override `.wrap`'s
 * `padding: 0 32px` and zero out the page's side gutters, pushing the row
 * edge-to-edge. Setting only the vertical sides preserves the 32px gutters. */
.product-nav {
  position: sticky;
  top: 64px;
  z-index: 40;
  background: color-mix(in oklab, var(--bg) 92%, transparent);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  border-bottom: 0.5px solid var(--line);
}
.pn-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding-top: 12px; padding-bottom: 12px; flex-wrap: nowrap; }
.pn-left { display: flex; align-items: center; gap: 14px; min-width: 0; flex: 1; }
.pn-mark { display: inline-flex; align-items: center; gap: 8px; font-family: var(--display); font-weight: 700; letter-spacing: -0.01em; font-size: 18px; color: var(--fg); flex-shrink: 0; }
.pn-mark .pn-logo { width: 30px; height: 30px; border-radius: 6px; object-fit: cover; display: block; }
/* Glyph fallback for products with no logo image (e.g. product-template): the
 * mixin renders `span.sq` in place of `img.pn-logo`. */
.pn-mark .sq {
  width: 18px;
  height: 18px;
  border-radius: 4px;
  background: var(--accent);
  box-shadow: 0 0 16px color-mix(in oklab, var(--accent) 70%, transparent);
  display: grid;
  place-items: center;
  position: relative;
}
.pn-mark .sq::after {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent-ink);
}
.pn-scroll { display: flex; gap: 14px; overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none; min-width: 0; flex: 1; }
.pn-scroll::-webkit-scrollbar { display: none; }
.pn-link {
  appearance: none;
  background: transparent;
  border: 0;
  border-bottom: 0.5px solid transparent;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-faint);
  padding: 6px 0;
  white-space: nowrap;
  cursor: pointer;
  transition: color 0.2s ease, border-color 0.2s ease;
}
.pn-link:hover { color: var(--fg); }
.pn-link.active { color: var(--accent); border-bottom-color: var(--accent); }
.pn-actions { display: flex; gap: 8px; flex-shrink: 0; }

/* Responsive behaviour — essentials row on desktop, <select> on mobile (refs #343).
 * The bar lists only the product's ESSENTIAL sections (Overview · Product ·
 * Expansions · Tutorials · Pricing/Download · Help — whichever it has), tagged
 * `essential: true` in its product_nav YAML. Non-essential sections carry
 * `.pn-extra` and are hidden here, so the desktop row is a consistent ~4–6
 * links that always fit beside the Buy/Try actions — no per-product overflow.
 * Below the mobile breakpoint the row is swapped for a native <select> that
 * lists the FULL section set (script.js builds it from every link, extras
 * included), so nothing is unreachable on a phone. */
.pn-scroll .pn-link.pn-extra { display: none; }
.product-nav .pn-select {
  display: none; /* shown only under the mobile breakpoint below */
  max-width: 48vw;
  appearance: none;
  -webkit-appearance: none;
  background-color: var(--card-2, #161620);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23888' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  background-size: 10px;
  border: 0.5px solid var(--line);
  border-radius: 999px;
  color: var(--fg);
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.04em;
  padding: 9px 32px 9px 14px;
  cursor: pointer;
}
.product-nav .pn-select:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Below the breakpoint: swap the essentials row for the full-list <select>.
 * Placed after the base `.pn-select { display:none }` so it wins at equal
 * specificity. */
@media (max-width: 880px) {
  .product-nav .pn-scroll { display: none; }
  .product-nav .pn-select { display: inline-block; }
}

/* Mobile product sub-nav (refs #343): the row is logo + name + page selector +
 * Buy/Try, which overlaps on phones (the selector took 48vw and the row is
 * nowrap, pushing the primary Buy button off-screen). Fixes, shared here so
 * they apply across every product page's duplicated .pn-* CSS:
 *   - drop the product name (the logo alone identifies the product — matching
 *     the legacy product nav),
 *   - let the selector SHRINK to fill only the leftover space (flex, not 48vw)
 *     so the actions are never pushed out,
 *   - shrink the Buy/Try buttons a little. */
@media (max-width: 700px) {
  .product-nav .pn-name { display: none; }
  /* A native <select>'s intrinsic content width is a min-width floor, so it
   * won't shrink below its longest option and would push the Buy/Try buttons
   * off-screen. Force min-width:0 + an explicit width cap (!important to beat
   * the control's own sizing); the selector label truncates instead. */
  .product-nav .pn-select { width: 140px !important; min-width: 0 !important; max-width: 140px !important; }
  .product-nav .pn-actions { gap: 6px; }
  .product-nav .pn-actions .btn { height: 34px; padding: 0 11px; font-size: 12px; }
}
@media (max-width: 400px) {
  .product-nav .pn-select { width: 104px !important; min-width: 0 !important; max-width: 104px !important; }
  .product-nav .pn-actions .btn { padding: 0 10px; font-size: 11.5px; }
}

/* Product Download section (.dl-grid / .dl-card) — base, shared by the
 * free/mobile products that show Download instead of Pricing (tkfx, ubridge,
 * product-template via +productDownload; lk keeps bespoke markup on the same
 * classes). Refs #445. All three +productDownload pages use these base cards
 * verbatim; the only remaining override is lk's link-button cursor / 1000px
 * breakpoint (its cards are <div>s with a real inner <a>).
 *
 * NB: `.dl-grid` is also used — unrelated — by the redesigned download PAGE
 * (public/pages/product-downloads.css, a `.dl-page` layout grid). That
 * sheet fully overrides display/columns/gap on its own `.dl-grid`, and that
 * page has no `.dl-card`, so the two never actually collide. */
.dl-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
@media (max-width: 900px) { .dl-grid { grid-template-columns: 1fr; } }
.dl-card { position: relative; border: 0.5px solid var(--line); border-radius: 18px; padding: 36px; min-height: 260px; display: flex; flex-direction: column; justify-content: space-between; background: var(--card); overflow: hidden; transition: border-color 0.25s ease, transform 0.25s ease; }
.dl-card:hover { border-color: var(--accent); transform: translateY(-2px); }
.dl-card.featured { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }
.dl-card .platform-glyph { position: absolute; top: 32px; right: 32px; font-family: var(--display); font-weight: 600; font-size: 16px; letter-spacing: -0.005em; display: flex; align-items: center; gap: 8px; color: var(--fg); }
.dl-card .platform-glyph svg { width: 22px; height: 22px; }
.dl-card.featured .platform-glyph { color: var(--accent-ink); }
.dl-card h3 { font-family: var(--display); font-weight: 500; font-size: 36px; letter-spacing: -0.025em; line-height: 1.05; margin: 0 0 8px; }
.dl-card h3 em { font-family: var(--serif); font-style: italic; font-weight: 400; }
.dl-card .meta-line { font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--fg-faint); margin-top: 12px; }
.dl-card.featured .meta-line { color: rgba(0,0,0,.55); }
.dl-card .row { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-top: 32px; flex-wrap: wrap; }
.dl-card .ver { font-family: var(--mono); font-size: 11px; letter-spacing: 0.06em; color: var(--fg-dim); }
.dl-card.featured .ver { color: rgba(0,0,0,.65); }
/* No `cursor` here: the whole .dl-card is the <a>, so the button (a <span>)
 * inherits the anchor's cursor — pointer when the card has a manifest href,
 * default when it doesn't (scaffold / no build yet). LK sets pointer
 * explicitly because its button is a real inner <a>. */
.dl-card .download-btn { display: inline-flex; align-items: center; gap: 10px; padding: 0 18px; height: 42px; border-radius: 999px; background: var(--fg); color: var(--bg); font-family: var(--display); font-weight: 500; font-size: 14px; letter-spacing: -0.005em; border: 0; transition: transform 0.25s ease, background 0.25s ease; }
/* Official store badge inside a download card (App Store / Google Play);
 * desktop cards (Mac / Windows) keep the .download-btn. In the shared mixin
 * the whole .dl-card is the link (badge is a bare <img>); LK's cards wrap the
 * badge in its own <a> (.dl-badge-link) since LK uses a real inner button. */
.dl-card .dl-badge { height: 52px; width: auto; display: block; }
.dl-card a.dl-badge-link { display: inline-flex; text-decoration: none; }
.dl-card a.dl-badge-link[href] { transition: opacity 120ms ease; }
.dl-card a.dl-badge-link[href]:hover { opacity: 0.85; }
.dl-card.featured .download-btn { background: var(--accent-ink); color: var(--accent); }
.dl-card .download-btn:hover { transform: translateY(-2px); }
.dl-card .download-btn .arrow { transition: transform 0.25s ease; }
.dl-card .download-btn:hover .arrow { transform: translateX(3px); }

/* Feature-mosaic shell — the 12-col Engine/Features grid + span helpers shared
 * across the product pages (drc/dlym/k7d/frms/grfx/tkfx/tv3). Refs #448. Only
 * the outer grid is shareable: each product keeps its own `.ftile` chrome and
 * bespoke per-tile `.fv-*` visuals per-page (high variation — different tile
 * heights, full-bleed vs inset visual panels, accent variants). Scoped to
 * `.mosaic` so these span utilities don't collide with the bare `.span-*`
 * helpers other pages use for unrelated 12-col grids (bam/lk/product-template).
 * Canonical = the former majority: 16px gap, collapse at 900px (dlym/grfx were
 * 18px / 1000px and now conform). */
.mosaic { display: grid; grid-template-columns: repeat(12, 1fr); gap: 16px; }
@media (max-width: 900px) { .mosaic { grid-template-columns: repeat(2, 1fr); } }
.mosaic .span-12 { grid-column: span 12; }
.mosaic .span-8  { grid-column: span 8; }
.mosaic .span-7  { grid-column: span 7; }
.mosaic .span-6  { grid-column: span 6; }
.mosaic .span-5  { grid-column: span 5; }
.mosaic .span-4  { grid-column: span 4; }
.mosaic .span-3  { grid-column: span 3; }
@media (max-width: 900px) {
  .mosaic .span-12, .mosaic .span-8, .mosaic .span-7,
  .mosaic .span-6, .mosaic .span-5, .mosaic .span-4, .mosaic .span-3 { grid-column: span 2; }
}

.theme-toggle {
  width: 38px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
  background: transparent;
  color: var(--fg);
}
.theme-toggle svg {
  display: block;
  transition: transform 0.5s ease;
}
.theme-toggle:hover svg {
  transform: rotate(260deg);
}
:root[data-theme="dark"] .theme-toggle .icon-sun {
  display: block;
}
:root[data-theme="dark"] .theme-toggle .icon-moon {
  display: none;
}
:root[data-theme="light"] .theme-toggle .icon-sun {
  display: none;
}
:root[data-theme="light"] .theme-toggle .icon-moon {
  display: block;
}

/* ---------- Language selector (refs #343) ---------- */
.lang-switch {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 3px;
  border-radius: 999px;
  border: 0.5px solid var(--line-strong);
}
.lang-opt {
  display: inline-flex;
  align-items: center;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 12px;
  line-height: 1;
  color: var(--fg);
  opacity: 0.55;
  text-decoration: none;
  white-space: nowrap;
  transition: opacity 0.2s ease, background 0.2s ease, color 0.2s ease;
}
.lang-opt:hover {
  opacity: 1;
}
.lang-opt.active {
  opacity: 1;
  background: var(--accent);
  color: var(--accent-ink);
}
/* Drawer variant: full-width row above the Account/Cart actions. */
.mnav-lang {
  /* Sits inside the drawer's action row, beside Account/Cart. Width fits its
     content (inherited from .lang-switch's inline-flex) and it's pushed to
     the right edge of the row. */
  margin-left: auto;
}

/* ---------- Nav widgets (cart + profile) ----------
 * Vue micro-app mounted at `#new-nav-vue` (see views/site/_nav.pug +
 * app/components/new-nav/*.vue). The container starts as two inert
 * .nn-trigger buttons so the chrome works without JS; Vue replaces
 * them with a single `.new-nav-widgets` wrapper containing two
 * `.nn-widget`s on mount.
 *
 * Both layouts use the SAME gap (16px) so there's no visible
 * "settle" when Vue takes over — pre-mount the gap lives on
 * `#new-nav-vue` (between two direct buttons), post-mount it lives
 * on `.new-nav-widgets` (between two `.nn-widget`s).
 */
#new-nav-vue {
  display: inline-flex;
  align-items: center;
  gap: 16px;
}
.new-nav-widgets {
  display: inline-flex;
  align-items: center;
  gap: 16px;
}
.nn-widget {
  position: relative;
  display: inline-flex;
}

/* Icon trigger button — 38×24 transparent hit-area centred on the
 * row baseline. No border / no fill / no border-radius (the open
 * state uses `scale`, not a chrome swap), so the SVG itself is the
 * only thing the user sees. Cart SVG ships at 22 and profile at
 * 24 — the cart looks visually heavier because of the wheels, so
 * the smaller size keeps the two icons reading as the same weight. */
.nn-trigger {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 24px;
  border: 0.5px solid transparent;
  background: transparent;
  color: var(--fg);
  cursor: pointer;
  position: relative;
  transition: background 0.2s ease, transform 0.2s ease;
}
/* Pre-mount placeholder buttons keep the full opacity/colour so the nav
 * doesn't visibly settle once Vue takes over. They're `disabled` to
 * suppress the navigation race (see _nav.pug). */
.nn-trigger[disabled] {
  cursor: default;
  opacity: 1;
  color: var(--fg);
}
.nn-trigger:hover {
  scale: 1.1;
  transition: scale 0.2s linear, transform 0.1s linear;
}
.nn-widget.open .nn-trigger {
  scale: 1.2;
}

/* Profile avatar (square logged-in img) + initials fallback. */
.nn-avatar {
  width: 26px;
  height: 26px;
  border-radius: 999px;
  object-fit: cover;
  display: block;
}
.nn-avatar-initials {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--display);
  font-weight: 500;
  font-size: 11px;
  letter-spacing: 0.02em;
  color: var(--accent-ink);
  background: var(--accent);
}

/* Cart count badge — top-right pip, accent-coloured, mono digits. */
.nn-badge {
  position: absolute;
  top: 0px;
  right: 0px;
  min-width: 11px;
  height: 11px;
  border-radius: 999px;
  background: var(--fg);
  color: var(--bg);
  font-family: var(--mono);
  font-size: 8px;
  font-weight: 600;
  text-align: center;
}
/* Brief lift on "new item added" — matches the ping in CartPopup.vue. */
.nn-cart.ping .nn-trigger {
  transform: translateY(-2px);
}

/* Popup — shared shell for both user and cart. Anchored to the
 * widget's right edge so the arrow column doesn't overflow the wrap.
 * Hidden by default; .open on the parent .nn-widget shows it. */
.nn-popup {
  position: absolute;
  top: calc(100% + 10px);
  right: 0;
  min-width: 260px;
  padding: 18px;
  border: 0.5px solid var(--line-strong);
  border-radius: 14px;
  background: var(--card);
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.45);
  z-index: 60;
  opacity: 0;
  pointer-events: none;
  transform: translateY(-4px);
  transition: opacity 0.18s ease, transform 0.18s ease;
}
.nn-widget.open .nn-popup {
  opacity: 1;
  pointer-events: auto;
  transform: none;
}
:root[data-theme="light"] .nn-popup {
  box-shadow: 0 14px 36px rgba(10, 10, 12, 0.16);
}

.nn-cart-popup {
  min-width: 320px;
}

/* Mobile: the popup anchors right:0 to the cart/user icon, which isn't at the
   far edge, so its fixed min-width overflowed off the left of the screen. Pin
   it to the viewport as a top sheet under the 64px sticky nav instead, and let
   a full cart scroll within it. */
@media (max-width: 560px) {
  .nn-popup {
    position: fixed;
    top: 74px;
    left: 12px;
    right: 12px;
    min-width: 0;
    max-height: calc(100dvh - 88px);
    overflow-y: auto;
  }
}

/* Head — eyebrow + title, shared by both widgets' popups. */
.nn-pop-head {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-bottom: 14px;
  padding-bottom: 14px;
  border-bottom: 0.5px dashed var(--line);
}
/* Cart popup header: title on the left, "Clear cart" on the right. */
.nn-cart-head-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}
.nn-cart-clear {
  appearance: none;
  border: 0;
  background: transparent;
  padding: 0;
  color: var(--fg-faint);
  font-family: var(--body);
  font-size: 11.5px;
  cursor: pointer;
  transition: color 0.15s ease;
}
.nn-cart-clear:hover {
  color: var(--fg);
}
.nn-cart-clear[disabled] {
  cursor: not-allowed;
  opacity: 0.6;
}
/* Cart notices (e.g. upgrade item removed — can't be mixed). Warning-toned
   to match the legacy BaseNotice the full cart page uses. */
.nn-cart-notes {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 14px;
}
.nn-cart-note {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 10px 12px;
  border-radius: 10px;
  border: 0.5px solid color-mix(in oklab, #ffb84d 45%, var(--line));
  background: color-mix(in oklab, #ffb84d 12%, transparent);
}
.nn-cart-note-t {
  font-family: var(--display);
  font-weight: 500;
  font-size: 12.5px;
  color: var(--fg);
}
.nn-cart-note-i {
  font-family: var(--body);
  font-size: 11.5px;
  line-height: 1.45;
  color: var(--fg-dim);
}
.nn-pop-eyebrow {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--fg-faint);
}
.nn-pop-name,
.nn-pop-title {
  font-family: var(--display);
  font-weight: 500;
  font-size: 16px;
  letter-spacing: -0.01em;
  color: var(--fg);
}
.nn-pop-email {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  color: var(--fg-dim);
}

/* User menu list. */
/* Generic underline-grow hover used by every text link/button inside
 * the popups (mirrors the legacy `.text.hoverable` style — underline
 * sweeps in from the left on hover). */
.nn-link {
  position: relative;
  display: inline-block;
  color: inherit;
  text-decoration: none;
  background-image: linear-gradient(currentColor, currentColor);
  background-size: 0% 1px;
  background-repeat: no-repeat;
  background-position: 0 100%;
  transition: background-size 0.25s ease;
  padding-bottom: 1px;
}
.nn-link:hover,
.nn-link:focus-visible {
  background-size: 100% 1px;
}
/* Cart popup: drop the underline-grow sweep on its links (product name +
   "Remove"); a subtle opacity shift keeps them feeling interactive. */
.nn-cart-popup .nn-link {
  background-image: none;
  transition: opacity 0.2s ease;
}
.nn-cart-popup .nn-link:hover,
.nn-cart-popup .nn-link:focus-visible {
  background-size: 0 1px;
  opacity: 0.65;
}

.nn-pop-list {
  list-style: none;
  margin: 0 0 12px;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.nn-pop-list li {
  padding-bottom: 10px;
  border-bottom: 0.5px solid var(--line);
}
.nn-pop-list li:last-child {
  border-bottom: 0;
  padding-bottom: 0;
}
.nn-pop-list a {
  display: inline-block;
  font-family: var(--display);
  font-weight: 500;
  font-size: 16px;
  letter-spacing: -0.005em;
  color: var(--fg);
}

/* Logout — danger-coloured underline-grow link, mirrors the legacy
 * `text-danger` button in UserHeader.vue. No border / pill chrome;
 * just a text link aligned with the rest of the menu. */
.nn-pop-logout {
  appearance: none;
  border: 0;
  background: transparent;
  padding: 0;
  margin-top: 4px;
  font-family: var(--display);
  font-weight: 500;
  font-size: 16px;
  letter-spacing: -0.005em;
  color: var(--danger, #e34a4a);
  cursor: pointer;
}

/* Generic popup CTA — pill button shared between login/signup and the
 * "Go to cart" / "See products" links. .primary uses the accent fill. */
.nn-pop-cta {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 0 14px;
  height: 38px;
  border-radius: 999px;
  border: 0.5px solid var(--line-strong);
  background: transparent;
  color: var(--fg);
  font-size: 13px;
  text-decoration: none;
  transition: border-color 0.2s ease, background 0.2s ease, color 0.2s ease;
}
.nn-pop-cta + .nn-pop-cta {
  margin-top: 8px;
}
.nn-pop-cta:hover {
  border-color: var(--fg);
}
.nn-pop-cta.primary {
  background: var(--accent);
  color: var(--accent-ink);
  border-color: var(--accent);
}
.nn-pop-cta.primary:hover {
  background: color-mix(in oklab, var(--accent) 88%, white);
}
.nn-pop-cta .arrow {
  transition: transform 0.2s ease;
}
.nn-pop-cta:hover .arrow {
  transform: translateX(3px);
}

.nn-pop-foot {
  margin-top: 12px;
  padding-top: 12px;
  border-top: 0.5px dashed var(--line);
  text-align: center;
}
.nn-pop-foot a {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg-faint);
  text-decoration: none;
}
.nn-pop-foot a:hover {
  color: var(--fg-dim);
}

/* Cart popup line items + footer total. Grid is `thumb / text / price`
 * — mirrors the legacy CartResume.vue row (40px logo, name + Remove
 * stack, price on the right). */
.nn-cart-list {
  list-style: none;
  margin: 0 0 16px;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0;
  max-height: 280px;
  overflow-y: auto;
  /* Thin, theme-aware scrollbar (Firefox). The default light track/thumb
     was glaring on the dark popup and ate horizontal space. */
  scrollbar-width: thin;
  scrollbar-color: var(--line-strong) transparent;
  /* room for the thin overlay scrollbar so rows don't sit under it */
  padding-right: 4px;
}
/* WebKit/Blink (Chrome, Safari, Edge) */
.nn-cart-list::-webkit-scrollbar {
  width: 6px;
}
.nn-cart-list::-webkit-scrollbar-track {
  background: transparent;
}
.nn-cart-list::-webkit-scrollbar-thumb {
  background: var(--line-strong);
  border-radius: 999px;
}
.nn-cart-list::-webkit-scrollbar-thumb:hover {
  background: var(--fg-faint);
}
.nn-cart-list.is-busy {
  opacity: 0.7;
  cursor: wait;
}
.nn-cart-row {
  display: grid;
  grid-template-columns: 40px 1fr auto;
  gap: 14px;
  padding: 12px 0;
  border-bottom: 0.5px solid var(--line);
  align-items: center;
}
.nn-cart-row.is-busy {
  pointer-events: none;
}
.nn-cart-row:first-child {
  padding-top: 0;
}
.nn-cart-row:last-child {
  border-bottom: 0;
}
/* Per-item "you already own this" warning — spans the full row under the
   item, mirroring the legacy CartResume warning. */
.nn-cart-owned {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 4px;
  padding: 8px 10px;
  border-radius: 9px;
  border: 0.5px solid color-mix(in oklab, #ff6b6b 45%, var(--line));
  background: color-mix(in oklab, #ff6b6b 12%, transparent);
  font-size: 11.5px;
  line-height: 1.35;
  color: color-mix(in oklab, #ff6b6b 70%, var(--fg));
}
.nn-cart-owned svg {
  width: 13px;
  height: 13px;
  flex: none;
}
.nn-cart-thumb {
  display: flex;
  width: 40px;
  height: 40px;
  border-radius: 8px;
  overflow: hidden;
  background: var(--card-2, rgba(255, 255, 255, 0.04));
  flex: 0 0 40px;
}
.nn-cart-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.nn-cart-row-l {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}
.nn-cart-name {
  font-family: var(--display);
  font-weight: 500;
  font-size: 13.5px;
  line-height: 1.25;
  color: var(--fg);
  letter-spacing: -0.005em;
}

/* Remove — × glyph + underlined "Remove" label. The × stays solid;
 * only the "Remove" word picks up the `.nn-link` underline-grow on
 * hover (matches the legacy `.text.hoverable` behaviour). */
.nn-cart-remove {
  appearance: none;
  border: 0;
  background: transparent;
  padding: 0;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--fg-faint);
  font-family: var(--body);
  font-size: 11.5px;
  cursor: pointer;
}
.nn-cart-remove[disabled] {
  cursor: not-allowed;
}
.nn-cart-remove-x {
  font-size: 13px;
  line-height: 1;
  color: var(--fg-faint);
}

.nn-cart-price {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--fg-dim);
  letter-spacing: 0.04em;
  white-space: nowrap;
}
/* Per-item on-sale cluster in the nav cart (refs #415): promo price stacked
   over the struck original, right-aligned (no "On sale" pill). */
.nn-cart-price:has(.nn-cart-was) {
  display: inline-flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 1px;
}
.nn-cart-price .nn-cart-now { font-weight: 600; }
.nn-cart-price .nn-cart-was {
  font-size: 10px;
  color: var(--fg-faint);
  text-decoration: line-through;
}
/* Promoted (current) per-item price number in the accent colour (#415). */
.nn-cart-price:has(.nn-cart-was) { color: var(--accent); }
/* Cart-level "Original price" row — struck (refs #415). */
.nn-cart-line-val.nn-cart-was {
  color: var(--fg-faint);
  text-decoration: line-through;
}

/* First-purchase offer block — headline + countdown, or the win-back reclaim
   button once the window closes. Mirrors the full cart page's .fpb banner,
   skinned to the popup's compact scale. */
.nn-cart-offer {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 14px;
  padding: 12px;
  border-radius: 10px;
  border: 0.5px solid color-mix(in oklab, var(--accent) 40%, var(--line-strong));
  background: color-mix(in oklab, var(--accent) 10%, transparent);
}
.nn-cart-offer.is-expired {
  border-color: var(--line-strong);
  background: transparent;
}
.nn-cart-offer-h {
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: var(--display);
  font-weight: 500;
  font-size: 13px;
  letter-spacing: -0.005em;
  color: var(--fg);
}
.nn-cart-offer-h svg {
  width: 18px;
  height: 18px;
  flex: none;
  color: var(--accent);
}
.nn-cart-offer-h b {
  font-weight: 600;
  color: var(--accent);
}
.nn-cart-offer.is-expired .nn-cart-offer-h b {
  color: var(--fg);
}
.nn-cart-offer-timer {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--mono);
  font-size: 11.5px;
  letter-spacing: 0.04em;
  color: var(--fg-dim);
}
.nn-cart-offer-timer svg {
  width: 13px;
  height: 13px;
  flex: none;
  color: var(--accent);
}
.nn-cart-offer-timer b {
  color: var(--fg);
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}
.nn-cart-offer-btn {
  align-self: flex-start;
  appearance: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 32px;
  padding: 0 14px;
  border-radius: 999px;
  border: 0.5px solid var(--accent);
  background: var(--accent);
  color: var(--accent-ink);
  font-size: 12px;
  transition: background 0.2s ease, border-color 0.2s ease;
}
.nn-cart-offer-btn:hover:not(:disabled) {
  background: color-mix(in oklab, var(--accent) 88%, white);
}
.nn-cart-offer-btn:disabled {
  opacity: 0.6;
  cursor: default;
}

.nn-cart-foot {
  border-top: 0.5px dashed var(--line);
  padding-top: 14px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
/* Subtotal row — same layout as `.nn-cart-total` but lighter weight. */
.nn-cart-line {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  font-size: 13px;
  color: var(--fg-dim);
}
.nn-cart-line-lbl {
  font-family: var(--body);
  letter-spacing: 0.01em;
}
.nn-cart-line-val {
  font-family: var(--mono);
  letter-spacing: 0.04em;
}
.nn-cart-line-discount .nn-cart-line-val {
  color: var(--accent);
}
.nn-cart-total {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  padding-top: 8px;
  margin-top: 2px;
  border-top: 0.5px solid var(--line);
}
.nn-cart-total-lbl {
  font-family: var(--display);
  font-weight: 500;
  font-size: 18px;
  letter-spacing: -0.01em;
  color: var(--fg);
}
.nn-cart-total-val {
  font-family: var(--display);
  font-weight: 500;
  font-size: 18px;
  letter-spacing: -0.01em;
  color: var(--fg);
}

.nn-cart-empty {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.nn-cart-empty p {
  margin: 0;
  font-size: 13px;
  color: var(--fg-dim);
  line-height: 1.5;
}

/* Mobile hiding is handled by the parent `.desk-only` (see the
 * `.nav-actions .desk-only` rule below) — no per-widget media query
 * needed. */

/* ---------- Mobile nav drawer ----------
 * Markup in views/site/_nav.pug (.mnav-overlay + .mnav-panel block,
 * rendered alongside .nav). JS in public/script.js toggles .open
 * on the overlay and locks <html> scroll while the drawer is shown.
 *
 * Breakpoint: <901px shows the hamburger and hides the Account/Cart
 * desktop buttons; ≥901px hides the drawer overlay entirely. */
.menu-toggle {
  display: none;
  width: 38px;
  padding: 0 !important;
  align-items: center;
  justify-content: center;
  border: 0.5px solid var(--line-strong);
  border-radius: 999px;
  background: transparent;
  color: var(--fg);
}
.menu-toggle svg { display: block; }
.menu-toggle:hover { border-color: var(--fg); }

@media (max-width: 1080px) {
  .menu-toggle { display: inline-flex; }
  .nav-actions .desk-only { display: none; }
}

.mnav-overlay {
  position: fixed;
  inset: 0;
  z-index: 200;
  pointer-events: none;
}
.mnav-overlay.open { pointer-events: auto; }
.mnav-scrim {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  opacity: 0;
  transition: opacity 0.35s ease;
  border: 0;
  padding: 0;
  cursor: default;
}
.mnav-overlay.open .mnav-scrim { opacity: 1; }
:root[data-theme="light"] .mnav-scrim { background: rgba(10, 10, 12, 0.35); }

.mnav-panel {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: min(440px, 92vw);
  background: var(--bg);
  border-left: 0.5px solid var(--line-strong);
  display: flex;
  flex-direction: column;
  transform: translateX(100%);
  transition: transform 0.42s cubic-bezier(0.2, 0.8, 0.2, 1);
  overflow-y: auto;
  box-shadow: -30px 0 60px rgba(0, 0, 0, 0.5);
}
.mnav-overlay.open .mnav-panel { transform: translateX(0); }

.mnav-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 18px 24px;
  height: 64px;
  border-bottom: 0.5px solid var(--line);
  position: sticky;
  top: 0;
  background: var(--bg);
  z-index: 2;
}
.mnav-close {
  width: 38px;
  padding: 0 !important;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
}

.mnav-list {
  display: flex;
  flex-direction: column;
  padding: 24px 12px;
  flex: 1;
  gap: 2px;
}
.mnav-link {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 18px;
  padding: 18px 16px;
  border-radius: 12px;
  font-family: var(--display);
  font-weight: 500;
  font-size: 26px;
  letter-spacing: -0.015em;
  color: var(--fg);
  opacity: 0;
  transform: translateX(20px);
  transition: opacity 0.5s ease, transform 0.5s ease, background 0.2s ease,
    color 0.2s ease;
}
.mnav-overlay.open .mnav-link {
  opacity: 1;
  transform: none;
}
.mnav-link:hover {
  background: var(--card-2, color-mix(in oklab, var(--fg) 6%, transparent));
}
.mnav-link.active { color: var(--accent); }
.mnav-link.active .mnav-ix { color: var(--accent); }
.mnav-ix {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.12em;
  color: var(--fg-faint);
  text-transform: uppercase;
}
.mnav-lbl { text-wrap: balance; }
.mnav-arr {
  font-family: var(--mono);
  color: var(--fg-faint);
  font-size: 16px;
  transition: transform 0.2s ease, color 0.2s ease;
}
.mnav-link:hover .mnav-arr {
  transform: translateX(4px);
  color: var(--fg);
}
.mnav-link.active .mnav-arr { color: var(--accent); }

.mnav-foot {
  margin-top: auto;
  padding: 20px 24px 28px;
  border-top: 0.5px solid var(--line);
  display: flex;
  flex-direction: column;
  gap: 14px;
  background: var(--bg);
}
.mnav-actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  align-items: center;
}
.mnav-act {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 0 14px;
  height: 38px;
  border-radius: 999px;
  border: 0.5px solid var(--line-strong);
  background: transparent;
  color: var(--fg);
  font-size: 13px;
  font-family: var(--body);
}
.mnav-act:hover { border-color: var(--fg); }
.mnav-tag {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--fg-faint);
}

@media (min-width: 901px) {
  .mnav-overlay { display: none; }
}
@media (max-width: 520px) {
  .mnav-link {
    font-size: 22px;
    padding: 16px 14px;
    gap: 14px;
  }
  .mnav-panel { width: 100vw; }
}

/* ============================================================
 * PRODUCT HERO — shared `.phero-*` (refs #444)
 * Namespaced (like `.pkhero-*`) so it never collides with the generic
 * content-page `.hero` below. Rendered by views/site/_sections/_hero.pug
 * `+productHero`. Standard shell: section#overview.phero > .phero-bg +
 * .wrap.phero-grid. The right-column visual is a per-page slot, so its
 * CSS (.hero-img-col, .acid-box, .bridge-vis, …) stays per-page.
 * Per-product tint comes from each page's `--accent`; the meta strip
 * surface from the global `--card`.
 * ============================================================ */
.phero {
  position: relative;
  padding: 48px 0 80px;
  border-bottom: 0.5px solid var(--line);
  overflow: hidden;
}
/* Positioning frame only — each product paints its own bespoke gradient
 * via a per-page `.phero-bg { background: … }` override. */
.phero-bg {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.phero-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 56px;
  align-items: end;
  position: relative;
}
@media (max-width: 1000px) {
  .phero-grid { grid-template-columns: 1fr; gap: 40px; align-items: start; }
}
/* Breadcrumb sits where the eyebrow used to (refs #444) — same top gap. */
.phero .crumbs { margin-bottom: 24px; }
.phero h1 {
  font-family: var(--display);
  font-weight: 500;
  font-size: clamp(72px, 11vw, 180px);
  line-height: 0.85;
  letter-spacing: -0.04em;
  margin: 0;
  text-wrap: balance;
}
.phero h1 em { font-family: var(--serif); font-style: italic; font-weight: 400; color: var(--accent); }
.phero h2 {
  font-family: var(--serif);
  font-style: italic;
  font-weight: 400;
  font-size: clamp(28px, 3.5vw, 42px);
  line-height: 1.1;
  letter-spacing: -0.01em;
  margin: 28px 0 0;
  color: var(--fg);
  max-width: 18ch;
}
.phero h2 em { color: var(--accent); font-style: italic; }
.phero .lede { margin: 24px 0 0; color: var(--fg-dim); font-size: 17px; line-height: 1.55; max-width: 46ch; text-wrap: pretty; }
.phero-actions { display: flex; gap: 10px; margin-top: 32px; flex-wrap: wrap; align-items: center; }
.phero-actions .price-tag {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--fg-faint);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin-left: 6px;
}
.phero-actions .price-tag em {
  color: var(--accent);
  font-family: var(--display);
  font-weight: 500;
  font-size: 18px;
  letter-spacing: -0.01em;
  margin-right: 4px;
  font-style: normal;
}
.phero-meta {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0;
  border: 0.5px solid var(--line);
  border-radius: 14px;
  margin-top: 44px;
  background: var(--card);
  overflow: hidden;
}
@media (max-width: 760px) { .phero-meta { grid-template-columns: repeat(2, 1fr); } }
.phero-meta .cell { padding: 18px 20px; border-right: 0.5px solid var(--line); display: flex; flex-direction: column; gap: 4px; }
.phero-meta .cell:last-child { border-right: 0; }
.phero-meta .cell .num { font-family: var(--display); font-weight: 500; font-size: 24px; letter-spacing: -0.01em; line-height: 1; }
.phero-meta .cell .num em { font-family: var(--serif); font-style: italic; font-weight: 400; color: var(--accent); }
.phero-meta .cell .lbl { font-family: var(--mono); font-size: 10px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--fg-faint); }
@media (max-width: 760px) {
  .phero-meta .cell:nth-child(2) { border-right: 0; }
  .phero-meta .cell:nth-child(-n+2) { border-bottom: 0.5px solid var(--line); }
}
/* ---------- Hero visual — shared photo framing (refs #444 follow-up) ----------
 * The `+productHero` visual slot is per-product, but the photo/screenshot
 * group (dlym · lk · k7d · grfx · drc · frms · tkfx → `.hero-img-col >
 * img.hero-shot`) shares its framing here so per-page CSS carries only the
 * bespoke `.phero-bg` gradient. Was drifting: radius 16/18/none, k7d
 * frameless, shadow alpha 0.5/0.55/0.65.
 *
 * Vertical alignment (`.phero-grid` is `align-items: end`):
 *   • Free-height photos pin to the TOP (`align-self: start`, +44px).
 *   • Fixed aspect-ratio frames (tv3 `.acid-box`, bam `.hero-shot` wrapper,
 *     vs `.hero-canvas`, ubridge `.bridge-vis`, product-template `.hero-vis`)
 *     keep the grid's bottom alignment, level with the text baseline.
 * `img.hero-shot` is tag-qualified so it never styles bam's `.hero-shot`
 * *wrapper* div. Each page keeps its own responsive `.hero-img-col` reset
 * because the 2-col→1-col breakpoint differs per page (1000/1100px). */
.hero-img-col { align-self: start; margin-top: 44px; }
img.hero-shot {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 6px;
  border: 0.5px solid var(--line-strong);
  box-shadow: 0 30px 80px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.05);
}

/* ---------- Hero ---------- */
.hero {
  position: relative;
  padding: 48px 0 56px;
  border-bottom: 0.5px solid var(--line);
  overflow: hidden;
}
/* Hero backdrop (home). Edge to edge up to 1580px, then capped and centred —
 * no gutter of its own (the padding lives only on the copy, via .wrap). */
.hero-bg {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  width: min(100%, 1380px);
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}
/* Legibility gradient over the photo (from Figma): dark on the left, under the
 * copy, fading to transparent on the right. Paints on top of .hero-bg-img
 * (same container, later in paint order). */
.hero-bg::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90.19deg, var(--bg) 12.45%, rgba(0, 0, 0, 0) 46.94%);
  pointer-events: none;
}
.hero-bg-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Framing of the (uncropped) shot — nudged up so the tablet sits a touch
   * above the vertical centre. */
  object-position: center 64%;
  display: block;
}
.hero .mono { margin-bottom: 24px; }
/* Lift the hero copy above the backdrop. */
.hero .wrap { position: relative; z-index: 1; }
.hero-content { max-width: 880px; }
.hero-ctas {
  display: flex;
  gap: 12px;
  margin-top: 36px;
  flex-wrap: wrap;
}
.hero h1 {
  font-family: var(--display);
  font-weight: 500;
  font-size: clamp(56px, 8.6vw, 132px);
  line-height: 0.92;
  letter-spacing: -0.035em;
  margin: 0;
  text-wrap: balance;
}
.hero h1 em {
  font-family: var(--serif);
  font-style: italic;
  font-weight: 400;
  color: var(--accent);
}
.hero-sub {
  margin-top: 24px;
  max-width: 520px;
  color: var(--fg-dim);
  font-size: 18px;
  line-height: 1.5;
  text-wrap: pretty;
}
.hero-meta {
  display: flex;
  gap: 24px;
  margin-top: 36px;
  flex-wrap: wrap;
}
.hero-meta .chip {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.hero-meta .chip b {
  font-family: var(--display);
  font-weight: 600;
  font-size: 18px;
  letter-spacing: -0.01em;
}
.hero-meta .chip span {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--fg-faint);
}

/* ---------- Press strip ---------- */
.press-strip {
  border-bottom: 0.5px solid var(--line);
  padding: 48px 0;
}
.press-strip .row {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 48px;
  align-items: start;
}
@media (max-width: 900px) {
  .press-strip .row {
    grid-template-columns: 1fr;
    gap: 24px;
  }
}
.press-strip .eyebrow {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--fg-faint);
  padding-top: 4px;
  white-space: nowrap;
}
.press-quotes {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 36px;
}
@media (max-width: 900px) {
  .press-quotes {
    grid-template-columns: 1fr;
    gap: 28px;
  }
}
.pq {
  display: flex;
  flex-direction: column;
  gap: 14px;
  margin: 0;
}
.pq blockquote {
  margin: 0;
  font-family: var(--display);
  font-weight: 500;
  font-size: 18px;
  line-height: 1.4;
  letter-spacing: -0.01em;
  color: var(--fg);
  text-wrap: balance;
  max-width: 38ch;
}
.pq blockquote em {
  font-style: italic;
  font-weight: 400;
  color: var(--accent);
}
.pq .src {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--fg-faint);
}
.pq .src::before {
  content: "";
  width: 18px;
  height: 0.5px;
  background: var(--line-strong);
  flex: none;
}
.pq .src b {
  color: var(--fg);
  font-weight: 500;
}

/* ---------- Section + section heading ---------- */
.sec-head {
  display: flex;
  justify-content: space-between;
  align-items: end;
  gap: 24px;
  margin-bottom: 32px;
}
/* Left column (eyebrow + h2). Capped so the product-detail heading scale
 * (`.sec-head h2 { max-width: none }`, per-page) still wraps; a no-op on
 * marketing pages where the base h2 is already `max-width: 14ch`. Hoisted
 * from the per-page copies (#443); vs overrides to 46ch. */
.sec-head .l { max-width: 48ch; }
.sec-head h2 {
  font-family: var(--display);
  font-weight: 500;
  font-size: clamp(34px, 4.5vw, 60px);
  line-height: 1;
  letter-spacing: -0.025em;
  margin: 0;
  text-wrap: balance;
  max-width: 14ch;
}
.sec-head h2 em {
  font-family: var(--serif);
  font-style: italic;
  font-weight: 400;
  color: var(--accent);
}
.sec-head .meta {
  display: flex;
  flex-direction: column;
  gap: 6px;
  text-align: right;
  flex: none;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-faint);
}
.sec-head .meta .subtitle {
  color: var(--fg-dim);
  font-size: 14px;
  font-family: var(--body);
  letter-spacing: normal;
  text-transform: none;
}
.sec-head .meta .btn { margin-top: 8px; }
/* On narrow screens the headline + the `flex:none` right-aligned meta can't
   sit side by side without overflowing the viewport (e.g. home "01 / Products
   · Synths, beat-makers, visuals"). Stack them and left-align the meta. */
@media (max-width: 700px) {
  .sec-head {
    flex-direction: column;
    align-items: start;
    gap: 12px;
  }
  .sec-head .meta {
    text-align: left;
    align-items: start;
  }
}
.section {
  padding: 96px 0;
  border-bottom: 0.5px solid var(--line);
}
.section.tight {
  padding: 64px 0;
}

/* ---------- Products grid ---------- */
.products {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: 16px;
  /* Each card sizes to its own content (5/4 visual + title) instead of
     stretching to the tallest card in the row — a narrow s4 card next to
     a wide s8 card would otherwise gain empty space below its title. */
  align-items: start;
}
.pcard {
  position: relative;
  isolation: isolate;
  border: 1px solid;
  border-color: var(--line);
  border-radius: 14px;
  overflow: hidden;
  background: var(--card);
  display: flex;
  flex-direction: column;
  transition: border-color 0.3s linear, transform 0.3s linear !important;
}
.pcard:hover {
  border-color: var(--accent);
  transform: translateY(-2px);
}
.pcard:hover .pcard-title {
  transform: translateY(-2px);
}
.pcard-link {
  display: flex;
  flex-direction: column;
  height: 100%;
  color: inherit;
}
/* Product image and title link to the product page (mirrors the products-page
   .product-card pattern), so the card's main surfaces are clickable. */
.pcard .visual-link {
  display: block;
  color: inherit;
}
.pcard-title h4 a {
  color: inherit;
  text-decoration: none;
}
.pcard-title h4 a:hover {
  color: var(--accent);
}
.pcard .visual {
  /* Fixed 5/4 image box, matching .product-card on the products page,
     so the padding around the product shot reads identically on both.
     The title block sits below at its natural height (card height =
     visual + title) rather than the whole card being 5/4.

     Uses the padding-bottom ratio hack rather than `aspect-ratio` so the
     box keeps its height on older mobile WebViews (e.g. Samsung Internet /
     the DuckDuckGo browser): there, an `aspect-ratio`-only box whose sole
     child is `position:absolute; inset:0` + `container-type:inline-size`
     collapses to near-zero, shrinking the product shot to a thumbnail.
     4/5 = 80% keeps the 5:4 ratio. Mirrors `.product-card.short .visual`. */
  position: relative;
  overflow: hidden;
  height: 0;
  padding-bottom: 80%;
}

/* Shared product-image treatment for .pcard (homepage) and .product-card
   (products page): solid base colour + two masked layers stacked on the
   pseudo-elements.
     ::before = at-rest "normal" glow (always visible, masked colour
                from --product-glow-base-color at --product-glow-base-opacity)
     ::after  = hover sweep (0 → --product-glow-opacity on hover,
                masked colour from --product-glow-color)
   Both pseudos use the same glow-background.png as a mask, so the
   at-rest texture and the hover sweep occupy exactly the same shapes;
   only colour and opacity change.

   `!important` on the base background beats the global
   `:root[data-theme="light"] .ph` placeholder rule above.
   container-type: inline-size establishes a query container so .ph-img
   can size its box-shadow in cqw units. */
.pcard .visual .ph,
.product-card .visual .ph,
.rcard .shot .ph {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  background: var(--product-glow-bg) !important;
  transition: background-color 0.35s ease;
  container-type: inline-size;
}
.pcard .visual .ph::before,
.product-card .visual .ph::before,
.rcard .shot .ph::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--product-glow-base-color);
  -webkit-mask: url("https://cdn.imaginando.pt/website3/global/glow-background.webp") center center / 252% 252% no-repeat;
          mask: url("https://cdn.imaginando.pt/website3/global/glow-background.webp") center center / 252% 252% no-repeat;
  opacity: var(--product-glow-base-opacity);
  pointer-events: none;
  z-index: 0;
  transform: scale(1.5);
}
.pcard .visual .ph::after,
.product-card .visual .ph::after,
.rcard .shot .ph::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--product-glow-color);
  -webkit-mask: url("https://cdn.imaginando.pt/website3/global/glow-background.webp") center center / 252% 252% no-repeat;
          mask: url("https://cdn.imaginando.pt/website3/global/glow-background.webp") center center / 252% 252% no-repeat;
  opacity: 0;
  transition: opacity 1s ease;
  pointer-events: none;
  z-index: 0;
}
.pcard:hover .ph::after,
.product-card:hover .ph::after,
.rcard:hover .ph::after {
  opacity: var(--product-glow-opacity);
}
.pcard .visual .ph-img,
.product-card .visual .ph-img,
.rcard .shot .ph-img {
  position: relative;
  z-index: 1;
  max-width: 78%;
  max-height: 82%;
  width: auto;
  height: auto;
  object-fit: contain;
  /* Shadow value `--product-shadow` is defined earlier in this file
     (dark default + light-mode override). It uses cqw units that resolve
     against the .ph container so the shadow scales with each card. */
  box-shadow: var(--product-shadow);
  transition: transform 1s cubic-bezier(0.2, 0.7, 0.2, 1);
}
.pcard:hover .ph .ph-img,
.product-card:hover .ph .ph-img,
.rcard:hover .ph .ph-img {
  transform: scale(1.05);
}

.pcard .corner {
  position: absolute;
  top: 12px;
  left: 12px;
  z-index: 2;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  color: var(--fg-dim);
  text-transform: uppercase;
}
.pcard .corner-r {
  position: absolute;
  top: 12px;
  right: 12px;
  z-index: 2;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  color: var(--fg-faint);
  text-transform: uppercase;
}
.pcard-title {
  padding: 18px;
  border-top: 0.5px solid var(--line);
  transition: transform 0.3s ease;
}
.pcard-title h4 {
  font-family: var(--display);
  font-weight: 600;
  font-size: 20px;
  letter-spacing: -0.01em;
  margin: 0 0 4px;
}
.pcard-title h4 .tag {
  color: var(--fg-faint);
  font-weight: 400;
}
.pcard-title p {
  margin: 0;
  color: var(--fg-dim);
  font-size: 13px;
  line-height: 1.45;
  text-wrap: pretty;
}
.pcard-title .row {
  display: flex;
  justify-content: space-between;
  align-items: end;
  gap: 12px;
  margin-top: 10px;
}
.pcard-title .row .price {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--fg-faint);
  letter-spacing: 0.06em;
}
/* On-sale price cluster (refs #415), shared across every marketing surface +
   the nav cart. `.sale-tag` = the "On sale" pill (accent background, black
   text); the promoted price (`.now` / `.num` / `.amt`) leads in the accent
   colour; the struck original (`.was`) follows, smaller (~half). Black text is
   fixed so the pill stays legible on the bright accent in light + dark. */
.sale-tag {
  display: inline-block;
  background: var(--accent);
  color: #0a0a0a;
  font-family: var(--mono);
  font-weight: 600;
  font-size: 10px;
  line-height: 1;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: 3px 5px;
  border-radius: 4px;
  vertical-align: middle;
  white-space: nowrap;
}

/* Home product strip. */
.pcard-title .row .price .sale-tag { margin-right: 6px; }
.pcard-title .row .price .now { font-weight: 600; font-size: 13px; color: var(--accent); }
.pcard-title .row .price .was {
  font-size: 9px;
  text-decoration: line-through;
  opacity: 0.7;
  margin: 0 4px 0 5px;
}
.pcard-title .row .price:has(.was) .now { color: var(--accent); }
/* Right-side CTA cluster: inline "Add" + open-details arrow. Kept static so the
   arrow's stretched-link overlay below anchors to .pcard (not this cluster). */
.pcard-title .row .card-ctas {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: none;
}
.pcard .add-cta {
  height: 32px;
  padding: 0 14px;
  border-radius: 999px;
  border: 0.5px solid var(--line-strong);
  display: grid;
  place-items: center;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg-dim);
  text-decoration: none;
  white-space: nowrap;
  transition: all 0.25s ease;
  /* Above the stretched-link overlay so its own click wins. */
  position: relative;
  z-index: 1;
}
.pcard .add-cta:hover {
  background: var(--accent);
  color: var(--accent-ink);
  border-color: var(--accent);
}
.pcard .arrow-cta {
  flex: none;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: 0.5px solid var(--line-strong);
  display: grid;
  place-items: center;
  font-size: 14px;
  color: var(--fg-dim);
  text-decoration: none;
  transition: all 0.25s ease;
}
.pcard:hover .arrow-cta {
  background: var(--accent);
  color: var(--accent-ink);
  border-color: var(--accent);
}

.pcard.s4 {
  grid-column: span 4;
}
.pcard.s6 {
  grid-column: span 6;
}
.pcard.s8 {
  grid-column: span 8;
}
.pcard.s12 {
  grid-column: span 12;
}
@media (max-width: 1000px) {
  .pcard.s4 {
    grid-column: span 6;
  }
  .pcard.s6 {
    grid-column: span 6;
  }
  .pcard.s8 {
    grid-column: span 12;
  }
}
@media (max-width: 640px) {
  .pcard.s4,
  .pcard.s6,
  .pcard.s8 {
    grid-column: span 12;
  }
}

/* ---------- Density ---------- */
body[data-density="comfy"] .section {
  padding: 128px 0;
}
body[data-density="compact"] .section {
  padding: 72px 0;
}
body[data-density="compact"] .hero {
  padding: 36px 0 40px;
}

/* ---------- Anniversary ---------- */
.anniv {
  position: relative;
  padding: 120px 0;
  border-bottom: 0.5px solid var(--line);
  overflow: hidden;
}
.anniv .mono { margin-bottom: 32px; }
.anniv-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 64px;
  align-items: center;
}
@media (max-width: 900px) {
  .anniv-grid {
    grid-template-columns: 1fr;
    gap: 40px;
  }
}
.anniv .ten {
  font-family: var(--display);
  font-weight: 500;
  font-size: clamp(180px, 28vw, 460px);
  line-height: 0.85;
  letter-spacing: -0.05em;
  color: var(--fg);
  position: relative;
  display: inline-block;
}
.anniv .ten span.years {
  display: block;
  font-size: 0.18em;
  letter-spacing: 0.04em;
  color: var(--fg-dim);
  margin-top: 0.4em;
}
.anniv .ten em {
  font-family: var(--serif);
  font-style: italic;
  font-weight: 400;
  color: var(--accent);
  font-size: 0.55em;
  display: inline-block;
  vertical-align: baseline;
  transform: translateY(0.05em);
  margin-left: -0.04em;
}
.anniv-text h3 {
  font-family: var(--display);
  font-weight: 500;
  font-size: clamp(28px, 3vw, 42px);
  line-height: 1.05;
  letter-spacing: -0.02em;
  margin: 0 0 20px;
  text-wrap: balance;
}
.anniv-text p {
  color: var(--fg-dim);
  font-size: 16px;
  line-height: 1.6;
  max-width: 48ch;
  text-wrap: pretty;
}
.timeline {
  margin-top: 32px;
  display: flex;
  flex-direction: column;
  gap: 0;
  border-top: 0.5px solid var(--line);
}
.timeline .row {
  display: grid;
  grid-template-columns: 80px 1fr;
  gap: 20px;
  padding: 14px 0;
  border-bottom: 0.5px solid var(--line);
  align-items: baseline;
}
.timeline .row .y {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--fg-faint);
  letter-spacing: 0.08em;
}
.timeline .row .t {
  font-family: var(--display);
  font-size: 15px;
  font-weight: 500;
}

/* ---------- News ---------- */
.news {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 18px;
}
@media (max-width: 1000px) {
  .news {
    grid-template-columns: repeat(2, 1fr);
  }
}
@media (max-width: 560px) {
  .news {
    grid-template-columns: 1fr;
  }
}
.ncard {
  display: flex;
  flex-direction: column;
  gap: 14px;
  border: 0.5px solid var(--line);
  border-radius: 12px;
  padding: 14px;
  background: var(--card);
  transition: border-color 0.25s ease, transform 0.25s ease;
}
.ncard:hover {
  border-color: var(--line-strong);
  transform: translateY(-2px);
}
.ncard .img {
  aspect-ratio: 16/9;
  border-radius: 8px;
  overflow: hidden;
  position: relative;
}
.ncard .img .ph {
  position: absolute;
  inset: 0;
}
.ncard .img img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.ncard .date {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--fg-faint);
  margin: 14px 0 14px;
}
.ncard h4 {
  font-family: var(--display);
  font-weight: 600;
  font-size: 17px;
  line-height: 1.2;
  margin: 0 0 6px;
  letter-spacing: -0.01em;
  text-wrap: balance;
}
.ncard p {
  margin: 0;
  color: var(--fg-dim);
  font-size: 13px;
  line-height: 1.5;
  text-wrap: pretty;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* ---------- Reviews ---------- */
.reviews {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 18px;
}
@media (max-width: 900px) {
  .reviews {
    grid-template-columns: 1fr;
  }
}
.rcard {
  border: 0.5px solid var(--line);
  border-radius: 14px;
  padding: 24px;
  background: var(--card);
  display: flex;
  flex-direction: column;
  gap: 14px;
  min-height: 260px;
}
.rcard .stars {
  color: var(--accent);
  letter-spacing: 0.18em;
  font-size: 14px;
}
.rcard h5 {
  font-family: var(--display);
  font-weight: 600;
  font-size: 18px;
  letter-spacing: -0.01em;
  margin: 0;
  text-wrap: balance;
}
.rcard p {
  margin: 0;
  color: var(--fg-dim);
  font-size: 14px;
  line-height: 1.55;
  text-wrap: pretty;
  flex: 1;
  /* Live App Store reviews vary wildly in length — clamp to ~5 lines so
   * cards stay even and the section doesn't sprawl. */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 5;
  line-clamp: 5;
  overflow: hidden;
}
.rcard .by {
  display: flex;
  justify-content: space-between;
  align-items: center;
  color: var(--fg-faint);
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  border-top: 0.5px dashed var(--line);
  padding-top: 12px;
}

/* ---------- Community strip ---------- */
.reviews + .strip { margin-top: 48px; }
.strip {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0;
  border-top: 0.5px solid var(--line);
  border-bottom: 0.5px solid var(--line);
}
.strip .cell {
  padding: 32px 24px;
  border-right: 0.5px solid var(--line);
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.strip .cell:last-child {
  border-right: 0;
}
.strip .cell .num {
  font-family: var(--display);
  font-weight: 500;
  font-size: 48px;
  letter-spacing: -0.02em;
  line-height: 1;
}
.strip .cell .lbl {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--fg-faint);
}
@media (max-width: 900px) {
  .strip {
    grid-template-columns: repeat(2, 1fr);
  }
  .strip .cell:nth-child(2) {
    border-right: 0;
  }
  .strip .cell:nth-child(-n + 2) {
    border-bottom: 0.5px solid var(--line);
  }
}

/* ---------- Edu/Mobile split block — shared `.sblock.warm` overrides ----------
 *
 * The accent-tinted "education" card (.sblock.warm) renders its
 * "50%" headline at a much larger scale than the neutral .sblock
 * variant, and the body copy is width-clamped so the big number can
 * dominate. These overrides ship with the product-template's design
 * (Imaginando Product Template.html) and apply to every product page
 * that mounts an .sblock.warm card — currently grfx / dlym / k7d /
 * tv3 / product-template; future product pages get it for free.
 *
 * The base `.sblock` chrome stays per-page (paddings + bg differ
 * slightly between products). These three rules only touch the warm
 * variant, so they don't fight per-page customization. Specificity
 * (0,3,0 / 0,2,1 / 0,3,0) is high enough to win over the per-page
 * `.sblock .num { font-size: 48–64px }` baseline.
 *
 * Keep the `%` glyph styling separate: per-page `.sblock .num em`
 * rules give it the serif italic look — we deliberately don't set
 * font-family / style here so that styling survives. */
.sblock.warm p { max-width: 38ch; }
.sblock.warm .row {
  justify-content: space-between;
  align-items: flex-end;
  gap: 18px;
}
.sblock.warm .num {
  font-size: clamp(96px, 12vw, 150px);
  letter-spacing: -0.04em;
  line-height: 0.95;
}

/* The base `.btn.outline` (border + text = `var(--accent)`) is defined up in
 * the button group. Inside an `.sblock.warm` card the background already IS
 * `var(--accent)`, so that outline button paints invisible-on-invisible until
 * the user hovers — at which point the hover rule swaps the text to
 * `var(--accent-ink)` and the button suddenly appears. Override here so the
 * outline button reads against the warm background at rest. Higher specificity
 * (0,3,0) than the base `.btn.outline` (0,2,0) so it wins regardless. */
.sblock.warm .btn.outline {
  border-color: var(--accent-ink);
  color: var(--accent-ink);
}
.sblock.warm .btn.outline:hover {
  background: var(--accent-ink);
  color: var(--accent);
}

/* ---------- Split CTAs ---------- */
.split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 18px;
}
@media (max-width: 900px) {
  .split {
    grid-template-columns: 1fr;
  }
}
.split .block {
  position: relative;
  border: 0.5px solid var(--line);
  border-radius: 18px;
  padding: 36px;
  min-height: 340px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  overflow: hidden;
  background: var(--card);
}
.split .block.accent {
  background: var(--accent);
  color: var(--accent-ink);
  border-color: var(--accent);
}
.split .block.accent .mono {
  color: rgba(0, 0, 0, 0.55);
}
.split .block.accent .btn {
  border-color: rgba(0, 0, 0, 0.35);
  color: var(--accent-ink);
}
.split .block .socials {
  display: flex;
  gap: 14px;
  font-family: var(--mono);
  font-size: 11px;
  color: var(--fg-dim);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.split .block h3 {
  font-family: var(--display);
  font-weight: 500;
  font-size: 32px;
  letter-spacing: -0.02em;
  line-height: 1.05;
  margin: 0 0 12px;
  text-wrap: balance;
}
.split .block p {
  color: var(--fg-dim);
  max-width: 38ch;
  text-wrap: pretty;
  font-size: 15px;
}
.split .block.accent p {
  color: rgba(0, 0, 0, 0.7);
}
.split .block .row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  margin-top: 24px;
}
.split .block .num {
  font-family: var(--display);
  font-weight: 500;
  font-size: 120px;
  line-height: 1;
  letter-spacing: -0.04em;
  opacity: 0.95;
}

/* ---------- Newsletter ---------- */
.nl {
  display: grid;
  grid-template-columns: 1.2fr 1fr;
  gap: 48px;
  align-items: center;
}
/* Grid items default to `min-width: auto`, so each `1fr` track is floored at
   its content's min-content width. The signup column's min-content is the
   form's (~315px: the email input keeps a ~183px intrinsic size from its
   default `size=20`, plus the 112px button). That floor stops the column
   shrinking on narrow viewports (~≤360 CSS-px, e.g. an Android device with a
   larger Display-size setting) and pushed the whole `.nl` wider than the
   screen. `min-width: 0` lets the tracks shrink so the form (whose input is
   already `min-width: 0`) can reflow to fit. */
.nl > * {
  min-width: 0;
}
@media (max-width: 900px) {
  .nl {
    grid-template-columns: 1fr;
    gap: 24px;
  }
}
.nl h3 {
  font-family: var(--display);
  font-weight: 500;
  font-size: clamp(28px, 3.4vw, 46px);
  line-height: 1.02;
  letter-spacing: -0.025em;
  margin: 12px 0 14px;
  text-wrap: balance;
}
.nl p {
  color: var(--fg-dim);
  font-size: 15px;
  line-height: 1.55;
  max-width: 46ch;
}
.nl-form {
  display: flex;
  border: 0.5px solid var(--line-strong);
  border-radius: 999px;
  padding: 6px;
  gap: 6px;
  background: var(--card);
  max-width: 520px;
}
.nl-form input {
  flex: 1;
  /* Flex items default to `min-width: auto`, so the email input refuses to
     shrink below its intrinsic content width; paired with the button's
     `min-width`, that overflowed the pill on narrow screens (visible as page
     horizontal-scroll on older Android WebViews, which ignore the `main`
     `overflow-x: clip` guard). `min-width: 0` lets the input shrink to fit. */
  min-width: 0;
  border: 0;
  background: transparent;
  color: var(--fg);
  font: inherit;
  padding: 0 18px;
  height: 44px;
  outline: none;
  font-size: 14px;
}
.nl-form input::placeholder {
  color: var(--fg-faint);
}
.nl-form button {
  height: 44px;
  padding: 0 22px;
  border-radius: 999px;
  border: 0;
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: 500;
  font-size: 13.5px;
  cursor: pointer;
  transition: background 0.25s ease;
  /* flex + min-width keep the pill a stable size when the label swaps for the
     loading spinner (see .nl-spin). */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 112px;
}
.nl-form button:hover {
  background: color-mix(in oklab, var(--accent) 88%, white);
}
.nl-form button:disabled {
  cursor: default;
}
/* Invalid state — client-side email validation / API error tints the pill. */
.nl-form.invalid {
  border-color: var(--bad, #ff7a6b);
}
.nl-form.invalid input {
  color: var(--bad, #ff7a6b);
}
/* Loading spinner shown inside the button in place of its label. */
.nl-spin {
  width: 16px;
  height: 16px;
  border: 2px solid color-mix(in oklab, var(--accent-ink) 35%, transparent);
  border-top-color: var(--accent-ink);
  border-radius: 50%;
  animation: nl-spin 0.7s linear infinite;
}
@keyframes nl-spin {
  to {
    transform: rotate(360deg);
  }
}
/* Error line under the form — collapsed until JS adds .show. */
.nl-msg {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 12.5px;
  line-height: 1.4;
  color: var(--bad, #ff7a6b);
  max-width: 520px;
  overflow: hidden;
  max-height: 0;
  opacity: 0;
  margin-top: 0;
  transition: max-height 0.25s ease, opacity 0.25s ease, margin-top 0.25s ease;
}
.nl-msg.show {
  max-height: 40px;
  opacity: 1;
  margin-top: 10px;
}
.nl-msg svg {
  flex: none;
}
/* `display:flex` on .nl-form / .nl-success outweighs the `hidden` attribute's
   weak UA `display:none`, so JS toggling `hidden` wouldn't hide them without
   this. Keeps the success card hidden on load and the form hidden post-submit. */
.nl-form[hidden],
.nl-success[hidden] {
  display: none;
}
/* Success card — replaces the form on a completed subscribe. */
.nl-success {
  display: flex;
  align-items: center;
  gap: 14px;
  max-width: 520px;
  padding: 16px 18px;
  border: 0.5px solid color-mix(in oklab, var(--accent) 45%, var(--line-strong));
  border-radius: 16px;
  background: color-mix(in oklab, var(--accent) 8%, var(--card));
  animation: nl-pop 0.4s cubic-bezier(0.2, 0.8, 0.2, 1);
}
@keyframes nl-pop {
  from {
    opacity: 0;
    transform: translateY(8px);
  }
}
.nl-check {
  flex: none;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--accent);
  color: var(--accent-ink);
}
.nl-success-txt {
  display: flex;
  flex-direction: column;
  gap: 3px;
  font-size: 14px;
  line-height: 1.45;
}
.nl-success-txt strong {
  font-weight: 600;
  color: var(--fg);
}
.nl-success-txt span {
  color: var(--fg-dim);
  font-size: 13px;
}
.nl-success-txt b {
  color: var(--fg);
  font-weight: 500;
}
@media (prefers-reduced-motion: reduce) {
  .nl-spin {
    animation-duration: 1.4s;
  }
  .nl-success {
    animation: none;
  }
}

/* ---------- Footer ---------- */
footer {
  padding: 80px 0 32px;
  border-top: 0.5px solid var(--line);
}
.foot-grid {
  display: grid;
  grid-template-columns: 1.5fr repeat(4, 1fr);
  gap: 32px;
  padding-bottom: 48px;
  border-bottom: 0.5px solid var(--line);
}
@media (max-width: 900px) {
  .foot-grid {
    grid-template-columns: 1fr 1fr;
    gap: 32px 24px;
  }
}
/* Phone: single stacked column. Brand on top, then each section in order,
   copyright last. Link lists stay vertical (clean tap targets); the Products
   sub-lists keep their own inline dot-separated styling. */
@media (max-width: 600px) {
  .foot-grid {
    grid-template-columns: 1fr;
    gap: 28px;
  }
}
.foot-col {
  /* Allow grid tracks to shrink below their content's min-content width so
     long inline product lists wrap instead of overflowing the page. */
  min-width: 0;
}
.foot-col h6 {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--fg-faint);
  margin: 0 0 16px;
  font-weight: 500;
}
.foot-col ul {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
  font-size: 14px;
  color: var(--fg-dim);
}
.foot-col ul li:hover {
  color: var(--fg);
}
.foot-bottom {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding-top: 24px;
  font-family: var(--mono);
  font-size: 11px;
  color: var(--fg-faint);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.foot-brand {
  display: flex;
  flex-direction: column;
  gap: 14px;
  max-width: 340px;
}
.foot-brand p {
  color: var(--fg-dim);
  font-size: 13.5px;
  line-height: 1.5;
  margin: 0;
}
.foot-social {
  list-style: none;
  padding: 0;
  margin: 6px 0 0;
  display: flex;
  gap: 24px;
}
.foot-social a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--fg);
  transition: color 0.2s, transform 0.2s;
}
.foot-social a:hover {
  color: var(--accent);
  transform: translateY(-2px);
}
.foot-social-icon { display: block; }
.foot-col.foot-products .sub {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--fg-faint);
  font-weight: 500;
  margin: 14px 0 8px;
}
.foot-col.foot-products > ul:first-of-type {
  margin-bottom: 4px;
}
.foot-col.foot-products .sub + ul {
  display: block;
  /* Wrap the dot-separated product list onto multiple lines instead of
     forcing a single nowrap line — a nowrap line sizes the grid track to
     its min-content and blows the footer past the viewport on mobile. */
  white-space: normal;
}
.foot-col.foot-products .sub + ul li {
  font-size: 13px;
  display: inline;
}
.foot-col.foot-products .sub + ul li:not(:last-child)::after {
  content: "·";
  color: var(--fg-faint);
  margin: 0 6px;
  font-size: 14px;
}
.foot-col.foot-products ul.inline-list {
  display: block !important;
  white-space: normal;
}
.foot-col.foot-products ul.inline-list li {
  font-size: 13px;
  display: inline !important;
}
.foot-col.foot-products ul.inline-list li:not(:last-child)::after {
  content: " · ";
  color: var(--fg-faint);
  margin: 3px;
  font-size: 14px;
}

/* ---------- Reveal-on-scroll ---------- */
.reveal {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity 0.9s ease, transform 0.9s ease;
}
.reveal.in {
  opacity: 1;
  transform: none;
}

/* ---------- Light-mode card / placeholder reskins ---------- */
:root[data-theme="light"] .pcard,
:root[data-theme="light"] .ncard,
:root[data-theme="light"] .rcard,
:root[data-theme="light"] .nl-form,
:root[data-theme="light"] .split .block:not(.accent) {
  background: var(--card) !important;
}

/* ============================================================
 * Long-document pages (privacy-policy, terms-and-conditions, …)
 *
 * Shared layout + components for pages built from the page-head /
 * utility-bar / doc / ferry mixin set. Any block that's specific to
 * a single document (e.g. privacy's rights-grid, basis-table,
 * vendors, controller-card) stays in that page's own sheet.
 * ============================================================ */

html { scroll-behavior: smooth; }
body {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}
main {
  flex: 1;
  /* Contains full-bleed (100vw / -50vw) content without making `body` a
     scroll/clip context — see the note on the `body` rule above. `clip`
     (not `hidden`) keeps vertical overflow visible and doesn't create a
     scroll container, so any sticky element inside main still works. */
  overflow-x: clip;
}

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

.page-head {
  padding: 36px 0 56px;
  border-bottom: 0.5px solid var(--line);
  position: relative;
  overflow: hidden;
}
/*.page-head::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image:
    linear-gradient(to right, rgba(244, 241, 234, 0.04) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(244, 241, 234, 0.04) 1px, transparent 1px);
  background-size: 64px 64px;
  mask-image: radial-gradient(
    ellipse 70% 60% at 30% 40%, #000 30%, transparent 80%
  );
  -webkit-mask-image: radial-gradient(
    ellipse 70% 60% at 30% 40%, #000 30%, transparent 80%
  );
}
:root[data-theme="light"] .page-head::before {
  background-image:
    linear-gradient(to right, rgba(10, 10, 12, 0.06) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(10, 10, 12, 0.06) 1px, transparent 1px);
}
*/
.crumbs {
  display: flex;
  gap: 10px;
  align-items: center;
  color: var(--fg-faint);
  margin-bottom: 18px;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  position: relative;
  flex-wrap: wrap;
}
.crumbs a {
  color: var(--fg-faint);
  transition: color 0.2s ease;
}
.crumbs a:hover { color: var(--fg); }
.crumbs .sep { color: var(--fg-faint); }
.crumbs .here { color: var(--fg); }

.page-head h1 {
  font-family: var(--display);
  font-weight: 500;
  font-size: clamp(56px, 8.4vw, 140px);
  line-height: 0.9;
  letter-spacing: -0.035em;
  margin: 0;
  text-wrap: balance;
  position: relative;
}
.page-head h1 em {
  font-family: var(--serif);
  font-style: italic;
  font-weight: 400;
  color: var(--accent);
}

.head-meta {
  display: grid;
  grid-template-columns: 1.05fr 0.95fr;
  gap: 48px;
  margin-top: 48px;
  align-items: end;
  position: relative;
}
@media (max-width: 900px) {
  .head-meta { grid-template-columns: 1fr; gap: 24px; }
}
.head-meta .lede {
  font-size: 18px;
  line-height: 1.55;
  color: var(--fg-dim);
  max-width: 48ch;
  margin: 0;
  text-wrap: pretty;
}
.head-meta .lede em {
  font-family: var(--serif);
  font-style: italic;
  color: var(--fg);
}
.head-meta .stamps {
  display: flex;
  gap: 10px;
  justify-content: flex-end;
  flex-wrap: wrap;
}
@media (max-width: 900px) {
  .head-meta .stamps { justify-content: flex-start; }
}
.stamp {
  display: inline-flex;
  flex-direction: column;
  gap: 6px;
  padding: 14px 18px;
  border: 0.5px solid var(--line);
  border-radius: 12px;
  background: var(--card);
  min-width: 130px;
}
.stamp .k {
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--fg-faint);
}
.stamp .v {
  font-family: var(--display);
  font-weight: 500;
  font-size: 16px;
  letter-spacing: -0.005em;
  color: var(--fg);
}
.stamp .v em {
  font-family: var(--serif);
  font-style: italic;
  font-weight: 400;
  color: var(--accent);
}

/* ---------- sticky utility bar ---------- */

.utility {
  position: sticky;
  top: 64px;
  z-index: 40;
  background: color-mix(in oklab, var(--bg) 92%, transparent);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border-bottom: 0.5px solid var(--line);
}
.utility-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  height: 48px;
  gap: 16px;
}
.utility .crumbs2 {
  display: flex;
  align-items: center;
  gap: 14px;
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--fg-faint);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.utility .crumbs2 .num { color: var(--accent); }
.utility .crumbs2 .here { color: var(--fg); }
.utility .acts {
  display: flex;
  gap: 6px;
  align-items: center;
  flex-shrink: 0;
}
.utility .ubtn {
  height: 30px;
  padding: 0 12px;
  border: 0.5px solid var(--line);
  border-radius: 999px;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--fg-dim);
  display: inline-flex;
  align-items: center;
  gap: 6px;
  transition: all 0.2s ease;
  background: transparent;
  cursor: pointer;
}
.utility .ubtn:hover {
  border-color: var(--fg);
  color: var(--fg);
}
.utility .ubtn svg { width: 11px; height: 11px; }
@media (max-width: 600px) {
  .utility .crumbs2 .full { display: none; }
}

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

.doc {
  padding: 64px 0 96px;
  border-bottom: 0.5px solid var(--line);
}
.doc-grid {
  display: grid;
  grid-template-columns: 260px 1fr;
  gap: 64px;
  align-items: start;
}
@media (max-width: 1000px) {
  .doc-grid { grid-template-columns: 1fr; gap: 32px; }
}

/* sticky TOC */
.toc {
  position: sticky;
  top: 128px;
  align-self: start;
}
@media (max-width: 1000px) {
  .toc {
    position: static;
    top: auto;
    border: 0.5px solid var(--line);
    border-radius: 14px;
    padding: 18px;
    background: var(--card);
  }
}
.toc h6 {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--fg-faint);
  margin: 0 0 18px;
  font-weight: 500;
}
.toc ol {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0;
  border-left: 0.5px solid var(--line);
}
.toc li { position: relative; }
.toc a {
  display: flex;
  gap: 12px;
  padding: 10px 0 10px 16px;
  font-size: 13.5px;
  color: var(--fg-dim);
  line-height: 1.35;
  transition: color 0.2s ease, padding 0.2s ease, border-color 0.2s ease;
  border-left: 1.5px solid transparent;
  margin-left: -0.5px;
}
.toc a:hover { color: var(--fg); padding-left: 20px; }
.toc a .num {
  font-family: var(--mono);
  font-size: 10.5px;
  color: var(--fg-faint);
  flex-shrink: 0;
  width: 22px;
  letter-spacing: 0.04em;
}
.toc a .label {
  flex: 1;
  text-wrap: balance;
}
.toc li.active a {
  color: var(--fg);
  border-left-color: var(--accent);
  padding-left: 20px;
}
.toc li.active a .num { color: var(--accent); }
.toc .progress {
  margin-top: 22px;
  padding-top: 18px;
  border-top: 0.5px solid var(--line);
}
.toc .progress .label {
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--fg-faint);
  display: flex;
  justify-content: space-between;
  margin-bottom: 8px;
}
.toc .progress .bar {
  height: 3px;
  background: var(--hatch);
  border-radius: 2px;
  overflow: hidden;
}
.toc .progress .bar i {
  display: block;
  height: 100%;
  background: var(--accent);
  width: 0;
  transition: width 0.15s linear;
  border-radius: inherit;
}

/* ---------- numbered sections ----------
   Scoped to `.sec` so the doc page's `.sec-head` (number + h2 + perma,
   baseline-aligned) doesn't collide with the homepage's `.sec-head`
   (heading + right-side meta, justified). */

.doc-body {
  display: flex;
  flex-direction: column;
  gap: 0;
}
.sec {
  padding: 0 0 80px;
  scroll-margin-top: 128px;
  border-bottom: 0.5px dashed var(--line);
  margin-bottom: 80px;
}
.sec:last-child {
  border-bottom: 0;
  margin-bottom: 0;
  padding-bottom: 0;
}
.sec .sec-head {
  display: flex;
  gap: 24px;
  align-items: baseline;
  margin-bottom: 8px;
  position: relative;
  justify-content: flex-start;
}
.sec .sec-head .num {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.14em;
  color: var(--fg-faint);
  padding-top: 14px;
}
.sec .sec-head h2 {
  font-family: var(--display);
  font-weight: 500;
  font-size: clamp(34px, 4.6vw, 52px);
  line-height: 1;
  letter-spacing: -0.025em;
  margin: 0;
  flex: 1;
  text-wrap: balance;
  max-width: none;
}
.sec .sec-head h2 em {
  font-family: var(--serif);
  font-style: italic;
  font-weight: 400;
  color: var(--accent);
}
.sec .sec-head .perma {
  opacity: 0;
  transition: opacity 0.2s ease, color 0.2s ease;
  color: var(--fg-faint);
  font-family: var(--mono);
  font-size: 14px;
  padding: 14px 6px 0;
}
.sec:hover .sec-head .perma { opacity: 1; }
.sec .sec-head .perma:hover { color: var(--accent); }

/* tl;dr summary box */
.tldr {
  margin: 24px 0 28px;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 18px;
  align-items: start;
  padding: 18px 22px;
  border: 0.5px solid var(--line);
  border-radius: 14px;
  background: linear-gradient(
    180deg,
    color-mix(in oklab, var(--accent) 6%, transparent),
    transparent
  );
}
.tldr .tag {
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent-ink);
  background: var(--accent);
  padding: 5px 9px;
  border-radius: 6px;
  line-height: 1;
  align-self: flex-start;
  transform: translateY(2px);
}
.tldr p {
  margin: 0;
  font-family: var(--body);
  font-size: 16px;
  line-height: 1.6;
  color: var(--fg);
  text-wrap: pretty;
}
.tldr p em { font-style: normal; font-weight: 600; color: var(--fg); }

/* prose */
.prose {
  font-size: 15.5px;
  line-height: 1.65;
  color: var(--fg-dim);
  text-wrap: pretty;
}
.prose p { margin: 0 0 16px; }
.prose p:last-child { margin-bottom: 0; }
.prose strong { color: var(--fg); font-weight: 600; }
.prose em {
  font-family: var(--serif);
  font-style: italic;
  color: var(--fg);
}
.prose a {
  color: var(--fg);
  border-bottom: 0.5px solid var(--line-strong);
  transition: border-color 0.2s ease;
}
.prose a:hover { border-color: var(--accent); }
.muted-note {
  color: var(--fg-dim);
  font-size: 13.5px;
  border-top: 0.5px solid var(--line);
  padding-top: 14px;
  margin-top: 18px;
}

/* clause cards */
.clauses {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 18px 0;
  padding: 0;
  list-style: none;
}
.clauses li {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 16px;
  align-items: start;
  padding: 14px 18px;
  border: 0.5px solid var(--line);
  border-radius: 10px;
  background: var(--card);
  color: var(--fg);
  font-size: 14.5px;
  line-height: 1.55;
  transition: border-color 0.2s ease, transform 0.2s ease;
}
.clauses li:hover {
  border-color: var(--line-strong);
  transform: translateX(3px);
}
.clauses li .bullet {
  font-family: var(--mono);
  font-size: 10px;
  color: var(--accent);
  letter-spacing: 0.1em;
  background: color-mix(in oklab, var(--accent) 18%, transparent);
  padding: 4px 7px;
  border-radius: 5px;
  line-height: 1;
  align-self: flex-start;
  transform: translateY(2px);
}
.clauses li strong { color: var(--fg); font-weight: 600; }
.clauses li a {
  color: var(--fg);
  border-bottom: 0.5px solid var(--line-strong);
  transition: border-color 0.2s ease;
}
.clauses li a:hover { border-color: var(--accent); }

/* warning callouts */
.callout {
  margin: 18px 0;
  padding: 18px 22px;
  border: 0.5px solid var(--line);
  border-left: 2px solid var(--accent);
  border-radius: 0 12px 12px 0;
  background: var(--card);
  font-size: 14.5px;
  line-height: 1.6;
  color: var(--fg);
}
.callout .head {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--fg-faint);
  margin-bottom: 8px;
}
.callout p { margin: 0 0 8px; }
.callout p:last-child { margin-bottom: 0; }
.callout strong { color: var(--fg); }
.callout a {
  color: var(--fg);
  border-bottom: 0.5px solid var(--line-strong);
  transition: border-color 0.2s ease;
}
.callout a:hover { border-color: var(--accent); }

/* sub-section divider inside a section */
.sub-divider {
  display: flex;
  align-items: center;
  gap: 14px;
  margin: 32px 0 18px;
}
.sub-divider .hash {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  color: var(--fg-faint);
}
.sub-divider .line {
  flex: 1;
  height: 0.5px;
  background: var(--line);
}

/* ---------- ferry — closing CTA card ---------- */

.ferry { padding: 48px 0 96px; }
.ferry-card {
  border: 0.5px solid var(--line-strong);
  border-radius: 18px;
  background: var(--card);
  padding: 36px;
  display: grid;
  grid-template-columns: 1.2fr 1fr;
  gap: 32px;
  align-items: center;
  position: relative;
  overflow: hidden;
}
.ferry-card::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(
    circle at 100% 0%,
    color-mix(in oklab, var(--accent) 10%, transparent),
    transparent 60%
  );
}
@media (max-width: 900px) {
  .ferry-card { grid-template-columns: 1fr; padding: 28px; }
}
.ferry-card h3 {
  font-family: var(--display);
  font-weight: 500;
  font-size: clamp(24px, 2.6vw, 32px);
  line-height: 1.1;
  letter-spacing: -0.015em;
  margin: 0;
  text-wrap: balance;
  position: relative;
}
.ferry-card h3 em {
  font-family: var(--serif);
  font-style: italic;
  font-weight: 400;
  color: var(--accent);
}
.ferry-card p {
  margin: 10px 0 0;
  color: var(--fg-dim);
  font-size: 14.5px;
  line-height: 1.55;
  text-wrap: pretty;
  position: relative;
}
.ferry-actions {
  display: flex;
  flex-direction: column;
  gap: 10px;
  position: relative;
}
.ferry-link {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 14px;
  padding: 14px 18px;
  border: 0.5px solid var(--line);
  border-radius: 12px;
  background: var(--bg);
  transition: border-color 0.2s ease, transform 0.2s ease;
}
.ferry-link:hover {
  border-color: var(--fg);
  transform: translateX(3px);
}
.ferry-link .l {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.ferry-link .l .lbl {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--fg-faint);
}
.ferry-link .l .nm {
  font-family: var(--display);
  font-weight: 500;
  font-size: 16px;
  color: var(--fg);
}
.ferry-link .arrow {
  color: var(--fg-faint);
  transition: transform 0.2s ease, color 0.2s ease;
}
.ferry-link:hover .arrow {
  color: var(--accent);
  transform: translateX(3px);
}

/* ---------- print — produce a clean PDF when the user picks the PDF action ---------- */
@media print {
  .nav,
  .utility,
  .ferry,
  footer,
  .toc,
  .crumbs,
  .page-head::before { display: none !important; }
  .doc-grid { grid-template-columns: 1fr; }
  .page-head { padding: 0 0 16px; border-bottom: 0.5px solid #000; }
  body { background: #fff; color: #000; }
  .sec { page-break-inside: avoid; border-bottom: 1px solid #000; }
  .tldr,
  .callout,
  .clauses li,
  .stamp { border: 1px solid #000; background: #fff !important; }
}

/* ============================================================
 * Shared utility — section-title
 *
 * Used as the heading inside any composed section that doesn't
 * have its own heading element (channels column on contact-us,
 * faq head, bundle filter strips, etc.).
 * ============================================================ */
.section-title {
  font-family: var(--display);
  font-weight: 500;
  font-size: 32px;
  line-height: 1.05;
  letter-spacing: -0.02em;
  margin: 0 0 24px;
  text-wrap: balance;
}
.section-title em {
  font-family: var(--serif);
  font-style: italic;
  font-weight: 400;
  color: var(--accent);
}

/* ============================================================
 * Shared — product coverage table (.product-list / .prow)
 *
 * The catalogue list rendered by the `+coverageList` mixin
 * (views/site/_sections/_product-coverage.pug) on the affiliate
 * (`.commission`) and education (`.discount`) pages. The row
 * scaffold is identical; only the price cell differs by variant.
 * Scoped under `.product-list` so it never collides with the
 * unrelated `.prow` leaderboard rows on /community.
 * ============================================================ */
.product-list {
  border-top: 0.5px solid var(--line);
  display: flex;
  flex-direction: column;
}
.product-list .prow {
  display: grid;
  grid-template-columns: 60px 1.4fr 1fr auto;
  gap: 20px;
  padding: 18px 0;
  border-bottom: 0.5px solid var(--line);
  align-items: center;
  transition: padding 0.3s ease;
}
.product-list .prow:hover { padding-left: 8px; }
.product-list .prow .idx {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--fg-faint);
  letter-spacing: 0.1em;
}
.product-list .prow .name {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.product-list .prow .name .ttl {
  font-family: var(--display);
  font-weight: 500;
  font-size: 18px;
  letter-spacing: -0.005em;
}
.product-list .prow .name .desc {
  font-family: var(--mono);
  font-size: 10.5px;
  color: var(--fg-faint);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.product-list .prow .platform {
  font-family: var(--mono);
  font-size: 10px;
  color: var(--fg-faint);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.product-list .prow .price {
  display: flex;
  flex-direction: column;
  gap: 2px;
  align-items: flex-end;
  text-align: right;
  font-family: var(--mono);
  font-size: 12px;
  color: var(--fg-dim);
  letter-spacing: 0.04em;
}
.product-list .prow .price .now {
  color: var(--fg);
  font-weight: 500;
}
.product-list .prow .price .now em {
  font-family: var(--serif);
  font-style: italic;
  font-weight: 400;
  color: var(--accent);
}
/* Discount variant (education): struck-through list price + discounted now. */
.product-list.discount .prow .price .was {
  color: var(--fg-faint);
  text-decoration: line-through;
  font-size: 11px;
}
.product-list.discount .prow .price .now em { font-size: 13px; }
/* Commission variant (affiliate): "customer pays" list + stacked "you receive". */
.product-list.commission .prow .price .pays {
  color: var(--fg-faint);
  font-size: 11px;
}
.product-list.commission .prow .price .now {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 1px;
  margin-top: 3px;
}
.product-list.commission .prow .price .now .recv-label {
  font-size: 9px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--fg-faint);
}
.product-list.commission .prow .price .now em { font-size: 22px; line-height: 1.05; }
@media (max-width: 680px) {
  .product-list .prow {
    grid-template-columns: 40px 1fr auto;
    gap: 12px;
  }
  .product-list .prow .platform { display: none; }
}

/* ============================================================
 * Values cards — 3-up grid of `.vcard`s with a mono index, display
 * h4 (with italic-accent emphasis), and body paragraph. Hoisted
 * from about-us.css when harpa-laser's Lessons section became the
 * second page to mount the same chrome.
 *
 * Markup contract:
 *   .values
 *     article.vcard(.reveal)
 *       span.num              — mono index ("01")
 *       h4                    — display title; <em> = serif italic accent
 *       p                     — body (may also carry <strong> / <em>)
 * ============================================================ */

.values {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 18px;
}
@media (max-width: 900px) {
  .values { grid-template-columns: 1fr; }
}
.vcard {
  position: relative;
  border: 0.5px solid var(--line);
  border-radius: 14px;
  padding: 28px;
  background: var(--bg-2);
  display: flex;
  flex-direction: column;
  gap: 14px;
  min-height: 240px;
}
.vcard .num {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--fg-faint);
  letter-spacing: 0.1em;
}
.vcard h4 {
  font-family: var(--display);
  font-weight: 500;
  font-size: 22px;
  letter-spacing: -0.01em;
  margin: 0;
  text-wrap: balance;
}
.vcard h4 em {
  font-family: var(--serif);
  font-style: italic;
  font-weight: 400;
  color: var(--accent);
}
.vcard p {
  margin: 0;
  color: var(--fg-dim);
  font-size: 14px;
  line-height: 1.55;
  text-wrap: pretty;
  flex: 1;
}
.vcard p strong {
  color: var(--fg);
  font-weight: 500;
}

/* ============================================================
 * Closing CTA block — accent-filled card with eyebrow + h3 + action
 * row. Hoisted from about-us.css when projects became the second
 * page to use it.
 *
 * Markup contract (any page can mount):
 *   .cta-block(.reveal)
 *     span.eyebrow.mono  — small uppercase mono label
 *     .row               — h3 (left) + .actions (right)
 *       h3                 — display heading; <em> = serif italic accent
 *       .actions           — wrap-on-mobile button row
 *         a.btn(.primary)  — `.primary` inverts to accent-ink fill
 * ============================================================ */

.cta-block {
  position: relative;
  border: 0.5px solid var(--accent);
  border-radius: 18px;
  padding: 48px;
  background: var(--accent);
  color: var(--accent-ink);
  overflow: hidden;
}
.cta-block .eyebrow {
  color: rgba(0, 0, 0, 0.55);
}
.cta-block .row {
  display: grid;
  grid-template-columns: 1.2fr 0.8fr;
  gap: 32px;
  align-items: end;
  margin-top: 18px;
}
@media (max-width: 800px) {
  .cta-block .row { grid-template-columns: 1fr; gap: 24px; }
  .cta-block { padding: 32px; }
}
.cta-block h3 {
  font-family: var(--display);
  font-weight: 500;
  font-size: clamp(32px, 4vw, 52px);
  letter-spacing: -0.02em;
  line-height: 1;
  margin: 0;
  text-wrap: balance;
}
.cta-block h3 em {
  font-family: var(--serif);
  font-style: italic;
  font-weight: 400;
}
.cta-block .actions {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  justify-content: flex-end;
}
@media (max-width: 800px) {
  .cta-block .actions { justify-content: flex-start; }
}
/* Invert the global `.btn` chrome inside the accent CTA so the buttons
 * read against the bright accent fill. */
.cta-block .btn {
  border-color: rgba(0, 0, 0, 0.35);
  color: var(--accent-ink);
}
.cta-block .btn:hover {
  border-color: var(--accent-ink);
}
.cta-block .btn.primary {
  background: var(--accent-ink);
  color: var(--accent);
  border-color: var(--accent-ink);
}
.cta-block .btn.primary:hover {
  background: color-mix(in oklab, var(--accent-ink) 88%, white);
  border-color: var(--accent-ink);
}

/* ============================================================
 * FAQ accordion
 *
 * Hoisted from contact-us.css when bundles became the second page
 * to use it. DOM contract is provided by views/site/_sections/_faq.pug:
 *   section.faq-section > .wrap > .faq-head (index + title + blurb)
 *                                 + .faq-grid > details.faq[open]
 *                                     > summary[h4 + .plus]
 *                                     > .answer
 * ============================================================ */

.faq-section {
  padding: 80px 0;
  border-bottom: 0.5px solid var(--line);
}
.faq-head {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 48px;
  align-items: end;
  margin-bottom: 32px;
}
.faq-head .index {
  display: block;
  margin-bottom: 14px;
}
.faq-head .section-title {
  font-size: clamp(34px, 4.5vw, 60px);
  line-height: 1;
  letter-spacing: -0.025em;
  margin: 0;
}
.faq-head .blurb {
  text-align: right;
  color: var(--fg-dim);
  font-size: 14px;
}
@media (max-width: 800px) {
  .faq-head { grid-template-columns: 1fr; gap: 16px; }
  .faq-head .blurb { text-align: left; }
}

.faq-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 12px;
}
.faq {
  border: 0.5px solid var(--line);
  border-radius: 14px;
  padding: 22px;
  background: var(--card);
  transition: border-color 0.25s ease;
}
.faq:hover { border-color: var(--line-strong); }
.faq summary {
  list-style: none;
  display: flex;
  justify-content: space-between;
  align-items: start;
  gap: 16px;
  cursor: pointer;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary h4 {
  font-family: var(--display);
  font-weight: 500;
  font-size: 18px;
  letter-spacing: -0.01em;
  margin: 0;
  text-wrap: balance;
  flex: 1;
}
.faq summary .plus {
  flex: none;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: 0.5px solid var(--line-strong);
  position: relative;
  font-size: 0;
  color: transparent;
  transition: all 0.3s ease;
}
/* Draw the +/× from two centred bars rather than a text glyph, so it is
   geometrically centred in the circle regardless of font metrics. */
.faq summary .plus::before,
.faq summary .plus::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 10px;
  height: 1px;
  background: var(--fg-dim);
  transform: translate(-50%, -50%);
  transition: background 0.3s ease;
}
.faq summary .plus::after {
  transform: translate(-50%, -50%) rotate(90deg);
}
.faq[open] summary .plus {
  background: var(--accent);
  border-color: var(--accent);
  transform: rotate(45deg);
}
.faq[open] summary .plus::before,
.faq[open] summary .plus::after {
  background: var(--accent-ink);
}
.faq .answer {
  padding-top: 14px;
  color: var(--fg-dim);
  font-size: 14px;
  line-height: 1.6;
  text-wrap: pretty;
}
.faq .answer a {
  color: var(--accent);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-thickness: 1px;
  transition: opacity 0.15s ease;
}
.faq .answer a:hover {
  opacity: 0.75;
}

/* ============================================================
 * Sticky filter strip + tab buttons
 *
 * Used by listing pages with category/type filters (bundles,
 * products, …). Sits under the nav (which has `top: 0`, height 64).
 * The tabs themselves work the same on every page; what each page
 * brings is the `data-filter`/`data-cat` attribute on each tab and
 * matching attribute on the cards, plus its own per-page JS to
 * toggle the `hidden` attribute on filtered-out items.
 * ============================================================ */

.filterbar {
  position: sticky;
  top: 64px;
  z-index: 40;
  background: color-mix(in oklab, var(--bg) 90%, transparent);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  border-bottom: 0.5px solid var(--line);
  padding: 14px 0;
}
.filter-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}
.filter-tabs {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}
.ftab {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 14px;
  border-radius: 999px;
  border: 0.5px solid var(--line);
  background: transparent;
  color: var(--fg-dim);
  font-size: 13px;
  white-space: nowrap;
  transition: all 0.2s ease;
  cursor: pointer;
  font-family: var(--body);
}
.ftab:hover {
  color: var(--fg);
  border-color: var(--line-strong);
}
.ftab.on {
  background: var(--accent);
  color: var(--accent-ink);
  border-color: var(--accent);
}
.ftab .ct {
  font-family: var(--mono);
  font-size: 10.5px;
  color: var(--fg-faint);
  letter-spacing: 0.06em;
}
.ftab.on .ct { color: rgba(0, 0, 0, 0.5); }
.filter-right {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
}
.filter-meta {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-faint);
}
/* Accent CTA in the filter row → the custom-bundle builder. Always filled
   (unlike the toggle .ftab pills) so it reads as an action, not a filter. */
.filter-cta {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 16px;
  border-radius: 999px;
  border: 0.5px solid var(--accent);
  background: var(--accent);
  color: var(--accent-ink);
  font-family: var(--body);
  font-size: 13px;
  font-weight: 500;
  white-space: nowrap;
  text-decoration: none;
  transition: all 0.2s ease;
}
.filter-cta:hover {
  filter: brightness(1.06);
  transform: translateY(-1px);
}
.filter-cta .arrow {
  transition: transform 0.2s ease;
}
.filter-cta:hover .arrow {
  transform: translateX(2px);
}
.filter-meta strong,
.filter-meta b {
  color: var(--fg);
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}

/* ============================================================
 * Form autofill — keep the input transparent on / forms.
 *
 * Our inputs are `background: transparent` (contact-us, education)
 * or sit on a per-form `--field` token (careers-apply). The usual
 * `box-shadow: 0 0 0 1000px <color> inset` trick paints a solid
 * block over the autofilled field, which on a transparent input
 * reads as a visible rectangle that breaks the form layout.
 *
 * Instead, suppress Chrome/WebKit's yellow paint entirely by
 * pushing its background-color transition far into the future —
 * the original (transparent) background never gets replaced.
 * Text color is forced via `-webkit-text-fill-color` since the
 * autofill state also overrides `color`.
 * ============================================================ */
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
input:-webkit-autofill:active,
textarea:-webkit-autofill,
textarea:-webkit-autofill:hover,
textarea:-webkit-autofill:focus,
textarea:-webkit-autofill:active,
select:-webkit-autofill {
  -webkit-text-fill-color: var(--fg) !important;
  caret-color: var(--accent);
  transition: background-color 0s 600000s, color 0s 600000s !important;
}

/* ============================================================
 * YouTube click-to-play poster overlay
 *
 * Used by views/site/_sections/_yt-poster.pug to defer the
 * YouTube player script until the user actually clicks. The
 * .yt-poster button stretches inside its parent (.tpl-trailer
 * for trailers, .tut-embed for tutorials, anything with a
 * defined aspect-ratio in general) — matches the absolute-inset
 * positioning the iframe used to have so containers don't need
 * to change.
 *
 * Swap happens in public/script.js — replaces the button
 * with an autoplay iframe on click.
 * ============================================================ */
.yt-poster {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  padding: 0;
  margin: 0;
  cursor: pointer;
  background: #000;
  overflow: hidden;
  display: block;
}
.yt-poster-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.4s ease, opacity 0.4s ease;
}
.yt-poster:hover .yt-poster-img,
.yt-poster:focus-visible .yt-poster-img {
  transform: scale(1.03);
}
.yt-poster-play {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 64px;
  height: 64px;
  border-radius: 14px;
  background: rgba(255, 40, 40, 0.92);
  display: grid;
  place-items: center;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
  transition: transform 0.2s ease, background 0.2s ease;
}
.yt-poster-play::after {
  content: "";
  width: 0;
  height: 0;
  border-left: 18px solid #fff;
  border-top: 11px solid transparent;
  border-bottom: 11px solid transparent;
  margin-left: 4px;
}
.yt-poster:hover .yt-poster-play,
.yt-poster:focus-visible .yt-poster-play {
  background: rgb(255, 40, 40);
  transform: translate(-50%, -50%) scale(1.06);
}
.yt-poster:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

/* ============================================================
 * Product "Expansions" section — SHARED across VS / BAM / DRC / FRMS.
 * Markup: views/site/_sections/_expansions.pug (+productExpansions).
 * Single source of truth — edit here and every product's expansions
 * section updates. (Per-page `.a-*` cover-fallback art classes stay in
 * the page sheets; they're product-specific decoration.)
 * ============================================================ */
.exp-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
@media (max-width: 900px) { .exp-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .exp-grid { grid-template-columns: 1fr; } }
.exp {
  position: relative;
  border: 0.5px solid var(--line);
  border-radius: 14px;
  background: var(--card, #0a0a12);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  transition: border-color 0.25s ease, transform 0.25s ease;
  color: inherit;
}
.exp:hover { border-color: var(--accent); transform: translateY(-2px); }
.exp .art {
  aspect-ratio: 1 / 1;
  position: relative;
  overflow: hidden;
  border-bottom: 0.5px solid var(--line);
  background-size: cover;
  background-position: center;
}
.exp .art .ph { position: absolute; inset: 0; display: grid; place-items: center; color: rgba(255, 255, 255, 0.65); font-family: var(--mono); font-size: 10px; letter-spacing: 0.14em; text-transform: uppercase; text-align: center; padding: 12px; }
.exp .art[style*="background-image"] .ph { display: none; }
.exp .art .price-pill { position: absolute; right: 14px; bottom: 14px; font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.06em; color: #f1e6cf; background: rgba(0, 0, 0, 0.55); border: 0.5px solid rgba(255, 255, 255, 0.16); border-radius: 999px; padding: 5px 10px; backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); }
.exp .body { padding: 18px; display: flex; flex-direction: column; gap: 6px; flex: 1; }
.exp .body h4 { font-family: var(--display); font-weight: 500; font-size: 17px; letter-spacing: -0.005em; line-height: 1.2; margin: 0; text-wrap: balance; }
.exp .body .desc { color: var(--fg-dim); font-size: 13px; line-height: 1.5; flex: 1; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
.exp .body .meta-row { display: flex; justify-content: space-between; align-items: center; margin-top: 8px; padding-top: 12px; border-top: 0.5px dashed var(--line); font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.06em; color: var(--fg-faint); }
.exp .body .add { color: var(--accent); font-family: var(--mono); font-size: 11px; letter-spacing: 0.08em; }
.exp-see-all { margin-top: 32px; padding: 22px 28px; border: 0.5px solid var(--line); border-radius: 14px; background: var(--card, #0a0a12); display: flex; gap: 18px; flex-wrap: wrap; align-items: center; justify-content: space-between; }
.exp-see-all .lbl { font-family: var(--mono); font-size: 11px; letter-spacing: 0.08em; color: var(--fg-dim); }
.exp-see-all .lbl b { color: var(--accent); font-weight: 500; }

/* ============================================================
 * Shared expansion-pack CARD — the unified component reused by the
 * product-page Expansions section, the all-expansions listing, and the
 * "related packs" grid on a pack detail page.
 * Markup: views/site/_sections/_expansion-card.pug (+expansionCard).
 * Hoisted here (was product-template-expansions.css) so every page that
 * loads the global sheet renders packs identically.
 * ============================================================ */
.exp-card {
  border: 0.5px solid var(--line);
  border-radius: 16px;
  background: var(--card);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  transition: border-color 0.25s ease, transform 0.25s ease;
  position: relative;
  color: inherit;
}
.exp-card:hover { border-color: var(--accent); transform: translateY(-3px); }
/* Hidden by the listing filter JS when the active category doesn't match. */
.exp-card[hidden] { display: none; }

.exp-art {
  aspect-ratio: 1 / 1;
  position: relative;
  border-bottom: 0.5px solid var(--line);
  overflow: hidden;
  display: grid;
  place-items: center;
}
.exp-art.has-cover {
  /* Real cover present: rendered as a real <img.exp-art-img> below, so the
   * panel itself carries no background (refs #480/#482). */
  background: none;
}
.exp-art-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.exp-tag {
  font-family: var(--display);
  font-weight: 600;
  font-size: 22px;
  letter-spacing: 0.05em;
  padding: 9px 16px;
  border: 1px solid;
  border-radius: 8px;
  background: rgba(0, 0, 0, 0.4);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  text-align: center;
}
.exp-wave {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 14px;
  height: 2px;
  opacity: 0.7;
  mask-image: repeating-linear-gradient(90deg, #000 0 4px, transparent 4px 8px);
  -webkit-mask-image: repeating-linear-gradient(90deg, #000 0 4px, transparent 4px 8px);
}
.exp-badge {
  position: absolute;
  top: 14px;
  right: 14px;
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent-ink);
  background: var(--accent);
  padding: 4px 8px;
  border-radius: 6px;
  font-weight: 500;
}
.exp-body { padding: 22px 24px 24px; display: flex; flex-direction: column; gap: 10px; flex: 1; }
.exp-body h4 {
  font-family: var(--display);
  font-weight: 500;
  font-size: 20px;
  letter-spacing: -0.005em;
  line-height: 1.25;
  margin: 0;
  text-wrap: balance;
}
/* "by <author>" credit line, sits right under the title. */
.exp-by { font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--fg-faint); margin-top: -4px; }
.exp-body .exp-desc { margin: 0; color: var(--fg-dim); font-size: 13.5px; line-height: 1.5; flex: 1; display: -webkit-box; -webkit-line-clamp: 3; line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
.exp-foot {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding-top: 14px;
  border-top: 0.5px dashed var(--line);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.exp-price { color: var(--accent); font-weight: 500; font-size: 14px; }
/* On-sale cluster on a pack card (refs #415): pill + promo (already accent) +
   struck original (smaller). */
.exp-price:has(.exp-was) { display: inline-flex; align-items: center; gap: 6px; }
.exp-price .sale-tag { font-size: 9px; }
.exp-price .exp-now { font-weight: 600; }
.exp-price .exp-was { color: var(--fg-faint); font-weight: 400; font-size: 11px; text-decoration: line-through; }
.exp-cta { color: var(--fg-dim); display: inline-flex; align-items: center; gap: 6px; }
.exp-cta .arrow { transition: transform 0.25s ease; }
.exp-card:hover .exp-cta { color: var(--fg); }
.exp-card:hover .exp-cta .arrow { transform: translateX(3px); }

/* Foot CTA cluster: inline "Add" + open-details arrow (replaces the old
   "Preview & details" text link). The card is an <article>; the arrow is a
   stretched link over the whole card and the Add button rides above it. */
.exp-card { isolation: isolate; }
.exp-foot .card-ctas {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: none;
}
.exp-card .add-cta {
  height: 30px;
  padding: 0 13px;
  border-radius: 999px;
  border: 0.5px solid var(--line-strong);
  display: grid;
  place-items: center;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg-dim);
  text-decoration: none;
  white-space: nowrap;
  transition: all 0.25s ease;
  position: relative;
  z-index: 1;
}
.exp-card .add-cta:hover {
  background: var(--accent);
  color: var(--accent-ink);
  border-color: var(--accent);
}
.exp-card .arrow-cta {
  flex: none;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  border: 0.5px solid var(--line-strong);
  display: grid;
  place-items: center;
  font-size: 14px;
  color: var(--fg-dim);
  text-decoration: none;
  transition: all 0.25s ease;
}
.exp-card .arrow-cta.exp-open::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
}
.exp-card:hover .arrow-cta {
  background: var(--accent);
  color: var(--accent-ink);
  border-color: var(--accent);
}

/* ============================================================
 * Product "Trailer" section — SHARED across all product pages.
 * Markup: views/site/_sections/_trailer.pug (+productTrailer).
 * Aspect-ratio is set inline by the mixin (16:9 for embeds, 21:9 for the
 * placeholder), so it's intentionally not declared here.
 * ============================================================ */
.tpl-trailer { position: relative; border: 0.5px solid var(--line); border-radius: 18px; background: var(--card, #0a0a12); overflow: hidden; }
.tpl-trailer-narrow { max-width: 880px; margin: 0 auto; }
.tpl-trailer .ph {
  position: absolute; inset: 0;
  background:
    radial-gradient(ellipse 50% 60% at 50% 50%, color-mix(in oklab, var(--accent) 14%, transparent), transparent 60%),
    repeating-linear-gradient(135deg, rgba(255, 255, 255, .025) 0 8px, transparent 8px 16px),
    linear-gradient(180deg, var(--card-2, #161620), var(--bg));
  display: grid; place-items: center; color: var(--fg-faint); font-family: var(--mono); font-size: 11px; letter-spacing: 0.14em; text-transform: uppercase;
}
.tpl-trailer .play { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); width: 88px; height: 88px; border-radius: 50%; border: 1.5px solid var(--accent); background: rgba(0, 0, 0, .4); display: grid; place-items: center; backdrop-filter: blur(8px); transition: transform 0.25s ease, background 0.25s ease; cursor: pointer; }
.tpl-trailer .play::after { content: ""; width: 0; height: 0; border-left: 18px solid var(--accent); border-top: 12px solid transparent; border-bottom: 12px solid transparent; margin-left: 5px; }
.tpl-trailer:hover .play { transform: translate(-50%, -50%) scale(1.06); background: rgba(0, 0, 0, .6); }
.tpl-trailer .stamp { position: absolute; bottom: 18px; left: 18px; font-family: var(--mono); font-size: 10px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--fg-faint); background: rgba(0, 0, 0, .55); border: 0.5px solid var(--line); padding: 5px 10px; border-radius: 999px; backdrop-filter: blur(4px); }
.tpl-trailer .timecode { position: absolute; top: 18px; right: 18px; font-family: var(--mono); font-size: 10px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--accent); background: rgba(0, 0, 0, .55); border: 0.5px solid color-mix(in oklab, var(--accent) 40%, transparent); padding: 5px 10px; border-radius: 999px; backdrop-filter: blur(4px); }

/* ============================================================
 * Product "Tutorials" section — SHARED across VS / BAM / FRMS / TKFX / LK.
 * Markup: views/site/_sections/_tutorials.pug (+productTutorials).
 * ============================================================ */
.tut-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
@media (max-width: 900px) { .tut-grid { grid-template-columns: 1fr; } }
.tut { position: relative; border: 0.5px solid var(--line); border-radius: 14px; background: var(--card, #0a0a12); overflow: hidden; display: flex; flex-direction: column; transition: border-color 0.25s ease, transform 0.25s ease; color: inherit; }
.tut:hover { border-color: var(--accent); transform: translateY(-2px); }
.tut-embed { position: relative; aspect-ratio: 16 / 9; background: #000; overflow: hidden; }
.tut-embed iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.tut .body { padding: 18px; display: flex; flex-direction: column; gap: 6px; flex: 1; }
.tut .body .ovr { font-family: var(--mono); font-size: 10px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--fg-faint); }
.tut .body h4 { font-family: var(--display); font-weight: 500; font-size: 17px; letter-spacing: -0.005em; line-height: 1.25; margin: 0; text-wrap: balance; }
.tut-foot { margin-top: 24px; display: flex; justify-content: center; }

/* ============================================================
 * Product "Overview" section — SHARED across DRC / FRMS / GRFX /
 * LK / K7D / DLYM. The "short version" primer: serif lead + spec
 * strip + preview pillars. Markup: views/site/_sections/_overview.pug
 * (+productOverview). Single source of truth — edit here and every
 * product's overview section updates.
 *
 * Per-page divergences stay in the page sheet (loaded after this one):
 *   - DRC redefines `.ov-pillars` to a 4-col grid (it has four pillars).
 *   - LK paints `.ov-pillar:hover` border in `--accent`.
 * ============================================================ */
.ov-intro { display: grid; grid-template-columns: 1.15fr 0.85fr; gap: 56px; align-items: start; }
@media (max-width: 980px) { .ov-intro { grid-template-columns: 1fr; gap: 36px; } }
.ov-lead { font-family: var(--serif); font-style: italic; font-weight: 400; font-size: clamp(26px, 3.1vw, 42px); line-height: 1.2; letter-spacing: -0.01em; color: var(--fg); margin: 0; text-wrap: pretty; }
.ov-lead em { font-style: normal; color: var(--accent); }
.ov-aside { display: flex; flex-direction: column; gap: 22px; }
.ov-aside p { margin: 0; color: var(--fg-dim); font-size: 15px; line-height: 1.6; text-wrap: pretty; }
.ov-specs { display: flex; flex-direction: column; border: 0.5px solid var(--line); border-radius: 14px; background: var(--card, #0e0e12); overflow: hidden; }
.ov-specs .srow { display: grid; grid-template-columns: 120px 1fr; gap: 16px; padding: 14px 20px; border-top: 0.5px solid var(--line); align-items: baseline; }
.ov-specs .srow:first-child { border-top: 0; }
.ov-specs .srow .k { font-family: var(--mono); font-size: 10px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--fg-faint); }
.ov-specs .srow .v { font-size: 14px; color: var(--fg); line-height: 1.45; }
.ov-pillars { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; margin-top: 56px; }
@media (max-width: 900px) { .ov-pillars { grid-template-columns: 1fr; } }
.ov-pillar { border: 0.5px solid var(--line); border-radius: 16px; background: var(--card, #0e0e12); padding: 28px 30px 30px; display: flex; flex-direction: column; gap: 10px; transition: border-color 0.25s ease; }
.ov-pillar:hover { border-color: var(--line-strong); }
.ov-pillar .idx { font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--accent); }
.ov-pillar h4 { font-family: var(--display); font-weight: 500; font-size: 22px; letter-spacing: -0.015em; line-height: 1.1; margin: 0; text-wrap: balance; }
.ov-pillar h4 em { font-family: var(--serif); font-style: italic; font-weight: 400; color: var(--accent); }
.ov-pillar p { margin: 0; color: var(--fg-dim); font-size: 14px; line-height: 1.55; text-wrap: pretty; }

/* ============================================================
 * Product "Audio demos" section — SHARED across DRC / FRMS / GRFX
 * (and BAM via the head-layout variant). A 2-col SoundCloud showcase:
 * intro copy on the left, the set embed on the right. Markup:
 * views/site/_sections/_audio-demos.pug (+productAudioDemos). These are
 * the inline-layout canonical values; BAM's page sheet keeps its own
 * sticky `.audio-l`, taller frame and `.artist-quote` overrides.
 * ============================================================ */
.audio-shell { display: grid; grid-template-columns: 1fr 1.15fr; gap: 28px; align-items: start; }
@media (max-width: 1000px) { .audio-shell { grid-template-columns: 1fr; } }
.audio-l { display: flex; flex-direction: column; gap: 14px; }
.audio-l .audio-h2 {
  font-family: var(--display);
  font-weight: 500;
  font-size: clamp(38px, 5vw, 72px);
  line-height: 0.95;
  letter-spacing: -0.02em;
  margin: 8px 0 8px;
  text-wrap: balance;
  max-width: 48ch;
}
.audio-l .audio-h2 em { font-family: var(--serif); font-style: italic; font-weight: 400; color: var(--accent); }
.audio-l h3 {
  font-family: var(--display);
  font-weight: 500;
  font-size: clamp(22px, 2vw, 28px);
  line-height: 1.15;
  letter-spacing: -0.015em;
  margin: 14px 0 0;
  text-wrap: balance;
}
.audio-l h3 em { font-family: var(--serif); font-style: italic; font-weight: 400; color: var(--accent); }
.audio-l p { margin: 0; color: var(--fg-dim); font-size: 14px; line-height: 1.55; text-wrap: pretty; max-width: 42ch; }
.audio-l p.muted { color: var(--fg-faint); }
.audio-r { display: flex; flex-direction: column; gap: 14px; }
.sc-frame {
  border: 0.5px solid var(--line);
  border-radius: 14px;
  overflow: hidden;
  background: var(--card, #0e0e12);
  height: 420px;
}
.sc-frame iframe { width: 100%; height: 100%; border: 0; display: block; }

/* ============================================================
 * Product "Press" section — SHARED across BAM / LK / GRFX / VS.
 * Featured publication quote (1.4fr) + two narrower cards as one
 * continuous panel, then a secondary credit strip. Markup:
 * views/site/_sections/_press.pug (+productPress); content hydrated
 * from press clippings by routes/site.js#enrichProductPress.
 * ============================================================ */
.press { display: grid; grid-template-columns: 1.4fr 1fr 1fr; gap: 0; border: 0.5px solid var(--line); border-radius: 18px; overflow: hidden; background: var(--card, #0e0e12); }
@media (max-width: 1000px) { .press { grid-template-columns: 1fr; } }
.press .pcard {
  /* `aspect-ratio: 5/4` leaks in from the global homepage `.pcard` rule —
     reset to auto so the wider 1.4fr featured card sizes to its content
     (+ grid stretch) instead of ballooning. Also neutralises the global
     card border/radius/hover-lift so the cells read as one panel. */
  aspect-ratio: auto;
  border: 0;
  border-radius: 0;
  border-right: 0.5px solid var(--line);
  padding: 36px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  position: relative;
  transition: background 0.25s ease;
}
.press .pcard:hover { transform: none; }
.press .pcard:last-child { border-right: 0; }
@media (max-width: 1000px) { .press .pcard { border-right: 0; border-bottom: 0.5px solid var(--line); } .press .pcard:last-child { border-bottom: 0; } }
.press .pcard.featured { background: linear-gradient(180deg, color-mix(in oklab, var(--accent) 7%, var(--card, #0e0e12)), var(--card, #0e0e12)); }
.press .pcard .pub { font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--accent); }
.press .pcard .quote { font-family: var(--serif); font-style: italic; font-weight: 400; font-size: 22px; line-height: 1.3; letter-spacing: -0.005em; color: var(--fg); text-wrap: balance; flex: 1; margin: 10px 0 0; }
.press .pcard.featured .quote { font-size: 28px; }
.press .pcard .quote::before { content: "\201C"; font-family: var(--serif); font-size: 64px; line-height: 0; color: var(--accent); margin-right: 4px; vertical-align: -18px; }
.press .pcard .by { font-family: var(--mono); font-size: 10px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--fg-faint); display: flex; justify-content: space-between; gap: 12px; border-top: 0.5px dashed var(--line); padding-top: 14px; }
.press .pcard .by a { color: var(--accent); white-space: nowrap; }
.press-more { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; margin-top: 18px; }
@media (max-width: 900px) { .press-more { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 520px) { .press-more { grid-template-columns: 1fr; } }
.pm-credit { border: 0.5px solid var(--line); border-radius: 12px; background: var(--card, #0e0e12); padding: 20px 22px; display: flex; flex-direction: column; gap: 8px; }
.pm-credit .pmc-pub { font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--accent); }
.pm-credit .pmc-line { font-family: var(--serif); font-style: italic; font-size: 16px; line-height: 1.35; color: var(--fg); text-wrap: pretty; }

/* ============================================================
 * Combined rating card — SHARED canonical reviews element.
 * One card across stores (the API returns a single average + total,
 * no per-store split); score + count hydrated live by
 * routes/site.js#enrichProductRatings. Sits inside `.reviews-row`
 * (alongside a press stack) or standalone as `.ratings.solo` (no press).
 * Was per-page in drc.css; hoisted here so every product can carry it.
 * ============================================================ */
.reviews-row { display: grid; grid-template-columns: 1.2fr 1fr; gap: 48px; align-items: start; margin-bottom: 32px; }
@media (max-width: 900px) { .reviews-row { grid-template-columns: 1fr; } }
.ratings { display: grid; grid-template-columns: 1fr; gap: 14px; }
.ratings.solo { max-width: 380px; margin-bottom: 28px; }
.rating-card { border: 0.5px solid var(--line); border-radius: 14px; background: var(--card, #0e0e12); padding: 24px; display: flex; flex-direction: column; gap: 8px; }
.rating-card .platform { font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--fg-faint); }
.rating-card .stars { display: flex; align-items: center; gap: 6px; font-family: var(--display); font-weight: 500; font-size: 42px; letter-spacing: -0.02em; line-height: 1; }
.rating-card .stars em { font-family: var(--serif); font-style: italic; font-weight: 400; color: var(--accent); font-size: 0.5em; margin-left: 4px; letter-spacing: 0; }
.rating-card .star-row { font-size: 14px; letter-spacing: 0.05em; }
.rating-card .star-row .on { color: var(--accent); }
.rating-card .star-row .off { color: var(--fg-faint); }
.rating-card .count { font-family: var(--mono); font-size: 11px; color: var(--fg-faint); letter-spacing: 0.06em; }

/* ============================================================
 * PRESS + TESTIMONIALS sections — SHARED across all PDPs.
 * Markup: views/site/_sections/_press.pug (+productPress, the editorial
 * clippings grid) and _sections/_testimonials.pug (+productTestimonials,
 * rating card + user reviews + photo testimonials) — split out of the
 * former single `_social-proof.pug` combined section. `.social-proof`
 * below now scopes only the Testimonials markup; `.press`'s gap under the
 * section head applies unscoped since Press is its own section now.
 * ============================================================ */
.press { margin-top: 28px; }

/* Row 2 — rating card (left) + user reviews (right). */
.social-proof .sp-reviews-row {
  display: grid;
  grid-template-columns: minmax(220px, 0.85fr) 2fr;
  gap: 24px;
  align-items: stretch;
  margin-top: 28px;
}
.social-proof .sp-reviews-row.no-rating { grid-template-columns: 1fr; }       /* reviews fill the row */
.social-proof .sp-reviews-row.no-reviews { grid-template-columns: minmax(220px, 360px); } /* lone rating card stays compact-left */
@media (max-width: 860px) { .social-proof .sp-reviews-row { grid-template-columns: 1fr; } }
/* Rating card stretches to match the reviews column height (content centred). */
.social-proof .sp-reviews-row .ratings { display: flex; }
.social-proof .sp-reviews-row .ratings .rating-card { flex: 1; justify-content: center; }
.social-proof .sp-reviews {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 14px;
}
.social-proof .sp-review {
  border: 0.5px solid var(--line);
  border-radius: 14px;
  background: var(--card, #0e0e12);
  padding: 22px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.social-proof .sp-review .stars { color: var(--accent); font-size: 13px; letter-spacing: 0.04em; }
.social-proof .sp-review .body {
  font-family: var(--display); font-weight: 500; font-size: 16px; line-height: 1.35;
  letter-spacing: -0.005em; margin: 0; text-wrap: balance;
}
.social-proof .sp-review .by { font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.06em; color: var(--fg-faint); }
.social-proof .sp-review .by b { font-family: var(--display); font-weight: 600; color: var(--fg); letter-spacing: -0.005em; }

/* Row 3 — featured testimonials. Standout quote panels: an optional
 * circular portrait on the left, then a large serif italic quote + mono
 * byline. Panels sit side by side on wide screens (auto-fit grid) and
 * stack on narrow ones. */
.social-proof .sp-testimonials {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(360px, 1fr));
  gap: 14px;
  margin-top: 28px;
}
.social-proof .sp-quote {
  margin: 0;
  padding: 36px 40px;
  border: 0.5px solid var(--line);
  border-radius: 18px;
  background: linear-gradient(135deg, color-mix(in oklab, var(--accent) 6%, var(--card, #0e0e12)), var(--card, #0e0e12));
  display: flex;
  gap: 24px;
  align-items: center;
  flex-wrap: wrap;
  overflow: hidden;
}
.social-proof .sp-quote-body {
  flex: 1 1 320px;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.social-proof .sp-quote-mark {
  font-family: var(--serif);
  font-size: 56px;
  line-height: 1;
  color: var(--accent);
  display: block;
  height: 0.5em;
  margin-bottom: -0.05em;
}
.social-proof .sp-quote-text {
  font-family: var(--serif);
  font-style: italic;
  font-size: clamp(19px, 1.7vw, 24px);
  line-height: 1.35;
  margin: 0;
  color: var(--fg);
  text-wrap: balance;
}
.social-proof .sp-quote-by {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--fg-faint);
}
.social-proof .sp-quote-by b { color: var(--accent); font-weight: 500; }
.social-proof .sp-quote-portrait {
  flex: 0 0 auto;
  width: 132px;
  height: 132px;
  border-radius: 50%;
  overflow: hidden;
}
.social-proof .sp-quote-portrait img {
  width: 100%;
  height: 100%;
  display: block;
  object-fit: cover;
}
@media (max-width: 720px) {
  .social-proof .sp-quote { padding: 28px; }
  .social-proof .sp-quote-portrait { width: 96px; height: 96px; }
}

/* ============================================================
 * PRICING — shared component (refs #343)
 * Canonical styling for the `+pricing()` mixin (views/site/_sections/
 * _pricing.pug), consolidated from the per-product copies (drc/frms/k7d
 * dominant, plus bam's strikethrough price, tv3's strike-out feature
 * rows / shrink number, grfx-dlym's small unit). Product pages no longer
 * ship their own pricing rules. Uses the global --card token so every
 * product's pricing reads as one component. (Complements the existing
 * shared .sblock.warm overrides above.)
 * ============================================================ */
.pricing-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
@media (max-width: 900px) { .pricing-grid { grid-template-columns: 1fr; } }
.pcol {
  position: relative;
  border: 0.5px solid var(--line);
  border-radius: 18px;
  background: var(--card);
  padding: 36px;
  display: flex;
  flex-direction: column;
  gap: 22px;
  min-height: 520px;
}
.pcol.featured {
  border-color: var(--accent);
  background: linear-gradient(180deg, color-mix(in oklab, var(--accent) 8%, var(--card)), var(--card));
}
.pcol .ribbon {
  position: absolute;
  top: -1px;
  left: 24px;
  background: var(--accent);
  color: var(--accent-ink);
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 6px 10px;
  border-radius: 0 0 6px 6px;
  font-weight: 500;
}
.pcol .eyebrow { font-family: var(--mono); font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--fg-faint); }
.pcol h3 {
  font-family: var(--display);
  font-weight: 500;
  font-size: 32px;
  letter-spacing: -0.02em;
  line-height: 1;
  margin: 0;
}
.pcol h3 em { font-family: var(--serif); font-style: italic; font-weight: 400; color: var(--accent); }
.pcol .price { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.pcol .price .sale-tag { align-self: center; margin-right: 2px; }
.pcol .price .was {
  font-family: var(--display);
  font-weight: 500;
  font-size: 28px; /* ~half the 56px promo */
  color: var(--fg-faint);
  text-decoration: line-through;
  letter-spacing: -0.02em;
}
/* Promoted (current) price number in the accent colour when on sale (#415). */
.pcol .price:has(.was) .num { color: var(--accent); }
.pcol .price .num {
  font-family: var(--display);
  font-weight: 500;
  font-size: 56px;
  letter-spacing: -0.03em;
  line-height: 1;
}
.pcol .price .num.small { font-size: 28px; }
.pcol .price .num em {
  font-family: var(--serif);
  font-style: italic;
  font-weight: 400;
  color: var(--accent);
  font-size: 0.45em;
  margin-left: 4px;
  letter-spacing: 0;
}
.pcol .price .per {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--fg-faint);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.pcol .price .unit {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--fg-faint);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.pcol ul {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
  flex: 1;
}
.pcol ul li {
  font-size: 14px;
  line-height: 1.4;
  color: var(--fg-dim);
  display: grid;
  grid-template-columns: 18px 1fr;
  gap: 10px;
  align-items: start;
}
.pcol ul li::before { content: "●"; color: var(--accent); font-size: 8px; line-height: 1.6; }
.pcol ul li.no { color: var(--fg-faint); text-decoration: line-through; }
.pcol ul li.no::before { content: "●"; color: var(--fg-faint); }
.pcol .pay {
  font-family: var(--mono);
  font-size: 10.5px;
  color: var(--fg-faint);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding-top: 10px;
  border-top: 0.5px dashed var(--line);
}
.pcol .pcol-cta { margin-top: auto; }
.pcol .pcol-cta a.btn { width: 100%; justify-content: center; }

/* "How rent-to-own works" link on the rent-to-own pricing card → the rent-to-
   own explainer page (refs #425). Small, muted; mirrors the legacy PDP "Learn
   more" affordance. Shared by the standard pricing mixin (.pcol) and the VS
   pricing cards (.price) — both are flex columns. Sits directly above the
   CTA button (whose margin-top:auto pins it to the card's bottom), so this
   can't push the button out of alignment with the other columns' buttons. */
.rto-learn {
  align-self: center;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin-bottom: -6px;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg-dim);
  text-decoration: none;
  transition: color 0.2s ease;
}
.rto-learn:hover { color: var(--accent); }
.rto-learn .arrow { transition: transform 0.25s ease; }
.rto-learn:hover .arrow { transform: translateX(3px); }

/* Degraded state — shown when the catalogue API can't supply a column's live
   price + buy link (enrichPurchase flags the column `unavailable`). The price
   number is replaced by a muted note and the CTA is rendered as an inert,
   dimmed <span> (no href) so the revenue path fails honestly rather than
   showing a stale price or a dead link. */
.pcol .price .price-out {
  font-family: var(--mono);
  font-size: 13px;
  letter-spacing: 0.02em;
  color: var(--fg-faint);
  padding: 8px 0;
}
.pcol .pcol-cta .btn.disabled {
  width: 100%;
  justify-content: center;
  opacity: 0.45;
  cursor: not-allowed;
  pointer-events: none;
}

/* Demo banner — accent-tinted strip under the price grid. */
.demo-banner {
  margin-top: 32px;
  padding: 22px 28px;
  border: 0.5px solid var(--line);
  border-radius: 14px;
  background: var(--card);
  display: flex;
  gap: 18px;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
}
.demo-banner-l { display: flex; gap: 18px; flex-wrap: wrap; align-items: center; }
.demo-banner .demo-label { font-family: var(--mono); font-size: 11px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--accent); }
.demo-banner .demo-body { font-family: var(--mono); font-size: 11px; letter-spacing: 0.08em; color: var(--fg-dim); }
.demo-banner .demo-cta { color: var(--accent); }

/* Upgrade banner — emphasised variant of the demo strip: the label reads as an
 * accent chip and the CTA is a filled accent button pinned right (the banner's
 * space-between already sends it right; this just re-skins it like .btn.primary). */
.upgrade-banner .demo-label {
  display: inline-flex;
  align-items: center;
  padding: 5px 12px;
  border-radius: 999px;
  border: 0.5px solid color-mix(in oklab, var(--accent) 45%, transparent);
  background: color-mix(in oklab, var(--accent) 12%, transparent);
  color: var(--accent);
}
.upgrade-banner .demo-cta {
  background: var(--accent);
  color: var(--accent-ink);
  border-color: var(--accent);
}
.upgrade-banner .demo-cta:hover {
  background: color-mix(in oklab, var(--accent) 88%, white);
  border-color: var(--accent);
}

/* Specs section — the "spec sheet": one identical `.specs-list` of numbered
 * `.spec` rows (num / text / tag) on every product page (canonical = the former
 * majority: num = accent, tag = fg-faint, solid row rule). Refs #447. */
.specs-list {
  display: flex;
  flex-direction: column;
  border: 0.5px solid var(--line);
  border-radius: 18px;
  overflow: hidden;
  background: var(--card);
}
.spec {
  display: grid;
  grid-template-columns: 48px 1fr 116px;
  gap: 18px;
  align-items: center;
  padding: 16px 22px;
  border-bottom: 0.5px solid var(--line);
}
.spec:last-child { border-bottom: 0; }
.spec .num { font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.08em; color: var(--accent); }
.spec .text { font-size: 14.5px; color: var(--fg); line-height: 1.4; }
.spec .text b { color: var(--accent); font-weight: 500; }
.spec .text em { font-family: var(--serif); font-style: italic; font-weight: 400; color: var(--accent); }
.spec .tag { font-family: var(--mono); font-size: 10px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--fg-faint); text-align: right; }
@media (max-width: 560px) {
  .spec { grid-template-columns: 30px 1fr; gap: 10px; }
  .spec .tag { display: none; }
}

/* Education + Mobile split. (Base chrome; .sblock.warm scale overrides
 * live in the shared block above.) */
.split-edu {
  margin-top: 24px;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
}
@media (max-width: 900px) { .split-edu { grid-template-columns: 1fr; } }
.split-edu .sblock {
  position: relative;
  border: 0.5px solid var(--line);
  border-radius: 18px;
  padding: 36px;
  min-height: 280px;
  display: flex;
  flex-direction: column;
  background: var(--card);
  overflow: hidden;
  gap: 14px;
}
.split-edu .sblock.warm {
  background: var(--accent);
  color: var(--accent-ink);
  border-color: var(--accent);
}
.split-edu .sblock h3 {
  font-family: var(--display);
  font-weight: 500;
  font-size: 32px;
  letter-spacing: -0.02em;
  line-height: 1.05;
  margin: 0 0 12px;
  text-wrap: balance;
}
.split-edu .sblock h3 em { font-family: var(--serif); font-style: italic; font-weight: 400; }
.split-edu .sblock p { margin: 0; font-size: 15px; line-height: 1.55; color: var(--fg-dim); max-width: 42ch; }
.split-edu .sblock.warm p { color: rgba(0, 0, 0, 0.7); }
.split-edu .sblock.warm .mono { color: rgba(0, 0, 0, 0.55); }
.split-edu .sblock .row {
  display: flex;
  /* Bottom-align so the CTA button sits on the card's bottom line — level with
   * the App Store / Google Play badges (.stores) in the neighbouring card,
   * instead of floating at the vertical centre of the big "50%" number. */
  align-items: flex-end;
  justify-content: space-between;
  gap: 20px;
  margin-top: auto;
  flex-wrap: wrap;
}
.split-edu .sblock .num {
  font-family: var(--display);
  font-weight: 500;
  font-size: 120px;
  line-height: 1;
  letter-spacing: -0.04em;
}
.split-edu .sblock .num em { font-family: var(--serif); font-style: italic; font-weight: 400; }
.split-edu .sblock .stores { display: flex; gap: 14px; flex-wrap: wrap; margin-top: auto; }
/* Official store badges (App Store / Google Play) — replace the text pill for
 * those two stores; other stores (e.g. Mac App Store) keep .store-pill. Served
 * same-origin from /images/badges (already in the repo); both are 168px tall. */
.split-edu .sblock .store-badge { display: inline-flex; text-decoration: none; }
.split-edu .sblock .store-badge img { height: 48px; width: auto; display: block; }
.split-edu .sblock .store-badge[href] { transition: opacity 120ms ease; }
.split-edu .sblock .store-badge[href]:hover { opacity: 0.85; }
.split-edu .sblock .store-pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 10px 16px;
  border: 0.5px solid var(--line-strong);
  border-radius: 10px;
  font-family: var(--display);
  font-weight: 500;
  font-size: 13px;
  letter-spacing: -0.005em;
  background: transparent;
}
.split-edu .sblock .store-pill .small {
  font-family: var(--mono);
  font-size: 9px;
  color: var(--fg-faint);
  letter-spacing: 0.08em;
  display: block;
  margin-top: -2px;
  font-weight: 400;
}
/* "Also on mobile" store pills (pricing.mobile.stores/.store) render as
   <a> when enrichReleases wires a live store href from the release manifest;
   otherwise as an <a> with no href (behaves like the old decorative span).
   The pill visuals come from the shared `.split-edu .sblock .store-pill` rules
   above — these just neutralise the default anchor underline/colour and add a
   hover affordance when the pill is actually a link. */
a.store-pill { text-decoration: none; color: inherit; }
a.store-pill[href] { cursor: pointer; transition: border-color 120ms ease; }
a.store-pill[href]:hover { border-color: var(--accent); }

/* ============================================================
 * Product-page HELP section (shared — see views/site/_sections/_help.pug).
 * Baseline for the .help-grid / .hcard resource cards used by every
 * product page. These rules were historically hand-copied into each
 * per-page stylesheet; this shared default means a new (or incomplete)
 * product page gets correct card + icon sizing for free — the giant-icon
 * bug on BAM/TV3 was a per-page copy that had dropped the .icon rules.
 * Per-page stylesheets may still override (e.g. a 2-col grid on the
 * 2-card pages; a few pages use a 20px h4). --card / --line* / --fg-* are
 * theme-aware, so this works in both light and dark.
 * ============================================================ */
.help-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; }
@media (max-width: 900px) { .help-grid { grid-template-columns: 1fr; } }
.hcard { position: relative; border: 0.5px solid var(--line); border-radius: 14px; background: var(--card); padding: 26px; display: flex; flex-direction: column; gap: 10px; transition: border-color 0.25s ease, transform 0.25s ease; text-decoration: none; color: inherit; }
.hcard:hover { border-color: var(--accent); transform: translateY(-2px); }
.hcard .icon { width: 40px; height: 40px; border-radius: 10px; border: 0.5px solid var(--line-strong); display: grid; place-items: center; color: var(--accent); margin-bottom: 4px; }
.hcard .icon svg { width: 20px; height: 20px; }
.hcard .kind { font-family: var(--mono); font-size: 10px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--fg-faint); }
.hcard h4 { font-family: var(--display); font-weight: 500; font-size: 18px; letter-spacing: -0.005em; line-height: 1.3; margin: 0; text-wrap: balance; }
.hcard h4 em { font-family: var(--serif); font-style: italic; font-weight: 400; color: var(--accent); }
.hcard p { margin: 0; color: var(--fg-dim); font-size: 13.5px; line-height: 1.5; flex: 1; }
.hcard .more { font-family: var(--mono); font-size: 11px; letter-spacing: 0.08em; color: var(--accent); margin-top: 6px; display: inline-flex; align-items: center; gap: 6px; }
.hcard .more .arrow { transition: transform 0.25s ease; }
.hcard:hover .more .arrow { transform: translateX(3px); }

/* ============================================================
 * Product-page REQUIREMENTS section (shared — see
 * views/site/_sections/_requirements.pug). Baseline for the
 * .req-grid / .req platform cards used by every product page.
 * Scoped under .req-grid on purpose: a bare .req collides with the
 * required-field asterisk (span.req) on the form pages and .ecard /
 * .buy-box .req elsewhere. Per-page stylesheets still override (3- vs
 * 4-column grid + its border logic, bam's 28px cards, the mono/11px vs
 * 18px icon glyphs, per-product --*-card background). --card / --line* /
 * --fg-* / --accent are theme-aware, so this holds in light and dark.
 * ============================================================ */
.req-grid { display: grid; grid-template-columns: repeat(4, 1fr); border: 0.5px solid var(--line); border-radius: 14px; background: var(--card); overflow: hidden; }
.req-grid .req { padding: 24px; border-right: 0.5px solid var(--line); border-bottom: 0.5px solid var(--line); display: flex; flex-direction: column; gap: 10px; }
.req-grid .req .icon { width: 36px; height: 36px; border-radius: 8px; border: 0.5px solid var(--line-strong); display: grid; place-items: center; font-family: var(--display); font-weight: 600; font-size: 18px; color: var(--accent); background: color-mix(in oklab, var(--accent) 6%, transparent); }
.req-grid .req h5 { font-family: var(--display); font-weight: 500; font-size: 17px; letter-spacing: -0.005em; margin: 0; }
.req-grid .req p { margin: 0; color: var(--fg-dim); font-size: 13px; line-height: 1.5; }
@media (max-width: 900px) {
  .req-grid { grid-template-columns: repeat(2, 1fr); }
  .req-grid .req:nth-child(2n) { border-right: 0; }
}
@media (max-width: 520px) {
  .req-grid { grid-template-columns: 1fr; }
}
