/* ============================================================
   BUILT FOR GRANT — GJB x D2G PITCH SITE (v4)
   Brand system per the GJB design spec:
   ivory page, forest + blush full-bleed bands, ink type,
   display serif for headlines, quiet sans for body, no accent.
   ============================================================ */

:root{
  --ivory:#FBFBEF;
  --forest:#1E3B32;
  --blush:#F9D9D6;
  --ink:#111111;
  --ink-soft:rgba(17,17,17,.66);
  --ivory-soft:rgba(251,251,239,.72);
  --rule:rgba(17,17,17,.16);

  /* Working tones drawn from the same three colours, so the page has rhythm
     instead of fifteen identical ivory scenes in a row:
       paper       a second ground, half a step warmer than ivory
       blush-tint  blush at card strength, so a card reads as warm, not grey
       blush-deep  blush with enough body to be a mark or a label on forest
       forest-line the hairline colour: every rule on the page is green, not black
       forest-soft forest for secondary type */
  --paper:#F4F2E1;
  --blush-tint:#FCEDEB;
  --blush-deep:#EFB6B0;
  --forest-line:rgba(30,59,50,.20);
  --forest-soft:rgba(30,59,50,.70);
  --serif:"Bodoni Moda", "Times New Roman", serif;
  --sans:"Figtree", -apple-system, "Helvetica Neue", Arial, sans-serif;
  /* used only for the thoughts written into the clouds: a soft, slanted face
     that reads as something half-said rather than something set in type */
  --dream:"Fredoka", "Trebuchet MS", Verdana, sans-serif;
  --gold:#C9A84C; /* D2G band only */
  --max:1000px;
}

*{ box-sizing:border-box; }
body{
  margin:0; background:var(--ivory); color:var(--ink);
  font-family:var(--sans); line-height:1.65;
  -webkit-font-smoothing:antialiased; overflow-x:hidden;
  /* Bodoni Moda is a variable Didone: the browser raises optical size at display
     sizes, which thins the hairlines to nothing. Pin opsz low so the strokes hold. */
  font-optical-sizing:none;
  font-variation-settings:"opsz" 9;
}

/* The scrollbar is the one piece of chrome the browser draws for us, and on a page
   this long it is on screen constantly. Tinted to the brand rather than left grey.
   Only the standard property: defining ::-webkit-scrollbar switches macOS from
   overlay to always-on scrollbars, which would steal layout width from the film. */
html{ scrollbar-color: var(--forest) transparent; }

/* ---------- progress hairline ---------- */
.progressbar{
  position:fixed; top:0; left:0; right:0; height:2px; z-index:80;
  background:var(--forest); transform:scaleX(0); transform-origin:left center;
}

/* ---------- sticky nav ---------- */
.topnav{
  position:fixed; top:0; left:0; right:0; z-index:60;
  display:flex; flex-direction:column; align-items:center; gap:6px;
  padding:14px 20px 10px;
  background:color-mix(in srgb, var(--ivory) 92%, transparent);
  backdrop-filter:blur(8px);
  border-bottom:1px solid var(--forest-line);
}
.topnav__lockup{ display:flex; flex-direction:column; align-items:center; line-height:1; }
.topnav__lockup b{
  font-family:var(--serif); font-weight:500; font-size:15px; letter-spacing:.08em;
}
.topnav__lockup span{
  font-family:var(--serif); font-size:8px; letter-spacing:.5em; margin-top:3px;
  padding:0 0 0 .5em;
}
.topnav__links{ display:flex; gap:24px; flex-wrap:nowrap; justify-content:center; width:100%; }
.topnav__links a{
  font-family:var(--serif); font-size:13px; color:var(--ink);
  text-decoration:none; letter-spacing:.02em; white-space:nowrap;
  border-bottom:1px solid transparent; padding-bottom:2px;
}
.topnav__links a.active{ color:var(--forest); border-bottom-color:var(--forest); }
@media (max-width:860px){
  .topnav__links{ gap:16px; }
  .topnav__links a{ font-size:12px; }
}

/* mobile nav */
.topnav__mobile{ display:none; }
@media (max-width:640px){
  .topnav{ padding:10px 14px 9px; flex-direction:row; justify-content:space-between; align-items:center; }
  .topnav__links{ display:none; }
  .topnav__lockup{ align-items:flex-start; }
  .topnav__lockup b{ font-size:12.5px; }
  .topnav__lockup span{ font-size:7px; }
  .topnav__mobile{ display:flex; align-items:center; gap:12px; }
  .topnav__current{
    font-family:var(--serif); font-size:11px; color:var(--ink-soft);
    white-space:nowrap; max-width:34vw; overflow:hidden; text-overflow:ellipsis;
  }
  .topnav__menu{
    font-family:var(--sans); font-size:12px; font-weight:500; color:var(--ink);
    background:transparent; border:1px solid var(--ink); border-radius:999px;
    padding:7px 14px; cursor:pointer;
  }
}
.navsheet{
  position:fixed; inset:0; z-index:70; background:var(--ivory);
  display:flex; flex-direction:column; align-items:center; justify-content:center; gap:30px;
}
.navsheet[hidden]{ display:none; }
.navsheet a{
  font-family:var(--serif); font-size:24px; color:var(--ink); text-decoration:none;
}
.navsheet .navsheet__close{
  position:absolute; top:18px; right:18px;
  font-family:var(--sans); font-size:13px; background:transparent;
  border:1px solid var(--ink); border-radius:999px; padding:8px 16px; cursor:pointer; color:var(--ink);
}

/* ---------- scenes ---------- */
.scene{
  position:relative; min-height:100svh;
  display:flex; align-items:center; justify-content:center;
  padding:140px 24px 96px; overflow:hidden;
  background:var(--ivory);
}
.scene--short{ min-height:64svh; }
.scene__inner{
  position:relative; z-index:2; width:100%; max-width:var(--max);
  display:flex; flex-direction:column; gap:30px; align-items:flex-start;
}
.scene__inner--center{ align-items:center; text-align:center; }
@media (max-width:640px){ .scene{ padding:120px 20px 72px; } }

/* full-bleed bands */
.scene--forest{ background:var(--forest); color:var(--ivory); }
.scene--blush{ background:var(--blush); }
/* the second ground: alternated through the ivory run so consecutive scenes
   separate from each other instead of reading as one long cream wash */
.scene--paper{ background:var(--paper); }

/* ---------- type ---------- */
/* The section label is a marker sitting ON the section, not a kicker floating
   above it: a short rule, tighter tracking than the 2023 eyebrow, and a colour
   that recedes so the headline is what the eye lands on. */
.eyebrow{
  font-family:var(--serif); font-size:11.5px; letter-spacing:.16em;
  text-transform:uppercase; margin:0; font-weight:600;
  color:var(--forest-soft);
  display:flex; align-items:center; gap:12px;
}
.scene .eyebrow::before{
  content:""; flex:none; width:26px; height:1px;
  background:currentColor; opacity:.5;
}
/* Centred labels take a rule on both sides: a single leading rule reads as a
   left-aligned marker that has drifted into the middle. */
.scene__inner--center .eyebrow{ justify-content:center; }
.scene__inner--center .eyebrow::after{
  content:""; flex:none; width:26px; height:1px;
  background:currentColor; opacity:.5;
}
.eyebrow--onforest{ color:var(--ivory-soft); }
/* the label binds to its heading; the 30px scene gap stays between the groups */
.scene__inner > .eyebrow{ margin-bottom:-8px; }
.display{
  font-family:var(--serif); font-weight:500;
  font-size:clamp(38px, 6.2vw, 70px);
  letter-spacing:-.015em; line-height:1.05; margin:0;
  text-wrap:balance; overflow-wrap:break-word; max-width:100%;
}
.display--md{ font-size:clamp(30px, 4.6vw, 56px); }
.hero-display{ font-size:clamp(40px, 6.2vw, 70px); }
.lede{
  font-family:var(--sans); font-size:clamp(16px,2vw,18px);
  color:var(--ink-soft); max-width:56ch; margin:0; text-wrap:pretty;
}
.lede--strong{ color:var(--ink); font-weight:600; }
/* full-strength ink at body weight: carries the same emphasis by colour alone */
.lede--ink{ color:var(--ink); }
.lede--onforest{ color:var(--ivory-soft); }
.lede--strongforest{ color:var(--ivory); font-weight:600; }
.scene--forest .display{ color:var(--ivory); }
.note{
  font-size:13px; color:var(--ink-soft); max-width:56ch; margin:0; text-wrap:pretty;
}
.note--wide{ max-width:72ch; }
/* the one number the whole month is judged on, so it stands off the notes around it */
.note--star{ font-weight:600; color:var(--ink); margin-top:2px; }
.scene--forest .note--star{ color:var(--ivory); }
.scene--forest .note{ color:var(--ivory-soft); }
.aud{
  font-family:var(--serif); font-size:clamp(17px,2.2vw,22px); margin:0;
  letter-spacing:.01em;
}
.aud--big{ font-size:clamp(20px,2.8vw,28px); }
.aud--onforest{ color:var(--ivory); }

