/* components part 7 of 9 — split for the host's 16KB FTP ceiling.
   Generated by split_for_ftp.py; edit the prototype, not this. */
@media(max-width:760px){
  .vs-cols{grid-template-columns:none;grid-auto-flow:column;grid-auto-columns:84%;overflow-x:auto;scroll-snap-type:x mandatory;padding-bottom:12px;-webkit-overflow-scrolling:touch}
  .vs-cols>.vs-card{scroll-snap-align:start}}@media(max-width:700px){
  .cardgrid.four{grid-template-columns:none!important;grid-auto-flow:column;grid-auto-columns:74%;overflow-x:auto;scroll-snap-type:x mandatory;padding-bottom:12px;-webkit-overflow-scrolling:touch}
  .cardgrid.four>.mini{scroll-snap-align:start}
  .timeline{grid-auto-columns:80%}}@media(max-width:900px){
  .cardgrid.ind{grid-template-columns:repeat(3,1fr);grid-auto-flow:row;grid-template-rows:none;grid-auto-columns:auto;overflow-x:visible;gap:14px 10px;padding-bottom:0}
  .cardgrid.ind .mini.svc{flex-direction:column;text-align:center;gap:10px;padding:14px 8px}
  .cardgrid.ind .mini.svc .svc-img{width:100%;height:auto;max-width:104px}
  .cardgrid.ind .mini.svc .svc-body{text-align:center}
  .cardgrid.ind .mini.svc h3{font-size:14px;line-height:1.2}
  .cardgrid.ind .mini.svc p,.cardgrid.ind .mini.svc .svc-more{display:none}}
/* reveal */
.rv{opacity:0;transform:translateY(20px);transition:opacity 1s ease,transform 1s cubic-bezier(.2,.7,.2,1)}
.rv.in{opacity:1;transform:none}@media(prefers-reduced-motion:reduce){.rv{opacity:1;transform:none;transition:none}.card img,.mini.svc .svc-img,.mini.why .why-img{transition:none}}

