/* ═══════════════════════════════════════════════════════════════════════════
   Fun-Tastic 悠樂長 · components.css
   Tones, the shell (header, menu, language panel, footer …) and every shared
   component of BRIEF 5.5. Each block starts with the markup it expects; the same
   snippets, with notes, are in tools/README.md and live at /styleguide/.

   Contents
     1  Tones                 6  Language panel          11  Photo frames, figures
     2  Icons                 7  Suggestion bar          12  Ghost numerals, stats, steps
     3  Buttons and links     8  Preloader, progress     13  FAQ, tabs, segmented
     4  Brand, header         9  Footer, seam            14  Contact, monogram
     5  Overlays, menu       10  Panels, dot rows        15  Marquee, carousel
                                                         16  Page hero, bridge, doors
                                                         17  Motion helpers
   ═══════════════════════════════════════════════════════════════════════════ */

@layer components {

/* ───────────────────────────────────────────────────────────── 1 Tones ──
   A tone paints a .section or a .panel and re-points the semantic colours, so
   every component inside adapts: text, links, focus ring, buttons, dots, rows.
   Use them on sections (.section--indigo), panels (.panel--mint) or anything (.tone-orchid).
   Text on orchid is white only. Text on mint is ink or indigo, never white. */

.tone-white, .section--white, .panel--white {
  --bg: var(--white); --fg: var(--ink); --fg-muted: var(--slate); --heading: var(--indigo);
  --link: var(--orchid-deep); --accent: var(--orchid); --rule: var(--line); --focus: var(--orchid);
  --row: var(--indigo-mist); --dot-a: var(--orchid); --dot-b: var(--mint); --ghost: var(--indigo-mist);
  --btn-bg: var(--indigo); --btn-fg: var(--white); --btn-bg-hover: var(--indigo-hover); --btn-line: var(--indigo);
  --wa-bg: var(--mint); --wa-fg: var(--indigo); --wa-bg-hover: var(--mint-hover);
}
.tone-mist-indigo, .section--mist-indigo, .panel--mist-indigo {
  --bg: var(--indigo-mist); --fg: var(--ink); --fg-muted: var(--slate); --heading: var(--indigo);
  --link: var(--orchid-deep); --accent: var(--orchid); --rule: var(--line); --focus: var(--orchid);
  --row: var(--white); --dot-a: var(--orchid); --dot-b: var(--mint); --ghost: var(--white);
  --btn-bg: var(--indigo); --btn-fg: var(--white); --btn-bg-hover: var(--indigo-hover); --btn-line: var(--indigo);
  --wa-bg: var(--mint); --wa-fg: var(--indigo); --wa-bg-hover: var(--mint-hover);
}
.tone-mist-orchid, .section--mist-orchid, .panel--mist-orchid {
  --bg: var(--orchid-mist); --fg: var(--ink); --fg-muted: var(--slate); --heading: var(--indigo);
  --link: var(--orchid-deep); --accent: var(--orchid); --rule: var(--line); --focus: var(--orchid-deep);
  --row: var(--white); --dot-a: var(--orchid); --dot-b: var(--indigo); --ghost: var(--white);
  --btn-bg: var(--indigo); --btn-fg: var(--white); --btn-bg-hover: var(--indigo-hover); --btn-line: var(--indigo);
  --wa-bg: var(--mint); --wa-fg: var(--indigo); --wa-bg-hover: var(--mint-hover);
}
.tone-mist-mint, .section--mist-mint, .panel--mist-mint {
  --bg: var(--mint-mist); --fg: var(--ink); --fg-muted: var(--slate); --heading: var(--indigo);
  --link: var(--mint-deep); --accent: var(--mint-deep); --rule: var(--line); --focus: var(--orchid);
  --row: var(--white); --dot-a: var(--mint); --dot-b: var(--indigo); --ghost: var(--white);
  --btn-bg: var(--indigo); --btn-fg: var(--white); --btn-bg-hover: var(--indigo-hover); --btn-line: var(--indigo);
  --wa-bg: var(--mint); --wa-fg: var(--indigo); --wa-bg-hover: var(--mint-hover);
}
.tone-indigo, .section--indigo, .panel--indigo {
  --bg: var(--indigo); --fg: var(--white); --fg-muted: var(--on-indigo-muted); --heading: var(--white);
  --link: var(--mint); --accent: var(--mint); --rule: var(--white-a24); --focus: var(--mint);
  --row: var(--white-a08); --dot-a: var(--mint); --dot-b: var(--orchid); --ghost: var(--white-a08);
  --btn-bg: var(--mint); --btn-fg: var(--indigo); --btn-bg-hover: var(--mint-hover); --btn-line: var(--white);
  --wa-bg: var(--mint); --wa-fg: var(--indigo); --wa-bg-hover: var(--mint-hover);
}
.tone-orchid, .section--orchid, .panel--orchid {
  --bg: var(--orchid); --fg: var(--white); --fg-muted: var(--white); --heading: var(--white);
  --link: var(--white); --accent: var(--white); --rule: var(--white-a40); --focus: var(--white);
  --row: var(--white-a14); --dot-a: var(--white); --dot-b: var(--indigo); --ghost: var(--white-a14);
  --btn-bg: var(--white); --btn-fg: var(--indigo); --btn-bg-hover: var(--indigo-mist); --btn-line: var(--white);
  --wa-bg: var(--white); --wa-fg: var(--indigo); --wa-bg-hover: var(--indigo-mist);
}
.tone-mint, .section--mint, .panel--mint {
  --bg: var(--mint); --fg: var(--ink); --fg-muted: var(--indigo); --heading: var(--indigo);
  --link: var(--indigo); --accent: var(--indigo); --rule: var(--indigo-a12); --focus: var(--indigo);
  --row: var(--white-a40); --dot-a: var(--indigo); --dot-b: var(--white); --ghost: var(--white-a24);
  --btn-bg: var(--indigo); --btn-fg: var(--white); --btn-bg-hover: var(--indigo-hover); --btn-line: var(--indigo);
  --wa-bg: var(--indigo); --wa-fg: var(--white); --wa-bg-hover: var(--indigo-hover);
}
:is([class*="tone-"], [class*="section--"], [class*="panel--"]) { color: var(--fg); }
[class*="tone-"] { background: var(--bg); }

/* ───────────────────────────────────────────────────────────── 2 Icons ──
   {{ icon('whatsapp') }}  →  <svg class="icon" viewBox="0 0 48 48" aria-hidden="true"><use href="#i-whatsapp"/></svg>
   One family: 48 box, 1.75px stroke that does not scale, round caps and joins, currentColor. */

.sprite { position: absolute; inline-size: 0; block-size: 0; overflow: hidden; }
.sprite :is(path, circle, ellipse, line, polyline, polygon, rect) { vector-effect: non-scaling-stroke; }
.icon {
  flex: none;
  inline-size: 1.5rem;
  block-size: 1.5rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
  overflow: visible;
}
.icon--up { rotate: -90deg; }
.icon--down { rotate: 90deg; }
.icon--back { transform: scaleX(-1); }   /* a chevron that points to the start side */

/* Icon in a filled circle (BRIEF 5.5): mint-mist + mint-deep by default.
   <span class="icon-circle icon-circle--orchid">{{ icon('svc-phone') }}</span> */
.icon-circle {
  --size: 3.5rem;
  flex: none;
  display: inline-grid;
  place-items: center;
  inline-size: var(--size);
  block-size: var(--size);
  border-radius: 50%;
  background: var(--mint-mist);
  color: var(--mint-deep);
}
.icon-circle > .icon { inline-size: calc(var(--size) * .5); block-size: calc(var(--size) * .5); }
.icon-circle--sm { --size: 2.75rem; }
.icon-circle--lg { --size: 4.5rem; }
.icon-circle--xl { --size: 6rem; }
.icon-circle--orchid { background: var(--orchid-mist); color: var(--orchid-deep); }
.icon-circle--indigo { background: var(--indigo); color: var(--white); }
.icon-circle--white { background: var(--white); color: var(--indigo); }
.icon-circle--mint-solid { background: var(--mint); color: var(--indigo); }
.icon-circle--orchid-solid { background: var(--orchid); color: var(--white); }
.icon-circle--ghost { background: var(--row); color: currentColor; }

/* ─────────────────────────────────────────────────── 3 Buttons and links ──
   <a class="btn btn--primary" href="…">See all six services</a>
   <a class="btn btn--secondary" href="…">Email us</a>
   <a class="btn btn--whatsapp" href="https://wa.me/…">{{ icon('whatsapp') }}WhatsApp Ms Leung</a>
   Pill, at least 52px high. The label says exactly what happens; no arrows glued on.
   On an indigo section the primary button turns mint by itself (tones). */

.btn {
  --_bg: var(--btn-bg);
  --_fg: var(--btn-fg);
  --_hover: var(--btn-bg-hover);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .6em;
  min-block-size: 3.25rem;
  padding: .65rem 1.65rem;
  border: 2px solid transparent;
  border-radius: var(--r-full);
  background: var(--_bg);
  color: var(--_fg);
  font-family: var(--font-text);
  font-size: 1.0625rem;
  font-weight: var(--fw-strong);
  line-height: var(--lh-ui);
  letter-spacing: var(--tracking-ui);
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--dur-2) ease, border-color var(--dur-2) ease, color var(--dur-2) ease, scale var(--dur-1) var(--ease-out);
}
.btn:active { scale: .97; }
.btn > .icon { inline-size: 1.5em; block-size: 1.5em; margin-inline-start: -.2em; }
.btn--primary { --_bg: var(--btn-bg); --_fg: var(--btn-fg); }
.btn--secondary { --_bg: transparent; --_fg: var(--btn-line); --_hover: var(--row); border-color: var(--btn-line); }
.btn--whatsapp { --_bg: var(--wa-bg); --_fg: var(--wa-fg); --_hover: var(--wa-bg-hover); }
.btn--on-dark { --_bg: var(--mint); --_fg: var(--indigo); --_hover: var(--mint-hover); }
.btn--lg { min-block-size: 3.75rem; padding-inline: 2rem; font-size: 1.125rem; }
.btn--block { display: flex; inline-size: 100%; }
/* With an icon, a label that needs two lines starts beside the icon instead of centring each line. */
.btn:has(> .icon) { text-align: start; }
@media (hover: hover) and (pointer: fine) {
  .btn:hover { background: var(--_hover); }
}

