/* ==========================================================================
   Mezzo - Design division page system (divisions/design.html only)

   A separate stylesheet, not another section of style.css: only design.html
   links it, so the blast radius is one page. Every selector is prefixed
   .mz- and every custom property --mz-, so nothing here collides with the
   shared header, nav page or footer this page still renders from style.css.

   Copy, colour and type family are fixed and not changed here. This revision
   is scale and spacing only:

     - every heading set with clamp(), never a fixed px size
     - one 1280px container with 40px side padding on every section, with the
       marquee as the single full-bleed exception
     - one vertical rhythm: 120px between sections, 72px on mobile
     - 100svh only on the hero, which must fit above the fold
     - 32px from eyebrow to heading, 56px from heading to content
     - flat throughout: 40px radius on cards and image blocks, pill on
       buttons, no shadows anywhere
   ========================================================================== */

:root {
  --mz-orange: #E97132;
  --mz-orange-deep: #CE5A1E;

  --mz-canvas: #E4E0D6;
  --mz-linen: #F2EFE7;

  --mz-ink: #14100E;
  --mz-ink-soft: rgba(20, 16, 14, 0.66);

  --mz-display: 'Montserrat', 'General Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;

  /* Flat geometry. Two radii and no third. */
  --mz-r-card: 40px;
  --mz-r-pill: 999px;

  --mz-max: 1280px;
  --mz-pad: 40px;

  /* The rhythm is expressed as half the gap, because it is applied as
     padding to both of two adjacent sections: 60px + 60px is the 120px
     between them. Setting 120px on each would put 240px between every pair,
     which is the dead space this pass exists to remove. */
  --mz-rhythm: 60px;

  /* .site-header .container is a fixed 88px (style.css). The hero is sized
     against it so the whole hero lands above the fold. */
  --mz-header: 88px;
}

/* --------------------------------------------------------------------------
   Page ground + container
   -------------------------------------------------------------------------- */
.mz-main {
  background: var(--mz-canvas);
  color: var(--mz-ink);
  overflow-x: clip;
}

.mz-wrap {
  max-width: var(--mz-max);
  margin-inline: auto;
  padding-inline: var(--mz-pad);
}

.mz-main > section {
  padding-block: var(--mz-rhythm);
}

/* --------------------------------------------------------------------------
   Type
   --------------------------------------------------------------------------
   Three clamped steps and nothing else. Line height sits between 0.95 and
   1.0 at every step - at these sizes anything looser reads as separate
   lines rather than as a set block of type.
   -------------------------------------------------------------------------- */
.mz-display {
  font-family: var(--mz-display);
  font-weight: 800;
  letter-spacing: -0.03em;
  margin: 0;
  /* style.css colours every h1/h2/h3 navy and caps h1 at 18ch, both as
     element selectors that this class would otherwise inherit past. */
  color: inherit;
  max-width: none;
  /* A last-resort guard: at these sizes a single long word can be wider than
     its column, and this makes that wrap rather than spill out of the card
     and open a horizontal scrollbar. */
  overflow-wrap: break-word;
}

/* The hero step is the one size that has to know the viewport's height as
   well as its width. The hero is pinned to the fold, so on a wide-but-short
   screen - 1512x713 and 1366x681 are both common laptops - a 120px headline
   was two fixed lines inside a hero only ~500px tall, which left barely any
   gradient between the two cut-outs and crushed the shape. min() takes
   whichever of the width- and height-derived sizes is smaller, so the
   headline can never outgrow the height it has to sit in. The ceiling came
   down from 120px to 100px and the floor from 48px to 40px at the client's
   request - the type was simply too big. */
.mz-display--hero {
  font-size: clamp(40px, min(7.6vw, 11vh), 100px);
  line-height: 0.95;
  /* Montserrat's ascender+descender is about 1.22em, so a line-height of
     0.95 makes the line BOX shorter than the glyphs it holds: the ink
     overhangs it by roughly 0.13em above and below. Every measurement of
     this heading was of the box, which is why the descender of "Marketing"
     kept sitting on the gradient while the numbers said it fitted. The
     padding is in em so it scales with the clamp - a pixel value would only
     be right at one step. */
  padding-bottom: 0.17em;
}
.mz-display--section { font-size: clamp(40px, 7.5vw, 96px); line-height: 0.95; }
.mz-display--block   { font-size: clamp(32px, 4.5vw, 64px); line-height: 1; }

.mz-body {
  font-family: var(--f-body);
  font-weight: 500;
  font-size: clamp(16px, 1.1vw, 18px);
  line-height: 1.55;
  margin: 0;
}

.mz-eyebrow {
  font-family: var(--f-mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  margin: 0;
}

/* The section head: eyebrow, 32px, heading. The content that follows starts
   56px below it. Both gaps are declared once here rather than per section. */
.mz-head {
  display: grid;
  justify-items: start;
  gap: 32px;
}

.mz-head + * { margin-top: 56px; }

/* --------------------------------------------------------------------------
   Controls - full pill, no shadow
   -------------------------------------------------------------------------- */
.mz-btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 16px 32px;
  border: 0;
  border-radius: var(--mz-r-pill);
  background: var(--mz-orange);
  color: var(--mz-ink);
  font-family: var(--f-body);
  font-weight: 600;
  font-size: 16px;
  text-decoration: none;
  transition: background-color 240ms ease, transform 240ms ease;
}

