/* ═══════════════════════════════════════════════════════════════════════════
   Fun-Tastic 悠樂長 · base.css
   Tokens, reset, typography defaults, layout primitives, section rhythm, focus.

   Cascade layers, lowest to highest:
     reset → base → layout → components → lang → utilities
   Page CSS (src/css/pages/<page>.css) is NOT layered, so it always wins over the
   design system without !important and without specificity games.

   Rules of the house (BRIEF 5): logical properties only (no left/right), no
   uppercase, no gradients, no glass, no glow, nothing under 16px, colours from
   tokens only. tools/build.mjs lints every CSS file for these.
   ═══════════════════════════════════════════════════════════════════════════ */

@layer reset, base, layout, components, lang, utilities;

/* ─────────────────────────────────────────────────────────────── tokens ──
   In the base layer on purpose: tones (components layer) and languages (lang layer)
   must be able to re-point these on the same element. An unlayered :root would beat them. */

@layer base {

:root {
  /* Colour (BRIEF 5.2). The palette is the logo's two colours and their product. */
  --white: #FFFFFF;        /* page ground */
  --indigo: #122F6C;       /* orchid × mint: headings, dark sections, primary button */
  --ink: #0E1B3F;          /* body text */
  --orchid: #BE3AAA;       /* seniors. White text on it passes AA */
  --orchid-deep: #9A2C8A;  /* orchid for text and hover on white */
  --orchid-mist: #FBEFF8;  /* tint ground */
  --mint: #18CDA2;         /* young people. Indigo text on it, never white */
  --mint-deep: #0A7D63;    /* mint for text and icons on white */
  --mint-mist: #E6FAF4;    /* tint ground */
  --indigo-mist: #EEF2FB;  /* tint ground */
  --line: #D9DFEE;         /* hairlines and borders */
  --slate: #4A5676;        /* secondary text (AA on white and on the mists) */

  /* Tints of the same hues, for text and hover states on colour */
  --on-indigo-muted: #C9D5F2;
  --indigo-hover: #1F4391;
  --mint-hover: #5CDCBE;
  --white-a08: rgb(255 255 255 / .08);
  --white-a14: rgb(255 255 255 / .14);
  --white-a24: rgb(255 255 255 / .24);
  --white-a40: rgb(255 255 255 / .4);
  --indigo-a12: rgb(18 47 108 / .12);

  /* The one shadow: soft, indigo-tinted, only for things that float */
  --shadow: 0 18px 40px -18px rgb(18 47 108 / .25);

  /* Semantic colours. Tones (.section--indigo, .panel--mint …) re-point these;
     components read these and never the raw palette, so they work on any ground. */
  --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);         /* tinted rows, hovers, chips */
  --dot-a: var(--orchid);
  --dot-b: var(--mint);
  --ghost: var(--indigo-mist);       /* ghost numerals */
  --btn-bg: var(--indigo);
  --btn-fg: var(--white);
  --btn-bg-hover: var(--indigo-hover);
  --btn-line: var(--indigo);         /* secondary (outlined) button */
  --wa-bg: var(--mint);              /* WhatsApp action */
  --wa-fg: var(--indigo);
  --wa-bg-hover: var(--mint-hover);

  /* Type families. lang.css swaps these per language (generated from languages.json). */
  --font-latin: "Lexend", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-display: var(--font-latin);
  --font-text: var(--font-latin);

  /* Weights */
  --fw-text: 400;
  --fw-medium: 500;
  --fw-strong: 600;
  --fw-sub: 600;
  --fw-display: 700;

  /* Per-script metrics. Defaults are Latin; lang.css sets the rest (BRIEF 5.3). */
  --lh-display: 1.08;
  --lh-sub: 1.22;
  --lh-text: 1.65;
  --lh-ui: 1.25;                     /* buttons, nav, labels: one or two lines */
  --tracking-display: -0.022em;
  --tracking-sub: -0.012em;
  --tracking-ui: 0em;
  --display-scale: 1;
  --text-scale: 1;
  --measure: 36em;                   /* about 66 characters of Lexend */
  --mask-pad: .22em;                 /* how far a line mask reaches past its line box */
  --underline-offset: .22em;
  --hyphens-display: auto;           /* long German, Russian and Filipino words in headings */
  --hyphens-text: manual;

  /* Text-size control: the root size steps, and --ts keeps the fluid part in step */
  --ts: 1;

  /* Space */
  --space-3xs: .25rem;
  --space-2xs: .5rem;
  --space-xs: .75rem;
  --space-s: 1rem;
  --space-m: 1.5rem;
  --space-l: 2rem;
  --space-xl: 3rem;
  --space-2xl: 4.5rem;
  --space-3xl: 7rem;
  --gutter: clamp(1rem, 4vw, 3rem);
  --section-pad: clamp(4rem, 2.6rem + 6.2vw, 8.5rem);
  --seam-h: clamp(2.25rem, 6.4vw, 6rem);
  --container: 76rem;
  --container-wide: 92rem;
  --container-narrow: 50rem;

  /* Shape */
  --r-panel: 28px;
  --r-row: 16px;
  --r-full: 999px;

  /* Motion */
  --ease-out: cubic-bezier(.16, 1, .3, 1);
  --ease-in-out: cubic-bezier(.77, 0, .175, 1);
  --ease-pop: cubic-bezier(.34, 1.56, .64, 1);
  --ease-iris: cubic-bezier(.22, .8, .2, 1);
  --dur-1: 140ms;
  --dur-2: 220ms;
  --dur-3: 360ms;
  --dur-4: 560ms;
  --dur-5: 900ms;
  --dir: 1;                          /* 1 in LTR, -1 in RTL: multiply an x translation by it */

  /* Layout facts */
  --header-h: 5.25rem;               /* core.js keeps this equal to the real header height */
  --dock-h: 0px;                     /* core.js: the room the language suggestion bar takes at the bottom of the viewport, while it is there */
  --target: 44px;                    /* minimum touch target */

  /* Stacking */
  --z-header: 50;
  --z-suggest: 55;
  --z-progress: 60;
  --z-preloader: 90;
  --z-skip: 100;
}

