/* REGISTER (direction pop) for sc05: palette blush-burgundy, type m-plus-rounded-1c over heebo, labels heebo, mark amatic-sc.
   Written by engine/directions.py; never edited by hand. */
:root {
  --ink: #1b0a0b;
  --ink-2: #211515;
  --ink-deep: #2a0007;
  --paper: #fdf5ef;
  --accent: #f56b76;
  --accent-deep: #b22c3f;
  --line: #9c9086;
  --shadow: #000000;
  --gr-light: #f8e8db;
  --gr-light-fg: #1b0a0b;
  --gr-light-accent: #b22c3f;
  --gr-light-pop: #bd3748;
  --gr-light-on: #fdf5ef;
  --gr-dark: #4c0f1a;
  --gr-dark-fg: #fdf5ef;
  --gr-dark-accent: #f56b76;
  --gr-dark-pop: #c74150;
  --gr-dark-on: #1b0a0b;
  --gr-vivid: #bd3748;
  --gr-vivid-fg: #fdf5ef;
  --gr-vivid-accent: #fdf5ef;
  --gr-vivid-pop: #fdf5ef;
  --gr-vivid-on: #1b0a0b;
  --gr-pale: #f9c5ca;
  --gr-pale-fg: #1b0a0b;
  --gr-pale-accent: #a11732;
  --gr-pale-pop: #bd3748;
  --gr-pale-on: #fdf5ef;
  --font-display: "M PLUS Rounded 1c", sans-serif;
  --font-mark: "Amatic SC", sans-serif;
  --font-body: "Heebo", Arial, sans-serif;
  --font-label: "Heebo", sans-serif;
  --ground: var(--gr-light);
  --ground-2: color-mix(in srgb, var(--gr-light) 94%, var(--gr-light-fg));
  --ground-deep: var(--ink-deep);
  --fg: var(--gr-light-fg);
  --fg-72: color-mix(in srgb, var(--gr-light-fg) 72%, transparent);
  --fg-45: color-mix(in srgb, var(--gr-light-fg) 45%, transparent);
  --alt: var(--gr-dark);
  --alt-fg: var(--gr-dark-fg);
  --brand: var(--gr-light-accent);
  --brand-alt: var(--gr-dark-accent);
  --on-brand: var(--gr-light-on);
  --pop: var(--gr-light-pop);
  --edge: var(--line);
  --on-media: var(--paper);
  --on-media-72: color-mix(in srgb, var(--paper) 72%, transparent);
  --brand-media: var(--gr-dark-accent);
  --grain: 0.035;
  --head-relief: 0;
  --head-measure: none;
  --head-size-max: 14rem;
  --head-stagger: 0;
  --head-weight: 900;
  --head-track: -0.015em;
  --key-weight: 900;
  --mark-weight: 700;
  --kicker-track: 0.02em;
  --label-weight: 700;
  --body-weight: 400;
  --brand-weight: 700;
  --kicker-weight: 700;
  --kicker-gap: 1.4rem;
  --manifest-weight: 900;
  --manifest-size: clamp(2.4rem, 7vw, 6rem);
  --manifest-measure: none;
  --section-pad-block: clamp(5.5rem, 16svh, 9rem);
  --cta-size: clamp(3.4rem, 12vw, 11rem);
  --btn-weight: 700;
  --radius-pill: 100px;
  --radius-mark: 0;
  --motion-ease: cubic-bezier(0.34, 1.56, 0.64, 1);
  --motion-dur: 0.6s;
  --motion-settle: 0.8s;
}
/* each section wears its own ground; the kit's roles follow it inside */
[data-ground="light"] {
  --ground: var(--gr-light); --ground-2: color-mix(in srgb, var(--gr-light) 94%, var(--gr-light-fg));
  --fg: var(--gr-light-fg); --fg-72: color-mix(in srgb, var(--gr-light-fg) 72%, transparent);
  --fg-45: color-mix(in srgb, var(--gr-light-fg) 45%, transparent);
  --alt: var(--gr-light); --alt-fg: var(--gr-light-fg);
  --brand: var(--gr-light-accent); --brand-alt: var(--gr-light-accent); --on-brand: var(--gr-light-on);
  --pop: var(--gr-light-pop); --edge: color-mix(in srgb, var(--gr-light-fg) 26%, var(--gr-light));
}

[data-ground="dark"] {
  --ground: var(--gr-dark); --ground-2: color-mix(in srgb, var(--gr-dark) 94%, var(--gr-dark-fg));
  --fg: var(--gr-dark-fg); --fg-72: color-mix(in srgb, var(--gr-dark-fg) 72%, transparent);
  --fg-45: color-mix(in srgb, var(--gr-dark-fg) 45%, transparent);
  --alt: var(--gr-dark); --alt-fg: var(--gr-dark-fg);
  --brand: var(--gr-dark-accent); --brand-alt: var(--gr-dark-accent); --on-brand: var(--gr-dark-on);
  --pop: var(--gr-dark-pop); --edge: color-mix(in srgb, var(--gr-dark-fg) 26%, var(--gr-dark));
}

[data-ground="vivid"] {
  --ground: var(--gr-vivid); --ground-2: color-mix(in srgb, var(--gr-vivid) 94%, var(--gr-vivid-fg));
  --fg: var(--gr-vivid-fg); --fg-72: color-mix(in srgb, var(--gr-vivid-fg) 72%, transparent);
  --fg-45: color-mix(in srgb, var(--gr-vivid-fg) 45%, transparent);
  --alt: var(--gr-vivid); --alt-fg: var(--gr-vivid-fg);
  --brand: var(--gr-vivid-accent); --brand-alt: var(--gr-vivid-accent); --on-brand: var(--gr-vivid-on);
  --pop: var(--gr-vivid-pop); --edge: color-mix(in srgb, var(--gr-vivid-fg) 26%, var(--gr-vivid));
}

[data-ground="pale"] {
  --ground: var(--gr-pale); --ground-2: color-mix(in srgb, var(--gr-pale) 94%, var(--gr-pale-fg));
  --fg: var(--gr-pale-fg); --fg-72: color-mix(in srgb, var(--gr-pale-fg) 72%, transparent);
  --fg-45: color-mix(in srgb, var(--gr-pale-fg) 45%, transparent);
  --alt: var(--gr-pale); --alt-fg: var(--gr-pale-fg);
  --brand: var(--gr-pale-accent); --brand-alt: var(--gr-pale-accent); --on-brand: var(--gr-pale-on);
  --pop: var(--gr-pale-pop); --edge: color-mix(in srgb, var(--gr-pale-fg) 26%, var(--gr-pale));
}

section[data-ground] { background-color: var(--ground); color: var(--fg); }

/* THE KIT — the chrome every wow-demo shares: grain, progress, header, brand,
   buttons, the manifest and CTA sections, the footer, the scroll hint.

   ~60% of each original template was exactly this, copy-pasted, which is why a
   third vertical used to mean a third copy.

   IT NAMES NO PALETTE TOKEN. Not one rule here mentions --ink or --paper. It
   speaks only in SEMANTIC roles — --ground, --fg, --alt, --brand — and the
   register decides which palette colour plays each part. That indirection is
   the whole reason a light register is possible at all: on a dark register
   --ground is the ink and --alt is the paper; on a light one they swap, and not
   a line of this file changes. Registers own the mapping; the kit owns the page.

   Final states are the CSS defaults: with no JS the page is complete. */