.mz-btn:hover,
.mz-btn:focus-visible {
  background: var(--mz-orange-deep);
  transform: translateY(-3px);
}

.mz-btn svg { width: 16px; height: 16px; flex: none; }

.mz-tag {
  display: inline-block;
  padding: 6px 16px;
  border-radius: var(--mz-r-pill);
  background: rgba(20, 16, 14, 0.07);
  font-family: var(--f-body);
  font-weight: 500;
  font-size: 13px;
  line-height: 1.2;
}

/* ==========================================================================
   1. Hero - the only 100svh section, and it fits above the fold
   ==========================================================================
   Height is the viewport minus the sticky header, and the section's own
   padding is inside that (border-box), so the bottom of the hero is the
   bottom of the first screen. The visual row is minmax(0, 1fr): it absorbs
   whatever is left after the headline and shrinks rather than pushing the
   composition past the fold on a short viewport.

   svh, not vh: on mobile browsers vh is the tallest possible viewport, so a
   100vh hero is guaranteed to be cut off by the address bar on load. svh is
   the smallest, which is the one that is always actually visible.
   ========================================================================== */
.mz-hero {
  min-height: calc(100svh - var(--mz-header));
  display: flex;
}

.mz-hero .mz-wrap { width: 100%; display: flex; }

/* --------------------------------------------------------------------------
   The shape
   --------------------------------------------------------------------------
   One continuous form, not two blocks: a single rounded rectangle carrying
   the gradient, with two panels of page-colour laid over its top-left and
   bottom-right corners. The union of what is left is the reference's Z form,
   and because each panel carries a radius on the corner that faces into the
   shape, the two inner corners come out concave - which is the part a pair
   of separate rounded rectangles cannot produce.

   The panels are exactly --mz-canvas, the page ground, so they read as the
   page showing through rather than as boxes sitting on top. They are also
   where the hero's content lives: headline in the top-left cut-out, lockup
   and CTA in the bottom-right one, as in the reference.

   Percentages, not pixels, so the whole form scales with the hero at any
   width while holding the reference's proportions.
   -------------------------------------------------------------------------- */
.mz-hero__frame {
  position: relative;
  flex: 1;
}

/* The gradient plate. The blend-mode group inside it is isolated and clipped
   to the radius here, and nothing else lives in this element. */
/* Only the two corners that are actually on show get a radius. The other two
   sit under the cut-out panels, and rounding them put a 40px arc directly
   underneath the panel's own 40px arc: two coincident antialiased curves,
   where the panel's half-transparent edge pixels let the near-black plate
   bleed through as a hairline crescent in both cut-outs. Square under a
   square-cornered panel, and there is no edge to misalign. */
.mz-hero__shape {
  position: absolute;
  inset: 0;
  isolation: isolate;
  overflow: hidden;
  border-radius: 0 var(--mz-r-card) 0 var(--mz-r-card);
  /* No background here any more (6 Sep 2026). It moved to .mz-hero__plate so
     the burst has something to reveal: painted on this element, the plate is
     already a solid black rectangle before the animation starts and there is
     nothing left to open. This element keeps the clip and the radius. */
}

/* What the burst opens. Everything the plate is - the near-black ground and
   the blend-mode gradient group - lives in here so one clip-path on this
   element reveals all of it at once, while .mz-hero__shape above keeps the
   corner radius and .mz-hero__ring below stays outside the clip and visible. */
.mz-hero__plate {
  position: absolute;
  inset: 0;
  background: #131110;
}

/* The ring that leads the burst out, and it is the menu button's own
   expansion (see .navpage__wave in css/style.css) rebuilt at this scale.

   Same construction and for the same reason: a 1px border on a box whose
   width and height are animated, never transform: scale(). Scaling a bordered
   box thickens the border along with it, so a hairline ring would arrive as a
   fat one; animating the box's size keeps the edge exactly 1px the whole way
   out. It has no children and nothing depends on its size, so there is
   nothing for the growth to reflow.

   left/top are set to the plate's centre and translate(-50%, -50%) keeps it
   centred there as it grows. Outside .mz-hero__plate's clip on purpose: the
   ring has to be visible running ahead of the fill, which is the whole read of
   the animation. */
.mz-hero__ring {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 0;
  height: 0;
  transform: translate(-50%, -50%);
  border: 1px solid rgba(20, 16, 14, 0.55);
  border-radius: 50%;
  background: transparent;
  pointer-events: none;
  z-index: 1;
  will-change: width, height;
}

/* The panels are siblings of the plate, not children of it. Nested inside,
   they sat within its clip and its screen-blend group, and the composited
   edge of that group showed as a hairline arc curving through both cut-out
   corners - visible whatever radius the panel itself carried. Painted over
   the plate instead, they cover it outright and the arcs are gone. */
.mz-hero__notch {
  position: absolute;
  z-index: 2;
  background: var(--mz-canvas);
  display: flex;
}

/* Top-left cut-out: the headline. The reference's own notch is 54% x 37%;
   this is a little taller because the headline set at its 120px ceiling is
   two lines of 114px and was spilling out of the bottom of a 37% box. */
