/* =============================================================================
   Applied Industrial Technologies — Global Styles
   Canvas: 3840 × 2160px (fixed, no responsiveness)
   =============================================================================

   TABLE OF CONTENTS
   -----------------
   1.  Design Tokens & Global Reset
   2.  HomeScreen — Layout
   3.  HomeScreen — Hero Image
   4.  HomeScreen — Text & Buttons
   5.  HomeScreen — Grey Background Polygons
   6.  HomeScreen — Teal Accent Shapes (bottom)
   7.  HomeScreen — Hero Teal Overlay & White Lines
   8.  SectionView — Layout
   9.  SectionView — Grey Background Polygons
   10. SectionView — Teal Accent Shapes (bottom)
   11. NavBar
   12. HamburgerMenu
   13. Animations (@keyframes + animation rules)
   ============================================================================= */


/* =============================================================================
   1. DESIGN TOKENS & GLOBAL RESET
   ============================================================================= */

:root {
  /* Brand colours */
  --teal:        #007B84;
  --teal-vivid:  #00BCC8;
  --navy:        #173542;
  --dir-bg:      #201B51;   /* hamburger menu header */
  --bg:          #ecf1f3;   /* section view background */
  --white:       #FFFFFF;
  --text-dark:   #173542;

  /* Typography */
  --font: 'din-2014', 'DIN 2014', 'Barlow', sans-serif;

  /* -- Animation speed controls -----------------------------------------------
     Adjust these values to tune all animation timings in one place.
     --------------------------------------------------------------------------- */
  --anim-fade-dur:            2s;      /* title & subtitle fade-in-up duration */
  --anim-fade-stagger:        0.18s;   /* subtitle delay after title */
  --anim-header-dur:          1.2s;    /* section-view header fade-in duration */
  --anim-card-dur:            1s;      /* each card fade-in duration */
  --anim-card-offset:         0.10s;   /* delay before the first card animates */
  --anim-card-stagger:        0.10s;   /* additional delay per card */
  --anim-arrow-period:        1.5s;    /* arrow nudge loop period (idle) */
  --anim-arrow-period-hover:  1.5s;    /* arrow nudge loop period (hover) */
  --anim-arrow-initial-delay: 0s;      /* pause before arrow first nudges */
  --anim-nav-color-dur:       0.8s;    /* navbar icon bg colour cross-fade */
  --anim-nav-svg-dur:         0.2s;    /* navbar svg filter colour snap */
  --anim-nav-icon-dur:        1.5s;    /* navbar icon scale-up on select */
  --anim-menu-slide-dur:      0.45s;   /* hamburger panel slide in/out */
  --anim-menu-overlay-dur:    0.35s;   /* hamburger backdrop fade */
  --anim-home-hero-pan-dur:   8s;      /* home hero image ken-burns pan */
}

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

html, body {
  width: 100%;
  height: 100%;
  overflow: hidden;
  background: #000;
  display: flex;
  align-items: center;
  justify-content: center;
}

#root {
  width: 3840px;
  height: 2160px;
  flex-shrink: 0;
  overflow: hidden;
  position: relative;
  font-family: var(--font);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* Web build only: ViewportFit sets a scale() transform here to fit the fixed
     3840x2160 canvas into the browser viewport. Electron never sets this, so
     transform stays the initial value (none) there. Must stay `none` while the
     viewport warning is showing, or it becomes a containing block for the
     warning's `position: fixed` overlay and anchors it to this box instead of
     the real viewport. */
  transform-origin: center center;
}

/* Full-screen image-preload gate, shown in place of the routed app until every
   image referenced by the fetched Sanity dataset has settled (loaded or
   errored). Sized to the fixed canvas, not the viewport — same convention as
   the rest of the app (see #root above). Reuses one of the SectionView page
   backdrops (`.section-root`, `.pdf-root` does the same) instead of a solid
   colour, so the loading bar sits over the same grey-polygon / teal-accent
   pattern used on the section pages. */
.preloader-root {
  align-items: center;
  justify-content: center;
}

.preloader-content {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 40px;
}

.preloader-bar-track {
  width: 1400px;
  height: 32px;
  border-radius: 30px;
  background: var(--white);
  overflow: hidden;
}

.preloader-bar-fill {
  height: 100%;
  border-radius: 30px;
  background: var(--teal);
  transition: width 0.2s ease-out;
}

.preloader-percent {
  font-family: var(--font);
  font-size: 32px;
  color: var(--navy);
}


/* =============================================================================
   2. HOMESCREEN -- LAYOUT
   ============================================================================= */

