/* Case study pages (work/*) share nav + type styles from ../styles.css.
   This file adds the black case-study background and the case-specific
   content layout. */

/* Overrides the shared html,body { background: var(--bg) } rule (green) —
   unscoped since this stylesheet only ever loads on case-study pages.
   Covers html too, not just body: on a fast/instant scroll, not-yet-rasterized
   tiles briefly fall back to the root element's background, which flashes
   green if html and body disagree. */
html,
body {
  background: #000000;
}

/* The full-bleed bands below measure themselves in vw, which counts the width
   of a classic scrollbar — without this the page would overflow sideways by the
   scrollbar's width wherever it isn't an overlay. `clip` rather than `hidden`:
   hidden would make the body a scroll container, which breaks the sticky
   prototype stage inside it. */
body {
  overflow-x: clip;
}

@media (max-width: 860px) {
  .nav-links.is-open {
    background: #000000;
  }

  /* The black nav needs a light overlay to open onto, the way the work
     landing page does it — black links on the black overlay would vanish. */
  .buying-page .nav-links.is-open {
    background: #ffffff;
  }
}

/* Full-bleed black band — but the logomark inside keeps its own natural
   842:197 aspect ratio (max-width caps it at native size) instead of being
   stretched to match the band's width. Same height rule as the video hero:
   820px at the design's 1440 frame, growing no taller than 900px. */
.case-hero {
  background: #000000;
  width: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  height: clamp(26.25rem, 57vw, 56.25rem);
  padding: 3rem 1.25rem;
}

.case-hero-mark {
  width: 100%;
  max-width: 52.625rem;
  height: auto;
  display: block;
}

/* Video hero variant (.case-page-media): the nav sits on top of a full-bleed
   video instead of above a black band, so it's lifted out of flow. Keeps the
   grid's max-width and gutters, so it still lines up with the content. */
.case-page-media .nav {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  margin: 0 auto;
}

/* Beats the rule above once nav.js docks it — same specificity, and this
   stylesheet loads after styles.css, so the absolute would otherwise win.
   Nothing shifts on docking here: the nav is already out of flow. */
.case-page-media .nav.is-docked {
  position: fixed;
}

/* 820/1440 in the design — scales with the viewport, floored so the video
   never collapses to a strip on mobile and capped at 900px so it doesn't
   swallow the screen on a large display. */
.case-hero-media {
  position: relative;
  width: 100%;
  height: clamp(26.25rem, 57vw, 56.25rem);
  background: #000000;
  overflow: hidden;
}

.case-hero-video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Dark band for a hero photograph rather than a video. The band takes the
   photograph's own 1.9:1 ratio instead of the video hero's clamped height: a
   1.9:1 image can't fill a band that tall without losing most of its width, and
   on a phone that was cropping the right of the handset clean off. Floored so
   it still reads as a hero band rather than a strip, and the extra height that
   floor creates is what the overlaid nav sits in. */
.case-hero-dark {
  background: #1a1a1a;
  height: auto;
  min-height: 15rem;
  aspect-ratio: 3537 / 1861;
}

/* Fills the band. This used to reproduce the Figma frame's own numbers — 88%
   wide, bled off the left, 104% tall — but in Figma that box holds the photo
   stretched vertically, and a browser can't stretch and cover at once: with
   object-fit: cover the same box cropped 132px off each side of the photo,
   taking the right of the phone with it, and left 240px of empty band beside
   it. Covering the band instead shows the whole phone and trims only about
   4px top and bottom, the band and the photo being within 1% of the same
   aspect ratio. */
.case-hero-image {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* White card starts right after the hero band and runs full-bleed to the
   bottom of the page — no black margin on any side. */
.case-content {
  background: #ffffff;
  color: #000000;
  width: 100%;
  padding: 3.75rem var(--page-gutter);
  display: flex;
  flex-direction: column;
  align-items: center;
}

/* The column is the full 1600px content grid, left-aligned on the same page
   gutter as the media — it is the body copy inside it that keeps a reading
   measure, not the column itself. */
.case-content-inner {
  width: 100%;
  max-width: 100rem;
  display: flex;
  flex-direction: column;
  gap: 2.5rem;
}

/* 760px measure for anything that is read as prose. The title, media and
   credit line run the full width instead. */
.case-intro,
.case-tagline,
.case-section {
  width: 100%;
  max-width: 47.5rem;
}

/* Capped at 800px so a long title wraps instead of running the full width of
   the grid — short ones are unaffected. */
.case-title {
  width: 100%;
  max-width: 50rem;
  font-weight: 400;
  font-size: clamp(1.75rem, 3.9vw, 3.5rem);
  line-height: 1.2;
}

.case-intro {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.case-tagline {
  font-weight: 500;
  font-size: clamp(1.375rem, 2.1vw, 1.875rem);
  line-height: 1.3;
}

.case-description {
  font-weight: 400;
  font-size: clamp(1rem, 1.7vw, 1.5rem);
  line-height: 1.42;
}

/* Title runs the full grid; below it the copy keeps its reading measure and the
   credit sits out to the right of the standfirst rather than at the foot of the
   page. 26% puts the credit's left edge at 74% of the column, which is where the
   design sets it — x1232 of the 100–1628 content column in the 1728 frame —
   plus a flat 40px pulling the block that much further left again. Widening the
   column is what moves it: its right edge is pinned to the grid, so the left
   edge is the only one free to travel. Floored at 11rem so "Design (design
   discovery)" doesn't break up as the grid narrows. Every case study opens this
   way, so it is named for the block rather than for the page it first appeared
   on. */
.case-intro-split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(11rem, calc(26% + 40px));
  gap: 2.5rem;
  align-items: start;
}

/* The left column. A flex column rather than plain flow because the gap has to
   match the 40px .case-content-inner puts between its own children — the copy
   should not close up just because it moved inside the split. */
.case-intro-main {
  display: flex;
  flex-direction: column;
  gap: 2.5rem;
}

.case-video {
  width: 100%;
  height: auto;
  display: block;
  border-radius: 0.75rem;
}

/* Matches the square-cornered images it sits among. */
.case-video-square {
  border-radius: 0;
}

/* Two-column run: the copy stacks down the left, the media sits alongside it
   on the right, each taking half the grid. The media is placed by grid rather
   than DOM position, so it can start level with the first section while the
   copy keeps flowing underneath it. */
.case-split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 20px;
  align-items: start;
}

