/* =====================================================================
 SIVRA STUDIOS — sivra.css: the custom code (every style the Webflow Designer cannot express with classes, plus the motion
 states each effect needs). Loads LAST: tokens > base > utilities > components > pages > sivra, in the demo and in Webflow
 (Site settings > Custom code > Head code, generated into webflow/custom-code/ by site-src/handoff.py).
 Pair: js/sivra.js (one IIFE, section numbers in brackets below). Palette: Rich Black #010B13 · Powder White #EBF1F5 · Blue Slate #536878.
 Rules: transitions + GSAP only (no keyframes) · Webflow breakpoints (991 / 767 / 479) plus the prefers-reduced-motion / hover /
 pointer feature queries · one reduced-motion block (§20).
 HOVER CONTRACT (owner audit 2026-10-07, effects never stack, one per element):
   pills (.button:not(.is-text), .sv-cta)              = PULSE only
   text CTAs (.button.is-text, [data-roll] labels)      = LETTER ROLL only + the arrow nudge
   nav + plain links (nav, footer, contact details, story nav, rich text) = UNDERLINE only (2px; menu overlay 3px)
 Theme variables (--ink, --paper, --accent, --bg-color, --txt-color, --underline …) come from tokens.css.

 CONTENTS
   1  Global: box-sizing, icon fill, selection, placeholder, focus ring
   2  Buttons + pill pulse ..................... sivra.js §4
   3  Underline links ......................... footer / story nav / contact / menu email (CSS only)
   4  Accents: radio dots, map pin
   5  Nav: bar + scrolled pill ................. sivra.js §1 (+ light swap §2)
   6  Menu overlay + image marquee ............. sivra.js §3
   7  Letter roll .............................. sivra.js §6
   8  Cursor pill .............................. sivra.js §7
   9  Hero reel ................................ sivra.js §8 (media gate) + §9 (scroll timeline)
  10  Bento grid + hover pill + zoom
  11  Service row ink wipe
  12  Card hover zooms · arrow nudges · logo hover
  13  FAQ open state ........................... sivra.js §11
  14  Case TOC + case sections + carousel ...... sivra.js §14 + §20
  15  Testimonial dots ......................... sivra.js §15
  16  Form states .............................. sivra.js §16
  17  Theme context (client / dark / contact page)
  18  Rich text nested tags
  19  Breakpoints (991 / 767 / 479)
  20  Reduced motion (the one block)
  21  Services Stack: card tones + pinned stack .. sivra.js §26
 Hooks (never rename; sivra.js reads them): see design-system/motion.md.
 ===================================================================== */


/* ---------- 1. GLOBAL ----------
 Zero-specificity :where() for the global focus ring / placeholder so every component rule (radius, outline) still wins. */
*, *::before, *::after { box-sizing: border-box; }
svg:not([fill]) { fill: currentColor; } /* stroke icons declare fill="none" themselves */
::selection { background: var(--sv-accent); color: var(--sv-paper); }
::placeholder { color: inherit; opacity: .5; }
:where(:focus) { outline: none; }
:where(:focus-visible) { outline: 2px solid var(--sv-accent); outline-offset: 4px; border-radius: 4px; }


/* ---------- 2. BUTTONS + PILL PULSE ----------
 Hover bloom in the page colour (outlined pill: in the text colour; nav CTA: paper; menu CTA: accent). sivra.js §4 tweens two
 custom properties with GSAP (--pulse-s 0 to 1.2, --pulse-o .35 to 0, .8s power2.out) — no keyframes.
 NO TEXT SHIFT ON HOVER: the pill is its own GPU layer up front so the label never re-rasterises (≈0.5px jump in Safari). */
.button, .sv-cta {
 position: relative; overflow: hidden; isolation: isolate; /* keeps the bloom inside the pill, behind the label */
 transform: translateZ(0); backface-visibility: hidden; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
}
.button::before, .sv-cta::before {
 content: ""; position: absolute; top: 50%; left: 50%; width: 120%; padding-bottom: 120%; /* circle sized on the pill width */
 border-radius: 50%; z-index: -1; pointer-events: none; will-change: transform, opacity; /* layer exists before hover */
 transform: translate(-50%, -50%) scale(var(--pulse-s, 0)); opacity: var(--pulse-o, 0);
 background: var(--bg-color);
}
.sv-cta::before { background: var(--sv-paper); }
.button.is-secondary::before { background: currentColor; }
.button.is-text::before, .button.is-media-pill::before { display: none; } /* text CTAs letter-roll; media pills have their own hover (§12, sivra.js §24) */
.button:hover, .button:focus-visible { transform: translateZ(0); } /* keep the label perfectly still */
.button[disabled], .button[aria-disabled="true"] { opacity: .4; pointer-events: none; }
.icon-arrow { width: .45em; height: auto; flex: none; }


/* ---------- 3. UNDERLINE LINKS (2px) ----------
 Footer / story nav / contact-detail / menu email: hidden at rest, in from the left on hover, out to the right on leave (CSS only).
 Nav links §5, menu links §6 (3px), rich text §18. */
