/* REGISTER (direction poster) for krifitzer: palette black-orange, type noto-sans-hebrew over heebo, labels cousine, mark gveret-levin.
   Written by engine/directions.py; never edited by hand. */
:root {
  --ink: #110f0e;
  --ink-2: #1f160f;
  --ink-deep: #020201;
  --paper: #faf6ee;
  --accent: #fc740e;
  --accent-deep: #b93400;
  --line: #989285;
  --shadow: #000000;
  --gr-light: #f4f0e7;
  --gr-light-fg: #110f0e;
  --gr-light-accent: #b93400;
  --gr-light-pop: #e76000;
  --gr-light-on: #faf6ee;
  --gr-dark: #120f0c;
  --gr-dark-fg: #faf6ee;
  --gr-dark-accent: #fc740e;
  --gr-dark-pop: #fc740e;
  --gr-dark-on: #110f0e;
  --gr-vivid: #fc740e;
  --gr-vivid-fg: #110f0e;
  --gr-vivid-accent: #120f0c;
  --gr-vivid-pop: #120f0c;
  --gr-vivid-on: #faf6ee;
  --gr-pale: #f3d8c4;
  --gr-pale-fg: #110f0e;
  --gr-pale-accent: #ab2500;
  --gr-pale-pop: #d55000;
  --gr-pale-on: #faf6ee;
  --font-display: "Noto Sans Hebrew", sans-serif;
  --font-mark: "Gveret Levin", sans-serif;
  --font-body: "Heebo", Arial, sans-serif;
  --font-label: "Cousine", monospace;
  --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.025em;
  --key-weight: 900;
  --mark-weight: 400;
  --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.76, 0, 0.24, 1);
  --motion-dur: 0.9s;
  --motion-settle: 1.2s;
}
/* 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: counter - a printed programme's running head. The name at the start
   edge, where we are in the page (02 / 05) in the label face in the middle, and
   the way to talk as a word with a rule under it, not a pill. No bar, no ground:
   the header wears the ground of the section beneath it (direction.js copies its
   data-ground), so it is always the right ink without ever covering the page.
   The footer is the name as a band running at poster size. */
.site-header.frame-counter {
  position: fixed; top: 0; inset-inline: 0; z-index: 60;
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center;
  padding: 1rem clamp(1rem, 4vw, 2.6rem);
  color: var(--fg); pointer-events: none;
  transition: color 0.4s ease;
}
.frame-counter > * { pointer-events: auto; }
.fc-name {
  justify-self: start; color: inherit; text-decoration: none; white-space: nowrap;
  font-family: var(--font-mark); font-weight: var(--mark-weight); font-size: 1.15rem;
}
.fc-count {
  display: flex; gap: 0.35rem; align-items: baseline; direction: ltr;
  font-family: var(--font-label); font-size: 0.78rem; letter-spacing: 0.06em;
}
.fc-count b { font-weight: var(--label-weight); font-size: 0.92rem; }
.fc-sep { opacity: 0.45; }
.fc-all { opacity: 0.6; }
.fc-cta {
  justify-self: end; color: inherit; text-decoration: none; font-weight: 700; font-size: 0.95rem;
  padding-block: 0.2rem; border-block-end: 2px solid currentColor;
}
@media (hover: hover) { .fc-cta:hover { color: var(--brand); } }
html[data-frame="counter"] .progress { height: 3px; background: var(--pop); }
/* Past the opening the running head sits on a veil of the ground beneath it, so
   a headline scrolling up under it stays a headline, not two lines of type
   printed over each other (sc01s, end of the page). */
html.js .site-header.frame-counter.past-hero {
  background-color: color-mix(in srgb, var(--ground) 86%, transparent);
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
  transition: color 0.4s ease, background-color 0.4s ease;
}