/* A round button for controls (carousel arrows, pause). 52px. */
.round-btn {
  flex: none;
  display: inline-grid;
  place-items: center;
  inline-size: 3.25rem;
  block-size: 3.25rem;
  border: 2px solid var(--btn-line);
  border-radius: 50%;
  color: var(--btn-line);
  transition: background-color var(--dur-2) ease, color var(--dur-2) ease, scale var(--dur-1) var(--ease-out), opacity var(--dur-2) ease;
}
.round-btn:active { scale: .94; }
.round-btn:disabled, .round-btn[aria-disabled="true"] { opacity: .35; cursor: default; }
@media (hover: hover) and (pointer: fine) {
  .round-btn:not(:disabled):hover { background: var(--btn-line); color: var(--bg); }
}

/* Text link with an arc drawn under it: the bridge, as an underline.
   <a class="link-arc" href="…">Read about EyeCare 365</a>    One line of text only. */
.link-arc {
  position: relative;
  display: inline-flex;
  align-items: center;
  align-self: flex-start;
  justify-self: start;
  inline-size: fit-content;
  max-inline-size: 100%;
  min-block-size: var(--target);
  padding-block-end: .5em;
  color: var(--heading);
  font-weight: var(--fw-strong);
  line-height: var(--lh-ui);
  text-decoration: none;
}
.link-arc::after {
  content: "";
  position: absolute;
  inset-inline: 0;
  inset-block-end: .12em;
  block-size: .5em;
  border-block-start: .14em solid var(--accent);
  border-radius: 50% 50% 0 0 / 100% 100% 0 0;
  pointer-events: none;
}
.link-arc:is(:hover, :focus-visible)::after { animation: ft-arc-draw var(--dur-4) var(--ease-out); }

/* ──────────────────────────────────────────────────── 4 Brand and header ── */

.brand {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: .7rem;
  min-block-size: var(--target);
  color: var(--indigo);
  text-decoration: none;
}
.brand__mark { flex: none; display: block; inline-size: 3.25rem; block-size: 3.25rem; }
.brand__mark > img { inline-size: 100%; block-size: 100%; object-fit: contain; }
.brand__mark--shared { view-transition-name: ft-mark; }
.brand__name { display: grid; row-gap: .1rem; }
.brand__zh { font-size: 1.4375rem; font-weight: 700; line-height: 1.15; letter-spacing: .06em; }   /* lint-ok: three brand glyphs, always Chinese */
.brand__latin { font-size: 1.0625rem; font-weight: 600; line-height: 1.1; letter-spacing: -0.01em; }   /* lint-ok: the Latin wordmark, always Lexend */

.site-header {
  position: sticky;
  inset-block-start: 0;
  z-index: var(--z-header);
  background: var(--white);
  transition: transform var(--dur-3) var(--ease-out), box-shadow var(--dur-3) ease;
  view-transition-name: ft-header;
}
.site-header.is-stuck { box-shadow: var(--shadow); }
.site-header.is-hidden { transform: translateY(-115%); }
.site-header__bar {
  display: flex;
  align-items: center;
  column-gap: clamp(.75rem, .2rem + 1.6vw, 1.75rem);
  row-gap: .25rem;
  inline-size: min(100% - 2 * var(--gutter), var(--container-wide));
  min-block-size: 5.25rem;
  margin-inline: auto;
  padding-block: .75rem;
}
.site-nav { flex: 1 1 auto; min-inline-size: 0; display: flex; justify-content: center; }
.site-nav__list { display: flex; flex-wrap: nowrap; gap: .125rem; white-space: nowrap; }
.site-nav__link {
  display: inline-flex;
  align-items: center;
  min-block-size: var(--target);
  padding-inline: .8rem;
  border-radius: var(--r-full);
  color: var(--indigo);
  font-size: 1.0625rem;
  font-weight: var(--fw-medium);
  line-height: var(--lh-ui);
  text-decoration: none;
}
.site-nav__link > span, .site-menu__link > span { position: relative; }
.site-nav__link > span::after, .site-menu__link > span::after {
  content: "";
  position: absolute;
  inset-inline: -.1em;
  inset-block-start: calc(100% + .08em);
  block-size: .42em;
  border-block-start: .13em solid var(--orchid);
  border-radius: 50% 50% 0 0 / 100% 100% 0 0;
  clip-path: inset(-2px 101% -2px 0);
  transition: clip-path var(--dur-3) var(--ease-out);
  pointer-events: none;
}
.site-nav__link:is(:hover, :focus-visible, [aria-current="page"]) > span::after,
.site-menu__link:is(:hover, :focus-visible, [aria-current="page"]) > span::after { clip-path: inset(-2px 0 -2px 0); }
.site-nav__link[aria-current="page"] { font-weight: var(--fw-strong); }

.site-tools { flex: none; display: flex; align-items: center; gap: .5rem; margin-inline-start: auto; }

/* Quiet pill buttons of the shell: language, menu, close. */
.tool-btn {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .5rem;
  min-inline-size: var(--target);
  min-block-size: var(--target);
  padding-inline: .7rem;
  border: 1.5px solid var(--rule);
  border-radius: var(--r-full);
  background: transparent;
  color: var(--heading);
  font-size: 1rem;
  font-weight: var(--fw-medium);
  line-height: var(--lh-ui);
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color var(--dur-2) ease, border-color var(--dur-2) ease, scale var(--dur-1) var(--ease-out);
}
.tool-btn:active { scale: .96; }
@media (hover: hover) and (pointer: fine) {
  .tool-btn:hover { background: var(--row); }
}
.menu-btn { display: none; border-color: var(--indigo); background: var(--indigo); color: var(--white); padding-inline: .9rem 1.1rem; }
@media (hover: hover) and (pointer: fine) {
  .menu-btn:hover { background: var(--indigo-hover); }
}
.site-header__cta { min-block-size: 3.25rem; white-space: nowrap; }