.mz-hero__notch--tl {
  top: 0;
  left: 0;
  width: 58%;
  /* Height comes from the headline, not from a share of the hero. A
     percentage height was the bug behind every "text overlapping" report:
     the headline is a fixed 228px at the 120px ceiling (two lines at 0.95),
     but 40% of the hero is only 202px at 1512x713 and 189px at 1366x681, so
     on any short viewport the type spilled out of the bottom of the cut-out
     and over the gradient. Sizing the panel to its content means the cut-out
     grows to hold the type instead. min-height keeps the shape's proportions
     when the headline is short. */
  height: auto;
  min-height: 34%;
  /* Only the inward corner is rounded - that is the concave one. The outer
     corner stays square and covers the plate's square corner beneath it. */
  border-bottom-right-radius: var(--mz-r-card);
  align-items: center;
  padding: 0 40px 32px 0;
}

/* Bottom-right cut-out: the lockup and the CTA, bottom-aligned so the button
   sits on the shape's own bottom edge exactly as the reference's do. */
.mz-hero__notch--br {
  right: 0;
  bottom: 0;
  width: 36%;
  /* Content-sized for the same reason as the panel above - at 1366x681 the
     lockup and button filled its 114px exactly, with nothing to spare. */
  height: auto;
  min-height: 22%;
  border-top-left-radius: var(--mz-r-card);   /* the concave corner */
  flex-direction: column;
  align-items: flex-start;
  justify-content: flex-end;
  gap: 20px;
  /* Even padding on all four sides so the lockup and the button share one
     left edge and sit the same distance off the foot of the shape - the
     button was previously flush to the bottom and read as unaligned with
     the panel it sits in. */
  padding: 32px;
}

.mz-hero__logo {
  width: min(300px, 100%);
  height: auto;
}

/* --------------------------------------------------------------------------
   The four convex corners the panels would otherwise leave square
   --------------------------------------------------------------------------
   Where a panel's edge meets the plate's edge, the reference has a rounded
   corner on the gradient - the top-left of the upper block, the top-left of
   the lower block, and the two matching corners at the bottom-right cut-out.
   A panel with a plain border-radius cannot make those: it rounds its own
   corner, which curves the wrong way.

   Each of these is an inverse corner - a square of page colour with a
   quarter-disc bitten out of it - built as a radial-gradient that is
   transparent inside the radius and page colour outside it. Placed just
   beyond the panel's edge, the transparent quarter lets the gradient through
   as a rounded corner and the rest covers the plate's square one.
   -------------------------------------------------------------------------- */
.mz-hero__notch::before,
.mz-hero__notch::after {
  content: '';
  position: absolute;
  width: var(--mz-r-card);
  height: var(--mz-r-card);
}

/* Circle centred on the box's bottom-right, for the two corners that open
   down and right. */
.mz-hero__notch--tl::before,
.mz-hero__notch--tl::after {
  background: radial-gradient(circle var(--mz-r-card) at 100% 100%,
              transparent calc(var(--mz-r-card) - 1px), var(--mz-canvas) var(--mz-r-card));
}

.mz-hero__notch--tl::before { left: 100%; top: 0; }    /* top of the upper block */
.mz-hero__notch--tl::after  { left: 0; top: 100%; }    /* top of the lower block */

/* Circle centred on the box's top-left, for the two that open up and left. */
.mz-hero__notch--br::before,
.mz-hero__notch--br::after {
  background: radial-gradient(circle var(--mz-r-card) at 0 0,
              transparent calc(var(--mz-r-card) - 1px), var(--mz-canvas) var(--mz-r-card));
}

.mz-hero__notch--br::before { right: 0; bottom: 100%; }  /* right edge, above the cut-out */
.mz-hero__notch--br::after  { right: 100%; bottom: 0; }  /* bottom edge, left of the cut-out */

/* --------------------------------------------------------------------------
   The living gradient
   --------------------------------------------------------------------------
   Radial-gradients with wide, soft stops and NO blur filter: a gradient that
   fades to transparent over 60%+ of its radius is already as soft as a
   blurred circle and costs nothing to composite, whereas filter: blur() on
   five hero-sized layers forces a re-rasterise every frame. Only `transform`
   animates, so each layer uploads to the GPU once and is then merely moved.
   screen blending is what makes the overlapping oranges bloom where they
   cross instead of stacking as flat discs.
   -------------------------------------------------------------------------- */
.mz-field { position: absolute; inset: 0; z-index: 0; pointer-events: none; }

.mz-field__blob {
  position: absolute;
  border-radius: 50%;
  mix-blend-mode: screen;
  will-change: transform;
}