.site-footer.frame-counter-foot {
  display: block; overflow: hidden; position: relative;
  padding: 2rem 0 1.4rem;
  background: var(--ground); color: var(--fg);
}
.fc-band { overflow: hidden; }
.fc-run {
  display: flex; inline-size: max-content;
  font-family: var(--font-mark); font-weight: var(--mark-weight);
  font-size: clamp(4.5rem, 21vw, 18rem); line-height: 0.95; white-space: nowrap;
  animation: fc-roll 26s linear infinite;
}
.fc-run > span { padding-inline-end: 0.35em; }
.fc-run > span:nth-child(even) { color: transparent; -webkit-text-stroke: max(1.5px, 0.012em) var(--fg); }
@keyframes fc-roll { from { transform: translateX(0); } to { transform: translateX(50%); } }
@media (prefers-reduced-motion: reduce) { .fc-run { animation: none; } }
.frame-counter-foot .foot-row {
  display: flex; justify-content: space-between; flex-wrap: wrap; gap: 0.6rem;
  margin-top: 1rem; padding-inline: clamp(1.2rem, 4vw, 3rem);
  font-family: var(--font-label); font-size: 0.75rem; color: var(--fg-72);
}
@media (max-width: 600px) { .fc-count { display: none; } .site-header.frame-counter { grid-template-columns: 1fr 1fr; } }

/* CHASSIS: vertical. v1 pages have no [data-span] and no .eng-stage, so nothing
   here reaches them. An engine section is --span screens tall and its stage
   sticks while the section scrolls past: the pin, done by CSS, so an engine never
   needs a ScrollTrigger of its own. --span is declared here as a default because
   tokens.lint_roles fails any var() the sheet never declares; the fragment's
   inline style="--span: N" wins over it. */
[data-span] { --span: 1; }
html[data-chassis="vertical"] [data-span] {
  position: relative;
  height: calc(var(--span) * 100svh);
}
html[data-chassis="vertical"] .eng-stage {
  position: sticky; top: 0;
  height: 100svh; overflow: hidden;
}


/* ── 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%; }


/* ── 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);
}


/* ── direction: poster ── */
/* DIRECTION: poster - type is the picture.
   Loaded only on pages dealt this direction. Written against roles only: inside
   a section every role is that section's ground (engine/directions.py). What it
   changes, section by section, is the template habit each one had:
   - a kicker over every heading   -> a running number in the label face
   - headlines as paragraphs        -> lines set to the width (direction.js)
   - services as three cards        -> a numbered list of rows, the name large
   - a pill button                  -> a round stamp that leans toward the cursor */

html[data-direction="poster"] #top { counter-reset: sec; }
html[data-direction="poster"] #top section { counter-increment: sec; }
/* The label is the text face in bold: Hebrew in a monospace face comes out
   letter-spaced, like a typing error. Only the running number is mono. */
html[data-direction="poster"] #top .kicker {
  font-family: var(--font-body); font-weight: 700; font-size: 0.86rem; letter-spacing: 0;
}
html[data-direction="poster"] #top section .kicker::before {
  content: counter(sec, decimal-leading-zero);
  display: inline-block; margin-inline-end: 0.75em; padding-inline-end: 0.75em;
  border-inline-end: 1px solid currentColor;
  font-family: var(--font-label); font-weight: var(--label-weight); font-size: 0.8rem;
}
/* the running head already says where we are; the scroll hint only crowded the
   opening's last line on a phone */
html[data-direction="poster"] .sec-hero-stage .scroll-hint { display: none; }

/* the opening: the words take half a wide screen, all of a phone */
html[data-direction="poster"] .hs-head, html[data-direction="poster"] .as-head { max-inline-size: none; inline-size: 100%; }
@media (min-width: 900px) {
  html[data-direction="poster"] .hs-head, html[data-direction="poster"] .as-head { inline-size: min(52vw, 46rem); }
}
html[data-direction="poster"] .hs-sub, html[data-direction="poster"] .as-sub {
  font-size: clamp(1.05rem, 1.5vw, 1.25rem); max-inline-size: 34ch; color: var(--fg);
}

/* the manifest: a statement set to the width, a thick rule in the loud colour.
   On a wide screen it takes the start two thirds, so the stage keeps a third to
   play in: set across the whole width, white type sat on white paper. */
