/* ============================================================================
   Homepage layout. Tokens, chrome and the reveal system live in css/ds.css,
   which is loaded first; everything here is home-only and prefixed .hp-.
   ========================================================================== */

/* ============================================================================
   Homepage — layered on top of style.css and scoped to body.home.
   Every selector here is prefixed .home or .hp- so case-study pages are
   untouched. Loaded after style.css.
   ========================================================================== */



/* ---------- Display face ----------
   Titles and bold display text switch to Open Sauce Sans (declared once in
   ds.css). Scoped to <main> so the nav wordmark and footer keep the shared
   face, matching the same opt-in used on the NOX case study. */
.home main {
  --font-display: 'Open Sauce Sans', 'Inter Tight', Inter, sans-serif;
  font-synthesis-weight: none;
}

/* ---------- Hero ---------- */

/* clip, not hidden: ring pieces past the side edges are cut off without
   adding a sideways scroll, and the sticky pin inside still works. */
.home .hp-hero-wrap { position: relative; overflow-x: clip; }
/* Pinned distance: a plain rotate-only run of ~35vh, plus exactly enough
   after it for the marquee's overlap climb (see the margin rule near the
   marquee) to land flush under the nav right as this releases. The two
   numbers are coupled — see that rule's comment before changing either. */
.home .hp-hero-wrap.is-active { height: calc(235vh - 144px); }
.home .hp-hero-pin { position: relative; z-index: 1; }
.home .hp-hero-wrap.is-active .hp-hero-pin {
  position: sticky;
  top: 72px;
  height: calc(100vh - 72px);
  /* Small viewport height on touch browsers, so the pinned hero doesn't jump
     when the address bar shows or hides. */
  height: calc(100svh - 72px);
  min-height: 560px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  /* Clip like overflow: hidden, except the top edge, which is pushed up by
     the nav's height so the aurora can run on underneath the nav. */
  clip-path: inset(-72px 0 0 0);
}
/* Soft orange ball glowing behind the portrait/plates stage. A pseudo-
   element of the PIN, not the wrap: the pin is what actually stays put on
   screen while the page scrolls (position: sticky), so anchoring the glow
   here keeps it fixed in place while the page scrolls. js/home.js drives
   --hero-glow from 0 (at rest) to 1 (rotation phase spent), and the glow
   fades out linearly with it instead of moving. */
.home .hp-hero-pin::before {
  content: "";
  position: absolute;
  inset: 0;
  /* Negative, not 0: .wrap.hp-hero (the copy/stage's container) is a plain
     non-positioned block, which paints ahead of a positioned z-index:0
     sibling regardless of source order — only a negative z-index reliably
     stays behind it. */
  z-index: -1;
  pointer-events: none;
  background: radial-gradient(circle min(34vw, 560px) at 82% 50%,
    rgba(226, 87, 46, 0.65) 0%,
    rgba(226, 87, 46, 0.28) 45%,
    transparent 100%);
  opacity: calc(1 - var(--hero-glow, 0));
  will-change: opacity;
}

/* Aurora curtain (js/aurora.js), behind everything in the hero — including
   the orange glow above, hence the lower z-index. It fills the pin and
   reaches 72px higher, up under the nav (whose own backdrop blur softens
   it); being inside the pin, it stays put on screen with it. */
.home .hp-aurora {
  position: absolute;
  top: -72px;
  left: 0;
  width: 100%;
  height: calc(100% + 72px);
  z-index: -2;
  pointer-events: none;
  opacity: 0.7;
}

.home .hp-hero {
  display: grid;
  gap: 2.5rem;
  padding-top: 3rem;
  padding-bottom: 2rem;
  width: 100%;
}
@media (min-width: 900px) {
  .home .hp-hero {
    grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.44fr);
    align-items: center;
    gap: clamp(2rem, 4vw, 4rem);
    padding-top: 0;
    padding-bottom: 0;
  }
}

.home .hp-eyebrow {
  margin: 0 0 1.75rem;
  /* Wide-set: on a phone it has to shrink to stay on one line. */
  font-size: clamp(1.2rem, 5.6vw, 1.75rem);
  font-weight: 100;
  line-height: 1.1;
  color: var(--ink);
}

.home .hp-title {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(2.1rem, 3.85vw, 3.6rem);
  line-height: 0.98;
  letter-spacing: -0.035em;
  margin: 0 0 1.5rem;
  max-width: 15ch;
  text-wrap: balance;
}
.home .hp-title em {
  font-family: var(--serif);
  font-style: italic;
  font-weight: 400;
  letter-spacing: -0.01em;
  padding-right: 0.06em;
}