.case-split-copy {
  display: flex;
  flex-direction: column;
  gap: 2.5rem;
}

/* Full content width — no breakout needed now that the column itself is the
   whole grid. */
.case-media-row,
.case-image,
.case-anatomy {
  width: 100%;
}

/* Three-up concept strip. */
.case-media-row {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 4px;
}

/* Scales to the breakout width at its own aspect ratio (the width/height
   attributes reserve the right box before the file loads). */
.case-image {
  height: auto;
  display: block;
}

/* Full-bleed media band. These exports are whole bands from the design — the
   grey field and the mockup's 164px inset within it are baked in — and the
   design runs them the full width of the frame. Sitting inside the 1600px
   content column, the grey stopped short with white either side. Breaking out
   to the viewport restores it, and because the inset is part of the export the
   mockup lands back at the design's 81% of the band with nothing further to
   adjust.

   50% - 50vw is the distance from the column's edge to the viewport's, whatever
   the column happens to be doing — sitting on its gutters, or centred at its
   max width on a wide display. */
.case-image-bleed {
  width: 100vw;
  max-width: none;
  margin-inline: calc(50% - 50vw);
}

/* Captioned media. The gap matches the one between a section heading and its
   body, so the caption reads as belonging to the image above it. */
.case-figure {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.case-caption {
  color: rgba(0, 0, 0, 0.6);
  font-size: clamp(0.875rem, 1.25vw, 1.125rem);
  line-height: 1.44;
}

/* Caption for a full-bleed band. The band runs edge to edge, so the caption
   has to supply the page's own 1600px column and gutter itself — that lands it
   on the same left edge as the body copy above. */
.case-caption-band {
  width: 100%;
  max-width: calc(100rem + var(--page-gutter) * 2);
  margin: 0 auto;
  padding: 0 var(--page-gutter);
}

/* A run of feature paragraphs, each led by its name inline. They need air
   between them, unlike the consecutive .case-section-body paragraphs
   elsewhere, which deliberately run on as continuous lines. */
.case-features {
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
}

/* Data anatomy band — the one black-on-white block in the case study, so it
   carries its own inverted colours rather than inheriting the white card's. */
.case-anatomy {
  background: #000000;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(1.5rem, 8.5vw, 7.5rem);
  padding: 0 1.25rem 2.5rem;
}

.case-anatomy-title {
  color: #ffffff;
  font-weight: 600;
  font-size: clamp(1.375rem, 2.8vw, 2.5rem);
  line-height: 1.2;
}

/* 650px at the design's 1440 frame; the percentage keeps the diagram to
   roughly that share of the band as it scales down. */
.case-anatomy-figure {
  width: min(40.625rem, 48%);
  height: auto;
  display: block;
}

.case-media-tile {
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  display: block;
}

/* Same 21px/32px as the copy it heads — the weight carries the hierarchy, not
   the size. */
.case-section-title {
  font-weight: 600;
  font-size: clamp(1rem, 1.7vw, 1.3125rem);
  line-height: 1.524;
  margin-bottom: 0.5rem;
}

/* 21px/32px at full size — 24px read slightly too large for running text.
   The line height is a ratio rather than a flat 32px so it tightens with the
   type between 860px and the point the clamp tops out. */
.case-section-body {
  font-weight: 300;
  font-size: clamp(1rem, 1.7vw, 1.3125rem);
  line-height: 1.524;
}

/* Lead-in to a line of body copy ("Swipe to explore.") — carries the same
   semibold as .case-section-title rather than the UA's full bold, so a run of
   lead-ins reads as one level of hierarchy with the section headings. The
   same goes for a bullet led by its own name. */
.case-section-body strong,
.case-list strong {
  font-weight: 600;
}

/* A paragraph that opens a new thought instead of continuing the run above it —
   one line's rest, which is what the design leaves. Consecutive
   .case-section-body paragraphs deliberately run on as continuous lines, so the
   break is opted into rather than being the default. */
.case-section-body.is-break {
  margin-top: 1.524em;
}

/* Bullets sit inside the text column rather than hanging outside it — the
   `*` reset zeroes the UA padding, so the indent is set here. Matches
   .case-section-body: the two interleave inside a single section. */
.case-list {
  padding-left: 1.5em;
  font-weight: 300;
  font-size: clamp(1rem, 1.7vw, 1.3125rem);
  line-height: 1.524;
  /* Space where the bullets hand back to running text. Without it the `*`
     reset leaves the closing paragraph flush against the last bullet, reading
     as one more bullet that lost its marker. In em, so it tracks the type down
     to the smaller mobile size. Nothing above the list: the lead-in ends on a
     colon, so it should stay tied to the bullets it introduces. */
  margin-bottom: 1em;
}

/* A list that opens straight off its lead-in line ("What came back, again and
   again:") needs a break from it — flush, the first bullet reads as one more
   line of that paragraph. The same line's rest .is-break gives a paragraph, so
   the two mean the same thing wherever they appear. A list under a
   .case-section-title is left alone: the heading carries its own margin. */
.case-section-body + .case-list {
  margin-top: 1.524em;
}

/* The .browser-frame component itself lives in ../styles.css — the work
   landing page uses it for the Catwalk card too. */

/* Dark hero band holding a .browser-frame. 112px of headroom at the design's
   1728 frame, which is what the overlaid nav sits in; no bottom padding, so the
   screenshot runs flush into the white content below the way the design places
   it. Named for the component it carries rather than a page — the Catwalk hero
   it was written for now draws its own chrome, leaving Pulse AI Search. */
.frame-hero {
  background: #141414;
  display: flex;
  justify-content: center;
  padding: clamp(5.5rem, 6.48vw, 7rem) 0 0;
}

/* Live Pulse banner, drawn over the top of the hero screenshot. The screenshot
   is 1993px wide, so every measurement below is that file's own pixel value
   divided by 19.93 and written in cqw — 1cqw is one hundredth of the displayed
   screenshot — which keeps the drawn banner registered with the still one
   underneath it at every width. The gradient and the logomark come from the
   product's own implementation; only the layout around them is new. */
.pulse-screen {
  position: relative;
  container-type: inline-size;
}

/* 399 of the screenshot's 968px height is banner — 20% of its width, which is
   what the Figma frame says too. */
.pulse-banner {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 20cqw;
  background: #000000;
  overflow: hidden;
  font-weight: 400;
  line-height: 1;
}

/* Pulls the right-hand colour back into the black, so the gradient arrives
   rather than starting at an edge. */
.pulse-banner::after {
  content: "";
  position: absolute;
  top: 0;
  right: 0;
  width: 40%;
  height: 100%;
  background: linear-gradient(90deg, rgba(0, 0, 0, 1), rgba(0, 0, 0, 0.5));
  z-index: 1;
}

.pulse-banner-gradient {
  position: absolute;
  top: 0;
  right: 0;
  width: 40%;
  height: 100%;
}

.pulse-banner-mark {
  position: absolute;
  left: -4.86cqw;
  top: -2.85cqw;
  width: 25.71cqw;
  height: auto;
}

/* Everything from here sits above the ::after wash. */
.pulse-banner-greeting,
.pulse-banner-strap,
.pulse-banner-field,
.pulse-banner-badge {
  position: absolute;
  z-index: 2;
}

.pulse-banner-greeting {
  left: 29.4cqw;
  top: 4.8cqw;
  font-size: 2.12cqw;
  color: #ffffff;
}

.pulse-banner-strap {
  left: 29.35cqw;
  top: 7.88cqw;
  font-size: 1cqw;
  color: rgba(255, 255, 255, 0.7);
}

/* 824 x 80 of the screenshot's pixels, on an 11.5px corner. */
.pulse-banner-field {
  left: 29.3cqw;
  top: 10.79cqw;
  width: 41.34cqw;
  height: 4.01cqw;
  border-radius: 0.58cqw;
  background: #ffffff;
  display: flex;
  align-items: center;
  padding: 0 0.83cqw 0 1.1cqw;
}

.pulse-banner-placeholder {
  flex: 1;
  font-size: 1.05cqw;
  color: #767676;
}

.pulse-banner-send {
  flex: none;
  width: 2.49cqw;
  height: 2.49cqw;
  border-radius: 50%;
  background: #eeeeee;
  color: #aaaaaa;
  display: flex;
  align-items: center;
  justify-content: center;
}

.pulse-banner-send svg {
  width: 1.1cqw;
  height: 1.1cqw;
  display: block;
}

.pulse-banner-badge {
  right: 3.5cqw;
  bottom: 2.91cqw;
  display: flex;
  align-items: center;
  gap: 0.3cqw;
  font-size: 1.36cqw;
  color: #ffffff;
}

.pulse-banner-badge-mark {
  width: 1.25cqw;
  height: 1.25cqw;
  display: block;
}

/* The still banner in the screenshot is the reduced-motion fallback — dropping
   the drawn one uncovers it, so the hero still reads exactly as designed. */
@media (prefers-reduced-motion: reduce) {
  .pulse-banner {
    display: none;
  }
}

/* Blue hero band. The export carries its own browser chrome and the same blue
   as the band, so the two read as one field, and the mockup is cut off by the
   foot of the band the way the design draws it. The artwork brings its own
   headroom — 114 of the design's 1728, so 6.6vw once it spans the viewport —
   which is what the overlaid nav sits in. The padding only tops that up to a
   96px floor, the same one the Stream hero uses: a few pixels at the design's
   width, growing as the band gets shorter, so there is never less than 96px of
   clear blue above the mockup for a nav that stands about 68px tall. */
.catwalk-hero {
  background: #0d47a1;
  padding: max(0rem, 6rem - 6.6vw) 0 0;
}

.catwalk-hero-figure {
  width: 100%;
  height: auto;
  display: block;
}

/* Grey field for the second mockup — top padding only, same flush bottom edge
   as the hero. The caption lives outside the band, on the white page, the way
   the event-apps figures do it. */
.catwalk-band {
  background: #f5f5f5;
  display: flex;
  justify-content: center;
  padding: clamp(1.5rem, 3.47vw, 3.75rem) 0 0;
}

/* 1400 of the design's 1728 frame. */
.catwalk-band .browser-frame {
  width: 81%;
  max-width: 87.5rem;
}

/* The hero browser is the exception: it is measured off the content grid rather
   than given its own width, so it tracks the case copy below it as the grid
   moves. It sits 10% inside that grid — the full column read a shade too big
   against the copy — and because both the percentage and the cap are the same
   10% down from .case-content's own 100% and 100rem, the inset holds at every
   width instead of only at the one it was judged on. */
.frame-hero {
  padding-left: var(--page-gutter);
  padding-right: var(--page-gutter);
}

.frame-hero .browser-frame {
  width: 90%;
  max-width: 90rem;
}

/* A run of full-bleed .catwalk-band screenshots, one after another. The gap is
   the 40px of white the design leaves between them, and the background makes
   that white explicit — case-study pages sit on black, so an unpainted gap
   would show the page through. */
.pulse-bands {
  background: #ffffff;
  display: flex;
  flex-direction: column;
  gap: 2.5rem;
  /* Closes the page. The credit used to sit under this run and supply the
     white; now that it runs beside the standfirst, the last caption would
     otherwise end flush against the foot of the page. */
  padding-bottom: 2.5rem;
}

/* Pinned prototype walkthrough — see prototype-scroll.js.

   The track is a tall empty box; the stage inside it is sticky, so it pins to
   the viewport for the track's whole length and releases on its own at the end.
   While pinned, the script maps how far the page has scrolled through the track
   onto how far the strip is translated sideways inside its window — so
   scrolling down walks along the carousel, and vertical scrolling resumes by
   itself once the strip runs out.

   The page scroll is never intercepted: no wheel handler, no preventDefault. So
   trackpad, keyboard, scrollbar and touch all keep working, and if the script
   fails the strip is simply a cropped band rather than a trap. */
.proto-track {
  position: relative;
  /* Placeholder only — prototype-scroll.js replaces this with a height derived
     from the strip's actual overflow, so the scroll ratio holds at any size. */
  height: 240vh;
}

/* The stage is one viewport tall — it has to be, since it pins — so this
   padding is the only thing standing between the frame and its full height.
   Kept tight for that reason: at the old 56px the frame came out a 1.7:1
   letterbox once it filled the width, which showed too little of the page at a
   time. Enough is left that the frame reads as sitting on the stage rather than
   butting against the viewport edges. */
/* No background of its own: the frame sits straight on the white content card,
   so the padding above and below reads as page rather than as a grey band. Safe
   to leave transparent because the track exists only to hold this — nothing
   else occupies that stretch of the page to show through behind it. */
.proto-stage {
  position: sticky;
  top: 0;
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(0.375rem, 0.7vw, 0.75rem) 0;
  overflow: hidden;
}

/* Half the standard figure gap. The stage above already contributes its own
   bottom padding before the caption, so the usual 8px landed on top of that and
   read as a gap rather than a caption attached to its image. */
.proto-figure {
  gap: 0.25rem;
}

/* The same device bezel the TikTok case study's screens carry, but built in CSS
   rather than baked into the export — this frame is a viewport the page scrolls
   inside, so a bezel in the image would scroll away with the first gesture.
   Measured off assets/tiktok-hashtag.jpg: 31px of #191919 and a 64px outer
   radius on a 2400px-wide export, i.e. 1.29% and 2.67% of the frame's width,
   which is what the vw terms below reproduce as it resizes.

   Fills the content column, like .tiktok-frame does, rather than the 81% the
   browser-chrome version used. */
.proto-frame {
  --proto-bezel: clamp(0.5rem, 1.26vw, 1.29rem);
  --proto-radius: clamp(1rem, 2.6vw, 2.67rem);

  width: 100%;
  max-width: 100rem;
  height: 100%;
  display: flex;
  flex-direction: column;
  background: #191919;
  border-radius: var(--proto-radius);
  padding: var(--proto-bezel);
}

/* min-height: 0 lets this flex item shrink below the page image's intrinsic
   height, which is the whole point — without it the window grows to 4000-odd
   pixels and there is nothing to scroll through. */
.proto-window {
  flex: 1;
  min-height: 0;
  overflow: hidden;
  background: #ffffff;
  position: relative;
  /* Concentric with the bezel: an inner radius of the outer less the bezel
     keeps the two curves parallel instead of pinching at the corners. */
  border-radius: calc(var(--proto-radius) - var(--proto-bezel));
}

/* The aspect-ratio matters beyond shape: it gives this box a definite height,
   which is what lets the strip window below position itself in percentages of
   the page rather than needing pixel maths at every viewport size. */
.proto-page {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  aspect-ratio: 1600 / 5986;
}

.proto-page-img {
  display: block;
  width: 100%;
  height: 100%;
}

/* Each window sits exactly over a row the page export clips, so at rest it is
   indistinguishable from the page underneath and panning reveals what the page
   frame cuts off. Every offset below was measured against the 1600x6086 page
   and expressed as a percentage, so it tracks the page image at any frame
   width. In both cases the window starts below the row's heading — that leaves
   the heading to come from the page image, where it stays put while the cards
   pan past it. */
.proto-strip-window {
  position: absolute;
  overflow: hidden;
}

/* Taller than its window and pulled up by the heading it excludes, so the cards
   line up with the page beneath; width free to overflow, and that overflow is
   the sideways travel. The percentages resolve against the window's height,
   which is why the window needs a definite one. max-width: none defeats any
   inherited img cap. */
.proto-strip {
  position: absolute;
  left: 0;
  width: auto;
  max-width: none;
  display: block;
}

/* Latest Collections: strip node at x=100, y=496; its top 60px are the heading,
   so the window is y=556..1025 — the block between the strip's own two 1px
   borders — by x=100..1600. */
.proto-strip-window--collections {
  left: 6.25%;
  top: 9.28834%;
  width: 93.75%;
  height: 7.83495%;
}

.proto-strip--collections {
  top: -12.79318%;
  height: 123.02772%;
}

/* Street Style: strip node at x=0, y=5216, running full page width and ending
   flush with the foot of the page. Its cards start 124px down, so the window is
   y=5340..5986. */
.proto-strip-window--street {
  left: 0;
  top: 89.20815%;
  width: 100%;
  height: 10.79185%;
}

.proto-strip--street {
  top: -19.19505%;
  height: 119.19505%;
}

/* Three feature mockups on their own grey field. The columns are sized in
   proportion to each export's natural width — the three exports share one
   height, so proportional columns keep them rendering at a matched height
   instead of one card standing taller than its neighbours. */
/* Full-bleed like the image bands above — the design runs this grey the whole
   width of the frame too. Unlike those, the band here is CSS rather than part
   of an export, so the mockups inside have to be held back deliberately: the
   max() floors the side padding at the design's own value and then, once the
   viewport passes the 1600px grid, grows it to keep the three of them on that
   grid instead of spreading to the edges with the grey. */
.catwalk-features {
  width: 100vw;
  margin-inline: calc(50% - 50vw);
  background: #f2f2f2;
  display: grid;
  grid-template-columns: 1007fr 1185fr 1111fr;
  gap: clamp(1rem, 2.08vw, 2.25rem);
  align-items: start;
  padding: clamp(2rem, 5.92vw, 6.375rem) max(clamp(1.25rem, 3.7vw, 4rem), calc(50vw - 50rem))
    clamp(1rem, 1.33vw, 1.5rem);
}

.catwalk-feature {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(0.75rem, 1.25vw, 1.35rem);
}

/* The exports carry their own drop shadow and transparent surround, so they
   sit on the grey field without a border or wrapper of their own. */
.catwalk-feature-media {
  width: 100%;
  height: auto;
  display: block;
}

.catwalk-feature-caption {
  font-weight: 500;
  font-size: clamp(0.875rem, 1.04vw, 1.125rem);
  line-height: 1.3;
  text-align: center;
}

/* Full-bleed WGSN Stream collage. The artwork carries its own headroom above
   the phone — 90 of the export's 1920, so 4.69vw once it spans the viewport —
   which is what the overlaid nav sits in. The padding only tops that up to a
   96px floor, so it is a few pixels at the design's width and grows as the
   band gets shorter; the two together mean there is never less than 96px of
   clear plum above the artwork. #453033 is the export's own field colour, so
   the padding and the image read as one. */
.stream-hero {
  background: #453033;
  padding: max(0rem, 6rem - 4.69vw) 0 0;
}

.stream-hero-figure {
  width: 100%;
  height: auto;
  display: block;
}

/* Two-up image row — 24px gutter on the design's 1728 frame. */
.stream-pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(0.75rem, 1.39vw, 1.5rem);
  align-items: start;
}