/* ── MOTION TOKENS ──
   The kit owns these, not a register. A register holds colour, typography and
   rhythm (_ROLES.md); how fast a thing answers a finger is neither — it is the
   same everywhere, because the hand is the same everywhere. Two durations,
   because a touch layer has exactly two speeds: the ACKNOWLEDGEMENT, which has
   to land inside the ~100ms a person reads as "it heard me", and the SETTLE,
   the long ease back to rest that is what separates a control from a toy.
   The JS pointer layer reads these off :root rather than carrying its own
   numbers, so the two halves of one gesture can never drift apart. */
:root {
  --dur-touch: 0.18s;
  --dur-settle: 1.1s;
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --page-p: 0;        /* page progress 0..1, written by runtime.js on every scroll */
  --drift: 2.2rem;    /* how far a data-drift element travels across the screen */
}

/* --mesh is set only by a generated register in the mesh-gradient world. The
   default lives in :where(:root) - zero specificity - so the register's own
   :root wins wherever the two sheets sit in the assembled file. */
:where(:root) { --mesh: none; }
/* THE MARK. A generated register splits type in two (engine/resolve.py): the dealt
   face, when it is a costume (the Rubik display cuts, a handwriting), sets only the
   business name and single words at poster size - --font-mark - and every headline
   and sentence is set in its readable pair, --font-display. A v1 register has one
   display face, so its mark IS its display face and nothing it renders changes.
   Same for the kicker's tracking: v1 keeps its 0.33em, a generated register sets
   its own (Hebrew has no small caps to track). */
:where(:root) {
  --font-mark: var(--font-display);
  --mark-weight: var(--head-weight);
  --kicker-track: 0.33em;
}

* { margin: 0; padding: 0; box-sizing: border-box; }
html { scroll-behavior: auto; }

body {
  font-family: var(--font-body);
  font-weight: var(--body-weight);
  background: var(--ground);
  color: var(--fg);
  overflow-x: hidden;
}

::selection { background: var(--brand); color: var(--on-brand); }
:focus-visible { outline: 2px solid var(--brand); outline-offset: 3px; }

img { display: block; max-width: 100%; }

h1, h2, h3, h4 { font-family: var(--font-display); letter-spacing: var(--head-track); }

/* ── film grain ── */
.grain {
  position: fixed; inset: -50%; z-index: 70; pointer-events: none;
  width: 200%; height: 200%;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.5'/%3E%3C/svg%3E");
  opacity: var(--grain); mix-blend-mode: overlay;
}

/* ── scroll progress ── */
.progress {
  position: fixed; top: 0; inset-inline: 0; z-index: 80;
  height: 2px; background: var(--brand);
  transform: scaleX(0); transform-origin: 100% 50%;
}
html.reduced .progress { display: none; }

/* ── header and footer: primitives/frames/<id>/frame.css (dealt since v2) ── */
.btn {
  text-decoration: none; border-radius: var(--radius-pill); font-weight: 500;
  transition: background 0.25s ease, color 0.25s ease, border-color 0.25s ease,
              transform var(--dur-touch) var(--ease-out);
}
/* THE PRESS. A phone has no hover, so until this rule a tap on either button was
   a link that did nothing for 300ms and then navigated — the one moment on the
   page where the machine looked broken on the device it is actually opened on.
   :active fires on touch and on mouse alike, so this needs no query. .98 is
   deliberately small: the button must dip, not bounce, or the page starts
   arguing with the restraint the register is selling. */
.btn:active { transform: scale(0.98); }
@media (prefers-reduced-motion: reduce) { .btn { transition: none; } .btn:active { transform: none; } }

.btn-ghost {
  color: inherit; border: 1px solid color-mix(in srgb, currentColor 45%, transparent);
  padding: 0.5rem 1.15rem; font-size: 0.9rem;
}

/* ── split-text line masks ──
   RTL: split by WORD only. Letters break niqqud, and the overflow:hidden mask
   clips HORIZONTALLY too, so a narrow container guillotines words silently. */
/* The mask also has to leave room for a RELIEF. A headline extruded with
   text-shadow paints copies of the glyph up to ~0.05em toward +x and +y, and
   in RTL the first letter of a line sits flush against the mask's start edge —
   so the +x copies fell outside the box and the leading glyph lost its depth
   while every other letter kept it. The bleed is padding pulled back by the
   same margin: the line's width and every indent measured from its content
   edge are unchanged; only the clipping edge moves out.
   The block-end bleed is widened for the same reason: 0.09em covered a
   descender, not a descender plus its extrusion. */
.sl-line {
  overflow: hidden;
  padding-inline-start: 0.1em; margin-inline-start: -0.1em;
  padding-block-end: 0.16em; margin-block-end: -0.16em;
}

/* ── scroll hint (lives inside whatever hero moment is mounted) ── */
.scroll-hint {
  position: absolute; z-index: 6; bottom: 3.4svh; left: 50%;
  transform: translateX(-50%);
  display: flex; flex-direction: column; align-items: center; gap: 0.6rem;
}
.hint-line {
  display: block; inline-size: 1px; block-size: 44px;
  background: linear-gradient(to bottom, transparent, var(--brand));
  animation: hint 2.2s ease-in-out infinite;
  transform-origin: top;
}
.hint-word { font-size: 0.67rem; letter-spacing: 0.4em; color: var(--fg-45); }
@keyframes hint { 0%, 100% { transform: scaleY(0.35); opacity: 0.4; } 55% { transform: scaleY(1); opacity: 1; } }
html.reduced .hint-line, html.reduced .scroll-hint { animation: none; }
@media (prefers-reduced-motion: reduce) { .hint-line { animation: none; } }

/* ── the kicker appears in TWO worlds ──
   Moments put it on the ground; the manifest puts it on the alt surface. On a
   dark register those are dark and light; on a light register they are light and
   dark. One colour cannot serve both, and getting it wrong is silent: pale gold
   on pale paper still renders, it just cannot be read. So it is scoped. */
.kicker {
  font-size: 0.77rem; letter-spacing: var(--kicker-track); font-weight: var(--kicker-weight);
  color: var(--brand); margin-block-end: var(--kicker-gap);
}
.manifest .kicker { color: var(--brand-alt); }

/* ── manifest ──
   Three layers: a rule along the top, a ghost word behind, the copy in front.
   The section clips its own overflow because the ghost is deliberately wider
   than the page on a phone and parallaxes past the section's edges — without
   the clip it would widen the document and break mobile. */
.manifest {
  --manifest-pad-inline: clamp(1.4rem, 8vw, 7rem);
  position: relative; overflow: hidden;
  background: var(--alt); color: var(--alt-fg);
  padding: var(--section-pad-block) var(--manifest-pad-inline);
}
.manifest .kicker, .manifest-line { position: relative; z-index: 1; }
.manifest-line {
  font-family: var(--font-display); font-weight: var(--manifest-weight);
  font-size: var(--manifest-size);
  line-height: 1.31; max-inline-size: var(--manifest-measure);
}
/* the rule draws right-to-left as the section enters (JS scrubs scaleX from 0);
   drawn is the resting state. It is --brand-alt, not --brand: it lies on the
   alt surface, and --brand is the accent as read on the GROUND — in a light
   register that is the deep tone, invisible on ink. Same scoping as the kicker. */