/* Word-level mask reveal. Each .w clips its inner span until .is-ready. */
.home .hp-title .w {
  display: inline-block;
  overflow: hidden;
  vertical-align: bottom;
  padding-bottom: 0.14em;
  margin-bottom: -0.14em;
}
.home .hp-title .w > span {
  display: inline-block;
  transform: translateY(110%);
  opacity: 0;
  transition:
    transform 1s var(--ease-out),
    opacity 0.8s var(--ease-out);
  transition-delay: calc(var(--i, 0) * 48ms + 120ms);
}
.home.is-ready .hp-title .w > span {
  transform: translateY(0);
  opacity: 1;
}

.home .hp-lede {
  color: var(--ink-soft);
  font-size: clamp(0.975rem, 1vw + 0.6rem, 1.075rem);
  line-height: 1.6;
  max-width: 42ch;
  margin: 0 0 2.25rem;
}

.home .hp-facts {
  list-style: none;
  margin: 0;
  padding: 1.25rem 0 0;
  border-top: 1px solid var(--rule);
  display: flex;
  flex-wrap: wrap;
  gap: 1rem 2.5rem;
}
/* Based in and Available for share the first row, each as wide as its own
   text so the gap between them is exact (equal columns left a wide hole
   after the short "Milan, Italy"). Focus always takes the row below. */
.home .hp-facts li { flex: 0 0 auto; }
.home .hp-facts .hp-fact-wide { flex: 0 0 100%; }
.home .hp-facts li {
  font-family: var(--micro);
  font-stretch: var(--micro-wide);
  font-weight: 500;
  font-size: var(--fs-micro);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-dim);
  line-height: 1.5;
}
.home .hp-facts b {
  display: block;
  font-family: var(--font-body);
  font-weight: 500;
  font-size: var(--fs-small);
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink);
  margin-top: 0.3rem;
}

/* Intro fade for the whole copy column (independent of the word masks). */
.home .hp-eyebrow,
.home .hp-lede,
.home .hp-facts {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 0.9s var(--ease-out), transform 0.9s var(--ease-out);
}
.home.is-ready .hp-eyebrow { opacity: 1; transform: none; transition-delay: 60ms; }
.home.is-ready .hp-lede    { opacity: 1; transform: none; transition-delay: 460ms; }
.home.is-ready .hp-facts   { opacity: 1; transform: none; transition-delay: 560ms; }

/* ---------- Hero stage: cut-outs orbiting the portrait ---------- */

.home .hp-stage {
  position: relative;
  aspect-ratio: 1000 / 820;
  width: 100%;
  transition: transform 0.7s var(--ease);
  will-change: transform;
}
@media (max-width: 899px) {
  .home .hp-stage { aspect-ratio: auto; }
  /* The ring on a phone or tablet: a near-square box so the orbit stays
     round, and a larger portrait at its centre. Pieces swinging past the
     sides may leave the screen; the hero clips them (see .hp-hero-wrap). */
  .home .hp-stage.is-active { aspect-ratio: 1 / 1.08; }
  .home .hp-stage.is-active .hp-portrait { width: 58%; }

  /* Order: name, title, then the person with the ring, then the paragraph
     and the facts. The copy block dissolves so its pieces can be reordered
     around the stage. */
  .home .hp-hero { gap: 0; }
  .home .hp-hero-copy { display: contents; }
  .home .hp-eyebrow { order: 1; }
  .home .hp-title   { order: 2; }
  .home .hp-stage   { order: 3; margin: -1.75rem 0 1.25rem; }
  /* The round orange glow belongs to the desktop layout, behind the ring
     beside the copy; on a phone it only blotted the column. */
  .home .hp-hero-pin::before { display: none; }
  .home .hp-lede    { order: 4; }
  .home .hp-facts   { order: 5; }
}
@media (min-width: 900px) {
  .home .hp-stage {
    align-self: start;
    margin-top: -3.5rem;
    margin-right: -5rem;
  }
}

/* The portrait anchors the ring. Pieces at the back of the orbit stack below
   it, pieces at the front above, so the whole thing reads as one depth. */
.home .hp-portrait {
  position: absolute;
  left: 50%;
  top: 52%;
  z-index: 60;
  width: calc(var(--w, 40) * 1%);
  pointer-events: none;
  transform: translate(-50%, -50%);
}
.home .hp-portrait img {
  width: 100%;
  height: auto;
  display: block;
  /* The cut-out ends at the chest; dissolve it instead of letting it stop
     on a hard edge. */
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 58%, rgba(0, 0, 0, 0.45) 84%, rgba(0, 0, 0, 0) 100%);
          mask-image: linear-gradient(to bottom, #000 0%, #000 58%, rgba(0, 0, 0, 0.45) 84%, rgba(0, 0, 0, 0) 100%);
  /* intro */
  opacity: 0;
  transform: scale(0.96);
  transition: opacity 1.2s var(--ease-out), transform 1.2s var(--ease-out);
  transition-delay: 180ms;
}
.home.is-ready .hp-portrait img { opacity: 1; transform: scale(1); }