.mz-field__blob--core {
  width: 90%; height: 95%; left: 16%; top: -34%;
  background: radial-gradient(circle at 50% 50%, #ff8a12 0%, rgba(255, 138, 18, 0.55) 34%, rgba(255, 138, 18, 0) 66%);
  animation: mz-drift-core 26s ease-in-out infinite;
}
.mz-field__blob--ember {
  width: 86%; height: 88%; left: -8%; top: 8%;
  background: radial-gradient(circle at 50% 50%, #e0550c 0%, rgba(224, 85, 12, 0.5) 32%, rgba(224, 85, 12, 0) 66%);
  animation: mz-drift-ember 34s ease-in-out infinite;
}
.mz-field__blob--plume {
  width: 78%; height: 104%; left: 4%; top: 26%;
  background: radial-gradient(circle at 50% 50%, var(--mz-orange) 0%, rgba(233, 113, 50, 0.45) 36%, rgba(233, 113, 50, 0) 68%);
  animation: mz-drift-plume 29s ease-in-out infinite;
}
.mz-field__blob--haze {
  width: 62%; height: 58%; left: -6%; bottom: -20%;
  background: radial-gradient(circle at 50% 50%, rgba(228, 224, 214, 0.5) 0%, rgba(228, 224, 214, 0.18) 38%, rgba(228, 224, 214, 0) 68%);
  animation: mz-drift-haze 38s ease-in-out infinite;
}
.mz-field__blob--gold {
  width: 66%; height: 60%; right: -12%; bottom: -22%;
  background: radial-gradient(circle at 50% 50%, rgba(184, 162, 92, 0.45) 0%, rgba(184, 162, 92, 0.16) 40%, rgba(184, 162, 92, 0) 70%);
  animation: mz-drift-gold 31s ease-in-out infinite;
}

.mz-field__wave {
  position: absolute;
  inset: -30%;
  mix-blend-mode: screen;
  opacity: 0.5;
  background: linear-gradient(122deg,
    rgba(255, 255, 255, 0) 34%,
    rgba(255, 187, 110, 0.16) 46%,
    rgba(255, 214, 156, 0.24) 52%,
    rgba(255, 187, 110, 0.16) 58%,
    rgba(255, 255, 255, 0) 70%);
  will-change: transform;
  animation: mz-wave 22s ease-in-out infinite;
}

/* The blend layers above can only add light, so the corner falloff has to
   sit on top of them rather than under. */
.mz-field__vignette {
  position: absolute;
  inset: 0;
  background: radial-gradient(115% 92% at 50% 42%, rgba(0, 0, 0, 0) 40%, rgba(8, 6, 6, 0.62) 100%);
}

/* Tiled 128x128 noise from style.css - see its --grain comment for why the
   tile carries an explicit intrinsic size. Stops the gradient banding on
   8-bit displays. */
.mz-field__grain {
  position: absolute;
  inset: 0;
  background-image: var(--grain);
  background-repeat: repeat;
  opacity: 0.16;
  mix-blend-mode: overlay;
}

@keyframes mz-drift-core {
  0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
  33%      { transform: translate3d(-5%, 5%, 0) scale(1.09); }
  66%      { transform: translate3d(4%, 2%, 0) scale(0.95); }
}
@keyframes mz-drift-ember {
  0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
  40%      { transform: translate3d(7%, -5%, 0) scale(1.11); }
  70%      { transform: translate3d(-3%, 4%, 0) scale(0.96); }
}
@keyframes mz-drift-plume {
  0%, 100% { transform: translate3d(0, 0, 0) scale(1) rotate(0deg); }
  35%      { transform: translate3d(6%, -8%, 0) scale(1.08) rotate(5deg); }
  68%      { transform: translate3d(-4%, -3%, 0) scale(0.97) rotate(-4deg); }
}
@keyframes mz-drift-haze {
  0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
  50%      { transform: translate3d(11%, -7%, 0) scale(1.14); }
}
@keyframes mz-drift-gold {
  0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
  50%      { transform: translate3d(-9%, -5%, 0) scale(1.1); }
}
@keyframes mz-wave {
  0%, 100% { transform: translate3d(-14%, -6%, 0); }
  50%      { transform: translate3d(14%, 6%, 0); }
}

/* ==========================================================================
   2. Statement + services
   ========================================================================== */
/* No band colour: every section sits on the same --mz-canvas as the hero, so
   the page reads as one continuous ground from the header down. */
.mz-statement { text-align: center; }

/* Centred head: the eyebrow and heading are centred over the cards rather
   than ranged left. .mz-head is a grid, so justify-items does it. */
.mz-statement .mz-head { justify-items: center; }

/* Three columns, 24px gaps, equal heights. align-items is not set, so the
   default `stretch` applies and every card fills its row - the fifth card
   simply leaves the last cell of row two empty rather than being stretched
   across it. */
/* --------------------------------------------------------------------------
   The linked bar
   --------------------------------------------------------------------------
   Removed on 7 Sep 2026 and restored the same day at the client's
   instruction, verbatim from b2edc69b. This is the deliverables bar. It
   stays - see the note in section 2 of divisions/design.html.

   The five containers are one continuous orange bar, not five cards in a
   grid. The bar carries the background and the 40px radius; each container
   is an equal flex segment of it (flex: 1 1 0 with min-width: 0, so all five
   are exactly the same width regardless of how much copy each holds).

   The division between segments is a page-coloured slot that stops short of
   the bar's top and bottom edges, so orange runs continuously above and
   below every gap - that unbroken run is what makes the row read as linked
   rather than as five separate cards. The slot's ends are fully rounded, so
   each join is a concave curve.
   -------------------------------------------------------------------------- */
.mz-services__grid {
  display: flex;
  align-items: stretch;
  background: var(--mz-orange);
  border-radius: var(--mz-r-card);
  list-style: none;
  padding: 0;
  /* No `margin: 0`: style.css already zeroes ul margins, and declaring it
     again at this specificity beat `.mz-head + *` on source order and
     silently collapsed the 56px below the heading to nothing. */
}

.mz-card {
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* Top-aligned, not centred. Centring let each segment's block float to its
     own middle, so the five titles landed at five different heights - the
     number is a single line in every segment, so starting from the top puts
     every number and every title on a shared baseline. */
  justify-content: flex-start;
  text-align: center;
  gap: 12px;
  padding: 36px 20px 40px;
  color: var(--mz-ink);
}

/* The slot. On every segment but the first, so there are four of them for
   five containers. Centred on the join by pulling it half its own width. */
.mz-card + .mz-card::before {
  content: '';
  position: absolute;
  left: -5px;
  top: 16px;
  bottom: 16px;
  width: 10px;
  background: var(--mz-canvas);
  border-radius: var(--mz-r-pill);
}

.mz-card__num  { color: rgba(20, 16, 14, 0.55); }
.mz-card__text { color: rgba(20, 16, 14, 0.8); font-size: 14px; line-height: 1.45; }

/* Five segments across a 1280px container leave roughly 200px of content
   each, so the title steps down from the block heading to a size that sets
   without breaking mid-word. */
.mz-card .mz-card__title {
  font-size: clamp(18px, 1.7vw, 24px);
  line-height: 1.05;
}

/* ==========================================================================
   3. Marquee - the only full-bleed section
   ==========================================================================
   The track holds the phrase group twice and translates exactly -50%, so the
   second copy lands where the first began and the loop has no seam.
   width: max-content stops the flex track wrapping at the viewport edge.
   ========================================================================== */
.mz-marquee {
  border-block: 1px solid rgba(20, 16, 14, 0.1);
  overflow: hidden;
}

.mz-marquee__track {
  display: flex;
  width: max-content;
  /* 77s, up from 38s (7 Sep 2026), and this is what KEEPS the band the same
     rather than changing it.

     The duration is time-per-loop, not speed: the keyframe translates -50%,
     which is exactly one phrase group, so the group's own width divided by
     this number is the pixels per second on screen. When the copy changed
     from "Brand Builders" to "Trade & Retail Design Solutions" the group went
     from 1668px to 3386px at 1440 - 2.03x - so at 38s the band silently
     doubled in speed: measured 43.9 px/sec before, 89.1 px/sec after.

     77s puts it back. Measured after: 44.0 px/sec at 1440 against 43.9
     before, 26.9 against 27.5 at 861, 26.1 against 26.7 at 390.

     So: if this copy is ever edited again, re-measure the group width and
     scale this number by the same factor, or the band changes pace for no
     reason anybody asked for. */
  animation: mz-marquee 77s linear infinite;
}

.mz-marquee__group { display: flex; flex: none; align-items: center; }

/* The vw term is 4.8, not the 6 originally specified, and the padding is
   tight for the same reason: "Brand Builders" is fourteen characters, and at
   6vw it measures 710px per repetition including its dot, which puts only
   2.03 of them on a 1440px screen. Two and a half repetitions needs each one
   at or under 576px, which this reaches (~556px, 2.59 visible). The 40px
   floor and the 88px ceiling are unchanged - the ceiling simply now takes
   effect above ~1830px rather than ~1470px. */
.mz-marquee__word {
  font-family: var(--mz-display);
  font-weight: 800;
  font-size: clamp(40px, 4.8vw, 88px);
  line-height: 1;
  letter-spacing: -0.03em;
  padding-inline: 12px;
  white-space: nowrap;
}

/* Alternating solid and outlined, so a long run of repeats reads as a rhythm
   rather than one continuous smear of the same word. */
.mz-marquee__word--hollow {
  color: transparent;
  -webkit-text-stroke: 1.5px rgba(20, 16, 14, 0.55);
}

.mz-marquee__dot {
  flex: none;
  width: 14px;
  aspect-ratio: 1;
  border-radius: 50%;
  background: var(--mz-orange);
}

@keyframes mz-marquee {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-50%, 0, 0); }
}