/* The closing pair of exports share a height but not a width — proportional
   columns keep them rendering level instead of one panel standing taller. */
.stream-pair-uneven {
  grid-template-columns: 1528fr 1624fr;
}

/* Fashion Buying Tools ---------------------------------------------------- */

/* Green hero band — the video hero's box (.case-hero-media) with the collage's
   own colour and proportions. The nav sits directly on the artwork, with no
   headroom strip beneath it, so the band is exactly the 1728:900 the design
   draws and never taller than the 900px it draws it at. Past 1728 the image
   covers and crops instead of the band growing with the viewport. */
.buying-hero {
  background: #3e6b3d;
  height: min(52.08vw, 56.25rem);
}

/* Black nav, like the work landing page and unlike every other case study.
   The nav sits straight on the hero here, and both ends of that artwork are
   white product UI — the logo over the chart card, the links over the
   assortment table — so the shared white glyphs would disappear into it. Black
   also holds up once the nav docks over the white content below. */
.buying-page .nav {
  color: #000000;
}

/* Full-bleed grey field for a screen export. Each export already carries its
   own grey surround; the band repeats that colour underneath so a sub-pixel
   rounding gap at an edge shows grey rather than the black page. #f2f2f2 is
   sampled from the exports — the research deck was exported on the slightly
   lighter #f5f5f5 the slide grid sits on. */