/* No-JS / reduced-motion / narrow fallback: a static, deliberate collage. */
.home .hp-stage:not(.is-active) {
  display: grid;
  /* Column count follows the room available, so the collage is four up beside
     the copy on a wide screen and two up on a phone. */
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  align-items: end;
  gap: 1.75rem 1.25rem;
  /* The ring's box no longer applies once the collage takes over. */
  aspect-ratio: auto;
  will-change: auto;
}
.home .hp-stage:not(.is-active) .hp-portrait {
  position: static;
  grid-column: 1 / -1;
  width: min(62%, 300px);
  margin: 0 auto 0.75rem;
  transform: none;
}
.home .hp-stage:not(.is-active) .hp-plate {
  position: static;
  width: auto;
  transform: none;
  opacity: 1 !important;
  will-change: auto;
}
/* The pieces have very different proportions; holding them to one height is
   what makes a grid of them read as a set rather than as leftovers. */
.home .hp-stage:not(.is-active) .hp-plate-media img {
  max-height: 132px;
  width: auto;
  max-width: 100%;
  margin-inline: auto;
}

.home .hp-plate {
  position: absolute;
  top: 0;
  left: 0;
  width: calc(var(--w, 24) * 1%);
  text-decoration: none;
  will-change: transform;
  transform: translate3d(calc(var(--x, 0px) - 50%), calc(var(--y, 0px) - 50%), 0)
             rotate(var(--r, 0deg)) scale(var(--s, 1));
}

/* The pieces are cut out of their backgrounds, so they carry no frame: no
   panel, no radius, no hairline. The only thing that grounds them is a shadow
   that follows the silhouette rather than a box. */
.home .hp-plate-media {
  display: block;
  position: relative;
  /* intro */
  opacity: 0;
  transform: scale(0.94);
  transition: opacity 1.1s var(--ease-out), transform 1.1s var(--ease-out);
  transition-delay: calc(var(--i, 0) * 90ms + 300ms);
}
.home.is-ready .hp-plate-media { opacity: 1; transform: scale(1); }

.home .hp-plate-media img {
  width: 100%;
  height: auto;
  display: block;
  filter: drop-shadow(0 18px 24px rgba(0, 0, 0, 0.55));
  transition: transform 0.7s var(--ease), filter 0.5s var(--ease);
}
.home.is-ready .hp-plate:hover .hp-plate-media img,
.home.is-ready .hp-plate:focus-visible .hp-plate-media img {
  transform: scale(1.06);
  filter: drop-shadow(0 20px 26px rgba(0, 0, 0, 0.6));
}

.home .hp-plate-tag,
.home .hp-stage-tag {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 0.45rem;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--fs-note);
  letter-spacing: -0.01em;
  color: var(--ink);
  white-space: nowrap;
  pointer-events: none;
}
.home .hp-plate-tag b,
.home .hp-stage-tag b { color: var(--accent); font-weight: 600; }
/* Two-line titles (a <br> inside the span) stay centred on each other. */
.home .hp-plate-tag span,
.home .hp-stage-tag span { text-align: center; line-height: 1.15; }

/* While the ring runs, the per-piece captions stay hidden; js/home.js shows
   one shared caption over the hovered piece instead, above every layer. */
.home .hp-stage.is-active .hp-plate-tag { display: none; }
.home .hp-stage-tag {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 1000;
  opacity: 0;
  transform: translate(-50%, calc(-100% - 0.3rem));
  transition: opacity 0.3s var(--ease), transform 0.35s var(--ease);
  text-shadow: 0 1px 8px rgba(18, 17, 16, 0.9);
}
.home .hp-stage-tag.is-on {
  opacity: 1;
  transform: translate(-50%, calc(-100% - 0.6rem));
}
.home .hp-stage:not(.is-active) .hp-plate-tag {
  position: static;
  opacity: 1;
  transform: none;
  margin-top: 0.6rem;
  text-align: center;
  justify-content: center;
  /* Out of the orbit the caption sits in flow, so it can wrap instead of
     running across the neighbouring piece. The ring's hand-set line breaks
     would fight the narrow cell, so the caption balances itself instead. */
  white-space: normal;
  text-wrap: balance;
  text-shadow: none;
}
.home .hp-stage:not(.is-active) .hp-plate-tag br { display: none; }
/* Seven pieces in two or three columns leave one alone on the last row:
   centre it across the row rather than leaving it hanging on the left. */
@media (max-width: 649px) {
  .home .hp-stage:not(.is-active) .hp-plate:last-of-type { grid-column: 1 / -1; justify-self: center; width: min(100%, 11rem); }
}

