/* =========================================================
   ZEISS Frutiger Next — the corporate typeface

   The official W07 set supplied by ZEISS, subset to Latin and
   converted to WOFF2: 836K of TTF becomes 221K, and the two faces
   the page preloads drop from 218K to 66K. Self-hosted, so nothing
   is fetched from anywhere else.
   ========================================================= */
@font-face {
  font-family: "ZEISS Frutiger Next";
  src: url("../fonts/ZEISSFrutigerNextW07-Light.woff2") format("woff2");
  font-weight: 300; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "ZEISS Frutiger Next";
  src: url("../fonts/ZEISSFrutigerNextW07-Rg.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "ZEISS Frutiger Next";
  src: url("../fonts/ZEISSFrutigerNextW07-Italic.woff2") format("woff2");
  font-weight: 400; font-style: italic; font-display: swap;
}
@font-face {
  font-family: "ZEISS Frutiger Next";
  src: url("../fonts/ZEISSFrutigerNextW07-Medium.woff2") format("woff2");
  font-weight: 500; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "ZEISS Frutiger Next";
  src: url("../fonts/ZEISSFrutigerNextW07-Bold.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "ZEISS Frutiger Next";
  src: url("../fonts/ZEISSFrutigerNextW07-BoldIt.woff2") format("woff2");
  font-weight: 700; font-style: italic; font-display: swap;
}
@font-face {
  font-family: "ZEISS Frutiger Next";
  src: url("../fonts/ZEISSFrutigerNextW07-Heavy.woff2") format("woff2");
  font-weight: 800; font-style: normal; font-display: swap;
}

/* =========================================================
   ZEISS VISION CENTER MALAYSIA — design tokens
   ========================================================= */
:root {
  /* ---------------------------------------------------------------
     Colour, built on the two blues the ZEISS CD Manual actually
     defines: Indigo (Pantone Reflex Blue C) and Cyan (Process Blue C).
     Everything between them is a straight interpolation, so the whole
     site sits on the ZEISS axis rather than the petrol-navy it used
     to. White still dominates, exactly as the manual asks.

     ZEISS Cyan is only 3.87:1 on white, so it is never used for small
     text — links and eyebrows take --brand-600, and Cyan is kept for
     accents, focus rings and decoration.
     --------------------------------------------------------------- */
  --zeiss-indigo: #141E8C;   /* the logo colour — reserved for the mark */
  --zeiss-cyan:   #0088D0;

  --brand-950: #080B35;
  --brand-900: #0B104D;
  --brand-800: #10176D;
  --brand-700: #141E8C;
  --brand-600: #0958B1;
  --brand-500: #0088D0;
  --brand-400: #47A9DD;
  --brand-200: #CCE7F6;
  --brand-100: #EBF5FB;
  --brand-50:  #F6FBFD;

  --ink:       #10162E;
  --ink-soft:  #39415C;
  --muted:     #5A6178;
  --line:      #E2E7F0;
  --surface:   #ffffff;
  --surface-2: #F6F8FC;

  --radius-sm: 6px;

  /* One shared height for every form control, so a date input and the
     select beside it always line up. */
  --control-h: 3rem;
  /* Form controls carry their own line, darker than the hairline that
     divides panels, and darker still under the pointer. */
  --field-line:        #9AA6B8;
  --field-line-strong: #6B7A90;
  --field-hint:        #78859A;
  --radius:    12px;
  --radius-lg: 20px;

  --shadow-sm: 0 1px 2px rgba(11, 16, 77, .06), 0 2px 8px rgba(11, 16, 77, .05);
  --shadow-md: 0 8px 24px rgba(11, 16, 77, .10);
  --shadow-lg: 0 24px 60px rgba(11, 16, 77, .16);

  /* Wider than before, but still a measured column rather than full bleed. */
  --container: 1480px;
  --gutter: clamp(1rem, 3.5vw, 3rem);
  /* Prose stays readable even though the shell grew. */
  --measure: 78ch;

  /* One place to tune the vertical rhythm. Sections used to run to 5.5rem
     top and bottom, which left the page feeling loose. */
  --section-y:    clamp(2.25rem, 4.2vw, 3.6rem);
  --section-y-sm: clamp(1.5rem, 3vw, 2.4rem);
  --head-gap:     clamp(1.25rem, 2.4vw, 1.9rem);

  --step--1: clamp(.82rem, .8rem + .1vw, .9rem);
  --step-0:  clamp(1rem, .97rem + .16vw, 1.075rem);
  --step-1:  clamp(1.18rem, 1.1rem + .35vw, 1.4rem);
  --step-2:  clamp(1.45rem, 1.3rem + .7vw, 1.95rem);
  --step-3:  clamp(1.8rem, 1.5rem + 1.3vw, 2.7rem);
  --step-4:  clamp(2.2rem, 1.7rem + 2.3vw, 3.9rem);

  --ease: cubic-bezier(.22, .61, .36, 1);

  --font-sans: "ZEISS Frutiger Next", "Frutiger Next", "Frutiger",
               -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
               "Helvetica Neue", Helvetica, Arial, sans-serif;

  --header-h: 96px;
}

/* =========================================================
   Reset & base
   ========================================================= */
*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

body {
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--step-0);
  line-height: 1.65;
  color: var(--ink);
  background: var(--surface);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

h1, h2, h3, h4 { margin: 0 0 .6em; line-height: 1.15; font-weight: 500; letter-spacing: -.015em; color: var(--brand-900); }
h1 { font-size: var(--step-4); }
h2 { font-size: var(--step-3); }
h3 { font-size: var(--step-1); }
p  { margin: 0 0 1.1em; }
p:last-child { margin-bottom: 0; }

a { color: var(--brand-700); text-decoration-color: rgba(20, 30, 140, .32); text-underline-offset: .18em; transition: color .18s var(--ease); }
a:hover { color: var(--brand-600); }

img, svg, video { max-width: 100%; height: auto; display: block; }

ul, ol { padding-left: 1.15rem; }
li { margin-bottom: .4em; }

hr { border: 0; border-top: 1px solid var(--line); margin: 2.5rem 0; }
sup { font-size: .7em; }

:focus-visible { outline: 3px solid var(--brand-500); outline-offset: 2px; border-radius: 2px; }

.visually-hidden {
  position: absolute !important; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.skip-link {
  position: absolute; left: 1rem; top: -4rem; z-index: 999;
  background: var(--brand-700); color: #fff; padding: .7rem 1.2rem;
  border-radius: 0 0 var(--radius-sm) var(--radius-sm); text-decoration: none;
  transition: top .2s var(--ease);
}
.skip-link:focus { top: 0; color: #fff; }

/* Back to top parks focus on the heading so the next Tab starts from the top
   of the page. That is a programmatic focus, not a reader reaching for it, so
   it gets no ring. Anything actually tabbed to still shows one. */
main[tabindex="-1"]:focus,
main h1[tabindex="-1"]:focus,
main h2[tabindex="-1"]:focus { outline: none; }

.container { width: 100%; max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); }

/* =========================================================
   Buttons & small components
   ========================================================= */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .5rem;
  padding: .8rem 1.6rem;
  font: inherit; font-weight: 500; line-height: 1.2;
  color: #fff; background: var(--brand-700);
  border: 1px solid var(--brand-700); border-radius: 999px;
  text-decoration: none; cursor: pointer;
  transition: background .2s var(--ease), border-color .2s var(--ease), transform .2s var(--ease), box-shadow .2s var(--ease);
}
.btn:hover { background: var(--brand-800); border-color: var(--brand-800); color: #fff; transform: translateY(-1px); box-shadow: var(--shadow-md); }
.btn:active { transform: translateY(0); }
.btn--light { background: #fff; color: var(--brand-900); border-color: #fff; }
.btn--light:hover { background: var(--brand-100); border-color: var(--brand-100); color: var(--brand-900); }
.btn--sm { padding: .55rem 1.1rem; font-size: var(--step--1); }
.btn--block { width: 100%; }

.link-arrow { display: inline-flex; align-items: center; gap: .4rem; font-weight: 500; text-decoration: none; }
.link-arrow::after { content: "\2192"; transition: transform .2s var(--ease); }
.link-arrow:hover::after { transform: translateX(3px); }

.eyebrow {
  margin: 0 0 .8rem; font-size: var(--step--1); font-weight: 500;
  letter-spacing: .14em; text-transform: uppercase; color: var(--brand-600);
}
.eyebrow--light { color: rgba(255, 255, 255, .85); }

.alert { margin: 1.5rem 0; padding: 1rem 1.25rem; border-radius: var(--radius); border: 1px solid; font-size: var(--step--1); }
.alert--success { background: #eaf6ee; border-color: #b6ddc3; color: #17603a; }
.alert--error   { background: #fdeced; border-color: #f2bfc2; color: #8f1d26; }

.empty-state { color: var(--muted); font-style: italic; }

/* =========================================================
   Header

   Two decks on a wide screen so the ZEISS mark gets real presence:
   a tall brand deck carrying the logo, the utilities and the booking
   button, and beneath it a full-width navigation rail. Below 1080px the
   rail folds away and everything collapses into the drawer.
   ========================================================= */
.site-header {
  position: sticky; top: 0; z-index: 100;
  background: rgba(255, 255, 255, .94);
  backdrop-filter: saturate(180%) blur(14px);
  border-bottom: 1px solid var(--line);
  transition: box-shadow .25s var(--ease);
}
.site-header.is-scrolled { box-shadow: var(--shadow-sm); }

/* --- Deck one: the brand --------------------------------- */
.site-header__bar { position: relative; }
.site-header__inner {
  display: flex; align-items: center; gap: 1rem;
  min-height: 72px;
  padding-block: .35rem;
  transition: min-height .28s var(--ease);
}

.brand { flex: 0 0 auto; display: flex; align-items: center; text-decoration: none; min-width: 0; }
/*
 * The mark is capped by height as well as width.
 *
 * Without the cap the deck's height is whatever the logo happens to work out
 * to, which had two consequences: the wordmark could reach past the bar and be
 * painted over by the navigation rail below it, cutting the second line in
 * half; and animating the logo's width animated the header's height with it,
 * so every state change dragged the whole page up or down for the length of
 * the transition. With a ceiling the logo can only ever be shorter than the
 * deck, so the deck's height is set by one rule instead of two.
 */
.brand__logo {
  display: block; height: auto;
  /*
   * 1.13.0 — the phone gets a bigger mark.
   *
   * The width used to be a clamp against the viewport, which cannot know
   * how much room the menu button and the gutters need, so it had to be
   * set small enough to be safe on the narrowest phone anyone might have.
   * Written the other way round, as everything that is left after the
   * gutters, the button and the gap beside it, the mark takes all the
   * room there actually is: about 275px on a 390px phone where it used
   * to take 190px, and it still cannot collide with the button on a
   * 320px one.
   */
  width: min(302px, calc(100vw - 116px));
  max-height: 40px;
  transition: width .28s var(--ease), max-height .28s var(--ease);
}

/* The brand deck's right-hand utilities. */
.site-header__actions { margin-left: auto; display: flex; align-items: center; gap: .6rem; }

.header-social { display: none; align-items: center; gap: .35rem; }
.header-social a {
  display: grid; place-items: center; width: 34px; height: 34px;
  border-radius: 999px; color: var(--brand-700); background: var(--surface-2);
  transition: background .2s var(--ease), color .2s var(--ease), transform .2s var(--ease);
}
.header-social a:hover { background: var(--brand-700); color: #fff; transform: translateY(-2px); }

.search {
  display: none; align-items: center; gap: .25rem;
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: 999px; padding: .2rem .2rem .2rem .95rem;
  transition: border-color .2s var(--ease), box-shadow .2s var(--ease);
}
.search:focus-within { border-color: var(--brand-500); box-shadow: 0 0 0 3px var(--brand-100); }
.search input { width: 130px; border: 0; background: transparent; font: inherit; font-size: var(--step--1); padding: .4rem 0; color: var(--ink); }
.search input:focus { outline: none; }
.search button { display: grid; place-items: center; width: 34px; height: 34px; border: 0; border-radius: 999px; background: var(--brand-700); color: #fff; cursor: pointer; transition: background .2s var(--ease); }
.search button:hover { background: var(--brand-800); }

.nav-toggle {
  width: 46px; height: 46px; display: grid; place-items: center;
  background: transparent; border: 1px solid var(--line);
  border-radius: var(--radius-sm); cursor: pointer;
  transition: background .2s var(--ease), border-color .2s var(--ease);
}
.nav-toggle:hover { background: var(--surface-2); border-color: var(--brand-500); }
.nav-toggle__bars, .nav-toggle__bars::before, .nav-toggle__bars::after {
  display: block; width: 22px; height: 2px; border-radius: 2px;
  background: var(--brand-900); transition: transform .25s var(--ease), background .25s var(--ease);
}
.nav-toggle__bars { position: relative; }
.nav-toggle__bars::before, .nav-toggle__bars::after { content: ""; position: absolute; left: 0; }
.nav-toggle__bars::before { top: -6px; }
.nav-toggle__bars::after  { top: 6px; }
.nav-toggle[aria-expanded="true"] .nav-toggle__bars { background: transparent; }
.nav-toggle[aria-expanded="true"] .nav-toggle__bars::before { transform: translateY(6px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] .nav-toggle__bars::after  { transform: translateY(-6px) rotate(-45deg); }

/* --- Deck two: the navigation rail ------------------------ */
.site-header__rail { display: none; border-top: 1px solid var(--line); background: rgba(245, 248, 251, .8); }
.site-header__rail-inner { display: flex; align-items: center; gap: 1rem; }

.main-nav { flex: 1 1 auto; min-width: 0; }
.main-nav__list { display: flex; flex-wrap: nowrap; gap: .1rem; margin: 0; padding: 0; list-style: none; }
.main-nav__item { position: relative; margin: 0; }
.main-nav__link {
  position: relative; display: inline-flex; align-items: center; gap: .32rem;
  padding: .95rem .8rem; font-size: var(--step--1); font-weight: 500;
  letter-spacing: .01em; color: var(--ink-soft); text-decoration: none;
  white-space: nowrap; transition: color .2s var(--ease);
}
/* A hairline that grows out of the middle on hover. */
.main-nav__link::before {
  content: ""; position: absolute; left: 50%; right: 50%; bottom: 0; height: 2px;
  background: var(--brand-500); border-radius: 2px;
  transition: left .25s var(--ease), right .25s var(--ease);
}
.main-nav__link:hover, .main-nav__link.is-active { color: var(--brand-700); }
.main-nav__link:hover::before,
.main-nav__link.is-active::before,
.main-nav__item:focus-within > .main-nav__link::before { left: .8rem; right: .8rem; }

/* The "there is more under here" caret. */
.nav-caret { flex: 0 0 auto; transition: transform .25s var(--ease); opacity: .65; }
.main-nav__item:hover > .main-nav__link .nav-caret,
.main-nav__item:focus-within > .main-nav__link .nav-caret { transform: rotate(180deg); opacity: 1; }

.dropdown {
  position: absolute; top: 100%; left: 0; z-index: 20; min-width: 260px;
  padding: .6rem; background: #fff; border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow-lg);
  opacity: 0; visibility: hidden; transform: translateY(8px);
  transition: opacity .2s var(--ease), transform .2s var(--ease), visibility .2s;
}
.main-nav__item:hover > .dropdown,
.main-nav__item:focus-within > .dropdown { opacity: 1; visibility: visible; transform: translateY(0); }
/* Keep the last items on screen rather than off the right edge. */
.main-nav__item:nth-last-child(-n+3) > .dropdown { left: auto; right: 0; }

.dropdown__list, .dropdown__sub { margin: 0; padding: 0; list-style: none; }
.dropdown__item { margin: 0; }
.dropdown__link {
  display: flex; align-items: center; justify-content: space-between; gap: .5rem;
  padding: .6rem .75rem; border-radius: var(--radius-sm);
  font-size: var(--step--1); color: var(--ink-soft); text-decoration: none;
  transition: background .18s var(--ease), color .18s var(--ease), padding-left .18s var(--ease);
}
.dropdown__link:hover { background: var(--surface-2); color: var(--brand-700); padding-left: 1rem; }
.dropdown__sub { padding-left: .75rem; border-left: 2px solid var(--line); margin: .1rem 0 .4rem .75rem; }
.dropdown__sub a { display: block; padding: .4rem .6rem; font-size: var(--step--1); color: var(--muted); text-decoration: none; border-radius: var(--radius-sm); transition: background .18s var(--ease), color .18s var(--ease), padding-left .18s var(--ease); }
.dropdown__sub a:hover { color: var(--brand-700); background: var(--surface-2); padding-left: .85rem; }

/* =========================================================
   1.8.0 — where you are

   Two levels of marking. A branch containing the open page stays lit, so
   a brand page keeps What's New underlined and its own section named in
   the dropdown. The page itself gets a solid rule beside it and carries
   aria-current, which is the part a screen reader announces.
   ========================================================= */
.dropdown__link.is-active,
.dropdown__sub a.is-active { color: var(--brand-700); background: var(--brand-100); }

.dropdown__link.is-current,
.dropdown__sub a.is-current {
  color: var(--brand-700); font-weight: 500;
  background: var(--brand-100);
  box-shadow: inset 3px 0 0 var(--brand-600);
}

/* The rail's parent keeps its hairline while a child page is open. */
.main-nav__link.is-current { color: var(--brand-700); font-weight: 700; }

/* The drawer, same idea at the sizes where the rail is folded away. */
.mobile-nav__row > a.is-active { color: var(--brand-700); }
.mobile-nav__row > a.is-current { color: var(--brand-700); font-weight: 700; }
.mobile-nav__sub a.is-active { color: var(--brand-700); }
.mobile-nav__sub a.is-current {
  color: var(--brand-700); font-weight: 500;
  box-shadow: inset 3px 0 0 var(--brand-600);
  padding-left: .55rem; border-radius: var(--radius-sm);
}

.site-header__rail .btn { flex: 0 0 auto; }

/* --- The drawer ------------------------------------------- */
.mobile-nav {
  border-top: 1px solid var(--line); background: #fff;
  max-height: calc(100dvh - 72px); overflow-y: auto;
  padding: 1rem var(--gutter) 2rem;
}
.mobile-nav__list, .mobile-nav__sub { list-style: none; margin: 0; padding: 0; }
.mobile-nav__item { border-bottom: 1px solid var(--line); }
.mobile-nav__row { display: flex; align-items: center; justify-content: space-between; gap: .5rem; }
.mobile-nav__row > a { flex: 1; padding: .95rem 0; font-weight: 500; color: var(--ink); text-decoration: none; }
.mobile-nav__expand {
  display: grid; place-items: center; width: 42px; height: 42px; flex: 0 0 auto;
  background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--radius-sm);
  color: var(--brand-700); cursor: pointer;
  transition: background .2s var(--ease), border-color .2s var(--ease);
}
.mobile-nav__expand:hover { background: var(--brand-100); border-color: var(--brand-500); }
.mobile-nav__expand svg { transition: transform .28s var(--ease); }
.mobile-nav__expand[aria-expanded="true"] { background: var(--brand-700); border-color: var(--brand-700); color: #fff; }
.mobile-nav__expand[aria-expanded="true"] svg { transform: rotate(180deg); }
.mobile-nav__sub { padding: 0 0 .7rem .9rem; border-left: 2px solid var(--brand-100); margin-left: .2rem; }
.mobile-nav__sub a { display: block; padding: .55rem 0; font-size: var(--step--1); color: var(--ink-soft); text-decoration: none; }
.mobile-nav__sub a:hover { color: var(--brand-700); }
.mobile-nav__sub--deep { padding-left: .9rem; }
.mobile-nav__footer { margin-top: 1.5rem; display: grid; gap: .75rem; }
.mobile-nav__social { display: flex; justify-content: center; gap: .6rem; margin-top: .3rem; }
.mobile-nav__social a { display: grid; place-items: center; width: 42px; height: 42px; border-radius: 999px; background: var(--surface-2); color: var(--brand-700); }
.search--mobile { display: flex; }
.search--mobile input { flex: 1; width: auto; }
.search--mobile button { width: auto; padding: 0 1rem; border-radius: 999px; }

/* --- Steps up ---------------------------------------------- */
@media (min-width: 480px) {
  .site-header__inner { min-height: 82px; }
  .brand__logo { width: min(336px, calc(100vw - 124px)); max-height: 44px; }
}

@media (min-width: 720px) {
  .site-header__inner { min-height: 92px; gap: 1.25rem; }
  .brand__logo { width: min(360px, calc(100vw - 268px)); max-height: 46px; }
  .header-social { display: flex; }
}

/* The rail appears — the logo can now take the full brand deck. */
@media (min-width: 1080px) {
  .site-header__inner { min-height: var(--header-h); }
  .brand__logo { width: clamp(286px, 23vw, 332px); max-height: 46px; }
  .site-header__rail { display: block; }
  .search { display: flex; }
  .nav-toggle { display: none; }
  .mobile-nav { display: none !important; }
}

@media (min-width: 1080px) and (max-width: 1279px) {
  .main-nav__link { padding-inline: .55rem; font-size: .82rem; }
  .search input { width: 100px; }
}

/* Scrolled: the brand deck tightens so more of the page shows.
   Driven by `is-compact`, not `is-scrolled`, because this rule changes the
   header's height and the shadow rule does not. See initHeader() in site.js
   for why the two are not allowed to share a threshold. */
@media (min-width: 1080px) {
  .site-header.is-compact .site-header__inner { min-height: 74px; }
  .site-header.is-compact .brand__logo { width: 252px; max-height: 34px; }
}
/* =========================================================
   Hero slideshow
   ========================================================= */
.hero { position: relative; }
.hero__track { position: relative; display: grid; }
.hero__slide {
  grid-area: 1 / 1; position: relative;
  min-height: clamp(420px, 62vh, 680px);
  display: grid; align-items: center;
  opacity: 0; visibility: hidden;
  transition: opacity .7s var(--ease), visibility .7s;
}
.hero__slide.is-active { opacity: 1; visibility: visible; }
.hero__image { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.hero__overlay { position: absolute; inset: 0; background: linear-gradient(100deg, rgba(11, 16, 77, .82) 0%, rgba(11, 16, 77, .55) 45%, rgba(11, 16, 77, .15) 100%); }
.hero__content { position: relative; padding-block: clamp(2.75rem, 5.5vw, 4.25rem); color: #fff; max-width: 900px; }
.hero__headline { color: #fff; font-size: var(--step-4); margin-bottom: .5em; }
.hero__subtext { font-size: var(--step-1); color: rgba(255, 255, 255, .88); margin-bottom: 1.8rem; max-width: 46ch; }
.hero__dots { position: absolute; left: 50%; bottom: 1.4rem; transform: translateX(-50%); display: flex; gap: .5rem; }
.hero__dot { width: 34px; height: 4px; padding: 0; border: 0; border-radius: 999px; background: rgba(255, 255, 255, .4); cursor: pointer; transition: background .2s var(--ease); }
.hero__dot.is-active { background: #fff; }

/* =========================================================
   Page hero
   ========================================================= */
.page-hero { position: relative; background: var(--surface-2); border-bottom: 1px solid var(--line); }
.page-hero__inner { padding-block: clamp(2rem, 4.5vw, 3.4rem); max-width: 1040px; }
.page-hero__title { margin-bottom: .35em; }
.page-hero__subtitle { font-size: var(--step-1); color: var(--brand-700); font-weight: 500; margin-bottom: .6em; }
.page-hero__intro { font-size: var(--step-1); color: var(--ink-soft); max-width: 60ch; }

.page-hero--image { background: var(--brand-900); border-bottom: 0; }
.page-hero--image .page-hero__bg { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: .38; }
.page-hero--image .page-hero__inner { position: relative; }
.page-hero--image .page-hero__title { color: #fff; }
.page-hero--image .page-hero__subtitle { color: rgba(255, 255, 255, .92); }
.page-hero--image .page-hero__intro { color: rgba(255, 255, 255, .82); }
.page-hero--image .eyebrow { color: var(--brand-500); }

.breadcrumbs { border-bottom: 1px solid var(--line); background: #fff; font-size: var(--step--1); }
.breadcrumbs__list { display: flex; flex-wrap: wrap; gap: .35rem; list-style: none; margin: 0; padding: .8rem 0; }
.breadcrumbs__list li { margin: 0; display: flex; align-items: center; gap: .35rem; color: var(--muted); }
.breadcrumbs__list li + li::before { content: "/"; color: var(--line); }
.breadcrumbs a { color: var(--muted); text-decoration: none; }
.breadcrumbs a:hover { color: var(--brand-700); }

/* =========================================================
   Sections & article content
   ========================================================= */
.section { padding-block: var(--section-y); }
.section__head { max-width: 820px; margin-bottom: var(--head-gap); }
.section__title { margin-bottom: .4em; }
.section__lead { font-size: var(--step-1); color: var(--muted); max-width: var(--measure); }

.article { padding-block: var(--section-y); }
/* Rhythm between the article's top-level blocks.
   Until 1.8.0 this only counted sections, so a wide figure sat flush against
   whatever followed it: on Cleaning Solutions the AntiFOG heading began
   immediately under the banner with no air at all. A full-width photograph
   also wants more room after it than a paragraph does, so that pairing gets
   half as much again. */
.article > section + section,
.article > section + figure,
.article > figure + figure { margin-top: var(--section-y); }
.article > figure.figure-wide + section { margin-top: calc(var(--section-y) * 1.5); }

.prose-block { max-width: var(--measure); }
.prose-block .lead { font-size: var(--step-1); color: var(--ink-soft); }
.note { padding: 1rem 1.25rem; background: var(--surface-2); border-left: 3px solid var(--brand-500); border-radius: 0 var(--radius-sm) var(--radius-sm) 0; font-size: var(--step--1); }

.stage { display: grid; gap: clamp(1.75rem, 4vw, 3.5rem); align-items: center; }
.stage__text { max-width: 62ch; }
.stage__number { display: inline-block; margin-bottom: .8rem; padding: .25rem .8rem; border-radius: 999px; background: var(--brand-100); color: var(--brand-700); font-size: var(--step--1); font-weight: 500; letter-spacing: .05em; text-transform: uppercase; }
.stage__lead { font-size: var(--step-1); color: var(--ink-soft); }
.stage__media { margin: 0; }
.stage__media img { width: 100%; border-radius: var(--radius-lg); box-shadow: var(--shadow-md); object-fit: cover; }
.stage__media img + img { margin-top: 1rem; }

@media (min-width: 900px) {
  .stage { grid-template-columns: 1fr 1fr; }
  .stage--reverse .stage__text { order: 2; }
  .stage--reverse .stage__media { order: 1; }
}

.step-block { max-width: 100%; }
.step-block__title { display: flex; flex-wrap: wrap; align-items: baseline; gap: .7rem; }
.step-block__title span { font-size: var(--step--1); font-weight: 500; text-transform: uppercase; letter-spacing: .12em; color: var(--brand-600); }

.card-grid { display: grid; gap: clamp(1.25rem, 3vw, 2rem); margin-top: 2rem; }
.card-grid--2 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr)); }
.card-grid--3 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr)); }
.card-grid--4 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 200px), 1fr)); }

.lens-card { background: #fff; border: 1px solid var(--line); border-radius: var(--radius-lg); overflow: hidden; padding-bottom: 1.4rem; transition: box-shadow .22s var(--ease), transform .22s var(--ease); }
.lens-card:hover { box-shadow: var(--shadow-md); transform: translateY(-2px); }
.lens-card__media { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 2px; background: var(--surface-2); }
.lens-card__media img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; }
.lens-card h3 { margin: 1.2rem 1.4rem .5rem; font-size: var(--step-1); }
.lens-card p { margin: 0 1.4rem; color: var(--muted); font-size: var(--step--1); }

.figure-wide { margin: 2rem 0 0; }
.figure-wide img { width: 100%; border-radius: var(--radius-lg); box-shadow: var(--shadow-sm); }

.age-card { display: block; text-align: center; text-decoration: none; color: var(--brand-900); font-weight: 500; }
.age-card img { border-radius: var(--radius); margin-bottom: .6rem; aspect-ratio: 1; object-fit: cover; transition: transform .25s var(--ease); }
.age-card:hover img { transform: scale(1.03); }

.stat-grid { display: grid; gap: 1.5rem; margin-top: 1.5rem; grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr)); }
.stat { padding: 1.5rem; background: var(--surface-2); border-radius: var(--radius); }
.stat__value { display: block; font-size: var(--step-2); font-weight: 500; color: var(--brand-700); line-height: 1.2; margin-bottom: .4rem; }
.stat__label { color: var(--muted); font-size: var(--step--1); }

.footnotes { margin-top: 3rem; padding-top: 1.5rem; border-top: 1px solid var(--line); font-size: .78rem; color: var(--muted); line-height: 1.6; }
.footnotes p { margin-bottom: .6em; }

.photo-grid { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fill, minmax(min(100%, 260px), 1fr)); margin-top: 2rem; }
.photo-grid figure { margin: 0; }
.photo-grid img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; border-radius: var(--radius); }

.cta-band { margin-top: var(--section-y); padding: var(--section-y) clamp(1.5rem, 4vw, 3rem); background: linear-gradient(120deg, var(--brand-900), var(--brand-700)); border-radius: var(--radius-lg); color: #fff; text-align: center; }
.cta-band h2 { color: #fff; }
.cta-band p { color: rgba(255, 255, 255, .85); max-width: 55ch; margin-inline: auto; }
.cta-band--full { border-radius: 0; margin-top: 0; padding-block: var(--section-y); }

.quote-band { margin-top: 3rem; padding: clamp(2rem, 5vw, 3rem); background: var(--surface-2); border-radius: var(--radius-lg); }
.quote-band blockquote { margin: 0; }
.quote-band p { font-size: var(--step-2); line-height: 1.4; color: var(--brand-900); font-weight: 300; }
.quote-band cite { display: block; margin-top: 1rem; font-style: normal; font-size: var(--step--1); font-weight: 500; letter-spacing: .08em; text-transform: uppercase; color: var(--brand-600); }

/* =========================================================
   Home page blocks
   ========================================================= */
.highlights { display: grid; gap: clamp(1.5rem, 3vw, 2.5rem); grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr)); padding-block: var(--section-y); }
.highlight { background: #fff; border: 1px solid var(--line); border-radius: var(--radius-lg); overflow: hidden; display: flex; flex-direction: column; transition: box-shadow .25s var(--ease), transform .25s var(--ease); }
.highlight:hover { box-shadow: var(--shadow-lg); transform: translateY(-3px); }
.highlight__media { display: block; overflow: hidden; }
.highlight__media img { width: 100%; aspect-ratio: 16 / 10; object-fit: cover; transition: transform .4s var(--ease); }

/* =========================================================
   1.9.0 — the three cards, always moving, never together

   A light travels across each photograph on a loop, and a ring pushes
   out from each icon. Both run on a clock of their own: the cycles are
   8s, 9.5s and 11s, which share no common factor, so the three cards
   drift apart within one pass and never line up again. Delays alone
   would not do that, because equal durations re-sync every cycle.

   The travel takes about a third of the cycle, so the sweep itself is
   unhurried and the pause after it is short enough that something is
   nearly always moving somewhere in the row.
   ========================================================= */
.highlight { --card-cycle: calc(8s + var(--card-i, 0) * 1.5s); }

.highlight__media { position: relative; isolation: isolate; }
.highlight__media::after {
  content: ""; position: absolute; inset: 0; z-index: 1;
  pointer-events: none;
  background: linear-gradient(
    105deg,
    rgba(255, 255, 255, 0)   40%,
    rgba(255, 255, 255, .30) 47%,
    rgba(255, 255, 255, .55) 50%,
    rgba(255, 255, 255, .30) 53%,
    rgba(255, 255, 255, 0)   60%
  );
  background-size: 280% 100%;
  background-position: 140% 0;
  animation: card-shine var(--card-cycle) linear infinite;
  animation-delay: calc(var(--card-i, 0) * -2.6s);
}
@keyframes card-shine {
  0%   { background-position: 140% 0; }
  34%  { background-position: -50% 0; }
  100% { background-position: -50% 0; }
}
.highlight:hover .highlight__media::after {
  animation-duration: 2.6s;
  animation-delay: 0s;
}

/* --- the icon: a wave, and a toned ground ------------------------------
   The icon itself is an opaque photograph, so a colour placed behind it
   would never be seen. The tone goes on the badge instead: a ring of it
   shows around the icon's white edge, a soft halo of it sits under the
   whole thing, and the waves that push outward are the same colour. Each
   card takes a different pair from the ZEISS ramp, so the row reads as
   three related things rather than three identical ones.

   The badge carries the rings because an <img> cannot grow
   pseudo-elements. Two rings run half a cycle apart, so one is always on
   its way out. */
.highlight__badge {
  --tone-a: var(--brand-500);
  --tone-b: var(--brand-700);
  position: absolute; top: -34px; left: 1.75rem;
  width: 68px; height: 68px;
  display: grid; place-items: center;
  border-radius: 50%; isolation: isolate;
  background:
    radial-gradient(circle at 34% 26%, rgba(255,255,255,.55), rgba(255,255,255,0) 60%),
    linear-gradient(145deg, var(--tone-a), var(--tone-b));
  box-shadow: 0 8px 22px -6px var(--tone-b);
}
.highlight:nth-child(2) .highlight__badge { --tone-a: #4FC3E8; --tone-b: var(--brand-600); }
.highlight:nth-child(3) .highlight__badge { --tone-a: var(--brand-600); --tone-b: var(--brand-900); }

.highlight__badge::before,
.highlight__badge::after {
  content: ""; position: absolute; inset: 0; z-index: -1;
  border-radius: 50%;
  border: 2px solid var(--tone-a);
  opacity: 0;
  animation: badge-wave var(--card-cycle) ease-out infinite;
  animation-delay: calc(var(--card-i, 0) * -2.6s);
}
.highlight__badge::after { animation-delay: calc(var(--card-i, 0) * -2.6s - var(--card-cycle) / 2); }

@keyframes badge-wave {
  0%   { transform: scale(1);    opacity: .6; }
  40%  { transform: scale(2);    opacity: 0; }
  100% { transform: scale(2);    opacity: 0; }
}

.highlight__icon {
  position: static;
  width: 56px; height: 56px; border-radius: 50%;
  border: 3px solid #fff;
  object-fit: cover; background: #fff;
  transition: transform .35s var(--ease);
}
.highlight:hover .highlight__icon { transform: scale(1.06); }
.highlight:hover .highlight__badge { box-shadow: 0 10px 26px -6px var(--tone-a); }

@media (prefers-reduced-motion: reduce) {
  .highlight__media::after,
  .highlight__badge::before,
  .highlight__badge::after { animation: none !important; opacity: 0 !important; }
}
.highlight:hover .highlight__media img { transform: scale(1.04); }
.highlight__body { position: relative; padding: 1.75rem; flex: 1; display: flex; flex-direction: column; }
.highlight__title { margin-top: 1.35rem; font-size: var(--step-2); }
.highlight__body p { color: var(--muted); flex: 1; }
.highlight__body .link-arrow { margin-top: 1rem; }

.quote-section { position: relative; padding-block: calc(var(--section-y) * 1.25); background: var(--brand-900); overflow: hidden; }
.quote-section__bg { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: .28; }
.quote-section__inner { position: relative; max-width: 900px; text-align: center; color: #fff; }
.quote-section__title { color: #fff; font-size: var(--step-3); margin-bottom: 1.5rem; }
.quote-section__quote { margin: 0; }
.quote-section__quote p { font-size: var(--step-2); font-weight: 300; line-height: 1.45; color: rgba(255, 255, 255, .93); }
.quote-section__quote cite { display: block; margin-top: 1.2rem; font-style: normal; font-size: var(--step--1); font-weight: 500; letter-spacing: .12em; text-transform: uppercase; color: rgba(255, 255, 255, .72); }

.brand-strip { display: flex; flex-wrap: wrap; gap: .6rem; list-style: none; margin: 0; padding: 0; }
.brand-strip li { margin: 0; }
.brand-strip a { display: inline-block; padding: .55rem 1.1rem; border: 1px solid var(--line); border-radius: 999px; font-size: var(--step--1); font-weight: 500; color: var(--ink-soft); text-decoration: none; transition: all .18s var(--ease); }
.brand-strip a:hover { border-color: var(--brand-700); background: var(--brand-100); color: var(--brand-700); }

.outlets-section { background: var(--surface-2); padding-block: var(--section-y); }
.outlet-grid { display: grid; gap: 1.25rem; grid-template-columns: repeat(auto-fit, minmax(min(100%, 250px), 1fr)); }
.outlet-card {
  background: #fff; border: 1px solid var(--line); border-radius: var(--radius);
  padding: 1.5rem; display: flex; flex-direction: column;
  transition: box-shadow .25s var(--ease), transform .25s var(--ease), border-color .25s var(--ease);
}
.outlet-card:hover { border-color: var(--brand-500); box-shadow: var(--shadow-md); transform: translateY(-2px); }
.outlet-card__address { flex: 1; }
.outlet-card__name { font-size: var(--step-1); margin-bottom: .6rem; }
.outlet-card__address { color: var(--muted); font-size: var(--step--1); margin-bottom: .8rem; }
.outlet-card__phone { font-weight: 500; text-decoration: none; }
.outlets-section__cta { margin-top: var(--head-gap); }

/* =========================================================
   Brand pages
   ========================================================= */
.brand-jump { display: flex; flex-wrap: wrap; gap: .5rem; padding-block: 2rem; }
.brand-jump a { padding: .45rem 1rem; border: 1px solid var(--line); border-radius: 999px; font-size: var(--step--1); text-decoration: none; color: var(--ink-soft); }
.brand-jump a:hover { border-color: var(--brand-700); color: var(--brand-700); background: var(--brand-100); }

.brand-section { padding-block: var(--section-y); border-top: 1px solid var(--line); scroll-margin-top: 90px; }
.brand-section:first-of-type { border-top: 0; }
.brand-section__head { margin-bottom: 2rem; max-width: 60ch; }
.brand-section__name { font-size: var(--step-2); margin-bottom: .3em; }
.brand-section__blurb { color: var(--muted); }

.brand-gallery { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 1fr)); }
.brand-gallery__item { margin: 0; background: var(--surface-2); border-radius: var(--radius); overflow: hidden; }
.brand-gallery__item img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; transition: transform .35s var(--ease); }
.brand-gallery__item:hover img { transform: scale(1.03); }

/* =========================================================
   Gallery + lightbox
   ========================================================= */
/* --- The album -------------------------------------------------------
   A masonry-ish grid: every fourth tile runs tall so the wall has rhythm
   rather than reading as a spreadsheet of identical rectangles. Tiles
   reveal in sequence and lift under the cursor.
   --------------------------------------------------------------------- */
.gallery-section { padding-top: var(--section-y-sm); }
.gallery-section .section__head { margin-bottom: var(--head-gap); }

.album {
  display: grid; gap: clamp(.5rem, 1.4vw, 1rem);
  /* minmax(0, 1fr), not 1fr: a bare 1fr takes its minimum from the image's
     intrinsic width, which keeps the columns from ever shrinking and pushes
     the whole page sideways. */
  grid-template-columns: repeat(2, minmax(0, 1fr));
  grid-auto-rows: 1fr;
  /* Dense packing back-fills the holes the tall tiles would otherwise leave. */
  grid-auto-flow: dense;
}
@media (min-width: 640px)  { .album { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 1000px) { .album { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

.album__item { margin: 0; position: relative; }

.album__button {
  display: block; width: 100%; height: 100%; padding: 0; border: 0;
  background: var(--surface-2); cursor: zoom-in; overflow: hidden;
  border-radius: var(--radius); position: relative;
  box-shadow: var(--shadow-sm);
  transition: transform .35s var(--ease), box-shadow .35s var(--ease);
}
.album__button img {
  display: block; width: 100%; height: 100%; aspect-ratio: 4 / 3;
  object-fit: cover; transition: transform .5s var(--ease), filter .35s var(--ease);
}
/* Every sixth tile runs tall, so the wall has rhythm instead of reading as a
   spreadsheet of identical rectangles. The last two are never given the span:
   a tall tile at the end has nothing beside it and leaves a dangling tail. */
@media (min-width: 640px) {
  .album__item:nth-child(6n + 1):not(:nth-last-child(-n + 2)) { grid-row: span 2; }
  .album__item:nth-child(6n + 1):not(:nth-last-child(-n + 2)) .album__button img { aspect-ratio: 4 / 5.6; }
}
.album__button:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); }
.album__button:hover img { transform: scale(1.06); }
.album__button:focus-visible { outline: 3px solid var(--brand-500); outline-offset: 3px; }

/* A brand-blue veil with a magnifier, fading up on hover. */
.album__veil {
  position: absolute; inset: 0; display: grid; place-items: center;
  background: linear-gradient(180deg, rgba(0, 43, 73, 0) 40%, rgba(11, 16, 77, .55) 100%);
  opacity: 0; transition: opacity .3s var(--ease);
}
.album__button:hover .album__veil,
.album__button:focus-visible .album__veil { opacity: 1; }
.album__veil svg {
  color: #fff; transform: scale(.8);
  transition: transform .3s var(--ease);
  filter: drop-shadow(0 2px 8px rgba(6, 9, 42, .45));
}
.album__button:hover .album__veil svg { transform: scale(1); }

.album__caption {
  position: absolute; left: .8rem; right: .8rem; bottom: .7rem;
  color: #fff; font-size: var(--step--1); font-weight: 500;
  text-shadow: 0 1px 6px rgba(6, 9, 42, .8);
  opacity: 0; transform: translateY(6px);
  transition: opacity .3s var(--ease), transform .3s var(--ease);
  pointer-events: none;
}
.album__button:hover ~ .album__caption,
.album__button:focus-visible ~ .album__caption { opacity: 1; transform: translateY(0); }

.album__count { margin-top: 1.25rem; font-size: var(--step--1); color: var(--muted); }

/* Kept for page bodies that still use the plain photo grid. */
.photo-grid--gallery .photo-grid__button { display: block; width: 100%; padding: 0; border: 0; background: none; cursor: zoom-in; border-radius: var(--radius); overflow: hidden; }
.photo-grid--gallery img { transition: transform .35s var(--ease); }
.photo-grid--gallery .photo-grid__button:hover img { transform: scale(1.04); }
.photo-grid--gallery figcaption { margin-top: .5rem; font-size: var(--step--1); color: var(--muted); }

/* --- Lightbox --------------------------------------------------------
   A real viewer: previous / next, a counter, keyboard arrows, swipe,
   and neighbours preloaded so stepping through is instant.
   --------------------------------------------------------------------- */
.lightbox {
  position: fixed; inset: 0; z-index: 200;
  /* A definite height, not just inset:0. Safari resolves the photograph's
     max-height:100% against an indefinite track as "none", which is why the
     enlarged picture came out at some arbitrary size instead of filling the
     stage. Everything below now measures against a real number. */
  height: 100vh; height: 100dvh;
  display: grid; grid-template-rows: auto minmax(0, 1fr) auto;
  background: rgba(6, 9, 42, .95);
  backdrop-filter: blur(6px);
  animation: lb-in .28s var(--ease) both;
}
.lightbox[hidden] { display: none; }
.lightbox, .lightbox__foot { min-width: 0; overflow-x: hidden; }
@keyframes lb-in { from { opacity: 0; } to { opacity: 1; } }

.lightbox__bar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; padding: .9rem clamp(.9rem, 3vw, 1.75rem);
  color: rgba(255, 255, 255, .82); font-size: var(--step--1);
}
.lightbox__counter { font-variant-numeric: tabular-nums; letter-spacing: .06em; }

.lightbox__stage {
  position: relative; display: grid; place-items: center;
  /* An explicit minmax(0, 1fr) track and min-width:0 on the figure. Without
     them the track is sized from the photograph's intrinsic width, the stage
     grows wider than the viewport and the Next button is pushed off screen. */
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: minmax(0, 1fr);
  padding: 0 clamp(3.5rem, 8vw, 6rem);
  min-width: 0; min-height: 0; overflow: hidden;
}
.lightbox__figure {
  margin: 0; display: flex; align-items: center; justify-content: center;
  width: 100%; height: 100%;
  min-width: 0; min-height: 0;
}
.lightbox__image {
  /* Two ceilings on purpose. The percentage one is what a browser with a
     properly resolved track uses; the viewport one is the floor under
     Safari, where the percentage can evaporate. The smaller always wins,
     so the picture fills the stage and never spills past it. */
  max-width: 100%;
  max-height: 100%;
  max-height: min(100%, calc(100dvh - 10.5rem));
  width: auto; height: auto; object-fit: contain;
  border-radius: var(--radius); background: rgba(255, 255, 255, .04);
  animation: lb-pop .3s var(--ease) both;
}
@supports not (height: 100dvh) {
  .lightbox { height: 100vh; }
  .lightbox__image { max-height: min(100%, calc(100vh - 10.5rem)); }
}
.lightbox__image.is-swapping { animation: none; opacity: .25; }
@keyframes lb-pop { from { opacity: 0; transform: scale(.97); } to { opacity: 1; transform: scale(1); } }

.lightbox__foot {
  padding: .9rem clamp(.9rem, 3vw, 1.75rem) 1.25rem;
  text-align: center; color: rgba(255, 255, 255, .8); font-size: var(--step--1);
  min-height: 2.5rem;
}

/* Controls */
.lightbox__btn {
  display: grid; place-items: center; flex: 0 0 auto;
  width: 46px; height: 46px; padding: 0;
  color: #fff; background: rgba(255, 255, 255, .1);
  border: 1px solid rgba(255, 255, 255, .16); border-radius: 50%;
  cursor: pointer;
  transition: background .2s var(--ease), transform .2s var(--ease), border-color .2s var(--ease);
}
.lightbox__btn:hover { background: rgba(255, 255, 255, .22); border-color: rgba(255, 255, 255, .38); transform: scale(1.08); }
.lightbox__btn:focus-visible { outline: 3px solid var(--brand-500); outline-offset: 2px; }
.lightbox__btn[disabled] { opacity: .3; cursor: default; transform: none; }

.lightbox__nav {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: 52px; height: 52px; z-index: 2;
}
.lightbox__nav:hover { transform: translateY(-50%) scale(1.08); }
.lightbox__nav--prev { left: clamp(.4rem, 1.5vw, 1.25rem); }
.lightbox__nav--next { right: clamp(.4rem, 1.5vw, 1.25rem); }

/* A filmstrip of thumbnails so you can jump anywhere in the album. */
.lightbox__strip {
  display: flex; gap: .4rem; overflow-x: auto; padding: .2rem .2rem .5rem;
  scrollbar-width: thin; justify-content: flex-start;
}
.lightbox__strip::-webkit-scrollbar { height: 6px; }
.lightbox__strip::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, .25); border-radius: 999px; }
.lightbox__thumb {
  flex: 0 0 auto; width: 62px; height: 44px; padding: 0; border: 0; cursor: pointer;
  border-radius: 6px; overflow: hidden; opacity: .45;
  outline: 2px solid transparent; outline-offset: 1px;
  transition: opacity .2s var(--ease), outline-color .2s var(--ease);
}
.lightbox__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.lightbox__thumb:hover { opacity: .85; }
.lightbox__thumb.is-current { opacity: 1; outline-color: var(--brand-500); }