/* Bodoni Moda carries an optical-size axis (6..96). The body pins it to 9 so the
   hairlines survive at 13-18px, but at 42-70px that same setting renders as
   blown-up small text: even strokes, no drama. The display roles take their own
   optical size, which is what the axis is for.
   Bodoni's high optical sizes are a poster cut: the hairlines thin out until parts
   of a word drop off the page at reading distance. The section headlines take the
   same opsz 9 as the hero, which is the thicker cut of the same face, so every
   stroke holds its weight and the display type matches the hero. */
.display, .display--md, .hero-display, .invite .reveal, .speed-big{
  font-variation-settings:"opsz" 9;
}
.bigquote p{ font-variation-settings:"opsz" 30; }
.aud--big{ font-variation-settings:"opsz" 18; }

/* The hero is the scroll world (hero-v2). Its styles live in hero-v2/hero-v2.css;
   the still-image hero that used to sit here told the same journey a second time
   and has been removed. */

/* ---------- press quotes ---------- */
.press{ display:grid; grid-template-columns:repeat(3,1fr); gap:26px; width:100%; margin-top:8px; }
@media (max-width:720px){ .press{ grid-template-columns:1fr; } }
.press .pq p{
  font-family:var(--serif); font-size:clamp(19px,2.4vw,26px); margin:0 0 10px;
  line-height:1.25;
}
.press .pq cite{
  font-style:normal; font-family:var(--sans); font-size:12px;
  letter-spacing:.14em; text-transform:uppercase; color:var(--ink-soft);
}

/* ---------- stats ---------- */
.stats{ display:flex; gap:70px; flex-wrap:wrap; justify-content:center; }
.stat b{
  display:block; font-family:var(--serif); font-weight:500;
  font-size:clamp(44px,7vw,88px); line-height:1;
  font-variant-numeric:tabular-nums;
  color:var(--forest);
}
.stat span{
  display:block; margin-top:10px; font-size:12px; letter-spacing:.16em;
  text-transform:uppercase; color:var(--ink-soft);
}
/* The two counters are the headline numbers, not the whole picture. This footnote
   says which platforms they are and leaves room for the rest of the footprint,
   so the figures cannot read as an undercount of what Grant actually reaches. */
.stats-note{
  margin:22px auto 0; max-width:52ch; font-size:14px; line-height:1.6;
  color:var(--ink-soft); text-align:center;
}
/* No ch max-width here: ch resolves against the parent's 16px sans, not the 34px
   serif inside, so 30ch measured ~267px and folded each half onto two lines. */
.bigquote{ margin:10px 0 0; max-width:100%; }
.bigquote p{
  font-family:var(--serif); font-size:clamp(22px,3.2vw,34px);
  line-height:1.25; margin:0 0 10px;
}
.bigquote cite{
  font-style:normal; font-family:var(--sans); font-size:12px;
  letter-spacing:.14em; text-transform:uppercase; color:var(--forest-soft);
}
/* the quote is set as two deliberate lines, so each half lands on its own */
.bigquote .qline{ display:block; white-space:nowrap; }
/* below this the longest half genuinely cannot fit, so let it wrap rather than overflow */
@media (max-width:430px){ .bigquote .qline{ white-space:normal; } }

/* ---------- reach tiles ----------
   Four figures off one source, so they are set as one continuous panel divided by
   hairlines rather than four separate rounded cards. The number carries the colour;
   everything under it stays quiet. */
/* 940px is shared with .adbox and #s04's .shot so the three stacked panels in
   this scene sit on the same left and right edges */
.tiles{
  display:grid; grid-template-columns:repeat(4,1fr); width:100%; max-width:940px;
  background:var(--blush-tint); border:1px solid var(--forest-line);
  border-radius:18px; overflow:hidden;
}
@media (max-width:860px){ .tiles{ grid-template-columns:repeat(2,1fr); } }
@media (max-width:480px){ .tiles{ grid-template-columns:1fr; } }
.tile{
  padding:30px 22px;
  display:flex; flex-direction:column; gap:9px; align-items:center; text-align:center;
  border-left:1px solid var(--forest-line);
  transition:background .35s ease;
}
.tile:first-child{ border-left:0; }
@media (max-width:860px){
  .tile:nth-child(odd){ border-left:0; }
  .tile:nth-child(n+3){ border-top:1px solid var(--forest-line); }
}
@media (max-width:480px){
  .tile{ border-left:0; }
  .tile + .tile{ border-top:1px solid var(--forest-line); }
}
.tile:hover{ background:color-mix(in srgb, var(--blush) 42%, var(--ivory)); }
.tile b{
  font-family:var(--serif); font-weight:500; font-size:clamp(28px,3.2vw,40px);
  font-variant-numeric:tabular-nums; line-height:1;
  color:var(--forest);
}
.tile span{ font-size:13px; color:var(--ink); }
.tile i{ font-style:normal; font-size:12px; line-height:1.45; color:var(--ink-soft); }
.tile em{ font-style:normal; font-size:11px; letter-spacing:.14em; text-transform:uppercase; color:var(--forest-soft); }

/* ---------- 04 the 74,986 advert ----------
   Their own creative, in its own contained forest panel: copy left, the advert
   itself right, at the shape it was actually cut in. A contained box rather than
   another full-bleed band, so the scene keeps its ivory frame. */
.adbox{
  width:100%; max-width:940px; margin-top:10px;
  display:grid; grid-template-columns:1.2fr .8fr;
  gap:clamp(24px,3.2vw,46px); align-items:center; text-align:left;
  background:var(--forest); color:var(--ivory);
  border-radius:22px; padding:clamp(26px,3.4vw,44px);
  box-shadow:0 30px 70px -46px rgba(30,59,50,.85);
}
@media (max-width:760px){ .adbox{ grid-template-columns:1fr; } }
.adbox__copy{ display:flex; flex-direction:column; gap:12px; }
.adbox__figure{
  margin:2px 0 0; font-family:var(--serif); font-weight:500;
  font-size:clamp(46px,7vw,86px); line-height:1; letter-spacing:.01em;
  font-variant-numeric:tabular-nums; color:var(--ivory);
}
.adbox__figlabel{
  margin:0; font-size:11.5px; letter-spacing:.2em; text-transform:uppercase;
  color:var(--blush-deep);
}
.adbox__body{ margin:4px 0 0; font-size:15px; line-height:1.62; color:var(--ivory-soft); }
/* the run-time flag: same panel, but boxed off so it reads as a separate point */
.adbox__flag{
  margin-top:6px; padding:16px 18px; border-radius:14px;
  background:rgba(251,251,239,.07);
  border:1px solid rgba(239,182,176,.36);
}
.adbox__flag b{
  display:block; margin-bottom:7px;
  font-family:var(--serif); font-weight:500; font-size:11.5px;
  letter-spacing:.2em; text-transform:uppercase; color:var(--blush-deep);
}
.adbox__flag p{ margin:0; font-size:14px; line-height:1.62; color:var(--ivory); }
.adbox__media{ margin:0; display:flex; justify-content:center; }
.adbox__media video{
  display:block; width:100%; max-width:300px; aspect-ratio:9 / 16;
  object-fit:cover; border-radius:18px; background:var(--ink);
  border:1px solid rgba(251,251,239,.22);
  box-shadow:0 26px 54px -30px rgba(0,0,0,.75);
}

/* 05 (the 740,982 house scene) was removed at Grant's request — its styles went with it. */
.eyebrow--big{ font-size:14px; letter-spacing:.18em; margin-top:6px; }
/* the primary-goal line closes the section, so it carries weight and full ink
   rather than the soft tone the eyebrow role uses everywhere else */
.eyebrow--bold{ font-weight:700; color:var(--forest); }

/* ---------- 16b what the campaign delivers ---------- */
/* five cards, so they wrap three then two and stay centred rather than
   leaving a hole in a rigid grid */
.results{
  display:flex; flex-wrap:wrap; justify-content:center;
  gap:16px; width:100%; max-width:980px; margin-top:8px;
}
/* the cards sit in a shared perspective so the tilt reads as five objects on one
   plane rather than five unrelated hovers. the fade-up owns the outer div's
   transform (GSAP writes it inline), so the tilt lives on an inner face and the
   two never fight for the same property. */