/* Scroll cue */
.home .hp-cue {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 1.75rem;
  display: flex;
  justify-content: center;
  opacity: 0;
  transition: opacity 0.6s var(--ease);
}
.home.is-ready .hp-cue { opacity: 1; transition-delay: 1.1s; }
.home .hp-hero-wrap:not(.is-active) .hp-cue { display: none; }
.home .hp-cue span {
  font-family: var(--micro);
  font-stretch: var(--micro-wide);
  font-weight: 500;
  font-size: var(--fs-micro);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-dim);
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
}
.home .hp-cue i {
  display: block;
  width: 1px;
  height: 22px;
  background: linear-gradient(to bottom, var(--rule-hi), transparent);
  animation: hp-cue-slide 2.2s var(--ease) infinite;
}
@keyframes hp-cue-slide {
  0%, 100% { transform: translateY(-3px); opacity: 0.4; }
  50%      { transform: translateY(3px);  opacity: 1; }
}
@media (prefers-reduced-motion: reduce) { .home .hp-cue i { animation: none; } }

/* ---------- Marquee ---------- */

.home .hp-marquee {
  overflow: hidden;
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  background: var(--bg);
  position: relative;
  z-index: 2;
}
/* While the hero is pinned (desktop, motion allowed), pull the marquee up
   into the tail of that pinned distance instead of waiting for it to end.
   The hero stays fixed and still turning underneath while this band (and
   the accent section right after it, same z-index) climbs over it — an
   overlap rather than a hand-off to plain scrolling. The offset is sized so
   the marquee's top lands exactly at 72px (the nav height, same as the
   hero pin's own `top`) at the moment the pin's scroll budget runs out:
   pin height is `100vh - 72px`, so pulling the marquee up by that same
   amount minus 72px puts it flush under the nav right as normal scrolling
   takes back over — no gap, no early hand-off. */
@media (min-width: 900px) {
  .home .hp-hero-wrap.is-active + .hp-marquee { margin-top: calc(144px - 100vh); }
}
.home .hp-marquee::before,
.home .hp-marquee::after {
  content: "";
  position: absolute;
  top: 0; bottom: 0;
  width: clamp(40px, 8vw, 120px);
  z-index: 2;
  pointer-events: none;
}
.home .hp-marquee::before { left: 0;  background: linear-gradient(to right, var(--bg), transparent); }
.home .hp-marquee::after  { right: 0; background: linear-gradient(to left,  var(--bg), transparent); }
.home .hp-marquee-track {
  display: flex;
  width: max-content;
  animation: hp-marquee 46s linear infinite;
}
.home .hp-marquee-set {
  display: flex;
  align-items: center;
  padding: 1.05rem 0;
  font-family: var(--micro);
  font-stretch: var(--micro-wide);
  font-weight: 500;
  font-size: var(--fs-micro);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-soft);
  white-space: nowrap;
}
.home .hp-marquee-set span { padding: 0 1.1rem; }
.home .hp-marquee-set i { color: var(--accent); font-style: normal; font-size: 0.55rem; }
@keyframes hp-marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }
/* WCAG 2.2.2: moving content can be stopped. Hovering the strip holds it;
   the round toggle at its right end pauses it outright, by pointer, touch
   or keyboard. */
.home .hp-marquee:hover .hp-marquee-track,
.home .hp-marquee.is-paused .hp-marquee-track { animation-play-state: paused; }
.home .hp-marquee-toggle {
  position: absolute;
  top: 50%;
  right: clamp(0.5rem, 1.5vw, 1.25rem);
  z-index: 3;
  width: 44px;
  height: 44px;
  margin-top: -22px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--ink-soft);
  cursor: pointer;
  transition: color 0.35s var(--ease);
}
.home .hp-marquee-toggle::before {
  content: "";
  position: absolute;
  inset: 10px;
  border: 1px solid var(--rule-hi);
  border-radius: 50%;
  background: var(--bg);
  transition: border-color 0.35s var(--ease);
}
.home .hp-marquee-toggle:hover { color: var(--ink); }
.home .hp-marquee-toggle:hover::before { border-color: var(--ink-soft); }
/* The hit area is 44px but the visible disc is 24px: the focus ring hugs the
   disc instead of floating around the invisible box. */
.home .hp-marquee-toggle:focus-visible { outline: none; }
.home .hp-marquee-toggle:focus-visible::before {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--accent);
}
.home .hp-marquee-toggle svg { position: relative; width: 9px; height: 9px; }
.home .hp-marquee-toggle .hp-i-play,
.home .hp-marquee.is-paused .hp-i-pause { display: none; }
.home .hp-marquee.is-paused .hp-i-play { display: block; }
@media (prefers-reduced-motion: reduce) { .home .hp-marquee-toggle { display: none; } }
@media (prefers-reduced-motion: reduce) { .home .hp-marquee-track { animation: none; } }