@media (max-width: 639px) {
  .lightbox__strip { display: none; }
  .lightbox__nav { width: 42px; height: 42px; }
  .lightbox__stage { padding-inline: 2.9rem; }
}

body.lb-open { overflow: hidden; }

/* =========================================================
   Forms
   ========================================================= */
.form-page { display: grid; gap: clamp(1.75rem, 3vw, 2.75rem); padding-block: var(--section-y); }
@media (min-width: 900px) { .form-page { grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr); align-items: start; } }
.form-page--narrow { max-width: 780px; }
@media (min-width: 900px) { .form-page--narrow { grid-template-columns: 1fr; } }

.form-card, .contact-form-card { background: #fff; border: 1px solid var(--line); border-radius: var(--radius-lg); padding: clamp(1.5rem, 4vw, 2.5rem); box-shadow: var(--shadow-sm); }
.form-note { color: var(--muted); font-size: var(--step--1); }

.form { display: grid; gap: 1.15rem; margin-top: 1.5rem; }
.field { display: grid; gap: .4rem; border: 0; padding: 0; margin: 0; min-width: 0; }
.field > label, .field > legend { font-size: var(--step--1); font-weight: 500; color: var(--ink); padding: 0; }
.field__hint { font-weight: 400; color: var(--muted); }
.field-row { display: grid; gap: 1.15rem; }
/* minmax(0, ...) rather than 1fr: a date input carries a wide intrinsic
   minimum on WebKit, and with a bare 1fr that minimum wins, stretching its
   column and pushing the time control into it. */
@media (min-width: 640px) { .field-row { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); } }