.manifest-rule {
  position: absolute; top: 0; inset-inline: var(--manifest-pad-inline);
  block-size: 2px; background: var(--brand-alt);
  transform-origin: 100% 50%;
}
/* the ghost: one huge display word in the edge colour, faint enough to read as
   texture. Sized in vw so it stays proportional to the page, not to the copy;
   nowrap because it is one word and must never break (a break would be a
   letter split by another name). It is positioned, so it needs an explicit
   inline-size — an absolutely positioned box shrink-wraps under any
   max-inline-size the register sets on text. */
.manifest-ghost {
  position: absolute; z-index: 0; inset-inline-start: -0.06em; top: 50%;
  inline-size: max-content; max-inline-size: none;
  transform: translateY(-50%);
  font-family: var(--font-display); font-weight: 900;
  font-size: clamp(7rem, 26vw, 24rem); line-height: 1; letter-spacing: -0.03em;
  white-space: nowrap; color: var(--edge); opacity: 0.08;
  pointer-events: none; user-select: none;
}

/* ── CTA ── */
/* THE OUTLINE WORD BELONGS TO EVERY HERO, not to one moment.
   config.json marks a word in hero.h1 with <em> and it is drawn as a brass stroke
   around an empty fill. The rule lived only in the scroll-world sheet, scoped to
   .hero-world, and a cold lead walked straight into the gap: the machine dealt it
   a line-draw hero, whose section is .hero, so the <em> got no rule at all and the
   browser did what it does with an unstyled <em> - synthesised italic. Hebrew has
   no italic; the browser SLANTS the glyphs, and a slanted Hebrew word in a 128px
   headline reads as a rendering fault, on the first screen.
   Only the base treatment moves here. Lighting the word under a finger stays in
   scroll-world, because that is the only moment with a pointer layer to light it.
   text-shadow is restated as none for the documented reason: it paints the glyph
   SHAPE whatever the fill is, so a relief stack reaching an empty letter turns the
   well into a solid dark ghost of the word. */
.hero .hero-head h1 em {
  font-style: normal;
  color: transparent;
  /* em with a 1px floor: at phone sizes 0.014em is under a pixel and the word
     would simply vanish. */
  -webkit-text-stroke: max(1px, 0.014em) var(--brand);
  text-shadow: none;
}

.cta {
  background: var(--alt); color: var(--alt-fg); text-align: center;
  padding: var(--section-pad-block) clamp(1.4rem, 6vw, 4rem);
}
.cta h2 { font-weight: var(--head-weight); font-size: var(--cta-size); line-height: 1.03; }
.cta-sub { margin-top: 1.55rem; font-size: clamp(1rem, 1.5vw, 1.15rem); color: color-mix(in srgb, var(--alt-fg) 62%, transparent); }
.cta-actions {
  margin-top: 2.7rem; display: flex; align-items: center; justify-content: center;
  gap: clamp(1.1rem, 3vw, 2rem); flex-wrap: wrap;
}
/* the solid button is the alt surface inverted — register-agnostic by construction */
.btn-solid {
  display: inline-flex; align-items: center; gap: 0.6rem;
  background: var(--alt-fg); color: var(--alt);
  padding: 1.05rem 1.9rem; font-weight: var(--btn-weight); font-size: 1.02rem;
}
/* HOVER LIVES BEHIND (hover: hover), ALWAYS.
   A touch browser keeps :hover latched on the last thing tapped until something
   else is tapped, so an ungated hover rule leaves a phone with one permanently
   inverted button and no way to clear it. Both of the kit's hovers are gated
   here, in one block, so the next one added has an obvious place to go.
   The inversion itself is unchanged: --brand-alt is by construction readable
   against --alt, which makes --alt readable on it. --on-brand here would put
   ink on accent_deep in a dark register. */
@media (hover: hover) {
  .btn-ghost:hover { background: var(--brand); border-color: var(--brand); color: var(--on-brand); }
  .btn-solid:hover { background: var(--brand-alt); color: var(--alt); }
}
.cta-phone {
  color: var(--alt-fg); font-weight: var(--btn-weight); letter-spacing: 0.055em;
  text-decoration: underline; text-underline-offset: 6px;
  text-decoration-color: var(--brand-alt);
}
.contact-note { margin-top: 1.6rem; font-size: 0.85rem; color: color-mix(in srgb, var(--alt-fg) 62%, transparent); }

/* ── v2: the phone layer ──
   The tilt chip exists only on iOS, which grants DeviceOrientation from a tap
   and never otherwise. It sits above the fold's lower edge, out of the hero's
   way, and removes itself after one answer either way. */
.tilt-chip {
  position: fixed; z-index: 75; inset-block-end: 5.2rem; inset-inline-start: 50%;
  transform: translateX(50%);
  padding: 0.55rem 1.1rem; border: 1px solid color-mix(in srgb, var(--on-media) 45%, transparent);
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--ground-deep) 72%, transparent);
  color: var(--on-media); font: inherit; font-size: 0.85rem;
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
}
/* Secondary motion that runs off the main thread: anything marked data-drift
   slides a little as it crosses the screen. Vertical pages only - a track moves
   by transform, and view() timelines only follow real scrolling. */
@supports (animation-timeline: view()) {
  html[data-chassis="vertical"] [data-drift] {
    animation: wow-drift linear both;
    animation-timeline: view();
    animation-range: entry 0% exit 100%;
  }
}
@keyframes wow-drift {
  from { transform: translateY(var(--drift)); }
  to { transform: translateY(calc(-1 * var(--drift))); }
}
@media (prefers-reduced-motion: reduce) { [data-drift] { animation: none; } }

/* ── v2 kit sections ──
   Entrances without ScrollTrigger: runtime.js writes --p (the section's own
   progress from the chassis, 0..1) on every .kit-sec, and each [data-enter]
   derives its opacity and lift from it, staggered by --i. --p defaults to 1 here,
   so with no JS or under reduced motion everything simply stands in place. */
.kit-sec {
  --p: 1;
  position: relative;
  padding: var(--section-pad-block) clamp(1.4rem, 7vw, 6rem);
  background: var(--ground); color: var(--fg);
  background-image: var(--mesh);
}
.kit-sec .sec-title {
  font-weight: var(--head-weight); line-height: 1.02; letter-spacing: var(--head-track);
  font-size: clamp(2.2rem, 5.6vw, 4.6rem); max-inline-size: 18ch;
  /* no one-word last line: "מה נכנס לזר בכל / עונה" on a phone (sc02 rehearsal) */
  text-wrap: balance;
}
.kit-sec [data-enter] {
  --i: 0;
  --e: clamp(0, (var(--p) - 0.04 - var(--i) * 0.035) * 6, 1);
  opacity: var(--e);
  transform: translateY(calc((1 - var(--e)) * 1.8rem));
}
.chassis-h.is-h .kit-sec { padding-block: 5.5rem 2.5rem; }