/* ==========================================================================
   4. Feature compositions
   ========================================================================== */
.mz-feature {
  display: grid;
  grid-template-columns: 4fr 6fr;
  gap: 56px;
  align-items: center;
}

.mz-feature + .mz-feature { margin-top: 120px; }

.mz-feature--flip { grid-template-columns: 6fr 4fr; }
.mz-feature--flip .mz-feature__text   { order: 2; }
.mz-feature--flip .mz-feature__visual { order: 1; }

.mz-feature__text {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 24px;
}

.mz-feature__visual {
  border-radius: var(--mz-r-card);
  overflow: hidden;
  aspect-ratio: 5 / 4;
  background: var(--mz-canvas);
}

.mz-feature__media { width: 100%; height: 100%; object-fit: cover; display: block; }

/* ==========================================================================
   5. Tag row
   ==========================================================================
   All that is left of the "Our work" grid (removed 7 Sep 2026, client - see
   section 6 of divisions/design.html). The tag row outlived it because the
   four feature blocks each carry one, so the class keeps its __work-item__
   name rather than being renamed across five call sites for tidiness.

   Deleted with the grid: .mz-work, its radial-gradient dot field,
   .mz-work__inner, .mz-work__grid, .mz-work-item, .mz-work-item__media,
   .mz-thumb, its hover scale and .mz-work-item__text, plus the two overrides
   those had in the responsive and reduced-motion blocks below. Confirmed dead
   by two independent routes before removal: no occurrence of any of them in
   any HTML or JS in the repo, and zero matching elements in the rendered DOM
   at all nine test widths after the markup change.

   .mz-tag itself is section 1 furniture, not part of this.
   ========================================================================== */
.mz-work-item__tags { display: flex; flex-wrap: wrap; gap: 8px; }

