/* ==========================================================================
   THE DEPARTMENT — the hero.

   The previous hero was one room photograph with a flat grey rectangle of
   text on it, and one background plane translating on scroll. Scroll moved;
   the scene did not. "Parallax" was true of the scroll position and false
   of the room.

   This file rebuilds it as a room with real depth. Five planes travel at
   five different rates, and — the part the old one was missing — they are
   separated in Z as well as in Y, so the eye reads distance instead of five
   copies of one texture:

     plane 1  the room                     slowest, furthest back
     plane 2  the light through the glass  mid-ground, the slowest drift
     plane 3  the campaign artefacts       near desks, tilted
     plane 4  the copy                     closest, fastest
     plane 5  sheen and grain              static, over everything

   Everything here is one cool slate hue family, per The One Hue Rule. The
   only departure from the rest of the page is depth: nothing else on the
   site is this dimensional, because nothing else on the site is the first
   impression.

   Rates and easings live in department.js as a single table so the motion
   can be reasoned about as one composition rather than tuned in six places.
   ========================================================================== */

:root {
  /* The hero is now a TALL SECTION with a pinned viewport inside it. The
     room does not scroll away — the frame stays put and the image slides
     within it — so the section has to be taller than one screen to give that
     slide anywhere to happen. 200vh: one screen of room, one screen of pass.
     The runway past the last pinned screen is the release.

     `--sc-hero-travel` is the single source of truth for how far the room
     slides, and BOTH the room's own height and department.js read it. If the
     image is not exactly one travel-unit taller than the frame, one end of
     the pass runs out of photograph and shows the section's flat background
     at the other — which is the single most visible way this effect can
     break, and it is why the number lives in one place.

     110px is deliberately modest. The previous build travelled 470px with the
     text moving against it and made a viewer ill; the amplitude here is a
     quarter of that and the text does not move at all. */
  /* THE PASS, re-paced.

     This was 200vh, and half of it was dead time. Measured: the room pinned
     from scroll 380 to 1419 — about 1040px, a full viewport — during which
     the only motion was 110px of parallax, and then took another ~970px to
     scroll off. Roughly half the section was the room standing still.

     At 155vh the pinned hold is 480px and the exit is shorter, so the
     section arrives with intent and leaves without a crawl. The room still
     gets a full 110px of travel inside that hold, which is the amplitude that
     reads as "the camera settled" rather than "the floor is sliding". */
  --sc-hero-runway: 155vh;
  --sc-hero-travel: 110px;
  --sc-hero-pad: clamp(2.5rem, 6vw, 5.5rem);
}

/* --------------------------------------------------------------------------
   THE WORDMARK CARD IS NOT HERE ANYMORE.

   The white marketing panel — the random video, the "Story Companion /
   Marketing Department" lockup and the walking stickman — used to be a
   full-width band above this room, then briefly a card sitting on this desk
   between the copy and the plates. Neither is right any more, and every rule
   for it is gone from this file along with the `--sc-panel-air-*` and
   `--sc-video-rise` tokens it needed.

   It now lives in two files of its own:

     wordmark-card.html                      the component, viewable alone
     assets/css/wordmark-card.compiled.css   its styles

   Its styles are NOT global like the ones in this directory. Every selector
   is scoped under `.wmc` and every custom property is declared on `.wmc`
   rather than on `:root`, so the markup can be dropped into another page
   without a `#video1 { position: absolute; left: 48% }` rule reaching out
   and moving that page's own video. The `#video1`, `#myVideo` and
   `#foton`-equivalent ids are preserved inside that scope because the site's
   `logo.js` looks them up by id to pick and drive the random video.

   Everything below this point is the department hero. The only change to it
   in that move was the grid going back to two columns.
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   1. The stage.

   TWO nested elements, and the distinction is the whole effect:

     .sc-hero        the tall runway — 200vh, scrolls normally
     .sc-hero__frame the viewport — position: sticky, 100vh, never scrolls

   The frame is what the eye reads as "the window". It is pinned for the
   whole pass, so the horizon never moves and there is nothing for the
   visual system to re-solve. Inside it, `.sc-hero__room` is TALLER than the
   frame and translates up as you scroll — that is the parallax, and it is a
   single vertical axis with a small amplitude.

   `overflow: clip` on the section, not `hidden`. `hidden` on an ancestor of
   a `position: sticky` child makes the sticky resolve against THAT ancestor
   instead of the viewport, which silently breaks the pin — the frame would
   scroll away normally and there would be no effect at all, with no error
   anywhere to explain why.
   -------------------------------------------------------------------------- */

.sc-hero {
  position: relative;
  isolation: isolate;
  overflow: clip;
  min-height: var(--sc-hero-runway);
  background: #0e181c; /* the room's darkest value; never pure black */
  color: #fff;
}

/* The pinned viewport. `top: 0` and `height: 100vh` are the whole pin. */
.sc-hero__frame {
  position: sticky;
  top: 0;
  height: 100vh;
  overflow: clip;
  display: flex;
  align-items: center;
  /* The padding lives on the FRAME, not on `.sc-hero`. On the section it
     would add 110px of empty runway above and below the pinned frame, so
     the sticky range would be 200vh of mostly blank space and the effect
     would start 110px late. Here it insets the copy inside the visible
     screen, which is what it is for. */
  padding-block: var(--sc-hero-pad);
  /* The frame establishes the containing block for the absolutely
     positioned planes below it. Without `position` on this element they
     would resolve against `.sc-hero` — the 200vh runway — and the room
     would be 200vh tall with the copy stranded in the middle of it. */
  isolation: isolate;
}

/* The scene wrapper: room, light, foreground and vignette together.

   It exists so the four planes are positioned against one box rather than
   four times against the frame, and so the vignette has a single element to
   sit on. `z-index: 0` plus the frame's `isolation: isolate` keeps the whole
   scene behind the copy without a z-index on every plane. */
.sc-hero__scene {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}

/* --------------------------------------------------------------------------
   Plane 1 — the room.

   TALLER THAN THE FRAME ON PURPOSE. This is what makes it option 3 rather
   than option D (stuck). A `cover` image sized to the frame would have
   nothing to reveal: the whole photograph is already visible, so sliding it
   up only ever shows empty space above it.

   Instead the image is sized to cover a box `100% + travel` tall, so there is
   always `travel` of image above the current crop that the slide can bring
   into view. The travel is a CSS variable so JS and CSS cannot disagree
   about how much there is — if they did, the room would run out of image at
   one end of the pass and show the section's flat background at the other.

   `--sc-hero-travel` is read by department.js. It is 110px on desktop and
   much smaller on a phone, and both are inside the range where the eye
   reads "the camera settled" rather than "the floor is sliding". */