/* The fluid type scale. Declared on every element that carries a lang attribute as well,
   so a Hindi or Urdu passage inside an English page gets its own --display-scale. */
:root,
[lang] {
  --fs-hero: calc(clamp(2.6rem, 1.66rem + 4.17vw * var(--ts), 5rem) * var(--display-scale));
  --fs-page: calc(clamp(2.4rem, 1.7rem + 3.1vw * var(--ts), 4.2rem) * var(--display-scale));
  --fs-section: calc(clamp(2rem, 1.45rem + 2.43vw * var(--ts), 3.4rem) * var(--display-scale));
  --fs-sub: calc(clamp(1.35rem, 1.19rem + .7vw * var(--ts), 1.75rem) * var(--display-scale));
  --fs-lead: calc(clamp(1.25rem, 1.15rem + .43vw * var(--ts), 1.5rem) * var(--text-scale));
  --fs-body: calc(1.125rem * var(--text-scale));
  --fs-small: calc(1rem * var(--text-scale));
  --fs-stat: clamp(3.25rem, 2.2rem + 4.6vw * var(--ts), 6.5rem);
  --fs-ghost: clamp(6.5rem, 4rem + 12vw, 14rem);
}

@media (max-width: 40rem) {
  :root { --r-panel: 20px; }
}

}   /* end of the token part of @layer base */

/* Cross-page view transitions: a quick cross-fade, with the header (and its mark) held in place.
   The names are given in components.css (.site-header, .brand__mark--shared). */