/* live colour switcher */
body.mono .btn-p{background:var(--ink);border-color:var(--ink)}
body.mono .btn-p:hover{background:#000;border-color:#000}
body.mono .ba-knob svg{color:var(--ink)}
.note{position:fixed;right:12px;bottom:12px;max-width:330px;z-index:210;background:var(--white);border:1px solid var(--hair);color:var(--muted);font-size:10.5px;letter-spacing:.02em;line-height:1.4;text-align:center;padding:9px 13px;border-radius:6px;box-shadow:0 6px 24px rgba(0,0,0,.1)}


/* ------------------------------------------------------------------ home hero
   One section, not two. The stats used to sit in a separate black strip below the
   hero, which read as a second component bolted on; they belong to the hero and are
   drawn in its own treatment, over the same image. */
/* Full height. The reel is the argument on a page selling images, so it gets the
   screen; the strip beneath it is the first thing a scroll reveals. */
.h-hero{position:relative;display:flex;flex-direction:column;justify-content:center;
        /* Full viewport, pulled up UNDER the sticky header so the footage runs behind
           the translucent bar instead of starting below it. The header stays
           position:sticky — it is the first in-flow block in <body>, so it already pins
           at y=0 from the very first pixel and behaves identically to fixed, without
           taking 81px out of flow on the ~60 other pages that share it. The hero's
           isolation:isolate keeps the video (z-index:-2) and the gradient (-1) inside
           its own stacking context, safely under the bar's z-index 80. */
        min-height:100vh;background:transparent}
/* .h-top owns the footage now and is what gets pulled under the sticky header. It runs
   taller than the hero by exactly the strip below it, so one video sits behind both and
   the fold falls inside a single image rather than on a colour change. */
.h-top{position:relative;margin-top:-81px;background:var(--ink);overflow:hidden;
  isolation:isolate}@media(max-width:1040px){ .h-top{margin-top:-71px}}@media(max-width:760px){ .h-hero{min-height:100svh}}
.h-top video,.h-top .h-still{position:absolute;inset:0;width:100%;height:100%;
        object-fit:cover;z-index:-2}
.h-top::after{content:"";position:absolute;inset:0;z-index:-1;
        background:linear-gradient(76deg,rgba(16,15,13,.92) 0%,rgba(16,15,13,.74) 36%,
                                   rgba(16,15,13,.30) 68%,rgba(16,15,13,.46) 100%)}
/* Centred, and the block padding is deliberately vh-based — do not "tidy" it back to vw.
   `justify-content:center` alone is nearly a no-op here (it moved the h1 by 35px at
   1440x900 and by nothing at all on a landscape phone): what actually held the text low
   was this element's own asymmetric 170px/72px padding, not the free space. Symmetric
   padding is therefore the declaration that does the centring.
   vh rather than vw because vw is blind to viewport HEIGHT — at 12vw a 844x390 landscape
   phone computed 101px of top padding on a 390px-tall screen. At 9svh it is 35px there and
   81px on a 900px desktop, so the minimum top offset shrinks exactly when the screen is
   short. The 28px floor is breathing room; the header needs no clearance of its own because
   it is position:sticky (in flow) and min-height already subtracts it. Safe because
   .h-hero sets MIN-height: when content outgrows it the hero grows, free space never goes
   negative, and centring degrades silently to top-alignment. If anyone ever gives .h-hero a
   fixed height or makes the header fixed, this must become `justify-content:safe center`. */
.h-hero-in{width:100%;padding:clamp(28px,9svh,110px) var(--edge-vp)}
.h-hero h1{color:#fff;max-width:16ch;font-size:clamp(38px,5.3vw,76px);line-height:1.02;
           letter-spacing:-.033em;margin:0 0 18px;text-wrap:balance}
/* Two paragraphs, not one: the sentence used to run on and end on the same promise the
   CTA underneath already makes, so the eye read the turnaround twice before reaching the
   button. Split, the first line says who it is for and the second says what to send —
   and the button is the only place the 30 minutes appears. */
.h-sub{color:rgba(255,255,255,.84);max-width:50ch;font-size:clamp(16px,1.35vw,19px);
       line-height:1.6;margin:0 0 1.6em}
/* 1.6em is exactly one line of this paragraph, so the space between the two reads as a
   blank line rather than as loose leading. At 12px it was under half a line and the two
   sentences still looked like one block. */
.h-sub + .h-sub{margin-bottom:30px}
.h-cta{display:flex;flex-wrap:wrap;gap:12px;align-items:center}
/* An outline button on a dark ground needs a light ring. Both of these inherited
   .btn-g's default border-color:var(--ink), which over the hero video and the dark CTA
   band is a black outline nobody can see. */
.h-hero .btn-g,.h-cta .btn-g{--btn-fg:#fff;color:#fff;border-color:rgba(255,255,255,.55)}
.h-cta .btn-g:hover,.h-cta .btn-g:focus{background:rgba(255,255,255,.12)}
/* one wrapper span keeps the whole label a single flex item, so the number inside it
   does not become an item of its own with a gap either side */
.btn > span{display:inline}
.h-scarce{margin:20px 0 0;font-size:14.5px;color:rgba(255,255,255,.62)}

/* ------------------------------------------------------------------ industries
   Three across, six items: the same card as the disciplines grid so the two read as
   one system, one answering "what we make" and one "who we make it for". */
.h-ind{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:1px;
       background:var(--hair);border:1px solid var(--hair);border-radius:12px;
       overflow:hidden;margin-top:clamp(30px,3.4vw,48px)}@media(max-width:960px){ .h-ind{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(max-width:560px){ .h-ind{grid-template-columns:1fr}}

/* ------------------------------------------------------------------ disciplines
   A fixed four-across grid, not auto-fit. auto-fit left the last row half empty at
   most widths, which reads as a layout that ran out rather than one that was set. */
.di{width:21px;height:21px;color:var(--accent);margin-bottom:4px}

/* The process band is the service pages' own .timeline component, markup and all —
   full-bleed four-across on desktop, a scroll-snapping carousel under 900px. It was
   reimplemented here as .h-steps, worse, with no mobile scroller. Reuse, do not
   reinvent: the only thing this page adds is which four stills go in it. */

/* ------------------------------------------------------------------ work strip
   The portfolio's justification, at the portfolio's density: flex-grow AND
   flex-basis both scale with the aspect ratio, so a wrapped row lands flush at one
   height with native ratios intact. Three across, like every other gallery here. */
.h-work{display:flex;flex-wrap:wrap;gap:8px;--gw:100%;--dens:5.6;--colscale:1;
        margin-top:clamp(26px,3vw,42px)}
.h-work a,.h-work .hw-t{--rowh:calc(var(--gw) / var(--dens) * var(--colscale));
          position:relative;display:block;overflow:hidden;background:var(--grey);
          flex-grow:var(--ar);flex-basis:calc(var(--ar) * var(--rowh));flex-shrink:1;
          min-width:0;
   /* 2.4, not 1.5. The cap exists so one unlucky row cannot spike, but with nine
      hand-picked frames that risk is nil, and at 1.5 the last row hit the cap before
      it reached the wall — which is the white wedge on the right of row three. */
   max-width:calc(var(--ar) * var(--rowh) * 2.4);
          aspect-ratio:var(--ar)}
.h-work .hw-t{padding:0;border:0;cursor:pointer;font:inherit;color:inherit}
.h-work img{width:100%;height:100%;object-fit:cover;display:block;transition:filter .35s}
.h-work a:hover img,.h-work .hw-t:hover img,.h-work .hw-t:focus img{filter:brightness(.9)}
.h-work a:focus-visible,.h-work .hw-t:focus-visible{outline:2px solid var(--accent);outline-offset:2px;z-index:2}@media(max-width:1100px){ .h-work{--colscale:1.4;gap:6px}}@media(max-width:700px){ .h-work{--colscale:1.5;gap:4px}}
.h-work-cta{display:flex;justify-content:center;margin-top:clamp(26px,3vw,40px)}

/* ------------------------------------------------------------------ price */
.h-price{display:grid;grid-template-columns:1.05fr .95fr;gap:clamp(28px,4vw,72px);
         align-items:start}
.h-price-list{display:flex;flex-direction:column;gap:2px;margin-top:6px}
.h-price-list div{display:flex;gap:12px;align-items:baseline;padding:11px 0;
                  border-bottom:1px solid var(--hair);font-size:14.5px}
.h-price-list b{flex:0 0 auto;min-width:124px;font-weight:700;color:var(--ink)}
.h-price-list span{color:var(--muted)}@media(max-width:860px){ .h-price{grid-template-columns:1fr}}