.footer_link::after, .story-nav_link::after, .contact-detail_link::after, .sv-menu-email::after {
 content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: var(--underline, 2px); background: var(--sv-accent);
 transform: scaleX(0); transform-origin: right; transition: transform .35s var(--sv-ease-out);
}
.footer_link:hover::after, .story-nav_link:hover::after, .contact-detail_link:hover::after, .sv-menu-email:hover::after,
.footer_link:focus-visible::after, .story-nav_link:focus-visible::after, .contact-detail_link:focus-visible::after, .sv-menu-email:focus-visible::after {
 transform: scaleX(1); transform-origin: left;
}


/* ---------- 4. ACCENTS (Blue Slate; ::selection is §1) ---------- */
/* form radio + multi-select checkbox: the same dot style, no square checkboxes anywhere */
.radio-option input[type="radio"],
.radio-option input[type="checkbox"] {
 appearance: none; width: 20px; height: 20px; border: 1px solid currentColor; border-radius: 50%;
 display: inline-grid; place-content: center; margin: 0 .5em 0 0;
}
.radio-option input:checked::before { content: ""; width: 12px; height: 12px; border-radius: 50%; background: var(--accent); }
.radio-option input:checked { border-color: var(--accent); }
.map-pin { width: 34px; height: 34px; border-radius: 50%; border: 12px solid var(--accent); background: var(--ink); box-sizing: border-box; } /* Location block */


/* ---------- 5. NAV — one bar that contracts inward on scroll into a glass pill (sivra.js §1 adds .is-scrolled past 20px) ----------
 Load:   [ logo Results Services Work Studio Journal Contact Get Started ] transparent, full width
 Scroll: the same bar shrinks to a centred glass pill [ logo ≡ Menu Get Started ] (max-width 1312 to 640, .6s; links fade out,
 Menu fades in). sivra.js §2 adds .is-light while a [data-nav-light] section sits under the bar. */
.sv-nav, .sv-nav *, .sv-menu, .sv-menu * { box-sizing: border-box; }
.sv-nav { position: fixed; inset: 0 0 auto 0; z-index: 999; display: flex; justify-content: center; pointer-events: none; }
.sv-bar {
 pointer-events: auto;
 width: calc(100% - 128px); max-width: 1312px; height: 72px; margin-top: 16px;
 display: flex; align-items: center; justify-content: space-between; gap: 24px;
 padding: 0 8px 0 24px; border-radius: 100px;
 background: transparent; border: 1px solid transparent;
 backdrop-filter: blur(0); -webkit-backdrop-filter: blur(0);
 color: var(--txt-color);
 transition: max-width .6s cubic-bezier(.65, 0, .35, 1), width .6s cubic-bezier(.65, 0, .35, 1), height .6s cubic-bezier(.65, 0, .35, 1),
 background-color .45s ease, color .45s ease, border-color .45s ease, backdrop-filter .45s ease, padding .6s cubic-bezier(.65, 0, .35, 1);
}
.sv-logo { display: inline-flex; align-items: center; color: inherit; text-decoration: none; flex: none; }
.sv-logo svg, .sv-menu-logo svg { display: block; height: 26px; width: auto; }
.sv-links { display: flex; gap: 32px; list-style: none; margin: 0; padding: 0; overflow: hidden; max-width: 900px; opacity: 1;
 transition: max-width .6s cubic-bezier(.65, 0, .35, 1), opacity .3s ease; }
.sv-links a { position: relative; color: inherit; text-decoration: none; font: 500 15px/1.3 var(--sv-font); padding: 4px 0; white-space: nowrap; }
.sv-links a::after { /* 2px Blue Slate underline: in from the left, out to the right; stays on the current page */
 content: ""; position: absolute; left: 0; right: 0; bottom: -4px; height: var(--underline, 2px); background: var(--sv-accent);
 transform: scaleX(0); transform-origin: right; transition: transform .35s cubic-bezier(.25, 1, .5, 1);
}
.sv-links a:hover::after, .sv-links a:focus-visible::after, .sv-links a[aria-current="page"]::after, .sv-links a.is-parent-current::after, .sv-links a.w--current::after { transform: scaleX(1); transform-origin: left; }
.sv-right { display: flex; align-items: center; gap: 8px; flex: none; } /* Menu (hidden on load) + Get Started */
.sv-menu-btn {
 display: flex; align-items: center; gap: 8px; background: none; border: 0; cursor: pointer;
 color: inherit; font: 500 15px/1.3 var(--sv-font); max-width: 0; opacity: 0; overflow: hidden; padding: 10px 0;
 transition: max-width .5s cubic-bezier(.65, 0, .35, 1), opacity .3s ease, padding .5s cubic-bezier(.65, 0, .35, 1);
}
.sv-menu-icon { position: relative; display: block; width: 20px; height: 8px; flex: none; }
.sv-menu-icon::before, .sv-menu-icon::after { content: ""; position: absolute; left: 0; width: 20px; height: 2px; background: currentColor; border-radius: 1px; }
.sv-menu-icon::before { top: 0; } .sv-menu-icon::after { top: 6px; }
.sv-menu-btn:focus-visible { outline: 2px solid var(--sv-accent); outline-offset: 4px; border-radius: 4px; }
.sv-cta { /* compact Blue Slate "Get Started" pill; pulse in §2 */
 display: inline-flex; align-items: center; gap: 6px; padding: 12px 20px; border-radius: 100px; text-decoration: none;
 background: var(--sv-accent); color: var(--sv-paper); font: 500 14px/1.3 var(--sv-font);
}
.sv-cta .icon-arrow { width: 6px; height: 11px; }
.sv-nav.is-scrolled .sv-bar {
 max-width: 640px; height: 60px; padding: 0 6px 0 20px;
 background: var(--sv-ink-80); color: var(--sv-paper); border-color: var(--sv-paper-12);
 backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
}
.sv-nav.is-scrolled .sv-links { max-width: 0; opacity: 0; pointer-events: none; }
.sv-nav.is-scrolled .sv-menu-btn { max-width: 120px; opacity: 1; padding: 10px 12px; }
.sv-nav.is-light .sv-bar { color: var(--sv-paper); } /* dark / client-coloured section under the bar */