/* What the measured level shows and hides (see header.html). */
.site-header:is([data-level="1"], [data-level="3"], [data-level="4"], [data-level="5"], [data-level="6"]) .lang-btn__name { display: none; }
.site-header:is([data-level="2"], [data-level="3"], [data-level="4"], [data-level="5"], [data-level="6"]) .site-nav { display: none; }
.site-header:is([data-level="2"], [data-level="3"], [data-level="4"], [data-level="5"], [data-level="6"]) .menu-btn { display: inline-flex; }
.site-header:is([data-level="4"], [data-level="5"], [data-level="6"]) .site-header__cta { display: none; }
.site-header:is([data-level="5"], [data-level="6"]) .site-tools > .textsize { display: none; }
.site-header[data-level="6"] .menu-btn__label { display: none; }
/* The tightest state (320px wide, largest text): fixed sizes, so the bar cannot outgrow the screen. */
.site-header[data-level="6"] .site-header__bar { column-gap: 10px; }
.site-header[data-level="6"] .site-tools { gap: 6px; }
.site-header[data-level="6"] .tool-btn { inline-size: 44px; block-size: 44px; padding-inline: 0; }
.site-header[data-level="6"] .tool-btn > .icon { inline-size: 24px; block-size: 24px; }
.site-header[data-level="6"] .brand { gap: 8px; }
.site-header[data-level="6"] .brand__mark { inline-size: 40px; block-size: 40px; }
.site-header[data-level="6"] .brand__zh { font-size: 19px; }      /* lint-ok: the last resort at 320px with 125% text; 19px and 16px are still above the floor */
.site-header[data-level="6"] .brand__latin { font-size: 16px; }
.site-header:is([data-level="5"], [data-level="6"]) .site-header__bar { min-block-size: 4.5rem; }
.site-header:is([data-level="5"], [data-level="6"]) .brand__mark { inline-size: 2.75rem; block-size: 2.75rem; }
.site-header:is([data-level="5"], [data-level="6"]) .brand__zh { font-size: 1.25rem; }
.site-header:is([data-level="5"], [data-level="6"]) .brand__latin { font-size: 1rem; }

/* Without JavaScript: no measuring, no menu, no text-size memory. The nav wraps instead. */
html:not(.js) .site-header { position: static; }
html:not(.js) .site-header__bar { flex-wrap: wrap; }
html:not(.js) .site-nav { order: 3; flex-basis: 100%; justify-content: flex-start; }
html:not(.js) .site-nav__list { flex-wrap: wrap; white-space: normal; }
html:not(.js) .textsize { display: none; }

/* Text size: three A's. A segmented control (see 13). */
.textsize {
  position: relative;
  flex: none;
  display: inline-flex;
  padding: 3px;
  border-radius: var(--r-full);
  background: var(--row);
}
.textsize__btn {
  position: relative;
  display: inline-grid;
  place-items: center;
  inline-size: var(--target);
  block-size: var(--target);
  border-radius: 50%;
  color: var(--heading);
  transition: background-color var(--dur-2) ease, color var(--dur-2) ease, scale var(--dur-1) var(--ease-out);
}
.textsize__btn:active { scale: .92; }
.textsize__a { font-family: var(--font-latin); font-weight: 600; line-height: 1; }
.textsize__btn:nth-of-type(1) .textsize__a { font-size: 1rem; }
.textsize__btn:nth-of-type(2) .textsize__a { font-size: 1.25rem; }
.textsize__btn:nth-of-type(3) .textsize__a { font-size: 1.5rem; }
.textsize__btn[aria-pressed="true"] { background: var(--indigo); color: var(--white); }
@media (hover: hover) and (pointer: fine) {
  .textsize__btn:not([aria-pressed="true"]):hover { background: var(--white); }
}

/* ───────────────────────────────────────────────────── 5 Overlays, menu ──
   The menu and the language panel are native <dialog> elements that fill the screen
   and open as an iris from the button that was pressed (core.js sets --ox --oy --or). */

.overlay {
  position: fixed;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  margin: 0;
  background: var(--white);
  color: var(--ink);
  overflow: hidden;
  clip-path: circle(0px at var(--ox, 100%) var(--oy, 0%));
  transition: clip-path var(--dur-3) var(--ease-in-out);
}
.overlay[open] { display: flex; flex-direction: column; }
.overlay::backdrop { background: transparent; }
.overlay.is-open { clip-path: circle(var(--or, 150vmax) at var(--ox, 100%) var(--oy, 0%)); transition: clip-path 620ms var(--ease-iris); }
.overlay__bar {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  inline-size: min(100% - 2 * var(--gutter), var(--container-wide));
  min-block-size: 5.25rem;
  margin-inline: auto;
  padding-block: .75rem;
}
.overlay__body {
  flex: 1 1 auto;
  min-block-size: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
  scrollbar-color: var(--line) transparent;
}
.overlay__close { padding-inline: .8rem 1.1rem; }

/* Rows that cascade in once the overlay is open. core.js numbers them with --i. */
.overlay [data-cascade] > * {
  opacity: 0;
  transform: translateY(.9rem);
  transition: opacity var(--dur-3) ease, transform var(--dur-4) var(--ease-out);
}
.overlay.is-open [data-cascade] > * {
  opacity: 1;
  transform: none;
  transition-delay: calc(140ms + var(--i, 0) * 34ms);
}
@media (prefers-reduced-motion: reduce) {
  .overlay, .overlay.is-open { clip-path: none; transition: opacity var(--dur-2) ease; }
  .overlay { opacity: 0; }
  .overlay.is-open { opacity: 1; }
  .overlay [data-cascade] > * { opacity: 1; transform: none; transition: none; }
}

.site-menu__body {
  display: grid;
  align-content: start;
  gap: clamp(2rem, 6vw, 4rem);
  padding-block: clamp(1rem, 4vw, 3rem) clamp(2rem, 6vw, 4rem);
  padding-inline: max(var(--gutter), (100% - var(--container)) / 2);   /* padding, not width: focus rings need the room */
}
.site-menu__list { display: grid; gap: .25rem; }
.site-menu__link {
  display: flex;
  align-items: center;
  gap: 1rem;
  min-block-size: 4rem;
  padding-block: .4rem;
  border-radius: var(--r-row);
  color: var(--indigo);
  font-family: var(--font-display);
  font-size: clamp(1.75rem, 1.3rem + 2.4vw, 2.75rem);
  font-weight: var(--fw-display);
  line-height: var(--lh-display);
  letter-spacing: var(--tracking-display);
  text-decoration: none;
}
.site-menu__link::before {
  content: "";
  flex: none;
  inline-size: .8rem;
  block-size: .8rem;
  border-radius: 50%;
  background: var(--orchid);
  transition: scale var(--dur-3) var(--ease-pop);
}
.site-menu__list > li:nth-child(even) .site-menu__link::before { background: var(--mint); }
.site-menu__link:is(:hover, :focus-visible)::before { scale: 1.5; }
.site-menu__link > span::after { border-block-start-width: .09em; }
.site-menu__tools { display: grid; gap: 1.25rem; justify-items: start; }
.site-menu__tools > .btn { min-inline-size: min(100%, 18rem); }
.site-menu__textsize { display: flex; flex-wrap: wrap; align-items: center; gap: .75rem 1rem; }
.site-menu__label { color: var(--slate); font-size: var(--fs-small); }
@media (min-width: 56rem) {
  .site-menu__body { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); align-items: start; }
  .site-menu__tools { padding-block-start: .75rem; }
}

/* ─────────────────────────────────────────────────── 6 Language panel ── */

