/* ==========================================================================
   AURELIS ENERGY — Hero
   ========================================================================== */

:root {
  --ink: #0d0f12;
  --paper: #f4f2ee;
  --line: rgba(255, 255, 255, 0.28);
  --font: "Segoe UI", -apple-system, BlinkMacSystemFont, "Helvetica Neue",
    Arial, sans-serif;
}

*,
*::before,
*::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--ink);
  color: #fff;
  font-family: var(--font);
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
}

/* ==========================================================================
   HERO
   ========================================================================== */

.hero {
  position: relative;
  width: 100%;
  height: 100vh;
  height: 100svh;
  overflow: hidden;
  background: #11141a;
}

.hero__canvas {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: block;
  width: 100%;
  height: 100%;
}

/* Sits OVER the canvas and matches its framing exactly, so the swap is
   invisible. Removed once the canvas is confirmed painting.

   It used to sit underneath, at z-index 0, which made it useless for the one
   job it has: the canvas is created with alpha:false, so it is opaque black
   from the moment it gets a context, and an opaque layer above this one
   hides it completely. A Hero whose canvas never managed to paint showed
   black with the real first frame sitting right behind it. Above the canvas
   it shows the photograph instead, and hero.js fades it out as soon as it
   has read real pixels back off the canvas. */
.hero__fallback {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 50%;
  transition: opacity 0.3s linear;
}

.hero__fallback[hidden] { display: none; }

/* On a phone the canvas stops at a 40% width floor and lets the remainder
   fall to the bottom. The fallback has to land on the same framing, or a
   phone that never gets a working canvas would show the very crop this is
   meant to avoid. 250% width is 1 / 0.4, anchored top-centre; .hero clips
   the overflow and its background fills the band, exactly like the canvas. */
@media (max-width: 767px) {
  .hero__fallback {
    inset: auto;
    top: 0;
    left: -75%;
    width: 250%;
    height: auto;
    object-fit: fill;
  }
}

/* Legibility scrim only — no glow.

   This carried no z-index while the canvas carries 1, which in one stacking
   context puts a positive layer above an auto one: the scrim was painting
   underneath an opaque canvas and had never been visible. It is stacked
   properly now and the values were eased back, because at full strength they
   were written for a gradient nobody was seeing. The third gradient is new:
   the headline sits bottom-left over a bright daytime roof, and a very slight
   lean into that corner is what lets the type stay thin. */
.hero__scrim {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background:
    linear-gradient(to bottom, rgba(6, 8, 11, 0.42) 0%, rgba(6, 8, 11, 0) 24%),
    linear-gradient(to top, rgba(6, 8, 11, 0.68) 0%, rgba(6, 8, 11, 0) 48%),
    linear-gradient(to right, rgba(6, 8, 11, 0.34) 0%, rgba(6, 8, 11, 0) 46%);
}

/* The narrative overlay. Its gradient is the shape of the darkening; the
   depth is the element's opacity, which hero-copy.js drives from the Hero's
   own scroll progress. Weakest across the middle on purpose — the house keeps
   its light while the ground and sky close in around the type. */
.hero__veil {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  opacity: 0;
  background: linear-gradient(
    to bottom,
    rgba(4, 6, 10, 0.7) 0%,
    rgba(4, 6, 10, 0.34) 42%,
    rgba(4, 6, 10, 1) 100%
  );
  will-change: opacity;
}

/* Nothing on a wide screen: there is no band to hide. */
.hero__seam { display: none; }

/* On a phone the canvas stops at the 40% width floor and the rest of the
   frame is filled with the backdrop colour, which leaves a hard horizontal
   edge partway down. The headline is too tall to clear it - there is no room
   under it for a label, two lines, a sentence and two buttons - so instead of
   moving the type, the edge is dissolved into the band.

   Its position is not a guess. hero.js scales the frame by cw / (iw * 0.4),
   so a 2560x1440 source ends at 1440 / (2560 * 0.4) = 1.40625 of the
   viewport width, which is what the offset below says. If a tall enough
   screen leaves no band, this lands past the bottom and .hero clips it. */
@media (max-width: 767px) {
  .hero__seam {
    display: block;
    position: absolute;
    left: 0;
    right: 0;
    top: calc(140.625vw - 170px);
    height: 170px;
    z-index: 2;
    pointer-events: none;
    background: linear-gradient(
      to bottom,
      rgba(17, 20, 26, 0) 0%,
      rgba(17, 20, 26, 0.72) 62%,
      #11141a 100%
    );
  }
}

/* --------------------------------- Nav ---------------------------------- */

.nav {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: 30px clamp(20px, 4.5vw, 64px);
}

.nav__brand {
  display: flex;
  align-items: baseline;
  gap: 8px;
  text-decoration: none;
  color: #fff;
  line-height: 1;
}

.nav__brand-a {
  font-size: 17px;
  font-weight: 600;
  letter-spacing: 0.24em;
}

.nav__brand-b {
  font-size: 17px;
  font-weight: 200;
  letter-spacing: 0.24em;
  opacity: 0.82;
}

.nav__links {
  display: flex;
  gap: clamp(22px, 2.6vw, 44px);
}

.nav__links a {
  color: rgba(255, 255, 255, 0.86);
  text-decoration: none;
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  padding-bottom: 3px;
  border-bottom: 1px solid transparent;
  transition: color 0.3s ease, border-color 0.3s ease;
}

.nav__links a:hover {
  color: #fff;
  border-bottom-color: var(--line);
}

.nav__cta {
  color: #fff;
  text-decoration: none;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  padding: 12px 22px;
  border: 1px solid var(--line);
  transition: background-color 0.3s ease, border-color 0.3s ease;
}

.nav__cta:hover {
  background: rgba(255, 255, 255, 0.1);
  border-color: rgba(255, 255, 255, 0.55);
}

/* ------------------------------- Content -------------------------------- */

.hero__content {
  position: absolute;
  z-index: 3;
  left: clamp(20px, 5vw, 78px);
  right: clamp(20px, 5vw, 78px);
  bottom: clamp(44px, 9vh, 96px);
  max-width: 1060px;
}

/* A hairline before the label, the same one sections 01 and 02 use. It is
   what ties the Hero to the rest of the site rather than decoration. */
.hero__eyebrow {
  display: flex;
  align-items: center;
  margin: 0 0 clamp(20px, 2.8vh, 32px);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.7);
}

.hero__rule {
  flex: 0 0 auto;
  width: clamp(28px, 4vw, 60px);
  height: 1px;
  margin-right: 18px;
  background: rgba(255, 255, 255, 0.42);
}

/* Bigger, thinner, tighter. At this size the weight does the luxury and the
   negative tracking does the architecture; anything heavier reads as a
   headline instead of a building. */
.hero__title {
  margin: 0;
  font-size: clamp(3.1rem, 9.2vw, 9.2rem);
  font-weight: 200;
  line-height: 0.89;
  letter-spacing: -0.035em;
  text-transform: uppercase;
  /* Measured under the type on the opening frame: the average is dark enough
     for white at about 10:1, but roughly 4% of it is glass catching the sun at
     240. A thin stroke crossing one of those would disappear into it. This is
     the same tight shadow the technical labels in section 02 use, scaled up -
     it does nothing where the picture is already dark, and no glow. */
  text-shadow: 0 1px 10px rgba(4, 6, 10, 0.45);
}

/* One line of the headline. Each stage is written as two of these, and the
   swap animates them, so they need to be their own boxes.

   The line breaks are the composition, not the browser's business: every
   stage is two lines so that the sentence and the buttons below never move
   while the words change. Left to itself, "what follows." wraps on a 320px
   phone and that stage alone becomes three lines high. The type scale below
   is set so the longest line clears the narrowest screen. */
.hero__line {
  display: block;
  white-space: nowrap;
}

.hero__sub {
  margin: clamp(22px, 3.2vh, 36px) 0 0;
  max-width: 46ch;
  font-size: clamp(1rem, 1.3vw, 1.22rem);
  font-weight: 300;
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.8);
  text-shadow: 0 1px 8px rgba(4, 6, 10, 0.5);
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  margin-top: clamp(30px, 4.4vh, 46px);
}

.btn {
  display: inline-block;
  text-decoration: none;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  padding: 16px 30px;
  border: 1px solid transparent;
  transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease;
}

.btn--solid {
  background: #fff;
  color: var(--ink);
}

.btn--solid:hover { background: rgba(255, 255, 255, 0.86); }

.btn--ghost {
  color: #fff;
  border-color: var(--line);
}

.btn--ghost:hover {
  background: rgba(255, 255, 255, 0.1);
  border-color: rgba(255, 255, 255, 0.55);
}

/* ==========================================================================
   01 / PHILOSOPHY — editorial transition
   ========================================================================== */