/* =========================================================
   1.10.0 — a field should look like a field

   The hairline that separates one panel from another is too quiet to
   describe a box you are meant to type in, so form controls take a line
   of their own that is several shades darker, and darker again under the
   pointer. Focus is unchanged: brand blue with a ring.

   The second half of this is what the empty state says. A select showing
   "Select an outlet" and a date showing dd/mm/yyyy were set in the same
   ink as a real answer, so a form the reader had not touched looked like
   a form they had filled in. Those read in the muted grey now, and only
   become ink once they hold a value.
   ========================================================= */
input[type="text"], input[type="email"], input[type="tel"], input[type="date"],
input[type="search"], input[type="password"], input[type="number"], select, textarea {
  width: 100%; padding: .75rem .9rem;
  font: inherit; font-size: var(--step-0); color: var(--ink);
  background: #fff; border: 1px solid var(--field-line); border-radius: var(--radius-sm);
  transition: border-color .18s var(--ease), box-shadow .18s var(--ease);
}
input:hover:not(:focus), select:hover:not(:focus), textarea:hover:not(:focus) { border-color: var(--field-line-strong); }
input:focus, select:focus, textarea:focus { outline: none; border-color: var(--brand-500); box-shadow: 0 0 0 3px rgba(0, 139, 208, .16); }