.lang-panel__title { font-size: var(--fs-sub); }
.lang-panel__body {
  display: grid;
  gap: clamp(1.5rem, 4vw, 4rem);
  align-content: start;
  padding-block: clamp(.5rem, 2vw, 1.5rem) clamp(2rem, 5vw, 3.5rem);
  padding-inline: max(var(--gutter), (100% - var(--container-wide)) / 2);
}
/* The greeting: two overlapping circles, a senior and a young person, saying hello. */
.lang-panel__hello { display: none; }
@media (min-width: 64rem) {
  .lang-panel__body { grid-template-columns: minmax(0, 4fr) minmax(0, 8fr); align-items: center; align-content: center; }
  .lang-panel__hello {
    position: relative;
    display: grid;
    place-items: center;
    inline-size: min(100%, 23rem);
    aspect-ratio: 1;
    isolation: isolate;
  }
  .lang-panel__dot { position: absolute; border-radius: 50%; }
  .lang-panel__dot--orchid { inset-block-start: 0; inset-inline-start: 0; inline-size: 82%; aspect-ratio: 1; background: var(--orchid-mist); }
  .lang-panel__dot--mint { inset-block-end: 0; inset-inline-end: 0; inline-size: 46%; aspect-ratio: 1; background: var(--mint); mix-blend-mode: multiply; opacity: .55; }
  .lang-panel__greeting {
    position: absolute;
    inset-block-start: 0;
    inset-inline-start: 0;
    display: grid;
    place-items: center;
    inline-size: 82%;
    aspect-ratio: 1;
    padding: 12%;
    color: var(--indigo);
    font-family: var(--font-display);
    font-size: clamp(1.75rem, 1rem + 2vw, 3rem);
    font-weight: var(--fw-display);
    line-height: var(--lh-display);
    text-align: center;
    transition: opacity var(--dur-1) ease;
  }
  .lang-panel__greeting.is-swapping { opacity: 0; }
}
.lang-list { display: grid; gap: .6rem; grid-template-columns: repeat(auto-fill, minmax(min(15.5rem, 100%), 1fr)); }
.lang-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-areas: "dot native mark" "dot english mark";
  align-items: center;
  column-gap: .9rem;
  min-block-size: 4.5rem;
  padding: .6rem 1rem .6rem 1.1rem;
  border-radius: var(--r-row);
  background: var(--indigo-mist);
  color: var(--indigo);
  text-decoration: none;
  line-height: 1.3;
  transition: background-color var(--dur-2) ease, scale var(--dur-1) var(--ease-out);
}
.lang-row:active { scale: .98; }
.lang-row::before {
  content: "";
  grid-area: dot;
  inline-size: .75rem;
  block-size: .75rem;
  border-radius: 50%;
  background: var(--orchid);
  transition: scale var(--dur-3) var(--ease-pop);
}
.lang-list > li:nth-child(even) .lang-row::before { background: var(--mint); }
.lang-row__native { grid-area: native; align-self: end; font-size: 1.3125rem; font-weight: var(--fw-strong); }
.lang-row__english { grid-area: english; align-self: start; color: var(--slate); font-family: var(--font-latin); font-size: 1rem; line-height: 1.3; }
.lang-row__current {
  grid-area: mark;
  display: grid;
  place-items: center;
  inline-size: 2.25rem;
  block-size: 2.25rem;
  border-radius: 50%;
  background: var(--mint);
  color: var(--indigo);
}
.lang-row__current > .icon { inline-size: 1.25rem; block-size: 1.25rem; stroke-width: 2.25; }
.lang-row[aria-current="true"] { background: var(--indigo); color: var(--white); }
.lang-row[aria-current="true"] .lang-row__english { color: var(--on-indigo-muted); }
@media (hover: hover) and (pointer: fine) {
  .lang-list > li:nth-child(odd) .lang-row:not([aria-current="true"]):hover { background: var(--orchid-mist); }
  .lang-list > li:nth-child(even) .lang-row:not([aria-current="true"]):hover { background: var(--mint-mist); }
  .lang-row:hover::before { scale: 1.45; }
}
.lang-row:focus-visible::before { scale: 1.45; }
.lang-panel__note { margin-block-start: 1.5rem; color: var(--slate); font-size: var(--fs-small); }

/* ─────────────────────────────────────────────────── 7 Suggestion bar ──
   Docked to the bottom edge of the viewport, laid out in the direction of the language it offers.
   Narrow screens: one low, full-width strip: the globe, the offer as a button, a cross to close.
   The question is still read out there, but not shown: the button says the same thing, and a low
   strip leaves the first screen to the page.
   From 48rem: a floating capsule in the middle of the bottom edge, with the question and the words
   of the close button.
   core.js publishes the room the bar takes as --dock-h on <html> (0px when there is no bar). */

.suggest {
  position: fixed;
  inset-inline: 0;
  inset-block-end: 0;
  z-index: var(--z-suggest);
  display: flex;
  align-items: center;
  gap: .5rem;
  padding: .5rem .75rem max(.5rem, env(safe-area-inset-bottom));
  border-block-start: 1.5px solid var(--line);
  border-start-start-radius: 1.25rem;
  border-start-end-radius: 1.25rem;
  background: var(--white);
  box-shadow: 0 -10px 30px -18px rgb(18 47 108 / .3);
  transform: translateY(calc(100% + 2rem));
  transition: transform var(--dur-4) var(--ease-out);
}
.suggest.is-in { transform: none; }
.suggest__text { max-inline-size: none; color: var(--indigo); font-size: 1.0625rem; font-weight: var(--fw-medium); line-height: var(--lh-ui); }
.suggest__text:empty { display: none; }
.suggest__actions { flex: 1 1 auto; display: flex; align-items: center; gap: .4rem; min-inline-size: 0; }
.suggest__yes { flex: 1 1 auto; min-block-size: 3rem; padding-inline: 1.1rem; }
.suggest__no { border-color: transparent; }
@media (max-width: 47.99rem) {
  .suggest__text { position: absolute; inline-size: 1px; block-size: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }   /* read out, not shown */
  .suggest__no { inline-size: var(--target); padding-inline: 0; }
  .suggest__no-label { display: none; }                     /* the button keeps its name: aria-label carries the same words */
}
@media (min-width: 48rem) {
  .suggest {
    inset-block-end: max(.75rem, env(safe-area-inset-bottom));
    gap: .9rem;
    inline-size: fit-content;
    max-inline-size: min(100% - 2rem, 54rem);
    margin-inline: auto;
    padding-block: .5rem;
    padding-inline: .7rem .6rem;
    border: 1.5px solid var(--line);
    border-radius: 2.25rem;
    box-shadow: var(--shadow);
  }
  .suggest__text { flex: 1 1 auto; min-inline-size: 9rem; text-wrap: balance; }
  .suggest__actions { flex: none; gap: .5rem; }
  .suggest__yes { flex: none; padding-inline: 1.3rem; }
}
@media (prefers-reduced-motion: reduce) { .suggest { transition: none; } }

/* ──────────────────────────────────────────── 8 Preloader and progress ── */

.preloader { display: none; }
html.js.ft-intro .preloader {
  position: fixed;
  inset: 0;
  z-index: var(--z-preloader);
  display: grid;
  place-items: center;
  background: var(--white);
  animation: ft-pre-out 320ms 1260ms both var(--ease-in-out);
}
html.js.ft-intro .preloader.is-handoff { animation: none; }
html.ft-failsafe .preloader { display: none !important; }
.preloader__mark { inline-size: min(44vmin, 15rem); aspect-ratio: 1; will-change: transform; }
.preloader__svg { inline-size: 100%; block-size: 100%; overflow: visible; }
.preloader__svg [id$="elder-body"], .preloader__svg [id$="elder-head"] {
  transform-box: view-box;
  transform-origin: 30% 80%;   /* lint-ok: the logo is never mirrored, so its figures keep their own sides in RTL */
  animation: ft-pre-elder 900ms cubic-bezier(.2, .9, .25, 1) both;
}
.preloader__svg [id$="youth-body"], .preloader__svg [id$="youth-head"] {
  transform-box: view-box;
  transform-origin: 66% 84%;   /* lint-ok: as above */
  animation: ft-pre-youth 900ms 70ms cubic-bezier(.2, .9, .25, 1) both;
}
.preloader__img { animation: ft-pre-png 700ms var(--ease-out) both; }
/* While the intro runs, the marks it will land on stay out of sight. */
html.js.ft-intro .brand__mark--shared > img,
html.js.ft-intro [data-intro-target] { visibility: hidden; }

.progress { display: none; }
html.js .progress {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  z-index: var(--z-progress);
  display: block;
  block-size: 4px;
  pointer-events: none;
  isolation: isolate;
}
.progress__line { position: absolute; inset-block: 0; inline-size: 50%; will-change: transform; }
.progress__line--start { inset-inline-start: 0; background: var(--orchid); transform: translateX(calc(-100% * var(--dir))); }
.progress__line--end { inset-inline-end: 0; background: var(--mint); mix-blend-mode: multiply; transform: translateX(calc(100% * var(--dir))); }
.progress__line::after {
  content: "";
  position: absolute;
  inset-block-start: -4px;
  inline-size: 12px;
  block-size: 12px;
  border-radius: 50%;
  background: inherit;
}
.progress__line--start::after { inset-inline-end: -6px; }
.progress__line--end::after { inset-inline-start: -6px; }