.ph {
  position: relative;
  z-index: 1;
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  align-items: center;
  overflow: hidden;
  padding: clamp(110px, 18vh, 210px) clamp(20px, 4.5vw, 64px)
    clamp(100px, 16vh, 180px);
  /* Carries the Hero's night tone into the section, then lifts slightly. */
  background: linear-gradient(180deg, #05070a 0%, #0a0d13 42%, #0b0e15 100%);
  color: #fff;
}

/* Passage from the Hero: a dark sheet the section emerges from. */
.ph__veil {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(180deg, #05070a 0%, rgba(5, 7, 10, 0) 62%);
}

.ph__inner {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 1560px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  column-gap: clamp(14px, 2vw, 32px);
}

/* ---------------------------------------------------------------- label -- */

.ph__label {
  grid-column: 1 / span 6;
  display: flex;
  align-items: center;
  margin: 0 0 clamp(52px, 8vh, 104px);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.5);
}

.ph__rule {
  width: clamp(36px, 5vw, 76px);
  height: 1px;
  margin-right: 20px;
  background: rgba(255, 255, 255, 0.42);
  transform: scaleX(0);
  transform-origin: left center;
}

.ph__label-text {
  opacity: 0;
  visibility: hidden;
}

/* ------------------------------------------------------------- headline -- */

.ph__head {
  grid-column: 1 / span 12;
  margin: 0;
  font-size: clamp(2.7rem, 9vw, 8.4rem);
  font-weight: 200;
  line-height: 0.94;
  letter-spacing: -0.025em;
}

/* Each line is its own mask; the inner span slides up out of it. */
.ln {
  display: block;
  overflow: hidden;
  padding-bottom: 0.1em;
  margin-bottom: -0.1em;
}

.ln__i {
  display: block;
  transform: translateY(105%);
  will-change: transform;
}

/* Asymmetry: the closing statement steps in from the left edge. */
.ln--shift { margin-left: 27%; }
.ln:nth-child(4) { margin-top: clamp(26px, 4.2vh, 58px); }

.ln__em {
  font-family: Georgia, "Times New Roman", Times, serif;
  font-style: italic;
  font-weight: 400;
  font-size: 1.03em;
  letter-spacing: -0.012em;
}

/* -------------------------------------------------------------- subtext -- */

.ph__sub {
  grid-column: 7 / span 5;
  margin: clamp(56px, 9vh, 112px) 0 0;
  font-size: clamp(0.95rem, 1.15vw, 1.08rem);
  font-weight: 300;
  line-height: 1.62;
  color: rgba(255, 255, 255, 0.58);
  opacity: 0;
  visibility: hidden;
}

/* No GSAP (script blocked or failed): show everything, unanimated. */
.no-gsap .ln__i { transform: none; }
.no-gsap .ph__rule { transform: scaleX(1); }
.no-gsap .ph__label-text,
.no-gsap .ph__sub { opacity: 1; visibility: visible; }

/* ==========================================================================
   02 / TECHNOLOGY
   ========================================================================== */

.tc {
  position: relative;
  z-index: 1;
  overflow: hidden;
  padding: clamp(104px, 15vh, 180px) clamp(20px, 4.5vw, 64px)
    clamp(104px, 15vh, 180px);
  /* Starts on the exact tone Philosophy closes on, then lifts to a graphite
     surface: same direction, one step brighter. */
  background: linear-gradient(180deg, #0b0e15 0%, #10141b 34%, #13171f 100%);
  color: #fff;
}

/* Scrubbed wash that introduces the lighter surface as the section enters. */
.tc__lift {
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0;
  background: linear-gradient(
    180deg,
    rgba(255, 255, 255, 0) 0%,
    rgba(255, 255, 255, 0.035) 55%,
    rgba(255, 255, 255, 0.05) 100%
  );
}

.tc__inner {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 1560px;
  margin: 0 auto;
}

/* ------------------------------------------------------- label + divider -- */

.tc__top {
  display: flex;
  align-items: center;
  gap: clamp(20px, 3vw, 44px);
  margin-bottom: clamp(48px, 7vh, 96px);
}

.tc__label {
  display: flex;
  align-items: center;
  margin: 0;
  flex: 0 0 auto;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.5);
}

.tc__rule {
  width: clamp(36px, 5vw, 76px);
  height: 1px;
  margin-right: 20px;
  background: rgba(255, 255, 255, 0.42);
  transform: scaleX(0);
  transform-origin: left center;
}

.tc__label-text {
  opacity: 0;
  visibility: hidden;
}

/* The architectural grid detail: one hairline running to the edge. */
.tc__divider {
  flex: 1 1 auto;
  height: 1px;
  background: rgba(255, 255, 255, 0.13);
  transform: scaleX(0);
  transform-origin: left center;
}

/* ==== THE PHOTOGRAPHS =====================================================
   Real filenames, kept exactly as they are on disk. To swap either photo,
   change only the url on its line.
   ========================================================================== */
.tc__main .tc__frame {
  --photo: url("../assets/technology/technology-main.webp");
}

.tc__d1 .tc__frame {
  --photo: url("../assets/technology/technology-detail.webp");
}
/* ========================================================================== */

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

.tc__grid {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  column-gap: clamp(14px, 2vw, 32px);
}

.tc__fig {
  position: relative;
  margin: 0;
}

/* The installers shot dominates: 9 of 12 columns, offset right. */
.tc__main {
  grid-column: 4 / -1;
  grid-row: 1;
  z-index: 1;
}

.tc__main .tc__frame { height: clamp(320px, 43vw, 600px); }

/* The connector close-up steps back in from the left, deliberately smaller. */
.tc__d1 {
  grid-column: 2 / span 6;
  grid-row: 2;
  z-index: 2;
  margin-top: clamp(130px, 15vh, 212px);
}

.tc__d1 .tc__frame { height: clamp(240px, 27vw, 380px); }

/* ---------------------------------------------------------------- frame -- */

.tc__frame {
  position: relative;
  overflow: hidden;
  background: #151a23;
  border: 1px solid rgba(255, 255, 255, 0.08);
  clip-path: inset(0% 0% 100% 0%);
}

.tc__photo {
  position: absolute;
  inset: 0;
  z-index: 1;
  background-image: var(--photo);
  background-size: cover;
  background-position: center;
  will-change: transform;
}

/* Keeps the headline readable where it crosses the main frame. */
.tc__scrim {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background: linear-gradient(
    to top,
    rgba(6, 8, 11, 0.68) 0%,
    rgba(6, 8, 11, 0) 46%
  );
}

/* The close-up is bright at both ends, so its notation needs cover there. */
.tc__scrim--detail {
  background: linear-gradient(
    to top,
    rgba(6, 8, 11, 0.6) 0%,
    rgba(6, 8, 11, 0.1) 46%,
    rgba(6, 8, 11, 0.45) 100%
  );
}

/* ---------------------------------------------------- technical notation -- */

.tc__annos {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
}

.anno {
  position: absolute;
  display: flex;
  align-items: center;
  gap: 12px;
}

/* Fallback only. Each label's real target is a point inside its photograph,
   written on the element as data-u / data-v and resolved by technology.js
   against the live cover crop. These percentages are what shows if that
   script never runs: roughly right, and never off the frame. */
.anno--a { top: 76%; left: 37%; }
.anno--b { top: 55%; left: 34%; }
.anno--c { top: 45%; left: 52%; }
.anno--d { top: 66%; left: 30%; }

/* Label on the left, dot on the right: the mirrored half of the same
   drawing, so a line near the right edge still reads inward. */
.anno--flip { flex-direction: row-reverse; }
.anno--flip .anno__svg { transform: scaleX(-1); }

.anno__svg { overflow: visible; flex: 0 0 auto; }

.tc__d1 .anno__svg { width: 76px; }

.anno__dot {
  fill: rgba(255, 255, 255, 0.92);
  opacity: 0;
  visibility: hidden;
}

.anno__line {
  stroke: rgba(255, 255, 255, 0.62);
  stroke-width: 1;
  stroke-dasharray: 128;
  stroke-dashoffset: 128;
}

.anno__txt {
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.22em;
  white-space: nowrap;
  color: #fff;
  text-shadow: 0 1px 6px rgba(6, 8, 11, 0.75);
  opacity: 0;
  visibility: hidden;
}

/* ------------------------------------------------------------- headline -- */

/* Shares grid-row 1 with the main frame, so it crosses its lower-left edge. */
.tc__head {
  grid-column: 1 / span 8;
  grid-row: 1;
  align-self: end;
  position: relative;
  z-index: 4;
  margin: 0 0 clamp(-84px, -4.6vw, -34px);
  font-size: clamp(2.5rem, 7.4vw, 6.6rem);
  font-weight: 200;
  line-height: 0.95;
  letter-spacing: -0.025em;
}

/* ---------------------------------------------------------- closing text -- */

.tc__sub {
  grid-column: 9 / -1;
  grid-row: 2;
  align-self: end;
  margin: 0;
  font-size: clamp(0.95rem, 1.15vw, 1.08rem);
  font-weight: 300;
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.72);
  opacity: 0;
  visibility: hidden;
}

.tc__note {
  grid-column: 9 / -1;
  grid-row: 3;
  margin: clamp(26px, 4vh, 44px) 0 0;
  max-width: 34ch;
  font-size: 0.82rem;
  font-weight: 300;
  line-height: 1.66;
  color: rgba(255, 255, 255, 0.42);
  opacity: 0;
  visibility: hidden;
}

/* No GSAP: show everything, unanimated. */
.no-gsap .tc__frame { clip-path: none; }
.no-gsap .tc__rule,
.no-gsap .tc__divider { transform: scaleX(1); }
.no-gsap .tc__label-text,
.no-gsap .tc__sub,
.no-gsap .tc__note,
.no-gsap .anno__dot,
.no-gsap .anno__txt { opacity: 1; visibility: visible; }
.no-gsap .anno__line { stroke-dashoffset: 0; }