.buying-band {
  background: #f2f2f2;
}

.buying-band-research {
  background: #f5f5f5;
}

/* A band that opens the unlocked content instead of a copy column. The teaser
   above it stops with no bottom padding of its own, so this supplies the same
   40px .case-content-gated gives — from outside the band, since the band's own
   field is grey and the seam needs to read as page white. */
.buying-band-lead {
  background: #ffffff;
  padding-top: 2.5rem;
}

/* Bullets that each carry a paragraph of their own, rather than running on as
   consecutive lines — 28px between them at the design's 21px type, so the
   space scales with the type rather than staying a flat value. Shared with the
   Trend Forecast Database, whose Trend Profile bullets are set the same way. */
.buying-list li + li,
.trend-list li + li {
  margin-top: 1.35em;
}

/* A band with its caption on the page below it rather than on the grey field.
   The white has to be painted here — case-study pages sit on black, so the
   strip the caption falls in would otherwise be black on black. The gap and
   the padding are the 26px and 68px of white the design leaves above and
   below the caption. Shared with the Trend Forecast Database, which captions
   its filter band the same way. */
.buying-figure,
.trend-figure {
  background: #ffffff;
  gap: 1.625rem;
  padding-bottom: 4.25rem;
}

/* Seams where one block runs into a block of copy — two .case-content sections
   back to back, or a captioned figure meeting one. Each block's padding is
   sized to stand alone, so at a seam the two stack: 120px and 128px of nothing,
   which reads as a hole rather than a break. Each side gives up half, landing
   them at 60px and 64px. */