/* ---------- Statement ---------- */

/* Everything after the hero scrolls over it on an opaque ground. The pinned
   hero's scroll budget can outlast the marquee and the accent band (their
   height follows the copy), and a transparent section would let the pin's
   last frame, cue and ring included, show through below the orange. */
.home #hpHero ~ .ds-section:not(#approach, #contact),
.home .site-footer { position: relative; z-index: 2; background: var(--bg); }

/* Full-bleed accent band, same treatment as the project pages' .ds-band. */
.home #approach {
  position: relative;
  z-index: 2;
  background: var(--accent);
  color: var(--on-accent);
  border-top-color: transparent;
}
.home #approach .ds-label { color: var(--on-accent); }
.home #approach .ds-label .ds-bracket { color: var(--on-accent); }
.home #approach .hp-statement,
.home #approach .hp-statement b,
.home #approach .hp-statement em { color: var(--ink); }
.home #approach .hp-statement-aside { color: var(--on-accent); }
/* Bone ink on Kiln Orange is 3.3:1, which only passes as large text. The
   statement's clamp stays under 24px below ~940px, so there it steps up to
   700: at 18.66px and bold it is still large text under WCAG 1.4.3. */
@media (max-width: 940px) {
  .home #approach .hp-statement,
  .home #approach .hp-statement b { font-weight: 700; }
}

.home .hp-statement-grid {
  display: grid;
  gap: 2rem;
}
@media (min-width: 900px) {
  .home .hp-statement-grid { grid-template-columns: 180px minmax(0, 1fr); gap: clamp(2rem, 5vw, 5rem); }
}
.home .hp-statement {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(1.35rem, 2.55vw, 2.35rem);
  line-height: 1.24;
  letter-spacing: -0.025em;
  color: var(--ink-dim);
  margin: 0;
  max-width: 24ch;
  text-wrap: pretty;
}
.home .hp-statement b { color: var(--ink); font-weight: 600; }
.home .hp-statement em {
  font-family: var(--serif);
  font-style: italic;
  font-weight: 400;
  color: var(--ink);
  letter-spacing: -0.005em;
}
.home .hp-statement-aside {
  margin: 2.5rem 0 0;
  max-width: 46ch;
  color: var(--ink-soft);
  font-size: 1.1rem;
  line-height: 1.6;
}
/* The motto sits under the aside, at twice the statement's size. */
.home .hp-statement-motto {
  margin: clamp(3rem, 6vw, 5.5rem) 0 0;
  font-family: var(--serif);
  font-style: italic;
  font-weight: 400;
  font-size: clamp(2.7rem, 5.1vw, 4.7rem);
  line-height: 1.02;
  letter-spacing: -0.015em;
  color: var(--ink);
  text-wrap: balance;
}
.home .hp-statement-motto em { font-style: inherit; }

/* Above 1100px the statement and its aside sit side by side, so the band is
   composed across its full measure rather than trailing off into empty space. */
@media (min-width: 1100px) {
  .home #approach .hp-statement-grid > div {
    display: grid;
    grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
    gap: clamp(2rem, 4vw, 4.5rem);
    align-items: start;
  }
  .home #approach .hp-statement { max-width: 20ch; }
  .home #approach .hp-statement-aside { margin-top: 0.55rem; max-width: 40ch; }
  /* The side column stretches to the statement's height and pushes the motto
     to the bottom, so both columns end on the same line. */
  .home #approach .hp-statement-grid > div { align-items: stretch; }
  .home #approach .hp-statement-side {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    gap: 2.5rem;
  }
  .home #approach .hp-statement-motto { margin-top: 0; }
}

/* ---------- Work grid ---------- */

.home .hp-work-grid {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: clamp(2.75rem, 5vw, 4.5rem) var(--col-gap);
}
@media (max-width: 759px) {
  .home .hp-work-grid { grid-template-columns: 1fr; }
  .home .hp-work { grid-column: 1 / -1 !important; margin-top: 0 !important; }
}

.home .hp-work {
  grid-column: span 6;
  display: block;
  text-decoration: none;
}
/* Every project shares the Verona square, and the frame that holds it never
   changes size — no scroll zoom. */
.home .hp-work-media {
  display: block;
  position: relative;
  aspect-ratio: 1 / 1;
  border-radius: 8px;
}
.home .hp-work-frame {
  position: absolute;
  inset: 0;
  overflow: hidden;
  border-radius: inherit;
  background: var(--card-bg);
}
/* The image is taller than the fixed frame, so there is vertical room for the
   content to travel: JS slides it (through `translate`, which composes with
   the hover zoom's `transform` and stays off the transition, so scroll is
   tracked 1:1 without layout) as the card crosses the
   viewport — drifting up as the card rises, so it reads as the picture rising
   into the frame from below and out through the top, with the frame and the
   image's scale held constant. Being taller than the square, the image is
   effectively enlarged to give that travel its room. */