/* ── v2: a STAGE behind the page ──
   A stage engine (engine.json slot "stage") draws every section's ground itself,
   on one canvas fixed behind the page, and plays its pieces across all of them.
   So once it is live (html.stage-live, set by the engine after it has READ each
   section's ground) the sections stop painting their own and become glass over
   the stage. Each section keeps its own text colours: the stage paints that
   section's ground under it, so a dark manifest is still light words on dark.
   Without JS, under reduced motion or on tier none, the class never lands and the
   page is exactly the page it was. --sec-ground is the ground a section's words
   were written for; the halo on small text uses it, because a piece can drift
   behind a sentence mid-flight. */
html.stage-live #top, html.stage-live .site-footer { position: relative; z-index: 1; }
html.stage-live #top section, html.stage-live #top .eng-stage, html.stage-live .site-footer {
  background-color: transparent; background-image: none;
}
html.stage-live #top section { --sec-ground: var(--ground); }
html.stage-live #top section[data-alt], html.stage-live #top .manifest, html.stage-live #top .cta {
  --sec-ground: var(--alt);
}
/* LEGIBILITY OVER A LIVE STAGE (reworked 28.09.2026).
   Between 28.09 and 29.09 five site builds each met eye.py's LEGIBILITY gate by
   thickening a -webkit-text-stroke around the words, in a contrasting colour
   (--on-brand), 0.16em to 0.34em wide. Every number passed and every page got
   worse: the headlines read as stickers with a white outline, the small text
   sat in a blurred blob - exactly the trap the taste ledger names ("the number
   went up and the page got worse"). The rule now:
   - small text keeps a KNOCKOUT in its own section's ground (--sec-ground). On
     gather's flat fog that halo is invisible - it only shows when a piece
     drifts behind a sentence, which is when it is needed;
   - headlines never get a stroke. They carry a soft shadow in the same ground,
     invisible on a flat ground, and are made legible by the STAGE instead:
     gather leaves the words room, and day calms the photograph under the words
     (engines/day: a neutral darken or lighten plus a light blur, only where
     text sits - the photo stays a photo, never a colour field);
   - on the day stage the photo IS the ground, so small text drops the stroke
     too (a stroke on a photograph is a visible blob) and leans on the same
     calm patch plus a tighter shadow. */
html.stage-live #top .kicker, html.stage-live #top section p,
html.stage-live #top .tset .tl.is-small .tl-in, html.stage-live #top figcaption {
  -webkit-text-stroke: 0.22em var(--sec-ground); paint-order: stroke fill;
  text-shadow: 0 0 1px var(--sec-ground), 0 1px 3px var(--sec-ground),
    0 0 16px color-mix(in srgb, var(--sec-ground) 85%, transparent);
}
html.stage-live #top .tset .tl-in, html.stage-live #top .tset .kw {
  -webkit-text-stroke: 0; paint-order: normal;
  text-shadow: 0 0.02em 0.5em color-mix(in srgb, var(--sec-ground) 55%, transparent);
}
html.stage-live.stage-is-day #top .kicker, html.stage-live.stage-is-day #top section p,
html.stage-live.stage-is-day #top .tset .tl.is-small .tl-in, html.stage-live.stage-is-day #top figcaption {
  -webkit-text-stroke: 0; paint-order: normal;
  text-shadow: 0 0 2px color-mix(in srgb, var(--sec-ground) 80%, transparent),
    0 1px 10px color-mix(in srgb, var(--sec-ground) 60%, transparent);
}
/* No glow on a split headline over the day stage: each line is its own mask box
   (overflow clipped for the rise-in), so the 0.5em ground-coloured glow was cut
   at the line's edges into a hard tinted RECTANGLE behind every big word - pink
   behind sc04's "נתכנן", the box behind sc03's "פינה" (29.09.2026 review). The
   stage's own calm tone, feathered wide, does this job without an edge. */
html.stage-live.stage-is-day #top .tset .tl-in, html.stage-live.stage-is-day #top .tset .kw { text-shadow: none; }
html.stage-live #top .sec-gallery figcaption { --sec-ground: var(--ground-deep); }
html.stage-live .manifest-ghost { opacity: 0.05; }
/* On the day stage the ground under a small accent word is a photograph, and an
   accent colour sits mid-luminance - no calm patch brings a photo far enough from
   it for 4.5:1 without turning the photo into a sheet. So small accent text there
   (kickers, prices) takes the section's own ink; the accent stays on the big words. */
html.stage-live.stage-is-day #top .kicker, html.stage-live.stage-is-day #top .svc-price,
html.stage-live.stage-is-day #top .kicker * { color: var(--fg); }

/* FRAME: marquee - a band across the top where the name runs on a loop.
   It moves RIGHT: in a right-to-left script the start of a phrase is its
   rightmost word, so a band entering from the left edge shows each phrase from
   its beginning. Two identical halves and a 0 -> +50% roll make the loop seamless
   (see the note on @keyframes mq-roll below); the gap lives inside each item as
   padding, so both halves measure the same. */
.site-header.frame-marquee {
  position: fixed; top: 0; inset-inline: 0; z-index: 60;
  display: flex; align-items: center; gap: 0.9rem;
  padding: 0.6rem clamp(0.8rem, 3vw, 1.6rem);
  color: var(--fg);
}
.mq-mark {
  flex: 0 0 auto; display: grid; place-items: center;
  inline-size: 2.2rem; block-size: 2.2rem; border: 1px solid currentColor; border-radius: 50%;
  color: inherit; text-decoration: none;
  font-family: var(--font-mark); font-weight: var(--mark-weight);
}
.mq-band {
  flex: 1 1 auto; overflow: hidden; min-inline-size: 0;
  -webkit-mask-image: linear-gradient(to left, transparent, black 8%, black 92%, transparent);
  mask-image: linear-gradient(to left, transparent, black 8%, black 92%, transparent);
}
.mq-run {
  display: flex; inline-size: max-content;
  font-family: var(--font-mark); font-weight: var(--mark-weight);
  font-size: 0.95rem; letter-spacing: 0.06em; white-space: nowrap;
  animation: mq-roll 28s linear infinite;
}
.mq-run > span { padding-inline-end: 2.4rem; }
.mq-run > span::after { content: "✦"; margin-inline-start: 2.4rem; opacity: 0.5; }
/* NOT translateX(-50%) -> 0: a block box with non-auto margins is flush to the
   CONTAINING BLOCK'S END edge under direction:rtl (its right edge here), so at
   rest (no transform) .mq-run already sits with its right edge at the band's
   right edge - only the last band-width slice of the doubled content is in
   view. Animating toward 0% only ever uncovers that same slice a little
   earlier; -50% (a full extra half-width further along the same direction)
   pushes the whole run out past the band on every measured width, so the band
   renders blank for most of the loop (confirmed: 0px visible from 0% to 70%
   of the cycle, only reaching the full 203px at 100%, on a 390px phone).
   Animating 0% -> +50% instead starts from that same rest position - already
   valid, already showing content - and moves right by exactly one half-width,
   landing on the next identical repeat: content stays in view for the whole
   cycle (confirmed: full band width visible at every sampled point). */