.case-content:has(+ .case-content) {
  padding-bottom: 1.875rem;
}

.buying-figure:has(+ .case-content),
.trend-figure:has(+ .case-content) {
  padding-bottom: 2.125rem;
}

.case-content + .case-content,
.buying-figure + .case-content,
.trend-figure + .case-content {
  padding-top: 1.875rem;
}

/* TikTok Dashboard -------------------------------------------------------- */

/* Indigo hero band. The nav sits in the headroom above the mockups — 100px at
   the design's 1728 frame — and the artwork carries the same indigo, so the
   padding and the image read as one field rather than two. The mockups are cut
   off by the foot of the band, which is how the design draws them. */
.tiktok-hero {
  background: #5c4de3;
  padding: clamp(4.5rem, 5.79vw, 6.25rem) 0 0;
}

.tiktok-hero-figure {
  width: 100%;
  height: auto;
  display: block;
}

/* Full-bleed band. Each export carries its own field — white around the device
   mockups, lilac behind the trend detail screens — so the band only repaints
   that field underneath, where a sub-pixel rounding gap at an edge would
   otherwise show the black page through. No spacing of its own: every band but
   the last sits between two blocks of copy, and .case-content already pads to
   60px, which is the air the band wants on each side. */
.tiktok-band {
  background: #ffffff;
}