/* ---------- 6. MENU OVERLAY — full screen, drops from the top (sivra.js §3) ----------
 Left: six links (3px underline). Right (≥ 992): two columns of 4:5 work tiles drifting in opposite directions while the menu is
 open (Webflow: each .sv-mq-col = a Collection List of project covers; sivra.js clones the tiles once for the seamless loop). */
.sv-menu { position: fixed; inset: 0; z-index: 99999; display: none; flex-direction: column; background: var(--sv-ink); color: var(--sv-paper); overflow-y: auto; padding: 32px 0 40px; }
.sv-menu.is-open { display: flex; }
.sv-menu-container { width: 100%; max-width: 1440px; margin: 0 auto; padding: 0 64px; }
.sv-menu-top { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; padding-bottom: 56px; }
.sv-menu-close { justify-self: start; width: 64px; height: 64px; border-radius: 50%; border: 0; cursor: pointer; background: var(--sv-paper-10); color: inherit; display: grid; place-items: center; }
.sv-menu-close svg { width: 20px; height: 20px; }
.sv-menu-logo { display: inline-flex; align-items: center; color: inherit; text-decoration: none; }
.sv-menu-top .sv-cta { justify-self: end; background: var(--sv-paper); color: var(--sv-ink); }
.sv-menu-top .sv-cta::before { background: var(--sv-accent); }
.sv-menu-inner { display: flex; gap: 10%; align-items: flex-start; }
.sv-menu-left { width: 55%; }
.sv-menu-links { display: flex; flex-direction: column; gap: 26px; list-style: none; margin: 0; padding: 0; }
.sv-menu-item { display: inline-flex; flex-direction: column; gap: 10px; align-self: flex-start; }
.sv-menu-link {
 color: var(--sv-paper-55); text-decoration: none; display: inline-flex; align-items: center; gap: 24px;
 font: 500 clamp(40px, 4.4vw, 64px)/1 var(--sv-font); letter-spacing: -.03em; transition: color .25s cubic-bezier(.55, 0, 1, .45);
}
.icon-arrow.is-menu { width: .45em; height: .45em; }
.sv-menu-underline { display: block; height: var(--underline-lg, 3px); width: 100%; background: var(--sv-accent); transform: scaleX(0); transform-origin: right; transition: transform .35s cubic-bezier(.25, 1, .5, 1); }
.sv-menu-item:hover .sv-menu-link, .sv-menu-item:focus-within .sv-menu-link, .sv-menu-link[aria-current="page"], .sv-menu-link.is-parent-current { color: var(--sv-paper); transition: color .25s cubic-bezier(.33, 1, .68, 1); }
.sv-menu-item:hover .sv-menu-underline, .sv-menu-item:focus-within .sv-menu-underline, .sv-menu-item.is-current .sv-menu-underline { transform: scaleX(1); transform-origin: left; }
.sv-menu-right {
 width: 35%; height: min(68vh, 760px); overflow: hidden; display: flex; gap: 16px; align-items: flex-start;
 -webkit-mask-image: linear-gradient(to bottom, transparent, var(--sv-ink) 12%, var(--sv-ink) 88%, transparent);
 mask-image: linear-gradient(to bottom, transparent, var(--sv-ink) 12%, var(--sv-ink) 88%, transparent);
}
.sv-mq-col { flex: 1 1 0; display: flex; flex-direction: column; gap: 16px; will-change: transform; }
.sv-mq-tile { flex: none; aspect-ratio: 4 / 5; border-radius: 12px; overflow: hidden; background: var(--sv-paper-08); position: relative; }
.sv-mq-tile .placeholder_label { color: var(--sv-paper); }
.sv-mq-tile img { width: 100%; height: 100%; object-fit: cover; display: block; }
.sv-menu-bottom { display: flex; justify-content: space-between; align-items: end; padding-top: 56px; }
.sv-menu-email { position: relative; color: inherit; text-decoration: none; font: 500 15px var(--sv-font); padding-bottom: 8px; } /* underline: §3 */
.sv-menu-legal { font: 500 13px var(--sv-font); letter-spacing: .04em; text-transform: uppercase; opacity: .8; }


/* ---------- 7. LETTER ROLL — text CTAs only (sivra.js §6 splits [data-roll] into .roll › .roll-w › .roll-c) ----------
 Markup: <a class="button is-text" href="#"><span class="button_label" data-roll>All results</span>[arrow]</a>
 Hover on the label or ANY link around it (a whole card included): letters rise one by one left to right; leave: they drop back
 left to right. */