.home .hp-work-media img {
  position: absolute;
  left: 0; right: 0;
  top: -15%;
  width: 100%;
  height: 130%;
  object-fit: cover;
  display: block;
  transform: scale(1.001);
  transition: transform 0.85s var(--ease), filter 0.6s var(--ease);
}
.home .hp-work-frame::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  border: 1px solid rgba(243, 241, 236, 0.08);
  pointer-events: none;
  transition: border-color 0.45s var(--ease);
}
.home .hp-work:hover .hp-work-media img { transform: scale(1.045); }
.home .hp-work:hover .hp-work-frame::after { border-color: var(--hairline-hi); }

/* Hover intro: the lower part of the picture blurs and darkens, and a short
   line introducing the project rises into it. The blur is its own layer,
   masked so it fades in from the bottom edge rather than stopping on a line. */
.home .hp-work-hover {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: flex;
  align-items: flex-end;
  padding: clamp(1.25rem, 2.4vw, 2rem);
  pointer-events: none;
}
/* The veil is a long, eased fade rather than a band: the blur holds fully
   only in the lowest strip (--veil-solid) and thins out along a smooth curve
   until --veil-end, so there is no visible edge where it stops. With a
   pointer the intro sits in the lowest ~21% of a large card; on touch the
   same copy climbs to ~40% of a narrow one, so the whole curve moves up.
   The darkening is light; a soft shadow under the letters keeps the copy
   legible where the veil is thin. */
.home .hp-work-hover {
  --veil-solid: 12%;
  --veil-end: 48%;
}
@media (hover: none) {
  .home .hp-work-hover {
    --veil-solid: 30%;
    --veil-end: 80%;
  }
}
.home .hp-work-hover::before {
  --veil-span: calc(var(--veil-end) - var(--veil-solid));
  --veil-curve:
    #000 var(--veil-solid),
    rgba(0, 0, 0, 0.93) calc(var(--veil-solid) + var(--veil-span) * 0.14),
    rgba(0, 0, 0, 0.78) calc(var(--veil-solid) + var(--veil-span) * 0.29),
    rgba(0, 0, 0, 0.57) calc(var(--veil-solid) + var(--veil-span) * 0.45),
    rgba(0, 0, 0, 0.35) calc(var(--veil-solid) + var(--veil-span) * 0.61),
    rgba(0, 0, 0, 0.17) calc(var(--veil-solid) + var(--veil-span) * 0.77),
    rgba(0, 0, 0, 0.05) calc(var(--veil-solid) + var(--veil-span) * 0.9),
    transparent var(--veil-end);
  content: "";
  position: absolute;
  inset: 0;
  -webkit-backdrop-filter: blur(16px);
  backdrop-filter: blur(16px);
  background: linear-gradient(to top, var(--veil-dark), var(--veil) var(--veil-solid), transparent var(--veil-end));
  -webkit-mask-image: linear-gradient(to top, var(--veil-curve));
  mask-image: linear-gradient(to top, var(--veil-curve));
  opacity: 0;
  transition: opacity 0.6s var(--ease);
}
.home .hp-work-hover p {
  position: relative;
  margin: 0;
  max-width: 28ch;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(1.15rem, 1.6vw, 1.5rem);
  line-height: 1.22;
  letter-spacing: -0.02em;
  color: var(--ink);
  text-shadow: 0 1px 14px rgba(14, 10, 8, 0.45), 0 0 2px rgba(14, 10, 8, 0.25);
  text-wrap: balance;
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 0.5s var(--ease), transform 0.7s var(--ease);
}
@media (hover: hover) {
  .home .hp-work:hover .hp-work-hover::before,
  .home .hp-work:focus-visible .hp-work-hover::before { opacity: 1; }
  .home .hp-work:hover .hp-work-hover p,
  .home .hp-work:focus-visible .hp-work-hover p {
    opacity: 1;
    transform: none;
    transition-delay: 0.08s;
  }
}
@media (hover: none) {
  .home .hp-work.is-inview .hp-work-hover::before { opacity: 1; }
  .home .hp-work.is-inview .hp-work-hover p {
    opacity: 1;
    transform: none;
    transition-delay: 0.08s;
  }
  .home .hp-work.is-inview h3 span { background-position: 0 100%; }
  .home .hp-work.is-inview .hp-work-no { color: var(--accent); }
}
@media (prefers-reduced-motion: reduce) {
  .home .hp-work-hover p { transform: none; }
}

/* Staggered two-up rhythm: the right column drops, so a pair never reaches the
   same scroll position together and the stretch reads across it. */