.results{ perspective:1200px; }
.result{
  flex:1 1 260px; max-width:300px; display:flex;
}
.result__face{
  flex:1 1 auto; position:relative; overflow:hidden;
  border:1px solid var(--forest-line); border-radius:14px; padding:22px 22px;
  text-align:left; display:flex; flex-direction:column; gap:8px;
  background:var(--blush-tint);
  box-shadow:0 2px 0 rgba(30,59,50,.04);
  transform-style:preserve-3d;
  transition:transform .45s cubic-bezier(.2,.7,.3,1),
             box-shadow .45s ease, border-color .45s ease;
}
/* the glint is a single light band sweeping across the face on hover.
   it runs as a keyframe rather than a transition on purpose: a transition also
   plays in reverse when the pointer leaves, so sweeping across the row left two
   or three cards glinting at once. an animation is simply removed on unhover, so
   the band snaps back off the left edge, out of sight, and only the card under
   the pointer ever shimmers. */
.result__face::after{
  content:""; position:absolute; inset:-60% -80%; pointer-events:none;
  background:linear-gradient(104deg,
    transparent 42%, rgba(255,255,255,.78) 50%, transparent 58%);
  transform:translate3d(-115%,0,0);
}
@keyframes result-glint{
  from{ transform:translate3d(-115%,0,0); }
  to{ transform:translate3d(115%,0,0); }
}
.result:hover .result__face,
.result:focus-within .result__face{
  transform:translateY(-6px) rotateX(4.5deg) rotateY(-5deg) scale(1.02);
  box-shadow:0 30px 50px -24px rgba(30,59,50,.42);
  border-color:var(--blush-deep);
}
.result:hover .result__face::after,
.result:focus-within .result__face::after{
  animation:result-glint .85s cubic-bezier(.3,.6,.3,1) forwards;
}
@media (hover:none){
  .result:hover .result__face{ transform:none; box-shadow:0 2px 0 rgba(30,59,50,.04); }
  .result:hover .result__face::after{ animation:none; }
}
@media (prefers-reduced-motion:reduce){
  .result__face{ transition:none; }
  .result:hover .result__face{ transform:none; }
  .result:hover .result__face::after,
  .result:focus-within .result__face::after{ animation:none; }
}
.result b{
  font-family:var(--serif); font-weight:500; font-size:18px; line-height:1.25;
  color:var(--forest);
}
.result p{ font-size:14px; line-height:1.5; color:var(--ink-soft); margin:0; }
@media (max-width:720px){ .result{ flex:1 1 100%; max-width:none; } }

/* ---------- 06 two jobs + the chain ---------- */
.jobs{ display:grid; grid-template-columns:1fr 1fr; gap:18px; width:100%; max-width:860px; }
@media (max-width:720px){ .jobs{ grid-template-columns:1fr; } }
/* Both jobs share one ground. They are two halves of a single campaign, not two
   options being weighed against each other, and a split palette was reading as
   the second one mattering more. */
.job{
  border:1px solid var(--forest); border-radius:14px; padding:28px 26px;
  text-align:left; display:flex; flex-direction:column; gap:10px;
  background:var(--forest); color:var(--ivory);
}
.job span{
  font-family:var(--serif); font-size:12px; letter-spacing:.22em;
  text-transform:uppercase; color:var(--blush-deep);
}
.job b{
  font-family:var(--serif); font-weight:500;
  font-size:clamp(24px,3.2vw,36px); line-height:1.12;
  color:var(--ivory);
}
.job__line{ margin:0; font-family:var(--serif); font-size:clamp(15.5px,2vw,18px); }
.job__aux{ margin:0; font-size:13.5px; color:var(--ivory-soft); }

/* The closing line of the section is the standing rule the whole campaign is
   judged against, so it is set in the brand green rather than body ink. */
#s06 .aud{ color:var(--forest); }

.chainwrap{ display:flex; flex-direction:column; gap:22px; align-items:center; width:100%; margin-top:12px; }
/* Five steps in a fixed order, so they are set as a rail: numbered nodes on one
   continuous line, left to right. A centred column of sentences read as five
   unrelated statements rather than a process. */
.chain{
  position:relative; display:grid; grid-template-columns:repeat(5,1fr);
  gap:0; width:100%; max-width:940px;
}
.chain::before{
  content:""; position:absolute; left:10%; right:10%; top:23px;
  height:1px; background:var(--forest-line);
}
.chstep{
  position:relative; display:flex; flex-direction:column; align-items:center;
  gap:15px; padding:0 clamp(6px,1vw,14px); text-align:center; max-width:none;
}
.chstep i{
  position:relative; z-index:1; flex:0 0 auto;
  display:flex; align-items:center; justify-content:center;
  width:46px; height:46px; border-radius:999px;
  background:var(--paper); border:1px solid var(--forest);
  font-style:normal; font-family:var(--serif); font-size:14px;
  letter-spacing:.05em; color:var(--forest); font-variant-numeric:tabular-nums;
}
.chstep span{
  font-family:var(--serif); font-weight:500;
  font-size:clamp(14px,1.55vw,17px); line-height:1.38; color:var(--forest);
}
/* below this the five columns are too narrow to read, so the rail stands up */
@media (max-width:820px){
  .chain{ grid-template-columns:1fr; max-width:470px; }
  .chain::before{ left:23px; right:auto; top:24px; bottom:24px; width:1px; height:auto; }
  .chstep{ flex-direction:row; align-items:center; gap:20px; text-align:left; padding:10px 0; }
  .chstep span{ font-size:17px; }
}

/* ---------- 07 proposition reveal ---------- */
/* The question is the whole scene, so it is lifted out of the stack into its own
   card. On plain ivory it was just another paragraph in a column of paragraphs. */
/* The card pivots on its own vertical axis as the scene passes, so the question
   reads as a card being turned towards you rather than a block of text. The
   stage holds the perspective and takes the fade-up's inline transform; the
   rotation goes on the card inside it, because one element cannot carry both. */
.invite-stage{
  width:100%; max-width:780px; margin:4px 0 2px;
  perspective:1200px; perspective-origin:50% 50%;
}
.invite{
  position:relative; width:100%;
  display:flex; flex-direction:column; align-items:center; gap:16px;
  background:var(--blush-tint); border:1px solid var(--forest-line);
  border-radius:26px;
  padding:clamp(30px,4vw,50px) clamp(24px,4vw,54px) clamp(28px,3.4vw,42px);
  box-shadow:0 36px 78px -54px rgba(30,59,50,.62);
  transform-style:preserve-3d; will-change:transform;
}
.invite__mark{
  display:block; font-family:var(--serif); font-size:56px; line-height:.5;
  color:var(--blush-deep); user-select:none;
}
.invite__rule{
  display:block; width:min(300px,64%); height:1px;
  background:var(--forest); opacity:.38; transform-origin:50% 50%;
}
.invite__sub{ margin:0; max-width:46ch; }
/* Scoped to the card. `.reveal` is also on the #reveal turntable section from
   hero-v2, and a bare class selector was setting that whole section's inherited
   type to 70px Bodoni. */
.invite .reveal{
  font-family:var(--serif); font-weight:500;
  font-size:clamp(30px, 6.2vw, 70px); line-height:1.06; margin:0;
  letter-spacing:-.015em; text-wrap:balance; color:var(--forest);
}
#s07 .scene__inner{ gap:26px; }

/* ---------- 08 drone scene ---------- */
/* A centred column at every size: copy, then the flyover directly beneath it.
   Bottom-anchoring the panel fixed its top edge at 286px from the top of the
   viewport regardless of height, which stranded a growing void under the copy
   on tall screens. */
.scene--drone{
  flex-direction:column; align-items:center; justify-content:center;
  gap:clamp(12px,2.2vh,26px);
  padding-top:84px; padding-bottom:36px; min-height:100svh;
}
.drone-copy{ order:1; }
.drone-stage{ position:static; order:2; width:100%; z-index:1; pointer-events:none; }
/* the panel carries the flyover's own 16:9, so nothing is cropped off the bottom.
   08 is pinned at top top, so the whole column has to fit inside one viewport: the
   336px in the third term is the padding, the copy block and the gap above it. */