.sc-hero__room {
  position: absolute;
  left: 0;
  right: 0;
  /* The overscan belongs at the BOTTOM, and getting this backwards is what
     put a black bar under the room.

     The layer travels UP by `--sc-hero-travel`, so the frame edge it uncovers
     is the bottom one. The extra height therefore has to hang BELOW the frame
     (`bottom: -travel`, `top: 0`) so the slide pulls fresh image up into the
     space it vacates.

     The first version had it the other way round — `top: -travel`, `bottom: 0`
     — which stacks the surplus above the frame. That is correct for a layer
     that moves DOWN and wrong for one that moves UP: by the end of the pass
     the whole layer had risen by exactly its own surplus, leaving its bottom
     edge 110px short of the frame's bottom edge and 110px of bare section
     background showing as a hard black band. The travel and the overscan have
     to be on OPPOSITE sides of the frame, always. */
  top: 0;
  bottom: calc(var(--sc-hero-travel) * -1);
  background-image: url("../img/hero-room.png");
  /* CRITICAL, and it was missing for three passes. `background-size` alone
     does not suppress tiling — the default `background-repeat` is `repeat`,
     so a percentage- or length-sized plate larger than its box drew itself
     once per box-height down the page. On a 1588px-tall phone frame that is
     the boardroom appearing three times. `cover` picks the scale and only
     `no-repeat` guarantees one copy. */
  background-repeat: no-repeat;
  /* Sized by HEIGHT, never by width. This is the fix for the black band
     under the room, and the reason is arithmetic rather than subtle.

     `background-size: <width> auto` scales the plate to that WIDTH and
     derives the height from the source aspect ratio. The plate is 16:9, so
     on a 1770px-wide frame `calc(100% + 110px)` = 1880px wide asks for
     1880 / 1.78 = 1057px tall — while the layer's box is the frame height
     plus the travel, about 1385px. So 328px of the box had no image in it
     at all, `no-repeat` dutifully did not repeat, and the hero showed a
     328px black bar across the bottom. On a shorter or narrower frame the
     arithmetic shifts, so the bar appeared at some viewport sizes and not
     others, which is why it looked intermittent.

     `auto <height>` inverts it: the plate is scaled to the layer's height
     (plus one travel-unit of overscan) and its width follows. The height is
     now always exactly filled by construction, and because the plate is
     16:9 the width comes out LARGER than the frame — which is what we want.
     It crops the sides slightly rather than leaving a gap, and the frame is
     full-bleed anyway, so horizontal surplus is invisible.

     The `+ var(--sc-hero-travel)` is the overscan: one extra travel-unit of
     image below the frame, which is what the upward slide pulls into view.
     Height-based sizing makes this exact — the surplus is precisely the
     distance the room travels, so it can never run out at the bottom and can
     never overshoot into a visible seam at the top. */
  background-size: auto calc(100% + var(--sc-hero-travel));
  /* Biased toward the upper-middle of the plate. The slide reveals the top
     of the photograph over the course of the pass, so the ceiling and window
     — the parts worth revealing — are kept well inside the crop, and the
     empty floor at the bottom of the plate is what gets cropped out. */
  background-position: center 38%;
  will-change: transform;
  transform: translate3d(0, 0, 0);
  /* The plate is lit correctly and the section is not, so the exposure is
     lifted here rather than in the source image. */
  filter: brightness(1.34) contrast(0.97) saturate(0.92);
}

/* Plane 2 — the light in the photograph already does the work; this layer
   exists so the brightness itself can travel separately from the geometry,
   which is what reads as "the light is moving through the room" rather than
   "the room is sliding". */

.sc-hero__light {
  position: absolute;
  inset: 0;
  will-change: transform, opacity;
  background:
    radial-gradient(58% 42% at 22% 34%, rgba(214, 233, 240, 0.30) 0%, rgba(214, 233, 240, 0) 70%),
    radial-gradient(44% 30% at 47% 52%, rgba(196, 222, 233, 0.20) 0%, rgba(196, 222, 233, 0) 72%),
    radial-gradient(38% 46% at 68% 30%, rgba(176, 206, 220, 0.16) 0%, rgba(176, 206, 220, 0) 74%);
  mix-blend-mode: screen;
  opacity: 0.8;
}

/* Plane 2b. The near foreground, and the one that makes the rest read.

   Parallax needs something in FRONT of the thing it is behind. With only
   background layers at different rates, the eye has a slow slide and a
   slightly-less-slow slide and no anchor — which is why the first version
   looked static no matter how the rates were tuned. This plane is a dark,
   defocused door frame and chair back shot at 16:9, with an empty centre.

   CRITICAL: it is masked to the frame EDGES, not laid over the whole plate.
   Unmasked it covers 100% of the hero in near-black, which is what buried
   the room — the plate is a silhouette, and a silhouette only reads as a
   foreground if there is lit scene visible either side of it. The mask keeps
   the door frame on the left and the chair on the bottom-right and clears
   the entire middle, which is exactly where the copy and the desk sit. */