.roll { display: inline-block; position: relative; overflow: hidden; line-height: 1.1; vertical-align: bottom; }
.roll .roll-w { display: inline-block; white-space: pre; }
.roll .roll-c { display: inline-block; position: relative; transform: translateY(0); transition: transform .45s cubic-bezier(.65, 0, .35, 1); transition-delay: calc(var(--i) * 22ms); }
.roll .roll-c::after { content: attr(data-c); position: absolute; left: 0; top: 100%; } /* the copy that rises in */
.roll:hover .roll-c, a:hover .roll .roll-c, a:focus-visible .roll .roll-c { transform: translateY(-100%); }


/* ---------- 8. CURSOR PILL — "● Read article" follower (sivra.js §7; fine pointers, motion allowed) ----------
 Add data-cursor-hover + data-cursor-text="Read article" (or "View project", "Read the case study") to any card.
 Markup once per page: <div class="cursor" aria-hidden="true"><div class="cursor-bubble"><span class="cursor-dot"></span><span class="cursor-text"></span></div></div> */
.cursor { position: fixed; top: 0; left: 0; z-index: 25; pointer-events: none; padding: 1em 1em 0 .5em; will-change: transform; }
.cursor-bubble {
 display: flex; align-items: center; gap: .5rem; height: 2.5em; padding: 0 .9rem; border-radius: 999px;
 background: var(--sv-paper); color: var(--sv-ink); font: 500 15px/1 var(--sv-font); white-space: nowrap;
 opacity: 0; transform: scale(.8); transition: opacity .25s ease, transform .35s cubic-bezier(.25, 1, .5, 1);
}
.cursor.is-on .cursor-bubble { opacity: 1; transform: scale(1); }
.cursor-dot { width: .75rem; height: .75rem; border-radius: 50%; background: var(--sv-accent); flex: none; }
[data-cursor-hover] { cursor: pointer; }
@media (hover: none), (pointer: coarse) { .cursor { display: none; } }


/* ---------- 9. HERO REEL — fullscreen (Home; sivra.js §8 media gate + §9 scroll timeline) ----------
 The reel video (or the glasses stills) fills the sticky 100vh frame with the headline bottom-left. Scrolling the 400vh section
 scrubs the video while pushing in (data-zoom-mid), dives into the glasses (data-zoom at data-zoom-origin), fades the dark lens to
 ink and lifts the statement in. The .reel-end still only shows when the stills fallback is in use (crossfaded by §9).
 Markup: section.section_hero-home[.is-video] › .reel-scrub-inner › .reel-frame[data-zoom-origin data-zoom-mid data-zoom]
 (img.reel-start, img.reel-end, video data-src / data-src-mobile) + .reel-hero (h1, p) + .reel-overlay + .reel-statement.
 Webflow: Section (400vh) › Div (sticky, 100vh, overflow hidden) › Image / Video + Hero text + Overlay + Statement. */
.section_hero-home { position: relative; height: 400vh; }
.reel-scrub-inner { position: sticky; top: 0; height: 100vh; overflow: hidden; background: var(--sv-ink); color: var(--sv-paper); }
.reel-frame { position: absolute; inset: 0; }
.reel-frame video, .reel-frame img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; will-change: transform; transform-origin: 50% 40%; }
.reel-frame .reel-end { opacity: 0; }
.reel-hero { position: absolute; left: 64px; right: 64px; bottom: 64px; display: grid; gap: 24px; max-width: 1100px; will-change: transform, opacity; }
.reel-hero h1 { font: var(--sv-weight-medium) clamp(56px, 8vw, 120px)/.92 var(--sv-font); letter-spacing: -.045em; margin: 0; }
.reel-hero p { font: 400 clamp(17px, 1.3vw, 20px)/1.5 var(--sv-font); max-width: 520px; margin: 0; opacity: .8; }
.reel-overlay { position: absolute; inset: 0; background: var(--sv-ink); opacity: 0; }
.reel-statement { position: absolute; inset: 0; display: grid; place-items: center; padding: 0 64px; text-align: center; opacity: 0; transform: translateY(40px); }
.reel-statement h2, .reel-statement p { font: var(--sv-weight-medium) var(--sv-text-display-l)/var(--sv-lh-display-l) var(--sv-font); letter-spacing: var(--sv-ls-display-l); max-width: 1200px; margin: 0; }


/* ---------- 10. BENTO WORK GRID + HOVER PILL ----------
 Webflow: Collection List = .bento-grid; each item gets a combo class from the CMS Option "Tile format" (.is-1x1 / .is-1x2 /
 .is-2x2, conditional classes). Dense flow packs the tiles with no gaps. Hover / focus: the media zooms 1.03 and the
 Blue Slate title pill slides up while the title un-stretches; leave = fade, then snap back below. Always shown ≤ 767. */
.bento-grid { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); grid-auto-rows: 320px; grid-auto-flow: dense; gap: 24px; }
.bento-tile { grid-column: span 4; grid-row: span 1; position: relative; border-radius: 12px; overflow: hidden; }
.bento-tile.is-1x2 { grid-column: span 4; grid-row: span 2; } /* tall */
.bento-tile.is-2x2 { grid-column: span 8; grid-row: span 2; } /* large */
.bento-tile img { width: 100%; height: 100%; object-fit: cover; transition: transform .6s ease; } /* a .placeholder carries the same transition (utilities) */
.bento-tile:hover img, .bento-tile:focus-visible img, .bento-tile:hover .placeholder, .bento-tile:focus-visible .placeholder { transform: scale(1.03); }
.bento-title-wrap { position: absolute; top: 40px; left: 40px; margin-right: 40px; overflow: hidden; z-index: 2; } /* clips the slide-up */
.bento-pill {
 display: flex; align-items: center; height: 60px; padding: 0 20px; border-radius: 30px; overflow: hidden;
 background: var(--accent); color: var(--paper); font-size: 16px; font-weight: 500; line-height: 1;
 opacity: 0; transform: translateY(100%);
 transition: all 0s ease .25s, opacity .25s ease 0s; /* out: fade, then snap back below */
}
.bento-title { position: relative; top: -.05em; display: inline-block; transform-origin: bottom;
 transform: translateY(150%) scaleY(2.5); /* starts pushed down + stretched */
 transition: all 0s ease .25s; }