.drone-curve{
  position:relative; margin:0 auto;
  width:min(1260px, 94vw, calc((100svh - 336px) * 1.778)); aspect-ratio:16 / 9;
  border-radius:28px;
  overflow:hidden;
  box-shadow:0 30px 80px -34px rgba(0,0,0,.55);
}
@media (max-width:720px){
  .scene--drone{ padding-top:100px; padding-bottom:40px; }
  /* the copy wraps to more lines on a phone, so the panel gets a bigger allowance */
  .drone-curve{ width:min(94vw, calc((100svh - 420px) * 1.778)); border-radius:18px; }
}
.drone-curve img{
  position:absolute; inset:0;
  width:100%; height:100%; object-fit:cover; object-position:50% 50%;
  display:block;
}
#droneCanvas{
  position:absolute; inset:0; width:100%; height:100%;
  display:block; opacity:0; transition:opacity .4s ease;
}
.drone-curve.is-live #droneCanvas{ opacity:1; }
.drone-curve.is-live img{ visibility:hidden; }

/* The thoughts are written into the sky of the flyover, which is its own sunset and
   already carries real cloud. So each thought IS a cloud: a cluster of soft puffs with
   no edge anywhere, dissolving into the footage instead of sitting on top of it. */
.bubbles{ position:absolute; inset:0; }
/* Centring lives on the anchor, not the cloud. The anchor is a zero-height band
   pulled back by half its own width, so the cloud sits dead on the point using only
   margin and flex, never a transform: GSAP owns the cloud's transform for the rise-in
   and would wipe any centring written there. */
.bubble-anchor{
  position:absolute; width:60%; height:0; margin-left:-30%;
  display:flex; align-items:center; justify-content:center;
}
/* Two of the six clouds ride a fraction too high against the skyline, so they drop
   as a unit, artwork and words together. Targeted by position because c1 and c2 are
   each used twice and a class hook would move both copies. */
#droneBubbles > .bubble-anchor:nth-child(4),
#droneBubbles > .bubble-anchor:nth-child(5){ margin-top:20px; }
/* Each thought is a painted cloud, not a box: the artwork carries its own soft edge, so
   nothing here draws a shape. The cloud sits on ::before purely so the repeated ones can
   be mirrored without mirroring the words with them. */
.bubble{
  position:relative; width:46%; container-type:inline-size;
  aspect-ratio:var(--ar);
}
.bubble::before{
  content:""; position:absolute; inset:0;
  background:var(--img) no-repeat center / 100% 100%;
  transform:scaleX(var(--flip, 1));
}
/* Each thought is set on two lines, and each line gets its OWN box rather than
   sharing one centred rectangle. A cloud is narrow at the crown and wide at the
   base, so a single box that fits both ends up the size of the narrowest part
   and wastes most of the paint. The boxes below are measured off each cloud's
   alpha: for the band of rows a line occupies, the widest run that is solid all
   the way through. Two different widths and two different left edges is also
   what makes the lines sit staggered instead of stacked. Sizing is in cqw, so
   the words scale with the cloud rather than floating free of it. */
/* The thoughts are half-formed, so they are not set in full ink: a softened grey
   green reads as something being turned over rather than something declared. */
.bubble span{
  position:absolute; inset:0;
  color:#44544D; mix-blend-mode:multiply;
  font-family:var(--dream); font-style:normal; font-weight:500;
  font-size:5.7cqw; letter-spacing:.005em;
}
.bubble .bline{
  position:absolute; display:flex; align-items:center; justify-content:center;
  font-style:inherit; white-space:nowrap; line-height:1;
}
.bubble .bline--1{ top:var(--l1t); left:var(--l1l); right:var(--l1r); height:var(--l1h); }
.bubble .bline--2{ top:var(--l2t); left:var(--l2l); right:var(--l2r); height:var(--l2h); }
/* The gap between a cloud's two lines is `--l2t` minus `--l1t`: the boxes are
   flex-centred, so moving the tops toward each other closes the leading without
   moving the pair off the middle of the paint. */
.bubble--c1{ --img:url(assets/cloud-1.webp); --ar:1040/471;
  --l1t:32.6%; --l1l:20.3%; --l1r:26.9%; --l1h:16.3%;
  --l2t:46.6%; --l2l:11.3%; --l2r:18.0%; --l2h:16.3%; }
.bubble--c2{ --img:url(assets/cloud-2.webp); --ar:1040/426;
  --l1t:26.2%; --l1l:30.9%; --l1r:21.7%; --l1h:17.4%;
  --l2t:41.2%; --l2l:22.5%; --l2r:11.3%; --l2h:17.4%; }
.bubble--c3{ --img:url(assets/cloud-3.webp); --ar:1040/483;
  --l1t:31.0%; --l1l:32.8%; --l1r:37.2%; --l1h:15.9%;
  --l2t:44.6%; --l2l:25.0%; --l2r:13.7%; --l2h:15.9%; }
/* Both lines dropped 2px in the paint: the cloud is ~232px tall at desktop, so
   the pair moves by 0.86% of its own height and keeps its leading. */
.bubble--c4{ --img:url(assets/cloud-4.webp); --ar:1040/431;
  --l1t:33.9%; --l1l:15.4%; --l1r:17.5%; --l1h:14.4%;
  --l2t:47.3%; --l2l:9.4%; --l2r:9.4%; --l2h:14.4%; }
/* The two reused clouds are mirrored, and both text boxes mirror with them.
   These carry their own horizontal nudges, so the repeat can sit differently in
   the paint from the first use of the same cloud. */
.bubble--c1.is-flipped{ --flip:-1; --l1l:24.4%; --l1r:22.8%; --l2l:15.5%; --l2r:13.8%; }
/* "Why" pulled 5px left of the box centre: the edges move by the same 1.58% of
   the cloud width in opposite directions, so the box keeps its size and only its
   centre shifts. */
.bubble--c2.is-flipped{ --flip:-1; --l1l:23.3%; --l1r:29.3%; --l2l:14.5%; --l2r:19.3%; }
/* The last one is the statement, so it is bigger in every sense: a wider cloud,
   full ink, and type set large enough to be read as a headline. */
/* The cloud is 10% wider than it was. Type is sized in cqw, so it would have
   grown with it; 7.1 x (74/81.4) holds the words at exactly the size they were
   and spends the extra width on paint. */
/* Text sits 5px lower in the big cloud than the shared c4 tops: 5px of the
   ~232px desktop paint is 2.2% of its height, applied to both lines so the
   leading holds. */
