/* =====================================================================
   PS · CRAFT LAYER  —  ps-spice.css
   Additive enhancement layer for the Power Shifter "Two Voices" system.
   Every rule is gated behind a body class so it is purely additive and
   can be toggled off to compare against the shipped design.

   Master gate ........ body.spice-on
   Module gates ....... body.sp-magnetic magnetic primary CTAs (JS)
                        body.sp-reveal   magenta-wipe image reveals
                        body.sp-index    editorial section numerals
                        body.sp-kinetic  kinetic Two-Voices headlines
                        body.sp-skew     scroll-velocity parallax
                        body.sp-rules    drawn hairlines + magenta seam edges

   Nothing here overrides brand tokens — it reuses --ink / --paper /
   --magenta / --display / --text and the house easing
   cubic-bezier(.76,0,.24,1).
   ===================================================================== */

main#main:focus{outline:none}
.sr-only{position:absolute!important;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}

/* --- Horizontal overflow guards (QA 2026-07) ------------------------
   `clip` (not `hidden`): clips paint without creating a scroll container,
   so position:sticky descendants keep working and no horizontal swipe /
   scrollbar can exist anywhere. Guards below fix the two real offenders:
   grid items default to min-width:auto, so unshrinkable content (odometer
   digits, logo marks) forced columns wider than their track. */
html{overflow-x:clip;overflow-y:auto}  /* explicit y: a lone overflow-x:clip computes y to clip too, freezing ALL scroll */
.proof-row .stat,.proof-row .stat b,.logos .cell{min-width:0}
/* The odometer is a flex child with overflow:hidden — per spec its auto
   min-size collapses to 0, so once the min-width:0 above lets a cell tighten,
   it shrinks below the digits and clips them horizontally. Pin it to content
   width; the vertical digit-roll still clips via the fixed box height. */
.odo{flex:none}


:root{
  --sp-ease:cubic-bezier(.76,0,.24,1);
}

/* =====================================================================
   1 · MAGNETIC CTAs  (sp-magnetic)
   JS nudges .btn toward the pointer; CSS just smooths the return and
   lets the existing cascade letters ride along.
   ===================================================================== */
body.sp-magnetic .btn{will-change:transform;transition:transform .35s var(--sp-ease),
  background .2s ease-out,color .2s ease-out,border-color .2s ease-out}

/* =====================================================================
   3 · MAGENTA-WIPE IMAGE REVEALS  (sp-reveal)
   Extends the secondary-link "highlight swipe" DNA to imagery: a
   magenta panel sits over each frame and wipes off on scroll-in while
   the image settles from a slight over-scale. Frames already clip.
   ===================================================================== */
body.sp-reveal .frame,
body.sp-reveal .wframe{position:relative;overflow:hidden}
body.sp-reveal .frame > img,
body.sp-reveal .wframe > img{transform:scale(1.075);transition:transform 1.15s var(--sp-ease)}
body.sp-reveal .frame.sp-in > img,
body.sp-reveal .wframe.sp-in > img{transform:scale(1)}
body.sp-reveal .frame::after,
body.sp-reveal .wframe::after{content:"";position:absolute;inset:0;z-index:4;
  background:var(--magenta);transform:scaleX(1);transform-origin:left center;
  transition:transform .85s var(--sp-ease)}
body.sp-reveal .frame.sp-in::after,
body.sp-reveal .wframe.sp-in::after{transform:scaleX(0);transform-origin:right center}
/* keep overlay text/badges above the wipe */
body.sp-reveal .wcard-overlay,
body.sp-reveal .slot-tag,
body.sp-reveal .film-play,
body.sp-reveal .wtint{z-index:6}
/* Mobile: drop the magenta-wipe reveal. On a phone every card wipes as it
   scrolls in, which reads as distracting flashing rather than craft. Show the
   images static (no wipe, no over-scale settle); desktop keeps the effect. */
@media(max-width:640px){
  body.sp-reveal .frame::after,
  body.sp-reveal .wframe::after{display:none}
  body.sp-reveal .frame > img,
  body.sp-reveal .wframe > img,
  body.sp-reveal .frame.sp-in > img,
  body.sp-reveal .wframe.sp-in > img{transform:none;transition:none}
}