.sc-hero__fore {
  position: absolute;
  inset: 0;
  will-change: transform;
  background-image: url("../img/hero-foreground.png");
  background-size: cover;
  background-position: center;
  /* Masked to a soft-edged border band. `-webkit-mask-image` is not optional
     here: this is a WebKit/Blink engine and an unprefixed `mask-image` is
     ignored in older Chromium, which would silently show the full black
     plate again. */
  -webkit-mask-image: linear-gradient(90deg, #000 0%, #000 20%, rgba(0, 0, 0, 0.35) 31%, rgba(0, 0, 0, 0) 42%),
                      linear-gradient(0deg, #000 0%, #000 14%, rgba(0, 0, 0, 0) 30%);
  -webkit-mask-composite: source-in;
  mask-image: linear-gradient(90deg, #000 0%, #000 20%, rgba(0, 0, 0, 0.35) 31%, rgba(0, 0, 0, 0) 42%),
              linear-gradient(0deg, #000 0%, #000 14%, rgba(0, 0, 0, 0) 30%);
  mask-composite: intersect;
  /* The plate is already near-black; this only takes the last of the
     contrast out so it never silhouettes as a hard shape. */
  opacity: 0.92;
}

/* Depth falloff. The room is a photograph lit from the left; the copy sits
   left, so the left is darkened and the right opened up. This is the step
   that makes the text plate legible without putting a grey box on it.

   The values are the third pass. The first two were 0.90/0.74/0.30/0.10
   plus a 0.72 floor and a 0.58 top, which stacked to roughly 80% black
   across the middle of the frame and buried the room entirely — you could
   read the copy and see nothing else. Legibility of white type on a
   photographic ground comes mostly from the text-shadow on the type itself,
   not from how much of the room is painted over, so the scrims are now
   light enough to darken the copy's half and leave the desk's half open. */
.sc-hero__vignette {
  position: absolute;
  inset: 0;
  z-index: 1; /* over the room and the foreground, under the copy */
  /* Scoped to the FRAME, not the section. It was `.sc-hero` before the
     section became a 200vh runway, and on a runway these gradients are
     stretched over two screens: the copy would sit in the middle of a
     2000px-tall wash, the top scrim would be 1000px above the headline, and
     the legibility device that is supposed to darken the copy's half would
     instead darken the empty runway above it. */
  background:
    linear-gradient(100deg, rgba(9, 16, 20, 0.78) 0%, rgba(9, 16, 20, 0.52) 28%, rgba(9, 16, 20, 0.14) 56%, rgba(9, 16, 20, 0) 100%),
    linear-gradient(0deg, rgba(9, 16, 20, 0.46) 0%, rgba(9, 16, 20, 0) 30%),
    linear-gradient(180deg, rgba(9, 16, 20, 0.30) 0%, rgba(9, 16, 20, 0) 20%);
}

/* Plane 5. A single cool-white sheen at 112deg — the same angle the CTA
   band's light sweep uses, so the page has one light source. The texture
   is a radial vignette tightening toward the edges rather than a hairline
   grid: a repeating stripe pattern over a photograph reads as a printed
   screen door laid over the room, not as material.

   On `.sc-hero__frame`, not `.sc-hero`, for the same reason the vignette
   moved: it is a one-screen effect and the section is now two. */

.sc-hero__frame::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  background:
    linear-gradient(112deg, rgba(255, 255, 255, 0) 34%, rgba(214, 233, 240, 0.13) 47%, rgba(255, 255, 255, 0) 60%),
    radial-gradient(130% 110% at 50% 50%, rgba(255, 255, 255, 0) 62%, rgba(6, 12, 15, 0.22) 100%);
  mix-blend-mode: overlay;
}

/* --------------------------------------------------------------------------
   2. The composition.

   Copy left, desks right, on the page's own container measure. The two
   columns are deliberately unequal — 1fr against 1fr would make the copy a
   wall and the desk a wall, and neither would read as a room.
   -------------------------------------------------------------------------- */

.sc-hero .container {
  position: relative;
  z-index: 2;
}

.sc-hero__grid {
  display: grid;
  /* Copy left, plates right. Two columns, as before the wordmark card was
     tried here, and the reason it went is worth keeping because the
     three-column version looked reasonable in isolation.

     Three objects across a 1300px measure left the wordmark at 390px, which
     is not the width of a wordmark, and it stood between the copy and the
     plates. Those two are the argument and its evidence, and the eye has to
     travel from one to the other; a branded object in the middle of that
     path interrupts the journey instead of reinforcing it. The card is not
     lost: it is a standalone page now, `wordmark-card.html`.

     The columns stay deliberately unequal. 1fr against 1fr would make the
     copy a wall and the desk a wall, and neither would read as a room. */
  grid-template-columns: minmax(0, 1.02fr) minmax(0, 1fr);
  gap: clamp(2rem, 4vw, 4.5rem);
  align-items: center;
}

/* --------------------------------------------------------------------------
   3. Plane 4 — the copy.

   No panel. The old hero put a `rgba(64,94,111,0.75)` rectangle behind the
   text, which flattened the photograph, cut the light in half and put a hard
   horizontal seam across the room. The vignette does that job now, so the
   copy sits directly on the scene.
   -------------------------------------------------------------------------- */

.sc-hero__copy {
  position: relative;
  will-change: transform;
  padding-block: 1rem;
}

.sc-hero__title {
  margin: 0 0 1.15rem;
  color: #fff;
  font-size: clamp(2.35rem, 5.2vw, 4rem);
  font-weight: 800;
  /* 1.06 rather than a display-tight 1.04. Montserrat 800 is a blunt
     geometric with a tall ascender and a deep bowl, and at 64px the tighter
     setting closed the counters of the "g" in "Fingertips" until they read
     as one mass. 1.06 keeps the block dense without touching them. */
  line-height: 1.06;
  letter-spacing: -0.028em;
  text-wrap: balance;
  /* The room is cool, the type is white on it, and the smallest weight that
     survives a busy photographic ground at 64px is 800. Montserrat is
     already on the page and already the brand's display voice — the wordmark
     is the only other place it is used — so the hero is where that voice
     earns the rest of its licence. This is display type doing display work,
     not the logo lockup borrowing a page. */
  font-family: Montserrat, "Helvetica Neue", Arial, sans-serif;
  text-shadow: 0 1px 2px rgba(6, 12, 15, 0.4), 0 2px 30px rgba(6, 12, 15, 0.55);
}

.sc-hero__lead {
  margin: 0 0 1.9rem;
  max-width: 46ch;
  color: rgba(233, 241, 244, 0.94);
  font-size: clamp(1rem, 1.15vw, 1.125rem);
  font-weight: 500;
  line-height: 1.62;
  text-shadow: 0 1px 14px rgba(6, 12, 15, 0.6);
}

.sc-hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-bottom: 1.5rem;
}

/* Both CTAs are on photographic ground, so neither takes the page's
   transparent-outline treatment — that has no contrast here. The solid
   carries the primary, the ghost is a light hairline that fills on hover.
   44px minimum, page-wide rule, unchanged. */

.sc-hero__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 46px;
  padding: 0.7rem 1.5rem;
  border-radius: 8px;
  font-size: 0.95rem;
  font-weight: 700;
  text-decoration: none;
  transition: transform 180ms cubic-bezier(0.16, 1, 0.3, 1),
    background-color 180ms ease, border-color 180ms ease, color 180ms ease;
}

.sc-hero__btn--solid {
  background: #f4f8f9;
  color: #16201f;
  border: 1px solid #f4f8f9;
}

.sc-hero__btn--solid:hover {
  background: #fff;
  transform: translateY(-2px);
}

.sc-hero__btn--ghost {
  background: rgba(255, 255, 255, 0.06);
  color: #fff;
  /* Cool Mist at 55% — the documented ground, taken to alpha, instead of
     an unrelated pale-cyan literal. */
  border: 1px solid rgba(246, 248, 248, 0.55);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}

.sc-hero__btn--ghost:hover {
  background: #f4f8f9;
  border-color: #f4f8f9;
  color: #16201f;
  transform: translateY(-2px);
}

/* The page-wide focus ring, unchanged, plus the halo the CTA band's buttons
   need on a gradient. A ring alone has no contrast on the photograph. */

.sc-hero__btn:focus-visible {
  outline: 2px solid var(--bs-primary);
  outline-offset: 2px;
  border-radius: 3px;
  box-shadow: 0 0 0 5px rgba(143, 176, 192, 0.45);
}

/* The dogfooding line. It is the smallest text on the page and it sits on
   the busiest ground, so it gets more relative contrast than its size would
   otherwise earn — never a grey the room is already.

   On a phone the room behind it is the lit middle of the photograph, which
   is the lightest part of the frame, so the line takes its own scrim: a
   local dark ground behind the text only, feathered on all sides, rather
   than darkening the whole lower third of the section to serve four words
   of caption. */

.sc-hero__note {
  position: relative;
  display: block;
  max-width: 52ch;
  padding: 0.5rem 0.65rem;
  border-radius: 8px;
  color: rgba(224, 238, 243, 0.95);
  font-size: 0.75rem;
  line-height: 1.5;
  text-shadow: 0 1px 2px rgba(6, 12, 15, 0.9), 0 1px 12px rgba(6, 12, 15, 0.8);
}

@media (min-width: 992px) {
  .sc-hero__note {
    padding: 0;
    background: none;
    color: rgba(206, 224, 231, 0.88);
    text-shadow: 0 1px 10px rgba(6, 12, 15, 0.7);
  }
}

/* --------------------------------------------------------------------------
   4. Plane 3 — the desks.

   Three plates, each on its own Z offset, each tilted by a different amount
   and rotated by a different sign, so the group reads as objects lying on a
   surface rather than as a carousel. The tilt is small on purpose: past
   about 3 degrees these stop being documents and start being stickers.

   Perspective is on the parent, not on the plates. Putting it on the plates
   themselves would give each one its own vanishing point and the group would
   come apart.
   -------------------------------------------------------------------------- */

.sc-hero__desk {
  position: relative;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.9rem;
  perspective: 1400px;
  perspective-origin: 62% 46%;
  will-change: transform;
}

.sc-plate {
  position: relative;
  margin: 0;
  transform-style: preserve-3d;
  /* The staggered start: each plate sits lower and further back than the one
     before it, so the eye enters at the front-left plate and travels in. */
  transform: translate3d(0, var(--plate-drop, 0px), -40px) rotateY(-7deg) rotateX(2deg);
  transition: transform 750ms ease-in-out;
  transition-delay: 0s;
}

.sc-plate--a { --plate-drop: 34px; z-index: 3; }
.sc-plate--b { --plate-drop: 0px;  z-index: 4; }
.sc-plate--c { --plate-drop: 34px; z-index: 3; }

.sc-plate--a { transform: translate3d(0, 34px, -30px) rotateY(-8deg) rotateX(2.5deg); }
.sc-plate--c { transform: translate3d(0, 34px, -30px) rotateY(8deg)  rotateX(2.5deg); }

.sc-hero:hover .sc-plate--a,
.sc-hero__desk:hover .sc-plate--a { transform: translate3d(0, 22px, 10px) rotateY(-5deg) rotateX(1.5deg); }
.sc-hero:hover .sc-plate--b,
.sc-hero__desk:hover .sc-plate--b { transform: translate3d(0, -12px, 40px) rotateY(0deg)  rotateX(0deg); }
.sc-hero:hover .sc-plate--c,
.sc-hero__desk:hover .sc-plate--c { transform: translate3d(0, 22px, 10px) rotateY(5deg)  rotateX(1.5deg); }

.sc-plate__frame {
  position: relative;
  overflow: hidden;
  aspect-ratio: 3 / 4;
  border-radius: 10px;
  background: #dfe6e7; /* cool ground so a slow image never flashes white */
  /* Cool Mist at 34% — the documented ground as alpha, replacing the
     unrelated pale-cyan literal rgba(214, 233, 240, …). */
  border: 1px solid rgba(246, 248, 248, 0.34);
  /* The one resting shadow on the page, and it is load-bearing: without it
     the plates detach from the wall and the group reads as a collage. It is
     cool-black like every other shadow in the system. */
  box-shadow:
    0 2px 6px rgba(9, 16, 20, 0.42),
    0 18px 40px -18px rgba(9, 16, 20, 0.62);
}

.sc-plate__frame::after {
  /* A raking light across the glass of each plate, from the room's own
     left-side window. Same 112deg as the page sheen.
     Fades in smoothly on mouse over (750ms) and fades out on mouse out (750ms). */
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(
    112deg,
    rgba(255, 255, 255, 0) 36%,
    rgba(233, 246, 250, 0.2) 46%,
    rgba(255, 255, 255, 0.38) 52%,
    rgba(233, 246, 250, 0.2) 58%,
    rgba(255, 255, 255, 0) 68%
  );
  opacity: 0;
  transition: opacity 750ms ease-in-out;
  transition-delay: 0s;
}

.sc-hero__desk:hover .sc-plate__frame::after,
.sc-plate:hover .sc-plate__frame::after {
  opacity: 1;
  transition-delay: 0s;
}

.sc-plate__slides {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  transition: transform 750ms ease-in-out;
}

.sc-plate:hover .sc-plate__slides {
  transform: scale(1.042);
}

.sc-plate__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center top;
  opacity: 0;
  pointer-events: none;
  transform: scale(1.035) translate3d(0, -6px, 0);
  transition: opacity 800ms cubic-bezier(0.16, 1, 0.3, 1),
              transform 800ms cubic-bezier(0.16, 1, 0.3, 1);
  will-change: opacity, transform;
}

