/* ============================================================
   SongGenie rebuild — shared design system
   Defined ONCE here. Every page inherits these tokens/components.

   THEME (2026-08-24): "Deco Diagonal" — ported from flavor 26 of the
   homepage flavor set (../(C) SongGenie Homepage Flavors/26-deco-diagonal-b.html),
   which itself layers its own <style> block on top of that folder's shared
   layout scaffold (_shared.css). This file is the UNION of both, with
   flavor 26's overrides winning wherever the two disagree — flavor 26 is
   NOT self-contained on its own, and porting only its own <style> block
   would silently drop real layout rules (.nav padding, .nav__logo flex
   gap, img{max-width:100%}, grid/hero/trust/case/gallery/form structure,
   the responsive breakpoints) that it depends on. Replaces the previous
   "Genie" / Arabian-Nights theme (Playfair Display, gold/emerald jewel
   tones, octagon medallions, dome arches, an oil-lamp emblem) wholesale.

   Palette: warm sand base, oxblood as the primary accent, deep gold and
   jewel teal as secondary accents. Oswald condensed caps for display,
   Jost for body. Flat, hard-edged, slanted-parallelogram clip-path
   framing instead of rounded corners/soft shadows — deliberately, this
   is a street-sign / art-deco register, not a soft one.
   ============================================================ */

/* Fonts: bunny.net (same free public CDN both codebases already use),
   exact family string as flavor 26 and the flavor set's own pages. */
/* Fonts are loaded by ONE <link> in layout.html, with display=swap.
   The @import that used to sit here fetched a second, slightly different URL
   (it omitted caveat), so every page requested the font CSS twice, and because
   an @import chains behind the stylesheet that contains it, the second request
   could not even start until styles.css had arrived. Removed 2026-09-07.
   Do not reinstate it: if a font is missing, add it to the link in layout.html. */

/* ---------- Design tokens ---------- */
:root {
  /* Deco Diagonal palette (flavor 26) */
  --bg:      #EFE4CB;  /* sand — body + default section bg */
  --surface: #E6D5B2;  /* slightly deeper sand — .section--light bands */
  --card:    #FBF4E3;  /* warm ivory — cards, form panels, the wordmark plate */
  --ink:     #2A2016;  /* near-black warm brown — headings, body text, footer bg */
  --oxblood: #9E3A2B;  /* primary accent — buttons, eyebrow marks, dividers */
  --teal:    #175E54;  /* secondary accent — ghost buttons, links, 3rd card variant */
  --gold:    #BE8F2C;  /* tertiary accent — dividers, sunburst, 2nd card variant */
  --line:    rgba(42,32,22,0.16);

  /* --muted deliberately DARKER than flavor 26's own literal #7B6949.
     Measured: #7B6949 on --bg is 4.20:1, which fails WCAG AA body-text
     contrast (needs 4.5:1) — flavor 26 is a reviewed mockup, not a
     contrast-audited stylesheet. #655537 (same warm-brown family) clears
     AA against all three surfaces this token is actually used on:
     5.72:1 on --bg, 5.00:1 on --surface, 6.59:1 on --card. */
  --muted: #655537;

  /* Compatibility aliases — real inline `style="color:var(--x)"` and
     `style="...var(--sp-n)..."` references exist today in src/pages and
     src/partials (index.html, footer.html). These names must keep
     resolving even though their old "on dark ink" meaning no longer
     applies: flavor 26 has no dark body sections (only the footer is
     dark), so both former "on light" and former "on dark" roles now
     collapse onto the same warm-ink-on-sand system. Flavor 26 itself
     makes exactly this call — its own `.muted-on-dark{ color:var(--muted) }`
     rule below is copied verbatim, not reinterpreted. */
  --accent:        var(--oxblood);
  --paper-ink:     var(--ink);
  --slate:         var(--muted);

  /* Typography */
  --font-display: 'Oswald', sans-serif;
  --font-sans:    'Jost', sans-serif;

  /* Spacing scale — unchanged from the previous theme. Referenced
     directly by var(--sp-N) inline styles in existing page markup
     (index.html, footer.html), so the names and rem values stay fixed
     regardless of visual theme. */
  --sp-1: 0.5rem; --sp-2: 1rem; --sp-3: 1.5rem; --sp-4: 2.5rem; --sp-5: 4rem; --sp-6: 6rem;

  --radius: 0;      /* deco is flat/slanted, not rounded — was 16px under the Genie theme */
  --radius-sm: 0;   /* was 10px */
  --maxw: 1180px;   /* matches flavor 26 / _shared.css .wrap exactly (was 1160px) */
  --shadow: 4px 4px 0 rgba(42,32,22,0.18); /* hard-edged "sticker" shadow, matches the wordmark plate's box-shadow language — soft blurred shadows belong to the old theme, not this one */
}

/* ---------- Reset-ish (from _shared.css, the flavor set's shared scaffold) ---------- */
*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 18px;   /* was 16px, raised 2pt at Max's request 5 Sep for readability */
  line-height: 1.7;
  color: var(--ink);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}
img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
button { font: inherit; }

h1, h2, h3 {
  font-family: var(--font-display); font-weight: 600; line-height: 1.05;
  letter-spacing: 0.01em; text-transform: uppercase; margin: 0 0 var(--sp-2);
  color: var(--ink); overflow-wrap: break-word;
}
h1 { font-size: clamp(2.6rem, 6vw, 5rem); font-weight: 700; }
h2 { font-size: clamp(2rem, 4vw, 3.4rem); }   /* raised 5 Sep, was 2.8rem max */
h3 { font-size: 1.2rem; letter-spacing: 0.02em; }
p { margin: 0 0 14px; }
.lead { font-size: 1.22rem; color: var(--muted); }

.eyebrow {
  font: 600 12px/1 var(--font-display); text-transform: uppercase; letter-spacing: 0.26em;
  color: var(--oxblood); margin: 0 0 var(--sp-2); display: flex; align-items: center; gap: 10px;
}
.eyebrow::before { content: '\25B8\25B8'; color: var(--gold); letter-spacing: -2px; }
.center .eyebrow, .measure.center .eyebrow { justify-content: center; }

/* NOTE on the class name: "italic-accent" no longer renders italic.
   Flavor 26 keeps the name from earlier flavors in the same set but
   flattens the style to non-italic oxblood — a naming leftover in the
   *reviewed, read-only* flavor file, reproduced faithfully rather than
   silently "corrected" here, since renaming it would mean hunting down
   every inline usage across pages for a purely cosmetic mismatch. */
.italic-accent { color: var(--oxblood); font-style: normal; font-weight: 500; }

/* Verbatim from flavor 26: since there are no dark body sections in this
   theme, "muted on dark" and plain "muted" are the same color. Kept as a
   distinct class because flavor 26 defines it as one. */
.muted-on-dark { color: var(--muted); }

/* Ornamental divider — DISABLED, matching flavor 26 exactly.
   The Genie-theme ornament (gold SVG flourish) belonged to that theme
   only. Flavor 26's own CSS sets `.ornament{ display:none }` because the
   deco-diagonal direction dropped ornamental dividers in favour of the
   slant-frame / chevron / repeating-stripe motifs below. index.html still
   has two `<div class="ornament ornament--center">` markers (Our Process,
   Case Studies headers) from the old theme; hiding them here reproduces
   flavor 26's actual behaviour without having to touch page markup. */
.ornament, .ornament--center { display: none; }

/* ---------- Layout helpers ---------- */
.wrap { width: 100%; max-width: var(--maxw); margin: 0 auto; padding: 0 var(--sp-3); }
.section { padding: var(--sp-6) 0; position: relative; }
.section--tight { padding: var(--sp-5) 0; }
.section--light { background: var(--surface); }

/* .section--dark WAS NEVER STYLED. Found 2026-09-08: the guarantee section on
   both main-offer.md and ai-remake.md has carried this class since it was
   first built, and it did nothing at all, so "Finish Your Song Without Risk"
   has been rendering as a plain undifferentiated section on both live pages
   this whole time. Caught because Max asked for it to visually alternate
   against its neighbour and a screenshot showed two identical backgrounds.

   Text colours are NOT optional extras here, they are the fix: h1/h2/h3
   default to color:var(--ink), which is the exact colour this rule sets as
   the background, so a bare "background: var(--ink)" with no text overrides
   would have made every headline in the section invisible, dark-on-dark.
   Contrast checked against #2A2016 (--ink), not eyeballed: --card (ivory)
   14.55:1, --gold 5.43:1, the footer's own #C9B896 8.19:1. --muted and
   --oxblood, which .lead and .eyebrow use by default, measure 2.21:1 and
   2.35:1 on this background and both fail AA, so they are overridden here
   specifically rather than left to inherit. Mirrors the footer's own
   light-on-dark palette (background:var(--ink); body text #C9B896; headings
   var(--gold)) rather than inventing a second dark palette. */