.bento-tile:hover .bento-pill, .bento-tile:focus-visible .bento-pill {
 opacity: 1; transform: translateY(0);
 transition: all .35s ease 0s, opacity 0s linear 0s; /* in: slide up, appear instantly */
}
.bento-tile:hover .bento-title, .bento-tile:focus-visible .bento-title {
 transform: translateY(0) scaleY(1);
 transition: all .3s ease .1s; /* the title settles .1s later */
}


/* ---------- 11. SERVICE ROW INK WIPE (Home Services List) ----------
 Markup (Webflow component "Service Row"): a.service-row › .service-name · .service-desc · .service-arrow (arrow svg) — never numbered
 Hover / focus: the fill (text colour) wipes UP from the bottom and exits through the top on leave, text flips to the page colour,
 the name nudges right, the arrow fills Blue Slate and turns -45°. */
.service-list { border-bottom: 1px solid var(--sv-ink-16); }
.service-row {
 position: relative; isolation: isolate; overflow: hidden;
 display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr) 56px; align-items: center; gap: 32px;
 padding: 40px var(--sv-gutter); border-top: 1px solid var(--sv-ink-16);
 color: var(--sv-ink); text-decoration: none;
 transition: color .35s ease .05s;
}
.service-row::before {
 content: ""; position: absolute; inset: 0; z-index: -1; background: var(--txt-color);
 transform: scaleY(0); transform-origin: top; /* leaving: collapses upward */
 transition: transform .55s cubic-bezier(.65, 0, .35, 1);
}
.service-row:hover::before, .service-row:focus-visible::before { transform: scaleY(1); transform-origin: bottom; } /* entering: rises from the bottom */
.service-row:hover, .service-row:focus-visible { color: var(--bg-color); }
.service-name { font: 500 clamp(32px, 3.9vw, 56px)/1.04 var(--sv-font); letter-spacing: -.025em; transition: transform .55s cubic-bezier(.25, 1, .5, 1); }
.service-desc { font: 400 17px/1.55 var(--sv-font); opacity: .66; max-width: 420px; }
.service-row:hover .service-name, .service-row:focus-visible .service-name { transform: translateX(16px); }
.service-arrow {
 width: 56px; height: 56px; border-radius: 50%; border: 1px solid currentColor; display: grid; place-items: center; font-size: 20px;
 transition: background .35s ease, border-color .35s ease, transform .55s cubic-bezier(.25, 1, .5, 1);
}
.service-row:hover .service-arrow, .service-row:focus-visible .service-arrow { background: var(--sv-accent); border-color: var(--sv-accent); transform: rotate(-45deg); }


/* ---------- 12. CARD HOVER ZOOMS · ARROW NUDGES · LOGO HOVER ----------
 The .placeholder (or image) inside the card carries the .6s transition (utilities); the arrows carry theirs (components). */
.insights_card:hover .placeholder { transform: scale(1.02); }
.journal-card_component:hover .journal-card_image .placeholder { transform: scale(1.03); }
.result-row_media:hover .placeholder { transform: scale(1.02); }
.extensions_card:hover .placeholder, .extensions_card:focus-visible .placeholder { transform: scale(1.03); }
.button.is-text:hover .icon-arrow, .button.is-text:focus-visible .icon-arrow { transform: translateX(3px); }
.story-nav_link.is-next:hover .icon-arrow, .story-nav_link.is-next:focus-visible .icon-arrow { transform: translateX(6px); }
.story-nav_link.is-prev:hover .icon-arrow, .story-nav_link.is-prev:focus-visible .icon-arrow { transform: rotate(180deg) translateX(6px); }
.logo-wall_item:hover .client-logo_component, .client-logo_component:hover { opacity: 1; } /* .85 at rest (components) */
/* media pills (owner audit #5): "View Project" + Brand Extensions pills fill ink on hover (their icon motion: sivra.js §24) */
.button.is-media-pill:hover, .button.is-media-pill:focus-visible, .extensions_card:hover .extensions_pill, .extensions_card:focus-visible .extensions_pill { background: var(--sv-ink); color: var(--sv-paper); border-color: var(--sv-ink); }
/* stat odometer (sivra.js §22 builds it): each digit is a 0–9 column in a 1.1em window, other characters rise from a mask */
.odo { display: inline-flex; align-items: flex-start; line-height: 1.1; }
.odo-col, .odo-mask { display: inline-block; height: 1.1em; overflow: hidden; vertical-align: top; }
.odo-strip { display: flex; flex-direction: column; }
.odo-strip > span, .odo-mask > span { display: block; height: 1.1em; line-height: 1.1; }
/* capabilities reveal guard (sivra.js §25): while html.sv-motion is set (the one-line head script adds it when motion is allowed) the
   grid stays hidden until §25 has set its start state, so a first load never flashes the finished grid. Safety: the head script
   drops the class after 3s whatever happens (no GSAP / ScrollTrigger). Reduced motion: the class is never added. */
