/* Essgefühl — site styles.
 *
 * Palette and type are the app's own, read from lib/theme/app_colors.dart and
 * app_shape.dart, with the Dart symbol named beside each value. Two rules
 * carried across because breaking either makes a lookalike:
 *   - sepia (#9A8462) draws lines and never sets text; text in that family
 *     uses sepiaText, which clears the app's WCAG pass targets (2026-09-27:
 *     AAA, >=7:1) on parchment.
 *   - waxRed is the seal and nothing else — never a warning, never a food
 *     or restriction signal. --wax stays the seal's own colour in both
 *     themes; --wax-text is the one exception, a brightened dark-mode-only
 *     value for the two places this site prints wax as TEXT (`a:hover`,
 *     `.card .no`) rather than a glyph fill, named where it is declared.
 *
 * WCAG pass (2026-09-27): --ink-2/--sepia-text/--rule were brought to the
 * same targets as the app's onSurfaceMuted/sepiaText/outline (AAA text,
 * >=7:1; non-text borders, >=3:1) against this file's OWN ground values
 * (--parchment/--surface/--surface-low/--parchment-deep). CORRECTED (WCAG
 * repair round, 2026-09-27): the three derived hexes are NOT "slightly
 * different" from app_colors.dart's, as this comment previously claimed --
 * they are IDENTICAL, because the ground that actually sets the darkening
 * floor (parchment-deep/surface-high, the hardest of the set) is copied
 * from the app exactly; only --surface itself (#F9F7F2, this file's own
 * value, never AppColors.surface/#F6EEDC) differs, and it happens not to be
 * the hardest ground either token was fitted against. Dark --sage/
 * --sage-deep were also brought from the retired mint (board 5,
 * 2026-09-25 -- see AppColors.darkPrimary's doc comment) to the app's
 * current brass values, since a stale accent is the same defect class as
 * stale muted text.
 *
 * WCAG repair round 2 (2026-09-27): the "IDENTICAL" claim above had gone
 * false again -- app_colors.dart's own sepiaText/darkSepia moved on to a
 * higher-saturation re-derivation (same relative luminance, so the ratios
 * above are unchanged) after this comment was written, and this file's
 * --sepia-text (both themes) was never updated to follow. Resynced to
 * #503E21/#D2BA92 so the claim holds again. Separately (unrelated to
 * the "IDENTICAL" reasoning above, which is only about --ink-2/
 * --sepia-text/--rule): --ink/--sage/--wax (light) and --surface/--ink
 * (dark) were plain stale copies of app_colors.dart's onSurface/primary/
 * waxRed/darkSurface/darkOnSurface -- corrected to match exactly. Every
 * text pair on the site was re-checked against this file's own ground
 * values after all of the above; all still clear their target (>=4.5:1
 * for plain text, >=7:1 where AAA is claimed).
 *
 * What does NOT carry across is AppShape's no-elevation rule. That governs a
 * UI where every plate is printed onto one sheet. A landing page is a
 * different artifact with a different job, and the portfolio's other sites
 * (pollenwarte, sicherundgeborgen) both pair a flat app UI with a site that
 * uses depth, gradients and motion. The first version of this file obeyed the
 * app's rule literally and read as unfinished.
 *
 * The hero reuses the app's own texture the way pollenwarte's reuses its chart
 * dot-matrix: fog blobs over parchment, which is what Explore looks like.
 *
 * No scripts anywhere — scroll reveals are CSS animation-timeline, so the CSP
 * stays default-src 'none' with no script-src.
 */

@font-face { font-family: 'Noto Serif'; src: url('/fonts/noto-serif-400.woff2') format('woff2'); font-weight: 400; font-display: swap; }
@font-face { font-family: 'Noto Serif'; src: url('/fonts/noto-serif-500.woff2') format('woff2'); font-weight: 500; font-display: swap; }
@font-face { font-family: 'Jakarta'; src: url('/fonts/jakarta-400.woff2') format('woff2'); font-weight: 400; font-display: swap; }
@font-face { font-family: 'Jakarta'; src: url('/fonts/jakarta-500.woff2') format('woff2'); font-weight: 500; font-display: swap; }
@font-face { font-family: 'Jakarta'; src: url('/fonts/jakarta-600.woff2') format('woff2'); font-weight: 600; font-display: swap; }
@font-face { font-family: 'Plex Mono'; src: url('/fonts/plex-mono-500.woff2') format('woff2'); font-weight: 500; font-display: swap; }