.sc-plate__img:first-child,
.sc-plate__img.is-active {
  opacity: 1;
  pointer-events: auto;
  transform: scale(1) translate3d(0, 0, 0);
  z-index: 2;
}

.sc-plate__img.is-leaving {
  opacity: 0;
  pointer-events: none;
  transform: scale(0.975) translate3d(0, 5px, 0);
  z-index: 1;
  transition-duration: 680ms;
}

/* Fallback for direct img if ever present */
.sc-plate__frame > img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center top;
  transition: transform 600ms cubic-bezier(0.16, 1, 0.3, 1);
}

.sc-plate:hover .sc-plate__frame > img {
  transform: scale(1.045);
}

/* Specular raking glint across the glass on image swap */
.sc-plate__glint {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(
    115deg,
    rgba(255, 255, 255, 0) 25%,
    rgba(255, 255, 255, 0.18) 46%,
    rgba(224, 245, 255, 0.35) 50%,
    rgba(255, 255, 255, 0.14) 54%,
    rgba(255, 255, 255, 0) 72%
  );
  opacity: 0;
  transform: translate3d(-130%, 0, 0);
  z-index: 4;
}

.sc-plate.is-glinting .sc-plate__glint {
  animation: scPlateGlint 820ms cubic-bezier(0.16, 1, 0.3, 1) forwards;
}