.bubble--final{ width:81.4%; --l1t:36.1%; --l2t:49.5%; }
.bubble--final span{ font-size:6.1cqw; color:#3C3C3C; }
/* A phone's sky band is only about sixty pixels deep, far too shallow for a wide
   scatter, so the clouds stay centred there and only their height varies. */
@media (max-width:720px){
  .bubble-anchor{ left:50% !important; width:86%; margin-left:-43%; }
  .bubble{ width:78%; }
  .bubble--final{ width:96%; }
  .bubble--final span{ font-size:7.0cqw; }
}
/* the copy is deliberately compact here: the flyover is the argument, the words
   only frame it, so they take the smallest band that still reads at a glance */
.drone-copy{ gap:18px; }
/* the eyebrow bind is a fixed -8px against the section gap, which on this
   compact band left the label almost touching the headline. Half the pull here. */
.drone-copy > .eyebrow{ margin-bottom:-3px; }
/* set as two fixed lines, so the panel below keeps a predictable allowance */
.scene--drone .display{ max-width:27ch; font-size:clamp(27px,3.3vw,42px); }
.drone-kicker{ max-width:52ch; font-size:14.5px; }
/* ---------- why-now proofs ---------- */
.proofs{ width:100%; max-width:860px; border-top:1px solid var(--forest); }
.proof{
  display:grid; grid-template-columns:1fr 1fr; gap:20px;
  padding:16px 0; border-bottom:1px solid var(--forest-line); text-align:left;
}
@media (max-width:560px){ .proof{ grid-template-columns:1fr; gap:4px; } }
.proof b{ font-family:var(--serif); font-weight:500; font-size:17px; color:var(--forest); }
.proof span{ font-size:14.5px; color:var(--ink-soft); }

/* ---------- 10 + 11 campaign cards (editorial, one per scene) ---------- */
.camp{
  border:1px solid var(--forest-line); border-radius:14px; padding:32px 30px;
  text-align:left; display:flex; flex-direction:column; gap:14px;
  width:100%; max-width:920px;
  background:var(--blush-tint);
}
@media (max-width:640px){ .camp{ padding:24px 18px; } }
/* both campaigns are the same offer to two sides of one market, so both cards sit
   on the same blush ground */
.camp__label{
  font-family:var(--serif); font-size:12px; letter-spacing:.22em;
  text-transform:uppercase; color:var(--forest-soft); margin:0;
}
.camp__line{
  font-family:var(--serif); font-weight:500;
  font-size:clamp(28px,4.4vw,48px); line-height:1.1; margin:0;
  color:var(--forest);
}
/* The card is 920px wide, which at these sizes was running the prose past a hundred
   characters a line: long enough that the eye loses its place coming back to the
   left edge. The headline and the ad row still use the full width; only the reading
   copy is held to a measure. */
.camp__aud{ font-size:clamp(15px,1.9vw,17px); max-width:66ch; }
.camp__barrier{
  margin:0; font-size:12px; letter-spacing:.14em; text-transform:uppercase;
  color:var(--forest); font-weight:600;
}
.camp__body{ margin:0; font-size:14.5px; color:var(--ink-soft); }
/* the three samples are one set, so they sit side by side at a size you can read,
   each hook line under the frame it belongs to */
.camp__ads{
  display:grid; grid-template-columns:repeat(3,1fr); gap:clamp(14px,2vw,24px);
  border-top:1px solid var(--forest-line); padding-top:22px; margin-top:4px;
}
.mad{ display:flex; flex-direction:column; gap:10px; }
.mad__still{ margin:0; }
.mad__still img{
  display:block; width:100%; height:auto; aspect-ratio:9 / 16; object-fit:cover;
  border-radius:12px; background:var(--paper);
  border:1px solid var(--forest-line);
}
.mad__body{ display:flex; flex-direction:column; gap:5px; }
.mad span{
  font-size:10px; letter-spacing:.16em; text-transform:uppercase; color:var(--forest-soft);
}
.mad p{
  margin:0; font-family:var(--serif); font-size:clamp(13.5px,1.5vw,16px); line-height:1.4;
}

/* The three operating notes under the ad grid. Loose centred paragraphs read as
   afterthoughts, so they sit as labelled rows on one panel, the same construction
   as the timeline and the rule stack. */
.campnotes{
  width:100%; max-width:860px; margin-top:6px; text-align:left;
  border:1px solid var(--forest-line); border-radius:18px;
  background:var(--ivory); padding:6px clamp(20px,3vw,34px);
}
.campnote{
  display:grid; grid-template-columns:150px 1fr; gap:22px;
  padding:18px 0; align-items:baseline;
}
.campnote + .campnote{ border-top:1px solid var(--forest-line); }
.campnote__k{
  font-family:var(--serif); font-size:12px; letter-spacing:.2em;
  text-transform:uppercase; color:var(--forest);
}
.campnote p{ margin:0; font-size:14.5px; line-height:1.6; color:var(--ink-soft); }
@media (max-width:640px){ .campnote{ grid-template-columns:1fr; gap:6px; } }

/* pill buttons */
.pill{
  display:inline-block; font-family:var(--sans); font-size:15px; font-weight:500;
  color:var(--forest); border:1px solid var(--forest); border-radius:999px;
  padding:12px 28px; text-decoration:none; background:transparent;
  transition:background .3s ease, color .3s ease, transform .3s ease;
}
.pill--live:hover{ background:var(--forest); color:var(--ivory); transform:translateY(-2px); }
/* the one real ask on the page, so it is filled green rather than outlined */
.pill--solid{
  background:var(--forest); color:var(--ivory); border-color:var(--forest);
  text-transform:uppercase; letter-spacing:.14em; font-size:13px;
  padding:16px 34px;
}
.pill--solid:hover{ background:var(--ink); border-color:var(--ink); color:var(--ivory); }
.scene--forest .pill{ color:var(--ivory); border-color:rgba(251,251,239,.5); }
.scene--forest .pill--live:hover{ background:var(--ivory); color:var(--forest); }

/* ---------- form mock ---------- */
.formmock{
  display:flex; flex-direction:column; gap:26px; width:100%; max-width:420px;
}
.formmock .ff{
  display:flex; justify-content:space-between; align-items:baseline; gap:14px;
  text-align:left; border-bottom:1px solid var(--forest); padding-bottom:10px;
}
.formmock .ff span{ font-size:13px; color:var(--forest); }
.formmock .ff b{ font-family:var(--serif); font-weight:500; font-size:16px; color:var(--forest); }

/* ---------- timeline (forest) ---------- */
/* the run of events is a record, so it is set as a panel with a hairline under each
   entry rather than a loose column of text with a single gap doing all the work */
.timeline{
  display:flex; flex-direction:column; width:100%; max-width:620px; text-align:left;
  border:1px solid rgba(249,217,214,.30); border-radius:18px;
  background:rgba(249,217,214,.05);
  padding:6px clamp(20px,3vw,32px);
}
.tstep{
  display:grid; grid-template-columns:120px 1fr; gap:20px; align-items:baseline;
  padding:17px 0;
}
.tstep + .tstep{ border-top:1px solid rgba(249,217,214,.20); }
/* the clock and the line beside it are one thought, so both carry the brand colour */
.tstep b{
  font-family:var(--serif); font-weight:500;
  font-size:clamp(15px,2vw,19px); color:var(--blush);
  font-variant-numeric:tabular-nums;
}
.tstep span{ font-size:clamp(14px,2vw,16px); color:var(--blush); opacity:.88; }
@media (max-width:520px){ .tstep{ grid-template-columns:1fr; gap:5px; } }
.wa-arrival{
  margin-top:10px; display:flex; align-items:flex-end; gap:10px; justify-content:center;
}
/* bare glyph, no chip: the mark reads as the app itself rather than a button */
.wa-badge{
  display:flex; align-items:center; justify-content:center; flex:0 0 auto;
}
.wa-badge svg{ width:30px; height:30px; fill:var(--blush); }
/* her reply is the one human line on the page, so the bubble is filled blush */
.wa-bubble{
  display:inline-block; padding:13px 20px; border-radius:16px;
  font-size:17px; background:var(--blush); color:var(--forest);
  border-bottom-left-radius:4px;
}
.speed-big{
  font-family:var(--serif); font-weight:500; color:var(--ivory);
  font-size:clamp(24px,3.8vw,42px); line-height:1.2; margin:6px 0 0;
  max-width:24ch;
}

/* ---------- 14 grace rule stack (blush band) ---------- */
/* the four rules are a specification, not a poem, so they sit on their own card with
   the possessive set small and the noun carrying the line */
.stack{
  display:flex; flex-direction:column; width:100%; max-width:780px; margin-top:8px;
  background:var(--ivory); border:1px solid var(--forest-line); border-radius:20px;
  padding:4px clamp(24px,3.4vw,42px);
  box-shadow:0 34px 74px -56px rgba(30,59,50,.6);
}
.stack .sline{ display:block; overflow:hidden; }
/* the rule sits on the mask, not inside it, so the reveal cannot drag it */
.stack .sline + .sline{ border-top:1px solid var(--forest-line); }
/* the gap is set in px, not em: em here resolves against the inherited body size,
   not the 50px noun, so it read as a hairline no matter what it was set to */
.stack b{
  display:flex; align-items:baseline; gap:clamp(32px,4.8vw,72px); text-align:left;
  font-family:var(--serif); font-weight:500; letter-spacing:.01em;
  /* 1.22 plus the bottom padding clears the descender the reveal mask used to cut */
  line-height:1.22; padding:24px 0 26px;
}
.stack em{
  flex:0 0 auto; font-style:normal; font-family:var(--sans);
  font-size:12px; letter-spacing:.22em; text-transform:uppercase;
  color:var(--forest-soft);
}
.stack i{
  font-style:normal; font-size:clamp(26px,4.6vw,50px); line-height:1.22;
  color:var(--forest);
}
#s14 .scene__inner{ gap:28px; }
.approve{
  font-family:var(--serif); font-weight:500;
  font-size:clamp(19px,2.8vw,27px); line-height:1.3; margin:10px 0 0;
  max-width:26ch;
}

/* ---------- grace phone ---------- */
.phone{
  width:100%; max-width:420px; background:#fff;
  border:1px solid var(--forest-line); border-radius:22px; padding:24px 20px;
  text-align:left; box-shadow:0 24px 60px -44px rgba(30,59,50,.7);
}
.phone__label{
  font-size:10px; letter-spacing:.16em; text-transform:uppercase;
  color:var(--forest-soft); text-align:center; margin-bottom:14px;
}
.phone__thread{ display:flex; flex-direction:column; gap:10px; }
.msg{ max-width:88%; padding:10px 14px; border-radius:12px; font-size:14px; line-height:1.5; }
.msg--grace{ background:#F1F1E4; align-self:flex-start; border-bottom-left-radius:4px; }
.msg--sarah{ background:var(--blush); align-self:flex-end; border-bottom-right-radius:4px; }

/* ---------- handover + routes ---------- */
.handover{ display:flex; flex-direction:column; gap:12px; width:100%; max-width:720px; align-items:center; }
.handover__label{ font-size:11px; letter-spacing:.16em; text-transform:uppercase; color:var(--forest-soft); }
/* Appointwize hands over no summary record, so this card must be the conversation
   itself, read back speaker by speaker, and must not look like a generated brief */
.handover__card{
  width:100%; text-align:left;
  border:1px solid var(--forest-line); border-radius:16px; background:var(--ivory);
  padding:8px clamp(22px,3vw,34px) 14px;
  box-shadow:0 30px 66px -54px rgba(30,59,50,.6);
}
.hrow{ display:flex; flex-direction:column; gap:5px; padding:14px 0 0; }
.hrow span{
  font-size:11px; letter-spacing:.16em; text-transform:uppercase; color:var(--forest-soft);
}
.hrow b{
  font-family:var(--serif); font-weight:500; font-size:16px; line-height:1.45; color:var(--forest);
  max-width:52ch;
}
/* the lead's own words are set apart, so the exchange reads as two people */
.hrow--lead{ padding-left:clamp(14px,3vw,32px); }
.hrow--lead b{ color:var(--forest-soft); }
.routes{ display:grid; grid-template-columns:1fr 1fr; gap:16px; width:100%; max-width:820px; }
@media (max-width:640px){ .routes{ grid-template-columns:1fr; } }
/* which route he picks is the only decision on the page, so each one is headed like
   a card he can point at rather than a paragraph with a bold first line */
.route{
  border:1px solid var(--forest-line); border-radius:16px; padding:0;
  overflow:hidden; text-align:left;
}
.route:first-child{ background:var(--blush-tint); }
.route:last-child{ background:var(--paper); }
.route b{
  display:block; font-family:var(--serif); font-size:13px; letter-spacing:.16em;
  text-transform:uppercase; font-weight:500; margin:0;
  padding:15px clamp(20px,2.6vw,28px);
  background:var(--forest); color:var(--ivory);
}
.route:last-child b{ background:var(--forest-soft); }
.route p{
  margin:0; padding:24px clamp(20px,2.6vw,28px) 28px;
  font-size:14.5px; line-height:1.62; color:var(--ink-soft);
}

/* ---------- 17 money ---------- */
.money{
  width:100%; max-width:680px; border-top:1px solid var(--forest); text-align:left;
}
.mrow{
  display:grid; grid-template-columns:150px 150px 1fr; gap:30px; align-items:baseline;
  padding:26px 0; border-bottom:1px solid var(--forest-line);
}
@media (max-width:560px){ .mrow{ grid-template-columns:1fr; gap:4px; padding:20px 0; } }
.mrow b{ font-family:var(--serif); font-weight:500; font-size:15px; letter-spacing:.06em; }
.mrow em{
  font-style:normal; font-family:var(--serif); font-weight:500;
  font-size:clamp(22px,3vw,30px); font-variant-numeric:tabular-nums;
  color:var(--forest);
}
.mrow:first-child em{ font-size:clamp(28px,3.8vw,38px); }
/* month one is the offer, not a line in a table, so it is given its own ground and
   the £0 is set at the same scale as the 74,986 figure at 04. The negative margins
   match the added padding, so the three columns still line up with the rows under it. */
.mrow--free{
  /* the £0 is set around three times the size of the row text, so a shared baseline
     drops the label and the note to the floor of the row. This row centres instead. */
  align-items:center;
  background:var(--blush-tint); border-radius:18px; border-bottom-color:transparent;
  padding-left:clamp(16px,2.2vw,26px); padding-right:clamp(16px,2.2vw,26px);
  margin-left:calc(-1 * clamp(16px,2.2vw,26px));
  margin-right:calc(-1 * clamp(16px,2.2vw,26px));
}
.mrow.mrow--free em{ font-size:clamp(40px,6vw,72px); line-height:1; }
.mrow span{ font-size:14px; line-height:1.6; color:var(--ink-soft); }
.money-mhead{ margin-top:10px; }
.goal__head{
  font-family:var(--serif); font-size:13px; letter-spacing:.26em;
  text-transform:uppercase; color:var(--forest-soft); margin:0;
}
.freedom{
  font-family:var(--serif); font-weight:500;
  font-size:clamp(22px,3.4vw,34px); line-height:1.25; margin:6px 0 0;
  max-width:24ch;
}
/* the second line is the qualifier, not the promise, so it sits a size down */
.freedom--second{ margin-top:2px; font-size:clamp(17px,2.3vw,23px); }

/* ---------- 20 recap ---------- */
/* the recap is a ledger of what was agreed, not a process to walk through, so the
   same numbered steps are set as ruled rows rather than the 06 rail */
.chain--recap{
  display:flex; flex-direction:column; max-width:880px;
  border-top:1px solid var(--forest); margin-top:8px;
}
.chain--recap::before{ display:none; }
.chain--recap .chstep{
  display:grid; grid-template-columns:clamp(56px,7vw,92px) 1fr; gap:clamp(14px,2vw,26px);
  align-items:baseline; text-align:left; padding:18px 0;
  border-bottom:1px solid var(--forest-line);
}
.chain--recap .chstep i{
  display:block; width:auto; height:auto; border:0; background:none; border-radius:0;
  font-size:clamp(28px,4.2vw,50px); line-height:1; letter-spacing:0;
  color:var(--blush-deep);
}
.chain--recap .chstep span{ font-size:clamp(16px,2.1vw,21px); line-height:1.4; }
/* the last line is the offer itself, so it lands on the brand ground */
.recap-final{
  font-family:var(--serif); font-weight:500;
  font-size:clamp(22px,3.4vw,38px); line-height:1.25; margin:26px 0 0;
  width:100%; max-width:880px; text-align:center;
  background:var(--forest); color:var(--ivory);
  border-radius:18px; padding:28px clamp(24px,3vw,36px);
}

/* ---------- brand strip ---------- */
.brandstrip{
  display:flex; flex-wrap:wrap; gap:14px 34px; justify-content:center;
  border-top:1px solid var(--forest-line); border-bottom:1px solid var(--forest-line);
  padding:20px 10px; width:100%; max-width:820px;
}
.brandstrip span{
  font-family:var(--serif); font-size:clamp(16px,2.2vw,22px);
  letter-spacing:.18em; text-transform:uppercase; color:var(--forest-soft);
}

/* ---------- form sent + stamp ---------- */
.formsent{ margin-top:-6px; }
.formsent span{
  display:inline-block; font-size:13px; font-weight:600; letter-spacing:.14em;
  text-transform:uppercase; color:var(--forest);
  border:1px solid var(--forest); border-radius:999px; padding:9px 22px;
  background:var(--blush-tint);
}
.stamp{
  display:flex; flex-direction:column; gap:4px; align-items:center; margin-top:-4px;
}
.stamp b{
  font-family:var(--serif); font-weight:500; font-size:clamp(24px,3.6vw,40px);
  letter-spacing:.04em; line-height:1.15;
}
.stamp b:last-child{ font-size:clamp(15px,2vw,20px); letter-spacing:.26em; text-transform:uppercase; }

/* ---------- close ---------- */
/* 21 is two beats on one blush page: the letter is an ivory card set down on the
   brand ground, the same card-on-colour construction as the rule stack at 14, and
   the ask, the button and the salute stand free on the blush underneath it */
#s21{ background:var(--blush); }
#s21 .scene__inner{ gap:34px; }
/* the note, the ask and the salute are three separate beats, so each gets its own
   pocket of air rather than running on the shared gap alone */
#s21 .display{ margin-top:22px; }
#s21 .pill + .salute{ margin-top:16px; }
.notecard{
  width:100%; max-width:720px;
  display:flex; flex-direction:column; align-items:center; gap:18px;
  background:var(--ivory); border-radius:24px;
  padding:clamp(30px,4vw,48px) clamp(24px,4vw,52px) clamp(28px,3.6vw,44px);
  box-shadow:0 40px 84px -60px rgba(30,59,50,.55);
}
/* It is a letter, so the paragraphs are ranged left the way a letter is written.
   The signature stays centred under them, closing the card. */
.personal{ margin:0; max-width:620px; text-align:left; }
.personal cite{ display:block; text-align:center; margin-top:6px; }
.personal p{
  font-family:var(--serif); font-size:clamp(17px,2.3vw,22px); line-height:1.5; margin:0 0 14px;
}
.personal cite{
  font-style:normal; font-family:var(--sans); font-size:12px;
  letter-spacing:.14em; text-transform:uppercase; color:var(--ink-soft);
}
/* forced line breaks in display headlines (widow control) */
.dline{ display:block; }
/* a two-beat headline where the first line only sets up the second. dropped right
   down so the statement underneath is the thing that carries, instead of two
   display lines fighting each other for the same weight */
/* This line sits inside .display, so it was inheriting the display optical size
   (opsz 44). Bodoni cuts its hairlines for 44px+ and this line renders at 26px,
   so the thin strokes dropped out and the words went ghostly. It takes the body
   optical size instead, and a heavier weight to hold the line at reading size. */
.dline--lead{
  font-size:clamp(18px,2.4vw,26px); letter-spacing:.02em; line-height:1.3;
  margin-bottom:.85em; color:rgba(30,59,50,.84);
  font-variation-settings:"opsz" 9;
  font-weight:600;
}
.scene--forest .dline--lead{ color:rgba(251,251,239,.82); }
.scene--blush .dline--lead{ color:rgba(30,59,50,.84); }
/* the man himself, off duty, standing in the corner of the band that says the
   system is built around him. He is not on the floor of the section: his feet
   are set on the "You approve her." line (measured at 131-202px off the section
   bottom, which holds at every width this figure is visible), and he is tall
   enough that head, shoulders and chest clear the top edge of the rule card.
   He is anchored off the card's own right edge, so any overlap falls on the
   card's empty right side and never on the type.
   The figure is set at 60% of its original 860px. Both offsets are re-solved so
   the shoes land on the same point they did at full size: the shoes sit at 77%
   across a frame whose aspect is 289/874, so the left offset moves out by the
   width the figure lost (300 + .77 * (284 - 171) = 387), and the bottom offset
   moves up by the transparent margin under the feet that shrank with it. */
#s14 .casual{
  position:absolute; left:calc(50% + 259px); bottom:157px; z-index:3;
  pointer-events:none;
}
/* the png keeps about 12px of empty canvas under the soles at display size, so
   the figure alone drops onto the disk. the shadow is a sibling and must not
   move with him, which is why this is on the image and not on .casual */
#s14 .casual-grant{
  display:block; height:516px; width:auto;
  transform:translateY(5px);
  filter:drop-shadow(-14px 8px 26px rgba(30,59,50,.18));
}
/* his weight has to land on something: one solid oval disk under both feet.
   the soles span 26% to 86% across the frame, so the disk is centred at 51.5%
   and cut wide enough to carry both */