/* Thumbnail hairline normalization — the .cards grid only draws its 1px
   line-light seam BETWEEN cells, so the first/top-left card has no seam on
   its outer edge. Give every work-card thumbnail a matching hairline below
   it so all cards (Iron Mountain included) read consistently. */
body.spice-on .work .card .frame{box-shadow:0 1px 0 0 var(--line-light)}

/* =====================================================================
   4 · EDITORIAL SECTION NUMERALS  (sp-index)
   A large outlined numeral floats in the top gutter of each major
   section, giving the scroll an editorial spine. Outline only, so it
   reads as structure, not content. JS injects .sp-idx + numbers them.
   ===================================================================== */
body.spice-on .sp-host{position:relative}
.sp-idx{position:absolute;top:clamp(16px,2.4vw,30px);right:var(--gutter);
  font-family:var(--display);font-weight:500;font-size:clamp(34px,5vw,76px);
  line-height:1;letter-spacing:-.03em;color:transparent;
  -webkit-text-stroke:1px var(--line);pointer-events:none;z-index:1;
  opacity:0;transform:translateY(-8px);transition:opacity .6s var(--sp-ease),transform .6s var(--sp-ease)}
body.sp-index .sp-host.sp-in > .sp-idx{opacity:.85;transform:none}
.sp-host.on-ink > .sp-idx{-webkit-text-stroke-color:var(--line-light)}

/* =====================================================================
   5 · KINETIC TWO-VOICES  (sp-kinetic)
   The serif "voice" line is the brand's emotional pivot — give it more
   stage: a touch larger, and a magenta underline that draws on as it
   enters. Amplifies the grotesque→serif contrast the whole system is
   built around.
   ===================================================================== */
body.sp-kinetic .sub-voice{font-size:clamp(22px,2.8vw,42px);line-height:1.1}
body.sp-kinetic em.voice,
body.sp-kinetic .voice.mag{position:relative;display:inline-block}
body.sp-kinetic em.voice::after,
body.sp-kinetic .voice.mag::after{content:"";position:absolute;left:0;right:0;
  bottom:-.04em;height:2px;background:var(--magenta);
  transform:scaleX(0);transform-origin:left center;
  transition:transform .75s var(--sp-ease) .15s}
body.sp-kinetic em.voice.sp-in::after,
body.sp-kinetic .voice.mag.sp-in::after{transform:scaleX(1)}
/* engine bands already carry their own .rule under the H2 — suppress the
   kinetic underline there so the two magenta marks don't stack. */
body.sp-kinetic .engine em.voice::after,
body.sp-kinetic .engine .voice.mag::after{content:none}
/* Hero cleanup (2026-07): the sub-hero sub-voice underline stacked with the byline
   divider as a "double line" — drop the kinetic underline in sub-heroes. Also hide the
   redundant hero eyebrow on case studies + listing pages (kept on .article blog heroes,
   where the eyebrow is the post's category). Homepage .hero underline is left as-is. */
body.sp-kinetic .sub-hero em.voice::after,
body.sp-kinetic .sub-hero .voice.mag::after{content:none}
.sub-hero:not(.article) .eyebrow{display:none}
/* give the word-mask rise a hair more travel + lead with magenta */
body.sp-kinetic .rise .wi{transition:transform .8s var(--sp-ease),opacity .8s var(--sp-ease)}

/* =====================================================================
   6 · SCROLL-VELOCITY PARALLAX  (sp-skew)
   Section media drifts a few px against scroll velocity — the page
   gains weight and inertia without distortion. JS sets --sp-shift.
   ===================================================================== */
body.sp-skew .frame,
body.sp-skew .wframe,
body.sp-skew .video-frame,
body.sp-skew .sp-stream{transform:translateY(var(--sp-shift,0px));transition:transform .25s linear}

/* =====================================================================
   7 · DRAWN HAIRLINES + MAGENTA SEAM  (sp-rules)
   Every house hairline (.rule) draws on; each paper→ink flip gets a
   magenta edge that scales in from centre, punctuating the section
   change the way the system already uses ink/paper flips.
   ===================================================================== */
body.sp-rules .rule{transform:scaleX(0);transform-origin:left center;
  transition:transform .8s var(--sp-ease)}