.section--dark { background: var(--ink); }
.section--dark h1, .section--dark h2, .section--dark h3 { color: var(--card); }
.section--dark .eyebrow { color: var(--gold); }
.section--dark .lead, .section--dark .field-hint { color: #C9B896; }
.center { text-align: center; }
.measure { max-width: 640px; }
.measure.center { margin-left: auto; margin-right: auto; }

/* ---------- Buttons ----------
   Structure (display/padding/cursor/line-height) from _shared.css;
   skin (flat rectangles, uppercase Oswald, per-variant colour) from
   flavor 26, which sets border-radius:0 explicitly — a deliberate
   rejection of the old theme's pill buttons. */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 15px 30px; border: 2px solid transparent; border-radius: 0;
  text-decoration: none; cursor: pointer; line-height: 1; white-space: nowrap;
  font-family: var(--font-display); font-weight: 600; font-size: 13px;
  text-transform: uppercase; letter-spacing: 0.1em;
  transition: transform .15s ease, background .15s ease, color .15s ease, border-color .15s ease;
}
.btn--lg { padding: 18px 34px; }
.btn--primary { background: var(--oxblood); color: var(--card); }
.btn--primary:hover { transform: translateX(3px); }
.btn--ghost { background: transparent; border-color: var(--teal); color: var(--teal); }
.btn--ghost:hover { transform: translateX(3px); }
.btn--dark { background: var(--teal); color: #F3E9D2; border-color: var(--teal); }
.btn--dark:hover { transform: translateX(3px); }

/* ---------- Top nav ---------- */
.nav { position: sticky; top: 0; z-index: 50; background: var(--bg); border-bottom: 3px solid var(--ink); }
/* Fixed AT SOURCE, not patched locally like flavor 26 had to (it doesn't
   own _shared.css, which 25 other flavors depend on). The bug: a
   shorthand `padding: Y 0` on the SAME element that also needs the
   .wrap horizontal inset zeroes that inset, so the nav sits flush to the
   viewport edge while every other section is inset by var(--sp-3). This
   file's nav__inner carries BOTH dimensions in one shorthand
   (`padding: Y X`), so the bug cannot recur — there's no second element
   supplying the horizontal padding for it to clobber. */
.nav__inner {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--sp-3); padding: 22px var(--sp-3);
  max-width: var(--maxw); margin: 0 auto;
}
.nav__links { display: flex; align-items: center; gap: 30px; list-style: none; margin: 0; padding: 0; }
.nav__links a {
  font-family: var(--font-display); font-size: 13px; text-transform: uppercase;
  letter-spacing: 0.08em; color: var(--ink);
}
.nav__links a:hover { color: var(--oxblood); }
.nav__cta { display: flex; align-items: center; gap: 12px; }

/* --- Street-sign wordmark plate ---
   Westminster-pattern nameplate: cream plate, dark keyline, oxblood band
   across the top carrying "LONDON / W1", big condensed caps below. Same
   Oswald 700 caps and the SONG (ink) / GENIE (oxblood) two-tone split the
   plain wordmark already used — just staged on a street-sign background,
   per the flavor's own reasoning. "LONDON / W1" rather than a real
   borough claim: London is already sold in the hero copy, "City of
   Westminster" would invent a civic association SongGenie doesn't have. */
.nav__logo {
  display: block; text-decoration: none; line-height: 1; overflow: hidden;
  background: var(--card); border: 2px solid var(--ink); box-shadow: 2px 2px 0 rgba(42,32,22,0.2);
}
.nav__logo .sign__band {
  display: flex; justify-content: space-between; align-items: center; gap: 16px;
  background: var(--oxblood); padding: 4px 7px 3px;
  font: 600 9px/1 var(--font-display); letter-spacing: 0.18em; text-transform: uppercase;
}
/* Scoped one level deeper than a bare `.sign__band{color:...}` on purpose:
   these spans sit ON the oxblood band, and a same-specificity rule here
   would be a coin-flip against any other single-class rule touching
   .nav__logo's descendants. Scoping to the child guarantees the band text
   reads in --card (cream), not invisible oxblood-on-oxblood. */
.nav__logo .sign__band span { color: var(--card); }
.nav__logo .sign__name {
  display: block; padding: 5px 10px 7px; text-align: center; white-space: nowrap;
  font-family: var(--font-display); font-weight: 700; font-size: 23px; letter-spacing: 0.01em;
  text-transform: uppercase; color: var(--ink);
}
/* <em> rather than a plain span so it can carry its own colour without
   fighting any other descendant-span rule. The word gap is explicit and
   load-bearing: nothing here gives SONG/GENIE a gap for free the way
   `.nav__logo{ display:flex; gap:10px }` would on the plain wordmark —
   this plate is `display:block`, so without `margin-left` it reads as
   one word, "SONGGENIE". */
.nav__logo .sign__name em { font-style: normal; color: var(--oxblood); margin-left: 0.28em; }

/* CSS-drawn hamburger — kept from the existing architecture (flavor 26 /
   _shared.css is a static comparison mockup and has no working mobile
   menu at all, it just hides nav links under 900px with nothing to
   replace them; that's fine for a side-by-side review page, not for a
   live site). Restyled to the new palette only. */
.nav__toggle { display: none; flex-direction: column; gap: 5px; background: none; border: 0; cursor: pointer; padding: 6px; }
.nav__toggle span { display: block; width: 24px; height: 2px; background: var(--ink); border-radius: 1px; }
.nav__mobile-only { display: none; }

@media (max-width: 900px) {
  .nav__links { display: none; }
  /* Book a Call STAYS VISIBLE on mobile, deliberately. It used to be
     display:none here, which left the booking page reachable only from inside
     the hamburger. Shrunk rather than hidden so it still fits beside the logo
     at 390px. Changed 2026-09-07 after an audit found one booking link on the
     entire homepage. */
  .nav__cta .btn { display: none; }
  .nav__cta .btn--primary {
    display: inline-flex; padding: 9px 12px; font-size: 12px; letter-spacing: 0.04em;
  }
  .nav__toggle { display: flex; }
  .nav__links.is-open {
    display: flex; flex-direction: column; align-items: flex-start;
    position: absolute; top: 100%; left: 0; right: 0;
    background: var(--card); padding: var(--sp-3);
    border-bottom: 3px solid var(--ink); gap: 1rem;
  }
  .nav__links.is-open .nav__mobile-only { display: block; }
}

@media (max-width: 640px) {
  /* Shrink the nameplate so it and the CTA fit one line comfortably. */
  .nav__logo .sign__name { font-size: 17px; padding: 4px 7px 5px; }
  .nav__logo .sign__band { font-size: 8px; letter-spacing: 0.12em; gap: 10px; padding: 3px 6px 2px; }
  .nav__cta .btn { padding: 12px 16px; font-size: 12px; }
  .nav__inner { padding-left: 16px; padding-right: 16px; }
}

/* ---------- Hero ----------
   Signature: diagonal energy — rotated sunburst + slanted parallelogram
   frame + a rushing chevron band once, against the trust strip. */