/* ==========================================================================
   03 / SOLUTIONS
   ========================================================================== */

.sol {
  position: relative;
  z-index: 1;
  /* Picks up the exact tone Technology closes on and keeps lifting. */
  background: linear-gradient(180deg, #13171f 0%, #171c25 38%, #1a1f2a 100%);
  color: #fff;
}

.sol__lift {
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0;
  background: linear-gradient(
    180deg,
    rgba(255, 255, 255, 0) 0%,
    rgba(255, 255, 255, 0.03) 60%,
    rgba(255, 255, 255, 0.045) 100%
  );
}

/* ---------------------------------------------------------------- intro -- */

.sol__intro {
  position: relative;
  z-index: 1;
  max-width: 1560px;
  margin: 0 auto;
  padding: clamp(104px, 15vh, 180px) clamp(20px, 4.5vw, 64px)
    clamp(64px, 9vh, 120px);
}

.sol__top {
  display: flex;
  align-items: center;
  gap: clamp(20px, 3vw, 44px);
  margin-bottom: clamp(48px, 7vh, 96px);
}

.sol__label {
  display: flex;
  align-items: center;
  margin: 0;
  flex: 0 0 auto;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.5);
}

.sol__rule {
  width: clamp(36px, 5vw, 76px);
  height: 1px;
  margin-right: 20px;
  background: rgba(255, 255, 255, 0.42);
  transform: scaleX(0);
  transform-origin: left center;
}

.sol__label-text {
  opacity: 0;
  visibility: hidden;
}

.sol__divider {
  flex: 1 1 auto;
  height: 1px;
  background: rgba(255, 255, 255, 0.13);
  transform: scaleX(0);
  transform-origin: left center;
}

.sol__head {
  margin: 0;
  max-width: 15ch;
  font-size: clamp(2.4rem, 6.4vw, 5.6rem);
  font-weight: 200;
  line-height: 0.98;
  letter-spacing: -0.025em;
}

/* Pushed to the opposite side of the headline, not centred under it. */
.sol__sub {
  margin: clamp(38px, 5.5vh, 70px) 0 0 auto;
  max-width: 40ch;
  font-size: clamp(0.95rem, 1.15vw, 1.08rem);
  font-weight: 300;
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.68);
  opacity: 0;
  visibility: hidden;
}

/* ==== THE PHOTOGRAPHS ======================================================
   Real filenames, kept exactly as they are on disk. --pos is each frame's
   own crop anchor: the sources are 16:9 but the frames are not, so every
   picture is anchored on what it has to keep.
   ========================================================================== */
.sol__item--1 .sol__frame {
  /* aerial house: symmetric, so the roof stays centred */
  --photo: url("../assets/solutions/solutions-residential.webp");
  --pos: 50% 50%;
}

.sol__item--2 .sol__frame {
  /* near-square frame cuts ~43% of the width, so it is pulled left to keep
     the isolators, the inverter and both batteries in shot */
  --photo: url("../assets/solutions/solutions-storage.webp");
  --pos: 33% 50%;
}

.sol__item--3 .sol__frame {
  /* barely cropped at this ratio; centred keeps both the array and the
     inverter bank */
  --photo: url("../assets/solutions/solutions-commercial.webp");
  --pos: 50% 50%;
}
/* ========================================================================== */

/* ---------------------------------------------------------------- stage -- */

/* One stage, three chapters passing through it. Not three cards. */
.sol__stage {
  position: relative;
  height: 100vh;
  height: 100svh;
  overflow: hidden;
}

.sol__track {
  position: relative;
  height: 100%;
  max-width: 1560px;
  margin: 0 auto;
}

.sol__item {
  position: absolute;
  top: 0;
  left: clamp(20px, 4.5vw, 64px);
  right: clamp(20px, 4.5vw, 64px);
  height: 100%;
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  column-gap: clamp(14px, 2vw, 32px);
  align-items: center;
}

/* Chapters two and three wait off-stage. */
.sol__item--2,
.sol__item--3 {
  opacity: 0;
  visibility: hidden;
}

.sol__fig {
  margin: 0;
  grid-row: 1;
}

/* `top` rather than a transform, so GSAP owns transform on this element. */
.sol__copy {
  grid-row: 1;
  position: relative;
  z-index: 2;
}

/* Each chapter sits slightly differently, so the stage recomposes instead of
   swapping a picture inside a fixed slot. */
.sol__item--1 .sol__fig { grid-column: 6 / -1; }
.sol__item--1 .sol__copy { grid-column: 1 / span 4; }
.sol__item--1 .sol__frame { height: 62vh; }

.sol__item--2 .sol__fig { grid-column: 7 / -1; }
.sol__item--2 .sol__copy { grid-column: 1 / span 5; top: -3vh; }
.sol__item--2 .sol__frame { height: 70vh; }

.sol__item--3 .sol__fig { grid-column: 5 / -1; }
.sol__item--3 .sol__copy { grid-column: 1 / span 4; top: 2.5vh; }
.sol__item--3 .sol__frame { height: 56vh; }

/* ---------------------------------------------------------------- frame -- */

.sol__frame {
  position: relative;
  overflow: hidden;
  background: #171d27;
  border: 1px solid rgba(255, 255, 255, 0.08);
  clip-path: inset(0% 0% 100% 0%);
}

.sol__photo {
  position: absolute;
  inset: 0;
  z-index: 1;
  background-image: var(--photo);
  background-size: cover;
  background-position: var(--pos, center);
  will-change: transform;
}

.sol__placeholder {
  position: absolute;
  inset: 0;
  z-index: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 16px;
  text-align: center;
  background-image: repeating-linear-gradient(
      90deg,
      rgba(255, 255, 255, 0.035) 0 1px,
      transparent 1px 64px
    ),
    repeating-linear-gradient(
      180deg,
      rgba(255, 255, 255, 0.035) 0 1px,
      transparent 1px 64px
    );
}

.sol__ph-title {
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.32em;
  color: rgba(255, 255, 255, 0.38);
}

.sol__ph-file {
  font-size: 10px;
  letter-spacing: 0.16em;
  color: rgba(255, 255, 255, 0.2);
}

/* ----------------------------------------------------------------- copy -- */

.sol__num {
  margin: 0 0 clamp(18px, 2.6vh, 30px);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.34em;
  color: rgba(255, 255, 255, 0.45);
  opacity: 0;
  visibility: hidden;
}

.sol__title {
  margin: 0;
  font-size: clamp(2.1rem, 4.6vw, 4.2rem);
  font-weight: 200;
  line-height: 0.98;
  letter-spacing: -0.02em;
}

.sol__desc {
  margin: clamp(26px, 3.6vh, 44px) 0 0;
  max-width: 38ch;
  font-size: clamp(0.92rem, 1.05vw, 1rem);
  font-weight: 300;
  line-height: 1.62;
  color: rgba(255, 255, 255, 0.6);
  opacity: 0;
  visibility: hidden;
}

/* ----------------------------------------------------------------- rail -- */

/* Tells you where you are in the three chapters. No arrows, no controls. */
.sol__rail {
  position: absolute;
  left: clamp(20px, 4.5vw, 64px);
  bottom: clamp(38px, 6vh, 70px);
  z-index: 3;
  display: flex;
  gap: clamp(22px, 2.6vw, 42px);
}

.sol__mark {
  position: relative;
  padding-top: 12px;
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.26em;
  color: rgba(255, 255, 255, 0.3);
}

.sol__mark-line {
  position: absolute;
  top: 0;
  left: 0;
  width: clamp(34px, 3.4vw, 52px);
  height: 1px;
  background: rgba(255, 255, 255, 0.55);
  transform: scaleX(0);
  transform-origin: left center;
}

/* No GSAP: show everything, unanimated. */
.no-gsap .sol__stage { height: auto; overflow: visible; }
.no-gsap .sol__item {
  position: static;
  height: auto;
  opacity: 1;
  visibility: visible;
  margin-bottom: 80px;
}
.no-gsap .sol__frame { clip-path: none; height: 60vh; }
.no-gsap .sol__rule,
.no-gsap .sol__divider,
.no-gsap .sol__mark-line { transform: scaleX(1); }
.no-gsap .sol__label-text,
.no-gsap .sol__sub,
.no-gsap .sol__num,
.no-gsap .sol__desc { opacity: 1; visibility: visible; }


/* ==========================================================================
   04 / ENERGY FLOW
   One photograph, held still, explained by a thin annotation system that draws
   itself as you scroll. Deliberately not a frame sequence: the Hero already
   owns that language, so this section reads as technical storytelling instead.
   ========================================================================== */

.ef {
  position: relative;
  z-index: 1;
  /* Continues from the tone Solutions closes on. */
  background: linear-gradient(180deg, #1a1f2a 0%, #1a1f2a 30%, #171c26 100%);
  color: #fff;
}

.ef__lift {
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0;
  background: linear-gradient(
    180deg,
    rgba(255, 255, 255, 0) 0%,
    rgba(255, 255, 255, 0.028) 60%,
    rgba(255, 255, 255, 0.04) 100%
  );
}

/* ---------------------------------------------------------------- intro -- */

.ef__intro {
  position: relative;
  z-index: 1;
  max-width: 1560px;
  margin: 0 auto;
  padding: clamp(104px, 15vh, 180px) clamp(20px, 4.5vw, 64px)
    clamp(56px, 8vh, 104px);
}

.ef__top {
  display: flex;
  align-items: center;
  gap: clamp(20px, 3vw, 44px);
  margin-bottom: clamp(48px, 7vh, 96px);
}

.ef__label {
  display: flex;
  align-items: center;
  margin: 0;
  flex: 0 0 auto;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.5);
}