@keyframes mq-roll { from { transform: translateX(0); } to { transform: translateX(50%); } }
@media (prefers-reduced-motion: reduce) { .mq-run { animation: none; } }
.mq-cta {
  flex: 0 0 auto; padding: 0.45rem 1.05rem; color: inherit; font-size: 0.86rem;
  border: 1px solid color-mix(in srgb, currentColor 45%, transparent);
}
html.js .site-header.frame-marquee { transition: color 0.35s ease, background-color 0.35s ease; }
html.js .site-header.frame-marquee.over-alt { color: var(--alt-fg); }
html.js .site-header.frame-marquee.over-media { color: var(--on-media); }
html.js .site-header.frame-marquee.past-hero {
  background-color: color-mix(in srgb, var(--ground) 92%, transparent);
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
}
html.js .site-header.frame-marquee.past-hero.over-alt { background-color: color-mix(in srgb, var(--alt) 92%, transparent); }

.site-footer.frame-marquee-foot {
  display: block; overflow: hidden;
  padding: 2.4rem clamp(1.2rem, 4vw, 3rem) 1.4rem;
  background: var(--ground); color: var(--fg-45); font-size: 0.8rem;
}
/* the name at poster scale, bleeding off the edge on purpose - decoration,
   aria-hidden; the readable name is in the row under it */
.frame-marquee-foot .foot-word {
  display: block; white-space: nowrap; line-height: 0.9;
  font-family: var(--font-mark); font-weight: var(--mark-weight);
  font-size: clamp(4rem, 19vw, 16rem); letter-spacing: var(--head-track); color: var(--fg);
}
.frame-marquee-foot .foot-row {
  display: flex; justify-content: space-between; flex-wrap: wrap; gap: 0.6rem;
  margin-top: 1.2rem; letter-spacing: 0.075em;
}

/* CHASSIS: horizontal. Without JS, or under reduced motion, this is a plain
   vertical page: .h-track is a block and every section stacks, which is what
   check.py's no-JS probe needs to see. Only when the chassis mounts (.is-h) does
   <main> become a tall runway with a sticky viewport, and the track a row laid
   right to left that the scroll slides sideways. */
/* --h-len is written inline by chassis.js; declared here so tokens.lint_roles
   sees it (an undeclared var() fails the build). */
.chassis-h { --h-len: 0px; }
.chassis-h .h-track { display: block; }
.chassis-h:not(.is-h) [data-span] { height: auto; }
.chassis-h:not(.is-h) .eng-stage { position: relative; height: 100svh; overflow: hidden; }

.chassis-h.is-h { position: relative; height: calc(var(--h-len) + 100svh); }
.chassis-h.is-h .h-viewport { position: sticky; top: 0; height: 100svh; overflow: hidden; }
.chassis-h.is-h .h-track {
  display: flex; flex-direction: row; flex-wrap: nowrap;
  height: 100%; width: max-content;
  will-change: transform;
}
.chassis-h.is-h .h-track > section {
  flex: 0 0 auto; position: relative;
  width: 100vw; height: 100svh; overflow: hidden;
}
/* On the track every section is a screen - a slide, not a stretch of page. Short
   copy (a manifest line, the contact) sits in the middle of that screen instead of
   under the header with half a phone empty below it (sc01 rehearsal, 27.09).
   "safe" falls back to the top when a section is taller than the screen, so a long
   one is clipped at its bottom as before, never at its top. */
.chassis-h.is-h .h-track > section:not(.eng) {
  display: flex; flex-direction: column; justify-content: safe center;
}
/* a section whose content is a row (menu cards, a gallery strip) is wider than a
   screen and says so; the track just takes longer to pass it */
.chassis-h.is-h .h-track > section[data-wide] { width: max-content; min-width: 100vw; }
.chassis-h.is-h .eng-stage { position: absolute; inset: 0; height: 100%; }


/* ── section: hero-stage ── */
/* SECTION: hero-stage - the opening's words over the stage. A screen of room for
   the stage and one block of words where it leaves space: low on a phone (the
   stage plays in the top of the screen), at the start edge on a wide screen.
   The words arrive after the stage's opening burst has begun (0.9s), so the
   first thing the eye gets is motion, then the sentence. */
.sec-hero-stage {
  min-height: 100svh;
  display: flex; flex-direction: column; justify-content: flex-end;
  padding-block: 7rem clamp(4.5rem, 13svh, 7.5rem);
}
.hs-head { position: relative; max-inline-size: 34rem; }
.hs-title {
  font-weight: var(--head-weight); line-height: 0.98; letter-spacing: var(--head-track);
  font-size: clamp(2.6rem, 7.6vw, 6.6rem); text-wrap: balance;
}
.hs-sub {
  margin-top: 1.1rem; max-inline-size: 38ch;
  font-size: clamp(1.02rem, 1.6vw, 1.25rem); color: var(--fg-72);
}
html.js:not(.reduced) .hs-head > * { animation: hs-in 1.1s var(--ease-out) both; }
html.js:not(.reduced) .hs-head > :nth-child(1) { animation-delay: 0.9s; }
html.js:not(.reduced) .hs-head > :nth-child(2) { animation-delay: 1.02s; }
html.js:not(.reduced) .hs-head > :nth-child(3) { animation-delay: 1.18s; }
@keyframes hs-in { from { opacity: 0; transform: translateY(1.4rem); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .hs-head > * { animation: none; } }
@media (min-width: 900px) {
  .sec-hero-stage { justify-content: center; }
  .hs-head { max-inline-size: min(40vw, 36rem); }
}
.chassis-h.is-h .h-track > section.sec-hero-stage { justify-content: flex-end; }
@media (min-width: 900px) { .chassis-h.is-h .h-track > section.sec-hero-stage { justify-content: center; } }


/* ── section: gallery ── */
/* SECTION: gallery - the deep ground, so any photograph sits on dark and the
   captions use the media roles the register already proved against it. Three
   columns with the middle one dropped: a grid, but not a spreadsheet. On a track
   it becomes a filmstrip. No width/height attributes on the <img>: they would
   turn into a presentational height and check.py's aspect-ratio rule would fail. */
.sec-gallery { background: var(--ground-deep); color: var(--on-media); }
.sec-gallery .kicker { color: var(--brand-media); }
.gal-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(0.6rem, 1.4vw, 1.2rem); margin-top: 3rem; }
.gal-item { position: relative; overflow: hidden; }
.gal-item:nth-child(3n+2) { margin-top: 3.5rem; }
.gal-item img { inline-size: 100%; aspect-ratio: 4 / 5; object-fit: cover; }
.gal-item figcaption { margin-top: 0.6rem; font-size: 0.82rem; color: var(--on-media-72); }
@media (max-width: 767px) {
  .gal-grid { grid-template-columns: repeat(2, 1fr); }
  .gal-item:nth-child(3n+2) { margin-top: 0; }
  .gal-item:nth-child(2n) { margin-top: 2.4rem; }
}
/* ".h-track > section." - the chassis's own "every section is a centred column"
   rule (.h-track > section:not(.eng)) outranked a plain .sec-gallery, so on a
   desk the strip fell UNDER the heading and off the bottom of the screen (sc05,
   29.09.2026). */