/* ───────────────────────────────────────────────── 9 Footer and seam ──
   Seam (BRIEF 5.5): where a coloured section meets the one above, its top edge is
   one large arc, never a straight line or a wave.
   <section class="section section--indigo"><div class="seam" aria-hidden="true"></div> … </section>
   The seam takes the section's own --bg and rises into the section above, which
   is given room for it automatically. */

.seam {
  position: absolute;
  inset-inline: 0;
  inset-block-end: calc(100% - 1px);
  block-size: var(--seam-h);
  background: var(--bg);
  border-radius: 50% 50% 0 0 / 100% 100% 0 0;
  pointer-events: none;
}
:is(.section, .page-hero):has(+ :is(.section, .site-footer) > .seam),
main:has(+ .site-footer > .seam) > :last-child { padding-block-end: calc(var(--section-pad) + var(--seam-h)); }

.site-footer {
  position: relative;
  background: var(--bg);
  color: var(--fg);
  padding-block: clamp(2.5rem, 2rem + 3vw, 4.5rem) calc(clamp(1.5rem, 1rem + 2vw, 2.5rem) + var(--dock-h, 0px));   /* --dock-h: room for the language suggestion bar while it is there */
}
.site-footer a { color: var(--white); text-decoration: none; }
.site-footer__inner { display: grid; gap: clamp(2.5rem, 2rem + 3vw, 4.5rem); }
.site-footer__lead {
  display: grid;
  gap: 1.5rem 3rem;
  align-items: center;
}
.site-footer__brand { display: flex; align-items: center; gap: 1.1rem; }
.site-footer__mark {
  position: relative;
  flex: none;
  display: block;
  inline-size: 5.5rem;
  block-size: 5.5rem;
  border-radius: 50%;
  background: var(--white);
}
.site-footer__mark > img { position: absolute; inset: 11%; inline-size: 78%; block-size: 78%; object-fit: contain; }
.site-footer__name { display: grid; row-gap: .15rem; max-inline-size: none; color: var(--white); }
.site-footer__name > [lang^="zh"] { font-size: 1.875rem; font-weight: 700; line-height: 1.15; letter-spacing: .06em; }   /* lint-ok: brand glyphs */
.site-footer__name > [lang="en"] { font-size: 1.25rem; font-weight: 600; line-height: 1.1; }
.site-footer__desc { max-inline-size: 26em; color: var(--white); font-size: var(--fs-sub); font-weight: var(--fw-medium); line-height: var(--lh-sub); text-wrap: balance; }
.backtotop {
  display: inline-flex;
  align-items: center;
  gap: .9rem;
  min-block-size: var(--target);
  justify-self: start;
  font-weight: var(--fw-strong);
  line-height: var(--lh-ui);
}
.backtotop .icon-circle { transition: translate var(--dur-3) var(--ease-out); }
@media (hover: hover) and (pointer: fine) {
  .backtotop:hover .icon-circle { translate: 0 -4px; }
}
@media (min-width: 56rem) {
  .site-footer__lead { grid-template-columns: auto minmax(0, 1fr) auto; }
  .backtotop { justify-self: end; }
}

.site-footer__grid { display: grid; gap: 2.5rem 3rem; grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr)); }
.site-footer__label {
  margin-block-end: .75rem;
  color: var(--mint);
  font-family: var(--font-text);
  font-size: var(--fs-small);
  font-weight: var(--fw-strong);
  line-height: var(--lh-ui);
  letter-spacing: var(--tracking-ui);
  text-wrap: balance;
}
.site-footer__links { display: grid; }
.site-footer__links a, .site-footer__reach a, .site-footer__social a, .site-footer__base a {
  display: inline-flex;
  align-items: center;
  gap: .7rem;
  min-inline-size: var(--target);
  min-block-size: var(--target);
  line-height: var(--lh-ui);
}
.site-footer__links a[aria-current="page"] { font-weight: var(--fw-strong); }
.site-footer__address { max-inline-size: 18em; color: var(--white); line-height: var(--lh-text); }
.site-footer__reach { display: grid; gap: .25rem; margin-block-start: 1rem; }
.site-footer__reach .icon { color: var(--mint); }
.site-footer__social { display: grid; gap: .35rem; }
.site-footer__social .icon-circle { color: var(--mint); }
.site-footer__facts { display: grid; gap: .6rem; color: var(--on-indigo-muted); }
.site-footer__facts li { max-inline-size: 22em; line-height: var(--lh-text); }
@media (hover: hover) and (pointer: fine) {
  .site-footer__links a:hover, .site-footer__reach a:hover span, .site-footer__social a:hover > span:nth-child(2), .site-footer__base a:hover {
    text-decoration: underline;
    text-decoration-thickness: .1em;
    text-underline-offset: var(--underline-offset);
    text-decoration-color: var(--mint);
  }
}

.site-footer__langlist { display: flex; flex-wrap: wrap; gap: .5rem; }
.site-footer__langlist a {
  display: inline-flex;
  align-items: center;
  min-block-size: var(--target);
  padding-inline: 1rem;
  border: 1.5px solid var(--white-a24);
  border-radius: var(--r-full);
  font-size: 1rem;
  line-height: 1.4;
  transition: background-color var(--dur-2) ease, border-color var(--dur-2) ease;
}
.site-footer__langlist a[aria-current="true"] { border-color: var(--mint); background: var(--mint); color: var(--indigo); font-weight: var(--fw-strong); }
@media (hover: hover) and (pointer: fine) {
  .site-footer__langlist a:not([aria-current="true"]):hover { background: var(--white-a14); border-color: var(--white-a40); }
}
.site-footer__langs:focus { outline: none; }

.site-footer__notes { display: grid; gap: .6rem; }
.site-footer__notes li {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: .9rem;
  padding: .9rem 1.1rem;
  border-radius: var(--r-row);
  background: var(--white-a08);
  color: var(--on-indigo-muted);
  font-size: var(--fs-small);
  line-height: var(--lh-text);
}
.site-footer__notes li::before { content: ""; inline-size: .6rem; block-size: .6rem; margin-block-start: calc((1lh - .6rem) / 2); border-radius: 50%; background: var(--mint); }
.site-footer__notes li:nth-child(even)::before { background: var(--orchid); }
.site-footer__base {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: .5rem 2rem;
  padding-block-start: 1.25rem;
  border-block-start: 1px solid var(--white-a24);
  color: var(--on-indigo-muted);
  font-size: var(--fs-small);
}
.site-footer__base p { max-inline-size: none; }

/* ───────────────────────────────────────────── 10 Panels and dot rows ──
   Panel: a large flat colour field, 28px corners (20px on phones).
   <div class="panel panel--orchid"> … </div>     orchid · mint · indigo · mist-orchid · mist-mint · mist-indigo
   Vary panels by role. Do not chop a section into rows of identical cards. */

.panel {
  position: relative;
  padding: clamp(1.5rem, 1rem + 2.6vw, 3.25rem);
  border-radius: var(--r-panel);
  background: var(--bg);
  color: var(--fg);
}
.panel--snug { padding: clamp(1.25rem, 1rem + 1.2vw, 2rem); }
.panel > * + * { margin-block-start: var(--flow, 1rem); }
.panel > .dot-rows, .panel > .cluster, .panel > .btn { margin-block-start: 1.5rem; }

/* Dot rows: every list is a stack of tinted rows, each starting with a coloured dot.
   <ul class="dot-rows" role="list"><li>Learn the phone with a companion</li> … </ul>
   Rich rows: <li><strong>Title</strong><span>One line about it.</span></li> with class dot-rows--rich */
.dot-rows { display: grid; gap: .6rem; }
.dot-rows > li {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  column-gap: .9rem;
  padding: .9rem 1.15rem;
  border-radius: var(--r-row);
  background: var(--row);
  max-inline-size: none;
}
.dot-rows > li::before {
  content: "";
  inline-size: .7rem;
  block-size: .7rem;
  margin-block-start: calc((1lh - .7rem) / 2);
  border-radius: 50%;
  background: var(--dot-a);
}
.dot-rows > li:nth-child(even)::before { background: var(--dot-b); }
.dot-rows--rich > li > * { grid-column: 2; }
.dot-rows--rich > li > :first-child { color: var(--heading); font-weight: var(--fw-strong); }
.dot-rows--rich > li > :not(:first-child) { color: var(--fg-muted); }
.dot-rows--plain > li { padding-inline: 0; background: none; }