:root {
  color-scheme: light dark;

  /* AppColors — light */
  --parchment:      #F1E8D6;   /* AppColors.parchment        */
  --parchment-deep: #E4D7BC;   /* AppColors.parchmentDeep    */
  --surface:        #F9F7F2;   /* AppColors.surface          */
  --surface-low:    #EDE3CE;   /* AppColors.surfaceLow       */
  --surface-high:   #E2D5B9;   /* AppColors.surfaceHigh      */
  --ink:            #2B2118;   /* AppColors.onSurface        */
  --ink-2:          #46403B;   /* AppColors.onSurfaceMuted, WCAG pass 2026-09-27 */
  --sepia-text:     #503E21;   /* AppColors.sepiaText, WCAG pass 2026-09-27 */
  --rule:           #8D744C;   /* AppColors.outline, WCAG pass 2026-09-27 */
  --rule-2:         #D8C9AB;   /* AppColors.outlineVariant   */
  --sage:           #4E6B55;   /* AppColors.primary          */
  --sage-deep:      #365239;   /* AppColors.primaryDeep      */
  --amber:          #B8845C;   /* AppColors.secondary        */
  --wax:            #9E2B25;   /* AppColors.waxRed           */
  /* Light --wax already clears 4.5:1 on every ground this file uses it as
     TEXT (a:hover, .card .no) as well as a glyph fill, so --wax-text is
     just an alias here -- the split only matters in dark mode below. */
  --wax-text:       #9E2B25;

  --fog:       rgba(154, 132, 98, 0.17);
  --sage-glow: rgba(74, 110, 80, 0.18);
  --shadow-sm: 0 1px 2px rgba(60, 48, 30, .06), 0 2px 8px rgba(60, 48, 30, .05);
  --shadow-md: 0 2px 6px rgba(60, 48, 30, .08), 0 10px 28px rgba(60, 48, 30, .09);
  --shadow-lg: 0 4px 12px rgba(60, 48, 30, .10), 0 24px 60px rgba(60, 48, 30, .14);

  --plate: 4px;   /* AppShape.plate */
  --pill: 999px;  /* AppShape.pill  */

  --sp-xs: 6px; --sp-s: 10px; --sp-m: 16px;
  --sp-l: 24px; --sp-xl: 36px; --sp-xxl: 56px;

  --column: 620px;  /* AppSpacing.column */
  --gutter: 20px;   /* AppSpacing.gutter */
  --wide: 1140px;
}