.sv-motion [data-capabilities] { visibility: hidden; }


/* ---------- 13. FAQ OPEN STATE (sivra.js §11 toggles .is-open and tweens the answer height) ---------- */
.faq_icon::before, .faq_icon::after { content: ""; position: absolute; left: 50%; top: 50%; width: 12px; height: 1.5px; background: currentColor; transform: translate(-50%, -50%); }
.faq_icon::after { transform: translate(-50%, -50%) rotate(90deg); }
.faq_item.is-open .faq_icon { transform: rotate(45deg); background: var(--sv-accent); color: var(--sv-paper); border-color: var(--sv-accent); }
.faq_item.is-open .faq_answer { height: auto; }


/* ---------- 14. CASE TOC + CASE SECTIONS + CAROUSEL (sivra.js §14 sets .is-active and scrubs .case-toc_fill; §20 drags the
 Brand Extensions carousel). The active link shows its 2px progress track; the fill is a GSAP-set scaleX. ---------- */
.case-toc_link.is-active .case-toc_bar { opacity: 1; }
.extensions_viewport::-webkit-scrollbar { display: none; }
.extensions_viewport.is-dragging .extensions_card { pointer-events: none; } /* a drag never clicks a card */


/* ---------- 15. TESTIMONIAL DOTS (sivra.js §15) — the active dot widens, its fill runs for the autoplay delay (7.25s) ---------- */
.testimonial-dot { height: 4px; width: 4px; border: 0; padding: 0; border-radius: 4px; background: color-mix(in srgb, currentColor 30%, transparent); overflow: hidden; position: relative; transition: width .35s var(--sv-ease-out); }
.testimonial-dot.is-active { width: 64px; }
.testimonial-dot::after { content: ""; position: absolute; inset: 0 auto 0 0; width: 4px; background: currentColor; opacity: 0; transition: width 7.25s linear; }
.testimonial-dot.is-active::after { opacity: 1; width: 100%; }


/* ---------- 16. FORM STATES (sivra.js §16 toggles the parent class) ---------- */
.form_field.is-error .form_error { display: block; }
.form_field.is-error .form_input { border-color: var(--sv-accent); box-shadow: 0 0 0 3px var(--sv-accent-24); }
.form_component.is-success .form_body { display: none; }
.form_component.is-success .form_success { display: block; }
.form_component.is-fail .form_fail { display: block; }


/* ---------- 17. THEME CONTEXT — a page / section theme re-colouring a nested component ---------- */
.is-client .button:not(.is-secondary):not(.is-text):not(.is-media-pill) { background: var(--sv-paper); color: var(--client, var(--sv-client)); border-color: var(--sv-paper); } /* media pills keep paper / ink and fill ink on hover (§12) */
/* Client brand (owner request #9) — project + case-study pages (.page-wrapper.is-client: --client / --on-client from the CMS Color
   fields, data-quote from the "Quote card" option). Sivra's Blue Slate gives way to the client: on the client-coloured page the accent
   is the contrast colour (underlines, focus rings, selection, the footer newsletter arrow stay visible); on paper surfaces (Contact
   Card Paper, gallery sheet) the accent IS the client colour (radio dots, focus, dropdown, errors, submit pill). The menu overlay
   keeps Sivra's colours. */
.page-wrapper.is-client { --sv-accent: var(--on-client, var(--sv-on-client)); --accent: var(--sv-accent); --sv-accent-24: color-mix(in srgb, var(--sv-accent) 24%, transparent); }
.page-wrapper.is-client .is-paper, .page-wrapper.is-client .sv-sheet { --sv-accent: var(--client, var(--sv-client)); --accent: var(--sv-accent); --sv-accent-24: color-mix(in srgb, var(--sv-accent) 24%, transparent); }
.page-wrapper.is-client .sv-menu { --sv-accent: var(--sv-blue-slate); --accent: var(--sv-blue-slate); --sv-accent-24: var(--sv-blue-slate-24); }
.page-wrapper.is-client ::selection { background: var(--on-client, var(--sv-on-client)); color: var(--client, var(--sv-client)); }
.page-wrapper.is-client .is-paper ::selection { background: var(--client, var(--sv-client)); color: var(--on-client, var(--sv-on-client)); }
/* nav "Get Started": client colour on the dark scrolled pill; at the top, over the client-coloured hero, it flips to the contrast colour */
.page-wrapper.is-client .sv-bar .sv-cta { background: var(--on-client, var(--sv-on-client)); color: var(--client, var(--sv-client)); }
.page-wrapper.is-client .sv-bar .sv-cta::before { background: var(--client, var(--sv-client)); }
.page-wrapper.is-client .sv-nav.is-scrolled .sv-bar .sv-cta { background: var(--client, var(--sv-client)); color: var(--on-client, var(--sv-on-client)); }
.page-wrapper.is-client .sv-nav.is-scrolled .sv-bar .sv-cta::before { background: var(--on-client, var(--sv-on-client)); }
/* Contact Card Paper: submit pill in the client colour */
.is-client .contact-card_component.is-paper .button:not(.is-secondary):not(.is-text) { background: var(--client, var(--sv-client)); color: var(--on-client, var(--sv-on-client)); border-color: var(--client, var(--sv-client)); }
/* footer on the client colour: the filled newsletter arrow shows the client colour inside the contrast-colour circle */
.page-wrapper.is-client .footer-newsletter_submit:hover, .page-wrapper.is-client .footer-newsletter_submit:focus-visible { color: var(--client, var(--sv-client)); }
/* pull quote: ink card by default, the client colour on the opening mark; data-quote="light" = paper card for brands ink can't stand on */
.page-wrapper.is-client .pull-quote_mark { color: var(--client, var(--sv-client)); opacity: 1; }
.page-wrapper.is-client[data-quote="light"] .pull-quote_component.is-dark { background: var(--sv-paper); color: var(--sv-ink); --txt-color: var(--sv-ink); --muted: var(--sv-ink-66); --hairline: var(--sv-ink-16); }
.page-wrapper.is-client[data-quote="light"] .pull-quote_mark.is-dark { background-color: transparent; } /* .is-dark paints its own ink background */
.is-dark .button.is-secondary { background: transparent; color: inherit; }