/* ──────────────────────────────────── 11 Photo frames and figures ──
   Photographs live in circles and embrace shapes, never in small-radius rectangles.
   <figure class="figure">
     <div class="frame frame--circle" data-reveal="iris">{{ picture('macau-retina-laptop', { sizes: '(min-width: 56rem) 40vw, 90vw' }) }}</div>
     <figcaption>{{ t('photo.macau-retina-laptop.caption') }}</figcaption>
   </figure>
   Shapes: frame--circle · frame--embrace-start (square corner at the bottom on the start side) ·
   frame--embrace-end (square corner at the bottom on the end side). Same convention as the art
   producer's clip paths in src/img/art/embrace-clips.svg; both follow the writing direction.
   Ratio: --ar on the frame (default 1 for the circle, 4/5 otherwise). */

.frame {
  position: relative;
  aspect-ratio: var(--ar, 4 / 5);
  overflow: hidden;
  background: var(--row);
  isolation: isolate;
}
.frame > picture, .frame > img { display: block; inline-size: 100%; block-size: 100%; }
.frame img {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  object-position: var(--focal-x, 50%) var(--focal-y, 50%);
  background: var(--ph, transparent);
}
.frame--circle { --ar: 1; border-radius: 50%; }
.frame--embrace-start { border-radius: var(--r-full); border-end-start-radius: 0; }
.frame--embrace-end { border-radius: var(--r-full); border-end-end-radius: 0; }
.frame--landscape { --ar: 4 / 3; }
.frame--wide { --ar: 3 / 2; }
.frame--square { --ar: 1; }

.figure { display: grid; gap: .9rem; }
.figure > figcaption {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: .7rem;
  max-inline-size: 30em;
  color: var(--fg-muted);
  font-size: var(--fs-small);
  line-height: max(1.5, calc(var(--lh-text) - .15));   /* 1.5 in Latin and Cyrillic; taller where the script needs it (Thai, Devanagari, Gurmukhi, Urdu, Chinese) */
}
.figure > figcaption::before { content: ""; inline-size: .55rem; block-size: .55rem; margin-block-start: calc((1lh - .55rem) / 2); border-radius: 50%; background: var(--dot-a); }
.figure--center > figcaption { justify-self: center; }

/* Two photographs that lean into each other like the two figures of the logo.
   <div class="frame-pair" data-reveal="embrace-pair">
     <figure class="figure"><div class="frame frame--embrace-end">…</div><figcaption>…</figcaption></figure>
     <figure class="figure"><div class="frame frame--embrace-start">…</div><figcaption>…</figcaption></figure>
   </div> */
.frame-pair { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); align-items: start; isolation: isolate; }
.frame-pair > :first-child { grid-column: 1 / span 8; grid-row: 1; }
.frame-pair > :last-child { grid-column: 7 / -1; grid-row: 1; margin-block-start: 34%; z-index: 1; }
.frame-pair > :last-child > .frame { box-shadow: 0 0 0 clamp(6px, 1vw, 12px) var(--bg); }
.frame-pair > :first-child > figcaption { max-inline-size: 62%; }

/* ──────────────────────────── 12 Ghost numerals, stats and steps ──
   Ghost numeral: very large, low contrast, behind the content. ONLY on real sequences and timeline years.
   <div class="has-ghost"><span class="ghost-num" aria-hidden="true">2021</span> … </div> */

.has-ghost { position: relative; isolation: isolate; }
.ghost-num {
  position: absolute;
  inset-block-start: var(--ghost-y, 0em);
  inset-inline-start: var(--ghost-x, -.03em);
  z-index: -1;
  color: var(--ghost);
  font-family: var(--font-latin);
  font-size: var(--fs-ghost);
  font-weight: 700;
  line-height: .8;
  letter-spacing: -0.04em;   /* lint-ok: Western digits only */
  font-variant-numeric: lining-nums;
  white-space: nowrap;
  pointer-events: none;
  user-select: none;
}
.ghost-num--end { inset-inline: auto var(--ghost-x, -.03em); }
.panel.has-ghost { overflow: clip; }
.panel.has-ghost > .ghost-num { --ghost-y: .14em; --ghost-x: .12em; }

/* Stat: a counted number with its label. The final value is in the HTML; core.js counts up to it.
   <div class="stat"><p class="stat__num"><span data-count>225</span></p><p class="stat__label">people screened in Macau in one day</p></div>
   "20+" is written <span data-count>20</span>+ */
.stat { display: grid; gap: .35rem; align-content: start; }
.stat__num {
  max-inline-size: none;
  color: var(--stat, var(--heading));
  font-family: var(--font-latin);
  font-size: var(--fs-stat);
  font-weight: 700;
  line-height: .95;
  letter-spacing: -0.035em;   /* lint-ok: Western digits only */
  font-variant-numeric: tabular-nums lining-nums;
  white-space: nowrap;
  direction: ltr;               /* "20+" stays "20+" in Urdu: Western digits, the plus after them */
  unicode-bidi: isolate;
  text-align: -webkit-match-parent;   /* Chromium knows only the prefixed keyword; Safari and Firefox take the next line */
  text-align: match-parent;
}
.stat__label { max-inline-size: 16em; color: var(--fg-muted); font-size: var(--fs-body); line-height: 1.45; text-wrap: balance; }
.stat--orchid { --stat: var(--orchid-deep); }
.stat--mint { --stat: var(--mint-deep); }
.section--indigo .stat--mint, .panel--indigo .stat--mint { --stat: var(--mint); }
.section--indigo .stat--orchid, .panel--indigo .stat--orchid { --stat: var(--white); }
.stat--row { grid-template-columns: auto minmax(0, 1fr); align-items: baseline; column-gap: 1rem; }
.stat--sm .stat__num { font-size: clamp(2.5rem, 2rem + 2.2vw, 3.75rem); }

/* Steps: for a REAL sequence only (the four screening steps, the four "how we come to you" steps).
   <ol class="steps" role="list">
     <li class="step"><p class="step__label">Step 1</p><h3 class="step__title">Sit at the camera</h3><p>…</p></li> …
   </ol>
   The numeral is drawn by a CSS counter (always Western digits); a line with a stop joins the steps. */
.steps {
  counter-reset: step;
  display: grid;
  gap: clamp(2rem, 4vw, 3rem) clamp(1.5rem, 3vw, 2.5rem);
  grid-template-columns: repeat(auto-fit, minmax(min(14.5rem, 100%), 1fr));
}
.step {
  counter-increment: step;
  position: relative;
  display: grid;
  align-content: start;
  gap: .6rem;
  padding-block-start: 3.4rem;
  border-block-start: 3px solid var(--rule);
  isolation: isolate;
}
.step::before {
  content: counter(step, decimal);
  position: absolute;
  inset-block-start: .35rem;
  inset-inline-end: 0;
  z-index: -1;
  color: var(--ghost);
  font-family: var(--font-latin);
  font-size: clamp(5.5rem, 4.5rem + 4vw, 8.5rem);
  font-weight: 700;
  line-height: .85;
  letter-spacing: -0.04em;   /* lint-ok: Western digits only */
  pointer-events: none;
}
.step::after {
  content: "";
  position: absolute;
  inset-block-start: -.6rem;
  inset-inline-start: 0;
  inline-size: 1.1rem;
  block-size: 1.1rem;
  border: 3px solid var(--bg);
  border-radius: 50%;
  background: var(--dot-a);
  box-sizing: content-box;
  translate: 0 -3px;
}
.step:nth-child(even)::after { background: var(--dot-b); }
.step__label { color: var(--fg-muted); font-size: var(--fs-small); font-weight: var(--fw-strong); line-height: var(--lh-ui); }
.step__title { font-size: var(--fs-sub); }
.step > p:not(.step__label) { color: var(--fg-muted); }

/* ───────────────────────────── 13 FAQ, tabs and segmented control ──
   FAQ: native <details>, so it works without JavaScript; core.js animates the height.
   <div class="faq">
     <details class="faq__item">
       <summary class="faq__q"><span>Who are you?</span><span class="faq__mark" aria-hidden="true">{{ icon('plus') }}</span></summary>
       <div class="faq__a"><p>…</p></div>
     </details> …
   </div> */