body.sp-rules .rule.sp-in{transform:scaleX(1)}
.sp-edge{position:absolute;top:0;left:0;right:0;height:2px;background:var(--magenta);
  transform:scaleX(0);transform-origin:center;z-index:7;pointer-events:none;
  transition:transform .9s var(--sp-ease)}
body.sp-rules .sp-host.sp-in > .sp-edge{transform:scaleX(1)}

/* Divider between the two engine "expression" modules (one-two punch).
   The line is present whenever the layer is on; sp-rules adds the draw. */
.sp-band-rule{padding:clamp(18px,5vh,56px) 0}
.sp-band-rule .sp-br-line{display:block;height:1px;
  background:var(--line);transform:scaleX(1);transform-origin:left center;
  transition:transform .9s var(--sp-ease)}
body.sp-rules .sp-band-rule:not(.sp-in) .sp-br-line{transform:scaleX(0)}

/* =====================================================================
   STREAM PLACEHOLDER  (preview only)
   The live pages embed Cloudflare Stream players. In this review
   sandbox those external embeds are swapped for a styled poster so the
   page is fast and never hangs. Dev keeps the real <iframe>.
   ===================================================================== */
.sp-stream{position:absolute;inset:0;background:var(--ink);overflow:hidden;
  display:flex;align-items:center;justify-content:center}
.sp-stream::before{content:"";position:absolute;inset:0;opacity:.5;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3CfeColorMatrix values='0 0 0 0 0.07 0 0 0 0 0.07 0 0 0 0 0.08 0 0 0 0.35 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E")}
.sp-stream .sp-play{position:relative;z-index:2;display:flex;flex-direction:column;
  align-items:center;gap:16px;color:rgba(250,250,247,.7)}
.sp-stream .sp-play b{width:74px;height:74px;border-radius:50%;border:1px solid var(--magenta);
  display:flex;align-items:center;justify-content:center;color:var(--magenta);font-size:22px;
  transition:background .3s var(--sp-ease),color .3s var(--sp-ease)}
.sp-stream:hover .sp-play b{background:var(--magenta);color:var(--paper)}
.sp-stream .sp-play span{font-family:var(--text);font-weight:700;font-size:11px;
  letter-spacing:.2em;text-transform:uppercase}
/* the .video-frame box is relative already; reel-bg too */

/* =====================================================================
   CONTROL PANEL  —  brand-styled, vanilla, persistent
   ===================================================================== */
.ps-panel,.ps-launch{font-family:var(--text);position:fixed;z-index:10001}
.ps-launch{right:22px;bottom:22px;width:54px;height:54px;background:var(--ink);
  color:var(--paper);border:1px solid var(--magenta);display:flex;align-items:center;
  justify-content:center;cursor:pointer;transition:background .25s var(--sp-ease)}
.ps-launch:hover{background:var(--magenta)}
.ps-launch svg{width:20px;height:20px}
.ps-launch[hidden]{display:none}

.ps-panel{right:22px;bottom:22px;width:312px;max-width:calc(100vw - 32px);
  background:var(--ink);color:var(--paper);border:1px solid rgba(250,250,247,.16);
  box-shadow:0 24px 60px rgba(0,0,0,.4);transform-origin:bottom right}
.ps-panel[hidden]{display:none}
.ps-phead{display:flex;align-items:center;justify-content:space-between;
  padding:16px 18px;border-bottom:1px solid rgba(250,250,247,.14)}
.ps-phead .ps-ey{font-weight:700;font-size:11px;letter-spacing:.18em;text-transform:uppercase;
  color:var(--magenta)}
.ps-phead .ps-x{cursor:pointer;color:rgba(250,250,247,.6);font-size:18px;line-height:1;
  background:none;border:0;padding:2px 4px}
.ps-phead .ps-x:hover{color:var(--paper)}
.ps-master{display:flex;align-items:center;justify-content:space-between;gap:12px;
  padding:14px 18px;border-bottom:1px solid rgba(250,250,247,.14)}
.ps-master .ps-mt{font-family:var(--display);font-weight:500;font-size:18px;letter-spacing:-.01em}
.ps-master .ps-ms{display:block;font-family:var(--text);font-weight:400;font-size:11.5px;
  color:rgba(250,250,247,.55);letter-spacing:0;text-transform:none;margin-top:2px}
.ps-mods{padding:6px 0 10px;max-height:46vh;overflow-y:auto}
.ps-mod{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;
  padding:11px 18px;transition:background .2s}
.ps-mod:hover{background:rgba(250,250,247,.04)}
.ps-mod .ps-mc{flex:1}
.ps-mod .ps-mn{font-weight:700;font-size:13px;letter-spacing:.01em}
.ps-mod .ps-md{font-size:11.5px;color:rgba(250,250,247,.5);line-height:1.4;margin-top:2px}
.ps-foot{padding:12px 18px 16px;border-top:1px solid rgba(250,250,247,.14);
  font-size:11px;color:rgba(250,250,247,.45);line-height:1.5}
.ps-foot code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;color:rgba(250,250,247,.7)}