@media (prefers-color-scheme: dark) {
  :root {
    --parchment:      #181715;  /* AppColors.darkBackground  */
    --parchment-deep: #2A241C;  /* AppColors.darkParchment   */
    --surface:        #2F251C;  /* AppColors.darkSurface     */
    --surface-low:    #2C2A27;  /* AppColors.darkSurfaceLow  */
    --surface-high:   #403C38;  /* AppColors.darkSurfaceHigh */
    --ink:            #EADFC8;  /* AppColors.darkOnSurface   */
    --ink-2:          #C4BBB1;  /* AppColors.darkOnSurfaceMuted, WCAG pass 2026-09-27 */
    --sepia-text:     #D2BA92;  /* AppColors.darkSepia, WCAG pass 2026-09-27 */
    --rule:           #8A775A;  /* AppColors.darkOutline, WCAG pass 2026-09-27 */
    --rule-2:         #3A352E;
    --sage:           #E0B25A;  /* AppColors.darkPrimary (brass, not the retired mint -- WCAG pass 2026-09-27) */
    --sage-deep:      #C99A45;  /* AppColors.darkPrimaryDeep, WCAG pass 2026-09-27 */
    --amber:          #DBA87A;  /* AppColors.darkSecondary   */
    /* WCAG repair round, 2026-09-27: the 2026-09-27 WCAG pass brightened
       THIS token off AppColors.darkWaxRed (#A8342C -- a large lit seal,
       ~2.3:1 against a dark plate, fine for a seal but under 4.5:1 for the
       two TEXT uses this variable also carried, `a:hover`/`.card .no`) --
       which also recoloured the seal glyph and the pill icon (`.pill svg`,
       `.places .seal-glyph`, both still reading THIS variable), so the
       seal read as salmon instead of wax. Restored to the seal's own
       colour; --wax-text below now carries the brightened value for the
       two TEXT call sites only. */
    --wax:            #A8342C;   /* AppColors.darkWaxRed */
    /* The brightened value the WCAG pass derived (still correct for TEXT,
       just renamed to its own variable so it stops feeding the seal glyph
       too) -- hue-preserving off darkWaxRed: H 7.8->7.7 degrees, S
       47.9%->48.2% held, L 52.6%->62.2%, until the hardest ground
       (--surface-low) cleared 4.5:1: 5.73/5.07/4.57/4.91:1 on
       parchment/surface/surface-low/parchment-deep respectively. */
    --wax-text:       #CD7C70;

    /* WCAG repair round, 2026-09-27: was rgba(180, 158, 124, .10), the RGB
       of the app's PRE-WCAG-pass darkSepia -- a stale copy. Matches
       AppColors.darkSepia as of the WCAG repair round. */
    --fog:       rgba(210, 186, 146, .10);
    /* WCAG repair round, 2026-09-27: was rgba(163, 212, 167, .13), the RGB
       of the retired mint AppColors.darkPrimaryContainer-era accent (board
       5, 2026-09-25 -- see AppColors.darkPrimary's doc comment); this file
       already moved --sage/--sage-deep off that mint but left this glow
       behind. Matches the current dark accent, AppColors.darkPrimary
       (brass, #E0B25A). */
    --sage-glow: rgba(224, 178, 90, .13);
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, .40);
    --shadow-md: 0 2px 6px rgba(0, 0, 0, .45), 0 10px 28px rgba(0, 0, 0, .35);
    --shadow-lg: 0 4px 12px rgba(0, 0, 0, .50), 0 24px 60px rgba(0, 0, 0, .45);
  }
}

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0; background: var(--parchment); color: var(--ink);
  font-family: 'Jakarta', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  font-size: 17px; line-height: 1.65; -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* ── Type ───────────────────────────────────────────────────────── */

h1, h2, h3 { font-family: 'Noto Serif', Georgia, serif; font-weight: 500; line-height: 1.12; letter-spacing: -.015em; margin: 0; text-wrap: balance; }
h1 { font-size: clamp(42px, 7.6vw, 76px); letter-spacing: -.025em; }
h2 { font-size: clamp(30px, 4.6vw, 46px); }
h3 { font-size: 20px; line-height: 1.3; letter-spacing: -.01em; }

p { margin: 0 0 1.1em; } p:last-child { margin-bottom: 0; }
.lede { font-size: clamp(18px, 2.2vw, 21px); line-height: 1.6; color: var(--ink-2); max-width: 54ch; }
.muted { color: var(--ink-2); } .small { font-size: 15px; }

.eyebrow { font-family: 'Plex Mono', ui-monospace, monospace; font-weight: 500; font-size: 12px; letter-spacing: .15em; text-transform: uppercase; color: var(--sepia-text); margin: 0 0 var(--sp-m); }