/* ==========================================================================
   6. Team band (7 Sep 2026, client)
   ==========================================================================
   The page carries the shared .crew component from style.css rather than a
   Mezzo-specific band, because the brief was "the same design as the other
   pages". Everything that makes it work - the navy ground, the dimmed
   backdrop, the clip-path wipes, the IntersectionObserver that arms them -
   is already in css/style.css and js/main.js, both of which this page loads.
   Neither was touched for this.

   These two rules are the only adjustments, and both are scoped under
   .mz-main so they can never reach the four division pages that use the
   same component.
   ========================================================================== */

/* `.mz-main > section` sets padding-block: var(--mz-rhythm), and at one class
   plus one type (0,1,1) it outranks .crew's own `padding` shorthand (0,1,0).
   Left alone it flattens the band's deep top and bottom to the page's
   60px section rhythm - measured 60px against the component's 128px at
   1440px wide. This restores the component's value, one class deeper.
   padding-inline is not restated: the shorthand's --crew-gap side padding is
   never overridden, because `.mz-main > section` only sets the block axis. */
.mz-main > .crew {
  padding-block: clamp(56px, 8vw, 128px);
}

/* Two portraits, where the other pages carry one, three or six.
   .crew__row is a three-column grid, so a pair would sit in columns one and
   two and leave the third as a hole in the row.

   Capped and centred for the same reason .crew__row--six is: two cells across
   the full band would each be ~700px wide at 1440, covering a 900x1000
   portrait source - a far heavier crop than the three-column pages take. At
   920px each cell is ~448px, which is the shape the other bands' cells hold.
   min() is not needed on the cap because the mobile rule below takes over
   long before 920px is the screen. */
.mz-main .mz-crew-pair {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  max-width: 920px;
  margin-inline: auto;
}

/* The supplied portraits are 1122x1402, exactly 4:5, so the cells take their
   height from that ratio instead of covering the component's default
   fixed-height box - which is sized for a taller crop and would cut the sides
   off a head-and-shoulders frame. Nothing is cropped by CSS at any width.
   Two classes deep, so it beats the base .crew__cell img and its 760px
   override wherever those sit in style.css. */
.mz-main .mz-crew-pair .crew__cell img {
  height: auto;
  aspect-ratio: 4 / 5;
}

@media (max-width: 760px) {
  /* style.css stacks .crew__row to a single column here, but that rule is one
     class (0,1,0) and the pair rule above is two, so without this the desktop
     pair survives onto a phone as two ~170px slivers. Restated at matching
     specificity, and after it in source order. */
  .mz-main .mz-crew-pair { grid-template-columns: minmax(0, 1fr); }
}

/* The shared footer is untouched; it is simply given the same rhythm as
   everything else on this page. Adjacent sibling, so it applies here only. */
.mz-main + .site-footer { padding-top: var(--mz-rhythm); }

/* ==========================================================================
   Motion
   ==========================================================================
   Initial states apply only under html.mz-motion, which js/mezzo-design.js
   confirms before first paint - the same pattern as .navpage-js in
   style.css. Without JS, without GSAP, or under reduced motion the class is
   never added and every element below is simply visible.
   ========================================================================== */
/* :not(.mz-shown) is load-bearing. Without it the hidden state keeps
   matching after the reveal has played, and the moment js/mezzo-design.js
   clears the inline transform GSAP was animating, the element is handed
   straight back to translate3d(0, 24px, 0) and visibly drops. The script
   adds .mz-shown as each element finishes, which stops these rules matching
   for good. */
.mz-motion [data-mz-reveal]:not(.mz-shown) {
  opacity: 0;
  transform: translate3d(0, 24px, 0);
}

.mz-motion [data-mz-reveal='visual']:not(.mz-shown) {
  transform: translate3d(0, 32px, 0) scale(0.97);
}

/* The hero fades in and does not move. A vertical offset there has to be
   unwound to exactly zero, and if the tween stalls, is interrupted, or the
   inline transform is cleared while this rule still matches, the headline
   settles in the wrong place - which is the "text jumps and falls down on
   load" the client kept seeing. Opacity has no such failure mode: whatever
   happens to the tween, the type is where the layout put it. The scroll
   reveals below the fold keep their small rise; they are never cleared. */
.mz-motion [data-mz-hero] [data-mz-reveal]:not(.mz-shown) {
  transform: none;
}

/* The two notches can never be given a hidden initial state, whatever anyone
   marks them up with (6 Sep 2026).

   They are not decoration over the gradient - they ARE the cut-out. Each is a
   page-coloured rectangle whose rounded inner corner, together with the four
   radial-gradient inverse corners, is what gives the plate its shape. An
   opacity of 0 on one of them does not hide a panel; it un-cuts the corner and
   shows the gradient straight through, and then the visitor watches a
   rectangle fade up to the colour of the page.

   That is exactly what shipped: data-mz-reveal was on .mz-hero__notch--br, so
   it measured 0.000 for the first 1.49s and did not reach full until 2.06s.
   The reveal now lives on the lockup and the button inside it. This rule is
   the guard - it outranks the hidden state above, so putting the attribute
   back on a panel changes nothing. Mask first, content second. */
.mz-motion [data-mz-hero] .mz-hero__notch[data-mz-reveal]:not(.mz-shown) {
  opacity: 1;
}