.home-root {
  position: relative;
  width: 3840px;
  height: 2160px;
  background: #E4EAED;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

/* Full-canvas layer that holds the hero image and all decorative geometry */
.home-bg {
  position: absolute;
  inset: 0;
  z-index: 5;
  pointer-events: none;
}

/* Main content column (title + buttons); sits above all background layers */
.home-main {
  position: relative;
  z-index: 10;
  flex: 1 1 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-left: 200px;
  padding-right: 200px;
  padding-top: 150px;
  padding-bottom: 150px;
  gap: 125px;
}

.home-navbar {
  position: relative;
  z-index: 10;
  flex-shrink: 0;
}

/* Cropped "A" logo mark -- top right corner */
.home-logo-mark {
  position: absolute;
  top: 90px;
  right: 150px;
  width: 200px;
  height: auto;
  z-index: 20;
  pointer-events: none;
  user-select: none;
}


/* =============================================================================
   3. HOMESCREEN -- HERO IMAGE
   Notes on the white-line technique:
   - drop-shadow on a WRAPPER traces the clip-path of the clipped child.
   - box-shadow on the parent ignores clip-path, so we use the two-div pattern.
   - filter is applied to the wrap; clip-path lives on the inner div.
   ============================================================================= */

/* Wrapper: apply white edge line + dark shadow AFTER clip-path renders */
.home-bg-hero-shape-wrap {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  filter:
    drop-shadow(0 0 1px #fff)
    drop-shadow(2px 3px 5px rgba(0, 0, 0, 0.22));
  pointer-events: none;
}

/* Inner div: clips the hero photo into a diagonal right-side trapezoid */
.home-bg-hero-shape {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  clip-path: polygon(90% 0%, 100% 0%, 100% 100%, 60% 100%);
  overflow: hidden;
}

/* Vignette depth inside the clipped hero */
.home-bg-hero-shape::before {
  content: '';
  position: absolute;
  inset: 0;
  box-shadow: inset 0 0 80px rgba(23, 53, 66, 0.28), inset 0 0 240px rgba(0, 0, 0, 0.18);
  pointer-events: none;
  z-index: 2;
}

/* Subtle tonal gradient for depth */
.home-bg-hero-shape::after {
  content: '';
  position: absolute;
  inset: 0;
  clip-path: inherit;
  background: linear-gradient(160deg, rgba(255,255,255,0.10) 0%, rgba(255,255,255,0) 40%, rgba(23,53,66,0.12) 100%);
  pointer-events: none;
  z-index: 2;
}

/* Hero image: slightly oversized so ken-burns pan does not reveal edges */
.home-bg-hero {
  position: absolute;
  top: 0;
  left: 0;
  width: 106%;
  height: 106%;
  transform: translate3d(-1.5%, -1%, 0);
  object-fit: cover;
  animation: homeHeroPan var(--anim-home-hero-pan-dur) ease-in-out both;
  will-change: transform;
  user-select: none;
  z-index: 2;
}

/* Previous image layer -- stays visible while next image fades in */
.home-bg-hero--prev {
  animation: none;
  opacity: 1;
  z-index: 1;
  transform: translate3d(-1.5%, -1%, 0);
}

/* Fade gradient: bleeds left background colour into photo so text stays readable */
.home-bg-gradient {
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg,
    rgba(228, 234, 237, 0.92) 0%,
    rgba(228, 234, 237, 0.72) 26%,
    rgba(228, 234, 237, 0) 58%
  );
}


/* =============================================================================
   4. HOMESCREEN -- TEXT & BUTTONS
   ============================================================================= */

.home-title-block {
  display: flex;
  flex-direction: column;
  gap: 35px;
  /* Fixed px, not 70vw: `vw` resolves against the real browser viewport, not
     the 3840px design canvas — harmless in Electron only because its window
     is always created at exactly 3840 wide, so vw and px coincided. The web
     build scales the canvas via CSS transform, which doesn't affect what vw
     resolves against, so vw would collapse this column on any smaller
     viewport. 70vw of 3840px. */
  max-width: 2688px;
}

/* min-height reserves 2-line space so buttons never shift on single-line titles.
   align-items: flex-end pins text to the bottom of that reserved space. */
.home-title {
  font-family: var(--font);
  font-size: 207px;
  font-weight: 700;
  color: var(--teal);
  text-transform: uppercase;
  line-height: 1.1;
  letter-spacing: -0.01em;
  transition: opacity 0.4s ease;
  min-height: calc(207px * 1.1 * 2);
  display: flex;
  align-items: flex-end;
}

/* Sized so the three-line CMS copy fits the space left by the nav bar: the main
   column gets 2160 - 558 = 1602px, and everything but this paragraph accounts for
   1385px of it. 56px/1.25 gives 3 x 70px = 210px and lands the nav bar at exactly
   1602px, matching .section-bottom-bar on the body pages. At 64px the same copy ran
   to 240px and pushed the nav bar 23px below its counterpart.
   height (not min-height) + overflow reserves that box unconditionally, so longer
   copy clips instead of shifting the buttons or the nav bar. A fourth line starts
   around 330 characters and is dropped rather than reflowed; the longest current
   subtitle is 260, so there is roughly one short sentence of headroom. */
.home-subtitle {
  font-family: var(--font);
  font-size: 56px;
  font-weight: 400;
  color: var(--navy);
  line-height: 1.25;
  height: 210px;
  overflow: hidden;
}

.home-btn-block {
  display: flex;
  flex-direction: column;
  gap: 100px;
}

.home-btn-learn {
  width: fit-content;
  padding-left: 100px;
  padding-right: 100px;
  padding-top: 50px;
  padding-bottom: 50px;
  background: var(--white);
  border-radius: 50px;
  border: 5px solid var(--teal);
  display: flex;
  align-items: center;
  gap: 50px;
  cursor: pointer;
  outline: none;
  transition: background 0.2s ease;
}
.home-btn-learn-label {
  font-family: var(--font);
  font-size: 96px;
  font-weight: 700;
  color: var(--navy);
  text-transform: uppercase;
  letter-spacing: 0.02em;
}


.home-btn-tertiary {
  width: 722px;
  height: 150px;
  padding-top: 16px;
  padding-bottom: 16px;
  border-radius: 30px;
  border: 4px solid var(--teal);
  background: rgba(255,255,255,0.50);
  backdrop-filter: blur(15px);
  -webkit-backdrop-filter: blur(15px);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  outline: none;
}
.home-btn-tertiary-label {
  font-family: var(--font);
  font-size: 64px;
  font-weight: 400;
  color: var(--navy);
}


/* =============================================================================
   5. HOMESCREEN -- GREY BACKGROUND POLYGONS
   Large angular shapes that sit behind the photo and text, creating tonal
   variation. White edge lines with dark shadows separate the grey tones.
   Polygon edges are kept away from the text region (left 40%, vertical centre).
   ============================================================================= */

.home-grey-shapes {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}

/* Each wrap applies white edge line + dark shadow via drop-shadow,
   which correctly traces the clip-path of the child div. */
.home-grey-wrap {
  position: absolute;
  inset: 0;
  filter:
    drop-shadow(0 0 1px #fff)
    drop-shadow(2px 3px 5px rgba(0, 0, 0, 0.18));
}

.home-grey {
  position: absolute;
  inset: 0;
}

/* Three parallel diagonal bands sharing the same -30%/100% slope as the hero
   diagonal (90% 0% → 60% 100%). Each band's right edge is the next band's left
   edge so they tile flush with no overlap and no gap. */

/* Darkest grey -- rightmost band, anchors against the right edge */
.home-grey-1 {
  background: #CCD4D9;
  clip-path: polygon(39% 0%, 100% 0%, 100% 100%, 0% 100%, 0% 15%);
}

/* Mid grey -- middle band, shares left edge of grey-1 */
.home-grey-2 {
  background: #D5DCE0;
  clip-path: polygon(100% 32%, 100% 100%, 0% 100%, 0% 69%);
}

/* Light grey -- leftmost band, shares left edge of grey-2 */
.home-grey-3 {
  background: #DDE3E6;
  clip-path: polygon(0% 0%,39% 0%, 0% 15%);
}


/* =============================================================================
   6. HOMESCREEN -- TEAL ACCENT SHAPES (bottom)
   Compact angular slivers at the bottom of the screen. Solid colour only --
   no white separator lines between teal shapes per design spec.
   ============================================================================= */

.home-bottom-shapes {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 3840px;
  height: 750px;
  z-index: 6;  /* above home-bg (z-index:5) so gradient doesn't cover teal shapes */
  pointer-events: none;
}

/* No filter on teal wrappers -- teal shapes have no white separator lines */
.home-bshape-wrap {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

.home-bshape {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

/* Inset vignette adds edge depth without a gradient overlay */
.home-bshape-1::before,
.home-bshape-2::before {
 display: none;
}

/* Gradient overlay disabled -- solid colours only */
.home-bshape-1::after,
.home-bshape-2::after {
  display: none;
}

.home-bshape-1 {
  background: #99CED1;
  clip-path: polygon(0% 40%, 20% 60%, 65% 100%, 0% 100%);
}

.home-bshape-2 {
  background: #4DA8AF;
  clip-path: polygon(25% 64.5%, 55% 90%, 65% 100%, 10% 100%);
}


/* =============================================================================
   7. HOMESCREEN -- HERO TEAL OVERLAY & WHITE TEXTURE LINES
   Teal band overlaid on part of the hero image:
   - Outer edge aligned exactly to the hero clip-path diagonal (90% 0% to 60% 100%)
   - Inner edge running parallel ~6% inward
   - Interior white line tracing that inner edge
   - Two angled white texture lines crossing over the image for added texture
   ============================================================================= */

/* Teal band: 1.5% wide, both edges follow the same -30%/100% slope.
   Gradient fades from dark teal at the top of the canvas to fully transparent
   at the bottom, running along the band's own diagonal axis. */
.home-hero-teal-overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: linear-gradient(
    162deg,
    rgba(0, 85, 92, 0.72) 0%,
    rgba(0, 123, 132, 0.18) 55%,
    rgba(0, 123, 132, 0) 100%
  );
  clip-path: polygon(90% 0%, 91.5% 0%, 61.5% 100%, 60% 100%);
  z-index: 3;
  pointer-events: none;
}

/* Wrapper for the interior white line */
.home-hero-teal-inner-line {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 4;
  pointer-events: none;
}
.home-hero-teal-inner-line::after {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  /* Thin white line at the right/outer edge of the teal overlay (91.5% top → 61.5% bottom).
     0.06% horizontal span ≈ 2px perpendicular at this angle on a 3840px canvas. */
  clip-path: polygon(91.47% 0%, 91.53% 0%, 61.47% 100%, 61.53% 100%);
  background: rgba(255, 255, 255, 0.7);
}

/* White texture lines -- plain lines crossing over the image, no shadow */
.home-texture-line {
  position: absolute;
  background: rgba(255, 255, 255, 0.55);
  height: 2px;
  pointer-events: none;
  z-index: 6;
  transform-origin: 0 0;
}
.home-texture-line-1 {
  top: 320px;
  left: 0px;
  width: 3840px;
  transform: rotate(-12deg);
}
.home-texture-line-2 {
  top: 1500px;
  left: 0px;
  width: 4000px;
  transform: rotate(-12deg);
}


/* =============================================================================
   8. SECTIONVIEW -- LAYOUT
   ============================================================================= */

/* Texture lines -- independent copies of the home page lines so they can be
   repositioned without affecting the home page. */
.section-texture-line {
  position: absolute;
  background: rgba(255, 255, 255, 0.55);
  height: 2px;
  pointer-events: none;
  z-index: 1;
  transform-origin: 0 0;
}
.section-texture-line-1 {
  left: 0px;
  width: 3840px;
  transform: rotate(-12deg);
}
.section-texture-line-2 {
  left: 0px;
  width: 4000px;
  transform: rotate(-12deg);
}

/* Page 1 — texture line positions */
.section-root[data-page="1"] .section-texture-line-1 { top: 760px; }
.section-root[data-page="1"] .section-texture-line-2 { top: 1500px; }

/* Page 2 — texture line positions */
.section-root[data-page="2"] .section-texture-line-1 { top: 450px; }
.section-root[data-page="2"] .section-texture-line-2 { top: 1280px; }

/* Page 3 — texture line positions */
.section-root[data-page="3"] .section-texture-line-1 { top: 920px; }
.section-root[data-page="3"] .section-texture-line-2 { top: 1660px; }

/* Page 4 — texture line positions
   Every [data-page="N"] group in this file must cover the highest page number any
   section carries, or that page renders with no backdrop at all: the grey slabs get
   no background (invisible) and both texture lines fall back to top:auto → 0. */
.section-root[data-page="4"] .section-texture-line-1 { top: 620px; }
.section-root[data-page="4"] .section-texture-line-2 { top: 1420px; }

.section-root {
  position: relative;
  width: 3840px;
  height: 2160px;
  background: #E4EAED;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* Header bar: icon + section title, z-index above geometry */
.section-header {
  position: relative;
  z-index: 2;
  width: 3840px;
  height: 380px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-left: 150px;
  padding-right: 150px;
  background: transparent;
}
.section-header-left {
  display: flex;
  align-items: center;
  gap: 60px;
}
.section-header-icon {
  width: 171px;
  height: 171px;
  border-radius: 30px;
  background: transparent;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.section-header-img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}
.section-header-title {
  font-family: var(--font);
  font-size: 128px;
  font-weight: 700;
  color: var(--navy);
  text-transform: uppercase;
  letter-spacing: -0.01em;
  line-height: 1.1;
  /* The title has no width limit of its own, so a long enough page title
     runs directly under `.section-logo-mark` (position:absolute, outside
     this flex flow, fixed at right:150px width:200px) — found while
     verifying Automation 7_2's new "Next-Generation Automation Supplies &
     Solutions" title (48 chars) against .section-header's own layout
     (150px padding, 171px icon, 60px gap): available width before the logo
     is ~3069px, comfortably under shorter titles like "Where Innovation
     Meets Automation" but not this one. Clip with an ellipsis rather than
     overlap illegibly — same "visible failure over silent overlap"
     preference as this app's other overflow handling. SectionView.tsx now
     shrinks an overlong title's font-size first (128px down to 80px, issues
     #90 and #108); the ellipsis only shows for a title that still won't fit
     at 80px. The 128px here is the starting size that logic overrides inline. */
  max-width: 3069px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* ℠ drawn as DIN letters (SectionView.tsx renderTitle, issue #118). Sized and
   placed like DIN's own ™ glyph (as in the home screen's "We Are Applied™"):
   top-aligned to the title's cap height, regular weight. At 0.35em its caps
   are ~31px in a 128px title, against ~27px for the ™ at 64px. DIN caps are
   0.69em tall, so lifting the mark's baseline by 0.69 × (1 − 0.35) / 0.35 of
   its own em puts its cap line on the title's. `position: relative` moves it
   without growing the line box, so the header's vertical centring is
   unchanged and the em-based values follow the shrink-to-fit size. */
.section-header-title-mark {
  font-size: 0.35em;
  font-weight: 400;
  position: relative;
  top: -1.281em;
}
.section-header-logo {
  display: none;
}

/* "A" mark — same image and size as the home page, vertically centred in the 380px header */
.section-logo-mark {
  position: absolute;
  top: 90px;
  right: 150px;
  width: 200px;
  height: auto;
  z-index: 20;
  pointer-events: none;
  user-select: none;
}

/* 3x2 card grid */
.section-card-grid {
  position: relative;
  z-index: 2;
  width: 3840px;
  flex: 1 1 0;
  padding-left: 150px;
  padding-right: 150px;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(2, 1fr);
  gap: 50px;
  align-items: stretch;
}
.section-card {
  background: var(--white);
  border-radius: 30px;
  padding: 50px 50px;
  display: flex;
  flex-direction: column;
  gap: 40px;
}
.section-card-title {
  font-family: var(--font);
  font-size: 64px;
  font-weight: 700;
  color: var(--navy);
  text-transform: uppercase;
  line-height: 1.15;
}
.section-card-desc {
  font-family: var(--font);
  font-size: 48px;
  font-weight: 400;
  color: var(--navy);
  line-height: 1.5;
}
.section-card-placeholder {
  flex: 1 1 0;
  background: #D5DCE0;
  border-radius: 20px;
  border: 3px dashed rgba(23, 53, 66, 0.3);
}

/* ════════════════════════════════════════════════════
   PAGE BODY — unique per-page layout system
   ════════════════════════════════════════════════════ */

/* Container — replaces section-card-grid */
.page-body {
  position: relative;
  z-index: 2;
  width: 3840px;
  flex: 1 1 0;
  padding: 0 150px;
  display: flex;
  flex-direction: column;
  gap: 50px;
  min-height: 0;
}

/* ── Layout primitives ── */

.body-row {
  display: flex;
  gap: 50px;
  flex: 1 1 0;
  min-height: 0;
}
.body-row-top { flex: 0 0 auto; }

/* Row-aligned columns (Flow Control 6_2, Case Studies 9_3) — always on for
   the `columns` template, not a CMS toggle: every column here is a repeated
   category comparison (Applications/Process Media/Types, Situation/Solution/
   Savings), so reading across a row is always the point, never staggered
   stacks. Each .fc2-col is a subgrid of this container's row tracks
   (--fc2-cols/--fc2-rows set inline from the real column/section counts, not
   the authored columnCount, so it stays correct if a column count changes),
   so same-index sections land on shared row lines and a column with fewer
   sections (--fc2-span) simply ends early instead of needing its own case.
   Row tracks are `auto`, sized by the tallest cell sharing that line, which
   is what makes headers line up — a short Situation in one column just
   leaves whitespace before the shared Solution line starts. Overflow clips
   rather than scrolls: nothing else in this kiosk scrolls, so a scroll
   container here would hide content behind a gesture nobody is prompted to
   make. Clipping keeps the failure visible to whoever is editing the copy,
   the same call `.home-subtitle` makes. If a columns page clips, shorten the
   copy or drop a type size — do not reintroduce overflow-y. */
.body-row-aligned {
  display: grid;
  /* minmax(0, 1fr), not 1fr: a bare `1fr` floors at min-content, which is the
     grid equivalent of the `min-width: 0` this rule replaced — without it a
     long unbroken token widens its track past its share and overflows. */
  grid-template-columns: repeat(var(--fc2-cols), minmax(0, 1fr));
  grid-template-rows: repeat(var(--fc2-rows), auto);
  align-items: start;
  /* align-content defaults to stretch on a grid container, which inflates
     every `auto` row track to fill leftover vertical space — invisible on
     6_2/9_3 because their bullet lists already nearly fill the card, but it
     opens ugly gaps between short rows (e.g. iconColumns' 3 icon rows). Pack
     rows at their content size instead. */
  align-content: start;
  overflow: hidden;
}
/* iconColumns (Automation 7_2) wants the opposite of the packed default above:
   client feedback 2026-08-28 was that its cards left dead space at the card
   foot instead of filling the fixed-height row. Scoped to this modifier only
   — 6_2/9_3 keep the packed default, since their bullet lists already nearly
   fill the card and re-enabling stretch there risks reopening the exact gap
   this rule was written to close. Growing the `auto` row tracks is enough:
   each `.fc2-icon-row` is itself a grid item of the subgrid, so it stretches
   to its row's new height automatically, and its own `align-items: center`
   centers the icon+label within that taller row. */
.body-row-aligned.body-row-aligned-fill { align-content: stretch; }
/* Case Studies 9_3 client feedback 2026-08-31: its 2-row subgrid (heading +
   .fc2-col-body) inherits `.body-row-aligned-fill`'s align-content:stretch so
   the image band can reach the card foot, but stretch splits the leftover
   height evenly across BOTH auto tracks — inflating the heading track well
   past its content and opening a gap before the text below it. Pin row 1 to
   its content height and let row 2 (1fr) claim all the leftover directly,
   sidestepping stretch's even split. Scoped to VAS3 only — Flow Control
   6_2/6_3's split-column cards were tuned against the shared stretch
   behavior above and are unaffected by this compound selector. */
.body-row-aligned.body-row-aligned-2row { grid-template-rows: min-content 1fr; }
.body-row-aligned > .body-card.fc2-col {
  display: grid;
  grid-template-rows: subgrid;
  grid-row: span var(--fc2-span);
  row-gap: 12px;
}

/* ── Automation page 1 — top icon-card row ── */
.auto1-top-row { align-items: center; }
.auto1-card { justify-content: center; align-items: center; text-align: center; }
.auto1-card .body-card-heading { text-align: center; }
/* Client feedback 2026-08-28: the "Robotic" / "Machine Vision" / "Industry
   4.0" labels read too small next to their 60x60 icon. Scoped to this card
   only — .body-text is the sitewide default used all over the app. */
.auto1-card .body-text { font-size: 64px; }

/* ── columns template sub-sections (Flow Control 6_2, Case Studies 9_3) ── */
.body-card.fc2-col { padding: 32px 40px; }
.body-card.fc2-col .body-card-heading { font-size: 36px; line-height: 1.1; margin-bottom: 16px; }
/* Flow Control 6_2/6_3 only (`.fc2-col-fc`, set in PageBody.tsx off page._id —
   the same gate as `fillCards`). Client feedback 2026-08-29 asked for bigger
   column headings/bullets; 9_3 shares `.fc2-col` but is being restructured
   separately in parallel, so its needs are scoped off instead of bumped here. */
.body-card.fc2-col.fc2-col-fc .body-card-heading { font-size: 44px; }
/* When a column carries an image, the card splits into two internal
   sub-columns — text left, image right — under a full-width heading. Client
   feedback 2026-08-28 rejected the earlier small-thumbnail-beside-heading
   treatment as too cramped; this gives the image real vertical presence next
   to its column's own text instead of competing for space in the header row.
   `.fc2-col-heading` still spans the full card width and sits on the outer
   subgrid's row 1, so headings keep aligning across sibling cards.
   `.fc2-col-body` is the card's single non-spanning row-2 track — deliberately
   NOT a multi-row span; see the render-side comment in PageBody.tsx for two
   different multi-row approaches that both produced real rendering bugs
   (overlapping text, and Chrome under-sizing multiple auto subgrid tracks
   for one spanning item), confirmed by screenshot and a CDP computed-style
   dump before landing this one-row version. Laid out internally with plain
   flex, unrelated to the outer subgrid mechanics. */
.fc2-col-heading { grid-column: 1 / -1; }
.fc2-col-body {
  grid-column: 1 / -1;
  grid-row: 2;
  display: flex;
  gap: 32px;
  min-height: 0;
}
/* Plain block flow, not a flex column: `.body-bullets` sets `flex: 1 1 0;
   min-height: 0` for its normal flex-card contexts, which is exactly what you
   want when one bullet list should grow/shrink to fill leftover space. Inside
   a flex column with 3 sibling lists of very different lengths, that rule
   forces all three to the SAME shrunk height regardless of content — measured
   live at a 9-item list flex-shrunk to 79px, well under its own headings'
   text, spilling into the next section. Block flow makes `flex` inert (only
   applies to flex items) and lets each list take its natural height instead. */
.fc2-col-text {
  flex: 1 1 0;
  min-width: 0;
}
.fc2-col-image-wrap {
  flex: 0 0 22%;
  min-width: 180px;
  border-radius: 15px;
  overflow: hidden;
}
/* Client feedback 2026-08-29 asked for the opposite tradeoff from the 22%
   value above: more room for the product photos, accepting that "Energy
   Management, Environmental & Decarbonization" (Flow Control 6_2,
   Instrumentation) wraps to 2 lines. Verified by screenshot at 3840x2160
   with the larger .fc2-col-fc bullet size below — still 2 lines, not 3, at
   30%. Scoped to Flow Control only; 9_3 keeps 22% via the unscoped rule
   above since its cards are being restructured separately (images stacked
   below text, not beside). */
.fc2-col-fc .fc2-col-image-wrap {
  flex: 0 0 30%;
}
.fc2-col-image { width: 100%; height: 100%; }
/* Stacked product photos (5_2b) replace the single wide composite. Each
   image is a flex item with its own flex-basis (set inline alongside the
   other per-image CmsImg overrides, same convention as .body-stat-cell's
   images) so unequal photo counts across columns each still fill the wrap's
   full height. */
.fc2-col-images {
  display: flex;
  flex-direction: column;
  gap: 16px;
  height: 100%;
}
/* 'below' image layout (Case Studies 9_3): stack the photo under the
   full-width text instead of splitting text | image side by side. Reuses
   .fc2-col-body/.fc2-col-text/.fc2-col-image-wrap wholesale — only the flex
   direction and each child's basis change — so .fc2-col-split (6_2/6_3)
   is untouched. Text keeps its natural height; the image is the sole
   flexible item and fills whatever height `fillCards`' stretched row leaves
   below it, matching the "cards fill the full body height" ask. */
.fc2-col-stack .fc2-col-body { flex-direction: column; }
.fc2-col-stack .fc2-col-text { flex: 0 0 auto; }
.fc2-col-stack .fc2-col-image-wrap {
  flex: 1 1 0;
  min-height: 0;
  width: 100%;
}
/* Client feedback 2026-09-29 (issues #88, #89, #97): Flow Control 6_2/6_3 and
   Case Studies 9_3 should spread their three sub-sections down the full card
   height with even space between them, not pack them at the top. Each column
   is spaced on its own — the sub-headings are not aligned across sibling cards.
   The column becomes a flex column here, which is safe despite the block-flow
   note on `.fc2-col-text` above: the flex items are the `.fc2-section`
   wrappers, held at their natural height, not the bullet lists, so
   `.body-bullets`' `flex: 1 1 0` is still inert inside each block-flow
   section. */
.fc2-col-fc .fc2-col-text,
.fc2-col-lg .fc2-col-text {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}
.fc2-col-fc .fc2-section,
.fc2-col-lg .fc2-section { flex: 0 0 auto; }
/* 9_3 stacks its photo below the text, and the photo used to take all of the
   leftover height, so there was no space to spread the sections into. Give the
   text the flexible share and pin the photo band to a fixed fraction of the
   card body instead. */
.fc2-col-lg.fc2-col-stack .fc2-col-text { flex: 1 1 auto; }
.fc2-col-lg.fc2-col-stack .fc2-col-image-wrap { flex: 0 0 30%; }
.fc2-sub-heading {
  font-family: var(--font);
  font-size: 32px;
  font-weight: 700;
  color: var(--teal);
  margin: 8px 0 0;
}
/* Flow Control only (see `.fc2-col-fc` above) — client feedback 2026-08-29
   round included the category sub-headings (Applications/Process Media/
   Types) in "headers", confirmed against a rendered screenshot to read
   proportionately against the bumped 44px column heading. */
.fc2-sub-heading.fc2-sub-heading-fc { font-size: 36px; }
.body-card.fc2-col .body-bullets li { font-size: 28px; line-height: 1.3; margin-bottom: 0; }
.body-card.fc2-col.fc2-col-fc .body-bullets li { font-size: 32px; }
/* Case Studies 9_3 client feedback 2026-08-29: text reads small once each
   card only holds one case study (vs. 6_2/6_3's denser multi-item lists).
   `.fc2-col-lg` is scoped by page._id in PageBody.tsx, not a shared default
   bump, so 6_2/6_3's `.fc2-col` sizes above are unaffected. Same (0,2,0)/
   (0,3,0) specificity trick as `.fc2-icon-col` — wins on source order.
   Client feedback 2026-08-31: still reads small at 32px now that the
   heading/text gap fix (`.body-row-aligned-2row`) frees up more of the card
   for copy, so bumped again to 38px. */
.body-card.fc2-col.fc2-col-lg .body-card-heading { font-size: 44px; }
.body-card.fc2-col.fc2-col-lg .fc2-sub-heading { font-size: 36px; }
.body-card.fc2-col.fc2-col-lg .body-bullets li { font-size: 38px; line-height: 1.4; }

/* ── iconColumns template (Automation 7_2) ──
   Reuses .fc2-col's subgrid row-alignment, but the fixed 4-category/3-item
   shape has far less content than a bullet-heavy `columns` card, so it needs
   a bigger row-gap and much larger icons to fill the same card height rather
   than leaving a mostly-empty card below a handful of small rows. Client
   feedback 2026-08-28 (round 3): column titles read too small next to the
   icon rows, and the icon-beside-label pairing looked cramped — the icon
   image should run the full width of the card with its label overlaid on
   top, not sit in a small fixed square next to the text. */
.body-card.fc2-col.fc2-icon-col { row-gap: 40px; }
/* Same specificity (0,2,0) as the shared `.body-card.fc2-col .body-card-heading`
   rule above; wins on source order since it's declared later, and is scoped to
   .fc2-icon-col so 6_2/6_3/9_3's fc2-col headings are unaffected. */
.body-card.fc2-icon-col .body-card-heading { font-size: 46px; }
/* No aspect-ratio/fixed height here on purpose: .fc2-icon-row is a subgrid
   item of .fc2-icon-col (see .body-row-aligned > .body-card.fc2-col), and
   .body-row-aligned-fill's align-content:stretch is what gives its row track
   a real height, splitting the card's available height across all items.
   Forcing aspect-ratio:1/1 at full card width (~767px) made 3 stacked rows
   taller than the fixed 1602px body ceiling — .body-card's overflow:hidden
   then clipped the label and part of the image on lower rows with no visible
   error. Let the grid assign the height instead; object-fit:cover crops the
   source photo to whatever band height results. */
.fc2-icon-row {
  position: relative;
  width: 100%;
  border-radius: 24px;
  overflow: hidden;
}
.fc2-icon-img { width: 100%; height: 100%; }
/* Client feedback 2026-09-29: issue #91's whole-photo navy tint + blur
   (`.fc2-icon-row::after`) was removed the same day — the photos show at full
   brightness. The label keeps #91's bold weight, and its own backing below
   still carries its contrast. */
/* Client feedback 2026-09-24 (round 4, issue #25): measured contrast as low
   as ~1.5:1 against brighter icon photos — the old bottom-to-top fade thinned
   out right at the label's own top edge, exactly where ascenders sit. Same
   flat near-opaque backing as `.ctm1-photo-bullets` (2_1), for the same
   reason: a fade reintroduces a thin, unpredictably-lit edge. */
.fc2-icon-label {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 24px 30px;
  z-index: 1;
  background: rgba(23, 53, 66, 0.88);
  font-family: var(--font);
  font-size: 40px;
  font-weight: 700;
  color: var(--white);
  line-height: 1.3;
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.55);
}

.body-col {
  display: flex;
  flex-direction: column;
  gap: 50px;
  flex: 1 1 0;
  min-width: 0;
}

.body-grid-3x2 {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(2, 1fr);
  gap: 50px;
  flex: 1 1 0;
  min-height: 0;
}

.body-grid-2x2 {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  grid-template-rows: repeat(2, 1fr);
  gap: 50px;
  flex: 1 1 0;
  min-height: 0;
}

/* ── 4 × 2 value grid (Service Centers page 4) ──
   Eight cards. Card content box is 767.5 × 486 (no footer bar; with one it would be
   767.5 × 402.2). Four columns is the tight axis, not the rows — hence padding 40
   rather than the usual 50, which buys 20px of width back for the headings. The
   longest ("One of the World's Largest Fluid Power Networks", 47 chars) then holds
   two lines at the stock 52px with ~24px to spare; at the default 50px padding that
   margin drops to ~4px and the card is one font-metric away from a third line.
   Vertically there is plenty: worst case (3-line heading + 3-line subtitle + stat
   line) is 454 of 486. .body-card's overflow:hidden zeroes each grid row's automatic
   minimum, so an over-long card clips rather than pushing past the 1602 ceiling. */
.body-grid-4x2 {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-template-rows: repeat(2, 1fr);
  gap: 50px;
  flex: 1 1 0;
  min-height: 0;
}
.body-grid-4x2 .body-card { gap: 24px; padding: 40px; }
/* Pins the teal stat line to the card foot, so the five cards that have one line
   up with each other regardless of how many lines their heading/subtitle took.
   Client feedback: the stat label itself should be much larger than the
   sitewide default (26px) to read as a headline figure, not a caption. */
.body-grid-4x2 .body-field-label { margin-top: auto; font-size: 56px; }
/* Client feedback round 3: the 3 stat-less cards previously used space-between
   to push their textBlock down to the card foot (filling the dead space left by
   no fieldLabel stat) -- client wants the textBlock directly under the heading
   on every card instead, so this is back to normal top-down flow, same as the
   5 stat cards. */
.body-grid-4x2 .body-card-fill { justify-content: flex-start; }
/* Client feedback (4_3 restore): the two page-scoped .body-text-lg overrides
   that used to live here (56px on .body-card-zoom, 60px on the stat-less
   .body-card-fill cards) have been removed -- this page's subtitle text now
   renders at the sitewide .body-text-lg default (50px, see below). */

.body-flex-2 { flex: 2 1 0 !important; }
.body-full { flex: 1 1 0; }
/* CMS toggle: optional fullWidthGraphic.caption paragraph below the graphic (9_1),
   in its own card at its natural height — the graphic's card above shrinks via
   flex to make room. */
.body-card.body-fwg-caption-card { flex: 0 0 auto; }
.body-fwg-caption { text-align: center; }

.body-row-inner {
  display: flex;
  gap: 40px;
  align-items: flex-start;
}
.body-row-tight { gap: 25px; align-items: center; }

.body-col-inner {
  display: flex;
  flex-direction: column;
  gap: 20px;
  flex: 1 1 0;
  min-width: 0;
}

/* ── Card (white panel) ── */

.body-card {
  background: var(--white);
  border-radius: 30px;
  padding: 50px;
  display: flex;
  flex-direction: column;
  gap: 30px;
  flex: 1 1 0;
  min-height: 0;
  min-width: 0;
  overflow: hidden;
}
.body-card-row {
  flex-direction: row;
  align-items: center;
  gap: 80px;
}

/* ── Typography ── */

.body-card-heading {
  font-family: var(--font);
  font-size: 52px;
  font-weight: 700;
  color: var(--navy);
  line-height: 1.2;
  flex-shrink: 0;
  margin: 0;
}
.body-heading-sm {
  font-size: 42px;
}
.body-text {
  font-family: var(--font);
  font-size: 42px;
  font-weight: 400;
  color: var(--navy);
  line-height: 1.5;
  margin: 0;
}
.body-text-sm { font-size: 36px; }
/* Content-editor toggle: textBlock/bulletList size 'lg' — for short content
   that would otherwise leave a card looking sparse (bigger text + more
   breathing room between lines, rather than shrinking the card around it). */
.body-text-lg { font-size: 50px; line-height: 1.6; }
.body-text-inline {
  font-family: var(--font);
  font-size: 38px;
  color: var(--navy);
  line-height: 1.4;
}
.body-text-accent {
  font-family: var(--font);
  font-size: 40px;
  font-weight: 600;
  font-style: italic;
  color: var(--teal);
  text-align: center;
  margin: 0;
}

/* ── Bullets ── */

.body-bullets {
  list-style: disc;
  padding-left: 50px;
  margin: 0;
  flex: 1 1 0;
  min-height: 0;
}
/* Line heights across the bullet tiers were tightened to 1.4 sitewide
   (client feedback 2026-09-29, issue #80) — only line spacing within a
   bullet; the margin-bottom gap between bullets is unchanged. */
.body-bullets li {
  font-family: var(--font);
  font-size: 42px;
  color: var(--navy);
  line-height: 1.4;
  margin-bottom: 8px;
}
.body-bullets-lg li {
  font-size: 50px;
  line-height: 1.4;
  margin-bottom: 22px;
}
/* Tightens wrapping within a bullet without touching the gap between bullets
   (margin-bottom). Doubled class to win specificity over .body-bullets-lg li
   deterministically — see CLAUDE.md §3.4. */
.body-bullets-lg.body-bullets-lg-tight li {
  line-height: 1.35;
}
/* The 'sm' counterpart to -lg. Both need their own `li` rule because .body-bullets
   li declares font-size directly — a size class on the <ul> is inert, which is why
   `bulletList.size: 'sm'` set in Studio appeared to do nothing. 36px matches
   .body-text-sm so 'sm' means the same thing on a textBlock and a bulletList. */
.body-bullets-sm li {
  font-size: 36px;
  line-height: 1.4;
  margin-bottom: 4px;
}
.body-bullets-2col {
  columns: 2;
  column-gap: 40px;
}
.body-bullets-sub {
  list-style: none;
  padding-left: 30px;
  margin: 4px 0 0;
}
.body-bullets-sub li::before {
  content: '– ';
}

/* ══ CTM3 — interactive facility map (Channels to Market page 3) ══

   Layout is pinned in px against the real body box, same derivation as .tl2:
   3540 × 1222 (3840 − 2×150 padding; 2160 − 380 header − 558 bottom bar).

   Two stacked rows now, not one: the map/detail row (2290 + 50 gap + 1200 =
   3540 wide, 1042 tall) and a full-width instruction CTA bar below it (130
   tall), with a 50px gap between them — 1042 + 50 + 130 = 1222, the same
   total the row alone used to claim. The area-indicator dot row that used to
   sit at the foot of the detail card is gone entirely (removed at client
   request, not replaced) — nothing is parallel to that footer any more. */

.ctm3 {
  display: flex;
  flex-direction: column;
  flex: 1 1 0;
  min-height: 0;
  gap: 50px;

  /* The two fixed spans of the map/detail row, plus the row's own fixed height.
     1222 total − 50 gap − 130 CTA bar = 1042. --ctm3-row-h must stay an absolute
     length (not the row's flex-computed height) because .ctm3-plate's centering
     math below needs a real number to calc() against. */
  --ctm3-map-w: 2290px;
  --ctm3-row-h: 1042px;
}

.ctm3-row {
  display: flex;
  flex: 0 0 var(--ctm3-row-h);
  min-height: 0;
  align-items: stretch;
  gap: 50px;
}

/* Full-width instruction bar under the map/detail row. Same visual language as
   the home screen's secondary buttons (white fill, teal border) rather than a
   solid teal block, so it reads as "part of this app" and not a new UI system. */
.ctm3-cta {
  flex: 0 0 130px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--white);
  border-radius: 30px;
  padding: 0 60px;
}

.ctm3-cta-text {
  font-family: var(--font);
  font-size: 56px;
  font-weight: 700;
  color: var(--navy);
  letter-spacing: 0.02em;
  text-align: center;
}

/* ── Map card ── */

.ctm3-map {
  flex: 0 0 var(--ctm3-map-w);
  min-width: 0;
  position: relative;
  background: var(--white);
  border-radius: 30px;
  overflow: hidden;
}

/* Pin coordinates are a percentage of the artwork, so the pin layer has to BE
   the artwork's box — not the card's. The card is a fixed 2290 × 1042; the plate
   is the largest box of the artwork's own ratio that fits inside it, centred on
   the card's white. At today's 2.135:1 that's now width-constrained (2225px
   wide, filling the full 1042px height edge-to-edge) rather than the old
   height-constrained fit — a shorter card leaves white on the sides instead of
   top/bottom. Deriving it from --ctm3-aspect rather than hardcoding means a
   re-cut map at any resolution *or aspect* keeps its pins registered, and there
   is no cap left to bite: a wider map shortens the plate, a taller one narrows it.

   ⚠️ Both min() terms must be absolute lengths, which is why --ctm3-row-h is a
   hardcoded 1222 rather than 100%. The earlier attempt — aspect-ratio plus
   max-width: 100% AND max-height: 100% — cannot work: whichever axis clamps
   leaves the other unchanged, breaking the ratio and desyncing the pins. */
.ctm3-plate {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width:  min(var(--ctm3-map-w), calc(var(--ctm3-row-h) * var(--ctm3-aspect, 2.135)));
  height: min(var(--ctm3-row-h), calc(var(--ctm3-map-w) / var(--ctm3-aspect, 2.135)));
}

.ctm3-plate > img,
.ctm3-plate > .body-image {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

/* ── Pins ── */

/* Two channels, one each:
     COLOUR = group membership. Pins outside the selected area are #99CED1, the
       light teal of the section background's accent wedge, so the selected
       group's pins are the only solid-teal marks on the map.
     SIZE   = the single current node. Only the pin actually stepped to grows;
       its groupmates stay at the idle 58px. (The timeline's "size carries state,
       colour does not" rule does not hold here — with 12 pins and 6 groups a
       size-only scheme cannot express both group and current node at once.)
   The button is a fixed 140px so the touch target stays comfortable at 4K
   whatever the dot inside it is doing. */
.ctm3-pin {
  position: absolute;
  transform: translate(-50%, -50%);
  width: 140px;
  height: 140px;
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1;
  -webkit-tap-highlight-color: transparent;
}

/* The white ring is a box-shadow, not a border, so `width` stays the diameter
   of the teal itself and the size steps below read as authored. */
.ctm3-pin-dot {
  width: 58px;
  height: 58px;
  border-radius: 50%;
  background: #99CED1;
  box-shadow: 0 0 0 6px var(--white);
  transition: width 0.3s, height 0.3s, background 0.3s, box-shadow 0.3s, transform 0.12s;
}

/* In the selected group: solid teal, still 58px. */
.ctm3-pin--area .ctm3-pin-dot {
  background: var(--teal);
}

/* The one current node. Raised above its groupmates so the halo is never clipped
   by an adjacent pin's button box. */
.ctm3-pin--active {
  z-index: 2;
}

.ctm3-pin--active .ctm3-pin-dot {
  width: 108px;
  height: 108px;
  box-shadow: 0 0 0 6px var(--white), 0 0 0 22px rgba(0, 123, 132, 0.55);
  animation: ctm3PinPulse 1.8s ease-in-out infinite;
}

.ctm3-pin:active .ctm3-pin-dot {
  transform: scale(0.9);
}

@keyframes ctm3PinPulse {
  0%, 100% { box-shadow: 0 0 0 6px var(--white), 0 0 0 22px rgba(0, 123, 132, 0.55); }
  50%      { box-shadow: 0 0 0 6px var(--white), 0 0 0 56px rgba(0, 123, 132, 0.05); }
}

/* ── Detail panel ── */

.ctm3-detail {
  flex: 1 1 0;             /* 3540 − 50 gap − 2290 map = 1200 */
  min-width: 0;
  display: flex;
  flex-direction: column;  /* title, then the single current bullet */
  background: var(--white);
  border-radius: 30px;
  padding: 60px;
  overflow: hidden;
  /* the swipe handler owns horizontal drag; vertical is left to the shell */
  touch-action: pan-y;
}

.ctm3-detail-in {
  flex: 1 1 0;
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

.ctm3-detail-title {
  font-family: var(--font);
  font-size: 62px;
  font-weight: 700;
  line-height: 1.1;
  color: var(--navy);
  margin: 0;
}

/* Exactly one bullet ever renders here (the current pin's), always in the
   highlighted teal style — there is no second state to dim, so the whole
   n-bullet sizing system this used to need (container queries keyed to bullet
   count, a 48/53/58px tier ladder) is gone. Client feedback 2026-08-31: plain
   text (no marker, no list semantics — it renders one item), sized at the
   sitewide default (`.body-bullets li`'s 42px), pinned to its own content
   height so the media band below is the one that grows into whatever the
   short text leaves over. */
.ctm3-detail-text {
  flex: 0 0 auto;
  margin: 24px 0 0;
  padding-right: 60px;
  font-family: var(--font);
  font-size: 42px;
  font-weight: 400;
  line-height: 1.6;
  color: var(--navy);
}

/* Present only when the area carries an image. Client feedback 2026-08-31:
   sized up now that the text above it is a single short line instead of a
   bullet list — `flex: 1 1 0` makes it the panel's one flexible element, so it
   claims whatever height `.ctm3-detail-text` doesn't need. `min-height` is the
   floor for the rare long-text case. */
.ctm3-detail-media {
  flex: 1 1 0;
  min-height: 260px;
  margin-top: 36px;
  border-radius: 24px;
  overflow: hidden;
  background: var(--white);
  display: flex;
  align-items: center;
  justify-content: center;
}

@keyframes ctm3InRight {
  from { opacity: 0; transform: translateX(70px); }
  to   { opacity: 1; transform: none; }
}
@keyframes ctm3InLeft {
  from { opacity: 0; transform: translateX(-70px); }
  to   { opacity: 1; transform: none; }
}

.ctm3-enter-right { animation: ctm3InRight 0.38s cubic-bezier(0.22, 1, 0.36, 1) both; }
.ctm3-enter-left  { animation: ctm3InLeft  0.38s cubic-bezier(0.22, 1, 0.36, 1) both; }

/* ── Field labels (CMS-driven section labels within cards) ── */

.body-field-label {
  display: block;
  font-family: var(--font);
  font-size: 26px;
  font-weight: 700;
  color: var(--teal);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-top: 14px;
}

/* ── MSS overview list — always bold, no inline markup needed ── */

.mss-overview-list li {
  font-weight: 700;
  line-height: 1.8;
  margin-bottom: 16px;
}

/* ── Labels ── */

.body-label {
  background: rgba(23, 53, 66, 0.06);
  border-radius: 15px;
  padding: 0 36px;
  font-family: var(--font);
  font-weight: 600;
  color: var(--navy);
  flex: 1 1 0;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  container-type: size;
  font-size: clamp(28px, 38cqh, 56px);
}

/* ── Heading bar ── */

.body-heading-bar {
  background: var(--white);
  border-radius: 20px;
  padding: 30px 50px;
  font-family: var(--font);
  font-size: 48px;
  font-weight: 600;
  /* Pinned, not `normal`. This bar's height sets how much room is left for the
     grid above it on a cardGrid page, so it has to be computable: 30 + 30 +
     48 × 1.2 = 117.6px at one line. Interior width is 3440px ≈ 130 characters;
     past that it wraps to two lines and steals 58px from the cards. */
  line-height: 1.2;
  color: var(--navy);
  text-align: center;
  flex-shrink: 0;
}

/* ── Image placeholder ── */

.body-image {
  background: #D5DCE0;
  border-radius: 20px;
  flex: 1 1 0;
  min-height: 100px;
  display: flex;
  align-items: center;
  justify-content: center;
}
/* An imageBlock rendered inside a card. Takes the space left over after the copy
   rather than competing with it: base 0 + grow, so it expands into the remainder,
   and min-height:0 so it can also give way when the copy needs more. Without this
   the list (flex: 1 1 0, base 0) collapsed to zero height and its items overflowed
   on top of the image — the 11_3 ISO document previews and 10_1 banners. Same shape
   as the .body-img-fade rule further down. */
.body-image-block {
  display: flex;
  flex: 1 1 0;
  min-height: 0;
  border-radius: 20px;
  overflow: hidden;
}
/* Channels to Market 2_1 (`.ctm1-card`, set in PageBody.tsx off page._id): a
   full-bleed photo card, bullets overlaid on the image instead of stacked
   above it — bypasses the generic `renderCard`/`renderBlock` path entirely
   (see the render-side comment), so it doesn't touch the sitewide
   `.body-image-block` default other cardGrid pages rely on. */
.ctm1-photo {
  position: relative;
  flex: 1 1 0;
  min-height: 0;
  width: 100%;
  border-radius: 24px;
  overflow: hidden;
  background: #D5DCE0;
}
/* `.ctm1-photo > img` rather than a dedicated class: CmsImg already defaults
   to width/height 100% + object-fit: cover, so nothing more needs setting. */
.ctm1-photo > img { display: block; }
/* Client feedback 2026-09-29: the navy backing covers the whole photo edge to
   edge rather than only the bullets' box — a flat tint, no blur (the round 4/5
   blur was dropped the same day). Flat, not a gradient, for the reason issue #12
   established: a fade reintroduces a thin, unpredictably-lit edge right where
   text sits. 0.7 opacity: worst case, a pure-white photo, blends to
   ~rgb(93,114,123), and white text on that is ~5.1:1 — inside WCAG AA at any
   text size. `::after`, not a background on `.ctm1-photo-bullets`, so it isn't
   tied to the bullets' own box. */
.ctm1-photo::after {
  content: '';
  position: absolute;
  inset: 0;
  background: rgba(23, 53, 66, 0.7);
}
.ctm1-photo-bullets {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 1;
  margin: 0;
  list-style: disc;
  padding: 40px 40px 34px 74px;
}
/* Bypassing the generic bulletList render path (see the render-side comment)
   means the sitewide `.body-bullets`/`-sm`/`-lg` tiers don't reach these — this
   is `.ctm1-photo-bullets`' own scale for the same `bulletList.size` CMS field,
   sized up from a first pass that read too small against the full-bleed photo. */
.ctm1-photo-bullets li {
  font-family: var(--font);
  font-size: 44px;
  font-weight: 700;
  line-height: 1.4;
  color: var(--white);
  margin-bottom: 8px;
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.55);
}
.ctm1-photo-bullets-sm li { font-size: 38px; }
.ctm1-photo-bullets-lg li { font-size: 52px; }
.ctm1-photo-bullets li:last-child { margin-bottom: 0; }
/* Copy sharing a card with an image band takes its natural height; the band above
   absorbs what's left. Mirrors .body-card:has(.body-img-fade) .body-bullets. */
.body-card:has(> .body-image-block) > .body-bullets {
  flex: 0 0 auto;
}

/* Content-editor toggle: contentCard.lowerTwoColumn — heading and opening block stay
   full width above this row, which then takes the rest of the card height and divides
   it: copy left, image right. Gives a tall portrait asset a column-height box to fill
   instead of a shallow full-width band. */
.body-card-split-row {
  display: flex;
  flex: 1 1 0;
  min-height: 0;
  gap: 40px;
  align-items: stretch;
}
.body-card-split-col {
  display: flex;
  flex-direction: column;
  gap: 20px;
  flex: 1 1 0;
  min-width: 0;
  min-height: 0;
}
/* Same reason as the :has() rule above — the image is nested here rather than a direct
   child of the card, so that selector no longer matches and the list needs its own
   floor. Without it flex: 1 1 0 collapses the <ul> to zero and the items overflow. */
.body-card-split-col > .body-bullets {
  flex: 0 0 auto;
}
.body-card-split-media {
  justify-content: center;
}

/* Content-editor toggle: imageBlock.size */
.body-image-lg {
  max-width: 100%;
}
.body-image-sm {
  max-width: 55%;
  height: auto;
  margin: 0 auto;
  align-self: center;
}
.body-image-label {
  font-family: var(--font);
  font-size: 36px;
  font-style: italic;
  color: rgba(23, 53, 66, 0.4);
  text-align: center;
  padding: 20px;
}

/* Full-width crossfading image slideshow — grows to fill the space left below
   the text, so the image scales with how little copy sits above it. Used by
   Fluid Power 5_1 (three cards, copy above) and Automation 7_3 (one card, image
   only). Images are stacked absolutely and crossfaded via opacity (ImageFader).
   object-fit is cover, so tall or wide assets lose their edges — deliberate, so
   the band always fills its card edge-to-edge. */
.body-img-fade {
  position: relative;
  flex: 1 1 0;
  min-height: 0;
  width: 100%;
  border-radius: 20px;
  overflow: hidden;
  background: #D5DCE0;
}
.body-img-fade-item {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 1000ms ease-in-out;
}
.body-img-fade-item.is-active { opacity: 1; }

/* imagePair.fit 'contain' — for cut-outs with transparent backgrounds (Automation
   7_3). The whole image is shown rather than cropped, and the backing goes white so
   neither the letterbox nor the image's own transparent pixels read as a grey slab
   sitting on the white card. Photographs stay on 'cover', where the image covers the
   band completely and the backing never shows. */
.body-img-fade-contain { background: var(--white); }
.body-img-fade-contain .body-img-fade-item { object-fit: contain; }
/* In a card with the slideshow, the bullet list takes its natural height so the
   image (not the list) absorbs the remaining vertical space. */
.body-card:has(.body-img-fade) .body-bullets { flex: 0 0 auto; }

/* imagePair.headings (MSS 8_2 Products & Solutions) — swipe/tap-navigable
   variant. cursor: pointer signals the carousel responds to touch, matching the
   other tappable interactives (InteractiveMap, InteractiveTimeline). */
.body-img-fade-headed { cursor: pointer; }
/* Fallback only: inside a cardRow (MSS 8_2) the heading renders in its own
   full-width `.body-heading-bar` card above the row instead (PageBody.tsx,
   CarouselHeadingRow). This in-image banner is what a headed carousel in any
   other template would show. */
.body-img-fade-heading {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: 2;
  background: var(--navy);
  color: var(--white);
  font-family: var(--font);
  font-weight: 600;
  font-size: 48px;
  padding: 28px 50px;
  text-align: center;
}
.body-img-fade-dots {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 24px;
  z-index: 2;
  display: flex;
  justify-content: center;
  gap: 14px;
}
.body-img-fade-dot {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.5);
  box-shadow: 0 0 0 2px rgba(23, 53, 66, 0.25);
}
.body-img-fade-dot.is-active { background: var(--teal); }

/* imagePair.nav 'timeline' (MSS 8_2, issue #110) — the dots swapped for a
   labelled rail under the image: one tappable node per heading, in About
   Applied's timeline language (.tl2-rail): grey line, teal fill to the current
   node, upcoming dots grey, passed dots teal, the current one teal and larger
   with a halo. Its own classes, so the tl2 rail is untouched.

   The rail is the band's sibling in the card (PageBody.tsx, ImageFader) at its
   natural height; the band is the card's only flex: 1 1 0 item, so it is the
   image that gives up the room. Node count (--fade-n) and current index
   (--fade-i) are set inline, and every size derives from the column width —
   100cqw over the count — so any card width and heading count lay out without
   a code change. At MSS 8_2 (11 headings, 3440px card interior) a column is
   ~313px: the dots sit at their caps, the current label at ~44px. */
.body-img-fade-timeline {
  flex: 0 0 auto;
  container-type: inline-size;
}
.body-img-fade-timeline-rail {
  position: relative;
  display: grid;
  grid-auto-flow: column;
  /* minmax(0, …), not 1fr: an unbreakable heading must not widen its column
     past its share and push the rest off the dot grid the line is drawn on. */
  grid-auto-columns: minmax(0, 1fr);
  align-items: start;

  --fade-col: calc(100cqw / var(--fade-n));
  /* Same caps and ratios as .tl2 */
  --fade-dot-active: clamp(26px, calc(var(--fade-col) * 0.5), 64px);
  --fade-dot: calc(var(--fade-dot-active) * 0.53);
  --fade-halo: calc(var(--fade-dot-active) * 0.22);
  /* Headings are words, not 4-digit years. The 0.14 ratio is what keeps the
     longest unbreakable one on MSS 8_2, "Transportation" (~6.25em at 700),
     inside its column at the larger, current-node size: 274px of 313, ~19px
     spare each side. A longer single word would need a smaller ratio. */
  --fade-label-active: clamp(20px, calc(var(--fade-col) * 0.14), 44px);
  --fade-label: calc(var(--fade-label-active) * 0.84);
  /* Room above the dot slot for the current node's halo */
  --fade-pad: calc(var(--fade-halo) + 2px);
  --fade-slot: calc(var(--fade-dot-active) + 4px);
  --fade-line-y: calc(var(--fade-pad) + var(--fade-slot) / 2);
}

/* Dot-centre to dot-centre, inset half a column at each end, as .tl2-rail-line */
.body-img-fade-timeline-line,
.body-img-fade-timeline-fill {
  position: absolute;
  top: var(--fade-line-y);
  height: 6px;
  border-radius: 3px;
  transform: translateY(-50%);
  left: calc(50% / var(--fade-n));
}
.body-img-fade-timeline-line {
  right: calc(50% / var(--fade-n));
  background: #C9D2D7;
}
.body-img-fade-timeline-fill {
  width: calc(100% * var(--fade-i) / var(--fade-n));
  background: var(--teal);
  transition: width 0.38s cubic-bezier(0.22, 1, 0.36, 1);
}

/* The whole column is the touch target (~313px wide on MSS 8_2), not the dot. */
.body-img-fade-timeline-node {
  display: grid;
  justify-items: center;
  row-gap: 20px;
  padding: var(--fade-pad) 0 0;
  background: none;
  border: none;
  cursor: pointer;
  position: relative;
  z-index: 1;
  -webkit-tap-highlight-color: transparent;
}
/* Fixed height so the dot growing on the current node never moves the label */
.body-img-fade-timeline-hit {
  height: var(--fade-slot);
  display: flex;
  align-items: center;
  justify-content: center;
}
.body-img-fade-timeline-dot {
  width: var(--fade-dot);
  height: var(--fade-dot);
  border-radius: 50%;
  background: #C9D2D7;
  transition: width 0.3s, height 0.3s, background 0.3s, box-shadow 0.3s;
}
.body-img-fade-timeline-node:active .body-img-fade-timeline-dot,
.body-img-fade-timeline-node.is-past .body-img-fade-timeline-dot { background: var(--teal); }
.body-img-fade-timeline-node.is-active .body-img-fade-timeline-dot {
  width: var(--fade-dot-active);
  height: var(--fade-dot-active);
  background: var(--teal);
  box-shadow: 0 0 0 var(--fade-halo) rgba(0, 123, 132, 0.14);
}

/* Up to two lines, balanced, in a box always two lines tall at the current-node
   size — so the rail (and the image above it) is the same height on every slide,
   whichever heading is current and however it wraps. The side padding keeps a
   gutter between neighbouring labels. Overflow can only show per line (a label
   fills its column by definition), so re-check line widths after editing
   headings. */
.body-img-fade-timeline-label {
  box-sizing: border-box;
  width: 100%;
  min-height: calc(var(--fade-label-active) * 1.15 * 2);
  padding: 0 10px;
  font-family: var(--font);
  font-size: var(--fade-label);
  font-weight: 700;
  line-height: 1.15;
  color: #8B9AA2;
  text-align: center;
  text-wrap: balance;
  transition: color 0.3s, font-size 0.3s;
}
.body-img-fade-timeline-node.is-past .body-img-fade-timeline-label { color: #5E727C; }
.body-img-fade-timeline-node.is-active .body-img-fade-timeline-label {
  font-size: var(--fade-label-active);
  color: var(--teal);
}

/* ── Icons ── */

.body-icon-square {
  width: 80px;
  height: 80px;
  background: #D5DCE0;
  border-radius: 12px;
  flex-shrink: 0;
}
.body-icon-row {
  display: flex;
  gap: 30px;
  align-items: center;
  flex-shrink: 0;
}
.body-icon-box {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
}
.body-icon-box-label {
  font-family: var(--font);
  font-size: 28px;
  color: var(--navy);
  text-align: center;
}
.body-icon-dot {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--teal);
  flex-shrink: 0;
}
.body-icon-grid-sm {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px;
  flex-shrink: 0;
}

/* ── Stats ── */

.body-stat-value {
  font-family: var(--font);
  font-size: 48px;
  font-weight: 700;
  color: var(--teal);
  white-space: nowrap;
  flex-shrink: 0;
}
.body-stat-label {
  font-family: var(--font);
  font-size: 34px;
  color: var(--navy);
  text-align: center;
}
.body-stat-line {
  font-family: var(--font);
  font-size: 38px;
  font-weight: 600;
  color: var(--teal);
  margin: 0;
  flex-shrink: 0;
}
.body-stat-row {
  display: flex;
  gap: 20px;
  align-items: flex-start;
}
.body-stat-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 30px;
  flex: 1 1 0;
}
.body-stat-cell {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}
/* Content-editor toggle: statGrid.hideImages — the text has to fill the space the
   icons used to occupy, so the rows stretch to fill the card and the type scales
   with the cell rather than sitting at a fixed size. Same container-query approach
   as .body-label. An earlier pass centered shorter rows at 64px/40px, which still
   left the card looking half empty.
   The cqh terms are generous enough that both values clamp to their max at the
   current card size — the max is the number to change if this wants to go bigger
   again. Careful past ~90px: .body-stat-value is nowrap, so a 5-character value
   like "9.1M+" starts to overrun a one-third-width card's grid column. */
.body-stat-grid-noimg {
  align-content: stretch;
  gap: 40px 30px;
}
.body-stat-grid-noimg .body-stat-cell {
  justify-content: center;
  gap: 4px;
  container-type: size;
  background: rgba(23, 53, 66, 0.06);
  border-radius: 15px;
  padding: 24px 20px;
}
.body-stat-grid-noimg .body-stat-value {
  font-size: clamp(64px, 26cqh, 88px);
  line-height: 1.05;
}
.body-stat-grid-noimg .body-stat-label {
  font-size: clamp(40px, 14cqh, 52px);
  line-height: 1.2;
}

/* Content-editor toggle: statGrid.columns '2' — for a label that will not fit a
   one-third-width cell on one line. On About Applied 3_1 the cells go
   328.9 × 494.8 → 508.3 × 323.2: 55% wider, 36% shorter.
   The clamps MUST be restated. They key off cell HEIGHT (cqh), so at three rows
   the inherited values would land at 82px / 44px — the type would shrink even
   though the cells got much wider. And the 88px ceiling noted above is a
   three-column constraint: at 508px wide, nowrap "3,200+" does not bind until
   ~169px, so 120px is comfortably safe here.
   Written as a doubled class on purpose. A lone .body-stat-grid-2col ties
   .body-stat-grid-noimg on specificity, so it would win or lose on source order. */
.body-stat-grid-noimg.body-stat-grid-2col {
  grid-template-columns: repeat(2, 1fr);
  /* 30px, not 40 — a third row would otherwise cost another 40px of cell height. */
  gap: 30px;
}
.body-stat-grid-noimg.body-stat-grid-2col .body-stat-value {
  font-size: clamp(72px, 38cqh, 120px);
}
.body-stat-grid-noimg.body-stat-grid-2col .body-stat-label {
  font-size: clamp(44px, 17cqh, 52px);
}

/* ── Stat block (number + label + optional bullets) ── */

.body-stat-block {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.body-stat-block + .body-stat-block {
  margin-top: 28px;
}
.body-stat-block .body-stat-value {
  font-size: 56px;
  line-height: 1;
}
.body-stat-block .body-stat-line {
  font-size: 36px;
  line-height: 1.3;
}
.body-stat-block .body-bullets {
  font-size: 34px;
  padding-left: 40px;
  flex: none;
}
.body-stat-block .body-bullets li {
  font-size: 34px;
  line-height: 1.4;
  margin-bottom: 4px;
}

/* ── Link bar ── */

.body-link-bar {
  background: var(--white);
  border-radius: 20px;
  padding: 30px 50px;
  font-family: var(--font);
  font-size: 40px;
  color: var(--teal);
  text-align: center;
  text-decoration: underline;
  flex-shrink: 0;
}

/* ── Logo marquee (brand partner pages) ── */

.body-logo-wrap {
  flex: 1 1 0;
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow: hidden;
}
.body-logo-marquee-wrap {
  flex: 1 1 0;
  display: flex;
  flex-direction: column;
  gap: 30px;
  min-height: 0;
  overflow: hidden;
}
.body-logo-marquee-row {
  flex: 1 1 0;
  overflow: hidden;
  display: flex;
  align-items: stretch;
}
.body-logo-track {
  display: flex;
  gap: 30px;
  width: max-content;
  align-items: stretch;
  animation: logoMarquee 40s linear infinite;
}
@keyframes logoMarquee {
  0%   { transform: translateX(0); }
  100% { transform: translateX(-50%); }
}
.body-logo-grid {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  grid-template-rows: repeat(3, 1fr);
  gap: 30px;
  flex: 1 1 0;
  min-height: 0;
}
.body-logo-cell {
  width: 565px;
  flex-shrink: 0;
  background: var(--white);
  border-radius: 15px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
}
.body-logo-fade {
  display: none;
  position: absolute;
  top: 0;
  bottom: 0;
  width: 200px;
  pointer-events: none;
  z-index: 1;
}
.body-logo-fade-l {
  left: 0;
  background: linear-gradient(to right, var(--bg), transparent);
}
.body-logo-fade-r {
  right: 0;
  background: linear-gradient(to left, var(--bg), transparent);
}

/* ── About Applied p2 — century timeline ──────────────────────────────────
   Body area is 3540 × 1222 (3840 − 2×150 padding; 2160 − 380 header − 558
   bottom bar), so the whole layout is pinned in px rather than guessed.

   The rail carries one directly-tappable node per milestone — all 17 fit at
   ~180px each, so nothing scrolls and the century reads left-to-right in one
   glance. That removes the two full-height chevron slabs the old version
   needed to drive a 5-at-a-time scrolling track: the arrows are now a
   secondary path (a 112px circle at each end of the rail), not the only way
   to reach a year.

   Node geometry is driven by --tl2-n (milestone count) and --tl2-i (active
   index), both set inline by InteractiveTimeline. Everything on the rail —
   column width, dot sizes, halo, year type, and the height of the line — is
   derived from those two, so adding milestones in Studio needs no code change:
   the rail keeps its exact current proportions until the columns get too
   narrow for the type, then scales down instead of overlapping.

   Practical ceiling: comfortable to ~24 milestones, still usable to ~32 (at
   which point a column is ~97px, near the floor for a reliable 4K touch
   target). Past that, one row stops being the right form — group by decade or
   split the rail across two rows rather than shrinking further. */

.tl2 {
  display: flex;
  flex-direction: column;
  flex: 1 1 0;
  min-height: 0;
  gap: 44px;

  /* rail width = 3540 body − 120 card padding − 2 × (112 arrow + 46 gap) */
  --tl2-rail-w: 3104px;
  --tl2-col: calc(var(--tl2-rail-w) / var(--tl2-n));

  /* Caps hold the tuned values at today's count (17) and shrink only when a
     column can no longer hold them: 2.2 is roughly the width of a 4-digit
     year in ems, and the subtracted px is the gutter kept between labels. */
  --tl2-dot-active: clamp(26px, calc(var(--tl2-col) * 0.5), 64px);
  --tl2-dot: calc(var(--tl2-dot-active) * 0.53);
  --tl2-halo: calc(var(--tl2-dot-active) * 0.22);
  --tl2-year-active: clamp(22px, calc((var(--tl2-col) - 14px) / 2.2), 54px);
  --tl2-year: calc(var(--tl2-year-active) * 0.74);

  /* dot slot, and the line that must stay pinned to its centre */
  --tl2-slot: calc(var(--tl2-dot-active) + 4px);
  --tl2-line-y: calc(62px + var(--tl2-slot) / 2);
}

/* ── Hero: photo left, copy right, one card ── */

.tl2-hero {
  flex: 1 1 0;             /* 1222 − 44 gap − 260 rail = 918 */
  min-height: 0;
  display: flex;
  align-items: stretch;
  gap: 70px;
  background: var(--white);
  border-radius: 30px;
  padding: 60px;
  overflow: hidden;
  /* the swipe handler owns horizontal drag; vertical is left to the shell */
  touch-action: pan-y;
}

/* 5:3 of the 798px inner height. Most milestone assets are exactly 900×540,
   so they land edge-to-edge here; the handful of transparent-background
   collages (1923) and off-ratio exports letterbox instead of being cropped,
   which is why the image is `contain` and this sits on the card's own white
   rather than a grey slab — a matted photo, not a placeholder box. */
.tl2-hero-media {
  flex: 0 0 1330px;
  align-self: stretch;
  border-radius: 24px;
  overflow: hidden;
  background: var(--white);
  display: flex;
  align-items: center;
  justify-content: center;
}

.tl2-hero-img {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  animation: tl2ImgIn 0.45s cubic-bezier(0.22, 1, 0.36, 1) both;
}

.tl2-hero-copy {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  overflow: hidden;
}

.tl2-year {
  font-family: var(--font);
  font-size: 124px;
  font-weight: 800;
  line-height: 0.95;
  letter-spacing: -0.01em;
  color: var(--teal);
  margin: 0;
}

.tl2-title {
  font-family: var(--font);
  font-size: 60px;
  font-weight: 700;
  line-height: 1.15;
  color: var(--navy);
  margin: 26px 0 0;
}

.tl2-text {
  font-family: var(--font);
  font-size: 42px;
  font-weight: 400;
  line-height: 1.45;
  color: var(--navy);
  /* Carries the gap the removed .tl2-rule used to hold open (28px + rule +
     28px). Without it the title butts straight onto the copy — .tl2-title's
     margin is top-only. */
  margin: 28px 0 0;
  /* the last milestone's copy carries an authored paragraph break */
  white-space: pre-line;
}

@keyframes tl2InRight {
  from { opacity: 0; transform: translateX(70px); }
  to   { opacity: 1; transform: none; }
}
@keyframes tl2InLeft {
  from { opacity: 0; transform: translateX(-70px); }
  to   { opacity: 1; transform: none; }
}
@keyframes tl2ImgIn {
  from { opacity: 0; transform: scale(1.035); }
  to   { opacity: 1; transform: none; }
}

.tl2-enter-right { animation: tl2InRight 0.38s cubic-bezier(0.22, 1, 0.36, 1) both; }
.tl2-enter-left  { animation: tl2InLeft  0.38s cubic-bezier(0.22, 1, 0.36, 1) both; }

/* ── Rail ── */

.tl2-rail-card {
  flex: 0 0 260px;
  display: flex;
  align-items: center;
  gap: 46px;
  background: var(--white);
  border-radius: 30px;
  padding: 0 60px;
}

.tl2-arrow {
  flex: 0 0 112px;
  width: 112px;
  height: 112px;
  padding: 0;
  border-radius: 50%;
  border: 5px solid var(--teal);
  background: var(--white);
  color: var(--teal);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 0.18s, color 0.18s, opacity 0.18s, transform 0.12s;
}
.tl2-arrow svg { width: 46px; height: 46px; }
.tl2-arrow:active:not(:disabled) {
  background: var(--teal);
  color: var(--white);
  transform: scale(0.94);
}
.tl2-arrow:disabled {
  cursor: default;
  opacity: 0.3;
  border-color: #9DAAB1;
  color: #9DAAB1;
}

/* Equal columns rather than proportional dates: 10 of the 17 milestones fall
   after 2008, so spacing them by year would collapse them into an untappable
   cluster. Even columns keep every target ~180px wide. */
.tl2-rail {
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  align-self: stretch;
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  align-items: start;
}

/* The line spans dot-centre to dot-centre, so it is inset by half a column. */
.tl2-rail-line,
.tl2-rail-fill {
  position: absolute;
  top: var(--tl2-line-y);
  height: 6px;
  border-radius: 3px;
  transform: translateY(-50%);
  left: calc(50% / var(--tl2-n));
}
.tl2-rail-line {
  right: calc(50% / var(--tl2-n));
  background: #C9D2D7;
}
.tl2-rail-fill {
  /* first dot-centre to active dot-centre = i/n of the rail width */
  width: calc(100% * var(--tl2-i) / var(--tl2-n));
  background: var(--teal);
  transition: width 0.38s cubic-bezier(0.22, 1, 0.36, 1);
}

.tl2-node {
  display: grid;
  justify-items: center;
  row-gap: 26px;
  padding: 62px 0 0;
  background: none;
  border: none;
  cursor: pointer;
  position: relative;
  z-index: 1;
  -webkit-tap-highlight-color: transparent;
}

/* Fixed height so the active dot growing --tl2-dot → --tl2-dot-active never
   nudges the year label or the rail line off centre. */
.tl2-node-hit {
  height: var(--tl2-slot);
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Three states, and only one of them is a lighter tint: reached years are the
   same solid teal as the selected one and read as smaller, not faded. Only
   years still ahead are grey. */
.tl2-node-dot {
  width: var(--tl2-dot);
  height: var(--tl2-dot);
  border-radius: 50%;
  background: #C9D2D7;
  transition: width 0.3s, height 0.3s, background 0.3s, box-shadow 0.3s;
}
.tl2-node:active .tl2-node-dot { background: var(--teal); }
.tl2-node--past .tl2-node-dot { background: var(--teal); }
.tl2-node--active .tl2-node-dot {
  width: var(--tl2-dot-active);
  height: var(--tl2-dot-active);
  background: var(--teal);
  box-shadow: 0 0 0 var(--tl2-halo) rgba(0, 123, 132, 0.14);
}

.tl2-node-year {
  font-family: var(--font);
  font-size: var(--tl2-year);
  font-weight: 700;
  line-height: 1;
  color: #8B9AA2;
  white-space: nowrap;
  transition: color 0.3s, font-size 0.3s;
}
.tl2-node--past .tl2-node-year { color: #5E727C; }
.tl2-node--active .tl2-node-year {
  font-size: var(--tl2-year-active);
  color: var(--teal);
}

/* ── Step rows (MSS p3) ── */

.body-step-icon {
  position: relative;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
.body-step-num {
  width: 160px;
  height: 160px;
  border-radius: 50%;
  background: var(--teal);
  color: var(--white);
  font-family: var(--font);
  font-size: 96px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.body-step-content {
  display: flex;
  flex-direction: column;
  gap: 15px;
  flex: 1 1 0;
  min-width: 0;
}

/* ── Animated entry for body panels ── */

.body-panel {
  animation: cardFadeIn var(--anim-card-dur) cubic-bezier(0.22, 1, 0.36, 1) both;
  animation-delay: calc(var(--anim-card-offset) + var(--anim-card-stagger) * var(--panel-i, 0));
}

/* Bottom bar with frosted glass pill */
.section-bottom-bar {
  position: relative;
  z-index: 2;
  width: 3840px;
  height: 558px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
.section-bottom-pill {
  width: 3540px;
  height: 358px;
  border-radius: 50px;
  background: rgba(255,255,255,0.50);
  backdrop-filter: blur(15px);
  -webkit-backdrop-filter: blur(15px);
  display: flex;
  align-items: center;
  padding-left: 150px;
  padding-right: 150px;
}
.section-bottom-left {
  display: flex;
  align-items: center;
  gap: 80px;
}
.section-bottom-center {
  flex: 1;
}
.section-bottom-right {
  display: flex;
  flex-direction: column-reverse;
  align-items: center;
  gap: 24px;
}
.section-bottom-pagination {
  display: flex;
  align-items: center;
  gap: 50px;
}
.section-bottom-dot {
  width: 75px;
  height: 75px;
  border-radius: 50%;
  background: transparent;
  border: 4px solid var(--teal);
  cursor: pointer;
  outline: none;
  padding: 0;
}
.section-bottom-dot-active {
  background: var(--teal);
  border: none;
}
.section-bottom-label {
  font-family: var(--font);
  font-size: 64px;
  font-weight: 400;
  color: var(--navy);
}


/* =============================================================================
   9. SECTIONVIEW -- GREY BACKGROUND POLYGONS
   Large angular shapes behind the card grid. White edge lines with dark
   shadows separate grey tones. Shapes vary per page via data-page attribute.
   ============================================================================= */

.section-grey-shapes {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}

.section-grey-wrap {
  position: absolute;
  inset: 0;
  filter:
    drop-shadow(0 0 1px #fff)
    drop-shadow(2px 3px 5px rgba(0, 0, 0, 0.18));
}

.section-grey {
  position: absolute;
  inset: 0;
}

/* ── Page 1: Home-like diagonal sweep (left-to-bottom, teal bottom-left)
      grey-1: large right+bottom slab with diagonal notch top-left
      grey-2: lower diagonal band that creates tonal depth near bottom
      grey-3: top horizontal band — keeps card area at lightest tone      ── */
.section-root[data-page="1"] .section-grey-1 {
  background: #CCD4D9;
  clip-path: polygon(40% 0%, 100% 0%, 100% 100%, 0% 100%, 0% 18%);
}
.section-root[data-page="1"] .section-grey-2 {
  background: #D5DCE0;
  clip-path: polygon(0% 60%, 100% 36%, 100% 100%, 0% 100%);
}
.section-root[data-page="1"] .section-grey-3 {
  background: #DDE3E6;
  clip-path: polygon(0% 0%, 93% 0%, 0% 35.5%);
}

/* ── Page 2: Centre-split diagonal (vertical bands, teal bottom-right)
      grey-1: right diagonal band, anchors the A mark
      grey-2: left diagonal band, complements grey-1 across the full canvas
      grey-3: shallow top strip — lightens the header/card area      ── */
.section-root[data-page="2"] .section-grey-1 {
  background: #CCD4D9;
  clip-path: polygon(62% 0%, 100% 0%, 100% 100%, 38% 100%);
}
.section-root[data-page="2"] .section-grey-2 {
  background: #D5DCE0;
  clip-path: polygon(0% 0%, 62% 0%, 38% 100%, 0% 100%);
}
.section-root[data-page="2"] .section-grey-3 {
  background: #DDE3E6;
  clip-path: polygon(0% 0%, 55% 0%, 0% 21%);
}

/* ── Page 3: Reverse low diagonal (top-right to bottom-left, teal bottom-center)
      grey-1: sweeps from near the top-right down to the bottom-left
      grey-2: top-left wedge left uncovered by grey-1 (lighter cap)
      grey-3: diagonal band overlaid across upper-middle for card readability      ── */
.section-root[data-page="3"] .section-grey-1 {
  background: #CCD4D9;
  clip-path: polygon(0% 24%, 100% 0%, 100% 100%, 0% 100%);
}
.section-root[data-page="3"] .section-grey-2 {
  background: #D5DCE0;
  clip-path: polygon(0% 0%, 100% 0%, 0% 24%);
}
.section-root[data-page="3"] .section-grey-3 {
  background: #DDE3E6;
  clip-path: polygon(0% 18%, 100% 0%, 100% 38%, 0% 54%);
}

/* ── Page 4: Mirror of page 1 (right-to-bottom, teal bottom-right)
      grey-1: large left+bottom slab with diagonal notch top-right
      grey-2: lower diagonal band, rising left-to-right
      grey-3: top horizontal band — keeps the card area at the lightest tone      ── */
.section-root[data-page="4"] .section-grey-1 {
  background: #CCD4D9;
  clip-path: polygon(0% 0%, 60% 0%, 100% 18%, 100% 100%, 0% 100%);
}
.section-root[data-page="4"] .section-grey-2 {
  background: #D5DCE0;
  clip-path: polygon(0% 36%, 100% 60%, 100% 100%, 0% 100%);
}
.section-root[data-page="4"] .section-grey-3 {
  background: #DDE3E6;
  clip-path: polygon(7% 0%, 100% 0%, 100% 35.5%);
}


/* =============================================================================
   10. SECTIONVIEW -- TEAL ACCENT SHAPES (bottom)
   Compact angular slivers at screen bottom. Solid colour -- no white separator
   lines between teal shapes. Shapes vary per page.
   ============================================================================= */

.section-bottom-shapes {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 3840px;
  height: 750px;
  z-index: 1;
  pointer-events: none;
}

/* No filter -- teal shapes have no white separator lines */
.section-bshape-wrap {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

.section-bshape {
  position: absolute;
  bottom: 0;
  height: 100%;
}

/* Inset vignette and gradient overlay disabled -- matching home page solid colours */
.section-bshape-1::before,
.section-bshape-2::before {
  display: none;
}
.section-bshape-1::after,
.section-bshape-2::after {
  display: none;
}

/* Shared base positioning for all bshapes */
.section-bshape-1,
.section-bshape-2 {
  left: 0;
  width: 100%;
}

/* ── Page 1: bottom-left cluster ── */
.section-root[data-page="1"] .section-bshape-1 {
  background: #99CED1;
  clip-path: polygon(0% 36%, 20% 52%, 58% 100%, 0% 100%);
}
.section-root[data-page="1"] .section-bshape-2 {
  background: #4DA8AF;
  clip-path: polygon(20% 54%, 58% 100%, 8% 100%);
}

/* ── Page 2: bottom-right cluster ── */
.section-root[data-page="2"] .section-bshape-1 {
  background: #99CED1;
  clip-path: polygon(40% 100%, 76% 40%, 100% 52%, 100% 100%);
}
.section-root[data-page="2"] .section-bshape-2 {
  background: #4DA8AF;
  clip-path: polygon(62% 64%, 100% 0%, 100% 62%, 100% 100%, 68% 100%);
}

/* ── Page 3: bottom center-left spread ── */
.section-root[data-page="3"] .section-bshape-1 {
  background: #99CED1;
  clip-path: polygon(0% 44%, 55% 22%, 76% 100%, 0% 100%);
}
.section-root[data-page="3"] .section-bshape-2 {
  background: #4DA8AF;
  clip-path: polygon(28% 33%, 58% 21%, 76% 100%, 20% 100%);
}

/* ── Page 4: bottom-right cluster (mirror of page 1) ── */
.section-root[data-page="4"] .section-bshape-1 {
  background: #99CED1;
  clip-path: polygon(100% 36%, 80% 52%, 42% 100%, 100% 100%);
}
.section-root[data-page="4"] .section-bshape-2 {
  background: #4DA8AF;
  clip-path: polygon(80% 54%, 42% 100%, 92% 100%);
}


/* =============================================================================
   11. NAVBAR
   Frosted-glass pill with a row of icon buttons. Active state uses teal fill.
   ============================================================================= */

.navbar-root {
  width: 3840px;
  height: 558px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.navbar-pill {
  width: 3540px;
  height: 358px;
  border-radius: 50px;
  background: rgba(255,255,255,0.50);
  backdrop-filter: blur(15px);
  -webkit-backdrop-filter: blur(15px);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 90px;
  padding-left: 115px;
  padding-right: 115px;
}
.navbar-btn {
  width: 250px;
  height: 250px;
  border-radius: 50px;
  flex-shrink: 0;
  background: var(--white);
  border: 4px solid var(--teal);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 9px 14px 40px rgba(0,0,0,0.05);
  transition:
    background-color var(--anim-nav-color-dur) ease,
    border-color     var(--anim-nav-color-dur) ease,
    transform        var(--anim-nav-icon-dur)  cubic-bezier(0.34, 1.56, 0.64, 1);
  outline: none;
  padding: 0;
}
.navbar-btn-active {
  background: var(--teal);
  border: none;
  transform: scale(1.1);
}
.navbar-btn-active .navbar-btn-img--svg {
  filter: brightness(0) invert(1);
}
.navbar-btn-img--svg {
  transition: filter var(--anim-nav-svg-dur) ease;
}
.navbar-btn-img {
  width: 175px;
  height: 175px;
  object-fit: contain;
}


/* =============================================================================
   12. HAMBURGER MENU
   Full-height slide-in panel from the left.
   ============================================================================= */

/* Dimmed blur overlay behind the panel */
.hamburger-overlay {
  position: absolute;
  inset: 0;
  z-index: 200;
  background: rgba(0,0,0,0.30);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  opacity: 0;
  transition: opacity var(--anim-menu-overlay-dur) ease;
  pointer-events: none;
}
.hamburger-overlay--open {
  opacity: 1;
  pointer-events: auto;
}

.hamburger-panel {
  position: absolute;
  top: 0;
  left: 0;
  width: 1575px;
  height: 2160px;
  z-index: 300;
  display: flex;
  flex-direction: column;
  background: var(--white);
  box-shadow: 40px 0 80px rgba(0,0,0,0.25);
  transform: translateX(-100%);
  transition: transform var(--anim-menu-slide-dur) cubic-bezier(0.4, 0, 0.2, 1);
  pointer-events: none;
}
.hamburger-panel--open {
  transform: translateX(0);
  pointer-events: auto;
}

/* Teal triangle — top-right corner accent */
.hamburger-panel::after {
  content: '';
  position: absolute;
  top: 0;
  right: 0;
  width: 0;
  height: 0;
  border-style: solid;
  border-width: 0 200px 200px 0;
  border-color: transparent var(--teal) transparent transparent;
  pointer-events: none;
  z-index: 1;
}

.hamburger-header {
  width: 100%;
  height: 332px;
  background: #E4EAED;
  flex-shrink: 0;
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: center;
  padding-left: 202px;
  padding-right: 202px;
}

/* Grey polygon shapes inside the header — mirrors the home page diagonal language */
.hamburger-header-shape {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
/* Darkest: large right slab */
.hamburger-header-shape-1 {
  background: #CCD4D9;
  clip-path: polygon(46% 0%, 100% 0%, 100% 100%, 28% 100%);
  filter: drop-shadow(0 0 1px #fff) drop-shadow(2px 3px 5px rgba(0,0,0,0.14));
}
/* Mid: narrower right strip */
.hamburger-header-shape-2 {
  background: #D5DCE0;
  clip-path: polygon(72% 0%, 100% 0%, 100% 100%, 60% 100%);
  filter: drop-shadow(0 0 1px #fff) drop-shadow(2px 3px 5px rgba(0,0,0,0.12));
}
/* Light: top-left wedge cap */
.hamburger-header-shape-3 {
  background: #DDE3E6;
  clip-path: polygon(0% 0%, 46% 0%, 28% 100%, 0% 100%);
  filter: drop-shadow(0 0 1px #fff) drop-shadow(2px 3px 5px rgba(0,0,0,0.10));
}

.hamburger-header-title {
  font-family: var(--font);
  font-size: 128px;
  font-weight: 700;
  color: var(--navy);
  text-transform: uppercase;
  letter-spacing: 0.01em;
  line-height: 1;
  position: relative;
  z-index: 1;
}

.hamburger-section-list {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-left: 40px;
  padding-right: 40px;
}
.hamburger-section-btn {
  width: 100%;
  height: 147px;
  background: none;
  border: none;
  border-bottom: 1px solid rgba(23,53,66,0.10);
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-left: 162px;
  padding-right: 162px;
  cursor: pointer;
  outline: none;
}
.hamburger-section-label {
  font-family: var(--font);
  font-size: 64px;
  font-weight: 600;
  text-align: left;
}

.hamburger-bottom-bar {
  width: 100%;
  height: 300px;
  flex-shrink: 0;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-left: 202px;
  padding-right: 202px;
}
.hamburger-home-btn {
  background: none;
  border: none;
  cursor: pointer;
  padding: 0;
  outline: none;
  flex-shrink: 0;
}
.hamburger-applied-btn {
  height: 150px;
  width: 722px;
  flex-shrink: 0;
  border-radius: 30px;
  border: 4px solid var(--teal);
  background: var(--white);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  outline: none;
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
}
.hamburger-applied-label {
  font-family: var(--font);
  font-size: 64px;
  font-weight: 400;
  color: var(--navy);
}
.hamburger-close-btn {
  width: 113px;
  height: 113px;
  border-radius: 50%;
  border: 4px solid var(--navy);
}


/* =============================================================================
   13. ANIMATIONS
   @keyframes definitions followed by the rules that apply them.
   ============================================================================= */

/* Slide up + fade in -- titles, subtitle, section header */
@keyframes fadeInUp {
  from { opacity: 0; transform: translateY(60px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Softer slide up -- reserved for secondary elements */
@keyframes fadeInUpSoft {
  from { opacity: 0; transform: translateY(40px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Infinite horizontal nudge on the Learn More arrow */
@keyframes arrowNudge {
  0%, 100% { transform: translateX(0); }
  50%       { transform: translateX(18px); }
}

/* Card entry -- slight scale + slide */
@keyframes cardFadeIn {
  from { opacity: 0; transform: translateY(50px) scale(0.97); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}

/* Zoom-in-from-center entry, scoped to Service Centers 4_3's card grid only
   (.body-card-zoom, applied in PageBody's cardGrid branch for
   page-service-centers-4) -- more dynamic than the sitewide cardFadeIn slide-up.
   Does not touch cardFadeIn or any other page's animation. */
@keyframes cardZoomIn {
  from { opacity: 0; transform: scale(0.6); }
  to   { opacity: 1; transform: scale(1); }
}
/* Higher specificity than .body-panel's shorthand `animation` so only the
   animation-name changes; duration/easing come from .body-panel. Delay is
   overridden here with a wider per-card stagger (0.40s vs. the sitewide
   0.10s -- doubled again per client feedback, was 0.20s) so the 4x2 grid
   reads as a deliberate cascade -- scoped to this page only, does not touch
   --anim-card-stagger used by .section-card and every other page's card
   fade-in. */
.body-card-zoom.body-panel {
  animation-name: cardZoomIn;
  animation-delay: calc(var(--anim-card-offset) + 0.40s * var(--panel-i, 0));
}

/* Ken Burns slow pan across the hero image */
@keyframes homeHeroPan {
  0%   { transform: translate3d(-1.5%, -1%, 0); opacity: 0; }
  8%   { opacity: 1; }
  100% { transform: translate3d(-5.5%, -5%, 0); opacity: 1; }
}

/* -- Apply animations -- */

/* HomeScreen title */
.home-title {
  animation: fadeInUp var(--anim-fade-dur) cubic-bezier(0.22, 1, 0.36, 1) both;
}

/* HomeScreen subtitle -- slightly delayed */
.home-subtitle {
  animation: fadeInUp var(--anim-fade-dur) cubic-bezier(0.22, 1, 0.36, 1) var(--anim-fade-stagger) both;
}

/* Learn More arrow nudge */
.home-btn-learn svg {
  animation: arrowNudge var(--anim-arrow-period) cubic-bezier(0.37, 0, 0.63, 1) var(--anim-arrow-initial-delay) infinite;
}
.home-btn-learn:hover svg {
  animation: arrowNudge var(--anim-arrow-period-hover) cubic-bezier(0.37, 0, 0.63, 1) infinite;
}

/* SectionView header */
.section-header {
  animation: fadeInUp var(--anim-header-dur) cubic-bezier(0.22, 1, 0.36, 1) both;
}

/* SectionView cards -- staggered entry */
.section-card {
  animation: cardFadeIn var(--anim-card-dur) cubic-bezier(0.22, 1, 0.36, 1) both;
}
.section-card:nth-child(1) { animation-delay: calc(var(--anim-card-offset) + var(--anim-card-stagger) * 0); }
.section-card:nth-child(2) { animation-delay: calc(var(--anim-card-offset) + var(--anim-card-stagger) * 1); }
.section-card:nth-child(3) { animation-delay: calc(var(--anim-card-offset) + var(--anim-card-stagger) * 2); }
.section-card:nth-child(4) { animation-delay: calc(var(--anim-card-offset) + var(--anim-card-stagger) * 3); }
.section-card:nth-child(5) { animation-delay: calc(var(--anim-card-offset) + var(--anim-card-stagger) * 4); }
.section-card:nth-child(6) { animation-delay: calc(var(--anim-card-offset) + var(--anim-card-stagger) * 5); }

/* ─── PDF Viewer Screen ─────────────────────────────────────── */
/* .section-root is also applied to .pdf-root for background shapes */
.pdf-root {
  position: relative;
}

/* Body — 3 columns, fills all space between top edge and bottom bar */
.pdf-body {
  position: relative;
  z-index: 2;
  flex: 1 1 0;
  min-height: 0;
  display: flex;
  flex-direction: row;
  align-items: stretch;
  gap: 50px;
  padding: 50px 150px 0;
  box-sizing: border-box;
}

/* Col 1 — wrapper for card + QR below */
.pdf-col-1 {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 60px;
}

/* Col 1 — callout card (reuses .body-card base styles) */
.pdf-callout-card {
  flex: 0 0 auto;
  gap: 40px;
  text-align: center;
  align-items: center;
}
.pdf-callout-card .body-card-heading {
  font-size: 72px;
  line-height: 1.15;
  color: var(--teal);
  width: 100%;
}

/* QR code sits directly on the background below the card */
.pdf-qr-below {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
}
.pdf-qr-below img {
  width: 340px;
  height: 340px;
  object-fit: contain;
  display: block;
}
.pdf-page-panel {
  flex: 1;
  align-self: stretch;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.pdf-canvas {
  max-width: 100%;
  max-height: 100%;
  display: block;
}

/* Full-width pill; left group is sized to push pagination under the PDF columns.
   Col 2 starts at 1047px within pill inner content (1347px screen - 300px pill offset). */
.pdf-root .section-bottom-left {
  flex: 0 0 1047px;
}
.pdf-root .section-bottom-center {
  display: none;
}
.pdf-bottom-pill {
  width: 3540px;
  height: 358px;
  border-radius: 50px;
  background: rgba(255, 255, 255, 0.50);
  backdrop-filter: blur(15px);
  -webkit-backdrop-filter: blur(15px);
  display: flex;
  align-items: center;
  padding: 0 150px;
  box-sizing: border-box;
}
/* Center group: Back + label bar + Next */
.pdf-bottom-center {
  display: flex;
  align-items: center;
  gap: 40px;
  flex: 1;
  justify-content: flex-end;
}
/* Back / Next / First / Last — match home-btn-tertiary height (150px) */
.pdf-nav-btn {
  width: 280px;
  height: 150px;
  border-radius: 30px;
  border: 4px solid var(--teal);
  background: rgba(255, 255, 255, 0.50);
  backdrop-filter: blur(15px);
  -webkit-backdrop-filter: blur(15px);
  font-family: var(--font);
  font-size: 64px;
  font-weight: 400;
  color: var(--navy);
  cursor: pointer;
  outline: none;
  flex-shrink: 0;
}
.pdf-nav-btn:disabled {
  opacity: 0.3;
  cursor: default;
}
.pdf-pagination-bar {
  flex: 1;
  height: 150px;
  border-radius: 30px;
  background: rgba(255, 255, 255, 0.50);
  backdrop-filter: blur(15px);
  -webkit-backdrop-filter: blur(15px);
  border: 4px solid var(--teal);
  color: var(--navy);
  font-family: var(--font);
  font-size: 64px;
  font-weight: 400;
  display: flex;
  align-items: center;
  justify-content: center;
  font-style: italic;
}