.chassis-h.is-h .h-track > section.sec-gallery { display: flex; flex-direction: row; align-items: center; gap: 3vw; padding-inline: 6vw; }
.chassis-h.is-h .sec-gallery .sec-head { flex: 0 0 min(26rem, 30vw); }
.chassis-h.is-h .gal-grid { display: flex; gap: 1.2rem; margin: 0; }
.chassis-h.is-h .gal-item { margin: 0; }
/* On the track each photograph keeps its OWN shape (auto, with 4/5 only until it
   loads). 29.09.2026 (review: "zoom and image faults", sc04/sc05): a 16:9 photo
   forced into a 4:5 tile 52svh tall showed 45% of its width at 2.3x its own
   pixels on a phone - a nail macro became a smear of lacquer, a model house a
   corner of a wall. */
.chassis-h.is-h .gal-item img { block-size: calc(66svh - 2.2rem); inline-size: auto; aspect-ratio: auto 4 / 5; max-inline-size: 62vw; object-fit: cover; }
@media (max-width: 767px) {
  .chassis-h.is-h .h-track > section.sec-gallery { flex-direction: column; align-items: flex-start; justify-content: center; }
  .chassis-h.is-h .sec-gallery .sec-head { flex: 0 0 auto; }
  .chassis-h.is-h .gal-item img { block-size: auto; inline-size: 84vw; max-inline-size: none; max-block-size: 52svh; }
}


/* ── section: act-stage ── */
/* SECTION: act-stage - the words beside the stage's climax. Two screens long;
   on a vertical page the block pins for the second one, on a track the chassis
   holds (data-span). The block sits where hero-stage's did, so the stage keeps
   the same room to play in. */
.sec-act-stage { padding: 0; }
.as-pin {
  min-height: 100svh;
  display: flex; flex-direction: column; justify-content: flex-end;
  padding: 7rem clamp(1.4rem, 7vw, 6rem) clamp(4.5rem, 13svh, 7.5rem);
}
html[data-chassis="vertical"] .sec-act-stage .as-pin { position: sticky; top: 0; height: 100svh; }
.as-head { max-inline-size: 34rem; }
.as-title { font-size: clamp(2.4rem, 7vw, 6rem); }
.as-sub { margin-top: 1rem; max-inline-size: 36ch; font-size: clamp(1rem, 1.5vw, 1.2rem); color: var(--fg-72); }
@media (min-width: 900px) {
  .as-pin { justify-content: center; }
  .as-head { max-inline-size: min(40vw, 36rem); }
}
.chassis-h.is-h .h-track > section.sec-act-stage { display: block; }
.chassis-h.is-h .sec-act-stage .as-pin { height: 100%; }


/* ── section: faq ── */
/* SECTION: faq - the secondary surface, two columns of questions with a plus
   that turns to a cross. Native <details>: accessible and complete without JS. */
