/* Homepage sections, rebuilt to the proposed layout.
 *
 * THIS IS THE SOURCE. build.sh copies it to assets/home.css before
 * build-assets.js runs. Never hand-edit the copy under assets/ - it gets
 * content-hashed and renamed, so the edit lands on a dead filename.
 *
 * The proposal was drawn light: white cards on #F5F7F8, a rule between every
 * band. That is deliberately not what is built here. This site's ground is the
 * structural set - one drawing fixed to the viewport and panned by scroll -
 * and an opaque ground on a section is a lid over it. So the STRUCTURE is the
 * proposal's (utility bar, marquee, service carousel, paged work grid, story
 * split, coverage panel, slim closing band) and the SURFACE stays this site's:
 * translucent grounds, hairline rules, cards that sit on the sheet rather than
 * hide it.
 */

/* ---- hero slideshow ----------------------------------------------------- */
/* Three stills that cross-fade, in place of the looping film.
 *
 * Each slide ships BOTH orientations in one <picture>: a 16:9 landscape frame
 * and a native 9:16 portrait one, swapped at 820px. The old hero had a single
 * 16:9 master and hero.css said so in a comment - "a native 9:16 render would
 * be better still; this at least never crops" - so the phone got the whole
 * landscape frame letterboxed above the copy rather than a hero. With real
 * portrait frames the phone can have the same full-bleed treatment the desktop
 * has, which is the block further down.
 *
 * Cross-fade on opacity only. The images are stacked, so anything that moved
 * them would have to composite three full-bleed layers per frame. */
.heroslide{
  position:absolute; inset:0; opacity:0;
  transition:opacity 1.6s cubic-bezier(.4,0,.2,1);
}
.heroslide.is-on{opacity:1}
.heroslide img{
  display:block; width:100%; height:100%;
  object-fit:cover; object-position:center 56%;
}
@media (prefers-reduced-motion:reduce){
  .heroslide{transition:none}
  /* hero.css hides .hero__meta and .hero__prog here, which was right when they
     held a video timecode and a Replay button. .hero__meta now holds the slide
     dots, and hero.js deliberately does not auto-advance under reduced motion -
     so hiding them leaves slides 2 and 3 unreachable by any means. The dots
     come back; the progress bar stays hidden, having nothing to report.
     This wins on order, not specificity: home.css loads after hero.css. */
  .hero__meta{display:flex}
}

/* The readout in the corner names the slide instead of a video timecode. The
   dots sit beside it, in the ground the phase chip already established.
 *
 * The bar is drawn by ::after rather than being the button itself, so the
 * control can carry a 44px touch target while the mark stays a thin rule. As
 * the button they were 26x3, which is both under the touch minimum and easy to
 * miss on a photograph. They also get their own ground now: these sit over
 * whatever the slide happens to be, and on the bright frames a translucent
 * white bar on sky was barely there. */