/* The hint, not the answer. */
::placeholder { color: var(--field-hint); opacity: 1; }
select.is-empty,
input.is-empty { color: var(--field-hint); }
select.is-empty option { color: var(--ink); }
input[type="date"].is-empty::-webkit-datetime-edit,
input[type="time"].is-empty::-webkit-datetime-edit { color: var(--field-hint); }
/* Where :has() is available the select needs no script at all. */
@supports selector(select:has(option:checked)) {
  select:has(option[value=""]:checked) { color: var(--field-hint); }
}
textarea { resize: vertical; min-height: 120px; }
select { appearance: none; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath fill='%235b6b7a' d='M1 1.5 6 6.5l5-5'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 1rem center; background-size: 12px; padding-right: 2.5rem; }
input[type="file"] { padding: .6rem; background: var(--surface-2); border: 1px solid var(--field-line); border-radius: var(--radius-sm); }

/* --- Date and time, side by side -----------------------------------------
   Safari and iOS give input[type=date] a native inner layout of their own:
   its own font, its own padding, an intrinsic width wider than the column,
   and a height that ignores the padding set above. Left alone it sat at a
   different height from the time select beside it and spilled across the
   gap. These rules take the control back: same box, same height, same
   baseline, at every screen size. */
input[type="date"], input[type="time"], input[type="datetime-local"] {
  -webkit-appearance: none; appearance: none;
  min-width: 0; max-width: 100%;
  min-height: var(--control-h);
}
/* WebKit lays the date out as separate fields inside a flex box that does
   not stretch by default, which is what left the text riding high. */
input[type="date"]::-webkit-date-and-time-value { text-align: left; margin: 0; }
input[type="date"]::-webkit-datetime-edit { padding: 0; line-height: 1.4; }
input[type="date"]::-webkit-calendar-picker-indicator {
  margin-left: auto; padding: 0; cursor: pointer;
  opacity: .55; transition: opacity .18s var(--ease);
}
input[type="date"]::-webkit-calendar-picker-indicator:hover { opacity: 1; }

/* One height for every control on a row, so nothing sits proud of its
   neighbour. The line-height has to be stated too: a select inherits the
   body's 1.65 while a date input was pinned to 1.2 above, and that alone
   left the two boxes three pixels out of step beside each other. */
input[type="text"], input[type="email"], input[type="tel"], input[type="date"],
input[type="search"], input[type="password"], input[type="number"], select {
  min-height: var(--control-h);
  line-height: 1.4;
}

.checkbox-row { display: flex; flex-wrap: wrap; gap: .9rem; }
.checkbox { display: inline-flex; align-items: center; gap: .45rem; font-size: var(--step--1); }
.checkbox input { width: 18px; height: 18px; accent-color: var(--brand-700); }

.rating-field legend { margin-bottom: .3rem; }
.rating { display: inline-flex; flex-direction: row-reverse; justify-content: flex-end; gap: .2rem; }
.rating input { position: absolute; opacity: 0; width: 0; height: 0; }
.rating label { font-size: 1.8rem; line-height: 1; color: var(--line); cursor: pointer; transition: color .15s var(--ease); }
.rating input:checked ~ label, .rating label:hover, .rating label:hover ~ label { color: #f2b134; }
.rating input:focus-visible + label { outline: 3px solid var(--brand-500); outline-offset: 2px; }

.hp-field { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.form-aside { background: var(--surface-2); border-radius: var(--radius-lg); padding: clamp(1.5rem, 4vw, 2.25rem); }
.form-aside h2 { font-size: var(--step-1); }
.numbered-list { counter-reset: step; list-style: none; padding: 0; margin: 0 0 1.2rem; }
.numbered-list li { counter-increment: step; position: relative; padding-left: 2.2rem; margin-bottom: .9rem; font-size: var(--step--1); color: var(--ink-soft); }
.numbered-list li::before { content: counter(step); position: absolute; left: 0; top: 0; width: 1.6rem; height: 1.6rem; display: grid; place-items: center; border-radius: 50%; background: var(--brand-700); color: #fff; font-size: .75rem; font-weight: 500; }

/* =========================================================
   Contact page
   ========================================================= */
.contact-layout { display: grid; gap: clamp(1.75rem, 3vw, 2.75rem); padding-block: var(--section-y); }
@media (min-width: 900px) { .contact-layout { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: start; } }
.contact-details__photo { margin: 0 0 2rem; }
.contact-details__photo img { width: 100%; border-radius: var(--radius-lg); box-shadow: var(--shadow-md); }
.outlet-list { list-style: none; padding: 0; margin: 0; display: grid; gap: 1.25rem; }
.outlet-list__item { padding: 1.25rem 1.4rem; background: var(--surface-2); border-radius: var(--radius); }
.outlet-list__item h3 { font-size: var(--step-1); margin-bottom: .4rem; }
.outlet-list__item address { font-style: normal; color: var(--muted); font-size: var(--step--1); margin-bottom: .6rem; }
.outlet-list__item .link-arrow + .link-arrow { margin-left: 1rem; }
.contact-details__meta { margin-top: 1.75rem; padding-top: 1.5rem; border-top: 1px solid var(--line); }
.social--dark { display: flex; gap: 1rem; }

/* =========================================================
   Search results
   ========================================================= */
.search--page { max-width: 560px; margin-top: 1.5rem; border-radius: var(--radius-sm); padding: .3rem .3rem .3rem 1rem; display: flex; }
.search--page input { flex: 1; width: auto; }
.result-list { list-style: none; padding: 0; margin: 1.5rem 0 0; display: grid; gap: 1.25rem; }
.result { padding-bottom: 1.25rem; border-bottom: 1px solid var(--line); margin: 0; }
.result__title { font-size: var(--step-1); margin-bottom: .3em; }
.result__title a { text-decoration: none; }
.result__excerpt { color: var(--muted); font-size: var(--step--1); margin: 0; }
.results-count { color: var(--muted); font-size: var(--step--1); }

/* =========================================================
   Footer
   ========================================================= */
.site-footer { margin-top: auto; background: var(--brand-900); color: rgba(255, 255, 255, .78); }
.site-footer__grid { display: grid; gap: 2.25rem; padding-block: var(--section-y); grid-template-columns: repeat(auto-fit, minmax(min(100%, 210px), 1fr)); }
.site-footer__col--wide { grid-column: span 1; }
@media (min-width: 900px) { .site-footer__col--wide { grid-column: span 2; max-width: 460px; } }
@media (min-width: 1150px) {
  .site-footer__grid { grid-template-columns: 1.5fr 1.1fr 1fr 1fr 1.15fr; }
  .site-footer__col--wide { grid-column: span 1; max-width: none; }
}
.site-footer__logo { width: 236px; height: auto; margin-bottom: 1.25rem; }
.site-footer__blurb { font-size: var(--step--1); line-height: 1.7; }
.site-footer__heading { font-size: var(--step--1); font-weight: 500; letter-spacing: .12em; text-transform: uppercase; color: #fff; margin-bottom: 1rem; }
.site-footer__heading + .site-footer__list + .site-footer__heading { margin-top: 1.75rem; }
.site-footer__list { list-style: none; padding: 0; margin: 0; }
.site-footer__list li { margin-bottom: .5rem; font-size: var(--step--1); }
.site-footer__list a { color: rgba(255, 255, 255, .78); text-decoration: none; }
.site-footer__list a:hover { color: #fff; text-decoration: underline; }
.site-footer__list--columns {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  grid-auto-flow: row; gap: 0 1.25rem;
}
.site-footer__list--outlets li { margin-bottom: .9rem; }
.site-footer__list--outlets strong { display: block; color: #fff; font-weight: 500; }

.social { display: flex; gap: .6rem; margin-top: 1.5rem; }
.social__link { display: grid; place-items: center; width: 40px; height: 40px; border-radius: 50%; background: rgba(255, 255, 255, .1); color: #fff; transition: background .2s var(--ease); }
.social__link:hover { background: var(--brand-700); color: #fff; }

.site-footer__base { border-top: 1px solid rgba(255, 255, 255, .12); }
.site-footer__base-inner { display: flex; flex-wrap: wrap; gap: 1rem; justify-content: space-between; align-items: center; padding-block: 1.5rem; font-size: .8rem; }
.site-footer__base p { margin: 0; }
.site-footer__links { display: flex; gap: 1.25rem; }
.site-footer__links a { color: rgba(255, 255, 255, .78); text-decoration: none; }
.site-footer__links a:hover { color: #fff; }

@media print {
  .site-header, .site-footer, .hero__dots, .breadcrumbs, .cta-band, .nav-toggle { display: none !important; }
  body { color: #000; }
  a { text-decoration: underline; }
}

/* =========================================================
   MOTION
   Scroll reveals, hero light sweep, image fade-in, pulses.
   Every effect is opt-out: if the visitor asks for reduced
   motion, or JS never runs, content shows immediately.
   ========================================================= */

/* --- Scroll reveal ---------------------------------------
   Elements start hidden only once JS has confirmed it is
   running (html.js), so a no-JS visitor still sees the page. */
.js [data-reveal] {
  opacity: 0;
  will-change: opacity, transform;
  transition:
    opacity   .75s var(--ease),
    transform .75s var(--ease);
}
.js [data-reveal="up"]    { transform: translateY(28px); }
.js [data-reveal="left"]  { transform: translateX(-34px); }
.js [data-reveal="right"] { transform: translateX(34px); }
.js [data-reveal="scale"] { transform: scale(.94); }
.js [data-reveal="fade"]  { transform: none; }

.js [data-reveal].is-visible {
  opacity: 1;
  transform: none;
}

/* Children of a staggered group come in one after another. */
.js [data-stagger] > * {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity .7s var(--ease), transform .7s var(--ease);
  transition-delay: var(--stagger-delay, 0ms);
}
.js [data-stagger].is-visible > * { opacity: 1; transform: none; }

/* --- Images fade in as they decode ------------------------ */
.js img[loading="lazy"] { opacity: 0; transition: opacity .6s var(--ease); }
.js img[loading="lazy"].is-loaded,
.js img[loading="lazy"].is-error { opacity: 1; }

/* --- Hero: a fine light line running across the section --- */
.hero__lightline {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 2px;
  overflow: hidden;
  background: linear-gradient(90deg,
    rgba(255, 255, 255, 0) 0%,
    rgba(255, 255, 255, .16) 50%,
    rgba(255, 255, 255, 0) 100%);
  pointer-events: none;
  z-index: 2;
}
.hero__lightline::after {
  content: "";
  position: absolute;
  top: 0; left: 0;
  width: 34%; height: 100%;
  background: linear-gradient(90deg,
    rgba(0, 139, 208, 0) 0%,
    rgba(0, 139, 208, .9) 35%,
    #7fd4ff 50%,
    rgba(0, 139, 208, .9) 65%,
    rgba(0, 139, 208, 0) 100%);
  filter: blur(.4px);
  animation: light-run 5.5s cubic-bezier(.55, 0, .45, 1) infinite;
}
@keyframes light-run {
  0%   { transform: translateX(-110%); opacity: 0; }
  12%  { opacity: 1; }
  88%  { opacity: 1; }
  100% { transform: translateX(400%); opacity: 0; }
}

/* A second, slower sheen high in the hero for depth. */
.hero__sheen {
  position: absolute; inset: 0;
  pointer-events: none; z-index: 1; overflow: hidden;
}
.hero__sheen::before {
  content: "";
  position: absolute;
  top: -60%; left: -30%;
  width: 40%; height: 220%;
  background: linear-gradient(105deg,
    rgba(255, 255, 255, 0) 0%,
    rgba(255, 255, 255, .07) 45%,
    rgba(255, 255, 255, 0) 100%);
  transform: rotate(8deg);
  animation: sheen-drift 11s ease-in-out infinite;
}
@keyframes sheen-drift {
  0%, 100% { transform: translateX(0) rotate(8deg); }
  50%      { transform: translateX(340%) rotate(8deg); }
}

/* --- Hero copy arrives in sequence ------------------------- */
.hero__slide.is-active .eyebrow      { animation: hero-in .8s var(--ease) .15s both; }
.hero__slide.is-active .hero__headline { animation: hero-in .8s var(--ease) .28s both; }
.hero__slide.is-active .hero__subtext  { animation: hero-in .8s var(--ease) .41s both; }
.hero__slide.is-active .btn            { animation: hero-in .8s var(--ease) .54s both; }
@keyframes hero-in {
  from { opacity: 0; transform: translateY(20px); }
  to   { opacity: 1; transform: none; }
}

/* --- Gentle pulse on the primary calls to action ----------- */
.btn--pulse { position: relative; }
.btn--pulse::after {
  content: "";
  position: absolute; inset: 0;
  border-radius: inherit;
  border: 2px solid var(--brand-500);
  opacity: 0;
  animation: cta-pulse 3.2s ease-out infinite;
  pointer-events: none;
}
@keyframes cta-pulse {
  0%   { transform: scale(1);    opacity: .55; }
  70%  { transform: scale(1.16); opacity: 0; }
  100% { transform: scale(1.16); opacity: 0; }
}

/* Active slideshow dot breathes so the carousel reads as live. */
.hero__dot.is-active { animation: dot-breathe 2.6s ease-in-out infinite; }
@keyframes dot-breathe {
  0%, 100% { opacity: 1; }
  50%      { opacity: .55; }
}

/* --- Hover lift, shared ------------------------------------ */
.brand-strip a, .breadcrumbs a, .link-arrow { transition: all .2s var(--ease); }
.brand-gallery__item { transition: transform .3s var(--ease), box-shadow .3s var(--ease); }
.brand-gallery__item:hover { transform: translateY(-3px); box-shadow: var(--shadow-md); }

/* --- Everything above is optional ------------------------- */
@media (prefers-reduced-motion: reduce) {
  .js [data-reveal],
  .js [data-stagger] > * { opacity: 1 !important; transform: none !important; transition: none !important; }
  .js img[loading="lazy"] { opacity: 1 !important; }
  .hero__lightline::after,
  .hero__sheen::before,
  .btn--pulse::after,
  .hero__dot.is-active { animation: none !important; }
  .hero__slide.is-active .eyebrow,
  .hero__slide.is-active .hero__headline,
  .hero__slide.is-active .hero__subtext,
  .hero__slide.is-active .btn { animation: none !important; }
  .btn--pulse::after { display: none; }
}

/* =========================================================
   PAGE HERO — split layout
   The photograph is product or editorial content, so it is
   shown whole beside the heading rather than faded out
   behind the text.
   ========================================================= */
.page-hero--split { background: var(--surface-2); border-bottom: 1px solid var(--line); overflow: hidden; }
.page-hero--split .page-hero__inner {
  display: grid; gap: clamp(1.75rem, 4vw, 3.5rem);
  align-items: center; max-width: var(--container);
  padding-block: var(--section-y);
}
@media (min-width: 900px) {
  .page-hero--split .page-hero__inner { grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr); }
}
.page-hero__copy { max-width: 60ch; }
.page-hero__figure { margin: 0; }
.page-hero__figure img {
  width: 100%; height: auto;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  background: #fff;
}

/* =========================================================
   CARE SECTION — photo on top, words below, toned ground
   ========================================================= */
.care-section {
  position: relative;
  padding-block: var(--section-y);
  background:
    radial-gradient(120% 90% at 50% 0%, #eef4fa 0%, rgba(238, 244, 250, 0) 70%),
    linear-gradient(180deg, #f7fafd 0%, #e9f1f8 100%);
  border-block: 1px solid var(--line);
  overflow: hidden;
}
/* a faint brand wash so the block reads as designed, not empty */
.care-section::before {
  content: "";
  position: absolute; inset: auto -10% -55% -10%;
  height: 70%;
  background: radial-gradient(60% 100% at 50% 100%, rgba(0, 139, 208, .10), transparent 70%);
  pointer-events: none;
}
.care-section__inner { position: relative; display: grid; gap: clamp(2rem, 4vw, 3rem); }

.care-section__figure { margin: 0; }
.care-section__figure img {
  width: 100%; height: auto;              /* whole photo, never cropped */
  border-radius: var(--radius-lg);
  box-shadow: 0 26px 60px rgba(11, 16, 77, .18);
  border: 6px solid #fff;
  background: #fff;
}

.care-section__copy { max-width: 62ch; margin-inline: auto; text-align: center; }
.care-section__title { font-size: var(--step-3); margin-bottom: 1.1rem; }
.care-section__quote { margin: 0; }
.care-section__quote p {
  font-size: var(--step-2); font-weight: 300; line-height: 1.45;
  color: var(--brand-900);
}
.care-section__quote p::before { content: "\201C"; }
.care-section__quote p::after  { content: "\201D"; }
.care-section__quote cite {
  display: block; margin-top: 1.1rem; font-style: normal;
  font-size: var(--step--1); font-weight: 500; letter-spacing: .1em;
  text-transform: uppercase; color: var(--brand-700);
}

@media (min-width: 1000px) {
  .care-section__inner { grid-template-columns: 1.15fr 1fr; align-items: center; gap: clamp(2.5rem, 5vw, 4rem); }
  .care-section__copy { text-align: left; margin-inline: 0; }
}

/* =========================================================
   VIDEOS
   ========================================================= */
.videos-section { scroll-margin-top: 90px; }
.video-grid {
  display: grid; gap: clamp(1.25rem, 3vw, 2rem);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 330px), 1fr));
}
.video-grid--tight { margin-top: 1.75rem; }
.video-grid--single { grid-template-columns: minmax(0, 1fr); max-width: 900px; }

.video-card {
  margin: 0; background: #fff;
  border: 1px solid var(--line); border-radius: var(--radius-lg);
  overflow: hidden; box-shadow: var(--shadow-sm);
  transition: box-shadow .25s var(--ease), transform .25s var(--ease);
}
.video-card:hover { box-shadow: var(--shadow-lg); transform: translateY(-3px); }

.video-card__frame {
  position: relative; width: 100%; aspect-ratio: 16 / 9;
  background: linear-gradient(135deg, var(--brand-900), var(--brand-700));
}
.video-card__frame iframe {
  position: absolute; inset: 0;
  width: 100%; height: 100%; border: 0; display: block;
}
.video-card__caption { padding: 1.15rem 1.35rem 1.4rem; }
.video-card__title { font-size: var(--step-1); margin-bottom: .35em; }
.video-card__caption p { color: var(--muted); font-size: var(--step--1); margin: 0; }

/* =========================================================
   FOOTER — brands as chips on their own full-width row,
   so no name is ever squeezed or broken across lines
   ========================================================= */
.site-footer__brands {
  display: grid; gap: 2rem;
  padding-bottom: clamp(2rem, 5vw, 3rem);
}
@media (min-width: 800px) { .site-footer__brands { grid-template-columns: 3fr 1fr; gap: 2.5rem; } }

.footer-chips { display: flex; flex-wrap: wrap; gap: .5rem; list-style: none; margin: 0; padding: 0; }
.footer-chips li { margin: 0; }
.footer-chips a {
  display: inline-block; white-space: nowrap;
  padding: .38rem .85rem; border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, .18);
  background: rgba(255, 255, 255, .04);
  color: rgba(255, 255, 255, .82);
  font-size: .8rem; text-decoration: none;
  transition: background .18s var(--ease), border-color .18s var(--ease), color .18s var(--ease);
}
.footer-chips a:hover {
  background: var(--brand-700); border-color: var(--brand-700); color: #fff;
}

/* the wide intro column no longer needs to span two tracks */
@media (min-width: 1150px) {
  .site-footer__grid { grid-template-columns: 1.6fr 1fr 1fr 1.3fr; }
}

/* Reveal for the new blocks */
.js .care-section__figure[data-reveal],
.js .care-section__copy[data-reveal],
.js .page-hero__copy[data-reveal],
.js .page-hero__figure[data-reveal] { will-change: opacity, transform; }

/* =========================================================
   1.3.0 — brand index and brand pages
   ========================================================= */

/* --- The index: one card per brand, logo forward ----------- */
.brand-index {
  display: grid; gap: clamp(.75rem, 2vw, 1.25rem);
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 200px), 1fr));
  margin-block: clamp(1.5rem, 4vw, 2.5rem);
}
.brand-card {
  position: relative; display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); overflow: hidden; text-decoration: none;
  transition: transform .3s var(--ease), box-shadow .3s var(--ease), border-color .3s var(--ease);
}
.brand-card:hover { transform: translateY(-5px); box-shadow: var(--shadow-md); border-color: var(--brand-500); }
.brand-card__media { position: relative; overflow: hidden; background: var(--surface-2); }
/*
 * 1.13.0 — this rule is from the days when the card showed a photograph
 * of a frame, and it was still reaching the brand marks that replaced
 * them: `width: 100%` with `aspect-ratio: 4/3` and `object-fit: cover`
 * pours a wordmark into a box of the wrong shape and cuts both ends off
 * it. A borrowed photograph is still a photograph and still fills the
 * tile, so it stays behind; a mark is measured by .brand-logo instead.
 */
.brand-card__media img:not(.brand-logo),
.brand-card__media img.brand-logo--photo {
  display: block; width: 100%; aspect-ratio: 4 / 3; object-fit: cover;
  transition: transform .5s var(--ease);
}
.brand-card:hover .brand-card__media img:not(.brand-logo),
.brand-card:hover .brand-card__media img.brand-logo--photo { transform: scale(1.06); }
.brand-card__count {
  position: absolute; top: .6rem; right: .6rem;
  padding: .2rem .55rem; border-radius: 999px;
  background: rgba(11, 16, 77, .78); color: #fff;
  font-size: .72rem; font-weight: 500; letter-spacing: .04em;
  backdrop-filter: blur(4px);
}
.brand-card__body {
  display: flex; align-items: center; justify-content: space-between; gap: .5rem;
  padding: .85rem 1rem 1rem;
}
.brand-card__name { margin: 0; font-size: var(--step-0); font-weight: 600; color: var(--brand-900); }
.brand-card__go { flex: 0 0 auto; color: var(--brand-500); transition: transform .25s var(--ease); }
.brand-card:hover .brand-card__go { transform: translateX(4px); }

/* --- A single brand --------------------------------------- */
.brand-page__head { padding-block: var(--section-y-sm); }
.brand-page__lead { max-width: 60ch; color: var(--muted); }

/* Two generous columns on a wide screen instead of four small ones. The
   photographs are the point of a brand page, and every one of them is wider
   than the 300px tile they used to sit in. The natural-size cap still applies,
   so nothing is enlarged past its own file. */
.brand-figures {
  display: grid; gap: clamp(.8rem, 1.8vw, 1.4rem);
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 520px), 1fr));
}
.brand-figures__item { margin: 0; }
.brand-figures__button {
  display: block; width: 100%; padding: 0; border: 0; cursor: zoom-in;
  background: var(--surface-2); border-radius: var(--radius); overflow: hidden;
  box-shadow: var(--shadow-sm);
  transition: transform .35s var(--ease), box-shadow .35s var(--ease);
}
.brand-figures__button img { display: block; width: 100%; aspect-ratio: 4 / 3; object-fit: cover; transition: transform .5s var(--ease); }
.brand-figures__button:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); }
.brand-figures__button:hover img { transform: scale(1.05); }
.brand-figures__button:focus-visible { outline: 3px solid var(--brand-500); outline-offset: 3px; }