.sec-faq { background: var(--ground-2); }
.faq-list { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0 clamp(1.6rem, 4vw, 3.4rem); margin-top: 2.6rem; max-inline-size: 72rem; }
.faq-item { padding-block: 1.2rem; border-top: 1px solid color-mix(in srgb, var(--edge) 70%, transparent); }
.faq-item summary {
  display: flex; justify-content: space-between; gap: 1rem; cursor: pointer; list-style: none;
  font-family: var(--font-display); font-weight: var(--head-weight); font-size: clamp(1.1rem, 2vw, 1.45rem);
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::after { content: "+"; color: var(--brand); font-family: var(--font-body); transition: transform var(--dur-touch) var(--ease-out); }
.faq-item[open] summary::after { transform: rotate(45deg); }
.faq-item p { margin-top: 0.8rem; color: var(--fg-72); max-inline-size: 48ch; }
@media (max-width: 767px) { .faq-list { grid-template-columns: 1fr; } }
.chassis-h.is-h .sec-faq { display: flex; flex-direction: column; justify-content: center; padding-inline: 6vw; }


/* ── stage: gather ── */
/* STAGE: gather - one canvas behind the whole page. It stays invisible until the
   engine has drawn its first frame (is-drawn - not the runtime's is-live, which
   lands the moment init returns), so a slow phone never sees an empty rectangle
   where the page used to be. This file is SERVED - no operator names in its
   comments (check.py). */
.stage { position: fixed; inset: 0; z-index: 0; pointer-events: none; opacity: 0; transition: opacity 0.6s ease; }
.stage.is-drawn { opacity: 1; }
.stage-canvas { display: block; inline-size: 100%; block-size: 100%; }


/* ── direction layer ── */
/* THE DIRECTION LAYER - shared by every direction (directions/<id>.css adds the
   direction's own grammar on top). Loaded only on pages dealt a direction, so the
   v1 leads and the milestone-1 pages never see a line of it.
   Every colour is a role: inside a section the roles are that section's ground. */

/* a register from before the key weight existed sets its key in the head weight */
:where(:root) { --key-weight: var(--head-weight); --pop: var(--brand); --label-weight: 700; --font-label: var(--font-body); }

/* ── headlines set as type (direction.js composes them) ──
   Each line is its own mask: overflow hidden, with room below for the Hebrew
   descenders (ק ך ן ף ץ) pulled back by the same margin so the rhythm holds. */
.typeset .tset { max-inline-size: none; font-size: 1rem; line-height: 1; text-wrap: nowrap; }
.tset .tl {
  display: block; overflow: hidden;
  padding-block: 0.02em 0.14em; margin-block-end: -0.1em;
  padding-inline: 0.06em; margin-inline: -0.06em;
}
.tset .tl-in {
  --dx: 0px;
  display: inline-block; white-space: nowrap; line-height: 0.9;
  font-family: var(--font-display); font-weight: var(--head-weight); letter-spacing: var(--head-track);
  translate: var(--dx) 0;
  transform: translateY(0);
}
.tset .tl.is-small .tl-in {
  font-family: var(--font-body); font-weight: 700; letter-spacing: 0; line-height: 1.25;
  color: var(--fg-72);
}
.tset .tl.is-key .tl-in { color: var(--pop); font-weight: var(--key-weight); }
.tset .kw { color: var(--pop); font-weight: var(--key-weight); }
.tset em { font-style: normal; }
html.js:not(.reduced) .tset .tl-in {
  transform: translateY(108%);
  transition: transform calc(var(--motion-dur) * 1.25) var(--motion-ease) calc(var(--i) * 85ms);
}
html.js:not(.reduced) .tset.is-shown .tl-in { transform: translateY(0); }
@media (prefers-reduced-motion: reduce) { .tset .tl-in { transform: none; transition: none; translate: none; } }

/* A headline set over a moving picture carries a well of the deep ground in its
   own letters' shadow, wide enough to hold 3:1 where the picture is brightest (the
   pale window frame of a loop measured 1.72:1 without it, sc02s). The mask gets
   room above and below so the well is not cut off by it. */
.eng-loop .tset .tl { padding-block: 0.1em 0.18em; margin-block: -0.08em -0.14em; }
.eng-loop .tset .tl-in {
  text-shadow:
    0 0 0.05em color-mix(in srgb, var(--ground-deep) 72%, transparent),
    0 0.02em 0.2em color-mix(in srgb, var(--ground-deep) 62%, transparent),
    0 0 0.5em color-mix(in srgb, var(--ground-deep) 40%, transparent);
}

/* ── the preloader ──
   On the page from the first paint (so nothing is seen before it), gone without
   JS (<noscript> in page.html) and under reduced motion. */
.preloader { display: none; }
html[data-preloader] .preloader {
  position: fixed; inset: 0; z-index: 95;
  display: grid; place-items: center;
  background: var(--gr-dark); color: var(--gr-dark-fg);
  clip-path: inset(0 0 0 0);
  transition: clip-path 0.85s var(--motion-ease);
}
html[data-preloader] .preloader.pl-out { clip-path: inset(0 0 100% 0); }
.pl-in { display: grid; justify-items: center; gap: 1.2rem; }
.pl-name {
  font-family: var(--font-mark); font-weight: var(--mark-weight);
  font-size: clamp(3rem, 16vw, 11rem); line-height: 0.9; white-space: nowrap;
}
.pl-count {
  font-family: var(--font-label); font-weight: var(--label-weight); font-size: 0.9rem;
  letter-spacing: 0.08em; color: var(--gr-dark-accent); direction: ltr;
}
@media (prefers-reduced-motion: reduce) { html[data-preloader] .preloader { display: none; } }

/* ── a button leans toward the cursor (direction.js sets --mx/--my) ── */
.is-magnetic {
  --mx: 0px; --my: 0px;
  translate: var(--mx) var(--my);
  transition: translate 0.35s var(--ease-out), background 0.25s ease, color 0.25s ease;
}

/* ── labels: a direction's kickers are set in its label face ── */
html[data-direction] .kicker {
  font-family: var(--font-label); font-weight: var(--label-weight);
  font-size: 0.8rem; letter-spacing: var(--kicker-track);
}

/* The kit's gallery assumes the deep ground and colours its kicker and captions with the
   media roles (proved against dark). A direction gives the gallery a ground of its own, so
   on light or pale those roles read as nothing: hasson (30.09.2026, gallery on pale) had a
   champagne kicker at 1.4:1 and paper-white captions. The ground's own roles instead. */
html[data-direction] section[data-ground].sec-gallery .kicker { color: var(--brand); }
html[data-direction] section[data-ground].sec-gallery .gal-item figcaption { color: color-mix(in srgb, var(--fg) 78%, transparent); }
/* ...and kit.css knocks a gallery caption out in --ground-deep over a live stage: a 0.22em
   near-black stroke around dark words on pale, which read as a blot (1.08:1). */
html.stage-live[data-direction] #top section[data-ground].sec-gallery figcaption { --sec-ground: var(--ground); }


/* ── direction: pop ── */
/* DIRECTION: pop - joyful colour blocks.
   Loaded only on pages dealt this direction; roles only, every role is the
   section's ground inside it (the palette gives dark/light/pale/vivid four
   DIFFERENT hues, not four shades of one - engine/directions.py - so the page
   genuinely jumps between bright families as it scrolls, section by section).
   Its grammar against the template habits:
   - a kicker over every heading   -> a sticker: a pill chip in the pop colour
   - headlines as paragraphs        -> rounded heavy type, stepped sizes (stair)
   - services as three cards        -> three badges that sit a little askew,
                                        the price its own round sticker
   - a pill button                  -> a chunky button that springs on the hand
   - motion                         -> quick, with a bounce past the rest point */

/* the loud keyword itself: every one of pop's five palettes was generated so
   --pop clears AA against the VIVID ground it was born on (4.5:1+), but that
   same --pop is reused as the .kw fill on light/dark/pale grounds too, and
   there it was never checked - measured 3.0-3.6:1 against all five palettes'
   light/pale grounds (contrast script, sc05/nails, 28.09). kit.css's own
   stage-live stroke rule for `.tset .kw` (added for sc06's split-headline
   fix) now reads that gap directly, because the stroke IS --sec-ground: the
   gate's "own stroke" check is exactly contrast(--sec-ground, fill), no
   pixels involved, so a weak pairing fails even with no piece anywhere near
   the text. Pulling the fill 38% toward the SECTION'S OWN ink (--fg, always
   built to clear 4.5:1 against its ground) clears every palette x ground
   pairing with margin (worst case 4.92:1, teal-rose/dark) while the hue still
   reads as the loud colour, not plain ink - verified against all five
   palettes' four grounds before picking 38%. */
html[data-direction="pop"] .tset .kw, html[data-direction="pop"] .tset .tl.is-key .tl-in {
  color: color-mix(in srgb, var(--pop) 62%, var(--fg) 38%);
}
/* .tl.is-key .tl-in (a whole "key"-composition headline, e.g. faq/contact
   here) only ever gets kit.css's shadow, never a stroke (its own comment:
   a stroke would force the strict 4.5:1 "own stroke" check pop's flat 3:1
   contract can't promise). That leaves it on the lenient 3:1-for-large-text
   pixel gate - fine at rest, but a gather piece drifting across the SAME
   worst-tenth sample can still drag it under 3:1 for a frame (sc05: 1.43:1,
   a nail-art cutout crossing "שואלים"). The mix above already lifts the
   flat baseline toward ~5.6:1, so a piece has real headroom to cross before
   the gate's floor is anywhere close - same fix, same reason as .kw. */

/* the kicker: a sticker, not a rule */
html[data-direction="pop"] #top .kicker {
  display: inline-flex; align-items: center;
  padding: 0.35em 0.9em; border-radius: 999px;
  background: var(--pop); color: var(--on-brand);
  letter-spacing: 0.01em; transform: rotate(-1.5deg);
}

/* ...and the sticker IS its own plate, so the stage's knockout (kit.css,
   `html.stage-live #top .kicker`: a 0.22em stroke + glow in the SECTION's
   ground) must not reach it: on sc05 it drew a purple or mint outline around
   dark letters on a coral pill and the owner could barely read "שאלות שחוזרות"
   (29.09.2026). The pill's own ink on the pill's own colour is the whole
   contrast - nothing else. */
html[data-direction="pop"].stage-live #top .kicker {
  -webkit-text-stroke: 0; paint-order: normal; text-shadow: none;
}

/* the opening and the act: room for the stepped headline to bounce */
html[data-direction="pop"] .hs-head, html[data-direction="pop"] .as-head { max-inline-size: none; inline-size: 100%; }
@media (min-width: 900px) {
  html[data-direction="pop"] .hs-head, html[data-direction="pop"] .as-head { inline-size: min(58vw, 48rem); }
}
html[data-direction="pop"] .hs-sub, html[data-direction="pop"] .as-sub {
  max-inline-size: 34ch; color: var(--fg); font-weight: 600;
}