/* ---------- 18. RICH TEXT (.text-rich-text: Journal article body, Privacy, Terms) — nested tags ---------- */
.text-rich-text > * + * { margin-top: var(--sv-space-24); }
.text-rich-text h2 { font-size: var(--sv-text-h3); line-height: var(--sv-lh-h3); letter-spacing: var(--sv-ls-h3); margin-top: var(--sv-space-48); }
.text-rich-text h3 { font-size: var(--sv-text-h4); line-height: var(--sv-lh-h4); letter-spacing: var(--sv-ls-h4); margin-top: var(--sv-space-32); }
.text-rich-text ul, .text-rich-text ol { padding-left: 24px; display: grid; gap: var(--sv-space-8); }
.text-rich-text li::marker { color: var(--sv-accent); }
.text-rich-text figure { display: grid; gap: var(--sv-space-12); margin-top: var(--sv-space-32); }
.text-rich-text figcaption { color: var(--muted); font-size: var(--sv-text-small); }
.text-rich-text a:not(.button) { text-decoration: underline; text-decoration-color: var(--sv-accent); text-decoration-thickness: var(--underline, 2px); text-underline-offset: 3px; transition: color .25s ease; }
.text-rich-text a:not(.button):hover { color: var(--sv-accent); }
.text-rich-text blockquote { padding: var(--sv-space-32) 0 0; }
.article-body_component ul { list-style: disc; }


/* ---------- 19. BREAKPOINTS ---------- */
@media screen and (max-width: 991px) {
 .sv-bar { width: calc(100% - 48px); height: 60px; padding: 0 6px 0 20px; }
 .sv-links { display: none; }
 .sv-menu-btn { max-width: 120px; opacity: 1; padding: 10px 12px; } /* Menu always visible: the pill is the nav */
 .sv-menu-container { padding: 0 24px; }
 .sv-menu-inner { flex-direction: column; gap: 40px; }
 .sv-menu-left { width: 100%; }
 .sv-menu-right { display: none; } /* image marquee is desktop-only */
 .sv-menu-top { padding-bottom: 40px; }
 .sv-menu-close { width: 56px; height: 56px; }
 .sv-menu-bottom { flex-wrap: wrap; gap: 24px; }
 .reel-hero { left: 32px; right: 32px; bottom: 40px; } .reel-statement { padding: 0 32px; }
 .bento-title-wrap { top: 20px; left: 20px; margin-right: 20px; } .bento-pill { height: 44px; }
 .bento-grid { grid-template-columns: repeat(6, 1fr); grid-auto-rows: 260px; }
 .bento-tile, .bento-tile.is-1x2 { grid-column: span 3; } .bento-tile.is-2x2 { grid-column: span 6; }
 .service-row { grid-template-columns: 1fr 48px; padding-top: 28px; padding-bottom: 28px; gap: 16px; }
 .service-desc { display: none; }
 .service-arrow { width: 48px; height: 48px; }
 .case-toc_component::-webkit-scrollbar { display: none; } /* the TOC becomes a sticky pill bar */
}
@media screen and (max-width: 767px) {
 .sv-menu-links { gap: 20px; }
 .sv-menu-btn, .sv-cta { min-height: 44px; } /* 44px tap targets */
 .sv-logo { display: inline-flex; align-items: center; min-height: 44px; }
 .sv-menu-email { display: inline-flex; align-items: flex-end; min-height: 44px; }
 .section_hero-home { height: 300vh; } .reel-hero { left: 24px; right: 24px; bottom: 32px; gap: 16px; } .reel-statement { padding: 0 24px; }
 .reel-frame video, .reel-frame img { object-position: var(--reel-x, 45%) 50%; } /* phone crop keeps the face in frame; sivra.js §9 pans --reel-x 42% to 50% */
 .bento-grid { grid-template-columns: 1fr; grid-auto-rows: auto; }
 .bento-tile, .bento-tile.is-1x2, .bento-tile.is-2x2 { grid-column: auto; grid-row: auto; aspect-ratio: 4 / 5; }
 .bento-tile.is-2x2 { aspect-ratio: 1 / 1; } .bento-pill { opacity: 1; transform: none; } .bento-title { transform: none; }
 .article-body_component h2 { margin-top: var(--sv-space-40); }
 .footer_link::after, .contact-detail_link::after { bottom: calc(50% - 14px); } /* links are 44px tall here */
}
@media screen and (max-width: 479px) { /* keep logo + Menu + Get Started inside the pill on phones */
 .sv-bar { gap: 8px; width: calc(100% - 32px); padding: 0 6px 0 16px; }   /* 375px: logo + Menu + Get Started fit inside the pill */
 .sv-menu-btn { padding: 10px 8px; }
 .sv-cta { padding: 12px 14px; }
 .sv-menu-logo svg { height: 20px; } /* open menu: smaller centred logo keeps clear air before Get Started (QA-15) */
 .sv-menu-top .sv-cta { padding: 12px 14px; }
 .reel-hero h1 { font-size: 60px; line-height: .94; letter-spacing: -.035em; }
}