@keyframes scPlateGlint {
  0% {
    opacity: 0;
    transform: translate3d(-130%, 0, 0);
  }
  25% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    transform: translate3d(130%, 0, 0);
  }
}

.sc-plate__cap {
  display: block;
  margin-top: 0.7rem;
  color: #fff;
  font-size: 0.8125rem;
  font-weight: 700;
  line-height: 1.3;
  letter-spacing: -0.01em;
  /* The plate is tilted, so its caption box is narrower than its column and
     a two-line second line would hang past the plate's edge. The measure is
     bounded here rather than trusted to wrap by luck. 24ch holds
     "Campaign overview" on one line at this size. */
  max-width: 24ch;
  text-shadow: 0 1px 10px rgba(6, 12, 15, 0.8);
}

/* The SUB-LINE only.

   `:not(.sc-plate__name)` is load-bearing. The caption's title is now a real
   element — `.sc-plate__name` — so that it can be given a reserved two-line
   height and land all three subtitles on one baseline. Without the
   exclusion this rule would also match that title and restyle it as 12px
   pale grey medium-weight text, which is the styling of the line BENEATH
   it: the plate names would lose their 700 weight and their white, and the
   captions would read as six identical grey lines with no hierarchy. */
.sc-plate__cap span:not(.sc-plate__name) {
  display: block;
  margin-top: 0.15rem;
  color: rgba(206, 224, 231, 0.88);
  /* 12px, not 11. The second line of a caption names the suite that made
     the artefact, so it is content, not a label — and at 11px it failed the
     11px functional-text floor the moment the parent scaled below 992px,
     because 0.6875rem resolved down with the parent and landed at 10px. */
  font-size: 0.75rem;
  font-weight: 500;
  line-height: 1.35;
  letter-spacing: 0;
  text-transform: none;
}

/* The plate's own name. It inherits the caption's 700 white, so the only
   thing it needs is to be a block — and, below 992px, a reserved height. */
.sc-plate__name {
  display: block;
}

/* --------------------------------------------------------------------------
   Hero Campaign Rotator Controls
   Sleek frosted glass pills with an active countdown progress beam,
   staggered optical glint on poster plates, and clean touch/keyboard controls.
   -------------------------------------------------------------------------- */

.sc-hero__campaigns {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.65rem;
  margin-top: 1.25rem;
  padding-top: 0.95rem;
  border-top: 1px solid rgba(246, 248, 248, 0.12);
  position: relative;
  z-index: 6;
  transform: translateZ(15px);
}

.sc-campaign-pills {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  flex-wrap: nowrap;
}

.sc-campaign-pill {
  position: relative;
  overflow: hidden;
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.32rem 0.65rem;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(246, 248, 248, 0.14);
  color: rgba(215, 228, 233, 0.7);
  font-family: inherit;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  line-height: 1.2;
  cursor: pointer;
  transition: all 260ms cubic-bezier(0.16, 1, 0.3, 1);
  -webkit-appearance: none;
  appearance: none;
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  white-space: nowrap;
}

.sc-campaign-pill:hover {
  background: rgba(255, 255, 255, 0.12);
  border-color: rgba(246, 248, 248, 0.32);
  color: #ffffff;
  transform: translateY(-1px);
}

.sc-campaign-pill:focus-visible {
  outline: 2px solid var(--bs-primary);
  outline-offset: 2px;
}

.sc-campaign-pill.is-active {
  background: rgba(255, 255, 255, 0.16);
  border-color: rgba(246, 248, 248, 0.5);
  color: #ffffff;
  box-shadow: 0 4px 14px rgba(9, 16, 20, 0.4);
  padding: 0.32rem 0.85rem;
}

.sc-campaign-pill__num {
  font-size: 0.72rem;
  opacity: 0.75;
  font-feature-settings: "tnum";
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.04em;
}

.sc-campaign-pill.is-active .sc-campaign-pill__num {
  color: #8fb0c0;
  opacity: 1;
  font-weight: 700;
}

/* Inactive pills hide the name; active and hovered pills expand it */
.sc-campaign-pill__name {
  max-width: 0;
  opacity: 0;
  overflow: hidden;
  white-space: nowrap;
  transition: max-width 300ms cubic-bezier(0.16, 1, 0.3, 1),
              opacity 220ms ease;
  pointer-events: none;
}

.sc-campaign-pill.is-active .sc-campaign-pill__name,
.sc-campaign-pill:hover .sc-campaign-pill__name {
  max-width: 120px;
  opacity: 1;
  margin-left: 0.15rem;
}

.sc-campaign-pill__progress {
  position: absolute;
  bottom: 0;
  left: 0;
  height: 2px;
  width: 0%;
  background: linear-gradient(90deg, #405e6f, #8fb0c0);
  opacity: 0;
  pointer-events: none;
}

.sc-campaign-pill.is-active .sc-campaign-pill__progress {
  opacity: 1;
  animation: scPillProgress var(--sc-cycle-duration, 6000ms) linear forwards;
}

.sc-hero__desk:hover .sc-campaign-pill.is-active .sc-campaign-pill__progress,
.sc-hero__campaigns:hover .sc-campaign-pill.is-active .sc-campaign-pill__progress {
  animation-play-state: paused;
}

@keyframes scPillProgress {
  from { width: 0%; }
  to { width: 100%; }
}

.sc-campaign-nav {
  display: flex;
  align-items: center;
  gap: 0.35rem;
}

.sc-campaign-nav__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(246, 248, 248, 0.14);
  color: rgba(215, 228, 233, 0.75);
  cursor: pointer;
  transition: all 200ms cubic-bezier(0.16, 1, 0.3, 1);
  padding: 0;
  -webkit-appearance: none;
  appearance: none;
}

