/* ============================================================
   Shapes Infinity — NOIR, the editorial dark layer
   Loaded LAST on every public page, after playground.css and the
   page stylesheet. Re-grounds the "paper" world to a flat near-
   black editorial canvas: colossal display type with a serif-
   italic accent, hairline rules, zero glow (owner rule), one
   light "statement" panel (.quote-cta) for contrast rhythm.
   Motion CSS for assets/js/motion.js lives at the bottom:
   masked word reveals, letter-roll labels, marquee, parallax.
   Every animated rule is gated off under html.motion-off.
   ============================================================ */

/* ---- 1. the ground: flat noir, no mesh, no fixed attachment ----
   (background-attachment:fixed forces full repaints on scroll —
   removing it is half of what "smooth" costs on this site) */
body.paper {
  background: #0c0c0c;
  background-image: none;
  --noir-ground: #0c0c0c;
  --noir-panel: #151513;
  --noir-paperlight: #d6d2c8;      /* the light statement panel */
  --ink: #f1efe8;
  --muted: #9b968a;
  --line: rgba(241, 239, 232, 0.16);
  --glass: rgba(241, 239, 232, 0.05);
  --font-serif: "Instrument Serif", Georgia, serif;
}
body.paper .page { background: transparent; }

/* the fixed dark band behind the chip rail blends into the ground */
@media (min-width: 1100px) {
  body.paper::before { background: #0c0c0c; border-inline-end: 1px solid var(--line); }
}
@media (max-width: 1099px) {
  body.paper .brand-mark { background: #0c0c0c; border: 1px solid var(--line); }
}

/* selection + focus keep brand orange, flat */
::selection { background: var(--orange); color: #0c0c0c; }
:focus-visible { outline: 2px solid var(--orange); outline-offset: 3px; box-shadow: none; }

/* ---- 2. the type scale: editorial, tight, enormous ---- */
h1, h2, h3, .display {
  font-weight: 600;
  letter-spacing: -0.03em;
  line-height: 1.02;
}
/* the serif-italic accent — the signature of the redesign */
.ph-accent,
.hero-ledger h1 .accent,
.mega-title .accent,
h1 em, h2 em,
.quote-cta-title em,
.footer-hail-accent,
.accent-serif {
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: 400;
  font-size: 1.06em;
  letter-spacing: -0.01em;
  background: none; -webkit-background-clip: initial; background-clip: initial;
  color: var(--orange);
}

/* section heads become mastheads: hairline, eyebrow gap, huge title */
.entries-head { border-top: 1px solid var(--line); padding-top: clamp(22px, 3vw, 38px); }
.entries-head h2 {
  font-size: clamp(2.3rem, 5.4vw, 4.6rem);
  max-width: 16ch;
  letter-spacing: -0.035em;
}
.entries-head .lead {
  font-size: clamp(1rem, 1.35vw, 1.2rem);
  color: var(--muted);
  max-width: 52ch;
}

/* ---- 3. THE HERO — full-height noir billboard ---- */
body.paper .paper-hero { min-height: 100svh; display: flex; align-items: center; }
body.paper .ph-stage { width: 100%; }
body.paper .ph-sheet {
  --ph-ink: var(--ink);
  --ph-muted: var(--muted);
  --ph-line: var(--line);
  background: transparent; box-shadow: none; border-radius: 0;
  color: var(--ph-ink);
  padding-block: clamp(90px, 14vh, 150px) clamp(28px, 4vh, 48px);
}
.ph-kicker {
  font-family: var(--font-display);   /* was "Space Grotesk" first — a whole 22 KB webfont for four micro-labels at 0.7rem, where it is indistinguishable from General Sans */
  font-weight: 500; letter-spacing: 0.26em; font-size: 0.7rem;
  color: var(--muted);
}
.ph-title {
  font-size: clamp(3rem, 9.4vw, 8.6rem);
  line-height: 0.96;
  letter-spacing: -0.04em;
  font-weight: 600;
  max-width: none;
}
.ph-accent { font-style: italic; }   /* home's em accent joins the serif system */
.ph-sub { font-size: clamp(1.02rem, 1.4vw, 1.25rem); max-width: 54ch; line-height: 1.65; }
.ph-facts { border-top: 1px solid var(--line); }
.ph-facts b { font-variant-numeric: tabular-nums; }
.ph-ghost { border-color: var(--line); }

/* typed rotator re-inked for noir */
body.paper .ph-sheet .ph-type { color: var(--ink); }
body.paper .ph-sheet .ph-type .type-word { color: var(--orange); }
body.paper .ph-sheet .ph-type .type-caret { background: var(--orange); }

/* ---- 4. cards: flat panels, hairline borders, no glass, no lift-glow ---- */
body.paper .block-card,
body.paper .art-card,
body.paper .tile {
  background: var(--noir-panel);
  border: 1px solid var(--line);
  box-shadow: none;
  border-radius: 18px;
  transition: transform var(--dur-ui) var(--ease-fluid), border-color var(--dur-ui) var(--ease-fluid);
}
body.paper .block-card:hover,
body.paper .tile:hover {
  border-color: rgba(241, 239, 232, 0.38);
  transform: translateY(-4px);
  box-shadow: none;
}
body.paper .pill {
  background: transparent; color: var(--ink);
  border: 1px solid var(--line); border-radius: 999px;
}
body.paper .pill:hover { border-color: var(--orange); color: var(--orange); }
body.paper .kicker { background: transparent; border: 1px solid var(--line); color: var(--muted); }

/* trust bar: four flat fact chips */
body.paper .trust-bar li {
  background: var(--noir-panel);
  border: 1px solid var(--line);
  box-shadow: none;
}

/* banner becomes a statement line, not a chip */
body.paper .banner {
  background: transparent; border: 0; border-block: 1px solid var(--line);
  border-radius: 0;
  font-family: var(--font-display);
  font-size: clamp(1.25rem, 2.6vw, 2.1rem);
  font-weight: 600; letter-spacing: -0.02em; line-height: 1.25;
}
.banner a { text-decoration-color: var(--orange); }
.banner a:hover { color: var(--orange); text-shadow: none; }

/* contact row: hairline ledger */
body.paper .contact-value,
body.paper .contact-arrow {
  background: transparent; border: 1px solid var(--line); box-shadow: none;
}

/* the quote page's minimal masthead: heading, rule, straight into the
   picker — the wizard is the page (owner order 2026-07-30) */
.hero-ledger--min { padding-block: clamp(70px, 10vh, 120px) clamp(10px, 2vh, 22px); }
.hero-ledger--min h1 { font-size: clamp(3rem, 9vw, 7.5rem); }
body.paper .q-build > h2 {
  font-size: clamp(1.7rem, 3.4vw, 2.7rem);
  letter-spacing: -0.03em;
  margin-top: clamp(18px, 3vh, 34px);
}

/* quote wizard surfaces back on the dark ground — the progress ticks
   keep their state colours (same specificity as the base override) */
body.paper .q-step { background: rgba(241, 239, 232, 0.14); border: 0; }
body.paper .q-step.is-done { background: rgba(255, 107, 0, 0.45); }
body.paper .q-step.is-now { background: var(--orange); }
body.paper .q-bar { background: rgba(12, 12, 12, 0.94); }
body.paper .footer-pills a {
  background: transparent; color: var(--ink);
  border: 1px solid var(--line);
}
body.paper .footer-pills a:hover { border-color: var(--orange); color: var(--orange); }

/* ---- 5. the LIGHT statement panel — .quote-cta flips to alabaster ----
   The one bright block on every page: giant dark type on warm paper,
   the contrast beat before the noir footer. */
body.paper .quote-cta {
  background: var(--noir-paperlight);
  border: 0; border-radius: 22px;
  color: #14120d;
  box-shadow: none;
}
body.paper .quote-cta::before { display: none; }
.quote-cta-kicker { color: #55503f; } /* 5.3:1 on the alabaster panel — AA */
.quote-cta-title {
  color: #14120d;
  font-size: clamp(2rem, 4.6vw, 3.9rem);
  letter-spacing: -0.035em;
}
.quote-cta-sub { color: #55503f; }
.quote-cta-alt { color: #55503f; border-bottom-color: rgba(20, 18, 13, 0.35); }
.quote-cta-alt:hover { color: #14120d; border-bottom-color: var(--orange); }

/* ---- 6. footer: noir, wordmark restored as the light signature ---- */
body.paper .v2-footer { background: transparent; border-top: 1px solid var(--line); }
body.paper .v2-footer::before { display: none; }
body.paper .wordmark { display: flex; }
.footer-hail h2 { font-size: clamp(2.2rem, 5vw, 4.4rem); letter-spacing: -0.035em; }

/* ---- 7. buttons: flat, letter-roll ready ---- */
.btn-hero {
  box-shadow: none;
  border-radius: 999px;
}
.btn-hero:hover { box-shadow: none; }

/* ============================================================
   MOTION CSS — consumed by assets/js/motion.js
   ============================================================ */

/* ---- masked word reveal: .sw wraps a word, .swi is the riser ---- */
.sw { display: inline-block; overflow: hidden; vertical-align: bottom; }
/* once risen (or with motion off) the mask lets go — focus rings on
   wrapped links must not be clipped by the word box */
.sw-in .sw, html.motion-off .sw { overflow: visible; }
.swi {
  display: inline-block;
  transform: translateY(118%);
  transition: transform 0.75s cubic-bezier(0.22, 1, 0.36, 1);
  transition-delay: var(--swd, 0ms);
  will-change: transform;
}
.sw-in .swi, html.motion-off .swi { transform: translateY(0); }

/* re-tune the existing scroll reveals to the noir curve: longer travel,
   expo settle — same classes effects.js already applies */
html.motion-on .reveal {
  opacity: 0; transform: translateY(30px);
  transition:
    opacity 0.7s cubic-bezier(0.22, 1, 0.36, 1),
    transform 0.7s cubic-bezier(0.22, 1, 0.36, 1);
  transition-delay: var(--reveal-delay, 0ms);
}
html.motion-on .reveal.in-view { opacity: 1; transform: none; }

/* ---- letter-roll labels: two stacked rows inside an overflow clip ---- */
.lr { position: relative; display: inline-block; overflow: hidden; line-height: 1.15; }
.lr .lr-row { display: block; white-space: pre; }
.lr .lr-row + .lr-row { position: absolute; inset-inline-start: 0; top: 100%; }
.lr .lr-ch {
  display: inline-block;
  transition: transform 0.5s cubic-bezier(0.22, 1, 0.36, 1);
  transition-delay: calc(var(--lri) * 18ms);
}
a:hover > .lr .lr-ch, button:hover > .lr .lr-ch { transform: translateY(-100%); }
html.motion-off .lr .lr-ch { transition: none; }

/* ---- marquee: duplicated track, linear drift, pause when off ---- */
.noir-marquee {
  overflow: hidden; display: flex; white-space: nowrap;
  border-block: 1px solid var(--line);
  padding-block: clamp(10px, 1.6vw, 22px);
  margin-block: var(--section-gap);
  user-select: none;
}
.noir-marquee .nm-track {
  display: flex; flex: none; align-items: baseline; gap: 0;
  animation: nm-drift 46s linear infinite;
}
.noir-marquee .nm-track > span {
  font-family: var(--font-display);
  font-size: clamp(2.4rem, 6.4vw, 5.8rem);
  font-weight: 600; letter-spacing: -0.03em; line-height: 1.1;
  color: var(--ink);
  padding-inline: clamp(18px, 2.6vw, 40px);
}
.noir-marquee .nm-track > span:nth-child(even) {
  font-family: var(--font-serif); font-style: italic; font-weight: 400;
  color: transparent;
  -webkit-text-stroke: 1px var(--muted);
}
@keyframes nm-drift { from { transform: translateX(0); } to { transform: translateX(-100%); } }
/* RTL keeps the same drift: reversing the keyframes starts each loop with
   both tracks outside the clip window (blank band for half the cycle) */
html.motion-off .noir-marquee .nm-track { animation: none; }

/* ---- parallax hook: motion.js drives --prlx ---- */
.prlx { transform: translateY(var(--prlx, 0px)); will-change: transform; }
html.motion-off .prlx { transform: none; }

/* ---- smooth-scroll handoff: motion.js animates scrollTop itself ---- */
html.noir-lenis { scroll-behavior: auto; }

/* ============================================================
   PRELOADER — same DOM, noir dress: flat black, hairlines,
   the percent counter becomes the billboard.
   ============================================================ */
#si-pre { background: #0c0c0c; }

/* ---- hero entrance choreography: fires when the preloader lifts ----
   (motion.js splits the hero lines; before html.si-revealed the words
   hold below their masks, then rise with a stagger) */
html.motion-on:not(.si-revealed) .ph-title .swi { transform: translateY(118%); transition: none; }

/* ---- reduced motion / motion-off: everything visible, nothing moves ----
   Belt and braces: the media query mirrors the html.motion-off class so
   the contract holds even if JS never runs (the class is JS-applied). */
html.motion-off .reveal { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .noir-marquee .nm-track { animation: none; }
  .swi { transform: none; transition: none; }
  .sw { overflow: visible; }
  .lr .lr-ch { transition: none; }
  .prlx { transform: none; }
  html.motion-on .reveal { opacity: 1; transform: none; transition: none; }
  html.motion-on:not(.si-revealed) .ph-title .swi { transform: none; }
}

/* ---- small screens: the scale steps down, the grammar stays ---- */
@media (max-width: 700px) {
  .ph-title { font-size: clamp(2.1rem, 10.4vw, 3.2rem); }
  .entries-head h2 { font-size: clamp(1.9rem, 8vw, 2.6rem); }
  body.paper .quote-cta { border-radius: 16px; }
}

/* preloader skip — a visible way out (keys and taps already work) */
#si-pre .si-skip {
  position: absolute; inset-inline-end: 18px; bottom: 52px; z-index: 5;
  padding: 8px 16px; border-radius: 999px;
  border: 1px solid rgba(241, 239, 232, 0.3);
  background: transparent; color: rgba(241, 239, 232, 0.75);
  font: inherit; font-size: 0.74rem; font-weight: 600; letter-spacing: 0.08em;
  cursor: pointer;
  transition: border-color var(--dur-tap, 160ms) ease, color var(--dur-tap, 160ms) ease;
}
#si-pre .si-skip:hover { border-color: var(--orange); color: var(--orange); }

/* ---- Phase 2: case-card hover — cover zoom + title slide (fine pointers,
   motion-on only; the owner's no-custom-cursor rule stands — links keep
   the normal pointer) ---- */
.case-card .shot-frame { overflow: hidden; }
.case-card .shot-frame img,
.case-card .shot-frame video {
  transition: transform 0.7s cubic-bezier(0.22, 1, 0.36, 1);
  will-change: transform;
}
.case-card h3 {
  transition: transform 0.55s cubic-bezier(0.22, 1, 0.36, 1),
              color 0.55s cubic-bezier(0.22, 1, 0.36, 1);
}
@media (hover: hover) and (pointer: fine) {
  html.motion-on .case-card:hover .shot-frame img,
  html.motion-on .case-card:hover .shot-frame video { transform: scale(1.045); }
  html.motion-on .case-card:hover h3 { transform: translateX(8px); color: var(--orange); }
  html[dir="rtl"].motion-on .case-card:hover h3 { transform: translateX(-8px); }
}
html.motion-off .case-card .shot-frame img,
html.motion-off .case-card .shot-frame video,
html.motion-off .case-card h3 { transition: none; }

/* ---- Phase 3: the infinity signature behind the home hero ----
   The canvas sits under the copy; .ph-stage stacks above it, so the
   headline never loses contrast to the wire. */
body.paper .paper-hero { position: relative; }
.inf-sig {
  position: absolute; inset: 0; z-index: 0;
  pointer-events: none;
  opacity: 0.5;
}
body.paper .ph-stage { position: relative; z-index: 2; }
@media (max-width: 900px) { .inf-sig { opacity: 0.3; } }

/* ---- Phase 4: animated link underlines (transform-only, RTL-aware) ---- */
.article-body a, .gallery-intro a, .entries-head .lead a,
.faq-body a, .footer-col a, .lh-sub a, .ph-sub a {
  position: relative; text-decoration: none;
  background-image: linear-gradient(currentColor, currentColor);
  background-repeat: no-repeat;
  background-size: 100% 1px;
  background-position: 0 100%;
}
html[dir="rtl"] .article-body a { background-position: 100% 100%; }
html.motion-on .article-body a, html.motion-on .gallery-intro a,
html.motion-on .entries-head .lead a, html.motion-on .faq-body a,
html.motion-on .footer-col a, html.motion-on .lh-sub a, html.motion-on .ph-sub a {
  transition: background-size 0.45s cubic-bezier(0.22, 1, 0.36, 1), color 0.45s cubic-bezier(0.22, 1, 0.36, 1);
}
html.motion-on .article-body a:hover, html.motion-on .gallery-intro a:hover,
html.motion-on .entries-head .lead a:hover, html.motion-on .faq-body a:hover,
html.motion-on .footer-col a:hover, html.motion-on .lh-sub a:hover, html.motion-on .ph-sub a:hover {
  background-size: 0% 1px; color: var(--orange);
}

/* ---- Phase 4: Selected work — horizontal snap rail on Home ----
   Native horizontal scroll with snap points: pans by touch, trackpad,
   drag on the scrollbar, keyboard arrows, or the two buttons. The
   vertical wheel is never hijacked. ---- */
.hcase { margin-block: var(--section-gap); }
.hcase-head { display: flex; align-items: end; justify-content: space-between; gap: 16px; }
.hcase-nav { display: flex; gap: 8px; }
.hcase-nav button {
  width: 46px; height: 46px; border-radius: 50%;
  border: 1px solid var(--line); background: transparent; color: var(--ink);
  font-size: 1.1rem; cursor: pointer;
  transition: border-color var(--dur-ui) var(--ease-fluid), color var(--dur-ui) var(--ease-fluid);
}
.hcase-nav button:hover { border-color: var(--orange); color: var(--orange); }
/* the rail is a continuous marquee: two identical tracks drift as one,
   so the work never stops moving. Hover or keyboard focus pauses it;
   reduced motion turns it into a normal scrollable strip. */
.hcase-rail {
  display: flex;
  margin-top: 22px;
  overflow: hidden;
  padding-bottom: 14px;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
}
.hcase-flow {
  display: flex; gap: 16px; flex: none;
  padding-inline-end: 16px;
}
html.motion-on .hcase-flow { animation: hcase-drift 46s linear infinite; }
@keyframes hcase-drift { from { transform: translate3d(0, 0, 0); } to { transform: translate3d(-100%, 0, 0); } }
html.motion-on .hcase-rail:hover .hcase-flow,
html.motion-on .hcase-rail:focus-within .hcase-flow { animation-play-state: paused; }
html[dir="rtl"].motion-on .hcase-flow { animation-direction: reverse; }
@media (prefers-reduced-motion: reduce) {
  .hcase-flow { animation: none !important; }
  .hcase-rail { overflow-x: auto; -webkit-mask-image: none; mask-image: none; }
  .hcase-rail .hcase-flow[aria-hidden="true"] { display: none; }
}
.hcase-card {
  flex: 0 0 clamp(240px, 30vw, 380px);
  border: 1px solid var(--line); border-radius: 16px;
  background: var(--noir-panel);
  overflow: hidden; text-decoration: none; color: var(--ink);
  transition: border-color var(--dur-ui) var(--ease-fluid), transform var(--dur-ui) var(--ease-fluid);
}
.hcase-card:hover { border-color: rgba(241, 239, 232, 0.4); transform: translateY(-4px); }
.hcase-shot { display: block; aspect-ratio: 16 / 10; overflow: hidden; }
.hcase-shot img {
  width: 100%; height: 100%; object-fit: cover; object-position: top;
  transition: transform 0.65s cubic-bezier(0.22, 1, 0.36, 1);
}
html.motion-on .hcase-card:hover .hcase-shot img { transform: scale(1.05); }
.hcase-meta { padding: 14px 16px 16px; }
.hcase-meta .card-tag { font-size: 0.68rem; }
.hcase-meta h3 { font-size: 1.05rem; margin-top: 6px; }
.hcase-meta p { margin-top: 4px; font-size: 0.8rem; color: var(--muted); }
html.motion-off .hcase-card, html.motion-off .hcase-shot img { transition: none; }

/* ---- the service index: numbered editorial rows (home) ----
   Short, loud, no 3D: giant names on hairlines; hovering a row slides
   the title, turns it orange, reveals the one-line description and
   answers with the arrow. Touch and motion-off get everything visible. */
.svc-list { border-bottom: 1px solid var(--line); }
.svc-row {
  display: grid;
  grid-template-columns: 3.2rem minmax(0, 1fr) auto;
  align-items: center; column-gap: 18px;
  padding-block: clamp(16px, 2.4vw, 26px);
  border-top: 1px solid var(--line);
  text-decoration: none; color: var(--ink);
}
.svc-num {
  font-family: var(--font-display);   /* was "Space Grotesk" first — a whole 22 KB webfont for four micro-labels at 0.7rem, where it is indistinguishable from General Sans */
  font-size: 0.78rem; font-weight: 700; letter-spacing: 0.14em;
  color: var(--muted);
}
.svc-name {
  display: block;
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 3.6vw, 2.9rem);
  font-weight: 600; letter-spacing: -0.03em; line-height: 1.05;
  transition: transform 0.5s cubic-bezier(0.22, 1, 0.36, 1),
              color 0.5s cubic-bezier(0.22, 1, 0.36, 1);
}
.svc-desc {
  display: grid; grid-template-rows: 1fr;
  color: var(--muted); font-size: 0.92rem; line-height: 1.55;
  max-width: 56ch;
}
.svc-desc > span { overflow: hidden; }
.svc-go {
  font-size: clamp(1.3rem, 2.2vw, 1.9rem);
  color: var(--muted);
  transition: transform 0.5s cubic-bezier(0.22, 1, 0.36, 1),
              color 0.5s cubic-bezier(0.22, 1, 0.36, 1);
}
/* fine pointers: descriptions rest closed, the hovered row opens */
@media (hover: hover) and (pointer: fine) {
  html.motion-on .svc-desc {
    grid-template-rows: 0fr;
    opacity: 0;
    transition: grid-template-rows 0.55s cubic-bezier(0.22, 1, 0.36, 1),
                opacity 0.55s cubic-bezier(0.22, 1, 0.36, 1),
                margin-top 0.55s cubic-bezier(0.22, 1, 0.36, 1);
    margin-top: 0;
  }
  html.motion-on .svc-row:hover .svc-desc,
  html.motion-on .svc-row:focus-visible .svc-desc {
    grid-template-rows: 1fr; opacity: 1; margin-top: 8px;
  }
  html.motion-on .svc-row:hover .svc-name,
  html.motion-on .svc-row:focus-visible .svc-name {
    transform: translateX(10px); color: var(--orange);
  }
  html[dir="rtl"].motion-on .svc-row:hover .svc-name { transform: translateX(-10px); }
  html.motion-on .svc-row:hover .svc-go {
    transform: translate(4px, -4px); color: var(--orange);
  }
}
.svc-desc { margin-top: 6px; }
@media (max-width: 700px) {
  .svc-row { grid-template-columns: 2.2rem minmax(0, 1fr) auto; column-gap: 10px; }
}
/* post 404 state keeps the article headline scale without a second H1 */
.post-missing-title {
  font-size: clamp(2rem, 5vw, 3.4rem);
  letter-spacing: -0.035em; line-height: 1.05;
}

/* ---- the cinematic pass: one quiet grid field on every hero ----
   The playful solids are gone. Heroes carry a faint monochrome grid
   that drifts imperceptibly — slow, small, smooth — and the home page
   keeps the wireframe infinity as the single signature effect. */
.hero-ledger, .article-head, .lesson-head, body.paper .paper-hero, .v2-hero {
  position: relative;
}
.hero-ledger::before, .article-head::before, .lesson-head::before,
body.paper .paper-hero::before, .v2-hero::before {
  content: ""; position: absolute; inset: -40px 0 0;
  pointer-events: none; z-index: 0;
  background-image:
    linear-gradient(rgba(241, 239, 232, 0.05) 1px, transparent 1px),
    linear-gradient(90deg, rgba(241, 239, 232, 0.05) 1px, transparent 1px);
  background-size: 72px 72px;
  -webkit-mask-image: radial-gradient(120% 90% at 50% 10%, #000 40%, transparent 100%);
  mask-image: radial-gradient(120% 90% at 50% 10%, #000 40%, transparent 100%);
}
html.motion-on .hero-ledger::before, html.motion-on .article-head::before,
html.motion-on .lesson-head::before, html.motion-on body.paper .paper-hero::before,
html.motion-on .v2-hero::before {
  animation: grid-drift 90s linear infinite alternate;
}
@keyframes grid-drift { from { transform: translate3d(0, 0, 0); } to { transform: translate3d(-36px, -24px, 0); } }
@media (prefers-reduced-motion: reduce) {
  .hero-ledger::before, .article-head::before, .lesson-head::before,
  body.paper .paper-hero::before, .v2-hero::before { animation: none !important; }
}
/* hero children stack above the grid */
.hero-ledger > *, .article-head > *, .lesson-head > *, .v2-hero > * { position: relative; z-index: 1; }

/* the last two-card hero becomes a single typographic statement */
.v2-hero--type { grid-template-columns: 1fr; }
.v2-hero--type .hero-copy-card { max-width: 900px; }

/* with the solids gone, the infinity wire is the hero — let it read */
.inf-sig { opacity: 0.62; }

/* ---- socials chip: WhatsApp + Instagram only, real brand colours,
   a size up. The old orange drop-shadow hover was glow — retired. ---- */
.chip-socials .soc, .chip-socials svg { width: 26px; height: 26px; }
.chip-socials a, .chip-socials .social-soon { color: inherit; }
.chip-socials a:hover { filter: none; }
.chip-socials .social-soon--live { opacity: 1; }
html.motion-on .chip-socials a svg,
html.motion-on .chip-socials .social-soon svg {
  transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1);
}
html.motion-on .chip-socials a:hover svg { transform: scale(1.12); }

/* ---- service rows: entrance choreography ----
   The row itself doesn't fade as one block — instead the hairline
   draws across, then number, name and arrow rise in sequence as the
   row scrolls into view. Transform/opacity only; off = all visible. */
html.motion-on .svc-row.reveal { opacity: 1; transform: none; }
.svc-row { border-top: 0; position: relative; }
.svc-row::before {
  content: ""; position: absolute; top: 0; left: 0; right: 0; height: 1px;
  background: var(--line);
  transform: scaleX(0); transform-origin: 0 50%;
}
html[dir="rtl"] .svc-row::before { transform-origin: 100% 50%; }
html.motion-off .svc-row::before { transform: scaleX(1); }
html.motion-on .svc-row.reveal::before {
  transition: transform 0.8s cubic-bezier(0.22, 1, 0.36, 1);
  transition-delay: var(--reveal-delay, 0ms);
}
html.motion-on .svc-row.reveal.in-view::before { transform: scaleX(1); }
html.motion-on .svc-row.reveal .svc-num,
html.motion-on .svc-row.reveal .svc-main,
html.motion-on .svc-row.reveal .svc-go {
  opacity: 0; transform: translateY(16px);
  transition: opacity 0.65s cubic-bezier(0.22, 1, 0.36, 1),
              transform 0.65s cubic-bezier(0.22, 1, 0.36, 1);
  transition-delay: calc(var(--reveal-delay, 0ms) + 100ms);
}
html.motion-on .svc-row.reveal .svc-main { transition-delay: calc(var(--reveal-delay, 0ms) + 170ms); }
html.motion-on .svc-row.reveal .svc-go { transition-delay: calc(var(--reveal-delay, 0ms) + 240ms); }
html.motion-on .svc-row.reveal.in-view .svc-num,
html.motion-on .svc-row.reveal.in-view .svc-main,
html.motion-on .svc-row.reveal.in-view .svc-go { opacity: 1; transform: translateY(0); }
@media (prefers-reduced-motion: reduce) {
  .svc-row::before { transform: scaleX(1) !important; transition: none !important; }
  .svc-row .svc-num, .svc-row .svc-main, .svc-row .svc-go {
    opacity: 1 !important; transform: none !important; transition: none !important;
  }
}

/* ---- the home service deck: sticky cards stacking on scroll ----
   Each card pins below the one before it; scrolling stacks the deck,
   edges peeking. Flat panels, hairlines, zero 3D. Where the browser
   supports scroll-driven animations the covered card eases back a
   touch; everywhere else the pure sticky stack carries it. */
.svc-deck { position: relative; }
.svc-card {
  position: sticky;
  top: calc(84px + var(--i, 0) * 18px);
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: end;
  gap: 18px;
  min-height: clamp(300px, 46vh, 430px);
  margin-bottom: 22px;
  padding: clamp(22px, 3vw, 40px);
  background: var(--noir-panel);
  border: 1px solid rgba(241, 239, 232, 0.22);
  border-radius: 22px;
  text-decoration: none; color: var(--ink);
  overflow: hidden;
}
.svc-card:last-child { margin-bottom: 0; }
.svc-card-num {
  position: absolute; top: clamp(14px, 2vw, 26px); inset-inline-end: clamp(18px, 2.6vw, 34px);
  font-family: var(--font-serif); font-style: italic;
  font-size: clamp(3.4rem, 7vw, 6rem); line-height: 1;
  color: rgba(241, 239, 232, 0.12);
}
.svc-card-body { display: block; max-width: 62ch; position: relative; }
.svc-card-name {
  display: block;
  font-family: var(--font-display);
  font-size: clamp(2rem, 4.6vw, 3.8rem);
  font-weight: 600; letter-spacing: -0.035em; line-height: 1.02;
  transition: color 0.5s cubic-bezier(0.22, 1, 0.36, 1);
}
.svc-card-desc {
  display: block; margin-top: 12px;
  color: var(--muted); font-size: clamp(0.95rem, 1.2vw, 1.1rem); line-height: 1.6;
}
.svc-card-go { margin-top: 18px; display: inline-flex; }
.svc-card:hover .svc-card-name { color: var(--orange); }
.svc-card:hover .svc-card-go { border-color: var(--orange); color: var(--orange); }

/* scroll-driven polish: the card settles back slightly as it is covered */
@supports (animation-timeline: view()) {
  html.motion-on .svc-card {
    animation: svc-settle linear both;
    animation-timeline: view();
    animation-range: exit 0% exit 100%;
  }
  @keyframes svc-settle {
    to { transform: scale(0.96) translateY(-8px); }
  }
  html.motion-off .svc-card { animation: none; }
  @media (prefers-reduced-motion: reduce) {
    .svc-card { animation: none !important; }
  }
}
@media (max-width: 700px) {
  .svc-card { top: calc(70px + var(--i, 0) * 10px); min-height: 300px; }
  .svc-card-num { font-size: 2.6rem; }
}

/* ============================================================
   THE PROJECT SHOWCASE (projects page) — a scrolled sequence.
   Each case is a full composition on the dark ground, not a box:
   the frame unveils with a wipe as it arrives, the copy rises
   behind it, and the frame drifts a few pixels against the page.
   The screenshot itself is never scaled or shifted — it stays
   whole inside its window (owner rule).
   ============================================================ */
body.paper .case-list { gap: 0; }
body.paper .case-card {
  background: transparent;
  border: 0;
  border-top: 1px solid var(--line);
  border-radius: 0;
  box-shadow: none;
  align-items: start;
  padding: clamp(38px, 6vw, 96px) 0;
  transform: none;
}
body.paper .case-card:first-child { border-top: 0; padding-top: clamp(18px, 2vw, 32px); }
body.paper .case-card:hover { transform: none; border-color: var(--line); }

/* the index number is not shown on the cases (owner order) */
body.paper .case-num { display: none; }
body.paper .case-num--unused {
  font-family: var(--font-serif); font-style: italic; font-weight: 400;
  font-size: clamp(3rem, 6vw, 5.2rem); line-height: 1;
  color: rgba(241, 239, 232, 0.16);
  -webkit-text-stroke: 0; opacity: 1; margin-bottom: 10px;
}
body.paper .case-copy h3 { font-size: clamp(1.7rem, 3.2vw, 2.9rem); letter-spacing: -0.03em; }

/* the copy column holds while the media passes it */
@media (min-width: 1000px) {
  body.paper .case-copy { position: sticky; top: 96px; }
}

/* ---- the wipe: the frame unveils bottom-to-top ---- */
.case-reveal .shot-frame { transform: translate3d(0, var(--frame-y, 0px), 0); }
html.motion-on .case-reveal .shot-frame {
  clip-path: inset(0 0 100% 0);
  transition: clip-path 1.05s cubic-bezier(0.22, 1, 0.36, 1),
              transform 0.2s linear;
}
html.motion-on .case-reveal.is-in .shot-frame { clip-path: inset(0 0 0 0); }

/* the screenshot never moves inside its window — the frame does */
body.paper .case-media:hover .shot-frame img,
body.paper .case-media:focus-visible .shot-frame img { transform: none; }
body.paper .shot-frame {
  transition: border-color 0.5s cubic-bezier(0.22, 1, 0.36, 1),
              box-shadow 0.5s cubic-bezier(0.22, 1, 0.36, 1);
}
body.paper .case-media:hover .shot-frame {
  border-color: rgba(241, 239, 232, 0.45);
  box-shadow: 0 26px 70px rgba(0, 0, 0, 0.62);
}

/* ---- the copy rises behind the wipe ---- */
html.motion-on .case-reveal .case-copy > * {
  opacity: 0; transform: translateY(20px);
  transition: opacity 0.7s cubic-bezier(0.22, 1, 0.36, 1),
              transform 0.7s cubic-bezier(0.22, 1, 0.36, 1);
}
html.motion-on .case-reveal .case-copy > :nth-child(1) { transition-delay: 0.10s; }
html.motion-on .case-reveal .case-copy > :nth-child(2) { transition-delay: 0.17s; }
html.motion-on .case-reveal .case-copy > :nth-child(3) { transition-delay: 0.24s; }
html.motion-on .case-reveal .case-copy > :nth-child(4) { transition-delay: 0.31s; }
html.motion-on .case-reveal .case-copy > :nth-child(5) { transition-delay: 0.38s; }
html.motion-on .case-reveal .case-copy > :nth-child(6) { transition-delay: 0.45s; }
html.motion-on .case-reveal.is-in .case-copy > * { opacity: 1; transform: none; }

/* reduced motion: everything present, nothing moves. The reel holds on its
   first slide and the dots stay live, so the other screenshots are still
   reachable — just never on a timer. */
@media (prefers-reduced-motion: reduce) {
  .case-reveal .shot-frame { clip-path: none !important; transform: none !important; transition: none !important; }
  .case-reveal .case-copy > * { opacity: 1 !important; transform: none !important; transition: none !important; }
  .shot-frame .shot-slide { transition: none !important; }
}

/* ---- the loader, redesigned: mark, one line, out ----
   No percentage, no terminal log. The wordmark settles, a hairline
   draws under it for the length of the hold, and the curtain lifts. */
#si-pre .si-core {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 0; text-align: center;
}
#si-pre .si-logo { width: min(260px, 62vw); height: auto; }
#si-pre .si-title {
  margin-top: 16px;
  font-family: var(--font-display);   /* was "Space Grotesk" first — a whole 22 KB webfont for four micro-labels at 0.7rem, where it is indistinguishable from General Sans */
  font-size: 0.68rem; font-weight: 500; letter-spacing: 0.3em;
  text-transform: uppercase; color: rgba(241, 239, 232, 0.55);
}
#si-pre .si-line {
  width: min(240px, 56vw); height: 1px; margin-top: 26px;
  background: rgba(241, 239, 232, 0.16); overflow: hidden;
}
#si-pre .si-line span {
  display: block; height: 100%; width: 100%;
  background: var(--orange);
  transform: scaleX(0); transform-origin: 0 50%;
  animation: si-draw 0.95s cubic-bezier(0.22, 1, 0.36, 1) forwards;
}
html.si-quick #si-pre .si-line span { animation-duration: 0.42s; }
@keyframes si-draw { to { transform: scaleX(1); } }
@media (prefers-reduced-motion: reduce) {
  #si-pre .si-line span { animation: none; transform: scaleX(1); }
}

/* ============================================================
   BLAZE, ALIVE — the dragon breathes, his ember pulses and his
   flame flickers. Three loops at different, non-multiple speeds
   so they drift out of phase and never look mechanical. Hovering
   wakes him: the breath quickens and the flame roars. Transform
   and opacity only; he holds perfectly still under reduced motion.
   ============================================================ */
html.motion-on .agent-dragon .bzx {
  transform-origin: 62% 56%;
  animation: bz-breathe 4.6s ease-in-out infinite;
  will-change: transform;
}
@keyframes bz-breathe {
  0%, 100% { transform: scale(1) translateY(0) rotate(0deg); }
  50%      { transform: scale(1.04) translateY(-2.5px) rotate(-1.4deg); }
}

/* the flame: quick, irregular, alive */
html.motion-on .bzx-flame {
  transform-box: fill-box; transform-origin: 8% 62%;
  animation: bz-flicker 780ms ease-in-out infinite alternate;
}
@keyframes bz-flicker {
  0%   { transform: scale(0.82) rotate(-5deg); opacity: 0.72; }
  40%  { transform: scale(1.08) rotate(3deg);  opacity: 1; }
  100% { transform: scale(0.93) rotate(-1deg); opacity: 0.88; }
}

/* the ember in his chest, slower than the breath */
html.motion-on .bzx-core {
  transform-box: fill-box; transform-origin: center;
  animation: bz-ember 3.1s ease-in-out infinite;
}
@keyframes bz-ember {
  0%, 100% { opacity: 0.7;  transform: scale(0.95); }
  50%      { opacity: 1;    transform: scale(1.07); }
}

/* the roar — he reacts to you */
html.motion-on .agent-launcher:hover .bzx,
html.motion-on .agent-launcher:focus-visible .bzx { animation-duration: 2.3s; }
html.motion-on .agent-launcher:hover .bzx-flame,
html.motion-on .agent-launcher:focus-visible .bzx-flame {
  animation-duration: 380ms;
  transform-origin: 6% 62%;
}

@media (prefers-reduced-motion: reduce) {
  .agent-dragon .bzx, .bzx-flame, .bzx-core { animation: none !important; }
}

/* the frame's title bar is a fixed band, so every case window ends up
   exactly the same height as well as the same width */
body.paper .shot-bar { height: 44px; padding-block: 0; flex: 0 0 auto; }
/* stack the bar and the shot with no stray line-box space between them */
body.paper .shot-frame { display: flex; flex-direction: column; line-height: 0; }
body.paper .shot-frame .shot-bar { line-height: 1; }
/* the reel is one more band in that stack — its own aspect ratio sets its
   height, so it must not be allowed to shrink or stretch against the bar */
body.paper .shot-frame .shot-reel { flex: 0 0 auto; }

/* ---- the hero's live badge: today's AI headline, one quiet line ----
   A hairline pill above the kicker. The dot breathes so the word LIVE
   is earned; everything else is flat, per the house rule. */
.live-badge {
  /* block-level so it sits on its own line above the kicker */
  display: flex; width: fit-content; align-items: center; gap: 10px;
  max-width: 100%; margin-bottom: 18px;
  padding: 7px 14px 7px 12px;
  border: 1px solid var(--line); border-radius: 999px;
  text-decoration: none; color: var(--ink);
  font-size: 0.74rem; line-height: 1.2;
  transition: border-color var(--dur-ui) var(--ease-fluid), color var(--dur-ui) var(--ease-fluid);
}
.live-badge:hover { border-color: var(--orange); }
.live-dot {
  flex: none; width: 7px; height: 7px; border-radius: 50%;
  background: var(--orange);
}
html.motion-on .live-dot { animation: live-pulse 2s ease-in-out infinite; }
@keyframes live-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%      { opacity: 0.45; transform: scale(0.72); }
}
.live-tag {
  flex: none;
  font-family: var(--font-display);   /* was "Space Grotesk" first — a whole 22 KB webfont for four micro-labels at 0.7rem, where it is indistinguishable from General Sans */
  font-weight: 700; letter-spacing: 0.16em; text-transform: uppercase;
  font-size: 0.62rem; color: var(--orange);
}
.live-text {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  color: var(--muted);
}
.live-badge:hover .live-text { color: var(--ink); }
.live-go { flex: none; color: var(--muted); }
.live-badge:hover .live-go { color: var(--orange); }
@media (max-width: 700px) {
  .live-badge { font-size: 0.68rem; gap: 8px; }
  .live-text { max-width: 46vw; }
}
@media (prefers-reduced-motion: reduce) { .live-dot { animation: none; } }