/* switch */
.ps-sw{appearance:none;-webkit-appearance:none;flex:none;width:38px;height:22px;border-radius:0;
  background:rgba(250,250,247,.16);position:relative;cursor:pointer;transition:background .25s var(--sp-ease);
  border:1px solid transparent;margin-top:1px}
.ps-sw::after{content:"";position:absolute;top:2px;left:2px;width:16px;height:16px;
  background:var(--paper);transition:transform .25s var(--sp-ease)}
.ps-sw:checked{background:var(--magenta)}
.ps-sw:checked::after{transform:translateX(16px)}
.ps-sw:disabled{opacity:.35;cursor:not-allowed}
.ps-mod.off{opacity:.5}

@media (max-width:560px){
  .ps-panel{right:12px;bottom:12px;left:12px;width:auto}
}

/* =====================================================================
   CLIENT TESTIMONIAL BANNER  (content component)
   Sits above the logo roster (.proof-spine) on Digital + Studios. The
   logos prove "who trusts us"; this proves "what it felt like" — the
   serif italic is the brand's human voice, so the quote lives there.
   Markup is authored per page (real copy); styling is shared here.
   ===================================================================== */
.quote-banner{background:var(--paper);padding:clamp(34px,5.5vh,80px) 0 clamp(8px,2.4vh,30px)}
.quote-banner .wrap{max-width:1080px;opacity:1 !important;transform:none !important}
.qb-mark{display:block;font-family:var(--serif);font-style:italic;font-weight:800;
  color:var(--magenta);font-size:clamp(58px,7vw,112px);line-height:.74;margin-bottom:.04em;
  user-select:none;opacity:0;transform:translateY(10px);
  transition:opacity .5s var(--sp-ease),transform .5s var(--sp-ease)}
.quote-banner blockquote{margin:0}
.quote-t{font-family:var(--serif);font-style:italic;font-weight:800;
  font-size:clamp(27px,3.7vw,52px);line-height:1.16;letter-spacing:-.015em;
  color:var(--ink);text-wrap:balance}
/* word-by-word cascade — each word rises from the top-left in reading order */
.quote-t .qw{display:inline-block;opacity:0;transform:translateY(14px);
  transition:opacity .55s var(--sp-ease),transform .55s var(--sp-ease);
  transition-delay:calc(var(--qi,0) * 46ms)}
.quote-banner.sp-in .qb-mark{opacity:1;transform:none}
.quote-banner.sp-in .quote-t .qw{opacity:1;transform:none}
/* attribution — hairline rule, name in display ink, role in magenta caps (no ink bar) */
.quote-by{margin-top:clamp(24px,3vw,42px);padding-top:clamp(16px,2vw,22px);
  border-top:1px solid var(--line);display:flex;align-items:baseline;gap:8px 16px;
  flex-wrap:wrap;font-style:normal;background:none;
  opacity:0;transform:translateY(10px);
  transition:opacity .6s var(--sp-ease),transform .6s var(--sp-ease)}
.quote-banner.sp-in .quote-by{opacity:1;transform:none;transition-delay:calc(var(--qcount,16) * 46ms)}
.qb-name{font-family:var(--display);font-weight:700;font-size:clamp(16px,1.5vw,21px);
  letter-spacing:-.01em;color:var(--ink)}
.qb-role{font-family:var(--text);font-weight:700;font-size:12px;letter-spacing:.14em;
  text-transform:uppercase;color:var(--magenta-ink)}
@media (prefers-reduced-motion:reduce){
  .qb-mark,.quote-t .qw,.quote-by{opacity:1 !important;transform:none !important;transition:none}
}