.sc-campaign-nav__btn:hover {
  background: rgba(255, 255, 255, 0.16);
  border-color: rgba(246, 248, 248, 0.36);
  color: #ffffff;
  transform: scale(1.08);
}

.sc-campaign-nav__btn:focus-visible {
  outline: 2px solid var(--bs-primary);
  outline-offset: 2px;
}

/* --------------------------------------------------------------------------
   5. Motion.

   One authored moment, and it is the reveal. The plates arrive in sequence
   with a slight rotate and a rise, the copy settles once, and then the
   scroll takes over. Every element starts from a visible default and
   animates in, so a JS failure or a slow first paint leaves a complete hero
   rather than an invisible one.

   `data-sc-in` is set by department.js on load. Until it is, everything is
   fully visible — the page is never blank waiting for an observer.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: no-preference) {
  [data-sc-in] [data-sc-anim] {
    opacity: 0;
    transform: translate3d(0, 26px, 0);
  }

  [data-sc-in].is-in [data-sc-anim] {
    opacity: 1;
    transform: none;
    transition: opacity 760ms cubic-bezier(0.16, 1, 0.3, 1),
      transform 760ms cubic-bezier(0.16, 1, 0.3, 1);
  }

  /* Staggered, front plate first — the reveal enters where the eye is. */
  [data-sc-in].is-in .sc-plate--a { transition-delay: 60ms; }
  [data-sc-in].is-in .sc-plate--b { transition-delay: 190ms; }
  [data-sc-in].is-in .sc-plate--c { transition-delay: 320ms; }
  [data-sc-in].is-in .sc-hero__title { transition-delay: 120ms; }
  [data-sc-in].is-in .sc-hero__lead { transition-delay: 220ms; }
  [data-sc-in].is-in .sc-hero__actions { transition-delay: 320ms; }
  [data-sc-in].is-in .sc-hero__note { transition-delay: 400ms; }
}

/* Reduced motion: the scene is still a room, it just does not travel. The
   plates keep their tilt — that is composition, not animation. */

@media (prefers-reduced-motion: reduce) {
  .sc-hero__room,
  .sc-hero__light,
  .sc-hero__fore,
  .sc-hero__copy,
  .sc-hero__desk {
    transform: none !important;
    will-change: auto;
  }

  /* The room is sized one travel-unit taller than the frame so the slide has
     image to reveal. A still scene does not slide, so with the transform
     pinned the extra height is simply dead space below the crop — the
     photograph hangs 110px past the bottom of the frame and nothing ever
     scrolls it into view, so the bottom of the hero is the frame's edge
     sitting on top of the section's flat background rather than the room.
     Collapsing the layer back to exactly the frame (`bottom: 0`) restores
     the intended framing for reduced-motion visitors, who should get the
     composition and not the animation.

     This has to be `!important`: department.js writes the transform inline
     on every frame, and an inline style beats a plain rule from the same
     origin, so the JS has to be told no by something stronger. */
  .sc-hero__room {
    top: 0 !important;
    bottom: 0 !important;
  }

  /* 200vh of scroll for a section that does not move is a scroll trap. A
     reduced-motion visitor gets a single screen and no runway at all. */
  .sc-hero { min-height: 100vh; }

  .sc-plate,
  .sc-hero__btn,
  .sc-plate__slides,
  .sc-plate__frame img {
    transition: none !important;
  }

  .sc-plate:hover .sc-plate__slides,
  .sc-hero:hover .sc-plate--a,
  .sc-hero:hover .sc-plate--b,
  .sc-hero:hover .sc-plate--c,
  .sc-plate:hover .sc-plate__frame img,
  .sc-hero__btn:hover {
    transform: none !important;
  }

  .sc-plate__img {
    transform: none !important;
    transition: opacity 300ms ease !important;
  }

  .sc-plate__img.is-active,
  .sc-plate__img.is-leaving {
    transform: none !important;
  }

  .sc-plate__glint {
    display: none !important;
  }

  .sc-campaign-pill.is-active .sc-campaign-pill__progress {
    display: none !important;
  }

  /* The light layer keeps its brightness, loses only its travel. */
  .sc-hero__light { opacity: 0.7; }
}

/* --------------------------------------------------------------------------
   6. Responsive.

   Below 992px the copy leads and the desk follows it, stacked. The desk
   keeps its three-up layout down to 576px because these are portrait
   documents and three narrow columns still read; below that it becomes a
   single column and the stagger is dropped, since a vertical stack has no
   Z to stagger through.
   -------------------------------------------------------------------------- */

@media (max-width: 1199.98px) {
  .sc-hero__title { font-size: clamp(2.15rem, 4.6vw, 3.2rem); }
  .sc-hero__desk { gap: 0.7rem; }
  .sc-plate__cap { font-size: 0.75rem; }
}