/* --------------------------------------------------------------------------
   The hero's three arrivals (6 Sep 2026)
   --------------------------------------------------------------------------
   Every initial state below is scoped to html.mz-motion, so the whole hero is
   simply already there under prefers-reduced-motion, with no JavaScript, or if
   the CDN fails to deliver GSAP. Nothing here can strand content.
   -------------------------------------------------------------------------- */

/* 1. The burst, and it is a CSS animation rather than a GSAP tween on purpose
   (rewritten 6 Sep 2026).

   It was a tween, and that was wrong for one reason: the hidden state is CSS,
   so it applies at first paint, while GSAP arrives from a CDN. Everything in
   between was a hero with no rectangle in it - bare type on linen for as long
   as the network took. A CSS animation starts when the element is first
   painted, so the plate is opening in the first frame the visitor sees and
   there is no window where the composition is missing.

   ellipse() rather than circle() because the plate is far wider than it is
   tall - a circle big enough to clear the left and right edges would have
   swept past the top and bottom long before, so the corners would arrive
   first and the shape would not read as opening from the middle.

   75% clears the corners at every aspect the plate takes: at the far corner
   (50%, 50%) from the centre, (50/75)^2 + (50/75)^2 = 0.89, inside the
   ellipse. forwards holds the open state after it finishes. */
.mz-motion .mz-hero__plate {
  animation: mz-burst 1.05s cubic-bezier(0.22, 0.61, 0.36, 1) both;
}
@keyframes mz-burst {
  from { clip-path: ellipse(0% 0% at 50% 50%); }
  to   { clip-path: ellipse(75% 75% at 50% 50%); }
}

/* The gradient arrives a beat behind the shape, and this is what makes the
   burst read as a burst.

   The clip reveals whatever is under it, and what was under it was the
   gradient - whose centre is bright orange and whose edges are near-black. So
   the thing seen growing was the gradient's own organic shape, an orange blob
   that only resolved into a rectangle in the last third. The Menu button does
   not have this problem because it expands a SOLID fill: the shape itself is
   the thing you see.

   Holding the field back for a quarter of a second gives the ellipse the
   plate's flat near-black ground to carry instead, so what opens out of the
   centre is a solid shape with a crisp edge - the same read as the menu - and
   the colour blooms into it as it settles. */
.mz-motion .mz-field {
  animation: mz-field-in 0.85s ease-out 0.3s both;
}
@keyframes mz-field-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* The ring runs slightly ahead of the fill and fades as it goes, so what is
   seen is an edge opening out rather than a hoop parked on the plate. Sized in
   vmax so it always clears the frame without JavaScript having to measure it. */
.mz-motion .mz-hero__ring {
  animation: mz-ring 1.15s cubic-bezier(0.22, 0.61, 0.36, 1) both;
}
@keyframes mz-ring {
  from   { width: 0; height: 0; opacity: 1; }
  55%    { opacity: 0.85; }
  to     { width: 190vmax; height: 165vmax; opacity: 0; }
}

/* 2. The typewriter, armed hidden before it can ever be painted finished.

   This rule is the whole difference between a reveal and a flicker, and the
   site has been here before: see the note this replaces in css/style.css's
   history, where the Import & Export heading was "readable at 278ms, wiped at
   762ms, still spelling itself out past 1.3s". Splitting can only happen once
   the script runs, which is after the headline has painted - so without an
   armed state the visitor reads the finished line, watches it be destroyed,
   and then watches it typed back. Measured here before this rule existed:
   fully readable at 82ms, cleared to a bare caret by 577ms.

   visibility, not display or opacity on the parent: the headline keeps its
   box, so the cut-out is sized by the finished text from the first frame and
   nothing moves when the characters arrive. The script clears this the moment
   it has split the line - by which point the characters themselves are what
   is hidden - and every path that cannot type unhides it instead: no
   JavaScript at all, reduced motion, or the inline failsafe below. */
.mz-motion [data-mz-type]:not(.mz-typing):not(.mz-shown) {
  visibility: hidden;
}

/* Characters are split into spans at run time and revealed
   one at a time by opacity - never by appending text, which would re-lay the
   headline out on every character and grow the cut-out as it typed. Split once
   and the line occupies its final space from the first frame.

   .mz-type-word keeps a word's characters together so nothing can re-wrap
   mid-word as it fills in; the spaces between words are ordinary spaces, so
   the headline wraps into exactly the lines it finishes on. */
.mz-type-word { white-space: nowrap; }