#s14 .casual__shadow{
  position:absolute; left:51.5%; bottom:1px; transform:translateX(-50%);
  width:150px; height:26px; border-radius:50%; pointer-events:none;
  background:rgba(30,59,50,.22);
  filter:blur(1.5px);
}
@media (max-width:1040px){ #s14 .casual{ display:none; } }
/* 07 sets up in two beats: the objection, then the answer. the answer is the
   statement piece, so it drops onto its own line at full ink strength */
.setup{ max-width:66ch; }
.setup__beat{ margin-top:.5em; color:var(--ink); font-weight:600; }
/* 13's closing line is one thought and reads as one line on desktop */
#s13 .lede--strongforest{ max-width:none; }
@media (min-width:920px){ #s13 .lede--strongforest{ white-space:nowrap; } }
/* 18 is two beats, the ask and what the ask is, so the second line is given air and
   the brand colour rather than running straight on under the first */
#s18 .scene__inner{ gap:30px; }
#s18 .dline + .dline{ margin-top:.44em; color:var(--blush); }
/* the lead line already carries its own space below it, so 18 does not add a second */
#s18 .dline--lead + .dline{ margin-top:0; }

/* the salute, closing the letter. it sits directly under the button rather than a
   third of a screen below it, and on its own layer so it reads as a cut-out
   standing on the page instead of a picture pasted onto it */
/* min-height:0 is load-bearing: the hero-v2 stylesheet ships a full-height
   .salute for its own standalone page, and it was reaching this figure. */
#s21 .salute{ margin:0; min-height:0; padding:0; width:100%;
  display:flex; align-items:flex-start; justify-content:center;
  position:relative; z-index:2; }
#s21 .salute img{
  display:block; width:auto; height:auto;
  max-height:min(52vh, 415px); max-width:100%;
  position:relative; z-index:1;
}
/* he needs ground under him, or the cut-out floats over the blush */
#s21 .salute::after{
  content:""; position:absolute; left:50%; bottom:-6px; transform:translateX(-50%);
  width:min(46%, 230px); height:26px; border-radius:50%; pointer-events:none;
  background:radial-gradient(ellipse at center,
    rgba(30,59,50,.24) 0%, rgba(30,59,50,.10) 48%, rgba(30,59,50,0) 74%);
  filter:blur(3px);
}