html[data-direction="poster"] .manifest-ghost { display: none; }
@media (min-width: 900px) { html[data-direction="poster"] .manifest-line { inline-size: 62vw; } }
html[data-direction="poster"] .manifest-rule { block-size: 4px; background: var(--pop); }
html[data-direction="poster"] .manifest .kicker { color: var(--brand); }

/* services: a numbered list, not cards. On a wide screen it keeps to the start
   two thirds, like the manifest, and the stage fans its pages in the rest. */
@media (min-width: 900px) {
  html[data-direction="poster"] #top .sec-services .sec-head,
  html[data-direction="poster"] #top .sec-services .svc-grid { inline-size: 60vw; }
}
html[data-direction="poster"] #top .svc-grid {
  display: grid; grid-template-columns: 1fr; gap: 0; margin-top: 2.4rem; counter-reset: svc;
}
html[data-direction="poster"] #top .svc {
  counter-increment: svc;
  display: grid; grid-template-columns: 3.4rem 1fr auto; grid-template-areas: "n name price" "n line line";
  align-items: baseline; column-gap: 1.2rem; row-gap: 0.35rem;
  padding: 1.25rem 0 1.4rem; border-top: 3px solid var(--fg);
  container-type: normal;
}
html[data-direction="poster"] #top .svc:last-child { border-bottom: 3px solid var(--fg); }
html[data-direction="poster"] #top .svc::before {
  content: counter(svc, decimal-leading-zero); grid-area: n; align-self: start; padding-top: 0.5rem;
  font-family: var(--font-label); font-weight: var(--label-weight); font-size: 0.85rem; color: var(--brand);
}
html[data-direction="poster"] #top .svc-name {
  grid-area: name; font-family: var(--font-display); font-weight: var(--head-weight);
  letter-spacing: var(--head-track); font-size: clamp(2rem, 5.4vw, 4.6rem); line-height: 0.95;
}
html[data-direction="poster"] #top .svc-price {
  grid-area: price; font-family: var(--font-label); font-weight: var(--label-weight);
  font-size: clamp(0.85rem, 1.1vw, 1rem); color: var(--brand); white-space: nowrap;
}
html[data-direction="poster"] #top .svc-line { grid-area: line; max-inline-size: 46ch; color: var(--fg-72); font-size: 1rem; }
@media (max-width: 767px) {
  html[data-direction="poster"] #top .svc { grid-template-columns: 2.4rem 1fr; grid-template-areas: "n name" "n price" "n line"; }
  /* on a track every section is exactly one screen: the list tightens to fit it */
  html[data-direction="poster"] .chassis-h.is-h .sec-services { padding-block: 4.6rem 1.2rem; }
  html[data-direction="poster"] .chassis-h.is-h .svc-grid { margin-top: 1.2rem; }
  html[data-direction="poster"] .chassis-h.is-h .svc { padding-block: 0.7rem 0.8rem; row-gap: 0.15rem; }
  html[data-direction="poster"] .chassis-h.is-h .svc-name { font-size: 1.7rem; }
  html[data-direction="poster"] .chassis-h.is-h .svc-line { font-size: 0.9rem; line-height: 1.35; }
}

/* the contact: the title set to the width, the action a round stamp */
html[data-direction="poster"] .cta { text-align: start; }
html[data-direction="poster"] .cta-sub { color: var(--fg-72); max-inline-size: 38ch; }
html[data-direction="poster"] .cta-actions { justify-content: flex-start; margin-top: 2.2rem; }
html[data-direction="poster"] .contact .btn-solid {
  inline-size: clamp(9rem, 22vw, 12rem); aspect-ratio: 1; border-radius: 50%;
  display: grid; place-items: center; text-align: center; padding: 1.4rem;
  background: var(--brand); color: var(--on-brand); font-size: 1.02rem; line-height: 1.25;
}
@media (hover: hover) { html[data-direction="poster"] .contact .btn-solid:hover { background: var(--fg); color: var(--ground); } }
html[data-direction="poster"] .contact-note { font-family: var(--font-label); font-size: 0.78rem; }