.home .hp-work:nth-child(even) { margin-top: clamp(3rem, 6vw, 6rem); }

/* Reduced motion: hold the image centred, no scroll-driven travel (JS also opts out). */
@media (prefers-reduced-motion: reduce) {
  .home .hp-work-media img { translate: none !important; }
}

.home .hp-work-info {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: start;
  gap: 0 1rem;
  padding-top: 1.1rem;
  margin-top: 1.1rem;
  border-top: 1px solid var(--rule);
  position: relative;
}

.home .hp-work-no {
  font-family: var(--micro);
  font-stretch: var(--micro-wide);
  font-weight: 500;
  font-size: var(--fs-micro);
  letter-spacing: 0.08em;
  color: var(--ink-dim);
  padding-top: 0.32rem;
  transition: color 0.4s var(--ease);
}
.home .hp-work:hover .hp-work-no { color: var(--accent); }

.home .hp-work h3 {
  margin: 0 0 0.3rem;
  font-size: clamp(1.15rem, 1.15vw + 0.85rem, 1.6rem);
  font-weight: 700;
  letter-spacing: -0.025em;
  line-height: 1.15;
}
/* On hover the title fills with the accent from the bottom up: a two-tone
   gradient twice the title's height, clipped to the letters, slides up. */
.home .hp-work h3 span {
  background-image: linear-gradient(to top, var(--accent) 50%, var(--ink) 50%);
  background-size: 100% 200%;
  background-position: 0 0;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  transition: background-position 0.55s var(--ease);
}
.home .hp-work:hover h3 span,
.home .hp-work:focus-visible h3 span { background-position: 0 100%; }
/* High-contrast modes drop background images, which would leave the
   clipped titles with no ink at all. Paint them as plain system text. */
@media (forced-colors: active) {
  .home .hp-work h3 span {
    background: none;
    -webkit-text-fill-color: CanvasText;
  }
}
.home .hp-work-cat {
  display: block;
  color: var(--ink-soft);
  font-size: var(--fs-small);
  line-height: 1.5;
}
.home .hp-work-year {
  font-family: var(--micro);
  font-stretch: var(--micro-wide);
  font-weight: 500;
  font-size: var(--fs-micro);
  letter-spacing: 0.08em;
  color: var(--ink-dim);
  padding-top: 0.32rem;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
}
.home .hp-work-year svg {
  width: 10px; height: 10px;
  opacity: 0;
  transform: translate(-3px, 3px);
  transition: opacity 0.4s var(--ease), transform 0.4s var(--ease);
}
.home .hp-work:hover .hp-work-year svg { opacity: 1; transform: translate(0, 0); }

/* ---------- Capabilities ---------- */

.home .hp-caps { border-top: 1px solid var(--rule); }
.home .hp-cap {
  display: grid;
  grid-template-columns: 3.25rem minmax(0, 1fr);
  gap: 0 1rem;
  padding: 1.5rem 0;
  border-bottom: 1px solid var(--rule);
  position: relative;
}
/* The row's content insets on hover while its rules stay put. Done with a
   transform on the children rather than padding on the row, so the hover
   runs on the compositor instead of relayouting the section every frame. */
.home .hp-cap > * { transition: transform 0.5s var(--ease); }
@media (min-width: 760px) {
  .home .hp-cap { grid-template-columns: 3.25rem minmax(0, 0.78fr) minmax(0, 1fr); padding: 1.85rem 0; }
  .home .hp-cap:hover > * { transform: translateX(1rem); }
}
.home .hp-cap::before {
  content: "";
  position: absolute;
  left: 0; bottom: -1px;
  width: 100%;
  height: 1px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 0.7s var(--ease);
}
.home .hp-cap:hover::before { transform: scaleX(1); }
.home .hp-cap-no {
  font-family: var(--micro);
  font-stretch: var(--micro-wide);
  font-weight: 500;
  font-size: var(--fs-micro);
  letter-spacing: 0.06em;
  color: var(--ink-dim);
  padding-top: 0.4rem;
  transition: color 0.4s var(--ease), transform 0.5s var(--ease);
}
.home .hp-cap:hover .hp-cap-no { color: var(--accent); }
.home .hp-cap h3 {
  margin: 0 0 0.4rem;
  font-size: clamp(1.1rem, 1.6vw, 1.5rem);
  font-weight: 700;
  letter-spacing: -0.025em;
}
@media (min-width: 760px) { .home .hp-cap h3 { margin-bottom: 0; } }
.home .hp-cap p {
  margin: 0;
  color: var(--ink-soft);
  font-size: var(--fs-small);
  line-height: 1.6;
  max-width: 56ch;
}
@media (max-width: 759px) { .home .hp-cap p { grid-column: 2; } }

/* ---------- Contact ---------- */