@media (prefers-reduced-motion: no-preference) {
  @view-transition { navigation: auto; }
  ::view-transition-old(root),
  ::view-transition-new(root) { animation-duration: .3s; }
  ::view-transition-group(ft-header),
  ::view-transition-group(ft-mark) { animation-duration: .36s; animation-timing-function: cubic-bezier(.16, 1, .3, 1); }
}

/* ──────────────────────────────────────────────────────────────── reset ── */

@layer reset {
  *, *::before, *::after { box-sizing: border-box; }
  * { margin: 0; }
  html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
  body { min-block-size: 100vh; min-block-size: 100svh; }
  img, picture, video, canvas, svg { display: block; max-inline-size: 100%; }
  img, video { block-size: auto; }
  input, button, textarea, select { font: inherit; color: inherit; letter-spacing: inherit; }
  button { padding: 0; border: 0; background: none; cursor: pointer; text-align: inherit; -webkit-tap-highlight-color: transparent; touch-action: manipulation; }
  a { color: inherit; -webkit-tap-highlight-color: transparent; }
  ul[role="list"], ol[role="list"] { padding: 0; list-style: none; }
  h1, h2, h3, h4, h5, h6 { font-size: inherit; font-weight: inherit; }
  address, em, i, cite { font-style: normal; }
  table { border-collapse: collapse; }
  dialog { padding: 0; border: 0; color: inherit; background: none; max-inline-size: none; max-block-size: none; }
  summary { cursor: pointer; list-style: none; }
  summary::-webkit-details-marker { display: none; }
  [hidden] { display: none !important; }
  :target { scroll-margin-block-start: calc(var(--header-h) + 1.5rem); }
}

/* ───────────────────────────────────────────────────────────────── base ── */