/* ---------- D2G band ---------- */
/* the band closes the page, so the type is at full strength on the green and the
   logo is set large enough to read as a signature rather than a favicon */
/* the hero's .sw-sky is a fixed, full-viewport ivory panel at z-index 0, and every
   .scene is position:relative so it paints over it. The footer was the one static
   band on the page, so it painted underneath the sky and read as an empty green
   strip with no type and no logo. It has to join the positioned layer. */
.d2g{
  position:relative; z-index:1;
  background:var(--forest); color:var(--ivory);
  display:flex; flex-direction:column; align-items:center;
  padding:72px 24px 78px; text-align:center;
}
/* "Driven by" now reads straight into the logo, so the three parts are one block
   and the contact details sit apart from it, out at the corner */
.d2g__stack{ display:flex; flex-direction:column; align-items:center; gap:14px; }
.d2g__line{
  font-family:var(--sans); font-size:13px; letter-spacing:.2em; text-transform:uppercase;
  margin:0; color:var(--ivory);
}
.d2g__foot{
  font-family:var(--serif); font-size:clamp(17px,2.3vw,23px); margin:6px 0 0;
  color:var(--ivory); display:flex; flex-wrap:wrap; gap:0 .35em;
  justify-content:center;
}
/* Solid gold. The leaf-gradient version ran dark-to-highlight-to-dark down the
   letterforms, which at 23px filled in Bodoni's thin strokes and read as muddy
   rather than metallic. One warm gold at full strength holds the line. */