.faq { display: grid; gap: .6rem; }
.faq__item { border-radius: var(--r-row); background: var(--row); }
.faq__q {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 1rem;
  min-block-size: 4.25rem;
  padding: .9rem 1rem .9rem 1.4rem;
  border-radius: var(--r-row);
  color: var(--heading);
  font-family: var(--font-display);
  font-size: var(--fs-lead);
  font-weight: var(--fw-sub);
  line-height: var(--lh-sub);
}
.faq__mark {
  display: grid;
  place-items: center;
  inline-size: 2.75rem;
  block-size: 2.75rem;
  border-radius: 50%;
  background: var(--bg);
  color: var(--heading);
  transition: rotate var(--dur-3) var(--ease-out), background-color var(--dur-2) ease, color var(--dur-2) ease;
}
.faq__item[open] > .faq__q > .faq__mark, .faq__item.is-opening > .faq__q > .faq__mark { rotate: 45deg; background: var(--heading); color: var(--bg); }
.faq__item.is-closing > .faq__q > .faq__mark { rotate: 0deg; background: var(--bg); color: var(--heading); }
.faq__a { padding-inline: 1.4rem; overflow: hidden; }
.faq__a::after { content: ""; display: block; block-size: 1.35rem; }
.faq__a > * + * { margin-block-start: .8em; }

/* Segmented control and tabs.
   <div class="tabs" data-tabs>
     <div class="segmented" role="tablist" aria-label="Who is it for">
       <button class="segmented__btn" role="tab" id="t-a" aria-controls="p-a" aria-selected="true">Seniors</button>
       <button class="segmented__btn" role="tab" id="t-b" aria-controls="p-b" aria-selected="false">Students</button>
     </div>
     <div class="tabs__panel" role="tabpanel" id="p-a" aria-labelledby="t-a">…</div>
     <div class="tabs__panel" role="tabpanel" id="p-b" aria-labelledby="t-b">…</div>
   </div>
   Without JavaScript the tab list is hidden and every panel shows, one under the other. */
.segmented {
  position: relative;
  display: inline-flex;
  flex-wrap: wrap;
  gap: 2px;
  max-inline-size: 100%;
  padding: 4px;
  border-radius: 1.9rem;
  background: var(--row);
  isolation: isolate;
}
.segmented__btn {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-block-size: var(--target);
  padding-inline: 1.25rem;
  border-radius: var(--r-full);
  color: var(--heading);
  font-weight: var(--fw-medium);
  line-height: var(--lh-ui);
  transition: color var(--dur-2) ease, scale var(--dur-1) var(--ease-out);
}
.segmented__btn:active { scale: .97; }
.segmented__btn:is([aria-selected="true"], [aria-pressed="true"]) { color: var(--btn-fg); font-weight: var(--fw-strong); }
.segmented:not(.has-thumb) .segmented__btn:is([aria-selected="true"], [aria-pressed="true"]) { background: var(--btn-bg); }
.segmented__thumb {
  position: absolute;
  inset-block-start: 0;
  left: 0;   /* lint-ok: positioned by JavaScript from measured physical offsets */
  z-index: 0;
  border-radius: var(--r-full);
  background: var(--btn-bg);
  transition: transform var(--dur-3) var(--ease-out), inline-size var(--dur-3) var(--ease-out);
  pointer-events: none;
}
html:not(.js) [role="tablist"] { display: none; }
.tabs__panel { margin-block-start: 1.5rem; }
.tabs__panel:focus-visible { outline-offset: 6px; }

/* ──────────────────────────────────────── 14 Contact panel, monogram ──
   Monogram: people are initials or characters in a circle. No portraits, no stock faces.
   <span class="monogram monogram--orchid" aria-hidden="true" data-reveal="pop">AL</span>
   Three or more characters: add monogram--long. */

.monogram {
  --size: 5.5rem;
  flex: none;
  display: inline-grid;
  place-items: center;
  inline-size: var(--size);
  block-size: var(--size);
  border-radius: 50%;
  background: var(--orchid);
  color: var(--white);
  font-family: var(--font-display);
  font-size: calc(var(--size) * .36);
  font-weight: var(--fw-display);
  line-height: 1;
  letter-spacing: 0em;
  white-space: nowrap;
}
.monogram--long { font-size: calc(var(--size) * .25); }
.monogram--lg { --size: 7.5rem; }
.monogram--sm { --size: 4rem; }
.monogram--mint { background: var(--mint); color: var(--indigo); }
.monogram--indigo { background: var(--indigo); color: var(--white); }
.monogram--white { background: var(--white); color: var(--indigo); }

/* Contact panel: who, about what, and two ways to reach them.
   <article class="contact panel panel--mist-orchid">
     <span class="monogram monogram--orchid" aria-hidden="true">IL</span>
     <div class="contact__who"><h3 class="contact__topic">General enquiries</h3><p class="contact__person">Ms Iris Leung</p></div>
     <div class="contact__actions">
       <a class="btn btn--whatsapp" href="…">{{ icon('whatsapp') }}WhatsApp Ms Leung</a>
       <a class="btn btn--secondary" href="mailto:…">{{ icon('mail') }}Email Ms Leung</a>
     </div>
   </article> */
.contact { display: grid; gap: 1.25rem; align-content: start; }
.contact > * + * { margin-block-start: 0; }
.contact__who { display: grid; gap: .3rem; }
.contact__topic { font-size: var(--fs-sub); }
.contact__person { color: var(--fg); font-size: var(--fs-lead); font-weight: var(--fw-medium); line-height: var(--lh-sub); }
.contact__note { color: var(--fg-muted); }
.contact__actions { display: flex; flex-wrap: wrap; gap: .6rem; margin-block-start: .25rem; }
.contact__actions > .btn { flex: 1 1 auto; }
@media (min-width: 62rem) {
  .contact--row { grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: 1.75rem; }
  .contact--row .contact__actions { flex-wrap: nowrap; margin-block-start: 0; }
}

/* ─────────────────────────────────────────── 15 Marquee and carousel ──
   Partner names as a line of large type whose speed follows the scroll (core.js).
   Without JavaScript, or under reduced motion, it is simply a wrapping list.
   <div class="marquee" data-marquee>
     <ul class="marquee__list" role="list"><li>Lions Clubs International District 303</li> … </ul>
     <div class="container">
       <button class="round-btn marquee__toggle" type="button" data-motion-toggle
               data-label-pause="{{ t('a11y.motion.pause') }}" data-label-play="{{ t('a11y.motion.play') }}"
               aria-label="{{ t('a11y.motion.pause') }}">{{ icon('pause', { class: 'icon--pause' }) }}{{ icon('play', { class: 'icon--play' }) }}</button>
     </div>
   </div>
   Place it outside .container: the moving line runs edge to edge, the still list lines up with the container. */

.marquee { position: relative; }
.marquee__viewport { overflow: clip; padding-block: .25em; }
.marquee__list { display: flex; flex-wrap: wrap; gap: .75rem 2.25rem; inline-size: min(100% - 2 * var(--gutter), var(--container)); margin-inline: auto; }
.marquee__list > li {
  display: inline-flex;
  align-items: center;
  gap: 2.25rem;
  max-inline-size: none;
  color: var(--heading);
  font-family: var(--font-display);
  font-size: var(--fs-sub);
  font-weight: var(--fw-sub);
  line-height: var(--lh-sub);
}
.marquee__list > li::before { content: ""; flex: none; inline-size: .7rem; block-size: .7rem; border-radius: 50%; background: var(--dot-a); }
.marquee__list > li:nth-child(even)::before { background: var(--dot-b); }
.marquee.is-running .marquee__track { display: flex; inline-size: max-content; will-change: transform; }
.marquee.is-running .marquee__list { flex: none; flex-wrap: nowrap; inline-size: auto; margin-inline: 0; white-space: nowrap; padding-inline-end: 2.25rem; }
.marquee.is-running .marquee__list > li { font-size: var(--fs-section); font-weight: var(--fw-display); line-height: var(--lh-display); }
.marquee__toggle { display: none; margin-block-start: 1.25rem; }
.marquee.is-running .marquee__toggle { display: inline-grid; }
.marquee__toggle[aria-pressed="true"] { background: var(--btn-line); color: var(--bg); }