@layer base {
  html {
    font-size: 100%;
    color-scheme: light;
    font-synthesis: none;                 /* never a faux bold or a faux italic */
    scroll-padding-block-start: calc(var(--header-h) + 1rem);
    scrollbar-gutter: stable;
    overflow-x: clip;
  }
  html[data-textsize="large"] { font-size: 112.5%; --ts: 1.125; }
  html[data-textsize="larger"] { font-size: 125%; --ts: 1.25; }

  body {
    font-family: var(--font-text);
    font-size: var(--fs-body);
    font-weight: var(--fw-text);
    line-height: var(--lh-text);
    color: var(--fg);
    background: var(--white);
    overflow-x: clip;
    overflow-wrap: break-word;
    hyphens: var(--hyphens-text);
    -webkit-hyphens: var(--hyphens-text);
    text-wrap: pretty;
    accent-color: var(--indigo);
    caret-color: var(--orchid);
  }

  /* Display type: one colour, no accent words, never uppercase. */
  h1, h2, h3, h4, .h-hero, .h-page, .h-section, .h-sub, .display {
    font-family: var(--font-display);
    font-weight: var(--fw-display);
    line-height: var(--lh-display);
    letter-spacing: var(--tracking-display);
    color: var(--heading);
    text-wrap: balance;
    hyphens: var(--hyphens-display);
    -webkit-hyphens: var(--hyphens-display);
    hyphenate-limit-chars: 10 4 4;
    -webkit-hyphenate-limit-before: 4;
    -webkit-hyphenate-limit-after: 4;
    overflow-wrap: anywhere;
    text-rendering: optimizeLegibility;
  }
  h1, .h-hero { font-size: var(--fs-hero); }
  .h-page { font-size: var(--fs-page); }
  h2, .h-section { font-size: var(--fs-section); }
  h3, .h-sub {
    font-size: var(--fs-sub);
    font-weight: var(--fw-sub);
    line-height: var(--lh-sub);
    letter-spacing: var(--tracking-sub);
  }
  h4 {
    font-size: var(--fs-lead);
    font-weight: var(--fw-sub);
    line-height: var(--lh-sub);
    letter-spacing: var(--tracking-sub);
  }

  p { max-inline-size: var(--measure); }
  .lead { font-size: var(--fs-lead); line-height: calc(var(--lh-text) - .1); }
  small, .small { font-size: var(--fs-small); }
  .muted { color: var(--fg-muted); }
  strong, b { font-weight: var(--fw-strong); }
  em, i, cite { font-weight: var(--fw-medium); }

  /* A link inside running text. Components give their links a class and their own look. */
  a:not([class]) {
    color: var(--link);
    text-decoration-line: underline;
    text-decoration-thickness: .08em;
    text-underline-offset: var(--underline-offset);
    transition: text-decoration-thickness var(--dur-1) ease;
  }
  @media (hover: hover) and (pointer: fine) {
    a:not([class]):hover { text-decoration-thickness: .16em; }
  }

  /* Focus: 3px, 3px away, always visible (BRIEF 5.5). --focus changes with the ground. */
  :focus-visible {
    outline: 3px solid var(--focus);
    outline-offset: 3px;
  }
  :focus:not(:focus-visible) { outline: none; }

  ::selection { background: var(--mint); color: var(--ink); }

  .skip-link {
    position: fixed;
    inset-block-start: .75rem;
    inset-inline-start: .75rem;
    z-index: var(--z-skip);
    display: inline-flex;
    align-items: center;
    min-block-size: 3.25rem;
    padding-inline: 1.5rem;
    border-radius: var(--r-full);
    background: var(--indigo);
    color: var(--white);
    font-weight: var(--fw-strong);
    text-decoration: none;
    transform: translateY(-200%);
    transition: transform var(--dur-2) var(--ease-out);
  }
  .skip-link:focus { transform: none; }

  /* Words kept whole in Chinese headings (the th() helper writes these). */
  .w { white-space: nowrap; }
  /* Names kept whole everywhere ("Fun-Tastic", "EyeCare 365", 悠樂長有限公司 …): the build wraps them in
     the body's text. No box of its own, so a name inside a button, a dot row or any other flex or
     grid parent stays part of its sentence; only the line break inside the name is taken away. */
  .nb { display: contents !important; white-space: nowrap !important; }

  /* ── Motion ground rules ──────────────────────────────────────────────────
     Hidden-until-animated exists only when scripts run (html.js), only when the
     visitor has not asked for reduced motion, and never after the failsafe. */
  @media (prefers-reduced-motion: no-preference) {
    html.js:not(.ft-failsafe) [data-reveal]:not(.is-in) { opacity: 0; }
  }
  html.ft-failsafe [data-reveal] {
    opacity: 1 !important;
    visibility: visible !important;
    transform: none !important;
    clip-path: none !important;
  }

  /* Lenis (desktop fine pointers only; core.js decides) */
  html.lenis, html.lenis body { block-size: auto; }
  .lenis:not(.lenis-autoToggle).lenis-stopped { overflow: clip; }
  .lenis [data-lenis-prevent] { overscroll-behavior: contain; }
  .lenis.lenis-smooth iframe { pointer-events: none; }

  /* A locked page behind the menu or the language panel */
  html.is-locked, html.is-locked body { overflow: hidden; }
}

/* ─────────────────────────────────────────────────────────────── layout ── */