.d2g__gold{ color:#D8B863; }
.d2g__logo{ display:block; margin:0; opacity:1; transition:opacity .2s ease; }
.d2g__logo:hover{ opacity:.8; }
.d2g__logo img{ display:block; height:clamp(38px,5vw,52px); width:auto; }
.d2g__contact{
  font-style:normal; display:flex; flex-direction:column; gap:4px;
  margin-top:34px; text-align:center;
}
.d2g__contact a{
  font-family:var(--sans); font-size:13.5px; letter-spacing:.04em;
  color:var(--ivory-soft); text-decoration:none;
  transition:color .2s ease;
}
.d2g__contact a:hover, .d2g__contact a:focus-visible{ color:var(--gold); }
@media (min-width:760px){
  .d2g__contact{
    position:absolute; right:clamp(24px,4vw,52px); bottom:34px;
    margin-top:0; text-align:right; align-items:flex-end;
  }
}

/* ---------- real photography + slots ---------- */
.photo{ margin:0; width:100%; max-width:820px; }
.photo img{ width:100%; height:auto; display:block; border-radius:14px; }
.slot{
  width:100%; max-width:560px; aspect-ratio:16/9;
  border:1px solid var(--rule); border-radius:14px;
  background:
    radial-gradient(ellipse 60% 70% at 50% 42%, rgba(17,17,17,.05), transparent 70%);
  display:flex; align-items:flex-end; justify-content:center; padding:14px;
}
.slot span{
  font-size:10px; letter-spacing:.16em; text-transform:uppercase; color:var(--ink-soft);
  text-align:center;
}
.slot--wide{ max-width:760px; }
/* 03 montage: the real cut, sitting where the placeholder slot used to be */
.montage{ margin:0; width:100%; max-width:760px; display:flex; flex-direction:column; gap:10px; }
.montage video{
  width:100%; aspect-ratio:16/9; display:block; object-fit:cover;
  border-radius:14px; border:1px solid var(--rule); background:var(--ink);
}
.montage figcaption{
  font-size:10px; letter-spacing:.16em; text-transform:uppercase;
  color:var(--ink-soft); text-align:center;
}
.shot{
  margin:0; width:100%; max-width:940px;
  display:flex; flex-direction:column; align-items:center; gap:10px;
}
.shot img{
  display:none; width:100%; height:auto; border-radius:18px;
  border:1px solid var(--forest-line); overflow:hidden;
}
.shot img.is-ready{ display:block; }
.shot figcaption{
  display:none; font-size:10px; letter-spacing:.16em; text-transform:uppercase;
  color:var(--forest-soft); text-align:center;
}
.shot.is-ready figcaption{ display:block; }

/* ---------- copy blocks added in the final pass ---------- */
/* a paragraph that belongs to the thing above it (a quote, a campaign panel)
   rather than starting a new beat, so it sits closer than the 30px scene gap */
.lede--tight{ margin-top:-12px; }

/* 03 closes on three lines that step down to the point. They are a group, not
   three loose paragraphs, so they carry their own tighter rhythm. */
.beat{
  display:flex; flex-direction:column; align-items:center; gap:14px;
  width:100%; max-width:64ch; margin-top:2px;
}
.beat .aud{
  font-size:clamp(19px,2.5vw,25px); color:var(--forest); max-width:40ch;
}
/* the reach line and the aim line are one thought, so they sit in one paragraph
   with a line step between them rather than a full paragraph gap */
.beat__close .dline + .dline{ margin-top:.5em; }
.beat__close .dline + .dline + .dline{ margin-top:0; }

/* 13: two minutes against six hours. Stacked centred paragraphs hid the whole
   point of the section, which is the gap between them, so they are set side by
   side and the fast one carries the light. */
/* the marks straddle the top edge, so the box cannot clip its own children.
   the rounded corners are moved onto the cells instead, which is what the
   overflow was clipping in the first place. */
.split{
  display:grid; grid-template-columns:1fr 1fr;
  width:100%; max-width:760px; text-align:left;
  border:1px solid rgba(251,251,239,.22); border-radius:18px;
  margin-top:17px;
}
.split__cell{
  display:flex; flex-direction:column; gap:8px;
  padding:22px clamp(18px,2.4vw,28px) 24px;
}
.split__cell + .split__cell{ border-left:1px solid rgba(251,251,239,.22); }
.split__cell--now{ background:rgba(251,251,239,.07); border-radius:17px 0 0 17px; }
/* The verdict mark straddles the top edge of its cell: half the 34px height sits
   inside the box, half outside. Pulling it up by the cell padding plus half its
   own height also closes the dead space that sat under it. */
.split__mark{
  align-self:center; height:34px; width:auto;
  margin-top:-39px; margin-bottom:6px; position:relative; z-index:1;
}
.split__k{
  font-family:var(--serif); font-size:clamp(20px,2.6vw,26px);
  letter-spacing:.01em; color:var(--ivory-soft);
}
.split__cell--now .split__k{ color:var(--blush); }
.split__cell p{ margin:0; font-size:14.5px; line-height:1.6; color:var(--ivory-soft); }
@media (max-width:640px){
  .split{ grid-template-columns:1fr; }
  .split__cell--now{ border-radius:17px 17px 0 0; }
  .split__cell + .split__cell{ border-left:0; border-top:1px solid rgba(251,251,239,.22); }
}

/* 17: the commercial ask stated once, in full, in its own panel */
.breath{
  width:100%; max-width:680px; text-align:left;
  display:flex; flex-direction:column; gap:10px;
  background:var(--ivory); border:1px solid var(--forest-line); border-radius:18px;
  padding:22px clamp(20px,3vw,32px) 24px;
}
.breath b{
  font-family:var(--serif); font-weight:500; font-size:12px; letter-spacing:.2em;
  text-transform:uppercase; color:var(--forest);
}
.breath p{ margin:0; font-size:15px; line-height:1.62; color:var(--ink-soft); }
/* the one number the month is judged on: a serif line in the brand colour, not a
   footnote under the four gates */
.aud--star{ color:var(--forest); max-width:40ch; margin-top:2px; }

/* 19: the record, at reading size. Both sentences are one piece of body copy and
   are set identically; splitting the second one into a softer tone made it read
   as a caption hung off the first. */
/* named `record`, not `proof`: `.proof` is already the s09 why-now grid row and
   its `span` rule was winning over the sentences here */
.record{
  margin:0; max-width:62ch; font-family:var(--sans);
  font-size:clamp(16px,1.9vw,18px); line-height:1.6;
  color:var(--ink); text-wrap:pretty;
}
.record__lead{ color:inherit; }
/* the proof line itself sits on the brand tint rather than running on as body copy */
.strip{
  width:100%; max-width:760px; margin:8px 0 0; text-align:center;
  font-family:var(--serif); font-weight:500; color:var(--forest);
  font-size:clamp(18px,2.4vw,25px); line-height:1.4;
  background:var(--blush-tint); border-radius:18px;
  padding:24px clamp(22px,3vw,34px);
}

/* ============================================================
   animation prep — gated on .anim (GSAP present + motion OK),
   so a blocked CDN or reduced-motion still shows everything.
   ============================================================ */
.anim .fade-up{ opacity:0; transform:translateY(24px); }
.anim .msg{ opacity:0; transform:translateY(12px); }
.anim .tstep{ opacity:0; }
.anim .wa-arrival{ opacity:0; transform:scale(.92); }
.anim .ff b{ opacity:0; }
.anim .formsent{ opacity:0; transform:scale(.9); }
.anim .stamp{ opacity:0; transform:scale(.94); }
.anim .chstep{ opacity:.14; }
/* 20px, not 26: the mask is shorter now that the lines carry their own padding */
.anim .stack b{ opacity:0; transform:translateY(20px); }
.anim .invite__rule{ transform:scaleX(0); }

/* drone start state */
.anim .bubble{ opacity:0; }
.anim .drone-kicker{ opacity:0; }

/* print and full-page capture: everything visible */
@media print{
  .anim *{ opacity:1 !important; transform:none !important; }
  .topnav, .navsheet, .progressbar{ display:none !important; }
  .scene{ min-height:auto; page-break-inside:avoid; }
}

/* ============================================================
   DESKTOP NOTICE
   The scroll world is a desktop piece: on a phone the hero is
   flattened to stills to keep the tab alive, so a visitor on a
   phone is not seeing the thing being pitched. Shown once per
   session, phones only, dismissible.
   ============================================================ */
.deskgate{
  position:fixed; inset:0; z-index:9000; display:none;
  align-items:center; justify-content:center; padding:24px;
  background:rgba(17,17,17,.55);
  -webkit-backdrop-filter:blur(3px); backdrop-filter:blur(3px);
}
.deskgate.is-open{ display:flex; }
.deskgate__card{
  background:var(--forest); color:var(--blush);
  border-radius:22px; padding:34px 26px 26px;
  width:100%; max-width:340px; text-align:center;
  box-shadow:0 24px 60px rgba(17,17,17,.35);
  animation:deskgateIn .42s cubic-bezier(.16,1,.3,1) both;
}
@keyframes deskgateIn{
  from{ opacity:0; transform:translateY(14px) scale(.97); }
  to{ opacity:1; transform:none; }
}
.deskgate__icon{ display:block; width:74px; height:auto; margin:0 auto 20px; }
.deskgate__title{
  font-family:var(--serif); font-weight:500;
  font-size:23px; line-height:1.3; margin:0 0 10px;
  color:var(--blush); text-wrap:balance;
}
.deskgate__body{
  font-family:var(--sans); font-size:14px; line-height:1.6;
  margin:0 0 22px; color:rgba(249,217,214,.78);
}
.deskgate__btn{
  font-family:var(--sans); font-size:13px; font-weight:500;
  letter-spacing:.04em; cursor:pointer;
  background:var(--blush); color:var(--forest);
  border:0; border-radius:999px; padding:12px 26px;
}
.deskgate__btn:focus-visible{ outline:2px solid var(--blush); outline-offset:3px; }
@media (prefers-reduced-motion:reduce){
  .deskgate__card{ animation:none; }
}

/* ---------- 16c the lettings aside ----------
   Every other scene sets its copy straight onto the ground. This one is boxed:
   a lighter panel on the blush band, contained and ruled, so it reads as a note
   pinned into the pitch rather than a chapter of it. Padding is deliberately
   larger than the internal gaps so the block holds together as one object. */
.aside{
  width:100%; max-width:70ch;
  display:flex; flex-direction:column; gap:22px; align-items:center;
  background:var(--blush-tint);
  border:1px solid var(--blush-deep);
  border-radius:18px;
  padding:clamp(30px,4vw,52px) clamp(24px,4vw,56px);
  box-shadow:0 18px 44px -34px rgba(17,17,17,.5);
}
.aside .display{ margin:0; }
.aside .note--wide{ max-width:64ch; text-wrap:pretty; }
/* the twist sentence is set apart from the three "sames" so the contrast lands
   as its own beat, both on the page and in the reveal timeline */
.aside__twist{ display:block; margin-top:.5em; color:var(--forest); }
@media (max-width:640px){
  .aside{ gap:18px; border-radius:14px; }
}