/* Previous / next brand, the way the old site paged through them. */
.brand-pager {
  display: grid; gap: .75rem; grid-template-columns: 1fr;
  padding-block: var(--section-y-sm); border-top: 1px solid var(--line);
}
@media (min-width: 640px) { .brand-pager { grid-template-columns: 1fr 1fr; } }
.brand-pager__link {
  display: flex; align-items: center; gap: .8rem;
  padding: 1rem 1.15rem; border: 1px solid var(--line); border-radius: var(--radius);
  text-decoration: none; color: var(--ink);
  transition: border-color .2s var(--ease), background .2s var(--ease), transform .2s var(--ease);
}
.brand-pager__link:hover { border-color: var(--brand-500); background: var(--brand-100); transform: translateY(-2px); }
.brand-pager__link--next { justify-content: flex-end; text-align: right; }
.brand-pager__label { display: block; font-size: .74rem; letter-spacing: .12em; text-transform: uppercase; color: var(--muted); }
.brand-pager__name { display: block; font-weight: 500; color: var(--brand-900); }
.brand-pager__link svg { flex: 0 0 auto; color: var(--brand-500); }

/* =========================================================
   1.3.0 — spacing corrections
   ========================================================= */

/* The gallery's first band sat too far below the breadcrumbs. */
.section--tight { padding-top: var(--section-y-sm); }
.section--tight .section__head { margin-bottom: var(--head-gap); }

/* Anchor targets clear the sticky header. */
:target { scroll-margin-top: calc(var(--header-h) + 60px); }
.brand-section { scroll-margin-top: calc(var(--header-h) + 60px); }

/* =========================================================
   1.3.0 — reveal additions
   ========================================================= */
.js [data-reveal="zoom"] { transform: scale(.94); }
.js [data-reveal="zoom"].is-visible { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .album__button, .album__button img, .brand-card, .brand-card__media img,
  .brand-figures__button, .brand-figures__button img, .lightbox, .lightbox__image {
    transition: none !important; animation: none !important; transform: none !important;
  }
}

/* A second-level parent points sideways rather than down. */
.nav-caret--sub { transform: rotate(-90deg); opacity: .5; }
.dropdown__link:hover .nav-caret--sub { transform: rotate(-90deg) translateY(2px); opacity: .9; }
.main-nav__item:hover > .main-nav__link .nav-caret--sub,
.main-nav__item:focus-within > .main-nav__link .nav-caret--sub { transform: rotate(-90deg); }

/* The typeset stand-in for the footer mark, used only until the original
   PNG has been copied off the old server. */
.site-footer__wordmark {
  margin: 0 0 1rem; font-size: 1.35rem; font-weight: 500;
  letter-spacing: .06em; color: #fff; line-height: 1.25;
}
.site-footer__wordmark span { font-weight: 800; letter-spacing: .02em; }
.site-footer__wordmark small {
  display: block; margin-top: .25rem; font-size: .7rem; font-weight: 400;
  letter-spacing: .18em; text-transform: uppercase; color: rgba(255, 255, 255, .6);
}
.site-footer__logo { max-width: 282px; height: auto; }

/* The brand you are already on, in the list at the foot of a brand page. */
.brand-strip a.is-current {
  border-color: var(--brand-700); background: var(--brand-700); color: #fff;
  cursor: default;
}
.brand-strip a.is-current:hover { background: var(--brand-700); color: #fff; }

/* The rail's booking button never shrinks below its label. */
.site-header__rail .btn { white-space: nowrap; }

/* Headings sit better in Frutiger at medium weight than at semibold. */
h1, h2, h3, h4 { font-weight: 500; letter-spacing: -.005em; }
.hero__headline, .page-hero__title { font-weight: 500; }

/* =========================================================
   1.4.0 — "Speak to us now"

   A launcher that breathes rather than shouts: two rings expand
   and fade continuously, and every twenty seconds or so the whole
   button hops three times. Both stop dead under reduced-motion,
   and both stop once the panel has been opened — it has done its
   job by then and would only be noise.
   ========================================================= */
.speak {
  position: fixed; right: clamp(1rem, 3vw, 1.9rem); bottom: 30px;
  z-index: 150; display: flex; align-items: flex-end; gap: .75rem;
  /* column-reverse keeps the launcher pinned to the bottom and grows the
     panel upwards out of it, which is where the eye expects it. */
  flex-direction: column-reverse;
}

.speak__launcher {
  position: relative; display: inline-flex; align-items: center; gap: .6rem;
  padding: .85rem 1.35rem .85rem 1.1rem; border: 0; border-radius: 999px;
  background: linear-gradient(125deg, var(--brand-700), var(--brand-500));
  color: #fff; font: inherit; font-size: var(--step--1); font-weight: 500;
  letter-spacing: .01em; cursor: pointer; white-space: nowrap;
  box-shadow: 0 10px 30px rgba(11, 16, 77, .32), 0 2px 6px rgba(11, 16, 77, .2);
  transition: transform .25s var(--ease), box-shadow .25s var(--ease), filter .25s var(--ease);
}
.speak__launcher:hover { transform: translateY(-3px); box-shadow: 0 16px 40px rgba(11, 16, 77, .38); filter: saturate(1.08); }
.speak__launcher:active { transform: translateY(-1px); }
.speak__launcher:focus-visible { outline: 3px solid var(--brand-500); outline-offset: 3px; }

.speak__icon { display: grid; place-items: center; position: relative; z-index: 1; }
.speak__label { position: relative; z-index: 1; }

/* The rings. Two of them, offset in time, expanding out of the button. */
.speak__rings { position: absolute; inset: 0; border-radius: inherit; pointer-events: none; }
.speak__rings::before, .speak__rings::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit;
  border: 2px solid var(--brand-500);
  animation: speak-ring 2.8s var(--ease) infinite;
  opacity: 0;
}
.speak__rings::after { animation-delay: 1.4s; }
@keyframes speak-ring {
  0%   { transform: scale(1);    opacity: .55; }
  70%  { transform: scale(1.45); opacity: 0; }
  100% { transform: scale(1.45); opacity: 0; }
}

/* The occasional hop — three beats, then a long rest. */
.speak--nudge .speak__launcher { animation: speak-hop 1.15s var(--ease) 1; }
@keyframes speak-hop {
  0%, 100%      { transform: translateY(0); }
  8%, 30%, 52%  { transform: translateY(-9px); }
  18%, 40%, 62% { transform: translateY(0); }
}

/* Once opened, it settles down for good. */
.speak.is-open .speak__rings,
.speak.is-engaged .speak__rings { display: none; }
.speak.is-open .speak__launcher { background: var(--brand-900); }

/* --- The panel --------------------------------------------- */
.speak__panel {
  width: min(390px, calc(100vw - 2rem));
  max-height: min(600px, calc(100dvh - 190px));
  display: flex; flex-direction: column; overflow: hidden;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: 0 28px 70px rgba(6, 9, 42, .28);
  transform-origin: bottom right;
  animation: speak-in .32s var(--ease) both;
}
.speak__panel[hidden] { display: none; }
@keyframes speak-in {
  from { opacity: 0; transform: translateY(14px) scale(.96); }
  to   { opacity: 1; transform: none; }
}
.speak__panel.is-closing { animation: speak-out .22s var(--ease) both; }
@keyframes speak-out {
  from { opacity: 1; transform: none; }
  to   { opacity: 0; transform: translateY(10px) scale(.97); }
}