/* Carousel: a snapping rail with previous / next, usable by swipe, wheel, keyboard and buttons.
   Add data-pin and, on desktop with a fine pointer, the rail is pinned and moved by the vertical scroll instead.
   <div class="carousel" data-carousel role="group" aria-roledescription="{{ t('a11y.carousel.role') }}" aria-label="…">
     <div class="carousel__viewport" tabindex="0">
       <ul class="carousel__rail" role="list">
         <li class="carousel__item" aria-roledescription="{{ t('a11y.carousel.slide') }}">…</li> …
       </ul>
     </div>
     <div class="carousel__nav container">
       <button class="round-btn" type="button" data-carousel-prev aria-label="{{ t('a11y.carousel.prev') }}">{{ icon('chevron', { class: 'icon--back' }) }}</button>
       <button class="round-btn" type="button" data-carousel-next aria-label="{{ t('a11y.carousel.next') }}">{{ icon('chevron') }}</button>
     </div>
   </div>
   Place it outside .container: the rail lines its first item up with the container edge by itself. */
.carousel { --item: min(82vw, 24rem); --gap: clamp(1rem, 2vw, 1.75rem); --edge: max(var(--gutter), (100% - var(--container)) / 2); }
.carousel__viewport {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: var(--edge);
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.carousel__viewport::-webkit-scrollbar { display: none; }
.carousel__viewport:focus-visible { outline-offset: -3px; }
.carousel__rail { display: flex; gap: var(--gap); inline-size: max-content; padding-inline: var(--edge); }
.carousel__item { flex: none; inline-size: var(--item); max-inline-size: none; scroll-snap-align: start; }
.carousel__nav { display: flex; gap: .6rem; margin-block-start: 1.75rem; }
html:not(.js) .carousel__nav { display: none; }
.carousel.is-pinned .carousel__viewport { overflow-x: clip; overflow-y: visible; scroll-snap-type: none; }   /* the rail runs out past the viewport's ends; it must not widen the page */
.carousel.is-pinned .carousel__rail { will-change: transform; }

/* ───────────────────────────────── 16 Page hero, bridge and doors ──
   Page hero for the inner pages: a tinted field, the page title, one lead, optional actions,
   and on the end side either the bridge (default) or a photograph in a frame.
   <section class="page-hero section--mist-orchid">
     <div class="container page-hero__inner">
       <div class="page-hero__text">
         <h1 class="h-page" data-reveal="lines">{{ th('about.hero.title') }}</h1>
         <p class="lead" data-reveal="fade">{{ t('about.hero.lead') }}</p>
         <div class="cluster"> buttons </div>
       </div>
       <div class="page-hero__art">{{> bridge}}</div>
     </div>
   </section>
   Its lower edge is the seam arc into the white page (set --hero-next if the next section is not white). */

.page-hero {
  position: relative;
  padding-block: clamp(2.5rem, 1.6rem + 4.4vw, 6rem) calc(clamp(2.5rem, 1.6rem + 4.4vw, 6rem) + var(--seam-h));
  background: var(--bg);
  color: var(--fg);
  overflow: clip;
}
.page-hero::after {
  content: "";
  position: absolute;
  inset-inline: 0;
  inset-block-end: -1px;
  block-size: var(--seam-h);
  background: var(--hero-next, var(--white));
  border-radius: 50% 50% 0 0 / 100% 100% 0 0;
}
.page-hero__inner { position: relative; z-index: 1; display: grid; gap: 2rem clamp(2rem, 5vw, 5rem); align-items: center; }
.page-hero__text { display: grid; gap: 1.25rem; justify-items: start; }
.page-hero__text > .cluster { margin-block-start: .5rem; }
.page-hero__art { max-inline-size: 26rem; inline-size: 100%; }
@media (min-width: 56rem) {
  .page-hero__inner { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); }
  .page-hero__art { justify-self: end; }
}
@media (max-width: 55.99rem) {
  .page-hero__art > .bridge { inline-size: min(60%, 14rem); }
}

/* The bridge: an arc from an orchid dot to a mint dot.   {{> bridge}}  {{> bridge cls="bridge--sm"}} */
.bridge { display: block; inline-size: 100%; block-size: auto; overflow: visible; }
.bridge__arc { fill: none; stroke: var(--heading); stroke-width: 5; stroke-linecap: round; vector-effect: non-scaling-stroke; }
.bridge__dot--orchid { fill: var(--orchid); }
.bridge__dot--mint { fill: var(--mint); }
.bridge--sm { inline-size: 7.5rem; }
.bridge--sm .bridge__arc { stroke-width: 3; }
.bridge__dot { transform-box: fill-box; transform-origin: center; }

/* Doors: three ways in. They widen on hover and focus (wide screens, fine pointers); stacked on phones.
   <ul class="doors" role="list">
     <li><a class="door panel--orchid" href="…">
       <span class="icon-circle icon-circle--lg icon-circle--white">{{ icon('aud-partners') }}</span>
       <h3 class="door__title">Invite us</h3>
       <p>For centres, schools, clubs and housing estates.</p>
     </a></li> …
   </ul> */
.doors { display: flex; flex-direction: column; gap: 1rem; }
.doors > li { flex: 1 1 0; min-inline-size: 0; display: flex; }
.door {
  flex: 1 1 auto;
  display: grid;
  justify-items: center;
  align-content: start;
  gap: .85rem;
  padding: clamp(2.25rem, 6vw, 3.5rem) 1.5rem clamp(1.75rem, 4vw, 2.5rem);
  border-radius: var(--r-panel);
  border-start-start-radius: min(11rem, 42vw);
  border-start-end-radius: min(11rem, 42vw);
  background: var(--bg);
  color: var(--fg);
  text-align: center;
  text-decoration: none;
  transition: scale var(--dur-1) var(--ease-out);
}
.door:active { scale: .985; }
.door:focus-visible { outline-offset: 5px; }
.door__title { font-size: var(--fs-sub); }
.door > p { max-inline-size: 15em; }
.door > .icon-circle { transition: scale var(--dur-4) var(--ease-pop); }
@media (min-width: 52rem) {
  .doors { flex-direction: row; align-items: stretch; gap: clamp(.75rem, 1.6vw, 1.5rem); }
  .door { padding-block-start: clamp(3.5rem, 7vw, 5.5rem); border-start-start-radius: var(--r-full); border-start-end-radius: var(--r-full); min-block-size: clamp(24rem, 36vw, 30rem); }
}
@media (min-width: 52rem) and (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .doors > li { transition: flex-grow var(--dur-5) var(--ease-out); }
  .doors > li:is(:hover, :focus-within) { flex-grow: 1.42; }
  .doors > li:is(:hover, :focus-within) .icon-circle { scale: 1.12; }
}

/* ───────────────────────────────────────────── 17 Motion helpers ──
   Markup written by core.js while a heading is revealed, and the pause button's icon swap. */

.ft-line, .ft-word { clip-path: inset(calc(var(--mask-pad) * -1) -100vw calc(var(--mask-pad) * -1.1) -100vw); }
.ft-line { display: block; }
.ft-word { display: inline-block; vertical-align: top; }
.ft-line__in, .ft-word__in { display: block; will-change: transform; }
.ft-word__in { display: inline-block; }
[data-count] { font-variant-numeric: tabular-nums lining-nums; }
[data-parallax], [data-magnetic] { will-change: transform; }
[data-motion-toggle] .icon--play, [data-motion-toggle][aria-pressed="true"] .icon--pause { display: none; }
[data-motion-toggle][aria-pressed="true"] .icon--play { display: block; }

}   /* end @layer components */

/* Keyframes live outside the layer (they are global either way). */
@keyframes ft-arc-draw {
  from { clip-path: inset(-2px 101% -2px 0); }
  to { clip-path: inset(-2px 0 -2px 0); }
}
@keyframes ft-pre-elder {
  0% { transform: translateX(-200px) rotate(-15deg); opacity: 0; }
  16% { opacity: 1; }
  64% { transform: translateX(8px) rotate(1.5deg); }
  100% { transform: none; opacity: 1; }
}
@keyframes ft-pre-youth {
  0% { transform: translateX(200px) rotate(15deg); opacity: 0; }
  16% { opacity: 1; }
  64% { transform: translateX(-8px) rotate(-1.5deg); }
  100% { transform: none; opacity: 1; }
}
@keyframes ft-pre-png {
  from { opacity: 0; scale: .94; }
  to { opacity: 1; scale: 1; }
}
@keyframes ft-pre-out {
  to { opacity: 0; visibility: hidden; }
}