.herodots{
  display:flex; align-items:center; gap:4px;
  padding:0 10px;
  background:rgba(5,7,11,.82); border:1px solid rgba(255,255,255,.14);
}
.herodot{
  width:52px; height:44px; padding:0; cursor:pointer; border:0;
  background:transparent; position:relative;
}
.herodot::after{
  content:""; position:absolute; left:0; right:0; top:50%;
  height:5px; margin-top:-2.5px;
  background:rgba(255,255,255,.45);
  transition:background .2s ease, height .2s ease, margin-top .2s ease;
}
.herodot:hover::after{background:rgba(255,255,255,.75)}
.herodot[aria-current="true"]::after{
  background:#fff; height:7px; margin-top:-3.5px;
}
.herodot:focus-visible{outline:2px solid #fff; outline-offset:-3px}

@media (max-width:520px){
  /* The label is hidden at this width, so the dots carry the corner alone and
     can take the full touch target without crowding anything. */
  .herodot{width:44px}
}

/* ---- hero headline ------------------------------------------------------ */
/* "More Steel" leads, smaller and in steel grey, so it reads as the name on the
   sheet rather than competing with the sentence under it. */
.hero__lead{
  display:block;
  font-size:.46em; line-height:1.05; letter-spacing:.02em;
  color:var(--steel); margin-bottom:.12em;
}
.hero__say{display:block}

/* hero.css sets the headline at line-height .96, which is right for a tight
   stack of display type and wrong the moment anything is drawn under a word:
   at that leading the mark lands on the line below. Opened up just enough to
   carry an underline, and no more. */
.hero__h1{line-height:1.08}

/* The three verbs, each underlined; the one whose slide is showing lights up.
 *
 * A real underline rather than an absolutely-positioned bar, because the bar
 * has to be placed against the line box by hand and gets it wrong the moment
 * the headline rewraps - which it does at every breakpoint. text-decoration
 * belongs to the line box and follows the text wherever it goes.
 *
 * Nothing dims. A headline whose words fade in and out is one you have to wait
 * to read, so the type stays at full contrast whatever the slideshow is doing
 * and the emphasis is carried entirely by the rule. */
.hero__v{
  font-weight:inherit; white-space:nowrap;      /* a verb never breaks in half */
  text-decoration-line:underline;
  text-decoration-color:rgba(255,255,255,.28);
  text-decoration-thickness:.055em;
  text-underline-offset:.13em;
  transition:text-decoration-color .45s ease, text-decoration-thickness .45s ease;
}
.hero__v.is-on{
  text-decoration-color:var(--accent-ink);
  text-decoration-thickness:.1em;
}
@media (prefers-reduced-motion:reduce){ .hero__v{transition:none} }

/* ---- hero kicker -------------------------------------------------------- */
/* Place and year, above the headline, as the proposal set it.
 *
 * The proposal could set this as bare text because its hero was a flat dark
 * gradient. Here it lands on the film, at the height where the frame is a
 * sunlit wall - measured on the shipped cut it came out near 1.3:1, which is
 * not text. The scrim cannot be leaned on either: its radial pool is anchored
 * at 22% 55%, under the headline, and this sits above that.
 *
 * So it gets its own ground, which is the same answer .hero__phase already
 * reached for the readout in the opposite corner. inline-flex so the ground is
 * the width of the words and not the width of the column. */
.hero__kick{
  display:inline-flex; align-items:center; gap:11px; margin:0 0 18px;
  padding:9px 14px;
  background:rgba(5,7,11,.82); border:1px solid rgba(255,255,255,.14);
  font:600 11px/1 ui-monospace,Menlo,monospace; letter-spacing:.16em;
  text-transform:uppercase; color:#C7D5DE;
}
.hero__kick i{width:22px; height:2px; background:var(--accent-ink); flex:none}

/* ---- hero on a phone ---------------------------------------------------- */
/* hero.css puts the media in normal flow at 1920/1080 below 820px and drops
   the copy underneath it, because a landscape master full-bleed on a phone
   crops the subject out. That reasoning is sound for a 16:9 source and wrong
   for a 9:16 one: the portrait frame is already the shape of the screen, so
   cover() takes almost nothing off it.

   So the phone gets the desktop treatment back - full-bleed image, copy over
   it - and the scrim goes back to carrying the copy rather than just seating
   it. Loaded after hero.css (see the {{STYLES}} slot in layout.html), so these
   win on order rather than by out-specifying anything. */
@media (max-width:820px){
  .hero{
    min-height:calc(100svh - var(--ubar-h));
    display:flex; align-items:flex-end;
  }
  .hero__media{position:absolute; inset:0; aspect-ratio:auto}
  .heroslide img{object-position:center center}
  /* Pool of shadow under the copy, plus a light top wash so the nav keeps its
     footing.
     Weighted much heavier than the desktop scrim on purpose. On a phone the
     copy is a stacked block roughly half the screen tall, and the frame behind
     it is whatever the portrait crop gives - on the shop slide that is a lit
     concrete floor, where white stencil measured close to unreadable. The
     stops hold near-opaque up the whole copy band and only release above it. */
  .hero__scrim{
    background:
      linear-gradient(to top, rgba(5,7,11,.94) 0%, rgba(5,7,11,.88) 30%, rgba(5,7,11,.68) 50%, rgba(5,7,11,.34) 66%, rgba(5,7,11,.08) 82%, transparent 92%),
      linear-gradient(to bottom, rgba(5,7,11,.72) 0%, rgba(5,7,11,.22) 14%, transparent 30%);
  }
  .hero__inner{padding:0 6vw var(--hero-inset)}
  .hero__h1{max-width:none}
  /* No slideshow controls on a phone. The readout is anchored to the same
     --hero-inset the copy block is padded by, so it lands in the band the
     last row of copy occupies - and on a narrow screen the two CTAs wrap to
     two rows, which puts the phone button straight under the dots. Rather
     than fight for that corner on a screen with no room for it, the corner
     goes: the stills still cross-fade behind the copy and the headline still
     lights the verb whose slide is up, there is just nothing drawn over the
     photograph to say so. The cost is that a phone in reduced-motion mode
     holds on slide one, since hero.js does not auto-advance there and the
     dots were the only other way through. A single still hero is the
     graceful version of that; a control sitting on the CTA is not. */
  .hero__meta{display:none}
  .hero__prog{position:absolute}
}

/* ---- shared: section head carrying a counter and arrows ----------------- */
.sechead{
  display:flex; align-items:flex-end; justify-content:space-between;
  gap:clamp(28px,5vw,60px); flex-wrap:wrap;
}
.sechead__t{min-width:0}
.sechead__t .big{margin-bottom:0}
.pager{display:flex; align-items:center; gap:14px; flex:none; padding-bottom:6px}
.pager__n{
  font:600 12px/1 ui-monospace,Menlo,monospace; letter-spacing:.14em;
  color:var(--steel); white-space:nowrap;
}
.pager__b{
  width:46px; height:46px; flex:none; padding:0; cursor:pointer;
  display:inline-flex; align-items:center; justify-content:center;
  background:transparent; border:1px solid var(--line); color:var(--ink);
  transition:border-color .2s ease, background .2s ease;
}
.pager__b:hover{border-color:var(--ink); background:rgba(255,255,255,.05)}
.pager__b:focus-visible{outline:2px solid var(--accent); outline-offset:2px}
.pager__b svg{display:block; pointer-events:none}

/* ---- service carousel --------------------------------------------------- */
.svcx{margin-top:clamp(26px,3.4vw,38px)}
.svcx__stage{border:1px solid var(--line); background:var(--bg-card)}
.svcx__slide{display:grid; grid-template-columns:1.35fr 1fr}
.svcx__slide[hidden]{display:none}
/* A fixed 340px height against a ~710px column forces a 2.09 aspect, and every
   service photograph is a 3:4 portrait - so cover() was showing a 36% band
   through the middle of each one and cropping the subject out. An aspect-ratio
   holds the same framing at every width instead of getting narrower as the
   column grows; the item still stretches if the copy beside it runs taller,
   which only ever reveals more of the photograph. */
.svcx__fig{position:relative; margin:0; aspect-ratio:3/2; overflow:hidden; background:var(--bg-2)}
.svcx__fig img{
  position:absolute; inset:0; display:block;
  width:100%; height:100%; object-fit:cover;
}
.svcx__body{
  padding:clamp(30px,3.4vw,48px);
  display:flex; flex-direction:column; justify-content:center; gap:16px;
}
.svcx__n{font:600 11px/1 ui-monospace,Menlo,monospace; letter-spacing:.16em; color:var(--steel-dim)}
.svcx__body h3{
  margin:0; text-transform:uppercase; letter-spacing:.005em;
  font:400 clamp(24px,2.5vw,34px)/1.04 var(--disp); color:var(--ink);
}
.svcx__body p{
  margin:0; max-width:38ch; text-wrap:pretty;
  font:700 clamp(16.5px,1.5vw,19px)/1.55 var(--body); color:rgba(242,244,247,.78);
}
.svcx__go{
  margin-top:6px; align-self:flex-start; text-decoration:none;
  font:700 15.5px/1 var(--disp-2); letter-spacing:.04em; color:var(--accent-ink);
  border-bottom:1px solid rgba(127,176,245,.4); padding-bottom:3px;
}
.svcx__go:hover{color:#fff; border-bottom-color:#fff}

/* Tabs. The active one is marked by a heavy top rule - the proposal's device,
   and it survives on a dark ground where a fill would have to invent one. */
.svcx__tabs{
  list-style:none; margin:clamp(14px,1.8vw,20px) 0 0; padding:0;
  display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:16px;
}
.svcx__tab{
  width:100%; text-align:left; cursor:pointer; padding:15px 2px 0;
  background:transparent; border:0; border-top:3px solid var(--line);
  display:flex; flex-direction:column; gap:5px;
  transition:border-top-color .2s ease;
}
.svcx__tab:focus-visible{outline:2px solid var(--accent); outline-offset:3px}
.svcx__tab .n{font:700 12px/1 ui-monospace,Menlo,monospace; letter-spacing:.2em; color:var(--steel-dim)}
.svcx__tab .t{
  font:700 clamp(16px,1.55vw,19.5px)/1.25 var(--disp-2); letter-spacing:.01em;
  color:var(--steel); transition:color .2s ease;
}
.svcx__tab:hover .t{color:var(--ink)}
.svcx__tab[aria-selected="true"]{border-top-color:var(--accent)}
.svcx__tab[aria-selected="true"] .t{color:var(--ink)}

@media (max-width:900px){
  .svcx__slide{grid-template-columns:1fr}
  .svcx__fig{min-height:0; aspect-ratio:16/10}
  .svcx__tabs{grid-template-columns:repeat(2,minmax(0,1fr)); gap:12px}
}

/* ---- paged work grid ---------------------------------------------------- */
.workx{margin-top:clamp(26px,3.4vw,38px)}
.workx__page{display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:clamp(14px,1.8vw,26px)}
.workx__page[hidden]{display:none}
/* A link, not a figure. lightbox.js binds to `.work figure`, which this grid is
   deliberately not - so a photo here cannot open full size the way one on the
   work page does. Rather than leave a dead data-full on every tile, the tile
   goes where the lightbox lives: the work page. */
.workx__card{
  border:1px solid var(--line); background:var(--bg-card);
  display:flex; flex-direction:column;
  text-decoration:none; color:inherit;
  transition:border-color .2s ease;
}
.workx__card:hover{border-color:var(--steel-dim)}
.workx__card:focus-visible{outline:2px solid var(--accent); outline-offset:3px}
.workx__frame{position:relative; margin:0; aspect-ratio:4/3; overflow:hidden; background:var(--bg-2)}
.workx__frame img{
  display:block; width:100%; height:100%; object-fit:cover;
  transition:transform .45s cubic-bezier(.16,.84,.24,1);
}
.workx__card:hover .workx__frame img{transform:scale(1.03)}
.workx__cap{padding:20px 22px; display:flex; flex-direction:column; gap:7px}
.workx__cap b{font:700 clamp(16px,1.5vw,18px)/1.25 var(--disp-2); letter-spacing:.01em; color:var(--ink)}
.workx__cap span{font:600 10.5px/1.45 ui-monospace,Menlo,monospace; letter-spacing:.1em; color:var(--steel-dim)}

.workx__foot{
  margin-top:clamp(20px,2.6vw,30px);
  display:flex; align-items:center; justify-content:space-between; gap:24px; flex-wrap:wrap;
}
.workx__dots{display:flex; align-items:center; gap:9px}
.workx__dot{
  width:30px; height:3px; padding:0; cursor:pointer;
  background:var(--line); border:0; transition:background .2s ease;
}
.workx__dot[aria-current="true"]{background:var(--accent)}
.workx__dot:focus-visible{outline:2px solid var(--accent); outline-offset:4px}
@media (prefers-reduced-motion:reduce){.workx__frame img{transition:none}}
@media (max-width:900px){.workx__page{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (max-width:600px){.workx__page{grid-template-columns:1fr}}

/* ---- story -------------------------------------------------------------- */
/* The signature rule and the owner's name, set as one line under the prose. */
.story__by{display:flex; align-items:center; gap:14px; margin:4px 0 0; flex-wrap:wrap}
.story__by i{width:40px; height:2px; background:var(--line); flex:none}
.story__by b{font:700 19px/1 var(--disp-2); letter-spacing:.03em; color:var(--ink)}
.story__by span{font:600 17px/1 var(--body); color:var(--steel-dim)}

/* The segment label inside the story pane. Set as a kicker rather than a
   heading: it divides two short blocks of prose that are already under an h2,
   and another heading weight there would compete with it. Same mono vocabulary
   the numbered section rules use, so it reads as a label and not a title. */
.story__sub{
  margin:26px 0 8px;
  font:600 11px/1 ui-monospace,Menlo,monospace;
  letter-spacing:.16em; text-transform:uppercase;
  color:var(--accent-ink);
}

/* ---- coverage panel ----------------------------------------------------- */
/* The proposal put the map in a bordered card beside the copy rather than full
   width beneath it. The legend goes in the left column with the heading, and
   the map alone goes in the panel. */
.areax{display:grid; grid-template-columns:1fr 1.15fr; gap:clamp(30px,5vw,68px); align-items:center}
.areax__panel{border:1px solid var(--line); background:rgba(23,29,37,.55); padding:clamp(18px,2.4vw,30px)}
.areax__panel img{display:block; width:100%; height:auto}
@media (max-width:900px){.areax{grid-template-columns:1fr}}