.speak__head {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem;
  padding: 1.15rem 1.25rem 1rem;
  background: linear-gradient(125deg, var(--brand-900), var(--brand-700));
  color: #fff;
}
.speak__eyebrow { margin: 0 0 .15rem; font-size: .68rem; font-weight: 500; letter-spacing: .16em; text-transform: uppercase; color: rgba(255, 255, 255, .72); }
.speak__title { margin: 0; font-size: var(--step-1); color: #fff; font-weight: 500; }
.speak__close {
  flex: 0 0 auto; display: grid; place-items: center; width: 32px; height: 32px;
  border: 0; border-radius: 50%; background: rgba(255, 255, 255, .16); color: #fff; cursor: pointer;
  transition: background .2s var(--ease), transform .2s var(--ease);
}
.speak__close:hover { background: rgba(255, 255, 255, .3); transform: rotate(90deg); }

.speak__body { padding: 1.1rem 1.25rem 1.35rem; overflow-y: auto; }

.speak__quick { display: grid; gap: .5rem; }
.speak__quick-item, .speak__calls > summary {
  display: flex; align-items: center; gap: .75rem;
  padding: .75rem .85rem; border: 1px solid var(--line); border-radius: var(--radius);
  text-decoration: none; color: var(--ink); cursor: pointer; list-style: none;
  transition: border-color .2s var(--ease), background .2s var(--ease), transform .2s var(--ease);
}
.speak__calls > summary::-webkit-details-marker { display: none; }
.speak__quick-item:hover, .speak__calls > summary:hover {
  border-color: var(--brand-500); background: var(--brand-100); transform: translateX(2px);
}
.speak__quick-icon {
  flex: 0 0 auto; display: grid; place-items: center; width: 36px; height: 36px;
  border-radius: 50%; background: var(--brand-100); color: var(--brand-700);
}
.speak__quick-item strong, .speak__calls summary strong { display: block; font-size: var(--step--1); font-weight: 500; }
.speak__quick-item small, .speak__calls summary small { display: block; font-size: .78rem; color: var(--muted); }

.speak__outlets { list-style: none; margin: .4rem 0 0; padding: 0 0 0 .5rem; display: grid; gap: .1rem; }
.speak__outlets a {
  display: flex; align-items: baseline; justify-content: space-between; gap: .75rem;
  padding: .5rem .6rem; border-radius: var(--radius-sm);
  font-size: var(--step--1); text-decoration: none; color: var(--ink-soft);
  transition: background .18s var(--ease);
}
.speak__outlets a:hover { background: var(--surface-2); color: var(--brand-700); }
.speak__outlets strong { color: var(--brand-700); white-space: nowrap; }

.speak__divider { display: flex; align-items: center; gap: .75rem; margin: 1.1rem 0 .9rem; color: var(--muted); font-size: .78rem; }
.speak__divider::before, .speak__divider::after { content: ""; flex: 1; height: 1px; background: var(--line); }

.speak__form .field { margin-bottom: .7rem; }
.speak__form .field-row { display: grid; gap: .7rem; grid-template-columns: 1fr 1fr; }
.speak__form label { display: block; font-size: .78rem; font-weight: 500; margin-bottom: .3rem; color: var(--ink-soft); }
.speak__form input, .speak__form textarea {
  width: 100%; padding: .6rem .75rem; font: inherit; font-size: var(--step--1);
  border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--surface-2);
  color: var(--ink); transition: border-color .18s var(--ease), box-shadow .18s var(--ease), background .18s var(--ease);
}
.speak__form input:focus, .speak__form textarea:focus {
  outline: none; border-color: var(--brand-500); background: #fff; box-shadow: 0 0 0 3px var(--brand-100);
}
.speak__form textarea { resize: vertical; min-height: 84px; }
.field-optional { font-weight: 400; color: var(--muted); font-size: .72rem; }