.mz-type-caret {
  position: relative;
  display: inline-block;
  width: 0;
  height: 0.78em;
  vertical-align: -0.06em;
  animation: mz-type-blink 0.85s steps(1, end) infinite;
}
.mz-type-caret::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 0.05em;
  width: 0.045em;
  min-width: 2px;
  background: var(--mz-orange);
}
@keyframes mz-type-blink {
  0%, 50% { opacity: 1; }
  50.01%, 100% { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .mz-type-caret { display: none; }
}

/* 3. The lockup, wiped in from the left. A mask rather than a slide or a
   fade: the logo never moves and never sits at a half-opacity that would
   dull the wordmark, it is simply uncovered. The soft 16% band is what makes
   it read as a fade passing across rather than a hard edge sweeping. */
.mz-hero__logo {
  -webkit-mask-image: linear-gradient(90deg, #000 var(--mz-wipe, 100%),
                                      transparent calc(var(--mz-wipe, 100%) + 16%));
  mask-image: linear-gradient(90deg, #000 var(--mz-wipe, 100%),
                              transparent calc(var(--mz-wipe, 100%) + 16%));
}
/* CSS, for the same reason as the burst: it starts at first paint instead of
   waiting on the CDN. @property is what makes --mz-wipe animate at all - a
   custom property is a string to the animation engine unless it has been
   given a type, so without this the mask would jump from -16% to 100% in one
   step rather than sweeping. Browsers without @property (Firefox before 128)
   simply get that jump, which is a logo that appears rather than wipes: the
   fallback is the finished state, never a missing one. */
@property --mz-wipe {
  syntax: '<percentage>';
  inherits: false;
  initial-value: 100%;
}
.mz-motion .mz-hero__logo {
  animation: mz-wipe 1s cubic-bezier(0.45, 0, 0.25, 1) 0.45s both;
}
@keyframes mz-wipe {
  from { --mz-wipe: -16%; }
  to   { --mz-wipe: 100%; }
}

/* ==========================================================================
   Responsive
   ========================================================================== */
@media (max-width: 1024px) {
  /* Five 200px segments stop being readable below this, so the bar turns on
     its side: same single linked shape, stacked, with the slot running
     horizontally between segments instead of vertically.

     Grid rather than a column flexbox, and grid-auto-rows: 1fr is the whole
     reason (4 Sep 2026). The brief is that the five containers are all the
     same size. Across the row they are, because flex: 1 1 0 divides the main
     axis evenly - but rotate the main axis and that same declaration makes
     each segment as tall as its own text instead, so the five went unequal
     the moment one deliverable wrapped to a different number of lines.
     Measured stacked: equal at 1024-900px, then 158/179px at 860px and
     158/179/199px at 500px - three different heights in one bar. 1fr rows
     size every segment to the tallest, so the shape stays regular all the
     way down. */
  .mz-services__grid {
    display: grid;
    grid-auto-flow: row;
    grid-auto-rows: 1fr;
  }

  .mz-card + .mz-card::before {
    left: 16px;
    right: 16px;
    top: -5px;
    bottom: auto;
    width: auto;
    height: 10px;
  }
}

@media (max-width: 860px) {
  :root { --mz-rhythm: 36px; }   /* 72px between sections on mobile */

  /* The hero keeps the desktop FORM on phones (7 Sep 2026, client): one
     landscape shape with the headline cut out of its top-left corner and the
     lockup out of its bottom-right, scaled down - not the full-width
     headline / band / lockup stack this used to become.

     The previous note here said the cut-outs "cannot hold their proportions at
     this width - 56% of a 310px shape is not a headline column", and that was
     true of the proportions as they stood. What makes the corners work is
     giving up two things instead of the form: the shape stops being a
     full-screen portrait panel and turns landscape, and everything inside it
     comes down a size. The panels are then wide enough to hold their content
     and there is still gradient left between them.

     Not the desktop's literal 2.1:1 though, and this is the one place the
     instruction cannot be followed to the letter: at 350px wide that ratio is
     a 167px-tall shape, and the headline needs ~70px of it while the lockup
     and its button need ~110px. There is no gradient left between them - the
     two cut-outs meet and the form collapses. 1.5:1 is the tightest that still
     reads as the reference. */
  .mz-hero {
    /* No longer a screen-height hero: the shape sets the height. */
    min-height: auto;
    /* 20px, not the 40px gutter - at this width every pixel taken off the side
       is a pixel of headline column, and the shape is the page here. */
    --mz-pad: 20px;
    /* 40px of radius on a 233px-tall shape is a quarter of it. The token is
       scoped to the hero so the cards further down the page keep theirs. */
    --mz-r-card: 22px;
  }

  .mz-hero__frame {
    flex: none;
    width: 100%;
    aspect-ratio: 1.5;
  }

  /* Wider than the desktop's 58%/36% because the content inside is no longer
     shrinking with the shape - a 16px button and a lockup have a floor. */
  .mz-hero__notch--tl {
    width: 74%;
    min-height: 30%;
    padding: 0 20px 14px 0;
  }

  .mz-hero__notch--br {
    width: 62%;
    min-height: 40%;
    gap: 12px;
    padding: 18px;
  }

  /* Down from the 40px floor: at 40px "Marketing" is 216px and will not fit a
     74% column of a 350px shape, so it wrapped onto the gradient. */
  .mz-hero .mz-display--hero {
    font-size: clamp(20px, 7.2vw, 34px);
  }

  .mz-hero__logo { width: min(160px, 100%); }

  .mz-hero .mz-btn {
    padding: 11px 20px;
    font-size: 14px;
    gap: 7px;
  }


  .mz-feature,
  .mz-feature--flip { grid-template-columns: 1fr; gap: 32px; }
  .mz-feature--flip .mz-feature__text   { order: 0; }
  .mz-feature--flip .mz-feature__visual { order: 0; }
  .mz-feature + .mz-feature { margin-top: 72px; }

  /* The 32px eyebrow-to-heading and 56px heading-to-content gaps are NOT
     reduced here: only the between-section rhythm has a mobile value. */
}

/* ==========================================================================
   Reduced motion
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  .mz-field__blob,
  .mz-field__wave,
  .mz-marquee__track { animation: none; }

  .mz-btn:hover,
  .mz-btn:focus-visible,
  .mz-card:hover { transform: none; }
}