/* a hero (or act) loop's picture is never the machine's to grade for legibility.
   .loop-sub (engines/hero-loop/style.css) is --on-media-72 - TRANSLUCENT paper -
   which is exactly why a veil under it was never enough: a rose in the bouquet
   sits right behind it in grape-coral's crop, and 28% of a saturated red still
   shows straight through every letter no matter how dark the scrim gets. The
   letters need their own OPAQUE plate, the way .loop-head .kicker already
   carries one (engines/hero-loop/style.css) - not a stronger wash behind a hole
   the colour still comes through.
   THE REAL CATCH: kit.css's stage-live rule (`html.stage-live #top .kicker,
   html.stage-live #top section p { -webkit-text-stroke: ... var(--sec-ground) }`)
   carries an #top ID, and beats BOTH that kicker rule and a same-shaped one here
   the moment the stage mounts - repainting the plate as the SECTION'S OWN ground
   instead of a dark one. On every other dealt hero that ground happens to read
   dark already; pop's hero can legitimately land on "vivid" (grape-coral's hero
   here does, by design - grounds jumping is the whole point), and vivid is
   exactly the bright colour a legibility plate must not be. `#top` in the
   selector below is not decoration - it is what wins that fight. */
html[data-direction="pop"] .eng-loop .loop-grade {
  background:
    linear-gradient(to top, color-mix(in srgb, var(--ground-deep) 88%, transparent) 0%,
      color-mix(in srgb, var(--ground-deep) 60%, transparent) 45%, transparent 82%);
}
html[data-direction="pop"] #top .eng-loop .loop-sub {
  color: var(--on-media);
  -webkit-text-stroke: 0.2em color-mix(in srgb, var(--ground-deep) 97%, transparent) !important;
  paint-order: stroke fill;
  text-shadow:
    0 0 1px color-mix(in srgb, var(--ground-deep) 98%, transparent),
    0 1px 2px color-mix(in srgb, var(--ground-deep) 94%, transparent),
    0 0 8px color-mix(in srgb, var(--ground-deep) 82%, transparent),
    0 0 18px color-mix(in srgb, var(--ground-deep) 58%, transparent);
}

/* seasons: four stickers in a column, not a two-up grid - a grid this short
   fit a laptop screen whole and never moved a pixel as the page scrolled past
   it (the gate's own MOTION check). A column is also one more sticker moment,
   matching services below it instead of falling back to the kit's plain bands. */
html[data-direction="pop"] #top .season-grid {
  display: grid; grid-template-columns: 1fr; gap: clamp(1.2rem, 2.6vw, 2rem); margin-top: 2.6rem;
}
html[data-direction="pop"] #top .season {
  min-block-size: 0; gap: 0.6rem;
  padding: clamp(1.8rem, 4vw, 2.4rem) clamp(1.6rem, 3vw, 2rem);
  border-radius: 1.6rem; background: var(--ground-2); box-shadow: 0 0.5rem 0 color-mix(in srgb, var(--fg) 16%, transparent);
}
html[data-direction="pop"] #top .season:nth-child(odd) { transform: rotate(-0.6deg); }
html[data-direction="pop"] #top .season:nth-child(even) { transform: rotate(0.6deg); }
html[data-direction="pop"] #top .season-name {
  font-size: clamp(2.4rem, 6vw, 4.6rem);
}
html[data-direction="pop"] #top .season-flowers {
  display: inline-block; padding: 0.3em 0.85em; border-radius: 999px;
  background: var(--pop); color: var(--on-brand);
  font-family: var(--font-label); font-weight: var(--label-weight); font-size: 0.78rem;
  letter-spacing: 0.01em;
}
@media (max-width: 767px) { html[data-direction="pop"] #top .season-grid { grid-template-columns: 1fr; } }

/* the manifest: the ghost word gets an outline in the hot colour, tilted */
html[data-direction="pop"] .manifest-ghost {
  color: transparent; -webkit-text-stroke: 2px var(--pop); opacity: 0.14;
  transform: translateY(-50%) rotate(-2deg);
}
html[data-direction="pop"] .manifest-rule { background: var(--pop); border-radius: 999px; block-size: 4px; }

/* services: three stickers, each nudged off the grid so the page reads as
   stuck-on rather than printed */
html[data-direction="pop"] #top .svc-grid {
  gap: clamp(1.4rem, 3vw, 2.2rem); margin-top: 2.6rem;
}
html[data-direction="pop"] #top .svc {
  padding: clamp(1.8rem, 4vw, 2.4rem) clamp(1.6rem, 3vw, 2rem);
  border-radius: 1.6rem; background: var(--ground-2);
  box-shadow: 0 0.5rem 0 color-mix(in srgb, var(--fg) 16%, transparent);
}
html[data-direction="pop"] #top .svc:nth-child(odd) { transform: rotate(-0.8deg); }
html[data-direction="pop"] #top .svc:nth-child(even) { transform: rotate(0.8deg); }
html[data-direction="pop"] #top .svc-price {
  display: inline-block; padding: 0.3em 0.85em; border-radius: 999px;
  background: var(--pop); color: var(--on-brand);
  font-family: var(--font-label); font-weight: var(--label-weight); font-size: 0.85rem;
}
html[data-direction="pop"] #top .svc-name {
  margin-top: 0.7rem; font-family: var(--font-display); font-weight: var(--head-weight);
  letter-spacing: var(--head-track); font-size: clamp(1.9rem, 4.6vw, 3.2rem); line-height: 1;
}
html[data-direction="pop"] #top .svc-line { margin-top: 0.5rem; color: var(--fg-72); max-inline-size: 30ch; }

/* the contact: a big springy button, the kind a thumb wants to press.
   KNOWN WEAK SPOT (the pop proof build, runs/_dir-pop, florist placeholder):
   kit.css softens .cta-sub / .contact-note to 62% of --alt-fg over transparent,
   which on a neutral print ground just fades toward grey. Pop's grounds are
   saturated colour blocks (vivid/dark), so 62% fades the ink toward the SAME
   hue it sits on instead - accent-on-accent, measured at 3.68:1 on grape-coral's
   vivid (contrast script, 28.09), under AA's 4.5:1 for body text even though it
   clears the gate's 3.0:1 floor. Pop keeps the sub-line closer to solid ink. */
html[data-direction="pop"] .cta-sub, html[data-direction="pop"] .contact-note {
  color: var(--alt-fg); opacity: 0.82;
}
html[data-direction="pop"] .cta { text-align: center; }
html[data-direction="pop"] .cta .tset .tl { text-align: center; }
html[data-direction="pop"] .contact .btn-solid {
  padding: 1.3rem 3.2rem; font-weight: 800; font-size: 1.1rem;
  border-radius: 999px;
  transition: translate 0.35s var(--motion-ease), scale 0.35s var(--motion-ease);
}
@media (hover: hover) { html[data-direction="pop"] .contact .btn-solid:hover { scale: 1.06; } }
html[data-direction="pop"] .contact .btn-solid:active { scale: 0.96; }
html[data-direction="pop"] .contact-note { font-weight: 700; }

/* the preloader: the name lands with the same spring as everything else */
html[data-direction="pop"] .pl-name {
  font-weight: var(--head-weight); letter-spacing: var(--head-track);
  scale: 0.7; opacity: 0; animation: pop-in 0.65s var(--motion-ease) 0.1s both;
}
@keyframes pop-in { to { scale: 1; opacity: 1; } }
@media (prefers-reduced-motion: reduce) { html[data-direction="pop"] .pl-name { animation: none; scale: 1; opacity: 1; } }