@layer layout {
  /* Fluid container: gutters shrink to 16px, width stops at 76rem. */
  .container {
    inline-size: min(100% - 2 * var(--gutter), var(--container));
    margin-inline: auto;
  }
  .container--wide { --container: var(--container-wide); }
  .container--narrow { --container: var(--container-narrow); }

  /* Section rhythm. A tone class paints it (see "tones" in components.css). */
  .section {
    position: relative;
    padding-block: var(--section-pad);
    background: var(--bg);
    color: var(--fg);
  }
  .section--tight { --section-pad: clamp(2.5rem, 2rem + 3vw, 5rem); }
  .section--flush-start { padding-block-start: 0; }
  .section--flush-end { padding-block-end: 0; }
  .section__head { margin-block-end: clamp(2rem, 1.4rem + 2.6vw, 4rem); }
  .section__head > * + * { margin-block-start: var(--space-s); }

  /* Vertical stack with one gap. */
  .stack { display: flex; flex-direction: column; gap: var(--gap, var(--space-m)); }
  .stack--xs { --gap: var(--space-xs); }
  .stack--s { --gap: var(--space-s); }
  .stack--l { --gap: var(--space-l); }
  .stack--xl { --gap: var(--space-xl); }
  .stack > :is(.btn, .tool-btn, .round-btn, .link-arc, .icon-circle, .monogram, .segmented) { align-self: flex-start; }   /* a column must not stretch a button */
  .stack > .btn--block { align-self: stretch; }

  /* Prose flow: space between siblings, more above a heading than below it. */
  .flow > * + * { margin-block-start: var(--flow, 1em); }
  .flow > :is(h2, h3, h4) { margin-block-start: 1.8em; }
  .flow > :is(h2, h3, h4) + * { margin-block-start: .6em; }

  /* Things in a row that wrap: buttons, tags, links. */
  .cluster { display: flex; flex-wrap: wrap; align-items: center; gap: var(--gap, var(--space-s)); }

  /* Even columns that fold by themselves; --min is the narrowest a column may be. */
  .grid-auto {
    display: grid;
    gap: var(--gap, var(--space-l));
    grid-template-columns: repeat(auto-fit, minmax(min(var(--min, 18rem), 100%), 1fr));
  }

  /* Twelve columns for deliberate compositions. Children place themselves with grid-column. */
  .grid {
    display: grid;
    gap: var(--gap, var(--space-l));
    grid-template-columns: repeat(12, minmax(0, 1fr));
  }
  .grid > * { grid-column: 1 / -1; }

  /* Two sides. One column on phones, two from 56rem; ratios by modifier. */
  .split {
    display: grid;
    gap: var(--gap, clamp(2rem, 1rem + 4.5vw, 5.5rem));
    grid-template-columns: minmax(0, 1fr);
    align-items: center;
  }
  .split--top { align-items: start; }
  @media (min-width: 56rem) {
    .split { grid-template-columns: minmax(0, var(--a, 1fr)) minmax(0, var(--b, 1fr)); }
    .split--5-7 { --a: 5fr; --b: 7fr; }
    .split--7-5 { --a: 7fr; --b: 5fr; }
    .split--4-8 { --a: 4fr; --b: 8fr; }
    .split--8-4 { --a: 8fr; --b: 4fr; }
    .split--flip > :first-child { order: 2; }
  }

  .measure { max-inline-size: var(--measure); }
  .measure-none { max-inline-size: none; }
}

/* ──────────────────────────────────────────────────────────── utilities ── */

@layer utilities {
  .visually-hidden {
    position: absolute !important;
    inline-size: 1px !important;
    block-size: 1px !important;
    margin: -1px !important;
    padding: 0 !important;
    border: 0 !important;
    overflow: hidden !important;
    clip-path: inset(50%) !important;
    white-space: nowrap !important;
  }
  .nowrap { white-space: nowrap; }
  .text-center { text-align: center; }
  .text-start { text-align: start; }
  .mx-auto { margin-inline: auto; }
  .ltr { direction: ltr; unicode-bidi: isolate; }   /* phone numbers, emails, URLs inside RTL text */
  .tabular { font-variant-numeric: tabular-nums lining-nums; }
  @media (max-width: 55.99rem) { .wide-only { display: none !important; } }
  @media (min-width: 56rem) { .narrow-only { display: none !important; } }
}

/* ──────────────────────────────────────────────────────────────── print ── */

@media print {
  [data-reveal] { opacity: 1 !important; transform: none !important; clip-path: none !important; visibility: visible !important; }
  .site-header { position: static !important; }
  .progress, .preloader, .suggest, .skip-link, .site-menu, .lang-panel { display: none !important; }
}