/* A single device mockup, lined up with the body copy rather than run full
   bleed. The export is cropped to the frame — the design's own white margins
   around it were a fixed share of its 1728 frame, so once the band spanned the
   viewport the frame sat further in than the copy, and further in the wider the
   screen got. Giving it the page's own 1600px column and gutter instead puts
   the frame's outer edge on the same line as the paragraphs above and below.
   Vertical space comes from the .case-content either side, so the band adds
   none of its own. */
.tiktok-frame {
  background: #ffffff;
  display: flex;
  justify-content: center;
  padding: 0 var(--page-gutter);
}

.tiktok-frame-figure {
  width: 100%;
  max-width: 100rem;
  height: auto;
  display: block;
}

/* Event apps -------------------------------------------------------------- */

/* The research grid needs no rule of its own — the export carries its own
   #f5f5f5 field and even margin, so a plain .case-image puts that grey on the
   content column's edges, level with the copy above and below it.

   The four editions do need one: their export stops at the phones, and the
   strip beneath it carries that same grey on down behind the labels. */
.events-editions {
  background: #f5f5f5;
}

.events-editions-media {
  width: 100%;
  height: auto;
  display: block;
}

/* Edition names — baked into the export until now, live text here. The row has
   to stay centred under the four phones however wide the column renders, so
   every measurement is a share of the design's 1728 frame rather than a fixed
   value: the row spans that frame's middle 1488, and the gap is 100 of those
   1488, which leaves the four 297 columns the phones are centred on. (The width
   and margins resolve against the column; the gap, against the row itself.) */
.events-editions-labels {
  width: 86.1111%;
  margin: 0.5787% auto 3.0671%;
  display: grid;
  /* minmax(0, …) rather than a bare 1fr: a bare fr floors the track at the
     item's min-content, so once the labels stop wrapping the longest of them
     would widen its own column and shunt the other three off their phones. */
  grid-template-columns: repeat(4, minmax(0, 1fr));
  column-gap: 6.7204%;
  list-style: none;
  font-weight: 400;
  font-size: clamp(0.6875rem, 1.22vw, 1.3125rem);
  line-height: 1.43;
  text-align: center;
  /* Once the phones are down to thumbnails their columns are narrower than
     "Europe 2019" sets, and the label broke over two lines. It stays on one and
     runs into the gutter instead — each label is centred on its own phone, so
     what it spills is the empty space between them. */
  white-space: nowrap;
}

/* Trend Forecast Database ------------------------------------------------- */