.ef__rule {
  width: clamp(36px, 5vw, 76px);
  height: 1px;
  margin-right: 20px;
  background: rgba(255, 255, 255, 0.42);
  transform: scaleX(0);
  transform-origin: left center;
}

.ef__label-text {
  opacity: 0;
  visibility: hidden;
}

.ef__divider {
  flex: 1 1 auto;
  height: 1px;
  background: rgba(255, 255, 255, 0.13);
  transform: scaleX(0);
  transform-origin: left center;
}

.ef__head {
  margin: 0;
  max-width: 14ch;
  font-size: clamp(2.4rem, 6.4vw, 5.6rem);
  font-weight: 200;
  line-height: 0.98;
  letter-spacing: -0.025em;
}

.ef__aside {
  margin: clamp(38px, 5.5vh, 70px) 0 0 auto;
  max-width: 46ch;
}

.ef__sub {
  margin: 0;
  font-size: clamp(0.95rem, 1.15vw, 1.08rem);
  font-weight: 300;
  line-height: 1.62;
  color: rgba(255, 255, 255, 0.68);
  opacity: 0;
  visibility: hidden;
}

/* ---------------------------------------------------------------- stage -- */

/* Pinned by ScrollTrigger only, no CSS sticky. */
.ef__stage {
  position: relative;
  height: 100vh;
  height: 100svh;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

/* Capped by height as well as width so the whole house always fits: the frame
   holds the source ratio exactly, which is what keeps every marker on target. */
.ef__visual {
  position: relative;
  width: min(94%, 164svh);
}

.ef__frame {
  position: relative;
  margin: 0;
  width: 100%;
  aspect-ratio: 1672 / 941;
  overflow: hidden;
  background: #11151c;
}

.ef__photo {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  will-change: transform;
}

.ef__net {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  overflow: visible;
}

/* ------------------------------------------------------- lines and dots -- */

/* Hairlines that stay hairlines at any size: no glow, no neon. */
.ef__link {
  fill: none;
  stroke: rgba(255, 255, 255, 0.5);
  stroke-width: 1.2;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
}

/* A short segment travelling the same path, so the connection reads as flow
   rather than decoration. */
.ef__spark {
  fill: none;
  stroke: rgba(255, 255, 255, 0.92);
  stroke-width: 1.6;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
  stroke-dasharray: 0.045 0.955;
  stroke-dashoffset: 1;
  opacity: 0;
}

.ef__lead {
  fill: none;
  stroke: rgba(255, 255, 255, 0.28);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
}

.ef__node {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: 0;
  height: 0;
  opacity: 0;
  visibility: hidden;
}

.ef__dot {
  position: absolute;
  left: -4px;
  top: -4px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #fff;
  /* the dark half keeps it readable on the lit cabinets and the interior */
  box-shadow: 0 0 0 1.5px rgba(255, 255, 255, 0.4),
    0 1px 7px rgba(0, 0, 0, 0.6);
}

.ef__ring {
  position: absolute;
  left: -7px;
  top: -7px;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.72);
  opacity: 0;
}

/* Each point is the measured centre of the thing it marks, not an estimate:
   the panel face, the cabinet bank, the lit interior, the charger, the pole. */
.ef__node--solar   { --x: 40.67%; --y: 28.48%; }
.ef__node--battery { --x: 60.53%; --y: 59.72%; }
.ef__node--home    { --x: 36.96%; --y: 58.24%; }
.ef__node--ev      { --x: 78.05%; --y: 62.27%; }
.ef__node--grid    { --x: 94.98%; --y: 38.68%; }

/* ---------------------------------------------------------------- steps -- */

/* On a wide screen these sit over the photograph as annotations. */
.ef__steps {
  position: absolute;
  inset: 0;
  margin: 0;
  padding: 0;
  list-style: none;
  pointer-events: none;
}

.ef__step {
  position: absolute;
  left: var(--sx);
  top: var(--sy);
  width: max-content;
  display: grid;
  grid-template-columns: auto auto;
  column-gap: clamp(8px, 0.7vw, 12px);
  align-items: baseline;
  opacity: 0;
  visibility: hidden;
  /* Keeps the labels legible over sky, concrete or foliage without putting a
     panel on the photograph. */
  text-shadow: 0 1px 16px rgba(0, 0, 0, 0.85), 0 0 3px rgba(0, 0, 0, 0.6);
}

.ef__step-i {
  grid-column: 1;
  grid-row: 1;
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.2em;
  color: rgba(255, 255, 255, 0.45);
}

.ef__step-n {
  grid-column: 2;
  grid-row: 1;
  font-size: clamp(12px, 1vw, 15px);
  font-weight: 500;
  letter-spacing: 0.22em;
  color: #fff;
}

.ef__step-d {
  grid-column: 2;
  grid-row: 2;
  margin-top: 5px;
  font-size: clamp(11px, 0.82vw, 13px);
  font-weight: 300;
  letter-spacing: 0.01em;
  color: rgba(255, 255, 255, 0.6);
}

/* Each label sits on a quiet part of the picture - sky, planting or the
   distant hillside - never on the architecture it is pointing at. */
/* Two in the sky, three along the ground: short leaders, none of them
   crossing a connection line, none landing on empty sky. */
.ef__step--solar   { --sx: 40.67%; --sy: 10.2%; }
.ef__step--grid    { --sx: 77.75%; --sy: 15.94%; }
.ef__step--home    { --sx: 29.90%; --sy: 74.39%; }
.ef__step--battery { --sx: 58.61%; --sy: 76.73%; }
.ef__step--ev      { --sx: 79.55%; --sy: 74.39%; }

/* No GSAP: the photograph, the full network and every label, unanimated. */
.no-gsap .ef__rule,
.no-gsap .ef__divider { transform: scaleX(1); }
.no-gsap .ef__label-text,
.no-gsap .ef__sub { opacity: 1; visibility: visible; }
.no-gsap .ef__link,
.no-gsap .ef__lead { stroke-dashoffset: 0; }
.no-gsap .ef__node,
.no-gsap .ef__step { opacity: 1; visibility: visible; }

/* ==========================================================================
   05 / PROJECTS
   ========================================================================== */

.pj {
  position: relative;
  z-index: 1;
  /* Starts on the tone Energy Flow closes on and settles back toward the ink,
     so the photography sits on the darkest ground of the page. */
  background: linear-gradient(180deg, #171c26 0%, #14181f 46%, #10141a 100%);
  color: #fff;
  /* Lets the last chapter unpin and release the scroll instead of the page
     ending on the pin itself. */
  padding-bottom: clamp(110px, 16vh, 200px);
}

.pj__lift {
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0;
  background: linear-gradient(
    180deg,
    rgba(255, 255, 255, 0.035) 0%,
    rgba(255, 255, 255, 0.012) 55%,
    rgba(255, 255, 255, 0) 100%
  );
}

/* ---------------------------------------------------------------- intro -- */

.pj__intro {
  position: relative;
  z-index: 1;
  max-width: 1560px;
  margin: 0 auto;
  padding: clamp(104px, 15vh, 180px) clamp(20px, 4.5vw, 64px)
    clamp(56px, 8vh, 104px);
}

.pj__top {
  display: flex;
  align-items: center;
  gap: clamp(20px, 3vw, 44px);
  margin-bottom: clamp(48px, 7vh, 96px);
}

.pj__label {
  display: flex;
  align-items: center;
  margin: 0;
  flex: 0 0 auto;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.5);
}

.pj__rule {
  width: clamp(36px, 5vw, 76px);
  height: 1px;
  margin-right: 20px;
  background: rgba(255, 255, 255, 0.42);
  transform: scaleX(0);
  transform-origin: left center;
}

.pj__label-text {
  opacity: 0;
  visibility: hidden;
}

.pj__divider {
  flex: 1 1 auto;
  height: 1px;
  background: rgba(255, 255, 255, 0.13);
  transform: scaleX(0);
  transform-origin: left center;
}

.pj__head {
  margin: 0;
  max-width: 13ch;
  font-size: clamp(2.4rem, 6.4vw, 5.6rem);
  font-weight: 200;
  line-height: 0.98;
  letter-spacing: -0.025em;
}

.pj__aside {
  margin: clamp(38px, 5.5vh, 70px) 0 0 auto;
  max-width: 42ch;
}

.pj__sub {
  margin: 0;
  font-size: clamp(0.95rem, 1.15vw, 1.08rem);
  font-weight: 300;
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.68);
  opacity: 0;
  visibility: hidden;
}

.pj__note {
  margin: clamp(20px, 2.8vh, 32px) 0 0;
  max-width: 44ch;
  font-size: 0.82rem;
  font-weight: 300;
  line-height: 1.66;
  color: rgba(255, 255, 255, 0.42);
  opacity: 0;
  visibility: hidden;
}