.hero { position: relative; overflow: hidden; padding: 80px 0; }
.hero::before {
  content: ''; position: absolute; right: -140px; top: -160px; width: 620px; height: 620px;
  background: repeating-conic-gradient(from 0deg at 50% 50%, var(--gold) 0deg 0.8deg, transparent 0.8deg 7deg);
  -webkit-mask: radial-gradient(circle, #000 0%, transparent 66%);
  mask: radial-gradient(circle, #000 0%, transparent 66%);
  opacity: 0.16; pointer-events: none;
}
.hero .wrap { position: relative; }
.hero__grid { position: relative; z-index: 1; display: grid; grid-template-columns: 1.1fr 0.9fr; gap: 56px; align-items: center; }
.hero__media { position: relative; }

/* Hand-written "click to play" nudge for the ai-remake hero video, added
   2026-09-08. THIRD placement, moved out from over the video entirely:
   top-right collided with the poster's own baked-in "Max H." label;
   bottom-left-over-the-video was invisible because .slant-frame clips
   (polygon clip-path) anything positioned outside its own box, and
   separately sat behind the browser's native video control bar, which
   paints over page content layered on the video element. Now a normal
   block below .hero-media-wrap's frame, arrow pointing up at the video
   above it, nothing to fight with. Reuses .scrawl (Caveat font, oxblood,
   slight rotation) rather than inventing a second handwriting treatment. */
.hero-media-wrap { display: flex; flex-direction: column; }
.hero-video-note {
  margin-top: 10px;
  text-align: center;
}
.hero-video-note .scrawl {
  display: inline-block;
  font-family: 'Caveat', 'Bradley Hand', cursive;
  font-size: 26px; line-height: 1.1;
  color: var(--oxblood);
  transform: rotate(-3deg);
}
@media (max-width: 640px) {
  .hero-video-note .scrawl { font-size: 20px; }
}
.hero__actions { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 28px; }

/* Slanted parallelogram frame, used for the hero video and reusable
   anywhere an image needs the same treatment. */
.slant-frame { clip-path: polygon(9% 0, 100% 0, 91% 100%, 0 100%); background: var(--oxblood); padding: 8px; }
.slant-frame img {
  /* height: auto is explicit and load-bearing, same reasoning as the video
     rule below it. build.js now auto-injects real width/height ATTRIBUTES on
     every <img> to stop layout shift, and those attributes carry a real
     priority in the cascade: with width:100% here overriding the attribute's
     width but nothing overriding its height, the image kept the ATTRIBUTE'S
     height (its true natural pixel height, e.g. 750px for a square photo)
     while its width scaled to the container, stretching/squashing it. Found
     2026-09-08 on main-offer.md; the identical photo has been silently
     stretched on about.md the same way this whole time, fixed here too. */
  display: block; width: 100%; height: auto;
  clip-path: polygon(9% 0, 100% 0, 91% 100%, 0 100%);
  filter: contrast(1.03) saturate(0.96);
}
/* height:auto is explicit and load-bearing: unlike <img>, <video> does
   not inherit `img{max-width:100%}` above, so without this it renders at
   its intrinsic pixel width and blows out the frame. */
.slant-frame video {
  display: block; width: 100%; height: auto;
  clip-path: polygon(9% 0, 100% 0, 91% 100%, 0 100%);
  filter: contrast(1.03) saturate(0.96);
}

@media (max-width: 900px) { .hero__grid { grid-template-columns: 1fr; } }
@media (max-width: 640px) {
  h1 { font-size: 2.05rem; }
  .hero { padding: 40px 0 44px; }
  .hero__actions .btn { width: 100%; }
}

/* Headline parenthetical — "(and release guidance)" as a genuine aside,
   not a second main clause. In the H1's own condensed uppercase Oswald it
   would carry identical visual weight to the rest of the line and read
   as a shout instead of a softener. display:block matters as much as the
   size drop: kept inline, the smaller span leaves a full-size "FOR"
   stranded on the same line, which reads as a mistake. Body face,
   sentence case, --muted — the same treatment .lead already uses, so it
   reads as spoken rather than displayed. */
.h1-aside {
  display: block; font-family: var(--font-sans); font-size: 0.34em; font-weight: 400;
  text-transform: none; letter-spacing: 0.01em; color: var(--muted); line-height: 1.3;
  margin: 0.18em 0 0.10em;
}
@media (max-width: 640px) {
  /* Sized in em, so it shrinks in step with h1 — at the reduced mobile
     headline size, 0.34em lands near 11px (weedy, reads as fine print).
     Nudged up so it stays a deliberate aside rather than an accident. */
  .h1-aside { font-size: 0.44em; margin: 0.24em 0 0.14em; }
}

/* Rushing chevron divider. Used ONCE only, between the hero and the trust
   strip, which is the one genuinely skewed section on the page
   (.trust below). Per the flavor's own note: this only reads correctly
   where the section edge behind it is actually diagonal too — elsewhere
   on the page sections are plain horizontal blocks, so this stays a
   single, deliberate accent rather than a repeated motif. */
.rush {
  height: 38px;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='30' height='38' viewBox='0 0 30 38'%3E%3Cpath d='M2 8 L14 19 L2 30 M14 8 L26 19 L14 30' fill='none' stroke='%239E3A2B' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") repeat-x center/auto 20px;
  transform: skewY(-1.6deg); opacity: 0.8;
}

/* ---------- Trust / logo strip ---------- */
.trust { background: var(--surface); padding: 60px 0; transform: skewY(-1.4deg); }
.trust > * { transform: skewY(1.4deg); }
.trust__label { font: 600 13px/1 var(--font-display); letter-spacing: 0.2em; text-transform: uppercase; color: var(--muted); margin: 0 0 8px; text-align: center; }
.trust__logos { display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: 52px; margin-top: 32px; }
.trust__logos img { height: 54px; width: auto; filter: grayscale(1) sepia(0.2); opacity: 0.9; }
@media (max-width: 640px) { .trust__logos { justify-content: center; } }

/* ---------- Grids / cards ---------- */
.grid { display: grid; gap: 32px; }
.grid--1 { grid-template-columns: 1fr; }   /* one column, for sections meant to read long rather than wide */
.grid--2 { grid-template-columns: repeat(2, 1fr); }
.grid--3 { grid-template-columns: repeat(3, 1fr); }
.grid--4 { grid-template-columns: repeat(4, 1fr); }

/* A grid item defaults to min-width:auto, meaning it refuses to shrink below its
   content's intrinsic minimum. Form inputs carry an intrinsic width from their
   `size` attribute, so on a narrow screen they forced the .grid--2 TRACK to
   368px inside a 312px container. Because body sets overflow-x:hidden, the
   surplus was not a sideways scroll, it was silently cut off: the right-hand end
   of every line on index, contact and not-good-fit at 360px. Found 2026-09-06 by
   sweeping all 22 pages; those three were the three pages carrying a form.
   min-width:0 lets the track shrink to its container, which is what was intended
   everywhere. */
.grid > * { min-width: 0; }
.field input, .field textarea { min-width: 0; }
@media (max-width: 900px) { .grid--2, .grid--3, .grid--4 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 640px) { .grid--2, .grid--3, .grid--4 { grid-template-columns: 1fr; } }

.card { background: var(--card); padding: 34px; border: none; border-left: 5px solid var(--oxblood); position: relative; }
.card:nth-child(2) { border-left-color: var(--gold); }
.card:nth-child(3) { border-left-color: var(--teal); }
.card:nth-child(4) { border-left-color: var(--oxblood); }

/* Step "medallion" numbers — notched deco tag shape, not the old theme's
   octagon. */
.step__num {
  display: inline-flex; align-items: center; justify-content: center;
  width: 52px; height: 52px;
  clip-path: polygon(0 0, 100% 0, 100% 78%, 78% 100%, 0 100%);
  background: var(--oxblood); color: var(--card);
  font-family: var(--font-display); font-weight: 700; font-size: 1.4rem;
  margin-bottom: 14px;
}
.card:nth-child(2) .step__num {
  background: var(--gold);
  /* Contrast fix, not a literal flavor-26 port: flavor 26's own CSS only
     swaps the background per variant and leaves the badge text colour
     fixed at --card (cream). Measured: cream-on-gold is 2.68:1, which
     fails WCAG AA even at the large-text threshold (3:1) that a 22px
     bold digit qualifies for. Swapping to --ink for this one variant
     gets 5.43:1. The flavor is a reviewed mockup, not a contrast-audited
     stylesheet — this is a real accessibility bug worth not reproducing
     on a live commercial page. */
  color: var(--ink);
}
.card:nth-child(3) .step__num { background: var(--teal); }
.card:nth-child(4) .step__num { background: var(--oxblood); }
.step__sub { color: var(--muted); margin: -0.3rem 0 14px; font-weight: 600; }
ul { padding-left: 18px; margin: 0; color: var(--muted); }
li { margin-bottom: 8px; }
li::marker { color: var(--oxblood); }


/* ---------- Step rows: big label left, bullets right ----------
   Revised 2026-09-05 (Max): the number AND the step name are the loud part and
   live together on the left; the bullets run as one plain list down the right.
   The earlier version put a small number beside a two-column bullet list, which
   buried the thing the section is actually about.
   Left column is ~34% and capped, so long names like "Lift-off" never squeeze
   the bullets into a gutter. Stacks on narrow screens. */
.step--row { display: grid; grid-template-columns: minmax(200px, 0.52fr) 1fr; gap: var(--sp-4); align-items: start; }
.step__lead {
  position: relative;      /* the step-4 genie anchors to THIS, not to the whole
                              card, so it stays welded to the number and title
                              as the layout narrows (Max, 5 Sep) */
  display: flex; flex-direction: column; gap: 10px;
  align-self: center;      /* vertically centred in the row, not pegged to the top */
  align-items: center;     /* and horizontally centred within its own column */
  text-align: center;
}
/* Bullets centred vertically too, so a short step like Repeat sits in the
   middle of its card instead of hugging the top edge (Max, 5 Sep). */
.step--row .step__body { align-self: center; }
.step--row .step__num {
  width: 88px; height: 88px; font-size: 2.6rem; margin-bottom: 2px;
}
.step--row .step__lead h3 {
  font-size: clamp(1.55rem, 2.1vw, 2.15rem);   /* eased down 5 Sep, was 2.6rem */
  line-height: 1.0; margin: 0; text-transform: uppercase; letter-spacing: 0.01em;
}
.step--row .step__lead .step__sub { margin: 0; font-size: 0.95rem; }
.step--row .step__body ul { columns: 1; margin: 0; }
.step--row .step__body li { margin-bottom: 12px; }
@media (max-width: 760px) {
  .step--row { grid-template-columns: 1fr; gap: var(--sp-3); }
  .step--row .step__num { width: 68px; height: 68px; font-size: 2rem; }
}

/* ---------- Case studies ---------- */
.case { display: grid; grid-template-columns: 128px 1fr; gap: 22px; align-items: start; }
.case img { aspect-ratio: 1; object-fit: cover; clip-path: polygon(6% 0, 100% 0, 94% 100%, 0 100%); }
.case__meta { font-weight: 700; margin: 0 0 8px; color: var(--oxblood); }
@media (max-width: 900px) { .case { grid-template-columns: 88px 1fr; } }
@media (max-width: 640px) { .case { grid-template-columns: 1fr; } .case img { max-width: 220px; } }

/* ---------- Project gallery ---------- */
.gallery { display: grid; grid-template-columns: repeat(3, 1fr); gap: 22px; }
@media (max-width: 900px) { .gallery { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 640px) { .gallery { grid-template-columns: 1fr; } }
.tile { position: relative; overflow: hidden; aspect-ratio: 1 / 1; }
/* Square, because every source image IS square (all six are 480x480). The old
   4/3 box with object-fit:cover was slicing the top and bottom off artwork that
   never needed cropping. Fixed 5 Sep. */
.tile img { width: 100%; height: 100%; object-fit: cover; clip-path: polygon(5% 0, 100% 0, 95% 100%, 0 100%); transition: transform .3s; }
.tile:hover img { transform: scale(1.05); }
.tile__cap { padding-top: 10px; display: flex; flex-direction: column; }
.tile__cap b { font-size: 1rem; color: var(--ink); }
/* Explicit colour rather than the --muted-on-dark alias: this caption
   sits directly under the image, on the page's own light background —
   not on a dark gradient overlay the way the old theme's tile captions
   did, so it needs an on-LIGHT muted tone, same family as --muted. */
.tile__cap span { font-size: 0.82rem; color: var(--muted); }

/* ---------- Callout band (not currently used by a shipped page; kept for future use) ---------- */
.band { background: var(--card); border-left: 5px solid var(--oxblood); padding: var(--sp-5); text-align: center; }

/* ---------- Price / product (not currently used by a shipped page; kept for future store use) ---------- */
.price { display: inline-flex; align-items: baseline; gap: 0.4rem; font-family: var(--font-display); font-weight: 700; font-size: 2.4rem; color: var(--ink); }
.price small { font-size: 1rem; color: var(--muted); font-family: var(--font-sans); font-weight: 600; }
.placeholder-flag {
  display: inline-block; margin-left: 0.6rem; vertical-align: middle;
  background: var(--card); color: var(--oxblood); border: 1px solid var(--gold);
  font-family: var(--font-sans); font-size: 0.72rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.5px; padding: 0.2rem 0.55rem;
}
.todo-flag {
  display: block; background: var(--card); color: var(--oxblood); border: 1px dashed var(--gold);
  padding: var(--sp-2); margin: var(--sp-2) 0; font-size: 0.92rem; font-weight: 600;
}

/* ---------- Forms (STUBBED — see comments in partials/pages) ---------- */
.formcard { background: var(--card); padding: 34px; border: none; border-top: 5px solid var(--oxblood); }
.field { margin-bottom: var(--sp-2); text-align: left; display: flex; flex-direction: column; gap: 6px; }
.field label { font: 600 12px var(--font-display); text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); }
.field input, .field textarea {
  padding: 13px 15px; border-radius: 0;
  border: 1px solid var(--line); font: inherit; font-size: 1rem; background: var(--bg); color: var(--ink);
}
.field textarea { min-height: 110px; resize: vertical; }
.form-note { font-size: 0.8rem; color: var(--muted); margin-top: var(--sp-2); }

/* ---------- Qualifying question groups (new, application.html) ----------
   Radio/checkbox option lists for a multi-question qualifying form. Reuses
   the existing --line/--muted/--oxblood tokens rather than introducing new
   colour, same approach as the FAQ accordion. `fieldset.field` piggybacks
   on `.field`'s existing spacing/label styling so a fieldset+legend reads
   identically to a plain label+input pair elsewhere in the same form. */
fieldset.field { border: none; padding: 0; margin: 0 0 var(--sp-3); }
fieldset.field legend { font: 600 12px var(--font-display); text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); padding: 0; margin-bottom: 10px; }
.field-hint { color: var(--muted); font-size: 0.85rem; margin: -6px 0 12px; }
.option-group { display: flex; flex-direction: column; gap: 10px; }
.option {
  display: flex; align-items: flex-start; gap: 10px; padding: 13px 15px;
  border: 1px solid var(--line); background: var(--bg); cursor: pointer;
  font-size: 0.95rem; color: var(--ink); transition: border-color .15s ease;
}
.option:hover { border-color: var(--oxblood); }
.option input { margin-top: 3px; accent-color: var(--oxblood); flex: none; }
.option span { display: block; }
.option small { display: block; color: var(--muted); font-size: 0.82rem; margin-top: 3px; font-weight: 400; }

/* ---------- Footer ----------
   Structure (3-column grid + bottom bar) kept from the existing
   architecture; flavor 26's own footer is a single mockup line and
   doesn't specify a real footer, but IS dark ink with warm cream text —
   ported directly as the base for this dark section (the only
   deliberately dark section in the whole theme). */
.footer { background: var(--ink); color: #C9B896; font-size: 14px; border-top: 2px solid var(--gold); padding: var(--sp-5) 0 var(--sp-3); }
.footer__grid { display: grid; grid-template-columns: 1.4fr 1fr 1fr; gap: var(--sp-4); }
.footer h4 { font-family: var(--font-display); text-transform: uppercase; letter-spacing: 1.5px; font-size: 0.78rem; color: var(--gold); margin: 0 0 var(--sp-2); }
.footer ul { list-style: none; margin: 0; padding: 0; color: inherit; }
.footer ul li { margin-bottom: 0.55rem; }
.footer a { color: #C9B896; }
.footer a:hover { color: var(--gold); }
.footer__logo { margin-bottom: var(--sp-2); }
/* Reuses the street-sign wordmark plate for brand consistency at the
   foot of the page. The plate carries its own cream background + dark
   keyline regardless of what sits behind it, so it reads fine dropped
   onto the dark footer, same as a real enamel sign would. */
.footer__logo .nav__logo { display: inline-block; }
.footer__bottom { margin-top: var(--sp-4); padding-top: var(--sp-3); border-top: 1px solid rgba(201,184,150,0.25); display: flex; justify-content: space-between; gap: var(--sp-2); flex-wrap: wrap; color: #C9B896; font-size: 0.85rem; }
@media (max-width: 720px) { .footer__grid { grid-template-columns: 1fr; gap: var(--sp-3); } }

/* ---------- Funnel / lead-magnet variant (releaseguide.html) ---------- */
.page-funnel .nav__links, .page-funnel .nav__cta { display: none; }
.page-funnel .footer__grid { grid-template-columns: 1fr; text-align: center; }
.page-funnel .footer__grid > div:not(:first-child) { display: none; }
.lm { display: grid; grid-template-columns: 0.9fr 1.1fr; gap: var(--sp-5); align-items: center; }
@media (max-width: 900px) { .lm { grid-template-columns: 1fr; gap: var(--sp-4); } }
/* Same slant treatment as the hero frame, extended to the lead-magnet
   cover — flavor 26's own mockup doesn't include this page, so this is
   an extrapolation of its motif rather than a literal port. */
.lm__cover img { clip-path: polygon(6% 0, 100% 0, 94% 100%, 0 100%); }
.lm .stat { font-family: var(--font-display); font-weight: 700; font-size: clamp(2.2rem,4vw,3rem); color: var(--oxblood); line-height: 1; }
.warn { border-left: 3px solid var(--gold); padding: 0.4rem 0 0.4rem 1rem; color: var(--muted); font-size: 0.95rem; }

/* ---------- FAQ accordion (new, faq.html) ----------
   Native <details>/<summary> for free keyboard + screen-reader support,
   no JS needed. Styled to match the deco system: Oswald caps summary,
   oxblood +/- indicator, hairline dividers instead of cards (a page of
   8 bordered cards stacked vertically was too heavy — this reads lighter
   while still using the existing --line/--muted tokens, not new colour). */
.faq-list { max-width: 760px; margin: 0 auto; }
.faq-item { border-bottom: 1px solid var(--line); padding: var(--sp-3) 0; }
.faq-item:first-child { border-top: 1px solid var(--line); }
.faq-item summary {
  cursor: pointer; list-style: none; display: flex; align-items: center;
  justify-content: space-between; gap: var(--sp-2);
  font-family: var(--font-display); font-weight: 600; font-size: 1.05rem;
  letter-spacing: 0.01em; color: var(--ink);
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::after {
  content: '+'; flex: none; font-size: 1.4rem; line-height: 1; color: var(--oxblood);
}
.faq-item[open] summary::after { content: '\2212'; } /* minus sign */
.faq-item summary:hover { color: var(--oxblood); }
.faq-item .faq-item__answer { margin-top: var(--sp-2); color: var(--muted); }
.faq-item .faq-item__answer p:last-child { margin-bottom: 0; }

/* ---------- Misc ---------- */
.stack > * + * { margin-top: var(--sp-2); }
/* De-skewed repeating-stripe divider — the in-section separator inside
   "Why it works", between the 3-card grid and the 4-point grid. Not a
   page break, so unlike .rush it can't just be dropped: without it the
   4-point grid runs straight into the block above it. */
.divider { height: 3px; background: repeating-linear-gradient(90deg, var(--oxblood) 0 18px, var(--gold) 18px 22px); margin: var(--sp-4) 0; }
.breadcrumb { color: var(--muted); font-size: 0.9rem; margin-bottom: var(--sp-2); }
.breadcrumb a:hover { color: var(--oxblood); }
.pill { display: inline-block; margin-top: 12px; font: 600 13px var(--font-display); text-transform: uppercase; letter-spacing: 0.06em; color: var(--teal); text-decoration: none; border-bottom: 2px solid var(--teal); padding-bottom: 2px; }

/* --- Contact form: honeypot + submit status -------------------------------
   .hp is the bot trap. Positioned off-screen rather than display:none, because
   some bots specifically skip display:none fields; this stays "visible" to them
   and invisible to people. aria-hidden + tabindex=-1 keep it away from screen
   readers and keyboard users, so it costs nobody anything.
   .form-status is announced politely via aria-live so a screen-reader user hears
   the result instead of the page silently changing. */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.form-status { margin: var(--sp-2) 0 0; font-size: 0.95rem; min-height: 1.2em; }
.form-status--ok { color: var(--accent-2, #14564b); font-weight: 600; }
.form-status--err { color: var(--accent); font-weight: 600; }

/* ---------- Genie overlays (2026-09-05) ----------
   Three animated genies, placed by Max. Transparent VP9 WebM with an optimised
   GIF fallback, because Safari plays WebM but ignores its alpha and would show
   a black box. The swap is done in (C) genie-fallback.js, not in markup.

   EVERYTHING HERE IS IN PERCENT, NOT PIXELS, AND THAT IS THE POINT.
   The first version used fixed px widths and offsets, then hid two of the three
   under 640px because at full size they covered the copy. Max, correctly: they
   should behave like any normal image and just scale with the layout. So width
   and offsets are percentages of the card, which means the genie keeps exactly
   the same relationship to its box at every screen size, phone included.
   Nothing is hidden and nothing is resized by breakpoint.

   `bottom: 100%` anchors a genie to the TOP EDGE of its card whatever the card's
   height, and a negative margin-bottom (also a %, so it scales) drops it back
   down to straddle the border line.

   Decorative only: aria-hidden, and pointer-events:none so they never swallow a
   click on the card underneath. .has-genie must not clip them. */
.has-genie { position: relative; overflow: visible; }
.genie { position: absolute; pointer-events: none; z-index: 3; height: auto; }

/* POSITIONS LOCKED IN 2026-09-05, placed by Max with the preview drag tool and
   read straight off the live page, not estimated.

   All three use the same anchoring on purpose:
     left           percentage of the card's WIDTH
     width          percentage of the card's WIDTH
     bottom: 100%   pins the genie's bottom edge to the card's TOP edge
     margin-bottom  negative percent drops it back down over the border line,
                    and margin percentages also resolve against WIDTH

   That last point is the whole reason this arrangement is used rather than
   `top`. A percentage `top` resolves against the card's HEIGHT, which changes
   whenever the copy reflows, so a genie placed perfectly at one width would
   drift at another. Everything here keys off width alone, so each genie holds
   its exact relationship to its box at any screen size, phone included. */

/* Lying on the top edge of the "tough out there" card, right-hand side */
.genie--pointing { width: 22%; left: 78%; bottom: 100%; margin-bottom: 0.1%; }

/* Sitting on the top edge of the Ceyeo card */
.genie--mixing { width: 34.7%; left: 64.9%; bottom: 100%; margin-bottom: -0.2%; }

/* In front of step 4's medallion.
   ANCHORED TO .step__lead, NOT THE CARD. It was pinned to the whole step card,
   which meant that as the card narrowed the genie drifted away from the number
   it belongs to. .step__lead is now the positioned ancestor, so every value
   below is a percentage of the NUMBER-AND-TITLE column (~310px) and the genie
   keeps its exact relationship to the "4" at any width.
   Vertical is `top: 0` plus a translateY, NOT a percentage `top`. A percentage
   `top` resolves against the container's HEIGHT, and .step__lead's height is set
   by its text, so it would shift whenever the title wrapped. translateY resolves
   against the GENIE'S OWN height, and since the genie is square and its width is
   a percentage of the column, its height scales with the column too. So the
   offset stays proportional. (Written wrong the first time, with top: -12.6%
   measured against width, which put the genie 19px too low.) */
.genie--sign4 { width: 49.1%; left: -13.7%; top: 0; bottom: auto; margin-bottom: 0; transform: translateY(-26.7%); }


/* ---------- Mitzi ----------
   Lying along the line where the "How We Work" band starts, above the heading.
   Anchored to the SECTION rather than the .wrap, because that colour change IS
   the line she is lying on. Percentages of viewport width, same reasoning as the
   genies: she scales with the layout instead of being resized at breakpoints. */
/* Both Mitzi and her label hang off the DIVIDING LINE, which is this section's
   own top edge, so `top: 0` is that line and margin-top lifts them above it.
   margin-top is used rather than translateY because a margin percentage
   resolves against the containing block's WIDTH. translateY resolves against
   the element's OWN height, which is fine for a square cat but wrong for a line
   of text: the label is only 25px tall, so its offset came out as -344%, and any
   change to the font size or a wrap would have thrown it across the page.
   Both now key off section width, so they hold their position on the line at
   any screen size. */
/* MITZI IS A FIXED SIZE, deliberately different from the genies.
   She was width:21%, so she shrank as the window narrowed and Max wanted her
   constant. min() holds her at 405px on any normal screen and only lets her
   shrink when the viewport is genuinely too small to fit her.
   Her VERTICAL now uses translateY, not margin-top. That is the opposite of the
   label below, and for a good reason: translateY is a percentage of the
   element's own height, so for a fixed-aspect image it keeps her the same
   distance above the line whatever size she ends up. A margin-top percentage
   would resolve against section width, which no longer has anything to do with
   how big she is, so she would drift off the line as the window changed. */
.genie--mitzi { width: min(405px, 40vw); left: 58.4%; top: 0; margin-top: 0; bottom: auto; margin-bottom: 0; transform: translateY(-46.5%); }

/* ---------- Mitzi's label ----------
   A scrawled note with an arrow pointing at the cat. Caveat is loaded from the
   same Bunny font request the rest of the site already uses, so this costs one
   extra family on an existing connection rather than a new one.
   Arrow removed 5 Sep at Max's request: the scrawled note alone reads better.
   Sized in percent like everything else here so it keeps its relationship to
   Mitzi at any width. Decorative: aria-hidden and pointer-events none. */
.genie--mitzi-label {
  /* NO fixed width. It was 15%, which made a box wider than the words and left
     dead space to the right of them that could overlap whatever sits nearby.
     width:auto + nowrap means the box is exactly the text, nothing more.
     It still scales, because the font-size below is a vw-based clamp. */
  width: auto; white-space: nowrap;
  left: 71.4%; top: 0; margin-top: -87px; transform: none;
}
.genie--mitzi-label .scrawl {
  display: block;
  font-family: 'Caveat', 'Bradley Hand', cursive;
  font-size: 20px;   /* fixed, so the note stays with Mitzi instead of scaling away from her */
  line-height: 1.15;
  color: var(--oxblood);
  transform: rotate(-4deg);
  margin-bottom: 0;
}

/* ---------- Turnstile ----------
   Sits directly above the submit button on both forms. Collapses to nothing
   when no site key is configured, so an unconfigured build shows no empty gap. */
.cf-turnstile { margin: 0 0 14px; min-height: 0; }
.cf-turnstile:empty { display: none; margin: 0; }
/* DO NOT try to collapse this box when the widget is invisible.
   This site key runs Turnstile in invisible/managed mode: the script injects a
   wrapper and the hidden response input, renders no visible widget, and leaves a
   ~74px blank gap above the submit button. It looks like a form that failed to
   load, and an audit flagged it on 2026-09-07.

   BOTH obvious fixes were tried against the live preview and BOTH broke the
   forms outright:
     .cf-turnstile:not(:has(iframe)) { display: none }            -> no token
     .cf-turnstile:not(:has(iframe)) { height: 0; overflow: hidden } -> no token
   Turnstile will not execute unless its container is genuinely rendered and
   sized. With no token the server, which fails closed by design, rejects every
   submission. Verified in a real browser both times: token length 0.

   The gap is therefore DELIBERATE and is the safe state. If it is ever worth
   removing, the fix is on the Cloudflare side (switch the widget mode) or by
   moving to an explicit-render callback, not in CSS. */

/* ============================================================
   BLOG. Index listing + long-form post body.

   Deliberately reuses the existing deco tokens rather than inventing a
   second visual language: square corners (--radius is 0 by design), the
   4px hard offset shadow, Oswald for headings, Jost for body.

   The one place this departs from the rest of the site is measure. Landing
   pages are wide, multi-column and scannable; a 900-word article read at
   1180px is a wall. .prose clamps to ~68 characters, which is the range
   long-form text is actually comfortable to read at.
   ============================================================ */

/* ---------- Post body ---------- */
.prose {
  max-width: 68ch;
  margin: 0 auto;
  font-size: 1.125rem;   /* body is 18px; articles get a touch more */
  line-height: 1.75;
  color: var(--ink);
}
.prose > * + * { margin-top: 1.4em; }
.prose h2 {
  font-family: var(--font-display);
  font-size: clamp(1.7rem, 3vw, 2.3rem);
  line-height: 1.15;
  text-transform: uppercase;
  letter-spacing: 0.01em;
  margin-top: 2.2em;
}
.prose h3 {
  font-family: var(--font-display);
  font-size: clamp(1.25rem, 2vw, 1.55rem);
  line-height: 1.25;
  margin-top: 1.8em;
}
.prose ul, .prose ol { padding-left: 1.3em; }
.prose li + li { margin-top: 0.5em; }
.prose a { color: var(--oxblood); text-underline-offset: 3px; }
.prose strong { font-weight: 600; }
.prose blockquote {
  border-left: 4px solid var(--oxblood);
  padding: 0.2em 0 0.2em 1.2em;
  margin-left: 0;
  font-style: italic;
  color: var(--muted);
}
.prose code {
  background: var(--card);
  border: 1px solid var(--line);
  padding: 0.1em 0.35em;
  font-size: 0.9em;
}
/* Copyable multi-line blocks (the AI-prompt post). white-space: pre-wrap +
   word-break keep this from causing horizontal overflow on narrow phones,
   since the point is the reader can select-all and copy it cleanly, not
   that it scrolls sideways. */
.prose pre {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: var(--sp-3);
  overflow-x: auto;
  white-space: pre-wrap;
  word-break: break-word;
  font-size: 0.85rem;
  line-height: 1.6;
}
.prose pre code {
  background: none;
  border: none;
  padding: 0;
  font-size: 1em;
}
.prose hr { border: 0; border-top: 1px solid var(--line); margin: 2.4em 0; }
/* Tables in posts (the stems checklist uses one) need to survive phones. */
.prose .table-wrap { overflow-x: auto; }
.prose table { width: 100%; border-collapse: collapse; font-size: 0.95rem; }
.prose th, .prose td { border: 1px solid var(--line); padding: 0.6em 0.8em; text-align: left; }
.prose th { background: var(--card); font-family: var(--font-display); text-transform: uppercase; letter-spacing: 0.02em; }

/* ---------- Legal draft placeholders (privacy-policy.md, terms.md) ----------
   The [MAX: ...] markers in the source drafts are open legal/business
   decisions, not policy text, and a reader must never mistake one for an
   actual statement of policy. So they get their own boxed, labelled
   treatment instead of sitting as italics inside a paragraph.

   .draft-summary is the one-per-page "how many decisions are open" box at
   the top — --gold border, since it is a status summary rather than an
   individual open item. .needs-max is the per-placeholder block used for
   anything long enough to read as its own paragraph. .needs-max-inline is
   for the handful of placeholders that sit mid-sentence in the source
   (the publish date, "the contact address") — turning those into a full
   block would break the sentence they live in, so they get a bordered,
   labelled inline span instead. Both use --oxblood, the site's primary
   accent, so every open decision reads as urgent/unresolved rather than
   decorative. */
.draft-summary {
  border: 2px solid var(--gold);
  background: rgba(190, 143, 44, 0.1);
  padding: var(--sp-3);
  max-width: 68ch;
  margin: 0 auto var(--sp-4);
}
.draft-summary__count {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.05rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--oxblood);
  margin: 0 0 0.8em;
}
.draft-summary p, .draft-summary li { color: var(--ink); }
.draft-summary > * + * { margin-top: 0.8em; }
.draft-summary ol, .draft-summary ul { padding-left: 1.3em; }

.needs-max {
  border: 2px solid var(--oxblood);
  background: rgba(158, 58, 43, 0.07);
  padding: var(--sp-2) var(--sp-3);
  margin: 1.4em 0;
}
.needs-max__label {
  display: inline-block;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.75rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--card);
  background: var(--oxblood);
  padding: 2px 8px;
  margin: 0 0 0.6em;
}
.needs-max p, .needs-max ul, .needs-max ol { margin: 0.6em 0 0; color: var(--ink); }
.needs-max p:first-of-type { margin-top: 0; }
.needs-max ul, .needs-max ol { padding-left: 1.3em; }

.needs-max-inline {
  display: inline;
  border: 1.5px solid var(--oxblood);
  background: rgba(158, 58, 43, 0.07);
  padding: 0.05em 0.5em;
  margin: 0 0.15em;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
}
.needs-max-inline__label {
  display: inline-block;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.65rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--card);
  background: var(--oxblood);
  padding: 1px 5px;
  margin-right: 0.35em;
}

/* ---------- Post header ---------- */
.post__head { max-width: 68ch; margin: 0 auto var(--sp-4); }
.post__meta {
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: 0.85rem;
  color: var(--muted);
}
.post__meta > * + *::before { content: "  ·  "; }

/* ---------- Index listing ---------- */
.postlist { display: grid; gap: var(--sp-3); max-width: 860px; margin: 0 auto; }
.postcard {
  display: block;
  background: var(--card);
  border: 1px solid var(--line);
  box-shadow: var(--shadow);
  padding: var(--sp-3);
  color: inherit;
  text-decoration: none;
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}
.postcard:hover {
  transform: translate(-2px, -2px);
  box-shadow: 6px 6px 0 rgba(42, 32, 22, 0.22);
}
.postcard h2 {
  font-family: var(--font-display);
  font-size: clamp(1.3rem, 2.4vw, 1.75rem);
  line-height: 1.15;
  text-transform: uppercase;
  margin: 0.35em 0 0.3em;
}
.postcard p { margin: 0; color: var(--muted); }

@media (max-width: 620px) {
  .prose { font-size: 1.05rem; }
}


/* ---------- Lead-magnet cover, built not sourced ----------
   Replaces a borrowed PNG (see the comment in src/pages/releaseguide.md).
   Deliberately CSS rather than an image: no asset to commission, nothing that
   goes stale when the guide is retitled, sharp on any screen, and it inherits
   the deco tokens so it cannot drift from the rest of the site. Reuses the nav
   wordmark's band/name construction so the brand reads identically. */
.guidecover {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 0.55rem; aspect-ratio: 4 / 5; padding: 12% 10%;
  background: var(--oxblood);
  border-left: 10px solid rgba(0, 0, 0, 0.22);   /* the spine */
  box-shadow: var(--shadow);
  clip-path: polygon(6% 0, 100% 0, 94% 100%, 0 100%);  /* same slant as .lm__cover img */
  text-align: center;
}
.guidecover__band {
  display: flex; justify-content: space-between; gap: 14px; align-self: stretch;
  max-width: 190px; margin: 0 auto;
  background: var(--card); color: var(--oxblood);
  padding: 4px 7px 3px;
  font: 600 9px/1 var(--font-display); letter-spacing: 0.18em; text-transform: uppercase;
}
.guidecover__brand {
  font-family: var(--font-display); font-weight: 700; text-transform: uppercase;
  font-size: clamp(1.3rem, 3.4vw, 2rem); line-height: 1; color: var(--card);
}
/* --gold (#BE8F2C) on --oxblood is 2.31:1, which fails even the relaxed
   large-text threshold. This lighter gold measures 4.54:1 on the same
   background, clearing the strict 4.5 requirement while still reading as gold
   rather than cream. Used only on the oxblood cover; --gold is fine everywhere
   else it appears, which is on sand. Found by the WCAG audit 2026-09-05. */
.guidecover__brand em { font-style: normal; color: #F0D080; }
.guidecover__rule { width: 46px; height: 3px; background: #F0D080; }
.guidecover__title {
  font-family: var(--font-display); font-weight: 700; text-transform: uppercase;
  font-size: clamp(1.9rem, 5.4vw, 3.1rem); line-height: 0.95; color: var(--card);
}
.guidecover__sub {
  font-family: var(--font-sans); font-size: clamp(0.75rem, 1.5vw, 0.9rem);
  letter-spacing: 0.04em; color: rgba(251, 244, 227, 0.78);
}


/* ---------- Mitzi's label on narrow screens ----------
   Found by the 360px responsive audit (2026-09-05): the About page scrolled
   sideways by 27px and this was the whole cause. The label is `white-space:
   nowrap` anchored at `left: 71.4%`, so on a phone the text starts near the
   right edge and simply keeps going past it. A percentage left-edge plus
   unwrappable text cannot be safe at every width, because the text does not
   shrink with the container.
   Anchoring to the RIGHT instead fixes it by construction rather than by
   picking a new percentage that happens to fit today: the box grows leftward
   into the page, so it can never push the document wider. */
@media (max-width: 620px) {
  .genie--mitzi-label { left: auto; right: 3%; margin-top: -72px; }
  .genie--mitzi-label .scrawl { font-size: 16px; }
}

/* ---------- Embedded video (guide thank-you pages) ----------
   aspect-ratio rather than the old padding-bottom hack: it is supported
   everywhere the rest of this stylesheet already relies on (the site uses
   aspect-ratio on .tile too), and it keeps the iframe from overflowing on a
   phone, which is the failure the 360px audit exists to catch. */
.videoframe {
  position: relative;
  aspect-ratio: 16 / 9;
  width: 100%;
  border: 1px solid var(--line);
  box-shadow: var(--shadow);
  background: var(--ink);
}
.videoframe iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }


/* ---------- Keep the footer at the bottom on short pages ----------
   Added 2026-09-05. Without this, a page shorter than the viewport leaves the
   footer floating mid-screen with page background below it, which reads as a
   half-loaded page. It was always true of 404, call-booked and app-completed;
   the two guide thank-you pages made it obvious.
   Flex on body rather than a min-height on main, because main's height is not
   knowable in advance. `margin-top: auto` on the last child does the work. */
body { min-height: 100vh; display: flex; flex-direction: column; }
body > main { flex: 1 0 auto; }
body > .site-footer, body > footer { margin-top: auto; }

/* ---------- Footer text contrast ----------
   Found by a WCAG audit, 2026-09-05. The footer sets `color: #C9B896`, which is
   readable on the dark ink background. But `.lead` and the tagline set
   `var(--muted)` directly ON the element, and a class on the element beats a
   colour inherited from an ancestor. So the footer's two pieces of secondary
   text rendered at 2.21:1 against a 4.5:1 requirement: legible if your eyes are
   good and the screen is bright, and not otherwise.
   --muted (#655537) is a light-background colour. On ink it is nearly invisible.
   These are the only two contrast failures on the whole site; the oxblood-on-sand
   body text everyone worried about passes comfortably. */
.footer .lead,
.footer p,
.footer__bottom { color: #C9B896; }

/* ---------- Playable project tiles ----------
   The tiles were static images. Max asked (2026-08) for them to play the songs;
   this is that. Built as a <button> rather than a div with a click handler, so
   it is keyboard-reachable and screen-reader-announced for free.
   Audio mapping was recovered from the OLD site's markup, where each <audio>
   sits immediately before its artist label. Not guessed. */
.tile--play {
  position: relative;
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}
.tile--play img { display: block; width: 100%; aspect-ratio: 1 / 1; object-fit: cover; }

/* Play badge. Sits over the artwork, becomes a pause bar while playing. */
.tile__play {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: 54px; height: 54px; border-radius: 50%;
  background: rgba(42, 32, 22, 0.72);
  border: 2px solid var(--card);
  transition: background 0.15s ease, transform 0.15s ease, opacity 0.15s ease;
}
/* Triangle, drawn in CSS so there is no icon asset to load or go missing. */
.tile__play::before {
  content: ""; position: absolute; top: 50%; left: 54%; transform: translate(-50%, -50%);
  border-style: solid; border-width: 11px 0 11px 17px;
  border-color: transparent transparent transparent var(--card);
}
.tile--play:hover .tile__play { background: var(--oxblood); transform: translate(-50%, -50%) scale(1.06); }
.tile--play:focus-visible { outline: 3px solid var(--oxblood); outline-offset: 3px; }

/* Playing: swap the triangle for a pause bar pair. */
.tile--play.is-playing .tile__play { background: var(--oxblood); }
.tile--play.is-playing .tile__play::before {
  border: 0; width: 5px; height: 20px; left: 38%;
  background: var(--card); box-shadow: 9px 0 0 var(--card);
}
.tile--play.is-loading .tile__play { opacity: 0.55; }

/* Progress, along the bottom edge of the artwork. */
.tile__bar {
  position: absolute; left: 0; right: 0; bottom: 0; height: 4px;
  background: rgba(42, 32, 22, 0.25); opacity: 0; transition: opacity 0.15s ease;
}
.tile--play.is-playing .tile__bar, .tile--play.is-loading .tile__bar { opacity: 1; }
.tile__bar i { display: block; height: 100%; width: 0; background: var(--oxblood); }

@media (prefers-reduced-motion: reduce) {
  .tile--play:hover .tile__play { transform: translate(-50%, -50%); }
}

/* ---------- Cal.com booking embed ----------
   min-height stops the page collapsing to nothing while the iframe loads, which
   otherwise looks like a broken page for the second or two before it appears. */
.cal-embed {
  max-width: 900px;
  margin: var(--sp-4) auto 0;
  min-height: 620px;
  border: 1px solid var(--line);
  background: var(--card);
  box-shadow: var(--shadow);
  overflow: hidden;
}
.cal-embed iframe { width: 100%; border: 0; display: block; }
@media (max-width: 620px) { .cal-embed { min-height: 560px; } }
/* Calendar failed or unconfigured: drop the reserved height and the framing, so
   the honest message stands alone instead of sitting in a big empty panel. */
.cal-embed--fallback {
  min-height: 0;
  border: 0;
  background: none;
  box-shadow: none;
  padding: var(--sp-2) 0;
}

/* ---------- Marketing consent checkbox ----------
   Deliberately understated. It is optional and must never look like a required
   field or a dark pattern; the tick has to be worth giving, not tricked out of
   someone. Generous hit area because a fiddly checkbox on a phone is its own
   kind of coercion. */
.field--consent { margin: var(--sp-2) 0; }
.consent {
  display: flex; align-items: flex-start; gap: 10px;
  cursor: pointer; font-size: 0.92rem; line-height: 1.45; color: var(--muted);
  padding: 6px 0;
}
.consent input[type="checkbox"] { margin-top: 3px; width: 18px; height: 18px; flex: 0 0 auto; accent-color: var(--oxblood); cursor: pointer; }
.consent span { flex: 1; }

/* ---------- Genies on the main-offer page ----------
DELIBERATELY SEPARATE CLASSES from the homepage and About versions
(.genie--pointing, .genie--mixing, .genie--mitzi). Those are positioned to the
pixel against sections on their own pages. Reusing them here would mean dragging
a genie on this page silently moved it on the homepage too. Same images,
independent positions.

POSITIONS LOCKED IN 2026-09-06, converted from Max's drags on the live preview.

Each genie is anchored to the BLOCK IT SITS ABOVE rather than to its section,
using the same `bottom: 100%` idiom as the homepage genies: that pins the
genie's bottom edge to the block's top edge, so it holds its position however
the copy reflows. Anchoring to a section does NOT survive reflow, because
`margin-top`/`margin-bottom` percentages resolve against the containing block's
WIDTH. On a narrow screen the offset collapses to a fraction of itself while the
content it was aligned to grows much taller. That trap has caused three separate
bugs in this project; the same note is on the homepage rules above.

Positive margin-bottom moves a genie UP off the block's top edge, negative pulls
it DOWN over the block.

The drag tool reports positions relative to whatever element the genie is
currently inside, and writes them as `top: 0; margin-top: <% of width>`. If these
get dragged again, its output will be relative to the parents these now live in
(.stack, .grid--3, .card), NOT the sections.

Widths are percentages of a block inside .wrap, which is capped at 1180px, so
they cannot scale unbounded on a very wide monitor. That is why there is no
min() clamp here, unlike .genie--mitzi on the homepage which is section-relative. */
/* Mitzy's GIF carries ~150px of TRANSPARENT padding to the right of the drawn
   cat. Nothing is visible there, but the element box still counts toward
   scrollWidth, so with her draped over the card corner the page reported a
   horizontal overflow at every viewport below ~1530px: 20px at 1440, 100px at
   1280, 77px at 360. Measured on the rendered pixels, the drawn cat itself fits
   at every width (rightmost cat pixel is 363px at a 360px viewport), so this
   clips empty space only, and only on the x axis.
   Applied to the section rather than the card because the card must keep
   overflow visible or the drape is cut off.

   NOT a user-visible fix: body already sets overflow-x: hidden, so nothing ever
   scrolled sideways (verified, canScrollRight false at every width). This keeps
   the 360px overflow check honest, so a REAL clipped-content bug on this page
   is not lost in a permanent warning about empty space. */
.clip-x { overflow-x: clip; }

.genie--mo-mixing   { width: 27.6%; left: 72.3%; top: auto; bottom: 100%; margin-top: 0; margin-bottom: 0.3%; transform: none; }
.genie--mo-pointing { width: 26.2%; left: 0.2%;  top: auto; bottom: 100%; margin-top: 0; margin-bottom: 0.2%; transform: none; }

/* RE-DRAGGED 2026-09-09, both cat and label together, via the drag tool's
   newer readout format (top: 0 + margin-top%, not the old bottom: 100% +
   margin-bottom% scheme the first drag used) — that's the fix from
   2026-09-05 noted in genie-position-tool.js, so this is what the tool
   outputs now regardless of anchor style used previously. Locked verbatim
   from Max's copied CSS, no manual nudging this time. */
.genie--mo-mitzi { width: 81.2%; left: 44.8%; top: 0; margin-top: -19.5%; bottom: auto; margin-bottom: 0; transform: none; }

.genie--mo-mitzi-label { width: 24%; left: 77%; top: 0; margin-top: -18.8%; bottom: auto; margin-bottom: 0; transform: none; white-space: nowrap; }
.genie--mo-mitzi-label .scrawl {
  display: block;
  font-family: 'Caveat', 'Bradley Hand', cursive;
  font-size: 20px; line-height: 1.15;
  color: var(--oxblood);
  transform: rotate(-4deg);
}
/* The same fix the About page needed: nowrap text at a percentage left-edge will
   run off a narrow screen, because the text does not shrink with the container. */
@media (max-width: 620px) {
  .genie--mo-mitzi-label { left: 4%; margin-top: -30px; }
  .genie--mo-mitzi-label .scrawl { font-size: 16px; }
}

/* ---------- Genies on the AI remake page ----------
   Added 2026-09-08 for Max to drag into place with the preview position tool.
   DELIBERATELY SEPARATE "ar-" CLASSES, same reasoning as every other page's
   genies: dragging one here must not silently move it on main-offer or the
   homepage. Same anchoring convention as the mo- genies above (bottom: 100% +
   a percentage margin-bottom, both resolved against WIDTH, so position holds
   at any screen size without drifting on reflow).

   POSITIONS LOCKED IN 2026-09-08, placed by Max with the preview drag tool
   and read straight off the live page.

   RE-HOMED 2026-09-09: mixing, pointing and sign4 each moved to be a child
   of the specific card they visually sit on (see ai-remake.md's comments at
   each one), instead of a wide section/grid/stack that merely happened to
   put them in the right place at one screen width. Values below are the
   SAME on-screen position as before, converted to top:0 + margin-top% now
   that the host is different for two of the three (pointing's new host,
   step 1's card, is coincidentally the same 1132px width as its old host,
   so its left/width are unchanged — only margin-top moved). Re-measured
   directly from the live page rather than re-derived by hand. */
.genie--ar-mixing   { width: 68.8%; left: 31.2%; top: 0; margin-top: -55.1%; bottom: auto; margin-bottom: 0; transform: none; }
.genie--ar-pointing { width: 30.6%; left: 69.5%; top: 0; margin-top: -14.6%; bottom: auto; margin-bottom: 0; transform: none; }
.genie--ar-sign4    { width: 10.8%; left: 2.9%;  top: 0; margin-top: -0.2%;  bottom: auto; margin-bottom: 0; transform: none; }

/* LOCKED IN 2026-09-08, re-dragged by Max after the caching fix so this is
   measured against the real ~60%-wide cat, not the stale oversized one. */
.genie--ar-mitzi    { width: 64.8%; left: 54.3%; top: auto; bottom: 100%; margin-top: 0; margin-bottom: -17.4%; transform: none; }

/* LOCKED IN 2026-09-08, positioned by Max alongside the cat above. */
.genie--ar-mitzi-label {
  width: auto; white-space: nowrap;
  left: 46.4%; top: 0; bottom: auto; margin-top: -66px; margin-bottom: 0; transform: none;
}
.genie--ar-mitzi-label .scrawl {
  display: block;
  font-family: 'Caveat', 'Bradley Hand', cursive;
  font-size: 20px; line-height: 1.15;
  color: var(--oxblood);
  transform: rotate(-4deg);
}
@media (max-width: 620px) {
  .genie--ar-mitzi-label { left: 4%; margin-top: -30px; }
  .genie--ar-mitzi-label .scrawl { font-size: 16px; }
}

/* PHONE SIZING. Below 640px the layout is a single ~312px column, and a genie
   sized as a percentage of that shrinks to about 85px, which reads as clip-art
   rather than a character. Max asked for them to stay a decent size (2026-09-06),
   so on phones they get a fixed px width instead.

   The catch: each genie is anchored to the TOP EDGE of the block below it, so
   making one bigger makes it grow UPWARDS into the heading above, and that gap
   is only 40px on a phone. .genie-room adds the missing height to the block, so
   the genie has somewhere to sit instead of landing on the copy. Heights come
   from the real assets: genie-mixing.gif is 380x300 (1.27), genie-pointing.gif
   is 380x180 (2.11), so 150px wide makes them 118px and 71px tall.

   Mitzy is deliberately NOT resized here: she is a percentage of the callout
   card, which stays wide on a phone, so she is already ~254px. */
@media (max-width: 640px) {
  .genie--mo-mixing   { width: 150px; left: auto; right: 0; }
  .genie--mo-pointing { width: 150px; left: 0; }
  /* Mitzy keeps her size on phones but is anchored from the card's RIGHT edge
     instead of the left. A fixed left% cannot work across 320-640px because the
     wrap's 24px padding is constant while the column is not, so the tail crept
     past the screen edge and clip-x cut it (measured: ink reached x=319 in a
     320px viewport). Her drawn cat occupies 1.2%-71.1% of the GIF, the rest is
     transparent, which is why the number below looks like it overhangs by more
     than it does. */
  .genie--mo-mitzi    { left: auto; right: -26%; }
  .genie-room--tall   { margin-top: 104px; }  /* 118px genie, ~26px clearance */
  .genie-room--short  { margin-top: 58px; }   /* 71px genie, ~27px clearance */

  /* AI-remake page genies, same fixed-px-on-phone treatment. genie-sign-4.webp
     is SQUARE (320x320, unlike the other two), so 150px wide is also 150px
     tall, taller than either existing room variant, hence the new --square
     modifier below rather than reusing --tall. */
  .genie--ar-mixing   { width: 150px; left: auto; right: 0; }
  .genie--ar-pointing { width: 150px; left: 0; }
  .genie--ar-sign4    { width: 150px; left: 0; }
  .genie--ar-mitzi    { left: auto; right: 0; }
  .genie-room--square { margin-top: 176px; }  /* 150px genie, ~26px clearance */
}