/* Black image band. The design gives it no headroom: the images run off the
   top edge of the frame and the nav is set over them, which is how it sits
   here — the band is flush and the artwork carries on up behind the nav.

   What the design does not survive is this site's own nav. Its links read
   because of where they fall; ours say About, Work, Photography, Pottery —
   different words at different x positions — and measured against the artwork
   at 1440, four of the five were below WCAG AA for white text: Work 2.65:1,
   Pottery 3.36:1, and About and Photography crossing the white hat at 1.00:1
   and 1.07:1. So the scrim below, rather than headroom, is what makes them
   readable: the composition stays as drawn and the top of the artwork is
   darkened instead.

   The stops are set by measurement, not by eye: composited against the real
   artwork under each link's own box, 0.62 fading from 45% left About at
   4.30:1, a shade under AA, because it sits where the hat image goes white.
   Holding 0.65 down to 50% before the fade brings the worst link to 6.04:1 and
   the rest to between 6.7 and 12.2, while still reading as a slightly deeper
   top edge rather than a visible band. Kept under the nav's own z-index: 20 so
   it darkens the artwork, never the links. */
.trend-hero {
  background: #000000;
  position: relative;
}

/* Tall enough that the nav sits inside the held part of the gradient at every
   width, and that the fade has room to finish before the artwork proper. */
.trend-hero::after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: clamp(8rem, 10vw, 11rem);
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0.65) 0%,
    rgba(0, 0, 0, 0.6) 50%,
    rgba(0, 0, 0, 0) 100%
  );
}

.trend-hero-figure {
  width: 100%;
  height: auto;
  display: block;
}

/* Full-bleed band, same as .tiktok-band: each export carries its own grey
   field and the mockup's inset within it, so the band only repaints that field
   underneath, where a sub-pixel rounding gap at an edge would otherwise show
   the black page through. No spacing of its own — the bands either run
   straight into one another, the way the design stacks the three filter,
   leaderboard and profile exports, or sit against a .case-content that already
   pads to the 60px of air they want. */
.trend-band {
  background: #ffffff;
}

/* Roadblock gate --------------------------------------------------------- */

/* The white card above the gate stops dead rather than padding out, so the
   fade runs straight into the gate below it. */
.case-content-teaser {
  padding-bottom: 0;
}

/* First block of the unlocked content. Once the gate is gone this meets the
   teaser's zero bottom padding, giving the same 40px between the teaser
   paragraph and "Core Features" as the design puts between every other pair
   of blocks. */
.case-content-gated {
  padding-top: 2.5rem;
}

/* The last block of copy shown before the gate. Only holds the reading measure
   — the grey panel below is what signals the roadblock, so the text is left
   fully legible rather than faded out. */
.gate-teaser {
  width: 100%;
  max-width: 47.5rem;
}

/* The same light grey field the screenshot bands use. On white the panel had
   nothing to separate it from the copy fading out above, so it read as part of
   the article — the grey makes it a distinct block. */
.gate {
  background: #f5f5f5;
  color: #000000;
  padding: clamp(2rem, 3.5vw, 3.125rem) var(--page-gutter);
}

/* Centred rather than left-aligned on the content grid: the gate is an
   interruption, not another block of the case study, so it reads as its own
   thing. The prose still keeps a reading measure. */
.gate-inner {
  width: 100%;
  max-width: 100rem;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 1rem;
}

.gate-title {
  max-width: 47.5rem;
  font-weight: 500;
  font-size: clamp(1.25rem, 1.9vw, 1.75rem);
  line-height: 1.3;
}

.gate-body {
  max-width: 37.5rem;
  color: rgba(0, 0, 0, 0.6);
  font-weight: 300;
  font-size: clamp(1rem, 1.7vw, 1.3125rem);
  line-height: 1.524;
}

.gate-body a {
  text-decoration: underline;
  text-decoration-color: currentColor;
}

/* Needs a width of its own: as a shrink-to-fit child of .gate-inner the row
   below would size itself from the input's intrinsic width and then wrap the
   button onto its own line once the input took its real 20rem. */
.gate-form {
  width: 100%;
  max-width: 37.5rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.75rem;
  margin-top: 1rem;
}

/* The field's own placeholder carries the label visually, so the real one is
   taken out of sight but left in the DOM for screen readers. */