/* ==== THE PHOTOGRAPHS ======================================================
   Real filenames, exactly as they sit on disk. All three sources are
   1672x941 (16:9). On desktop the frame keeps that same ratio at every stage
   of REVEAL, EXPAND and DETAILS, so nothing is ever cropped and nothing is
   ever stretched. --pos only comes into play on the taller mobile frame.
   ========================================================================== */
.pj__chapter--1 .pj__photo {
  --photo: url("../assets/projects/project-coast-house.webp");
  --pos: 50% 50%;
}

.pj__chapter--2 .pj__photo {
  --photo: url("../assets/projects/project-horizon.webp");
  --pos: 50% 50%;
}

.pj__chapter--3 .pj__photo {
  --photo: url("../assets/projects/project-atrium.webp");
  --pos: 50% 50%;
}
/* ========================================================================== */

/* ---------------------------------------------------------- the chapters -- */

/* One stage per project. Pinned briefly by ScrollTrigger while the picture
   goes REVEAL, EXPAND, DETAILS, then the page carries on. No CSS sticky. */
.pj__stage {
  position: relative;
  height: 100vh;
  height: 100svh;
  overflow: hidden;
}

/* Absolutely centred, so growing it never reflows anything around it.
   JS owns the transform (xPercent/yPercent plus the DETAILS lift). */
.pj__figure {
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: 1;
  margin: 0;
  width: min(60%, 88svh);
  aspect-ratio: 1.7768;
  transform: translate(-50%, -50%);
}

.pj__frame {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
  background: #11151c;
  border: 1px solid rgba(255, 255, 255, 0.07);
  /* the REVEAL mask, opened by scroll */
  clip-path: inset(0% 0% 100% 0%);
}

.pj__photo {
  position: absolute;
  inset: 0;
  background-image: var(--photo);
  background-size: cover;
  background-position: var(--pos, 50% 50%);
  will-change: transform;
}

/* ----------------------------------------------------------------- copy -- */

/* The band the picture retreats from in DETAILS. It never sits on top of the
   architecture: the frame lifts and shrinks first, the text lands underneath. */
.pj__meta {
  position: absolute;
  z-index: 2;
  left: clamp(20px, 4.5vw, 64px);
  right: clamp(20px, 4.5vw, 64px);
  bottom: clamp(34px, 6vh, 64px);
  max-width: 1560px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  column-gap: clamp(14px, 2vw, 32px);
  align-items: end;
}

.pj__cat {
  grid-column: 1 / span 6;
  grid-row: 1;
  margin: 0 0 clamp(18px, 2.6vh, 30px);
  font-size: clamp(12px, 0.95vw, 14px);
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.45);
  opacity: 0;
  visibility: hidden;
}

.pj__title {
  grid-column: 1 / span 7;
  grid-row: 2;
  margin: 0;
  font-size: clamp(44px, 4.4vw, 72px);
  font-weight: 300;
  line-height: 0.98;
  letter-spacing: -0.02em;
}

.pj__desc {
  grid-column: 9 / -1;
  grid-row: 2;
  margin: 0;
  padding-bottom: 0.4em;
  max-width: 38ch;
  font-size: clamp(20px, 1.45vw, 24px);
  font-weight: 300;
  line-height: 1.58;
  color: rgba(255, 255, 255, 0.58);
  opacity: 0;
  visibility: hidden;
}

/* No GSAP: the pictures and the copy simply stand there. */
.no-gsap .pj__frame { clip-path: none; }
.no-gsap .pj__rule,
.no-gsap .pj__divider { transform: scaleX(1); }
.no-gsap .pj__label-text,
.no-gsap .pj__sub,
.no-gsap .pj__note,
.no-gsap .pj__cat,
.no-gsap .pj__desc { opacity: 1; visibility: visible; }

/* ==========================================================================
   06 / OUR APPROACH
   The quiet after the photography: type, negative space and hairlines. No
   image, no canvas, no pin. The reader drives this one by opening a step.
   ========================================================================== */

.ap {
  position: relative;
  z-index: 1;
  /* Lands on the base ink the page opened with. */
  background: linear-gradient(180deg, #10141a 0%, #0d0f12 100%);
  color: #fff;
  padding-bottom: clamp(96px, 14vh, 180px);
}

/* ---------------------------------------------------------------- intro -- */

.ap__intro {
  max-width: 1560px;
  margin: 0 auto;
  padding: clamp(104px, 15vh, 180px) clamp(20px, 4.5vw, 64px)
    clamp(64px, 9vh, 116px);
}

.ap__top {
  display: flex;
  align-items: center;
  gap: clamp(20px, 3vw, 44px);
  margin-bottom: clamp(48px, 7vh, 96px);
}

.ap__label {
  display: flex;
  align-items: center;
  margin: 0;
  flex: 0 0 auto;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.5);
}

.ap__rule {
  width: clamp(36px, 5vw, 76px);
  height: 1px;
  margin-right: 20px;
  background: rgba(255, 255, 255, 0.42);
  transform: scaleX(0);
  transform-origin: left center;
}

.ap__label-text {
  opacity: 0;
  visibility: hidden;
}

.ap__divider {
  flex: 1 1 auto;
  height: 1px;
  background: rgba(255, 255, 255, 0.13);
  transform: scaleX(0);
  transform-origin: left center;
}

.ap__head {
  margin: 0;
  /* wide enough that "YOUR WORLD." always stays on its own single line */
  max-width: 13ch;
  font-size: clamp(2.4rem, 6.4vw, 5.6rem);
  font-weight: 200;
  line-height: 0.98;
  letter-spacing: -0.025em;
}

/* Opposite the headline, not centred under it. */
.ap__aside {
  margin: clamp(38px, 5.5vh, 70px) 0 0 auto;
  max-width: 40ch;
}

.ap__sub {
  margin: 0;
  font-size: clamp(0.95rem, 1.15vw, 1.08rem);
  font-weight: 300;
  line-height: 1.62;
  color: rgba(255, 255, 255, 0.68);
  opacity: 0;
  visibility: hidden;
}

/* ---------------------------------------------------------------- steps -- */

.ap__steps {
  max-width: 1560px;
  margin: 0 auto;
  padding: 0 clamp(20px, 4.5vw, 64px);
}

/* Hairlines between the steps, nothing enclosing them: three rows, not three
   cards. */
.ap__step {
  border-top: 1px solid rgba(255, 255, 255, 0.11);
}

.ap__step:last-child {
  border-bottom: 1px solid rgba(255, 255, 255, 0.11);
}

.ap__h {
  margin: 0;
  font-size: inherit;
  font-weight: inherit;
}