@media (max-width: 991.98px) {
  :root {
    /* A phone has no room for a long runway. The stacked layout —
       headline, lead, two CTAs, the note, three plates — is already ~900px
       of content, so pinning it inside a 100vh frame would clip it, and
       200vh of scroll before "How it works" is a scroll trap on exactly the
       device least able to absorb one.

       128vh gives the frame one screen and a quarter of a screen of pass.
       The content is taller than the frame on a phone, so the pin is only
       useful while the copy is still in view; past that it releases and the
       rest of the section scrolls normally. That is the honest behaviour
       here and it is why the runway is short. */
    --sc-hero-runway: 128vh;
    /* 44px, not 110. Same reasoning as the runway: a thumb scroll on a
       phone amplifies small displacements enormously, and 110px of room
       travel against a 915px screen is enough to read as the room sliding
       rather than settling. */
    --sc-hero-travel: 44px;
  }

  .sc-hero__frame {
    padding-block: clamp(2.5rem, 7vw, 4rem);
    /* `flex-start` replaces the base `center`, and this is the one that
       actually matters on a phone.

       The base frame centres its single child inside a `100vh` box. That is
       correct on a desktop, where the grid is shorter than the frame and
       centring is the point — it is what puts the composition on a
       horizontal mid-line.

       On a phone the stacked grid is TALLER than the frame, and a centred
       flex child overflows equally in both directions: the top of the copy
       rendered above the frame's own padding box and underneath the navbar.
       Nothing clipped it — `.sc-hero`'s `overflow: clip` clips the section,
       not the grid's position inside the frame.

       So the frame stops centring below 992px and starts at the top. The
       overflow then goes the only direction it can, downwards, where it
       becomes ordinary page scroll. The `align-items: start` on the grid
       below is the matching half: the grid's own rows must also not centre
       against each other. */
    align-items: flex-start;
  }

  .sc-hero__grid {
    grid-template-columns: minmax(0, 1fr);
    gap: clamp(1.75rem, 6vw, 2.5rem);
    /* `start` replaces the inherited `center`, and it is what keeps the
       headline on a phone from rendering ABOVE the frame's own edge.

       The stacked layout is taller than the 100vh frame — measured at 900px
       against an 844px viewport — and a centred grid overflows equally in
       both directions, so the top of the copy rendered above the frame's
       padding box and underneath the navbar. Nothing clipped it:
       `.sc-hero`'s `overflow: clip` clips the section, not the grid's
       position inside the frame.

       `start` puts the overflow where it belongs: below, where it becomes
       ordinary page scroll. */
    align-items: start;
  }

  /* Nothing in the section may exceed the frame. The section is
     `overflow: clip`, but `clip` is silently downgraded to `hidden` on some
     engines, which then becomes a real scroll container and scopes every
     sticky descendant. Both the plates' 3D transforms and the copy's parallax
     write transforms inside this box, so a scroll container here is a real
     bug source rather than a theoretical one. */
  .sc-hero__copy,
  .sc-hero__desk { max-width: 100%; }

  /* On a narrow screen the copy is no longer over the left of the room, so
     the vignette stops being a legibility device and becomes a scrim for the
     whole frame.

     The values are much lower than the desktop ones on purpose. A full-width
     0.86/0.62/0.80 wash over a 1034px-tall hero meant the phone showed a
     flat black rectangle with white text on it and no room at all — the
     section had become a text panel with a background image nobody could
     see. On a phone the type is the thing that must survive, and it survives
     on its own text-shadow at a 45% top scrim. */
  .sc-hero__vignette {
    background:
      linear-gradient(180deg, rgba(9, 16, 20, 0.52) 0%, rgba(9, 16, 20, 0.16) 40%, rgba(9, 16, 20, 0.10) 74%, rgba(9, 16, 20, 0.46) 100%);
  }

  /* The room is portrait-cropped here, so the deep end of the photograph
     behind the copy is gone and the frame lands on empty wall. The exposure
     is lifted further and the crop pushed toward the lit side, where the
     window and the table are. */
  .sc-hero__room {
    filter: brightness(1.5) contrast(0.95) saturate(0.9);
    /* Height-sized, exactly as on the base rule, for the same reason: a
       width-sized 16:9 plate cannot fill a tall portrait frame and leaves a
       black band. The portrait crop keeps the width over-filling, which on a
       phone means the plate's sides are cropped rather than its bottom being
       exposed — and the sides are the least informative part of this
       photograph anyway. */
    background-size: auto calc(100% + var(--sc-hero-travel));
    background-position: 58% 38%;
    background-repeat: no-repeat;
  }

  /* The foreground mask is tuned for a 16:9 frame with a lit middle. On a
     narrow, tall frame there is no "middle" to clear — the door frame and
     the chair would both fall across the copy — so it is withdrawn to a
     whisper rather than removed, keeping the depth cue without the cost. */
  .sc-hero__fore {
    opacity: 0.4;
    -webkit-mask-image: linear-gradient(90deg, #000 0%, #000 8%, rgba(0, 0, 0, 0) 24%),
                        linear-gradient(0deg, #000 0%, #000 6%, rgba(0, 0, 0, 0) 18%);
    mask-image: linear-gradient(90deg, #000 0%, #000 8%, rgba(0, 0, 0, 0) 24%),
                linear-gradient(0deg, #000 0%, #000 6%, rgba(0, 0, 0, 0) 18%);
  }

  .sc-hero__lead { max-width: 58ch; }

  /* Type on a narrow frame. The reported failure was not that the headline was
     too big — it was that it "almost falls out of the left side", which is a
     horizontal problem and is fixed below and in department.js, not here. What
     this block does own is the vertical density: three lines of 800-weight
     Montserrat at the clamp floor plus a 5-line lead plus two half-width CTAs
     pushed the plates entirely below the fold, which is what made the room
     feel like a wall. */
  .sc-hero__title {
    font-size: clamp(1.95rem, 8vw, 2.6rem);
    line-height: 1.08;
    letter-spacing: -0.024em;
    margin-bottom: 0.9rem;
  }

  .sc-hero__lead {
    margin-bottom: 1.5rem;
    font-size: 1rem;
    line-height: 1.58;
  }

  /* One button per row. `flex: 1 1 8.5rem` at the 575px breakpoint let both
     CTAs share a line at exactly the width where the ghost label wrapped, and
     a wrapped ghost label reads as two buttons because the two boxes sit at
     different heights. Stacking is what the page already does for its mobile
     CTAs elsewhere, and 46px min-height keeps both well over the tap floor. */
  .sc-hero__actions {
    gap: 0.7rem;
    margin-bottom: 1.15rem;
  }

  .sc-hero__btn {
    flex: 1 1 100%;
    width: 100%;
  }

  /* THE RECEIPTS LINE IS WITHDRAWN ON A PHONE.

     Measured before this change, at 390x844: the three captions' bottoms sat
     at 853-869px against an 844px viewport. The posters were complete and
     uncut — `desk.scrollWidth` equalled `clientWidth` at every width from
     320px up — but the visitor arrived to see the bottom 5% of them. The
     proof was on the screen and unreadable at the same time, which is the
     one outcome worse than either showing it properly or hiding it.

     The line is 72px tall plus 18px of margin above it: 90px. Removing it
     lifts the desk to 657px and the caption bottoms to 691px, so all three
     captions finish above the fold with 153px to spare instead of 63.

     `display: none` rather than a smaller font, and this is the real reason
     rather than a preference: at 12px on a 351px measure the sentence is
     three lines of grey over a photograph, and the reader's reward for
     finishing it is a thumbnail. The claim is not lost. The footer carries
     the same statement in the site's own words, and each poster keeps its
     own caption naming the suite that produced it — which is the stronger
     form of the same evidence, because it is attached to the thing it
     describes instead of floating above it.

     Desktop keeps it at ≥992px, where there is room for it and the copy
     column is wide enough that three lines of small grey text costs the
     composition nothing. */
  .sc-hero__note {
    display: none;
  }

  .sc-hero__actions {
    margin-bottom: 0;
  }

  .sc-hero:hover .sc-plate--a,
  .sc-hero:hover .sc-plate--b,
  .sc-hero:hover .sc-plate--c { transform: none; }
  .sc-plate--a { transform: translate3d(0, 18px, -20px) rotateY(-6deg) rotateX(2deg); }
  .sc-plate--b { transform: none; }
  .sc-plate--c { transform: translate3d(0, 18px, -20px) rotateY(6deg) rotateX(2deg); }
}

/* --------------------------------------------------------------------------
   The desk, below the copy column.

   The mobile report was precise: "one poster gets lost, one is cut in half and
   one is seen" — three artefacts, one visible, and that is exactly what a
   `overflow-x: auto` strip at `flex: 0 0 min(72%, 300px)` produces. At 412px
   wide the third plate starts past the right edge, and a horizontally
   scrollable element inside a section that is `overflow: clip` has no
   discoverable affordance at all: no scrollbar was drawn, no fade marked the
   cut, and the section's own clip silently truncated the third plate the
   moment it was dragged further right. It read as broken rather than as a
   carousel.

   So there is no strip. All three artefacts are in a three-column grid at
   every width, because they are the proof and proof that has to be dragged
   sideways is not proof.

   Three columns at 412px means ~112px plates. That is small, so the frame's
   aspect ratio is relaxed from 3/4 to 4/5 here and the plate keeps a small
   fixed Z drop instead of the desktop 34px stagger — at 112px wide a 34px
   drop pushes the caption off the bottom of its own column.

   Nothing is clipped, nothing is scrolled, nothing is cut in half, and all
   three captions stay in the document where a screen reader can reach them.
   -------------------------------------------------------------------------- */

@media (max-width: 991.98px) {
  .sc-hero__desk {
    /* Grid, not flex. The base rule is already `repeat(3, 1fr)`, so this is
       a restatement rather than an override — and stating it here is the
       point: the strip was an *override* of the base, and the base was
       correct all along. */
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: clamp(0.5rem, 2.2vw, 0.85rem);
    max-width: none;
    padding-bottom: 0;
    overflow: visible;
    perspective: 900px;
    perspective-origin: 50% 42%;
  }

  .sc-plate {
    transform: none;
  }

  .sc-plate--a,
  .sc-plate--c { transform: none; }

  .sc-hero:hover .sc-plate--a,
  .sc-hero:hover .sc-plate--b,
  .sc-hero:hover .sc-plate--c { transform: none; }

  /* Relaxed from 3/4 so a 112px-wide plate is a recognisable document
     thumbnail rather than a sliver, and rounded a little less so the three
     do not read as three identical lozenges. */
  .sc-plate__frame {
    aspect-ratio: 4 / 5;
    border-radius: 8px;
  }

  /* The caption is bounded by 24ch on the base rule, which on a 112px column
     wraps "Campaign overview" to three lines and unsticks the three captions
     from each other. Below 992px the bound is dropped so each caption is as
     wide as its own column, and the sub-line goes to a tighter leading
     because it is now four words per line rather than a line and a half. */
  .sc-plate__cap {
    max-width: none;
    margin-top: 0.5rem;
  }

  /* THE TITLE GETS A RESERVED SECOND LINE.

     A `min-height` on the caption is not enough, and the reason is worth
     recording because the obvious fix looked like it worked. Measured at
     360px with `min-height: 4em` applied and confirmed at 48px computed:

         Key visual         1 title line   caption 49px
         Campaign overview  2 title lines  caption 64px
         Style guide        1 title line   caption 49px

     The floor sets a minimum; it does not make two boxes equal when one of
     them has more content than the floor. "Campaign overview" needs 31px of
     title and the others need 16px, so its subtitle starts 15px lower and
     the row of three captions is ragged again.

     The fix is on the TITLE, not the caption. `.sc-plate__cap` used to hold
     its title as a bare text node, so there was no element to give a height
     to — every attempt to reserve space had to go on the caption as a whole,
     and a `min-height` on the parent is only a floor: it does not make a
     2-line box the same height as a 1-line one. The title is now wrapped in
     `.sc-plate__name`, and that is the element that takes the reservation.

     This is a RESERVATION, not a clamp: no `overflow: hidden` and no
     `-webkit-line-clamp`, so a longer string at an unforeseen width would
     still show rather than being silently cut. The sub-line names the suite
     that produced the artefact — that is the proof, and the whole reason the
     caption exists. A clamp on an 89px column would eat "channel rules" on
     most phones. */
  .sc-plate__name {
    display: block;
    /* Two lines of 15.6px leading. 2.6em, not 2em, because the title's own
       line-height is 1.3 and the reservation has to be the FULL two lines
       including the second one's leading, not two times the font size. */
    min-height: 2.6em;
  }

  .sc-plate__cap span:not(.sc-plate__name) {
    line-height: 1.28;
    /* Two lines at 12px/1.28 = 30.72px, written in em against the caption's
       own size so it moves with it. */
    min-height: 2.56em;
    max-height: 2.56em;
  }

  .sc-hero__campaigns {
    margin-top: 1rem;
    padding-top: 0.75rem;
    gap: 0.5rem;
  }
}

@media (max-width: 767.98px) {
  .sc-hero__desk { gap: clamp(0.45rem, 2vw, 0.7rem); }
  .sc-plate__cap { font-size: 0.75rem; }
  /* Never below 12px. The caption is content.

     Excluded for the same reason as the base rule: the title keeps the
     caption's own size and weight, only the sub-line is pinned to 12px. */
  .sc-plate__cap span:not(.sc-plate__name) { font-size: 0.75rem; }

  .sc-hero__campaigns {
    justify-content: center;
  }
  .sc-campaign-nav {
    display: none;
  }
  .sc-campaign-pill {
    padding: 0.28rem 0.55rem;
    gap: 0.25rem;
  }
  .sc-campaign-pill__name {
    display: none;
  }
}

@media (max-width: 575.98px) {
  .sc-hero__actions { gap: 0.6rem; }
  /* `flex: 1 1 8.5rem` was the value that let both CTAs share a line at this
     exact width and wrap the ghost label. The full-width rule in the 991px
     block above is the one that applies; this is left here only as a gap
     tightening so the two rows do not drift apart. */
  .sc-hero__btn { flex: 1 1 100%; }
}

@media (min-width: 1600px) {
  .sc-hero__title { font-size: 4.25rem; }
}

/* Short viewports: the hero is a full impression, not a wall. Below 700px of
   height the room is pulled down so the copy and at least one plate are
   above the fold rather than being pushed under it. */

@media (max-height: 700px) and (min-width: 992px) {
  .sc-hero { min-height: 0; }
  .sc-hero__title { font-size: clamp(1.9rem, 3.4vw, 2.6rem); }
  .sc-plate__frame { aspect-ratio: 4 / 5; }
}