.gate-label {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Field and button share one line. The explicit width matters: centred in a
   flex column the row would otherwise shrink to fit, sizing itself from the
   input's intrinsic width and then wrapping once the input took its real
   20rem. */
.gate-row {
  width: 100%;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.75rem;
}

/* Inherits the page typeface rather than the UA's form font, and sits as an
   outlined field on the white so it reads as part of the card. */
.gate-input {
  width: min(20rem, 100%);
  background: transparent;
  color: #000000;
  font-family: inherit;
  font-weight: 300;
  font-size: 1.0625rem;
  line-height: 1.5;
  border: 1px solid rgba(0, 0, 0, 0.4);
  border-radius: 4px;
  padding: 0.75rem 1rem;
}

.gate-input::placeholder {
  color: rgba(0, 0, 0, 0.5);
  opacity: 1;
}

/* One line on focus, not two: the offset outline sat outside the border and
   read as a double ring. The border itself does the work instead, thickening
   to 2px and going solid black — the padding gives back the pixel it takes so
   the field doesn't change size as you tab into it. */
.gate-input:focus-visible {
  outline: none;
  border: 2px solid #000000;
  padding: calc(0.75rem - 1px) calc(1rem - 1px);
}

.gate-button {
  background: #000000;
  color: #ffffff;
  font-family: inherit;
  font-weight: 500;
  font-size: 1.0625rem;
  line-height: 1.5;
  border: none;
  border-radius: 4px;
  padding: 0.75rem 1.5rem;
  cursor: pointer;
}

.gate-button:hover {
  opacity: 0.9;
}

/* Always in the DOM so the live region can announce into it — it just has
   nothing to say until a password is refused. */
.gate-error {
  color: #b3261e;
  font-size: clamp(0.875rem, 1.25vw, 1.125rem);
  line-height: 1.44;
  min-height: 1.44em;
}

/* Credit line closing the case study. Sits at 60% black so it reads as
   metadata, but takes the same size and leading as .case-section-body — the
   design sets the credit in the body size, not a step down from it. */
.case-role {
  color: rgba(0, 0, 0, 0.6);
  font-size: clamp(1rem, 1.7vw, 1.3125rem);
  line-height: 1.524;
}

/* Full-contrast variant — the credit sits in the same black as the body copy
   rather than reading as faint metadata. */
.case-role-strong {
  color: #000000;
}

.case-role-label {
  font-weight: 600;
}

.case-role-value {
  font-weight: 300;
}

@media (max-width: 860px) {
  .case-content {
    padding: 40px 20px;
  }

  /* The halved seams again, against the smaller padding here. Both selectors
     out-specify the shorthand above, so they need restating rather than
     inheriting the rem values set for the desktop sizes. */
  .case-content:has(+ .case-content) {
    padding-bottom: 20px;
  }

  .case-content + .case-content,
  .buying-figure + .case-content {
    padding-top: 20px;
  }

  .case-content-inner {
    gap: 40px;
  }

  .case-media-row {
    gap: 2px;
  }

  /* No room for two columns. `display: contents` dissolves the copy wrapper so
     its sections become siblings of the video, which `order` then lifts back
     up to sit directly under the section it illustrates. */
  .case-split {
    display: flex;
    flex-direction: column;
    gap: 40px;
  }

  .case-split-copy {
    display: contents;
  }

  .case-split-copy > .case-section {
    order: 2;
  }

  .case-split-copy > .case-section:first-child {
    order: 0;
  }

  .case-split-media {
    order: 1;
  }

  /* At the design's 81% the screenshots would come out a few hundred pixels
     wide and illegible — give them the full column minus the page gutters. */
  .catwalk-band .browser-frame {
    width: calc(100% - 40px);
  }

  /* Same reasoning for the hero's 10% inset: on a phone that inset is what
     stands between the screenshot and being unreadable, and the margin it
     would leave reads as a mistake rather than as breathing room. The hero
     already sits on the page gutters, so 100% is that full column. */
  .frame-hero .browser-frame {
    width: 100%;
  }

  .pulse-bands {
    gap: 20px;
  }

  /* The vw term above is tuned to the desktop nav; this one is shorter. */
  .tiktok-hero {
    padding-top: 4.5rem;
  }

  .gate {
    padding: 32px 20px;
  }

  .gate-title {
    font-size: 20px;
  }

  .gate-body {
    font-size: 16px;
    line-height: 1.5;
  }

  /* Too narrow for the field and the button to share a line. */
  .gate-input,
  .gate-button {
    width: 100%;
  }

  /* Holding a phone still for six screens while a 1600px page pans past in a
     thumbnail is a good way to make a page feel broken. Below 860px the whole
     mechanism is dropped: the stage un-sticks and the prototype becomes an
     ordinary tall image in the flow. prototype-scroll.js sees the un-stuck
     stage and stays out of it. */
  .proto-track {
    height: auto;
  }

  .proto-stage {
    position: static;
    height: auto;
    padding: 20px 0;
  }

  .proto-frame {
    height: auto;
  }

  .proto-window {
    flex: none;
  }

  .proto-page {
    position: static;
    aspect-ratio: auto;
  }

  .proto-page-img {
    height: auto;
  }

  /* With no pan available, the overlay would just sit there as a duplicate of
     the carousel beneath it — the page's own clipped version stands in. */
  .proto-strip-window {
    display: none;
  }

  /* No room for three columns of UI detail — one mockup per row. */
  .catwalk-features {
    grid-template-columns: 1fr;
    gap: 32px;
    padding: 32px 20px;
  }

  .catwalk-feature-caption {
    font-size: 16px;
  }

  /* At full width the 16:9 collage collapses to a ~210px strip. Give it a fixed
     height instead and let it crop in from the sides, which keeps the phone and
     the cards flanking it at a legible size. The padding above is a flat value
     here — the vw term is tuned to the desktop nav, and this one is shorter. */
  .stream-hero {
    padding-top: 5rem;
  }

  .stream-hero-figure {
    height: 21.875rem;
    object-fit: cover;
  }

  /* No room for two columns of mockup detail — one panel per row. */
  .stream-pair,
  .stream-pair-uneven {
    grid-template-columns: 1fr;
    gap: 20px;
  }

  /* No room for the credit to sit beside the standfirst — it drops below the
     copy it was flanking. */
  .case-intro-split {
    grid-template-columns: 1fr;
    gap: 40px;
  }

  .case-intro-main {
    gap: 40px;
  }

  /* Side by side, the diagram's labels get illegibly small — stack instead. */
  .case-anatomy {
    flex-direction: column;
    align-items: flex-start;
    gap: 20px;
    padding: 20px;
  }

  .case-anatomy-title {
    font-size: 20px;
  }

  .case-anatomy-figure {
    width: 100%;
  }

  .case-title {
    font-size: 28px;
  }

  .case-tagline {
    font-size: 20px;
  }

  .case-description,
  .case-section-title,
  .case-section-body,
  .case-list,
  .case-role {
    font-size: 16px;
    line-height: 1.5;
  }
}