.home .hp-contact { padding-bottom: clamp(4rem, 8vh, 6rem); }
.home .hp-contact-lead {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.75rem, 4.2vw, 3.4rem);
  line-height: 1.06;
  letter-spacing: -0.035em;
  margin: 0 0 2.5rem;
  max-width: 17ch;
}
.home .hp-contact-lead em {
  font-family: var(--serif);
  font-style: italic;
  font-weight: 400;
  color: var(--accent);
}
.home .hp-mail {
  display: inline-flex;
  align-items: center;
  gap: 0.9rem;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(1.05rem, 2.2vw, 1.75rem);
  letter-spacing: -0.02em;
  text-decoration: none;
  color: var(--ink);
  padding-bottom: 0.45rem;
  border-bottom: 1px solid var(--rule-hi);
  transition: color 0.4s var(--ease), border-color 0.4s var(--ease);
  word-break: break-word;
}
.home .hp-mail-row {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 1.1rem 1.75rem;
}
.home .hp-mail:hover { color: var(--accent); border-color: var(--accent); }
.home .hp-mail svg { width: 14px; height: 14px; flex: none; transition: transform 0.45s var(--ease); }
.home .hp-mail:hover svg { transform: translate(3px, -3px); }

.home .hp-links {
  list-style: none;
  padding: 0;
  margin: 3rem 0 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem 2rem;
}
.home .hp-links a {
  font-family: var(--micro);
  font-stretch: var(--micro-wide);
  font-weight: 500;
  font-size: var(--fs-micro);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-soft);
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  transition: color 0.35s var(--ease);
}
.home .hp-links a:hover { color: var(--ink); }
@media (pointer: coarse) {
  .home .hp-links { gap: 1.5rem 2rem; }
  .home .hp-links a { position: relative; }
  .home .hp-links a::before { content: ""; position: absolute; inset: -12px -8px; }
  .home .hp-mail { padding-block: 0.6rem; }
  .home .ds-copy { position: relative; }
  .home .ds-copy::before { content: ""; position: absolute; inset: -14px -8px; }
}
.home .hp-links a svg { width: 9px; height: 9px; transition: transform 0.4s var(--ease); }
.home .hp-links a:hover svg:not(.ds-link-i) { transform: translate(2px, -2px); }
/* Service icons lead each link at cap-plus size; the trailing arrow still
   marks the ones that leave the site. */
.home .hp-links a .ds-link-i { width: 16px; height: 16px; flex: none; margin-right: 0.15rem; }

/* Contact closes the page as a second accent band. Everything on it is
   on-kiln at full strength (Full-Strength-On-Orange Rule); bone ink appears
   only on the lead, which is always display size. The orange marks used
   elsewhere (hover colour, focus ring, selection, the Copied state) would
   vanish here, so each one switches to on-kiln. */
.home #contact {
  position: relative;
  z-index: 2;
  background: var(--accent);
  color: var(--on-accent);
  border-top-color: transparent;
}
.home #contact .ds-label,
.home #contact .ds-label .ds-bracket,
.home #contact .hp-mail,
.home #contact .hp-mail:hover,
.home #contact .hp-links a,
.home #contact .ds-copy,
.home #contact .ds-copy:hover,
.home #contact .ds-copy.is-done { color: var(--on-accent); }
/* The lead is bone ink: 3.3:1 on the orange, which passes because it never
   drops below 28px at weight 700. */
.home #contact .hp-contact-lead,
.home #contact .hp-contact-lead em { color: var(--ink); }
.home #contact .hp-mail { border-bottom-color: rgba(27, 15, 8, 0.35); }
.home #contact .hp-mail:hover { border-bottom-color: var(--on-accent); }
.home #contact .hp-links a { font-weight: 600; text-underline-offset: 4px; }
.home #contact .hp-links a:hover { text-decoration: underline; text-decoration-thickness: 1px; }
.home #contact :focus-visible { outline-color: var(--on-accent); }
.home #contact ::selection { background: var(--on-accent); color: var(--accent); }



@media (prefers-reduced-motion: reduce) {
  .home .hp-cap > * { transition: none; }
  .home .hp-title .w > span,
  .home .hp-eyebrow,
  .home .hp-lede,
  .home .hp-facts,
  .home .hp-plate-media,
  .home .hp-portrait img {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
}

.home .hp-work-grid { align-items: start; }

/* Jumping to #work (nav link, or /#work from another page) puts the section
   top at the viewport edge, so the orange Approach band ends above the
   window. Any less and the band shows as a strip, or tints the translucent
   nav from behind. The section's own top padding (clamp(5rem, 11vh, 9rem))
   keeps the label clear of the nav. */
.home #work { scroll-margin-top: -1px; }   /* -1px swallows sub-pixel rounding */