.speak__status { margin: .7rem 0 0; font-size: var(--step--1); min-height: 1.2em; }
.speak__status.is-ok    { color: #17603a; }
.speak__status.is-error { color: #8f1d26; }
.speak__legal { margin: .5rem 0 0; font-size: .72rem; color: var(--muted); }
/* =========================================================
   1.9.0 — the line that closes a form

   Fine print, and it should look like fine print. It is an assurance,
   not something to read, so it sits well below the size of the words
   above it and in the muted grey the site uses for anything secondary.
   ========================================================= */
.form-secured {
  display: flex; align-items: flex-start; gap: .4rem;
  margin-top: .85rem; margin-bottom: 0;
  font-size: .74rem; line-height: 1.45;
  color: var(--muted); font-weight: 400;
  max-width: 46ch;
}
.form-secured svg { flex: 0 0 auto; width: 13px; height: 13px; margin-top: .12em; opacity: .75; }

.form-noscript { margin: .5rem 0; font-size: var(--step--1); color: var(--muted); }

/* Sending */
.speak__form.is-sending { opacity: .6; pointer-events: none; }

@media (max-width: 560px) {
  .speak { right: .8rem; left: .8rem; bottom: 30px; align-items: stretch; }
  .speak__launcher { align-self: flex-end; }
  .speak__panel { width: 100%; max-height: calc(100dvh - 140px); }
}

@media (prefers-reduced-motion: reduce) {
  .speak__rings::before, .speak__rings::after,
  .speak--nudge .speak__launcher, .speak__panel { animation: none !important; }
  .speak__rings { display: none; }
}

/* =========================================================
   1.4.0 — the brand wall on the front page
   ========================================================= */
.brand-wall {
  display: grid; gap: clamp(.6rem, 1.4vw, 1rem);
  grid-template-columns: repeat(auto-fill, minmax(min(46%, 168px), 1fr));
  list-style: none; margin: 0; padding: 0;
}
.brand-wall li { margin: 0; }
.brand-wall a {
  display: grid; place-items: center; height: 100%; min-height: 92px;
  padding: 1rem 1.1rem; text-decoration: none;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  transition: border-color .25s var(--ease), box-shadow .25s var(--ease),
              transform .25s var(--ease), background .25s var(--ease);
}
.brand-wall a:hover {
  border-color: var(--brand-500); background: #fff;
  box-shadow: var(--shadow-md); transform: translateY(-4px);
}
.brand-wall img {
  max-width: 100%; max-height: 46px; width: auto; height: auto;
  object-fit: contain;
  /* Logos arrive in every colour and on every background. Muting them to a
     single grey keeps the wall calm, and colour returns on hover. */
  filter: grayscale(1) contrast(.85); opacity: .68;
  transition: filter .3s var(--ease), opacity .3s var(--ease), transform .3s var(--ease);
}
.brand-wall a:hover img { filter: none; opacity: 1; transform: scale(1.04); }

/* Where no logo file exists, the name is set instead — deliberately, so the
   wall never shows a gap or a broken picture. */
.brand-wall__word {
  font-size: var(--step-0); font-weight: 500; letter-spacing: .04em;
  color: var(--brand-900); text-align: center; line-height: 1.2;
  transition: color .25s var(--ease), letter-spacing .25s var(--ease);
}
.brand-wall a:hover .brand-wall__word { color: var(--brand-700); letter-spacing: .06em; }

/* =========================================================
   1.4.0 — video refinements
   ========================================================= */
.video-card__frame { position: relative; }
/* Reserve the space before the player arrives, so nothing jumps. */
.video-card__frame::after {
  content: ""; display: block; padding-top: 56.25%;
}
.video-card__frame > iframe,
.video-card__frame > .video-card__poster {
  position: absolute; inset: 0; width: 100%; height: 100%; border: 0;
}
.video-card__poster {
  display: grid; place-items: center; cursor: pointer;
  background: linear-gradient(135deg, var(--brand-900), var(--brand-700));
  color: #fff; border-radius: inherit;
}
.video-card__poster::before {
  content: ""; width: 58px; height: 58px; border-radius: 50%;
  background: rgba(255, 255, 255, .18); border: 2px solid rgba(255, 255, 255, .5);
}

/* The feature video: one large player leading the section. */
.video-feature { margin-bottom: clamp(1rem, 2.4vw, 1.75rem); }
.video-feature .video-card__frame { border-radius: var(--radius-lg); overflow: hidden; box-shadow: var(--shadow-lg); }
.video-feature .video-card__caption { padding-top: .9rem; }
.video-feature .video-card__title { font-size: var(--step-2); }
@media (min-width: 1000px) {
  .video-feature { max-width: 1040px; margin-inline: auto; }
}

/* =========================================================
   1.4.0 — banner hero

   For pages whose original artwork carried the wording inside the
   picture. The photograph runs full width behind a ZEISS-blue wash,
   and the heading is laid over it as real text — selectable, scalable,
   readable to a screen reader and to Google, at the weight and
   placement the artwork used.
   ========================================================= */
.page-hero--banner {
  position: relative; isolation: isolate;
  min-height: clamp(230px, 30vw, 380px);
  display: grid; align-items: center;
  background: var(--brand-900); border-bottom: 0; overflow: hidden;
}
.page-hero__plate {
  position: absolute; inset: 0; z-index: -2;
  width: 100%; height: 100%; object-fit: cover; object-position: center 38%;
}
.page-hero__wash {
  position: absolute; inset: 0; z-index: -1;
  background:
    linear-gradient(100deg, rgba(11, 16, 77, .93) 0%, rgba(11, 16, 77, .74) 42%, rgba(11, 16, 77, .3) 100%),
    linear-gradient(0deg,   rgba(6, 9, 42, .38), rgba(0, 21, 38, 0) 55%);
}
.page-hero__banner-inner { padding-block: clamp(2.5rem, 5vw, 4rem); max-width: 900px; }
.page-hero__banner-title {
  margin: 0 0 .45em; color: #fff; font-size: var(--step-4);
  font-weight: 500; line-height: 1.08; letter-spacing: -.01em;
  text-shadow: 0 2px 20px rgba(6, 9, 42, .35);
}
.page-hero__banner-lead {
  margin: 0; max-width: 62ch; font-size: var(--step-1);
  color: rgba(255, 255, 255, .9); line-height: 1.5;
}

/* =========================================================
   1.7.0 — a heading in two voices

   The ZEISS artwork does not set a display line in one run. "ZEISS
   VISION ANALYSIS" is the brand word upright, then the subject in
   ZEISS Cyan italic. `subtitle_accent` on a page names the second run
   and page-hero.php splits the line around it, so any page can take
   the treatment from Admin without a code change.

   On contrast: ZEISS Cyan is 3.87:1 on white, which carries display
   type (AA large needs 3:1) but not body-size type. So the banner
   heading, which is --step-4, gets the true cyan; the split hero's
   subtitle, which is --step-1, takes --brand-600 at 6.9:1 instead.
   The italic and the tonal shift still read as the same treatment.
   ========================================================= */
.hero-title__mark   { font-style: normal; font-weight: 700; }
.hero-title__accent { font-style: italic; font-weight: 500; color: var(--brand-500); }

/* On the blue wash the brand word is white, and cyan clears 4.5:1
   against the wash, so it keeps its own colour. */
.page-hero--banner .hero-title__mark   { color: #fff; }
.page-hero--banner .hero-title__accent { color: var(--brand-500); }

/* On white, at subtitle size, step down to the darker blue. */
.page-hero__subtitle .hero-title__mark   { color: var(--ink); }
.page-hero__subtitle .hero-title__accent { color: var(--brand-600); }

@media (prefers-contrast: more) {
  .page-hero--banner .hero-title__accent { color: #7fd0f5; }
}

/* =========================================================
   1.4.0 — article headings

   The old site set its section headings in ZEISS blue. That is
   inherited here, with more air above each one so a long article
   reads as a series of sections rather than one wall.
   ========================================================= */
.article h2,
.stage__text h2,
.prose-block h2,
.lens-card h2 { color: var(--brand-700); }

.article h3,
.stage__text h3,
.prose-block h3 { color: var(--brand-700); font-weight: 500; }

/* Room above a subheading, but never above the first thing in a box. */
.stage__text h2,
.prose-block h2 { margin-top: clamp(1.75rem, 3.5vw, 2.75rem); }
.stage__text h3,
.prose-block h3 { margin-top: clamp(1.5rem, 3vw, 2.25rem); }

.stage__text > h2:first-child,
.stage__text > h3:first-child,
.prose-block > h2:first-child,
.prose-block > h3:first-child,
.stage__number + h2 { margin-top: 0; }

/* A video sitting inside a section rather than at the foot of the page. */
.video-inline { margin-top: clamp(1.25rem, 2.5vw, 1.75rem); }
.video-inline .video-card { margin: 0; }
.video-inline .video-card__frame { border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow-sm); }

/* =========================================================
   1.5.0 — brand logos

   Every mark is the real artwork: thirteen of them traced to vector
   from the files ZEISS supplied, so they stay sharp at any size and
   on any screen. Maui Jim keeps its colour parrot as a raster, and
   the ZEISS shield is the CD Manual's own geometry.

   Logos arrive in wildly different proportions — ic! berlin is 5.5:1,
   OVVO is 1.9:1 — so each is capped on BOTH axes inside a fixed box.
   A wide mark meets the width limit, a squarer one meets the height
   limit, and every brand ends up with the same optical weight.
   ========================================================= */
.brand-logo {
  display: block; width: auto; height: auto;
  max-width: 100%; max-height: var(--logo-cap, 40px);
  object-fit: contain;
}
/* Traced marks inherit currentColor, so one rule tints the whole wall. */
.brand-logo--vector { color: var(--ink); }

/* --- The wall on the front page --------------------------- */
.brand-wall a {
  --logo-cap: 42px;
  display: grid; place-items: center; height: 100%; min-height: 104px;
  padding: 1.15rem 1.25rem; text-decoration: none;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  transition: border-color .28s var(--ease), box-shadow .28s var(--ease),
              transform .28s var(--ease), background .28s var(--ease);
}
.brand-wall a:hover { border-color: var(--brand-500); background: #fff; box-shadow: var(--shadow-md); transform: translateY(-4px); }

/* Calm by default, brand colour on hover — so the wall reads as one
   family rather than twenty competing marks. */
.brand-wall .brand-logo {
  filter: grayscale(1);
  opacity: .55;
  transition: filter .32s var(--ease), opacity .32s var(--ease), transform .32s var(--ease);
}
.brand-wall .brand-logo--vector { color: var(--ink-soft); }
.brand-wall a:hover .brand-logo { filter: none; opacity: 1; transform: scale(1.05); }
.brand-wall a:hover .brand-logo--vector { color: var(--brand-700); }

/* --- On the brand index cards ----------------------------- */
.brand-card__media {
  --logo-cap: 62px;
  position: relative; display: grid; place-items: center;
  aspect-ratio: 16 / 10; padding: 1.4rem 1.6rem;
  background: var(--surface-2); overflow: hidden;
}
.brand-card__media .brand-logo {
  filter: grayscale(1); opacity: .72;
  transition: filter .32s var(--ease), opacity .32s var(--ease), transform .38s var(--ease);
}
.brand-card__media .brand-logo--vector { color: var(--ink); }
.brand-card:hover .brand-card__media .brand-logo { filter: none; opacity: 1; transform: scale(1.05); }
.brand-card:hover .brand-card__media .brand-logo--vector { color: var(--brand-700); }
.brand-card__media::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(135deg, rgba(20,30,140,.05), rgba(0,136,208,.05));
  opacity: 0; transition: opacity .32s var(--ease);
}
.brand-card:hover .brand-card__media::after { opacity: 1; }

/* --- On a single brand's own page -------------------------- */
.brand-page__mark {
  --logo-cap: 92px;
  display: grid; place-items: center; min-height: 150px;
  padding: 1.75rem 2rem; margin: 0;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
}
.brand-page__mark .brand-logo--vector { color: var(--ink); }

@media (min-width: 720px) { .brand-wall a { --logo-cap: 46px; } }
@media (min-width: 1100px) {
  .brand-wall a { --logo-cap: 50px; min-height: 116px; }
  .brand-card__media { --logo-cap: 70px; }
  .brand-page__mark { --logo-cap: 108px; }
}

@media (prefers-reduced-motion: reduce) {
  .brand-wall .brand-logo, .brand-card__media .brand-logo { transition: none; }
}

/* =========================================================
   1.5.0 — flow lines

   A slow current of aerodynamic streamlines behind every hero.
   Drawn as one SVG with stroke-dasharray offsets animating, so it
   costs nothing: no JavaScript, no images, one compositor layer.

   Each line has its own length, speed and delay, and every duration
   is a whole number of seconds so the whole field loops seamlessly.
   ========================================================= */
.flowlines {
  position: absolute; inset: 0; z-index: 0;
  width: 100%; height: 100%;
  pointer-events: none; overflow: hidden;
  color: currentColor;
}
.flowlines svg { width: 100%; height: 100%; display: block; }
.flowlines path {
  fill: none;
  stroke: currentColor;
  vector-effect: non-scaling-stroke;
  stroke-linecap: round;
  animation: flow-run var(--dur, 14s) linear infinite;
  animation-delay: var(--delay, 0s);
}
@keyframes flow-run {
  from { stroke-dashoffset: var(--dash-from, 1200); }
  to   { stroke-dashoffset: var(--dash-to, 0); }
}

/* On a dark hero the lines are light; on a pale one they are brand blue. */
.flowlines--light { color: rgba(255, 255, 255, .5); }
.flowlines--dark  { color: rgba(20, 30, 140, .3); }

/* A soft moving sheen underneath, to give the field some depth. */
.flowlines::after {
  content: ""; position: absolute; inset: -20%;
  background: radial-gradient(46% 60% at 22% 42%, rgba(0, 136, 208, .22), transparent 70%);
  animation: flow-drift 24s ease-in-out infinite;
}
@keyframes flow-drift {
  0%, 100% { transform: translate3d(0, 0, 0)      scale(1); }
  50%      { transform: translate3d(6%, -4%, 0)   scale(1.12); }
}

/* Heroes need a stacking context so the lines sit behind the words, and
   they clip: everything decorative in here is drawn deliberately wider
   than the box (the line field runs off both edges, the copy's glow
   reaches past the gutter), and without clipping that overspill becomes
   real page width. */
.hero, .page-hero { position: relative; isolation: isolate; overflow: hidden; }
.hero .container, .page-hero .container { position: relative; z-index: 2; }
/* On a banner the lines go UNDER the wash, not over it. Sitting on top they
   were bright enough to read as a strikethrough where one crossed a line of
   text — technically behind it, but not visually. The wash mutes them the
   same way it mutes the photograph. */
.page-hero--banner .plate-crop,
.page-hero--banner .page-hero__plate { z-index: -3; }
.page-hero--banner .flowlines { z-index: -2; opacity: .75; }
.page-hero--banner .page-hero__wash { z-index: -1; }

/* A split or plain hero has no wash to mute the lines with, so the words
   carry their own. A soft white glow sits between the line field and the
   type: the lines still run edge to edge, but nothing crosses a letter
   brightly enough to read as a rule through it. */
.page-hero:not(.page-hero--banner) .flowlines { opacity: .8; }
.page-hero:not(.page-hero--banner) .page-hero__copy { position: relative; }
.page-hero:not(.page-hero--banner) .page-hero__copy::before {
  content: ""; position: absolute; z-index: -1;
  /* The horizontal reach is clamped to the page gutter. At -4rem flat it
     overhung a 390px screen by 48px, and because the hero did not clip,
     that became 48px of real page width. */
  inset: -2.5rem clamp(1rem, 4vw, 4rem);
  /* The radii are exactly half the box, so the glow reaches nothing at the
     box edge and leaves no seam of its own. */
  background: radial-gradient(50% 50% at 50% 50%,
              rgba(255, 255, 255, .92) 0%,
              rgba(255, 255, 255, .70) 52%,
              rgba(255, 255, 255, 0)  100%);
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  .flowlines path { animation: none; stroke-dashoffset: 0; }
  .flowlines::after { animation: none; }
}

/* =========================================================
   1.5.0 — the two hero animations

   Both are inline SVG with SMIL-free CSS animation, and both loop
   seamlessly: every duration is a whole number of seconds and every
   keyframe returns to its starting state.
   ========================================================= */
.hero-anim {
  position: relative; display: block; width: 100%;
  border-radius: var(--radius-lg); overflow: hidden;
  background: linear-gradient(140deg, var(--brand-900), var(--brand-700) 55%, var(--brand-600));
  box-shadow: var(--shadow-lg);
}
.hero-anim svg { width: 100%; height: auto; display: block; }
.hero-anim--flat { border-radius: 0; box-shadow: none; }

/* Spectacles: the frame draws itself, the lenses catch light, and a
   pair of streamlines sweep past. */
.spec-draw {
  stroke-dasharray: var(--len, 1400);
  stroke-dashoffset: var(--len, 1400);
  animation: spec-draw 9s var(--ease) infinite;
}
@keyframes spec-draw {
  0%   { stroke-dashoffset: var(--len, 1400); }
  34%  { stroke-dashoffset: 0; }
  86%  { stroke-dashoffset: 0; opacity: 1; }
  96%  { stroke-dashoffset: 0; opacity: 0; }
  100% { stroke-dashoffset: var(--len, 1400); opacity: 0; }
}
.spec-shine { animation: spec-shine 9s var(--ease) infinite; opacity: 0; }
@keyframes spec-shine {
  0%, 36%   { opacity: 0; transform: translateX(-14%); }
  52%       { opacity: .85; }
  70%, 100% { opacity: 0; transform: translateX(14%); }
}
.spec-lens { animation: spec-lens 9s var(--ease) infinite; opacity: 0; }
@keyframes spec-lens {
  0%, 32%  { opacity: 0; }
  46%, 88% { opacity: .28; }
  100%     { opacity: 0; }
}

/* Outlets: four markers light up in turn, on a slow radar sweep. */
@keyframes pin-pulse {
  0%, 22%, 100% { opacity: .62; transform: translateY(0) scale(1); }
  8%            { opacity: 1;   transform: translateY(-5px) scale(1.14); }
  16%           { opacity: .8;  transform: translateY(0) scale(1); }
}
.outlet-ring { transform-origin: center; animation: ring-out 4s ease-out infinite; }
@keyframes ring-out {
  0%   { transform: scale(.35); opacity: .65; }
  100% { transform: scale(1.9); opacity: 0; }
}
@keyframes scan-sweep {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}
/* The dotted routes that used to run between the markers are gone: on a
   real coastline they read as flight paths rather than as anything about
   the centres, and they crossed the land. The markers speak for
   themselves. */

@media (prefers-reduced-motion: reduce) {
  .spec-draw { stroke-dashoffset: 0; }
  .spec-lens { opacity: .28; }
  .spec-draw, .spec-shine, .spec-lens,
  .outlet-pin, .outlet-ring, .outlet-scan { animation: none; }
  .outlet-pin { opacity: .9; }
}

/* The contact illustration sits where the duplicated photograph was. */
.contact-details .hero-anim { margin-bottom: clamp(1.25rem, 2.5vw, 1.9rem); }

/* The ZEISS mark is never desaturated, faded or tinted — CD Manual 1.1,
   "Definite don'ts": no other colour, no transparent ZEISS. It sits out
   the greyscale-until-hover treatment the other brand marks get. */
.brand-wall .brand-logo--locked,
.brand-card__media .brand-logo--locked {
  filter: none !important;
  opacity: 1 !important;
}
.brand-wall a:hover .brand-logo--locked,
.brand-card:hover .brand-card__media .brand-logo--locked { transform: scale(1.05); }

/* It also needs a touch more room than a wordmark, being nearly square. */
.brand-logo--locked { max-height: calc(var(--logo-cap, 40px) * 1.18); }

/* =========================================================
   1.6.0 — cropping a header image

   Some of the artwork carried over from the old site has its wording
   burned into one half of the picture. Those words are set as real
   text now, so the half that holds them is hidden rather than left to
   compete with the live copy underneath.

   The wrapper is the window; the picture inside is twice as wide and
   slid sideways, so exactly half of it shows. Deterministic at any
   container size — unlike object-position, which does nothing once
   object-fit: cover has already decided the crop.
   ========================================================= */
.plate-crop { display: block; position: absolute; inset: 0; z-index: -2; overflow: hidden; }
.plate-crop img {
  position: absolute; top: 0; height: 100%;
  /* The window keeps --crop-keep of the picture, so the picture has to be
     1 / --crop-keep times the window's width. At the default .5 that is the
     200% this started as, and every saved value still behaves as it did. */
  width: calc(100% / var(--crop-keep, .5)); max-width: none;
  object-fit: cover; object-position: center;
}
/* Slide it left by everything the window is not showing. */
.plate-crop--keep-right img { left: calc(100% - 100% / var(--crop-keep, .5)); }
.plate-crop--keep-left  img { left: 0; }

/* A banner is wide and shallow; a cropped slice of a portrait is tall and
   narrow. Covering one with the other cuts the top and bottom hard, and
   centring that cut lands on the mouth. The uncropped plate already leans
   upward for the same reason (object-position: center 38%), so the crop
   does too, a little more strongly because its slice is narrower. */
.page-hero--banner .plate-crop img { object-position: center 30%; }

/* The same crop inside a split hero's figure, where the figure has its
   own rounded frame. */
/* A <span> is inline by default, so width and aspect-ratio simply did not
   apply to it and the figure collapsed to nothing; and the base rule's
   negative z-index — which belongs to the banner, where the crop is the
   backdrop — would have buried it behind the hero's own ground. Inside a
   figure the crop is the content, not the backdrop. */
.page-hero__figure .plate-crop {
  display: block;
  position: relative; inset: auto; z-index: auto;
  width: 100%; aspect-ratio: 3 / 2;
  border-radius: var(--radius-lg); overflow: hidden;
  box-shadow: var(--shadow-lg); background: #fff;
}
.page-hero__figure .plate-crop img { border-radius: 0; }

/* =========================================================
   1.6.0 — corrections
   ========================================================= */

/* The article heading colour introduced in 1.4.0 sits later in this file
   than the call-to-action band's own white, and at equal specificity —
   so it was winning, and "Ready to start your vision analysis?" was
   rendering brand blue on a brand blue ground. Anything on a dark band
   states its colour again, after the fact. */
.cta-band h2, .cta-band h3, .cta-band p,
.quote-section h2, .quote-section h3,
.page-hero--banner h1, .page-hero--banner h2 { color: #fff; }
.cta-band p, .quote-section p { color: rgba(255, 255, 255, .88); }

/* Phones and tablets scan text for anything that looks like a telephone
   number and turn it into a link — which is why the SSM registration
   numbers in the copyright line were coming out blue and underlined on
   mobile but not on desktop. Detection is switched off for the document
   in the head; this is the belt-and-braces half, in case a browser
   ignores that. */
.site-footer__base a[href^="tel"],
.site-footer__base [x-apple-data-detectors],
.no-autolink, .no-autolink a {
  color: inherit !important;
  text-decoration: none !important;
  pointer-events: none;
  -webkit-text-size-adjust: none;
}
.site-footer__base p { color: rgba(255, 255, 255, .74); }

/* The opening line of a header's copy, where the original artwork set it
   larger than the paragraph beneath. */
.hero-lead__strong {
  display: block; margin-bottom: .35em;
  font-size: 1.14em; font-weight: 500; color: #fff;
}
.page-hero__intro .hero-lead__strong { color: var(--brand-700); }


/* =========================================================
   1.6.0 — Back to top

   Centred at the foot of the window. The "Speak to us now" launcher sits
   80px up in the right-hand corner, so this one rides lower and in the
   middle: on a narrow screen the two stack rather than collide, and on a
   wide one they are nowhere near each other.
   ========================================================= */
.to-top {
  position: fixed; z-index: 140;
  left: 50%; bottom: 18px;
  display: inline-flex; align-items: center; gap: .5rem;
  padding: .6rem 1.1rem .6rem .95rem;
  font: inherit; font-size: var(--step--1); font-weight: 500; line-height: 1;
  color: var(--brand-700); background: rgba(255, 255, 255, .92);
  border: 1px solid var(--line); border-radius: 999px;
  box-shadow: var(--shadow-md);
  cursor: pointer;
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  /* The translate carries the centring, so the transition can add to it
     without ever losing the -50%. */
  transform: translate(-50%, 14px);
  opacity: 0;
  transition: opacity .26s var(--ease), transform .26s var(--ease),
              background .2s var(--ease), color .2s var(--ease),
              border-color .2s var(--ease);
}
.to-top[hidden] { display: none; }
.to-top.is-in { opacity: 1; transform: translate(-50%, 0); }
.to-top:hover {
  color: #fff; background: var(--brand-700); border-color: var(--brand-700);
}
.to-top.is-in:hover { transform: translate(-50%, -3px); }
.to-top:focus-visible { outline: 3px solid var(--brand-500); outline-offset: 3px; }
.to-top svg { flex: 0 0 auto; }

/* The arrow lifts a little on hover, which reads as the direction of travel. */
.to-top svg { transition: transform .26s var(--ease); }
.to-top:hover svg { transform: translateY(-2px); }

@media (max-width: 600px) {
  /* Just the arrow on a small screen: the words would crowd a phone, and
     the launcher already owns the other corner.

     1.13.2 — and it moves to the left. Centred was clear of the launcher
     while the launcher sat 80px up; now that it sits lower, the two would
     have met in the middle of a narrow screen. */
  .to-top { padding: .62rem; bottom: 14px; left: 14px; transform: translate(0, 14px); }
  .to-top.is-in { transform: translate(0, 0); }
  .to-top.is-in:hover { transform: translate(0, -3px); }
  .to-top span { position: absolute; width: 1px; height: 1px; overflow: hidden;
                 clip-path: inset(50%); white-space: nowrap; }
}

@media (prefers-reduced-motion: reduce) {
  .to-top { transition: none; }
  .to-top svg { transition: none; }
  .to-top.is-in:hover { transform: translate(-50%, 0); }
}


/* =========================================================
   1.6.0 — nothing bigger than the file it came from

   Every content image is measured on the server and handed a --nat-w.
   Capping at min(100%, --nat-w) lets a large photograph fill its column
   and stops a small product shot at its own width, where it stays sharp.
   Images with no measurement (SVG, or a file not yet copied across) fall
   back to 100% and behave exactly as before.
   ========================================================= */
.article img,
.prose-block img,
.stage__text img,
.stage__media img,
.lens-card img,
.brand-figures__button img,
.figure-inline img {
  max-width: min(100%, var(--nat-w, 100%));
  height: auto;
}

/* An image that cannot fill its column should sit in it deliberately
   rather than hug the left edge by accident. */
.article > p > img,
.prose-block > p > img,
.stage__text > p > img { display: block; margin-inline: auto; }

/* =========================================================
   1.7.0 — the cap reaches the last two places it did not

   `figure-wide` was left off the list in 1.6.0, so a 1120px banner was
   still asked to fill a 1480px container. And a figure's image was
   capped but never re-centred, so the moment the cap started biting --
   which is exactly what this release makes happen -- a 280px product
   shot sat against the left edge of a 500px column with the rest of the
   column empty beside it. Both are the same rule: stop at your own
   width, then sit in the middle of what is left.
   ========================================================= */
.figure-wide img,
.page-hero__figure > img,
.care-section__figure img {
  max-width: min(100%, var(--nat-w, 100%));
  height: auto;
}

.stage__media > img,
.figure-wide img,
.page-hero__figure > img,
.care-section__figure img,
.prose-block figure > img,
.article figure > img,
.figure-inline img {
  display: block;
  margin-inline: auto;
}

/* A crop plate is the one image that is *meant* to be twice its box: it
   is 200% wide and slid sideways so exactly half of it shows, and it
   fills its window's height. Both rules above would undo that -- the cap
   by pulling it back to 100% so the hidden half reappears, and the
   height by letting the picture find its own aspect and leave the rest
   of the window blank. The child combinators keep them apart, and this
   restates the crop's own geometry after them so ordering cannot bite. */
.plate-crop img,
.crop-inline img {
  max-width: none;
  height: 100%;
  margin-inline: 0;
}

/* --- Brand galleries -----------------------------------------------------
   These were cropped to a 4:3 tile, which cut the content of the picture
   about as often as it framed it. Each photograph now keeps its own shape
   and shows all of itself; the tiles stay aligned because the grid rows
   do the aligning, not the crop. */
.brand-figures {
  align-items: start;
}
.brand-figures__button {
  display: grid; place-items: center;
  background: #fff; padding: .35rem;
}
.brand-figures__button img {
  width: auto; height: auto;
  aspect-ratio: auto; object-fit: contain;
  border-radius: calc(var(--radius) - 4px);
}


/* The map is a wide, shallow drawing rather than the squarish panel the
   old illustration was, so it gets a ground that reads as sea and a little
   breathing room above and below the coastline. */
.hero-anim--map {
  background: linear-gradient(150deg, var(--brand-900), #10256e 52%, var(--brand-700));
  padding-block: clamp(.6rem, 2vw, 1.4rem);
}

/* =========================================================
   1.8.0 — the map, redrawn

   The coastline is traced from the client's own outline, so the shape is
   the one they recognise. It draws itself once, then the four centres
   arrive in turn. Every marker names its mall on hover and to the
   keyboard, which is the part that could not be done with a picture.
   ========================================================= */
.outlet-map { display: block; width: 100%; height: auto; overflow: visible; }

.outlet-coast {
  stroke-dasharray: 2600;
  stroke-dashoffset: 2600;
  animation: coast-draw 3.4s var(--ease) forwards;
}
.outlet-coast--borneo    { animation-delay: .35s; }
.outlet-coast--langkawi  { animation-delay: 1.1s; stroke-dasharray: 90; stroke-dashoffset: 90; }
@keyframes coast-draw { to { stroke-dashoffset: 0; } }

/* The sea breathes. A slow swell behind the land, and a second, slower
   wash over the whole panel, so the ground is never quite still. */
.outlet-scan { transform-origin: 50% 50%; animation: scan-breathe 14s ease-in-out infinite; }
@keyframes scan-breathe {
  0%, 100% { transform: scale(.90); opacity: .28; }
  50%      { transform: scale(1.10); opacity: .62; }
}
.hero-anim--map { position: relative; overflow: hidden; }
.hero-anim--map::after {
  content: ""; position: absolute; inset: -20%;
  pointer-events: none;
  background:
    radial-gradient(38% 52% at 26% 38%, rgba(0,136,208,.30), rgba(0,136,208,0) 70%),
    radial-gradient(42% 58% at 74% 62%, rgba(79,195,232,.22), rgba(79,195,232,0) 72%);
  animation: map-glow 18s ease-in-out infinite;
}
@keyframes map-glow {
  0%, 100% { opacity: .45; transform: translate3d(-2%, 1%, 0) scale(1); }
  50%      { opacity: .95; transform: translate3d(2%, -1%, 0) scale(1.08); }
}

/* --- the centres ---------------------------------------- */
.outlet-pin { cursor: pointer; outline: none; }
.outlet-pin__leader { stroke: rgba(255,255,255,.55); stroke-width: 1.4; stroke-dasharray: 3 4; }
.outlet-pin__anchor { fill: #fff; }

.outlet-pin__dot {
  fill: #fff; stroke: var(--brand-500); stroke-width: 4;
  transition: r .25s var(--ease);
  filter: drop-shadow(0 2px 6px rgba(0, 20, 60, .45));
}
/* The wave. Two rings out of every marker on a five second beat, the
   second half a beat behind the first, so a centre is always pushing
   one outward rather than blinking on a long cycle. */
.outlet-pin__halo {
  fill: none; stroke: var(--brand-500); stroke-width: 3;
  opacity: 0;
  transform-box: fill-box; transform-origin: 50% 50%;
  animation: pin-ping 5s ease-out infinite;
  animation-delay: var(--pin-delay, 0s);
}
.outlet-pin__halo--second { animation-delay: calc(var(--pin-delay, 0s) - 2.5s); }
@keyframes pin-ping {
  0%   { opacity: .75; transform: scale(.34); }
  70%  { opacity: 0;   transform: scale(1.25); }
  100% { opacity: 0;   transform: scale(1.25); }
}

/* --- the name, on hover and on focus ---------------------- */
.outlet-pin__label { opacity: 0; transition: opacity .22s var(--ease), transform .22s var(--ease); transform: translateY(8px); pointer-events: none; }
.outlet-pin__label rect { fill: #fff; }
.outlet-pin__label text {
  fill: var(--brand-900); font-family: var(--font-sans);
  font-size: 34px; font-weight: 500;
}
.outlet-pin__label rect { filter: drop-shadow(0 4px 14px rgba(0, 16, 60, .35)); }
/* A pointer under the chip, over the marker, so a wide label still
   reads as belonging to the dot it names. */
.outlet-pin__tail { fill: #fff; }
.outlet-pin:hover .outlet-pin__label,
.outlet-pin:focus-visible .outlet-pin__label,
.outlet-pin:focus .outlet-pin__label { opacity: 1; transform: translateY(0); }
.outlet-pin:hover .outlet-pin__dot,
.outlet-pin:focus .outlet-pin__dot { r: 13; }
.outlet-pin:focus-visible .outlet-pin__dot { stroke: #fff; stroke-width: 4; }

@media (prefers-reduced-motion: reduce) {
  .outlet-coast { animation: none; stroke-dashoffset: 0; }
  .outlet-scan  { animation: none; }
  .outlet-pin__halo { animation: none; opacity: 0; }
  .hero-anim--map::after { animation: none; opacity: .6; }
}


/* A brand with no mark of its own borrows its first photograph for the
   card. A logo wants air around it; a photograph wants to fill the tile,
   so it is treated as a picture rather than as a mark. */
.brand-logo--photo {
  width: 100%; height: 100%;
  max-width: none; object-fit: cover;
  filter: none; opacity: 1;
  border-radius: calc(var(--radius) - 2px);
}


/* The Fashion page's sign-off: a sparkle catching the corner of the right
   lens once the frame has finished drawing itself. Same nine second clock
   as the spectacles, and it returns to nothing at 100% so the loop is
   still seamless. */
.spec-star {
  opacity: 0;
  animation: spec-star 9s var(--ease) infinite;
}
.spec-star--small { animation-duration: 9s; }
@keyframes spec-star {
  0%, 48%   { opacity: 0; transform: scale(.3)  rotate(-35deg); }
  60%       { opacity: 1; transform: scale(1.18) rotate(6deg); }
  68%       { opacity: .82; transform: scale(.94) rotate(0deg); }
  76%       { opacity: 1; transform: scale(1.06) rotate(3deg); }
  88%       { opacity: 1; transform: scale(1)   rotate(0deg); }
  96%, 100% { opacity: 0; transform: scale(.3)  rotate(-35deg); }
}
@media (prefers-reduced-motion: reduce) {
  .spec-star { animation: none; opacity: .9; transform: none; }
}


/* =========================================================
   1.6.0 — showing half of a picture, inline

   The Happening header was one wide photograph. Its left half is the
   header; its right half belongs in the article, and this is what puts
   it there without cutting a second file. Same mechanism as the hero
   crops, wrapped so it can sit inside any figure.
   ========================================================= */
.crop-inline {
  display: block; position: relative;
  width: 100%; aspect-ratio: 8 / 5;
  overflow: hidden;
  border-radius: var(--radius-lg); box-shadow: var(--shadow-md);
  background: var(--surface-2);
}
.crop-inline img {
  position: absolute; top: 0; left: 0;
  width: calc(100% / var(--crop-keep, .5)); max-width: none; height: 100%;
  object-fit: cover; object-position: center;
  border-radius: 0; box-shadow: none;
}
.crop-inline--keep-right img { left: calc(100% - 100% / var(--crop-keep, .5)); }
.crop-inline--keep-left  img { left: 0; }
.crop-inline + img { margin-top: 1rem; }

/* A video sitting inside a paragraph column rather than in a grid of its
   own: it takes the column's width and keeps its own rhythm. */
.stage__text .video-inline,
.prose-block .video-inline { margin-top: clamp(1.1rem, 2.4vw, 1.6rem); }
.stage__text .video-inline .video-card,
.prose-block .video-inline .video-card { margin: 0; }


/* =========================================================
   1.6.0 — the picture keeps pace with a long passage

   A section whose text runs to a bullet list and a numbered list is far
   taller than the single photograph beside it, which left a column of
   nothing on the right. The picture now travels with the reader instead
   of sitting at the top and being left behind. Only where there is room
   for two columns; on a phone the figure simply follows the text.
   ========================================================= */
@media (min-width: 900px) {
  .stage__media {
    position: sticky;
    top: calc(var(--header-h, 5rem) + 1.25rem);
    align-self: start;
  }
  /* A stage tall enough to warrant it; short ones are left alone so the
     picture stays optically centred against its paragraph. */
  .stage { align-items: start; }
  .stage--centred { align-items: center; }
}


/* =========================================================
   1.6.0 — every picture arrives, and answers to the pointer

   Article images are given data-reveal on the server (see
   content_images() in app/lib/helpers.php), so they fade and settle into
   place as the reader reaches them rather than being simply present. On
   top of that, a picture lifts very slightly under the pointer: enough
   to feel alive, not enough to distract from the words beside it.
   ========================================================= */
.stage__media img,
.prose-block figure img,
.photo-grid figure img,
.article figure img,
.crop-inline img {
  transition: transform .55s var(--ease), box-shadow .45s var(--ease);
  will-change: transform;
}
@media (hover: hover) and (pointer: fine) {
  .stage__media img:hover,
  .prose-block figure:hover img,
  .photo-grid figure:hover img,
  .crop-inline:hover img {
    transform: scale(1.03);
  }
  .crop-inline--keep-right:hover img { transform: scale(1.03); }
  .crop-inline--keep-left:hover  img { transform: scale(1.03); }
}

/* A figure holding more than one picture cascades them, so a stacked
   column arrives one after another rather than all at once. */
.js .stage__media img[data-reveal]:nth-of-type(2) { transition-delay: .09s; }
.js .stage__media img[data-reveal]:nth-of-type(3) { transition-delay: .18s; }

@media (prefers-reduced-motion: reduce) {
  .stage__media img, .prose-block figure img,
  .photo-grid figure img, .article figure img, .crop-inline img {
    transition: none !important;
  }
  .stage__media img:hover, .crop-inline:hover img { transform: none !important; }
  .crop-inline--keep-right img { transform: none; }
}

/* =========================================================
   1.13.0 — a mark is never cropped, and the panel never scrolls
   ========================================================= */

/* --- The brand marks on a card ----------------------------
   The tile clips what overflows it, so a mark that already fills the
   tile's width cannot be scaled up on hover without losing its ends.
   It rests a shade under full size instead and settles into it, which
   reads as the same small lift and can never crop anything. */
/* One tile to a row on a phone, so the tile is wide and a mark that is
   capped for a three-across grid looks lost in it. The cap comes up;
   a wide wordmark is already held by the tile's width and does not
   move, a tall mark uses the room. */
@media (max-width: 560px) {
  .brand-card__media { --logo-cap: 84px; }
}

.brand-card__media .brand-logo { transform: scale(.96); }
.brand-card:hover .brand-card__media .brand-logo,
.brand-card:hover .brand-card__media .brand-logo--locked { transform: scale(1); }

@media (prefers-reduced-motion: reduce) {
  .brand-card__media .brand-logo,
  .brand-card:hover .brand-card__media .brand-logo,
  .brand-card:hover .brand-card__media .brand-logo--locked { transform: none; }
}

/* --- "Speak to us now": one card, three faces --------------
   The card is a fixed size. What used to be a single column taller
   than any panel it could sit in is dealt into three faces that swap
   inside it: the choices, the phone numbers, and the message. The
   card does not resize when you move between them, and none of them
   needs scrolling. */
.speak__panel {
  width: min(392px, calc(100vw - 2rem));
  height: min(486px, calc(100dvh - 100px));
  max-height: none;
}

.speak__head {
  align-items: center;
  padding: .9rem 1rem .85rem;
  gap: .7rem;
}
.speak__heading { min-width: 0; }
.speak__title { font-size: 1.02rem; line-height: 1.25; }

.speak__back {
  flex: 0 0 auto; display: grid; place-items: center; width: 30px; height: 30px;
  border: 0; border-radius: 50%; background: rgba(255, 255, 255, .16); color: #fff;
  cursor: pointer; transition: background .2s var(--ease), transform .2s var(--ease);
}
.speak__back:hover { background: rgba(255, 255, 255, .3); transform: translateX(-2px); }
.speak__back[hidden] { display: none; }

.speak__body {
  position: relative; flex: 1 1 auto; min-height: 0;
  padding: 0; overflow: hidden;
}

.speak__face {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; justify-content: center;
  gap: .5rem; padding: .95rem 1rem 1.05rem;
  opacity: 0; visibility: hidden; transform: translateX(16px);
  transition: opacity .26s var(--ease), transform .34s var(--ease), visibility .26s;
  /* Only ever reached on a screen too short to hold the card at all,
     a phone held sideways for instance. Nothing is put out of reach. */
  overflow-y: auto;
}
.speak__face.is-current { opacity: 1; visibility: visible; transform: none; }
.speak__face.is-leaving { transform: translateX(-16px); }
.speak__face[data-face="message"] { justify-content: flex-start; }

@media (prefers-reduced-motion: reduce) {
  .speak__face { transition: opacity .01s linear, visibility .01s; transform: none; }
  .speak__face.is-leaving { transform: none; }
}

/* The three choices. Each is a full-width row that says where it goes. */
.speak__quick { gap: .55rem; }
.speak__quick-item {
  width: 100%; font: inherit; text-align: left; background: var(--surface);
  color: var(--ink);
}
.speak__quick-text { flex: 1 1 auto; min-width: 0; }
.speak__quick-go { flex: 0 0 auto; color: var(--brand-500); transition: transform .22s var(--ease); }
.speak__quick-item:hover .speak__quick-go { transform: translateX(3px); }
.speak__quick-item:focus-visible { outline: 3px solid var(--brand-500); outline-offset: 2px; }

/* The phone numbers, one tappable row each. */
.speak__face[data-face="calls"] .speak__outlets {
  margin: 0; padding: 0; gap: .4rem;
}
.speak__face[data-face="calls"] .speak__outlets a {
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: .6rem .8rem; align-items: center;
  transition: border-color .2s var(--ease), background .2s var(--ease), transform .2s var(--ease);
}
.speak__face[data-face="calls"] .speak__outlets a:hover {
  border-color: var(--brand-500); background: var(--brand-100); transform: translateX(2px);
}
.speak__panel:focus { outline: none; }
.speak__lead { margin: 0 0 .15rem; font-size: .78rem; color: var(--muted); }
.speak__note { margin: .8rem 0 0; font-size: .74rem; color: var(--muted); text-align: center; }
.speak__face[data-face="home"] .speak__note { text-align: left; margin-top: .55rem; }
.speak__note a { color: var(--brand-700); }

/* The message, written to fit the card rather than to fill a page. Every
   measurement here is smaller than the same field on a full page, because
   the card is the size it is and the form has to live inside it. */
.speak__form .field { margin-bottom: .45rem; }
.speak__form label { margin-bottom: .18rem; font-size: .74rem; }
.speak__form .field-row { gap: .5rem; }
.speak__form input, .speak__form textarea { padding: .5rem .7rem; }
.speak__form textarea { min-height: 54px; resize: none; }
.speak__form .btn { padding: .58rem 1rem; }
.speak__status { margin: .4rem 0 0; font-size: .78rem; min-height: 0; }
.speak__status:empty { display: none; }
.speak__form .form-secured { margin-top: .5rem; font-size: .68rem; line-height: 1.4; max-width: none; }
.speak__form .form-secured svg { width: 11px; height: 11px; margin-top: .16em; }

@media (max-width: 560px) {
  .speak__panel { width: 100%; height: min(486px, calc(100dvh - 100px)); }
}

/* Short screens, a phone held sideways above all: the card takes what
   there is rather than reaching past the top of the window. */
@media (max-height: 520px) {
  .speak { bottom: 18px; }
  .speak__panel { height: calc(100dvh - 88px); }
  .speak__face { justify-content: flex-start; }
}