.ap__trigger {
  display: grid;
  grid-template-columns: clamp(52px, 6vw, 104px) 1fr auto;
  column-gap: clamp(16px, 2.4vw, 40px);
  align-items: baseline;
  width: 100%;
  margin: 0;
  padding: clamp(28px, 3.8vh, 46px) 0 clamp(26px, 3.4vh, 42px);
  border: 0;
  background: none;
  color: inherit;
  font-family: inherit;
  text-align: left;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.ap__trigger:focus-visible {
  outline: 1px solid rgba(255, 255, 255, 0.75);
  outline-offset: 6px;
}

.ap__num {
  font-size: clamp(11px, 0.85vw, 13px);
  font-weight: 500;
  letter-spacing: 0.24em;
  color: rgba(255, 255, 255, 0.38);
  transition: color 0.45s ease;
}

.ap__title {
  font-size: clamp(1.45rem, 3.1vw, 2.9rem);
  font-weight: 200;
  line-height: 1.04;
  letter-spacing: -0.015em;
  color: rgba(255, 255, 255, 0.62);
  transition: color 0.45s ease;
}

/* The open step is the one you can read: it simply carries more contrast. */
.ap__step.is-open .ap__num { color: rgba(255, 255, 255, 0.75); }
.ap__step.is-open .ap__title { color: #fff; }

@media (hover: hover) {
  .ap__trigger:hover .ap__title { color: rgba(255, 255, 255, 0.88); }
  .ap__trigger:hover .ap__num { color: rgba(255, 255, 255, 0.6); }
  .ap__step.is-open .ap__trigger:hover .ap__title { color: #fff; }
}

/* ------------------------------------------------------------ indicator -- */

/* Two hairlines. The upright one retracts when the step opens. */
.ap__mark {
  position: relative;
  align-self: center;
  width: 15px;
  height: 15px;
  flex: 0 0 auto;
}

.ap__mark-h,
.ap__mark-v {
  position: absolute;
  top: 50%;
  left: 50%;
  background: rgba(255, 255, 255, 0.55);
  transition: transform 0.45s cubic-bezier(0.4, 0, 0.2, 1),
    background-color 0.45s ease;
}

.ap__mark-h {
  width: 15px;
  height: 1px;
  transform: translate(-50%, -50%);
}

.ap__mark-v {
  width: 1px;
  height: 15px;
  transform: translate(-50%, -50%) scaleY(1);
}

.ap__step.is-open .ap__mark-h,
.ap__step.is-open .ap__mark-v { background: #fff; }
.ap__step.is-open .ap__mark-v { transform: translate(-50%, -50%) scaleY(0); }

/* --------------------------------------------------------------- panel -- */

/* These two rules are the state on their own, so the accordion still opens and
   closes if GSAP never loads. When it is there, approach.js drives the height
   inline and those inline values win. */
.ap__panel {
  overflow: hidden;
  height: 0;
  visibility: hidden;
}

.ap__step.is-open .ap__panel {
  height: auto;
  visibility: visible;
}

.ap__panel-i {
  display: grid;
  grid-template-columns: clamp(52px, 6vw, 104px) 1fr;
  column-gap: clamp(16px, 2.4vw, 40px);
}

/* Aligned under the title, in a measure short enough to stay readable. */
.ap__desc {
  grid-column: 2;
  margin: 0;
  padding-bottom: clamp(32px, 4.4vh, 54px);
  max-width: 50ch;
  font-size: clamp(0.95rem, 1.12vw, 1.12rem);
  font-weight: 300;
  line-height: 1.66;
  color: rgba(255, 255, 255, 0.6);
  opacity: 0;
  transition: opacity 0.4s ease 0.06s;
}

.ap__step.is-open .ap__desc { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .ap__desc,
  .ap__num,
  .ap__title,
  .ap__mark-h,
  .ap__mark-v { transition-duration: 0s; }
}

/* No GSAP: the intro copy simply stands there. The accordion does not depend
   on GSAP, so it keeps working. */
.no-gsap .ap__rule,
.no-gsap .ap__divider { transform: scaleX(1); }
.no-gsap .ap__label-text,
.no-gsap .ap__sub { opacity: 1; visibility: visible; }

/* ==========================================================================
   07 / START A PROJECT + FOOTER
   The page turns over: after six dark sections the surface becomes the warm
   paper the palette already carries (--paper), and the brand signs off on it.
   ========================================================================== */

.cl {
  position: relative;
  isolation: isolate;
  background: var(--paper);
  /* deep green-grey, the warm counterpart to --ink */
  color: #1b2120;
  --cl-ink: #1b2120;
  --cl-soft: rgba(27, 33, 32, 0.62);
  --cl-faint: rgba(27, 33, 32, 0.4);
  --cl-hair: rgba(27, 33, 32, 0.16);
}

/* The dark page retreats downward and leaves the paper behind. Scrubbed, so
   it is always right for the scroll position, however you arrive. */
.cl__veil {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  background: var(--ink);
  clip-path: inset(0% 0% 0% 0%);
}

/* ---------------------------------------------------------- the invite -- */

.ct {
  position: relative;
  z-index: 1;
  max-width: 1560px;
  margin: 0 auto;
  padding: clamp(104px, 15vh, 190px) clamp(20px, 4.5vw, 64px)
    clamp(80px, 11vh, 150px);
}

.ct__top {
  display: flex;
  align-items: center;
  gap: clamp(20px, 3vw, 44px);
  margin-bottom: clamp(56px, 8vh, 110px);
}

.ct__label {
  display: flex;
  align-items: center;
  margin: 0;
  flex: 0 0 auto;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--cl-faint);
}

.ct__rule {
  width: clamp(36px, 5vw, 76px);
  height: 1px;
  margin-right: 20px;
  background: rgba(27, 33, 32, 0.45);
  transform: scaleX(0);
  transform-origin: left center;
}

.ct__label-text {
  opacity: 0;
  visibility: hidden;
}

.ct__divider {
  flex: 1 1 auto;
  height: 1px;
  background: var(--cl-hair);
  transform: scaleX(0);
  transform-origin: left center;
}

/* Headline left and large, everything else opposite it. */
.ct__grid {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  column-gap: clamp(14px, 2vw, 32px);
  align-items: start;
}

.ct__head {
  grid-column: 1 / span 7;
  margin: 0;
  font-size: clamp(2.4rem, 6vw, 5.4rem);
  font-weight: 200;
  line-height: 0.98;
  letter-spacing: -0.025em;
  color: var(--cl-ink);
}

.ct__aside {
  grid-column: 9 / -1;
  padding-top: clamp(8px, 1.4vh, 18px);
}

.ct__sub {
  margin: 0;
  max-width: 38ch;
  font-size: clamp(0.98rem, 1.15vw, 1.12rem);
  font-weight: 300;
  line-height: 1.66;
  color: var(--cl-soft);
  opacity: 0;
  visibility: hidden;
}

.ct__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: clamp(20px, 2.4vw, 36px);
  margin-top: clamp(38px, 5vh, 62px);
  opacity: 0;
  visibility: hidden;
}

/* ------------------------------------------------------------- buttons -- */

.ct__cta {
  display: inline-flex;
  align-items: center;
  gap: 18px;
  min-height: 58px;
  padding: 18px clamp(24px, 2.4vw, 34px);
  border: 1px solid var(--cl-ink);
  background: var(--cl-ink);
  color: var(--paper);
  font-family: inherit;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.22em;
  cursor: pointer;
  transition: background-color 0.35s ease, color 0.35s ease;
  -webkit-tap-highlight-color: transparent;
}

.ct__arrow {
  width: 22px;
  height: 8px;
  flex: 0 0 auto;
  fill: none;
  stroke: currentColor;
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
  transition: transform 0.35s cubic-bezier(0.4, 0, 0.2, 1);
}

@media (hover: hover) {
  .ct__cta:hover {
    background: transparent;
    color: var(--cl-ink);
  }
  .ct__cta:hover .ct__arrow { transform: translateX(5px); }
  .ct__alt:hover .ct__alt-line { transform: scaleX(1); }
  .ct__alt:hover .ct__alt-text { color: var(--cl-ink); }
}

.ct__cta:focus-visible,
.ct__alt:focus-visible,
.ft__nav a:focus-visible,
.ft__top-link:focus-visible {
  outline: 1px solid rgba(27, 33, 32, 0.8);
  outline-offset: 6px;
}

.ct__alt {
  position: relative;
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 10px 0;
  text-decoration: none;
}

.ct__alt-text {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.22em;
  color: var(--cl-soft);
  transition: color 0.35s ease;
}

.ct__alt-line {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 6px;
  height: 1px;
  background: var(--cl-ink);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}

/* --------------------------------------------------------- the signature */

.ft {
  position: relative;
  z-index: 1;
  max-width: 1560px;
  margin: 0 auto;
  padding: 0 clamp(20px, 4.5vw, 64px) clamp(36px, 5vh, 64px);
}

.ft__hr {
  display: block;
  height: 1px;
  background: var(--cl-hair);
  transform: scaleX(0);
  transform-origin: left center;
}

/* Sized in vw so the word spans nearly the whole measure at any width. It is
   a signature, not a heading: tight tracking, thin weight, no decoration. */
.ft__mark {
  margin: clamp(30px, 4.5vh, 58px) 0 clamp(34px, 5vh, 66px);
  /* Calibrated from the measured glyph width so the word spans about 92% of
     the measure at every size, with headroom for a wider fallback face. The
     .ln mask around it clips rather than ever pushing the page sideways. */
  font-size: clamp(3.2rem, 25.4vw, 25.3rem);
  font-weight: 200;
  line-height: 0.82;
  letter-spacing: -0.035em;
  color: var(--cl-ink);
  white-space: nowrap;
}

/* ------------------------------------------------------------- the bar -- */

.ft__bar {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: start;
  column-gap: clamp(24px, 4vw, 72px);
  padding-top: clamp(26px, 3.6vh, 44px);
  border-top: 1px solid var(--cl-hair);
}

.ft__id {
  display: grid;
  gap: 8px;
}

.ft__name {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.28em;
  color: var(--cl-ink);
}

.ft__tag {
  font-size: 10px;
  font-weight: 400;
  letter-spacing: 0.2em;
  color: var(--cl-faint);
}

.ft__nav {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: clamp(16px, 2.4vw, 38px);
}

.ft__nav a,
.ft__top-link {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.2em;
  color: var(--cl-soft);
  text-decoration: none;
  transition: color 0.3s ease;
}

.ft__top-link { justify-self: end; }

.ft__top-arrow {
  width: 8px;
  height: 22px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
  transition: transform 0.35s cubic-bezier(0.4, 0, 0.2, 1);
}

@media (hover: hover) {
  .ft__nav a:hover,
  .ft__top-link:hover { color: var(--cl-ink); }
  .ft__top-link:hover .ft__top-arrow { transform: translateY(-4px); }
}

.ft__note {
  margin: clamp(30px, 4vh, 52px) 0 0;
  max-width: 62ch;
  font-size: 10px;
  font-weight: 300;
  line-height: 1.7;
  letter-spacing: 0.04em;
  color: var(--cl-faint);
}

/* The studio's name, lifted just enough to read as the author of the work
   rather than another word in the small print: one step up the same ink,
   one step up the same weight, and the site's own tracking. No box, no
   rule, no size change - it stays a line of text in the footer. */
.ft__studio {
  font-weight: 500;
  letter-spacing: 0.1em;
  color: var(--cl-soft);
  /* A name split over two lines stops reading as a name. About 110px at this
     size, so it fits the gutter of the narrowest phone. */
  white-space: nowrap;
}

/* Puts the second sentence on its own line where there is room for it, and
   lets it run on where there is not. */
.ft__note-line { display: block; }

/* No GSAP: the paper is already there and everything is legible. */
.no-gsap .cl__veil { display: none; }
.no-gsap .ct__rule,
.no-gsap .ct__divider,
.no-gsap .ft__hr { transform: scaleX(1); }
.no-gsap .ct__label-text,
.no-gsap .ct__sub,
.no-gsap .ct__actions { opacity: 1; visibility: visible; }

/* ==========================================================================
   PROGRESSIVE PHOTOGRAPHS
   Held back by js/media.js until the section is about a screen and a half
   away. Only the fetch is delayed: no size, position, crop or animation is
   touched, and without JavaScript none of this applies.
   ========================================================================== */

html.js-defer .tc:not(.is-near) .tc__photo,
html.js-defer .sol:not(.is-near) .sol__photo,
html.js-defer .pj:not(.is-near) .pj__photo {
  background-image: none;
}

/* ==========================================================================
   RESPONSIVE
   ========================================================================== */

@media (max-width: 860px) {
  .nav__links { display: none; }
  .nav { padding: 22px clamp(18px, 5vw, 32px); }
  .nav__cta {
    padding: 13px 16px;
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    font-size: 10px;
    letter-spacing: 0.14em;
  }
  /* The phone keeps the same composition, one step down in scale. The
     headline is deliberately not pushed to the limit here: it has to clear
     the roofline of the house and leave room for three more blocks under it
     on a screen a third as tall. */
  .hero__content { bottom: clamp(36px, 7vh, 64px); max-width: none; }
  .hero__eyebrow {
    margin-bottom: clamp(14px, 2vh, 20px);
    font-size: 10px;
    letter-spacing: 0.26em;
  }
  .hero__rule { width: clamp(20px, 6vw, 34px); margin-right: 14px; }
  /* The floor is 2.3rem rather than something rounder because the longest
     line of the set, "what follows.", has to fit on a 320px screen without
     wrapping: at 2.5rem it needed 283px of the 280 available. This leaves
     about 20px of slack, which also absorbs the font the phone actually
     substitutes for the first name in the stack. */
  .hero__title {
    font-size: clamp(2.3rem, 11.4vw, 3.9rem);
    line-height: 0.94;
    letter-spacing: -0.02em;
  }
  .hero__sub {
    margin-top: clamp(16px, 2.4vh, 24px);
    max-width: 34ch;
    font-size: 0.95rem;
    line-height: 1.58;
  }
  .hero__actions { margin-top: clamp(22px, 3.4vh, 32px); }
  .btn { flex: 1 1 auto; text-align: center; padding: 15px 20px; }
}

@media (max-width: 860px) {
  .ph { padding: clamp(96px, 14vh, 150px) clamp(20px, 5vw, 32px) clamp(88px, 13vh, 140px); }
  .ph__label { grid-column: 1 / span 12; margin-bottom: clamp(40px, 6vh, 66px); }
  .ph__head { font-size: clamp(1.9rem, 11.2vw, 4rem); line-height: 0.98; }
  .ln--shift { margin-left: 9%; }
  .ln:nth-child(4) { margin-top: clamp(20px, 3vh, 34px); }
  .ph__sub { grid-column: 1 / span 12; margin-top: clamp(44px, 7vh, 72px); max-width: 34ch; }
  .ph__sub br { display: none; } /* let it wrap naturally at this width */
}

/* --- 02 / TECHNOLOGY: its own composition, not a squeezed desktop -------- */
@media (max-width: 860px) {
  .tc { padding: clamp(88px, 12vh, 140px) clamp(20px, 5vw, 32px) clamp(88px, 12vh, 140px); }
  .tc__top { margin-bottom: clamp(36px, 5vh, 60px); }
  .tc__grid { grid-template-columns: 1fr; }

  /* main photograph goes near full width */
  .tc__main { grid-column: 1; grid-row: 1; }
  /* a near-square box cropped 45% of a 16:9 source; this keeps ~81% */
  .tc__main .tc__frame { height: clamp(230px, 62vw, 340px); }

  .tc__head {
    grid-column: 1;
    grid-row: 1;
    margin-bottom: clamp(-30px, -3.4vw, -14px);
    font-size: clamp(2rem, 10.4vw, 3.4rem);
    line-height: 0.98;
  }

  /* the close-up stays smaller and offset right, so it still reads as a
     detail rather than a second hero */
  .tc__d1 {
    grid-column: 1;
    grid-row: 2;
    width: 82%;
    margin: clamp(62px, 9vh, 104px) 0 0 auto;
  }
  .tc__d1 .tc__frame { height: clamp(170px, 44vw, 250px); }

  /* Three annotations: one on the main shot, two on the close-up. The frame
     is narrow enough here that two labels at the same height would touch, so
     the script's fallback percentages are kept apart vertically too. */
  .anno--b { display: none; }
  .anno--a { top: 74%; left: 8%; }
  .anno--c { top: 42%; left: 8%; }
  .anno--d { top: 68%; left: 8%; }
  .anno { gap: 10px; }
  .anno__svg,
  .tc__d1 .anno__svg { width: 54px; }
  .anno__txt { font-size: 10px; letter-spacing: 0.16em; }

  /* Below about 360px the close-up is only 228px wide, and LOW-PROFILE
     MOUNTING at the size above is two thirds of that: there is no leader
     length that keeps both the label and its endpoint inside the frame. One
     step down in the tracking and the size buys back the room, and the dot
     stays exactly on the part. */
  @media (max-width: 360px) {
    .anno { gap: 7px; }
    .anno__txt { font-size: 9px; letter-spacing: 0.12em; }
  }

  .tc__sub {
    grid-column: 1;
    grid-row: 3;
    align-self: start;
    margin-top: clamp(58px, 8vh, 98px);
    max-width: 36ch;
  }
  .tc__sub br { display: none; }

  .tc__note {
    grid-column: 1;
    grid-row: 4;
    margin-top: clamp(24px, 3.6vh, 40px);
    max-width: 40ch;
  }
}

/* --- 03 / SOLUTIONS: three chapters in sequence, no pin ----------------- */
@media (max-width: 860px) {
  .sol__intro {
    padding: clamp(88px, 12vh, 140px) clamp(20px, 5vw, 32px) clamp(52px, 7vh, 84px);
  }
  .sol__top { margin-bottom: clamp(36px, 5vh, 60px); }
  .sol__head { font-size: clamp(2rem, 9.6vw, 3.4rem); line-height: 1.02; max-width: 13ch; }
  .sol__sub { margin-top: clamp(30px, 4.5vh, 48px); max-width: 34ch; }
  .sol__sub br { display: none; }

  /* the stage stops being a stage: the chapters simply follow one another */
  .sol__stage { height: auto; overflow: visible; }
  .sol__track { height: auto; }

  .sol__item {
    position: static;
    left: auto;
    right: auto;
    height: auto;
    display: block;
    opacity: 1;
    visibility: visible;
    padding: 0 clamp(20px, 5vw, 32px) clamp(76px, 11vh, 128px);
  }

  .sol__item:last-child { padding-bottom: clamp(96px, 14vh, 160px); }

  .sol__item--1 .sol__frame,
  .sol__item--2 .sol__frame,
  .sol__item--3 .sol__frame { height: clamp(300px, 78vw, 440px); }

  /* the mobile frame is much squarer: re-anchor rather than reuse the
     desktop crop */
  .sol__item--1 .sol__frame { --pos: 50% 50%; }
  .sol__item--2 .sol__frame { --pos: 35% 50%; }
  .sol__item--3 .sol__frame { --pos: 68% 50%; }

  .sol__item--1 .sol__copy,
  .sol__item--2 .sol__copy,
  .sol__item--3 .sol__copy { top: 0; margin-top: clamp(30px, 4.5vh, 48px); }

  .sol__num { margin-bottom: clamp(14px, 2vh, 22px); }
  .sol__title { font-size: clamp(1.9rem, 9vw, 3rem); line-height: 1.02; }
  .sol__desc { margin-top: clamp(20px, 3vh, 30px); max-width: 38ch; }
  .sol__desc br { display: none; }

  .sol__rail { display: none; }
}

/* --- 04 / ENERGY FLOW: no pin, and the labels become a legible list ----- */
@media (max-width: 860px) {
  .ef__intro {
    padding: clamp(88px, 12vh, 140px) clamp(20px, 5vw, 32px) clamp(44px, 6vh, 72px);
  }
  .ef__top { margin-bottom: clamp(36px, 5vh, 60px); }
  .ef__head { font-size: clamp(2rem, 9.6vw, 3.4rem); line-height: 1.02; max-width: 12ch; }
  .ef__aside { margin-top: clamp(30px, 4.5vh, 48px); max-width: 36ch; }
  .ef__sub br { display: none; }

  /* The stage stops being a stage: photograph, then the sequence under it. */
  .ef__stage {
    height: auto;
    display: block;
    overflow: visible;
    padding-bottom: clamp(88px, 13vh, 150px);
  }

  .ef__visual { width: 100%; }

  /* Five labels cannot sit on a phone-width photograph without covering the
     architecture, so only the points stay on the picture. */
  .ef__lead { display: none; }

  .ef__steps {
    position: static;
    display: grid;
    gap: clamp(2px, 0.6vh, 6px);
    margin: clamp(30px, 4.5vh, 48px) clamp(20px, 5vw, 32px) 0;
  }

  .ef__step {
    position: static;
    width: auto;
    grid-template-columns: 2.6em 1fr auto;
    align-items: baseline;
    column-gap: clamp(12px, 3.6vw, 20px);
    padding: clamp(11px, 1.7vh, 16px) 0;
    border-top: 1px solid rgba(255, 255, 255, 0.09);
    text-shadow: none;
  }

  .ef__step:last-child { border-bottom: 1px solid rgba(255, 255, 255, 0.09); }

  .ef__step-i { grid-column: 1; grid-row: 1; font-size: 10px; letter-spacing: 0.18em; }
  .ef__step-n { grid-column: 2; grid-row: 1; font-size: 13px; letter-spacing: 0.2em; }

  .ef__step-d {
    grid-column: 3;
    grid-row: 1;
    margin-top: 0;
    font-size: 12px;
    text-align: right;
    color: rgba(255, 255, 255, 0.5);
  }

  .ef__dot { left: -3px; top: -3px; width: 6px; height: 6px; }
  .ef__ring { left: -6px; top: -6px; width: 12px; height: 12px; }
}

@media (max-width: 430px) {
  .ef__step { grid-template-columns: 2.4em 1fr; }
  .ef__step-d {
    grid-column: 2;
    grid-row: 2;
    margin-top: 4px;
    text-align: left;
  }
}

/* --- 05 / PROJECTS: one column per chapter, in reading order -------------
   This block was lost when section 04 was rebuilt, which left the phone
   running the desktop composition: an absolutely positioned figure and a
   twelve-column caption whose description sat in four of them, about 107px
   wide, on top of the photograph. Restored and reordered.

   `display: contents` on the caption lets its three children become grid
   items of the chapter itself, so the order can be category, photograph,
   title, description without touching the markup. ---------------------- */
@media (max-width: 860px) {
  .pj__stage {
    position: static;
    height: auto;
    overflow: visible;
    display: grid;
    grid-template-columns: 1fr;
    /* vw, not vh: the Safari address bar changes vh as you scroll, which
       would reflow the whole chapter under your finger. */
    padding: 0 clamp(20px, 5vw, 32px) clamp(74px, 22vw, 124px);
  }

  .pj__chapter--3 .pj__stage { padding-bottom: clamp(96px, 30vw, 160px); }

  .pj__meta {
    display: contents;
  }

  /* The desktop rules pin these into named grid rows and columns; on one
     column that has to be cleared or they land on top of each other. */
  .pj__cat,
  .pj__title,
  .pj__desc { grid-area: auto; }

  /* 01 — category */
  .pj__cat {
    order: 1;
    position: static;
    width: auto;
    margin: 0 0 clamp(14px, 4.4vw, 20px);
    font-size: 11px;
    letter-spacing: 0.24em;
  }

  /* 02 — photograph. The figure is a fixed box: full column width, a height
     in vw so the address bar cannot change it, and it never moves. The frame
     inside is what scales, and this box clips it, so the picture can never
     reach the title below. */
  .pj__figure {
    order: 2;
    position: static;
    transform: none;
    justify-self: center;
    width: 100%;
    height: clamp(260px, 68vw, 420px);
    aspect-ratio: auto;
    margin: 0;
    overflow: hidden;
  }

  /* Scaled by projects.js. Compositor-only: no reflow, and no re-scaling of
     the background image on every frame. */
  .pj__frame {
    transform-origin: center center;
    will-change: transform;
  }

  /* 03 — title, close under the photograph */
  .pj__title {
    order: 3;
    position: static;
    width: auto;
    margin: clamp(22px, 7vw, 32px) 0 0;
    font-size: clamp(1.8rem, 8.6vw, 2.6rem);
    line-height: 1.04;
  }

  /* 04 — description, full width and readable */
  .pj__desc {
    order: 4;
    position: static;
    width: auto;
    max-width: 46ch;
    margin: clamp(16px, 5.2vw, 24px) 0 0;
    padding-bottom: 0;
    font-size: clamp(16px, 4.4vw, 18px);
    line-height: 1.62;
  }

  /* The mobile frame is much squarer than the 16:9 source, so each picture is
     re-anchored on what it has to keep. */
  .pj__chapter--1 .pj__photo { --pos: 58% 48%; }
  .pj__chapter--2 .pj__photo { --pos: 50% 44%; }
  .pj__chapter--3 .pj__photo { --pos: 44% 46%; }
}

/* --- 05 / PROJECTS: the DETAILS band has a fixed height, so a short desktop
   window takes the bottom of the desktop range instead of pushing the copy
   into the photograph ---------------------------------------------------- */
@media (min-width: 861px) and (max-height: 800px) {
  .pj__cat { margin-bottom: clamp(14px, 2.2vh, 22px); }
  .pj__desc { font-size: clamp(16px, 1.45vw, 20px); }
}

/* --- 05 / PROJECTS: tablet sits between the phone and desktop scales --- */
@media (min-width: 561px) and (max-width: 860px) {
  .pj__cat { font-size: 12px; }
  .pj__title { font-size: clamp(40px, 6.4vw, 54px); }
  .pj__desc { font-size: clamp(18px, 2.4vw, 20px); max-width: 42ch; }
}

/* --- 06 / OUR APPROACH: one column, larger touch targets ---------------- */
@media (max-width: 860px) {
  .ap { padding-bottom: clamp(80px, 12vh, 140px); }

  .ap__intro {
    padding: clamp(88px, 12vh, 140px) clamp(20px, 5vw, 32px) clamp(52px, 7vh, 88px);
  }
  .ap__top { margin-bottom: clamp(36px, 5vh, 60px); }
  .ap__head { font-size: clamp(2rem, 9.6vw, 3.4rem); line-height: 1.02; max-width: 14ch; }
  .ap__aside { margin-top: clamp(30px, 4.5vh, 48px); max-width: 34ch; }
  .ap__sub br { display: none; }

  .ap__steps { padding: 0 clamp(20px, 5vw, 32px); }

  /* A comfortable target the whole width of the row, not just the marker. */
  .ap__trigger {
    grid-template-columns: 40px 1fr 20px;
    column-gap: 14px;
    align-items: center;
    min-height: 68px;
    padding: clamp(22px, 3vh, 30px) 0 clamp(20px, 2.6vh, 26px);
  }

  .ap__num { font-size: 11px; letter-spacing: 0.2em; align-self: center; }

  .ap__title {
    font-size: clamp(1.15rem, 5.4vw, 1.75rem);
    line-height: 1.12;
    letter-spacing: -0.01em;
  }

  .ap__mark { width: 14px; height: 14px; }
  .ap__mark-h { width: 14px; }
  .ap__mark-v { height: 14px; }

  /* The description takes the full width rather than inheriting the desktop
     indent, which would waste a third of a phone screen. */
  .ap__panel-i { grid-template-columns: 1fr; column-gap: 0; }
  .ap__desc {
    grid-column: 1;
    max-width: none;
    padding-bottom: clamp(26px, 3.4vh, 36px);
    font-size: 1rem;
    line-height: 1.64;
  }
  .ap__desc br { display: none; }
}

/* --- 07 / START A PROJECT + FOOTER: stacked, generous touch targets ----- */
@media (max-width: 860px) {
  .ct {
    padding: clamp(88px, 12vh, 140px) clamp(20px, 5vw, 32px) clamp(64px, 9vh, 110px);
  }
  .ct__top { margin-bottom: clamp(42px, 6vh, 70px); }

  .ct__grid { display: block; }

  /* "WHAT COMES NEXT." is the long line: the measure and the floor are set
     so it stays on one masked line down to 320px. */
  .ct__head {
    font-size: clamp(1.8rem, 9.2vw, 3.4rem);
    line-height: 1.02;
    max-width: 17ch;
  }

  .ct__aside { padding-top: 0; margin-top: clamp(34px, 5vh, 54px); }
  .ct__sub { max-width: 36ch; }

  /* Full-width primary action, secondary under it: both easy to hit. */
  .ct__actions {
    display: grid;
    justify-items: start;
    gap: clamp(14px, 2.2vh, 22px);
    margin-top: clamp(32px, 4.5vh, 46px);
  }

  .ct__cta {
    width: 100%;
    justify-content: space-between;
    min-height: 62px;
    padding: 20px 24px;
  }

  .ct__alt { min-height: 48px; }

  .ft { padding: 0 clamp(20px, 5vw, 32px) clamp(34px, 5vh, 52px); }

  .ft__mark {
    /* same vw curve as the desktop: it is the measure that changes, not the
       proportion the word occupies of it */
    letter-spacing: -0.03em;
    margin: clamp(26px, 4vh, 44px) 0 clamp(30px, 4.5vh, 48px);
  }

  /* One column: identity, then links, then back to top. */
  .ft__bar {
    grid-template-columns: 1fr;
    row-gap: clamp(22px, 3.2vh, 34px);
  }

  .ft__nav {
    justify-content: flex-start;
    gap: 0;
    display: grid;
    grid-template-columns: 1fr 1fr;
  }

  .ft__nav a { min-height: 50px; }

  .ft__top-link { justify-self: start; min-height: 50px; }

  .ft__note { margin-top: clamp(26px, 3.6vh, 40px); max-width: none; }
}

@media (max-width: 430px) {
  .ft__nav { grid-template-columns: 1fr; }
}

@media (max-width: 420px) {
  .nav__brand-a,
  .nav__brand-b { font-size: 15px; letter-spacing: 0.2em; }
}