a { color: var(--sage-deep); text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:hover { color: var(--wax-text); }
:focus-visible { outline: 2px solid var(--sage-deep); outline-offset: 3px; border-radius: var(--plate); }

.skip { position: absolute; left: -9999px; top: 0; background: var(--surface); color: var(--ink); padding: 12px 18px; border: 1px solid var(--rule); z-index: 20; }
.skip:focus { left: var(--gutter); top: 12px; }

/* ── Frame ──────────────────────────────────────────────────────── */

.wrap { width: min(var(--wide), calc(100% - 2 * var(--gutter))); margin-inline: auto; }
.col { max-width: var(--column); }
section { padding-block: clamp(var(--sp-xxl), 9vw, 112px); position: relative; }
.ruled { border-top: 1px solid var(--rule-2); }

/* Scroll reveal with no JavaScript. Browsers without animation-timeline just
   show the content; the @supports guard is what makes that safe. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .rv { animation: rise linear both; animation-timeline: view(); animation-range: entry 0% entry 62%; }
    @keyframes rise { from { opacity: 0; transform: translateY(26px); } to { opacity: 1; transform: none; } }
  }
}

/* ── Header ─────────────────────────────────────────────────────── */

header.site { position: sticky; top: 0; z-index: 10; padding-block: 14px; background: color-mix(in srgb, var(--parchment) 86%, transparent); backdrop-filter: saturate(150%) blur(14px); -webkit-backdrop-filter: saturate(150%) blur(14px); border-bottom: 1px solid var(--rule-2); }
.bar { display: flex; align-items: center; gap: var(--sp-s); }
.wordmark { font-family: 'Noto Serif', Georgia, serif; font-size: 19px; font-weight: 500; color: var(--ink); text-decoration: none; letter-spacing: -.01em; }
.bar nav { margin-left: auto; display: flex; gap: var(--sp-xs); }
.bar nav a { font-family: 'Plex Mono', ui-monospace, monospace; font-size: 11px; font-weight: 500; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-2); text-decoration: none; padding: 7px 11px; border-radius: var(--pill); border: 1px solid transparent; transition: color .15s ease, border-color .15s ease; }
.bar nav a[aria-current] { color: var(--ink); border-color: var(--rule); background: var(--surface); }
.bar nav a:hover { color: var(--wax-text); }

/* ── Hero ───────────────────────────────────────────────────────── */

/* The fogged survey sheet: soft fog blobs over parchment, which is what
   Explore actually looks like before a reader has charted much. */
.hero { padding-top: clamp(44px, 7vw, 84px); overflow: hidden; }
.hero::before {
  content: ''; position: absolute; inset: -30% -20% auto -20%; height: 1100px; pointer-events: none; z-index: 0;
  background:
    radial-gradient(38% 30% at 22% 26%, var(--fog), transparent 70%),
    radial-gradient(30% 24% at 74% 16%, var(--fog), transparent 70%),
    radial-gradient(34% 26% at 60% 52%, var(--fog), transparent 72%),
    radial-gradient(46% 36% at 50% 0%, var(--sage-glow), transparent 68%);
}
.hero > .wrap { position: relative; z-index: 1; }

.pill { display: inline-flex; align-items: center; gap: 8px; padding: 7px 14px 7px 11px; border: 1px solid var(--rule); border-radius: var(--pill); background: var(--surface); box-shadow: var(--shadow-sm); font-family: 'Plex Mono', ui-monospace, monospace; font-size: 11px; font-weight: 500; letter-spacing: .12em; text-transform: uppercase; color: var(--sepia-text); margin-bottom: var(--sp-l); }
.pill svg { width: 14px; height: 14px; flex: none; fill: var(--wax); }

/* Two columns from 940px: copy left, the product right. Below that the fan
   drops under the copy and turns back into a plain pair. */
.hero-grid { display: grid; grid-template-columns: 1.02fr 0.98fr; gap: clamp(28px, 4vw, 64px); align-items: center; }
.hero h1 { max-width: 15ch; }
.hero .lede { margin-top: var(--sp-l); }

/* A fan of two phones, the way the app's own plates overlap on the OG card:
   the front one upright, the second tucked behind and lower. */
.fan { position: relative; padding-block: var(--sp-l); }
.fan .phone { width: 58%; }
/* The back phone stays inside the column -- letting it bleed past the
   viewport edge chops a screen in half and reads as a layout bug. */
.fan .phone.back {
  position: absolute; inset: auto 0 14% auto; z-index: 0;
  transform: rotate(4deg) scale(.86); transform-origin: bottom right; opacity: .94;
}
.fan .phone.front { position: relative; z-index: 1; transform: rotate(-1.5deg); }
.fan:hover .phone.front { transform: rotate(-1.5deg) translateY(-6px); }
.fan:hover .phone.back { transform: rotate(5deg) scale(.9) translateY(-3px); }