/* ---------- 20. REDUCED MOTION — the one block ----------
 Transitions off and smooth scroll off everywhere; sivra.js checks the same query before every tween (pulse, wipe, cursor, reel,
 rise, menu, sheet, FAQ, load more, values, testimonial autoplay never start). Static fallbacks for what the code would move: */
@media (prefers-reduced-motion: reduce) {
 html { scroll-behavior: auto; }
 *, *::before, *::after { transition-duration: .01ms !important; transition-delay: 0s !important; scroll-behavior: auto !important; }
 .button::before, .sv-cta::before { transform: translate(-50%, -50%) scale(0) !important; opacity: 0 !important; } /* no bloom */
 .bento-pill, .bento-title { transition: opacity .2s ease !important; transform: none !important; } /* pill fades, never slides */
 .cursor { display: none; } /* no follower */
 /* reel: static 100vh hero (row 1: frame + headline, which position against the row), statement below it on ink (row 2) */
 .section_hero-home { height: auto; } .reel-scrub-inner { position: relative; height: auto; display: grid; grid-template-rows: 100vh auto; }
 .reel-frame, .reel-hero { grid-row: 1 / 2; grid-column: 1 / -1; } /* explicit end lines: an absolute child's auto line is the padding edge */
 .reel-overlay, .reel-frame .reel-end { display: none; }
 .reel-statement { grid-row: 2; grid-column: 1; position: relative; inset: auto; opacity: 1; transform: none; padding: 96px 64px; background: var(--sv-ink); }
}


/* ---------- 21. SERVICES STACK (owner request #7 — sivra.js §26) ----------
 Tones: each Service Card's data-card (Webflow: a custom attribute bound to the Services Option field "Card tone"). Stacked: sivra.js
 adds .is-stacked to the list while the pinned 3D stack runs (motion allowed; never under reduced motion), and the cards then sit on
 top of each other in a stage of one viewport minus the nav; without it they stay one under another (components.css). */
[data-card="ink"]   { --card-bg: var(--sv-ink);        --card-fg: var(--sv-paper); --card-muted: var(--sv-paper-80); --card-hairline: var(--sv-paper-16); }
[data-card="deep"]  { --card-bg: var(--sv-slate-deep); --card-fg: var(--sv-paper); --card-muted: var(--sv-paper-80); --card-hairline: var(--sv-paper-22); }
[data-card="slate"] { --card-bg: var(--sv-accent);     --card-fg: var(--sv-paper); --card-muted: var(--sv-paper);    --card-hairline: var(--sv-paper-30); }
[data-card="mist"]  { --card-bg: var(--sv-slate-mist); --card-fg: var(--sv-ink);   --card-muted: var(--sv-ink-75);   --card-hairline: var(--sv-ink-24); }
.services-stack_list.is-stacked { --stack-top: 96px; --stack-gap: 32px; display: block; position: relative; perspective: 1400px;
  height: calc(100vh - var(--stack-top) - var(--stack-gap)); height: calc(100svh - var(--stack-top) - var(--stack-gap)); }
/* descendant (not child) selectors: in Webflow the cards are Collection Items inside the Collection List */
.services-stack_list.is-stacked .services-card_component { position: absolute; inset: 0; will-change: transform;
  grid-template-rows: auto auto minmax(120px, 1fr); padding: min(48px, 5.5svh); gap: min(40px, 4svh); }
.services-stack_list.is-stacked .services-card_name { font-size: min(var(--sv-text-h1), 9svh); }   /* height-aware type: short laptops keep the image row */
.services-stack_list.is-stacked .services-card_deliverable { padding: min(8px, .8svh) 0; }
.services-stack_list.is-stacked .services-card_media { height: 100%; }
.services-stack_list.is-stacked .services-card_image { aspect-ratio: auto; height: 100%; }
[data-card] :focus-visible { outline-color: var(--card-fg); }   /* the slate focus ring vanished on the Blue Slate card */
@media screen and (max-width: 991px) {   /* tablets + phones: one large image while stacked (three would be tall strips) */
  .services-stack_list.is-stacked .services-card_component { padding: min(32px, 4svh); }
  .services-stack_list.is-stacked .services-card_media { grid-template-columns: minmax(0, 1fr); }
  .services-stack_list.is-stacked .services-card_image:nth-child(n+2) { display: none; }
}
@media screen and (max-width: 767px) {
  .services-stack_list.is-stacked { --stack-top: 88px; --stack-gap: 24px; }
  .services-stack_list.is-stacked .services-card_component { padding: 24px; gap: 20px; }
  .services-stack_list.is-stacked .services-card_deliverables { display: none; }
}