@media (max-width: 940px) {
  .hero-grid { grid-template-columns: 1fr; }
  .hero h1 { max-width: 18ch; }
  .fan { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-m); padding-block: 0; margin-top: var(--sp-xl); }
  .fan .phone, .fan .phone.back, .fan .phone.front { position: static; width: 100%; transform: none; }
  .fan:hover .phone.front, .fan:hover .phone.back { transform: none; }
}

.cta-row { display: flex; flex-wrap: wrap; gap: var(--sp-s); margin-top: var(--sp-xl); }
.btn { display: inline-flex; align-items: center; justify-content: center; gap: 9px; min-height: 50px; padding: 0 26px; border-radius: var(--pill); border: 1px solid transparent; font-size: 16px; font-weight: 600; text-decoration: none; transition: transform .15s ease, box-shadow .15s ease, background .15s ease; }
.btn svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.btn:hover { text-decoration: none; transform: translateY(-2px); }
.btn-primary { background: var(--sage); color: var(--surface); box-shadow: 0 6px 20px var(--sage-glow); }
.btn-primary:hover { background: var(--sage-deep); color: var(--surface); box-shadow: 0 10px 28px var(--sage-glow); }
.btn-secondary { background: var(--surface); border-color: var(--rule); color: var(--ink); box-shadow: var(--shadow-sm); }
.btn-secondary:hover { color: var(--ink); box-shadow: var(--shadow-md); }
@media (prefers-color-scheme: dark) { .btn-primary, .btn-primary:hover { color: #17231A; } }

/* ── Store buttons ──────────────────────────────────────────────── */

/* The portfolio pattern (geborgen, stackflight): a live store is a solid
   button, a store we are not on yet is the same button greyed out and not a
   link, with a "coming soon" line under the row. When Android ships, the Play
   button becomes an <a> like the App Store one and the line goes. Ink on
   parchment rather than sage, so it does not compete with .btn-primary; the
   dot is amber and the text sepiaText, because --amber itself is under 4.5:1
   as text on parchment. Never wax -- that is the seal's colour only. */
.stores { display: flex; flex-wrap: wrap; gap: var(--sp-s); margin-top: var(--sp-l); }
.store-btn { display: inline-flex; align-items: center; gap: 11px; min-height: 54px; padding: 8px 22px 8px 17px; border-radius: 12px; background: var(--ink); color: var(--parchment); border: 1px solid var(--ink); box-shadow: var(--shadow-sm); text-decoration: none; transition: transform .15s ease, box-shadow .15s ease, background .15s ease, border-color .15s ease; }
.store-btn:hover { color: var(--surface); background: var(--sage-deep); border-color: var(--sage-deep); box-shadow: var(--shadow-md); transform: translateY(-2px); }
.store-btn svg { width: 23px; height: 23px; fill: currentColor; flex: none; }
.store-btn .st-text { display: block; font-size: 11px; line-height: 1.1; opacity: .8; }
.store-btn .st-name { display: block; font-size: 17px; font-weight: 600; line-height: 1.25; }
.store-btn.disabled, .store-btn.disabled:hover { background: var(--surface); border-color: var(--rule-2); color: var(--ink-2); box-shadow: none; cursor: default; transform: none; }
@media (prefers-color-scheme: dark) { .store-btn:hover { color: #17231A; } }
/* Keeps both buttons on one row at a 375px phone width. */
@media (max-width: 420px) { .store-btn { gap: 9px; padding: 8px 15px 8px 12px; } .store-btn .st-name { font-size: 16px; } .store-btn svg { width: 21px; height: 21px; } }
.soon { display: flex; align-items: center; gap: 8px; margin: var(--sp-s) 0 0; font-family: 'Plex Mono', ui-monospace, monospace; font-size: 11px; font-weight: 500; letter-spacing: .12em; text-transform: uppercase; color: var(--sepia-text); }
.soon::before { content: ''; width: 7px; height: 7px; border-radius: 50%; background: var(--amber); flex: none; }

.spec { margin-top: var(--sp-l); font-family: 'Plex Mono', ui-monospace, monospace; font-size: 12px; letter-spacing: .06em; color: var(--sepia-text); }

/* ── Device frames ──────────────────────────────────────────────── */

/* Warm, not a black slab: the bezel is the app's own surfaceHigh/outline
   pair, so a phone here reads as an object resting on the sheet rather than
   a product shot borrowed from another brand. */
.shots { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(14px, 2.6vw, 30px); margin-top: clamp(40px, 6vw, 72px); align-items: start; }
.shots-2 { grid-template-columns: repeat(2, 1fr); max-width: 720px; }
.shot { margin: 0; }
.phone { position: relative; padding: 6px; border-radius: clamp(20px, 5.5vw, 34px); background: linear-gradient(158deg, var(--surface-high), var(--rule-2) 55%, var(--surface-high)); box-shadow: var(--shadow-lg), 0 0 0 1px var(--rule-2); transition: transform .3s ease, box-shadow .3s ease; }
.shot:hover .phone { transform: translateY(-5px); box-shadow: var(--shadow-lg), 0 0 0 1px var(--rule); }
.phone img { display: block; width: 100%; height: auto; border-radius: clamp(15px, 4.4vw, 28px); background: var(--surface); }
.shot figcaption { margin-top: var(--sp-m); text-align: center; font-family: 'Plex Mono', ui-monospace, monospace; font-size: 11px; font-weight: 500; letter-spacing: .12em; text-transform: uppercase; color: var(--sepia-text); }
.shots > .shot:nth-child(2) .phone { transform: translateY(-16px); }
.shots > .shot:nth-child(2):hover .phone { transform: translateY(-22px); }

@media (max-width: 760px) {
  .shots { grid-template-columns: repeat(2, 1fr); }
  .shots > .shot:nth-child(3) { display: none; }
  .shots > .shot:nth-child(2) .phone, .shots > .shot:nth-child(2):hover .phone { transform: none; }
}

/* ── Places ─────────────────────────────────────────────────────── */

.places { list-style: none; margin: var(--sp-xl) 0 0; padding: 0; display: grid; gap: 1px; background: var(--rule-2); border: 1px solid var(--rule-2); border-radius: 10px; overflow: hidden; }
.places li { background: var(--parchment); display: grid; grid-template-columns: 40px 128px 1fr; gap: var(--sp-m); align-items: start; padding: var(--sp-l); transition: background .15s ease; }
.places li:hover { background: var(--surface); }
.places .glyph { width: 25px; height: 25px; margin-top: 3px; fill: none; stroke: var(--sage); stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.places .seal-glyph { fill: var(--wax); stroke: none; }
.places dfn { font-family: 'Plex Mono', ui-monospace, monospace; font-size: 12px; font-weight: 500; letter-spacing: .12em; text-transform: uppercase; color: var(--sage-deep); font-style: normal; padding-top: 4px; }
.places p { margin: 0; color: var(--ink-2); }
@media (max-width: 620px) {
  .places li { grid-template-columns: 30px 1fr; gap: var(--sp-s) var(--sp-m); }
  .places dfn, .places p { grid-column: 2; }
  .places dfn { padding-top: 0; }
}

/* ── Cards ──────────────────────────────────────────────────────── */

.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: var(--sp-m); margin-top: var(--sp-xl); }
.card { background: var(--surface); border: 1px solid var(--rule-2); border-radius: 10px; padding: var(--sp-l); box-shadow: var(--shadow-sm); transition: transform .2s ease, box-shadow .2s ease; }
.card:hover { transform: translateY(-3px); box-shadow: var(--shadow-md); }
.card h3 { font-family: 'Jakarta', sans-serif; font-size: 16px; font-weight: 600; margin-bottom: var(--sp-xs); letter-spacing: 0; }
.card p { font-size: 15px; line-height: 1.55; color: var(--ink-2); margin: 0; }
.card .no { display: block; font-family: 'Plex Mono', ui-monospace, monospace; font-size: 11px; letter-spacing: .14em; color: var(--wax-text); margin-bottom: var(--sp-s); }

/* ── Feature band ───────────────────────────────────────────────── */

.band { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(28px, 5vw, 72px); align-items: center; }
@media (max-width: 900px) { .band { grid-template-columns: 1fr; } }

/* ── Plate ──────────────────────────────────────────────────────── */

.plate { margin-top: var(--sp-xl); padding: clamp(24px, 4vw, 40px); background: var(--surface); border: 1px solid var(--rule-2); border-radius: 12px; box-shadow: var(--shadow-md); position: relative; overflow: hidden; }
.plate::after { content: ''; position: absolute; inset: auto -60px -80px auto; width: 260px; height: 260px; background: radial-gradient(circle, var(--sage-glow), transparent 68%); pointer-events: none; }
.plate > * { position: relative; }
.price { margin-top: var(--sp-m); font-family: 'Plex Mono', ui-monospace, monospace; font-size: 12.5px; letter-spacing: .05em; color: var(--sepia-text); }

/* ── Footer ─────────────────────────────────────────────────────── */

footer { border-top: 1px solid var(--rule); padding: var(--sp-xxl) 0 72px; background: var(--parchment-deep); }
.foot-links { display: flex; flex-wrap: wrap; gap: var(--sp-s) var(--sp-l); margin: 0 0 var(--sp-l); padding: 0; list-style: none; }
.foot-links a { font-size: 15px; }
.disclaimer { font-size: 14px; line-height: 1.6; color: var(--ink-2); max-width: 62ch; }
.copyright { margin-top: var(--sp-l); font-family: 'Plex Mono', ui-monospace, monospace; font-size: 11px; letter-spacing: .1em; text-transform: uppercase; color: var(--sepia-text); }

/* ── Long-form legal pages ──────────────────────────────────────── */

.doc { width: min(760px, calc(100% - 2 * var(--gutter))); margin-inline: auto; padding: var(--sp-xxl) 0 88px; }
.doc h1 { font-size: clamp(32px, 6vw, 52px); margin-bottom: var(--sp-xs); }
.doc h2 { font-size: 22px; margin-top: var(--sp-xxl); padding-top: var(--sp-l); border-top: 1px solid var(--rule-2); }
.doc p, .doc li { color: var(--ink-2); }
.doc ul { padding-left: 1.2em; }
.doc li { margin-bottom: var(--sp-xs); }
.doc code { font-family: 'Plex Mono', ui-monospace, monospace; font-size: .9em; background: var(--surface-low); padding: 1px 5px; border-radius: var(--plate); }
.doc .updated { font-family: 'Plex Mono', ui-monospace, monospace; font-size: 12px; letter-spacing: .1em; text-transform: uppercase; color: var(--sepia-text); margin-bottom: var(--sp-xl); }
.back { display: inline-block; margin-bottom: var(--sp-l); font-family: 'Plex Mono', ui-monospace, monospace; font-size: 12px; font-weight: 500; letter-spacing: .1em; text-transform: uppercase; text-decoration: none; }
.back:hover { text-decoration: underline; }

/* Two band variants that used to be inline `style` attributes. They cannot
   be: firebase.json's CSP is `style-src 'self'` with no 'unsafe-inline', so
   an inline style attribute is blocked outright and the block silently falls
   back to the two-column `.shots-2` grid. Caught in review after it had
   already shipped. */
.shots-solo { margin-top: 0; grid-template-columns: 1fr; max-width: 340px; }
.shots-flush { margin-top: 0; }

/* Spam-bot decoy. The address is split by a hidden span in the link text and
   written as numeric character references in the href, so neither the rendered
   HTML nor a regex over the source yields a harvestable address. There is no
   JavaScript here on purpose: the site's CSP sets `default-src 'none'` with no
   `script-src`, so the usual deobfuscate-on-load trick would simply be blocked.
   If this rule ever fails to load the decoy becomes visible and reads as an
   obvious instruction rather than as part of the address -- the link itself
   still works, because the href does not depend on this. */
.eml-x { display: none; }
