/* base.css — foundation workstream. Figma "Concept v2" (Components 98:316, Course Detail A1 120:1223).
   Uses tokens.css variables. Mobile-first: tablet ≥768, desktop ≥1200.
   Contents: 1 reset · 2 typography · 3 layout/surfaces · 4 eyebrow/section heading · 5 buttons/icons
   6 forms/lead form · 7 cards/lists · 8 decoration · 8b editorial utilities · 9 FAQ (accordion) · 10 marquee · 11 carousel
   12 header · 13 footer · 14 utilities · 15 motion (scroll reveal) · 16 404 · 17 VISUAL SYSTEM v2 (bands, texture,
   display type, marks, colour fields, photo treatments, scroll, hover, header/footer v2, media slots) */

/* ---------- 1. Reset ---------- */
*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;text-size-adjust:100%;scroll-behavior:smooth;scroll-padding-top:calc(var(--header-h) + 16px)}
@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}}
body{margin:0;min-height:100vh;background:var(--c-cream);color:var(--c-ink);font-family:var(--font);font-size:var(--fs-body);line-height:1.6;font-weight:400;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-rendering:optimizeLegibility;overflow-x:clip}
h1,h2,h3,h4,h5,h6,p,figure,blockquote,dl,dd,ul,ol{margin:0}
/* Class-level reset (0,1,1): a component that needs list padding must qualify its selector, e.g. ol.legal-toc__list.
   (Lowering this would switch on padding that other components declare but have never shown, e.g. .hp-stats.) */
ul[class],ol[class]{list-style:none;padding:0}
img,picture,video,canvas,svg{display:block;max-width:100%}
img,video{height:auto}
img{object-fit:cover}
input,button,textarea,select{font:inherit;color:inherit;letter-spacing:inherit}
button{cursor:pointer;background:none;border:0;padding:0}
button:disabled{cursor:not-allowed}
a{color:inherit;text-decoration-thickness:1px;text-underline-offset:.2em}
address{font-style:normal}
table{border-collapse:collapse}
[hidden]{display:none!important}
::selection{background:var(--c-lime);color:var(--c-ink)}
/* Focus (Round 2): mouse focus shows nothing; keyboard focus is a 3px INK ring everywhere (cream on dark surfaces).
   Every rule that removes an outline on :focus-visible must add a replacement (see .field__control). */
:focus{outline:none}
:focus-visible{outline:3px solid var(--c-ink);outline-offset:3px}
:is(.surface-dark,.band--ink) :focus-visible,.site-footer :focus-visible,.lead-form--dark :focus-visible{outline-color:var(--c-cream)}
/* Programmatic focus targets only (not interactive): the skip-link target and the form status message. */
main[tabindex="-1"]:focus,main[tabindex="-1"]:focus-visible,.form-status:focus-visible{outline:none}
/* Touch: no 300ms double-tap delay, no grey tap flash (every control has its own :active state). */
a,button,input,select,textarea,label,summary,[role="button"],[role="tab"],[tabindex],.card-hover,.carousel-slide{touch-action:manipulation;-webkit-tap-highlight-color:transparent}

/* ---------- 2. Typography ---------- */
h1,h2,h3,h4,.h1,.h2,.h3,.h4{font-weight:700;line-height:1.265;letter-spacing:-.012em;text-wrap:balance;overflow-wrap:break-word}
/* Round 5: ONE h1 style, sentence case at every width (36 / 44 / 56, always ≥ h2). */
h1,.h1{font-size:var(--fs-h1);line-height:1.15;text-transform:none}
h2,.h2{font-size:var(--fs-h2)}
h3,.h3{font-size:var(--fs-h3);line-height:1.3}
h4,.h4{font-size:var(--fs-body);line-height:1.4}
p,li,dd,dt,figcaption,blockquote{text-wrap:pretty;overflow-wrap:break-word}
label,.eyebrow,.btn,th,caption{text-wrap:balance}
/* balance only for headings/labels of ≤ ~2 lines (.balance, FAQ questions). Multi-line descriptions use `pretty`, which
   fills the full column and still avoids a 1-word last line (tmm_glue() glues the last 3 words of longer copy);
   balance would squeeze them into a narrow column with an empty right side on phones. */
.balance,.faq__q{text-wrap:balance}
.section-heading__desc,.lead-form__desc,.faq__a,.faq__a p,.form__note,.footer-brand__tagline,.footer-contact__hours{text-wrap:pretty}
h1 em,h2 em,h3 em,h4 em,.h1 em,.h2 em,.h3 em,.h4 em,.lead-form__title em{font-style:normal;color:var(--c-green)}
:is(.surface-dark,.band--ink) h1 em,:is(.surface-dark,.band--ink) h2 em,:is(.surface-dark,.band--ink) h3 em,:is(.surface-dark,.band--ink) h4 em,:is(.surface-dark,.band--ink) .h1 em,:is(.surface-dark,.band--ink) .h2 em,:is(.surface-dark,.band--ink) .h3 em,:is(.surface-dark,.band--ink) .h4 em,:is(.surface-dark,.band--ink) .lead-form__title em{color:var(--c-lime)}
/* Two-line split heading (Figma h2a / h2b): add .is-split to the heading or wrap highlight in <em class="block"> */
.is-split em,em.block{display:block}
strong,b{font-weight:700}
small,.text-small{font-size:var(--fs-small);line-height:1.5}
.lead{font-size:var(--fs-body);line-height:1.7}
.text-muted{color:rgba(var(--c-ink-rgb),.72)}
:is(.surface-dark,.band--ink) .text-muted{color:rgba(255,250,243,.7)}
.text-green{color:var(--c-green)}
.measure{max-width:60ch}
.prose > * + *{margin-top:1em}
.prose ul,.prose ol{padding-left:1.25em}
/* Olive text is too faint on cream at body sizes (~2.2:1), so in-text links are ink with a thick olive underline. */
.prose a,.faq__a a,.form__note a{color:inherit;text-decoration:underline;text-decoration-color:var(--c-green);text-decoration-thickness:2px;text-underline-offset:.22em;transition:text-decoration-color .15s var(--ease),background-color .15s var(--ease)}
@media (hover:hover){.prose a:hover,.faq__a a:hover,.form__note a:hover{text-decoration-color:var(--c-ink);background:rgba(var(--c-green-rgb),.16)}}
.prose a:active,.faq__a a:active,.form__note a:active{background:rgba(var(--c-green-rgb),.28)}
:is(.surface-dark,.band--ink) .prose a,:is(.surface-dark,.band--ink) .faq__a a,:is(.surface-dark,.band--ink) .form__note a{text-decoration-color:var(--c-lime)}

/* ---------- 3. Layout & surfaces ---------- */
.container{width:min(var(--container),100% - 2*var(--gutter));margin-inline:auto}
.section{position:relative;padding-block:var(--section-y);overflow:hidden;overflow:clip;isolation:isolate}
.section > .container{position:relative;z-index:1}
.surface-cream{background:var(--c-cream);color:var(--c-ink)}
.surface-lime-50{background:var(--c-cream-warm);color:var(--c-ink)}
.surface-lime{background:var(--c-lime);color:var(--c-ink)}
.surface-dark{background:var(--c-ink);color:var(--c-cream)}
.site-main{display:block;min-width:0}
.stack > * + *{margin-top:var(--stack-gap,16px)}

/* ---------- 4. Eyebrow & section heading ---------- */
/* Figma eyebrow: Be Vietnam Pro Bold, sentence case, brand olive (--c-green). No uppercase / tracking. */
/* Round 2: plain text, exactly as Figma. No outlined pill, no background, no leading dash/mark. Olive by client decision. */
.eyebrow{display:block;font-size:var(--fs-eyebrow);font-weight:700;line-height:1.265;color:var(--c-green);letter-spacing:0;margin:0 0 12px;padding:0;border:0;border-radius:0;background:none;text-transform:none}
.eyebrow::before,.eyebrow::after{content:none}
:is(.surface-dark,.band--ink) .eyebrow{color:var(--c-lime)}
.eyebrow--soft{color:var(--c-green-soft)}
.section-heading{margin-bottom:var(--heading-gap);max-width:62rem}
.section-heading .h1,.section-heading .h2,.section-heading .h3{margin:0}
.section-heading__desc{margin-top:16px;max-width:49rem;font-size:var(--fs-body);line-height:1.6}
:is(.surface-dark,.band--ink) .section-heading__desc{color:rgba(255,250,243,.8)}
.section-heading--center{margin-inline:auto;text-align:center}
.section-heading--center .section-heading__desc{margin-inline:auto}
@media (min-width:1200px){.eyebrow{margin-bottom:16px}.section-heading__desc{margin-top:20px}}

/* ---------- 5. Buttons & icons ---------- */
/* Figma Button: Primary #92b93c/ink → hover ink/lime · Outline ink stroke → hover ink/cream · Link green → hover ink */
.icon{width:1em;height:1em;flex:none}
.icon-arrow{width:1em;height:1em;flex:none;transition:transform .2s var(--ease)}
.btn{--btn-bg:var(--c-green-soft);--btn-fg:var(--c-ink);--btn-bd:var(--c-green-soft);--btn-bg-h:var(--c-ink);--btn-fg-h:var(--c-lime);--btn-bd-h:var(--c-ink);
  display:inline-flex;align-items:center;justify-content:center;gap:12px;min-height:48px;padding:12px 24px;border:1px solid var(--btn-bd);border-radius:var(--radius-pill);background-color:var(--btn-bg);background-image:linear-gradient(var(--btn-bg-h),var(--btn-bg-h));background-repeat:no-repeat;background-position:0 0;background-size:0% 100%;color:var(--btn-fg);font-size:16px;font-weight:600;line-height:1.25;text-align:center;text-decoration:none;cursor:pointer;
  transition:background-size .4s var(--ease),background-color .2s var(--ease),color .25s var(--ease),border-color .25s var(--ease),transform .15s var(--ease),box-shadow .2s var(--ease);-webkit-tap-highlight-color:transparent;max-width:100%}
.btn > span{min-width:0}
/* Micro-interactions (Round 2): arrow nudges 2px on hover (diagonal ↗ = up-right, → = right); press = scale(.96) + 1px down. */
.btn .icon-arrow-right,.link-arrow .icon-arrow-right{transition:transform .2s var(--ease)}
@media (hover:hover){
  .btn:hover{background-size:100% 100%;color:var(--btn-fg-h);border-color:var(--btn-bd-h)}
  .btn:hover .icon-arrow{transform:translate(3px,-3px)}
  .btn:hover .icon-arrow-right{transform:translateX(4px)}
}
/* v2: keyboard focus gets the same sweep as hover (plus the ring). */
.btn:focus-visible{background-size:100% 100%;color:var(--btn-fg-h);border-color:var(--btn-bd-h)}
.btn:focus-visible .icon-arrow{transform:translate(3px,-3px)}
.btn:focus-visible .icon-arrow-right{transform:translateX(4px)}
.btn:focus-visible{outline:3px solid var(--c-ink);outline-offset:3px}
:is(.surface-dark,.band--ink) .btn:focus-visible,.site-footer .btn:focus-visible,.lead-form--dark .btn:focus-visible{outline-color:var(--c-cream)}
.btn:active{transform:translateY(1px) scale(.96)}
.btn:disabled,.btn[aria-disabled="true"]{opacity:.5;cursor:not-allowed;transform:none}
@media (hover:hover){.btn:disabled:hover{background-size:0% 100%;color:var(--btn-fg);border-color:var(--btn-bd)}}
.btn--dark{--btn-bg:var(--c-ink);--btn-fg:var(--c-lime);--btn-bd:var(--c-ink);--btn-bg-h:var(--c-green-soft);--btn-fg-h:var(--c-ink);--btn-bd-h:var(--c-green-soft)}
.btn--lime{--btn-bg:var(--c-lime);--btn-fg:var(--c-ink);--btn-bd:var(--c-lime);--btn-bg-h:var(--c-cream);--btn-fg-h:var(--c-ink);--btn-bd-h:var(--c-cream)}
.btn--ghost{--btn-bg:transparent;--btn-fg:var(--c-ink);--btn-bd:var(--c-ink);--btn-bg-h:var(--c-ink);--btn-fg-h:var(--c-cream);--btn-bd-h:var(--c-ink)}
:is(.surface-dark,.band--ink) .btn--ghost{--btn-fg:var(--c-cream);--btn-bd:var(--c-cream);--btn-bg-h:var(--c-cream);--btn-fg-h:var(--c-ink);--btn-bd-h:var(--c-cream)}
/* Text-style button: ink text (olive text on cream is ~2.2:1); the olive lives in the underline. */
.btn--link{font-weight:700;--btn-bg:transparent;--btn-fg:var(--c-ink);--btn-bd:transparent;--btn-bg-h:transparent;--btn-fg-h:var(--c-ink);--btn-bd-h:transparent;padding-inline:4px;min-height:44px;text-decoration:underline;text-decoration-color:var(--c-green);text-decoration-thickness:2px;text-underline-offset:.3em}
@media (hover:hover){.btn--link:hover{text-decoration-color:var(--c-ink)}}
:is(.surface-dark,.band--ink) .btn--link{--btn-fg:var(--c-cream);--btn-fg-h:var(--c-lime);text-decoration-color:var(--c-lime)}
.btn--sm{min-height:44px;padding:10px 20px;font-size:15px}
.btn--block{display:flex;width:100%}
@media (min-width:768px){.btn{font-size:18px;padding:14px 26px}.btn--sm{font-size:16px;padding:10px 22px}}
@media (min-width:1200px){.btn{font-size:17px;min-height:54px;padding:14px 28px}.btn--sm{font-size:16px;min-height:46px;padding:10px 22px}}
/* Text link with arrow */
/* Text link with arrow: its size follows the context (can be <16px), so the text is ink and the olive lives in the underline + arrow. */
.link-arrow{display:inline-flex;align-items:center;gap:8px;min-height:44px;color:var(--c-ink);font-weight:600;text-decoration:underline;text-decoration-color:var(--c-green);text-decoration-thickness:2px;text-underline-offset:.3em;transition:text-decoration-color .15s var(--ease)}
.link-arrow .icon-arrow{color:var(--c-green);transition:transform .2s var(--ease),color .15s var(--ease)}
@media (hover:hover){.link-arrow:hover{text-decoration-color:var(--c-ink)}.link-arrow:hover .icon-arrow{color:var(--c-ink);transform:translate(2px,-2px)}.link-arrow:hover .icon-arrow-right{transform:translateX(2px)}}
.link-arrow:active{opacity:.75;transform:translateY(1px)}
:is(.surface-dark,.band--ink) .link-arrow{color:var(--c-cream);text-decoration-color:var(--c-lime)}
:is(.surface-dark,.band--ink) .link-arrow .icon-arrow{color:var(--c-lime)}
@media (hover:hover){:is(.surface-dark,.band--ink) .link-arrow:hover{color:var(--c-lime)}}

/* ---------- 6. Forms (Figma Form Field 98:841: underline, label-in-field, focus = brand olive, error #b3261e) ---------- */
.form{display:grid;gap:4px;position:relative}
.field{position:relative;display:flex;flex-direction:column;min-width:0}
.field--honeypot{position:absolute!important;left:-9999px!important;width:1px;height:1px;overflow:hidden}
/* Floating label: sits inside the field like the Figma default; lifts when focused or filled. */
.field__label{position:absolute;left:0;top:18px;max-width:100%;font-size:16px;font-weight:500;letter-spacing:0;line-height:1.265;color:rgba(var(--c-ink-rgb),.7);pointer-events:none;transform-origin:left top;transition:transform .18s var(--ease),color .18s var(--ease);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.field__req{margin-left:.3em}
.field__control{display:block;width:100%;min-height:55px;padding:22px 0 8px;border:0;border-bottom:1px solid rgba(var(--c-ink-rgb),.2);border-radius:0;background:transparent;color:var(--c-ink);font-size:16px;line-height:1.4;appearance:none;-webkit-appearance:none;transition:border-color .18s var(--ease),box-shadow .18s var(--ease)}
.field__control::placeholder{color:transparent}
textarea.field__control{min-height:96px;resize:vertical;padding-top:26px}
/* Focus (Round 2): a clear 2px INK underline (border + 1px inset shadow = 2px, no layout shift) and the label turns full
   ink + semibold. The outline is replaced by this underline, never just removed. */
.field__control:focus,.field__control:focus-visible{outline:none;border-bottom-color:var(--c-ink);box-shadow:0 1px 0 0 var(--c-ink)}
.field:focus-within .field__label{color:var(--c-ink);font-weight:600}
/* The label lifts only for text inputs/textarea with a value, or any field that is focused / .is-filled (JS; covers
   <select>, which never matches :placeholder-shown and would otherwise always float over an empty choice). */
input.field__control:focus + .field__label,textarea.field__control:focus + .field__label,input.field__control:not(:placeholder-shown) + .field__label,textarea.field__control:not(:placeholder-shown) + .field__label,
.field:focus-within .field__label,.field.is-filled .field__label,.field:has(input.field__control:not(:placeholder-shown)) .field__label,.field:has(textarea.field__control:not(:placeholder-shown)) .field__label{transform:translateY(-13px) scale(.75)}
.field__select{position:relative}
.field__select .field__control{padding-right:32px;cursor:pointer}
.field__chevron{position:absolute;right:4px;top:50%;width:20px;height:20px;transform:translateY(-35%);pointer-events:none;color:rgba(var(--c-ink-rgb),.7)}
.field__error{margin-top:6px;font-size:14px;font-weight:500;line-height:1.265;color:#b3261e;text-align:left}
.field.is-invalid .field__control{border-bottom-color:#b3261e;box-shadow:0 1px 0 0 #b3261e}
.field.is-invalid .field__label{color:#b3261e}
.field.is-valid .field__control{border-bottom-color:var(--c-green)}
@media (hover:hover){.field__control:hover:not(:focus){border-bottom-color:rgba(var(--c-ink-rgb),.45)}}
@media (min-width:1200px){
  .field__label{font-size:17px;letter-spacing:0;top:17px}
  .field__control{font-size:17px;padding:24px 0 8px}
  .field:focus-within .field__label,.field.is-filled .field__label,.field:has(input.field__control:not(:placeholder-shown)) .field__label,.field:has(textarea.field__control:not(:placeholder-shown)) .field__label{transform:translateY(-14px) scale(.7)}
  .field__error{font-size:15px}
}
.form__actions{margin-top:20px;display:flex;flex-direction:column;align-items:stretch;gap:12px}
.form__actions .btn{width:100%;padding-inline:20px}
/* Small phones: every lead-form submit stays on one line ("Đăng ký kiểm tra trình độ ngay" at 360): tighter padding,
   icon gap and size; the tiles give the full-width button 4px more room each side. */
@media (max-width:480px){
  .form__actions .btn{padding-inline:14px;gap:8px;font-size:15px}
  .form__actions .btn > span{white-space:nowrap}
  .lead-form--lime,.lead-form--card,.lead-form--dark{padding-inline:16px}
}
@media (min-width:768px){.form__actions{align-items:flex-start}.form__actions .btn{width:auto;padding-inline:28px}.lead-form--card .form__actions{align-items:stretch}.lead-form--card .form__actions .btn{width:100%}}
.form__note{font-size:var(--fs-small);line-height:1.5;color:rgba(var(--c-ink-rgb),.72)}
.form-status{margin-top:12px;padding:12px 16px;border-radius:var(--radius-sm);background:rgba(var(--c-ink-rgb),.06);font-size:var(--fs-small);line-height:1.5}
.form-status.is-success{background:rgba(var(--c-green-rgb),.18);color:var(--c-ink);box-shadow:inset 3px 0 0 var(--c-green)}
.form-status.is-error{background:rgba(179,38,30,.1);color:#8c1d17}
.form[aria-busy="true"] .form-submit{opacity:.6}
/* Dark-surface fields */
:is(.surface-dark,.band--ink) .field__label,.lead-form--dark .field__label{color:rgba(255,250,243,.7)}
:is(.surface-dark,.band--ink) .field__control,.lead-form--dark .field__control{color:var(--c-cream);border-bottom-color:rgba(255,250,243,.25)}
:is(.surface-dark,.band--ink) .field:focus-within .field__label,.lead-form--dark .field:focus-within .field__label{color:var(--c-cream)}
:is(.surface-dark,.band--ink) .field__control:focus,.lead-form--dark .field__control:focus{border-bottom-color:var(--c-cream);box-shadow:0 1px 0 0 var(--c-cream)}
:is(.surface-dark,.band--ink) .field__chevron,.lead-form--dark .field__chevron{color:rgba(255,250,243,.7)}
:is(.surface-dark,.band--ink) .field__control option,.lead-form--dark .field__control option{color:var(--c-ink)}
:is(.surface-dark,.band--ink) .field__error,.lead-form--dark .field__error{color:#ffb4ab}
:is(.surface-dark,.band--ink) .form__note,.lead-form--dark .form__note{color:rgba(255,250,243,.7)}
:is(.surface-dark,.band--ink) .form-status,.lead-form--dark .form-status{background:rgba(255,250,243,.1);color:var(--c-cream)}
/* Lead form wrappers */
.lead-form{position:relative;min-width:0}
.lead-form__head{margin-bottom:12px}
.lead-form__head .eyebrow{margin-bottom:8px}
.lead-form__title{font-size:var(--fs-h2);font-weight:900;line-height:1.265;margin:0}
.lead-form__desc{margin-top:12px;max-width:none;line-height:1.7}
@media (min-width:768px){.lead-form__desc{max-width:34em}}
/* The CTA tile is solid cream (the lime-40% tint read as mint; client 24 Sep). It sits on the olive band. */
.lead-form--lime{padding:28px 20px;border-radius:var(--radius-card);background:var(--c-cream);box-shadow:0 1px 2px rgba(var(--c-ink-rgb),.05),0 32px 64px -40px rgba(var(--c-ink-rgb),.45)}
.lead-form--lime .lead-form__head .eyebrow{color:var(--c-green)}
.lead-form--card{padding:24px 20px;border:1px solid rgba(var(--c-ink-rgb),.1);border-radius:var(--radius-card);background:var(--c-cream);box-shadow:0 1px 2px rgba(var(--c-ink-rgb),.04),0 24px 48px -32px rgba(var(--c-ink-rgb),.28)}
.lead-form--card .lead-form__title{font-size:var(--fs-h3);font-weight:700}
.lead-form--card .form__actions{align-items:stretch}
.lead-form--card .form__actions .btn{width:100%}
.lead-form--dark{padding:28px 20px;border-radius:var(--radius-card);background:rgba(255,250,243,.06);border:1px solid rgba(255,250,243,.12);color:var(--c-cream)}
@media (min-width:768px){.lead-form--lime{padding:40px}.lead-form--card{padding:32px}.lead-form--dark{padding:40px}}
@media (min-width:1200px){.lead-form--lime{padding:48px}.lead-form--card{padding:36px}.lead-form--card .lead-form__title{font-size:24px}.lead-form__head{margin-bottom:16px}.lead-form__desc{margin-top:16px}}
/* Canonical "Kiểm tra đầu vào 1:1 MIỄN PHÍ" CTA (Figma Homepage 1:2 › 4:100): lime-40% tile, olive UPPERCASE eyebrow
   (h3 size), ink Black-900 title at h2 size with "MIỄN PHÍ" upper-case ≥1200 (wrap it in <span class="lead-form__upper">;
   the legacy .hp-upper-desktop / .cd-upper spans work too), ink body description, resting in-field labels (none
   prefilled), auto-width olive button on the left ≥768. Pages must not restyle this block: use surface 'lime'. */
.lead-form--lime .lead-form__head .eyebrow,.band .lead-form--lime .lead-form__head .eyebrow{color:var(--c-green);font-weight:700;letter-spacing:0}
.lead-form--lime .lead-form__title{font-size:var(--fs-h2);font-weight:900;line-height:1.265;color:var(--c-ink)}
.lead-form--lime .lead-form__title em{color:inherit}
.lead-form--lime .lead-form__desc{font-size:var(--fs-body);line-height:1.7;color:var(--c-ink)}
@media (min-width:1200px){.lead-form__title :is(.lead-form__upper,.hp-upper-desktop,.cd-upper){text-transform:uppercase}}

/* ---------- 7. Cards & lists ---------- */
.card{position:relative;display:flex;flex-direction:column;min-width:0;padding:24px;border:1px solid rgba(var(--c-ink-rgb),.09);border-radius:var(--radius-card);background:var(--c-cream);color:var(--c-ink);transition:transform .25s var(--ease),box-shadow .25s var(--ease),background-color .25s var(--ease),border-color .25s var(--ease)}
.card--lime{background:var(--c-cream-warm);border-color:transparent}
.card--white{background:var(--c-white)}
.card--dark{background:rgba(255,250,243,.06);border-color:rgba(255,250,243,.12);color:var(--c-cream)}
.card--link{text-decoration:none}
@media (hover:hover){.card--link:hover,.card--hover:hover{transform:translateY(-4px);box-shadow:0 18px 40px -24px rgba(var(--c-ink-rgb),.35);border-color:rgba(var(--c-ink-rgb),.2)}}
.card--link:active{transform:translateY(-1px)}
.card__media{overflow:hidden;border-radius:var(--radius-sm)}
.card__media img{width:100%;height:100%;object-fit:cover}
@media (min-width:768px){.card{padding:28px}}
@media (min-width:1200px){.card{padding:32px}}
.list-dot{list-style:none;padding:0;display:grid;gap:10px}
.list-dot > li{position:relative;padding-left:22px}
.list-dot > li::before{content:"";position:absolute;left:4px;top:.65em;width:7px;height:7px;border-radius:var(--radius-pill);background:var(--c-green-soft)}
.list-check{list-style:none;padding:0;display:grid;gap:12px}
.list-check > li{position:relative;padding-left:32px}
.list-check > li::before{content:"";position:absolute;left:0;top:.2em;width:20px;height:20px;border-radius:var(--radius-pill);background:var(--c-lime) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2332231f' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 12 4 4 8-8'/%3E%3C/svg%3E") center/12px no-repeat}
.list-inline{display:flex;flex-wrap:wrap;gap:8px 24px;list-style:none;padding:0}

/* ---------- 8. Decoration layer (absolute, aria-hidden, no pointer events) ---------- */
.deco{position:absolute;pointer-events:none;z-index:0}
/* Round 2 (lead): radial glows, washes, offset panels, watermark numerals and rings are NOT in Figma and are retired.
   The classes stay defined as hidden so any leftover markup renders nothing (no stray text, no boxes). Use flat surfaces
   (.surface-*), .grain, .frame-offset and the hand-drawn marks instead. */
.deco-wash,.deco-glow,.deco-panel,.deco-numeral,.deco-rings,.deco-ribbon,.deco-sparkle{display:none!important}
/* Line grid: ONLY for the course roadmap (it is in Figma). Anywhere else it is an AI tell. */
.deco-grid{position:absolute;inset:0;pointer-events:none;z-index:0;--grid:32px;background-image:repeating-linear-gradient(0deg,rgba(255,250,243,.06) 0 1px,transparent 1px var(--grid)),repeating-linear-gradient(90deg,rgba(255,250,243,.06) 0 1px,transparent 1px var(--grid))}
@media (min-width:768px){.deco-grid{--grid:48px}}
/* v2: hand-drawn line motifs (.deco-line) and hand-drawn proofreading marks (.deco-mark) are RETIRED (client, 24 Sep:
   too cartoonish for working professionals). tmm_deco() returns '' and legacy 'mark-*' names return ''. Precise marks,
   colour fields, display type and the flat .hl-swipe bar live in §17 (Visual system v2). */
.deco-line,.deco-mark{display:none!important}

/* Asymmetric olive hairline frame, offset --frame-offset (10px) down-right of a media element (--left: down-left).
   The offset is RESERVED as padding on the wrapper, so the frame never sticks out of its container at any width.
   Markup: <div class="frame-offset"><img …> or .media</div>. The first child fills the wrapper minus the offset. */
.frame-offset{--frame-offset:10px;--frame-color:var(--c-green);--frame-radius:var(--radius-card);position:relative;isolation:isolate;display:block;max-width:100%;padding:0 var(--frame-offset) var(--frame-offset) 0}
.frame-offset::before{content:"";position:absolute;inset:var(--frame-offset) 0 0 var(--frame-offset);border:1px solid var(--frame-color);border-radius:var(--frame-radius);pointer-events:none;z-index:-1}
.frame-offset > *{position:relative;z-index:1;border-radius:var(--frame-radius)}
.frame-offset--left{padding:0 0 var(--frame-offset) var(--frame-offset)}
.frame-offset--left::before{inset:var(--frame-offset) var(--frame-offset) 0 0}
.frame-offset--sm{--frame-radius:var(--radius-sm)}
:is(.surface-dark,.band--ink) .frame-offset{--frame-color:var(--c-lime)}

/* Paper grain: a very subtle SVG feTurbulence noise (~3.5%) multiplied over a flat section surface. Clipped to the
   element (inset:0 + inherited radius; .section already clips). Sits under the content (.section > .container is z1). */
.grain{position:relative;isolation:isolate}
.grain::after{content:"";position:absolute;inset:0;z-index:0;pointer-events:none;border-radius:inherit;opacity:.035;mix-blend-mode:multiply;
  background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E") 0 0/240px 240px repeat}
:is(.surface-dark,.band--ink).grain::after{mix-blend-mode:screen;opacity:.05;filter:invert(1)}
@media print{.grain::after{display:none}}

/* Card title underline that draws on hover / keyboard focus. Put .card-link on the link (or the card containing it) and
   wrap the title text in <span class="card-link__title"> (an inline span, so it follows every wrapped line). */
.card-link{text-decoration:none;color:inherit}
.card-link__title{background:linear-gradient(currentColor,currentColor) 0 100%/0 2px no-repeat;-webkit-box-decoration-break:clone;box-decoration-break:clone;padding-bottom:.06em;transition:background-size .35s var(--ease)}
@media (hover:hover){.card-link:hover .card-link__title{background-size:100% 2px}}
.card-link:focus-visible .card-link__title,.card-link:has(:focus-visible) .card-link__title{background-size:100% 2px}
.card-link:active .card-link__title{background-size:100% 2px}
@media (prefers-reduced-motion:reduce){.card-link__title{transition:none}}

/* ---------- 9. FAQ (always expanded: question + answer, dividers) ---------- */
.faq{display:grid;gap:8px}
.faq__head .section-heading{margin-bottom:16px}
/* FAQ titles ("Những câu hỏi thường gặp" = three compounds) must stay on one line on phones: any 2-line wrap either
   orphans or splits a compound. Fluid size = text width (~13.6 em at weight 700–900) fitted to the container. */
@media (max-width:767.98px){.faq-section .faq__head .h2{font-size:min(var(--fs-h2),calc((100vw - 2 * var(--gutter)) / 13.6))}}
/* Accordion (client, 24 Sep: collapsed by default). Hairline dividers; the whole question row is the button; a +/−
   drawn with two hairlines that rotate. Closed panels are hidden only when JS runs (html.js), so without JS every
   answer shows. Height animates with grid-template-rows 0fr → 1fr (no measuring); instant under reduced motion. */
.faq__list{--faq-rule:rgba(var(--c-ink-rgb),.16);margin:0;border-top:1px solid var(--faq-rule)}
.faq__item{position:relative;border-bottom:1px solid var(--faq-rule)}
.faq__q{margin:0;font-size:16px;font-weight:600;line-height:1.5;color:var(--c-ink)}
.faq__btn{position:relative;display:flex;align-items:flex-start;justify-content:space-between;gap:20px;width:100%;min-height:56px;padding:16px 4px 16px 0;text-align:left;font:inherit;color:inherit;background:linear-gradient(var(--faq-hover,rgba(var(--c-ink-rgb),.035)),var(--faq-hover,rgba(var(--c-ink-rgb),.035))) 0 0/0% 100% no-repeat;transition:background-size .4s var(--ease),padding-left .3s var(--ease),padding-right .3s var(--ease)}
.faq__btn::before{content:"";position:absolute;left:0;top:-1px;bottom:-1px;width:2px;background:var(--c-green);transform:scaleY(0);transition:transform .3s var(--ease)}
.faq__q-text{min-width:0;text-wrap:balance}
.faq__icon{position:relative;flex:none;width:18px;height:18px;margin-top:.2em}
.faq__icon::before,.faq__icon::after{content:"";position:absolute;left:0;right:0;top:50%;height:1.5px;margin-top:-.75px;background:currentColor;transition:transform .35s var(--ease)}
.faq__icon::after{transform:rotate(90deg)}
.faq__item.is-open .faq__icon::before{transform:rotate(180deg)}
.faq__item.is-open .faq__icon::after{transform:rotate(180deg)}
@media (hover:hover){.faq__btn:hover{background-size:100% 100%;padding-left:16px;padding-inline-end:16px}.faq__btn:hover::before{transform:scaleY(1)}}
.faq__btn:focus-visible{outline:3px solid var(--c-ink);outline-offset:-3px;background-size:100% 100%;padding-left:16px;padding-inline-end:16px}
.faq__btn:focus-visible::before{transform:scaleY(1)}
.faq__btn:active{background-color:rgba(var(--c-green-rgb),.14)}
.faq__panel{display:grid;grid-template-rows:1fr}
.faq__panel-inner{min-height:0;overflow:hidden}
.faq__a{padding:0 40px 20px 0;font-size:16px;line-height:1.6;color:rgba(var(--c-ink-rgb),.78)}
.faq__a p + p{margin-top:.6em}
.js .faq__panel{grid-template-rows:0fr;visibility:hidden;transition:grid-template-rows .4s var(--ease),visibility 0s linear .4s}
.js .faq__item.is-open .faq__panel{grid-template-rows:1fr;visibility:visible;transition:grid-template-rows .4s var(--ease),visibility 0s}
@media (prefers-reduced-motion:reduce){.faq__panel,.faq__btn,.faq__btn::before,.faq__icon::before,.faq__icon::after{transition:none!important}}
@media print{.js .faq__panel{grid-template-rows:1fr;visibility:visible}}
:is(.surface-dark,.band--ink) .faq__list{--faq-rule:rgba(255,250,243,.16)}
:is(.surface-dark,.band--ink) .faq__q{color:var(--c-cream)}
:is(.surface-dark,.band--ink) .faq__a{color:rgba(255,250,243,.78)}
:is(.surface-dark,.band--ink) .faq__btn{background-image:linear-gradient(rgba(255,250,243,.06),rgba(255,250,243,.06))}
:is(.surface-dark,.band--ink) .faq__btn::before{background:var(--c-lime)}
:is(.surface-dark,.band--ink) .faq__btn:focus-visible{outline-color:var(--c-cream)}
.band--lime .faq__btn{--faq-hover:rgba(var(--c-ink-rgb),.06)}
@media (min-width:1200px){
  .faq__q{font-size:18px;line-height:1.5}
  .faq__btn{min-height:64px;padding-block:20px}
  .faq__icon{width:18px;height:18px}
  .faq__a{padding-bottom:26px;font-size:var(--fs-body);line-height:1.65;max-width:52em}
  .faq--split{grid-template-columns:minmax(0,5fr) minmax(0,7fr);gap:64px;align-items:start}
  .faq--split .faq__head{position:sticky;top:calc(var(--header-h) + 24px)}
}

/* ---------- 10. Marquee ([data-marquee] > .marquee__track > .marquee__group; JS clones the group) ---------- */
.marquee{--marquee-duration:40s;--marquee-gap:48px;position:relative;display:flex;overflow:hidden;max-width:100%;user-select:none}
.marquee__track{display:flex;flex:none;gap:var(--marquee-gap);min-width:max-content;will-change:transform}
.marquee__group{display:flex;flex:none;align-items:center;gap:var(--marquee-gap);min-width:max-content;margin:0;padding:0;list-style:none}
.marquee.is-running .marquee__track{animation:tmm-marquee var(--marquee-duration) linear infinite}
.marquee--reverse.is-running .marquee__track{animation-direction:reverse}
.marquee.is-running:hover .marquee__track,.marquee.is-running:focus-within .marquee__track,.marquee.is-paused .marquee__track{animation-play-state:paused}
@keyframes tmm-marquee{from{transform:translateX(0)}to{transform:translateX(calc(-50% - var(--marquee-gap)/2))}}
@media (prefers-reduced-motion:reduce){.marquee:not([data-marquee-always]) .marquee__track{animation:none!important;transform:none!important}.marquee{flex-wrap:wrap}}

/* ---------- 11. Carousel ([data-carousel] / [data-carousel-track] / .carousel-slide / controls) ---------- */
.carousel{position:relative;min-width:0}
.carousel__track,[data-carousel-track]{display:flex;gap:16px;overflow-x:auto;overscroll-behavior-x:contain;scroll-snap-type:x mandatory;scroll-padding-inline:0;scrollbar-width:none;-webkit-overflow-scrolling:touch;padding-bottom:4px;margin:0;list-style:none;padding-left:0}
[data-carousel-track]::-webkit-scrollbar{display:none}
[data-carousel-track]:focus-visible{outline:3px solid var(--c-ink);outline-offset:4px;border-radius:var(--radius-sm)}
:is(.surface-dark,.band--ink) [data-carousel-track]:focus-visible{outline-color:var(--c-cream)}
/* Videos: visible ink ring on keyboard focus. Drawn inside the frame (negative offset) so the scrolling track's
   overflow cannot clip it. In a carousel slide the ring is an ink border + cream inner line on the slide, so it reads on
   both the dark video frame and the light page. Native controls stay reachable with Tab/Space/arrow keys. */
video:focus-visible{outline:3px solid var(--c-ink);outline-offset:-3px}
.carousel-slide:has(> video:focus-visible){position:relative}
.carousel-slide:has(> video:focus-visible)::after{content:"";position:absolute;inset:0;z-index:2;pointer-events:none;border:3px solid var(--c-ink);border-radius:var(--radius-card);box-shadow:inset 0 0 0 2px var(--c-cream)}
.carousel-slide:has(> video:focus-visible) > video:focus-visible{outline:none}
.carousel-slide{flex:0 0 var(--slide-w,85%);scroll-snap-align:start;min-width:0}
@media (min-width:768px){.carousel-slide{--slide-w:calc((100% - 16px)/2)}[data-carousel-track]{gap:20px}}
@media (min-width:1200px){.carousel-slide{--slide-w:calc((100% - 48px)/3)}[data-carousel-track]{gap:24px}}
.carousel__controls{display:flex;align-items:center;gap:12px;margin-top:24px}
.carousel__status{min-width:3.5em;font-size:var(--fs-small);font-weight:600;font-variant-numeric:tabular-nums;color:rgba(var(--c-ink-rgb),.72)}
.carousel-btn{display:inline-grid;place-items:center;width:48px;height:48px;border:1px solid var(--c-ink);border-radius:var(--radius-pill);background:transparent;color:var(--c-ink);transition:background-color .2s var(--ease),color .2s var(--ease),transform .15s var(--ease),opacity .2s}
.carousel-btn .icon{width:20px;height:20px}
@media (hover:hover){.carousel-btn:hover:not(:disabled){background:var(--c-green);border-color:var(--c-green);color:var(--c-ink)}}
.carousel-btn:active:not(:disabled){transform:scale(.94)}
.carousel-btn:disabled{opacity:.35}
:is(.surface-dark,.band--ink) .carousel-btn{border-color:var(--c-cream);color:var(--c-cream)}
@media (hover:hover){:is(.surface-dark,.band--ink) .carousel-btn:hover:not(:disabled){background:var(--c-lime);border-color:var(--c-lime);color:var(--c-ink)}}
:is(.surface-dark,.band--ink) .carousel__status{color:rgba(255,250,243,.72)}

/* ---------- 12. Header (sticky; desktop nav + outline CTA; burger panel <1200) ---------- */
.skip-link{position:fixed;left:16px;top:-100px;z-index:1000;padding:12px 18px;border-radius:var(--radius-sm);background:var(--c-ink);color:var(--c-cream);font-weight:600;text-decoration:none;transition:top .15s}
/* Shown only on real keyboard focus (:focus-visible), never on programmatic or mouse focus. */
.skip-link:focus-visible{top:12px;outline:3px solid var(--c-ink);outline-offset:3px}
.site-header{position:sticky;top:0;z-index:100;background:var(--c-header);border-bottom:1px solid rgba(var(--c-ink-rgb),.12)}
.admin-bar .site-header{top:var(--wp-admin--admin-bar--height,0px)}
@media (max-width:600px){.admin-bar .site-header{top:0}}
.header-inner{display:flex;align-items:center;justify-content:space-between;gap:16px;min-height:var(--header-h)}
.brand{display:inline-flex;align-items:center;min-height:44px;font-size:16px;font-weight:700;line-height:1.265;color:var(--c-ink);text-decoration:none;white-space:nowrap;text-transform:uppercase}
@media (hover:hover){.brand:hover{color:var(--c-green)}}
.brand:active{opacity:.8}
.menu-toggle{position:relative;display:inline-grid;place-items:center end;width:44px;height:44px;border-radius:var(--radius-sm);color:var(--c-ink)}
.menu-toggle__bars{position:relative;width:28px;height:21px}
.menu-toggle__bars span{position:absolute;left:0;width:28px;height:2.5px;border-radius:var(--radius-pill);background:currentColor;transition:transform .25s var(--ease),opacity .2s,top .25s var(--ease)}
.menu-toggle__bars span:nth-child(1){top:0}
.menu-toggle__bars span:nth-child(2){top:9px}
.menu-toggle__bars span:nth-child(3){top:18px}
.menu-toggle[aria-expanded="true"] .menu-toggle__bars span:nth-child(1){top:9px;transform:rotate(45deg)}
.menu-toggle[aria-expanded="true"] .menu-toggle__bars span:nth-child(2){opacity:0}
.menu-toggle[aria-expanded="true"] .menu-toggle__bars span:nth-child(3){top:9px;transform:rotate(-45deg)}
@media (hover:hover){.menu-toggle:hover{background:rgba(var(--c-ink-rgb),.06)}}
.menu-toggle:active{transform:scale(.95)}
.primary-nav .menu{display:flex;list-style:none;margin:0;padding:0}
.primary-nav .menu a{display:inline-flex;align-items:center;min-height:44px;color:var(--c-ink);text-decoration:none;font-weight:500;transition:color .15s var(--ease)}
.primary-nav .menu a{position:relative}
.primary-nav .menu a::after{content:"";position:absolute;left:0;right:0;bottom:6px;height:2px;background:var(--c-green);transform:scaleX(0);transform-origin:right center;transition:transform .35s var(--ease)}
@media (hover:hover){.primary-nav .menu a:hover::after{transform:scaleX(1);transform-origin:left center}}
.primary-nav .menu a:focus-visible::after{transform:scaleX(1);transform-origin:left center}
.primary-nav .menu a:active{opacity:.7}
.primary-nav .current-menu-item > a,.primary-nav .current_page_item > a,.primary-nav .menu a[aria-current="page"]{text-decoration:none}
.primary-nav .current-menu-item > a::after,.primary-nav .current_page_item > a::after,.primary-nav .menu a[aria-current="page"]::after{transform:scaleX(1);background:var(--c-ink)}
/* Mobile / tablet panel (below 1024 since round 5: the full nav fits from 1024) */
@media (max-width:1023.98px){
  .site-header.nav-enhanced .primary-nav{position:absolute;left:0;right:0;top:100%;max-height:calc(100dvh - var(--header-h));overflow-y:auto;padding:24px var(--gutter) 32px;background:var(--c-header);border-bottom:1px solid rgba(var(--c-ink-rgb),.12);box-shadow:0 24px 40px -28px rgba(var(--c-ink-rgb),.35);visibility:hidden;opacity:0;transform:translateY(-8px);transition:opacity .2s var(--ease),transform .2s var(--ease),visibility 0s linear .2s}
  .site-header.nav-enhanced .primary-nav.is-open{visibility:visible;opacity:1;transform:none;transition:opacity .2s var(--ease),transform .2s var(--ease),visibility 0s}
  .primary-nav .menu{flex-direction:column;gap:6px}
  .primary-nav .menu a{font-size:24px;font-weight:700;line-height:1.25;min-height:48px}
  .primary-nav .header-cta{margin-top:24px;--btn-bg:var(--c-green-soft);--btn-bd:var(--c-green-soft);--btn-fg:var(--c-ink);--btn-bg-h:var(--c-ink);--btn-fg-h:var(--c-lime);min-height:52px;padding:14px 26px;font-size:18px}
  .site-header:not(.nav-enhanced) .primary-nav{padding-bottom:16px}
  .site-header:not(.nav-enhanced) .header-inner{flex-wrap:wrap}
  .site-header:not(.nav-enhanced) .primary-nav .menu{flex-direction:row;flex-wrap:wrap;gap:4px 20px}
  .site-header:not(.nav-enhanced) .primary-nav .menu a{font-size:16px}
}
@media (min-width:768px) and (max-width:1023.98px){.brand{font-size:18px}.primary-nav .menu a{font-size:28px}}
/* Backdrop behind the open burger panel (created by main.js, sits under the sticky header): outside taps close the
   menu without reaching the page. Page scroll is locked while open; the scrollbar gap is padded so nothing shifts. */
.nav-backdrop{position:fixed;inset:0;z-index:99;background:rgba(var(--c-ink-rgb),.28);opacity:0;visibility:hidden;transition:opacity .2s var(--ease),visibility 0s linear .2s;-webkit-tap-highlight-color:transparent}
.nav-backdrop.is-visible{opacity:1;visibility:visible;transition:opacity .2s var(--ease),visibility 0s}
@media (min-width:1024px){.nav-backdrop{display:none}}
html.nav-locked{overflow:hidden}
html.nav-locked body{padding-right:var(--scrollbar-gap,0px)} /* no overflow on body: it would become the sticky header's scroll container */
@media (prefers-reduced-motion:reduce){.site-header.nav-enhanced .primary-nav{transition:none!important;transform:none!important}.nav-backdrop{transition:none!important}}
@media (min-width:1024px){
  .brand{font-size:18px;letter-spacing:.02em}
  .menu-toggle{display:none!important}
  .primary-nav{display:flex;align-items:center;flex:1;justify-content:space-between;gap:32px;margin-left:clamp(32px,4vw,96px);min-width:0}
  .primary-nav .menu{gap:clamp(24px,2.4vw,38px);margin-inline:auto}
  .primary-nav .menu li{flex:none}
  .primary-nav .menu a{font-size:17px;line-height:1.5;white-space:nowrap}
  .header-cta{flex:none}
}
@media (min-width:1024px) and (max-width:1359.98px){.primary-nav .menu a{font-size:16px}.primary-nav .header-cta{font-size:16px}}
/* 1024–1199: the same one-row nav, tighter (brand 16, gaps 20, CTA 15) so brand + 5 links + CTA fit in 944px. */
@media (min-width:1024px) and (max-width:1199.98px){.brand{font-size:16px;letter-spacing:0}.primary-nav{margin-left:24px;gap:20px}.primary-nav .menu{gap:20px}.primary-nav .menu a{font-size:15px}.primary-nav .header-cta{font-size:15px;padding-inline:18px}}
body.menu-open{overflow:hidden}
@media (min-width:1024px){body.menu-open{overflow:visible}}

/* ---------- 13. Footer (dark ink; brand + nav/legal/social | contact; mega-type marquee) ---------- */
.site-footer{position:relative;overflow:hidden;background:var(--c-ink);color:var(--c-cream);padding-top:48px}
.site-footer a{color:inherit;text-decoration:none}
.footer-cols{display:grid;gap:14px 40px;grid-template-areas:"brand" "nav" "contact" "bottom"}
.footer-brand{grid-area:brand}.footer-nav{grid-area:nav}.footer-contact{grid-area:contact}.footer-bottom{grid-area:bottom;display:flex;flex-direction:column;gap:8px}
.footer-bottom .footer-social{order:1}.footer-bottom .footer-meta{order:2}.footer-bottom .footer-legal{order:3}
.footer-brand__name{display:inline-flex;align-items:center;min-height:44px;font-size:24px;font-weight:700;line-height:1.25;text-transform:uppercase}
.footer-brand__tagline{margin-top:14px;font-size:16px;line-height:1.5;color:rgba(255,250,243,.7)}
.footer-nav .menu{display:flex;flex-wrap:wrap;gap:0 24px;list-style:none;margin:0;padding:0}
.footer-nav .menu a,.footer-legal a,.footer-contact a{display:inline-flex;align-items:center;min-height:44px;transition:color .15s var(--ease)}
.footer-nav .menu a{font-size:16px;font-weight:500;color:var(--c-cream)}
.footer-nav .menu a[aria-current="page"]{text-decoration:underline;text-underline-offset:.3em}
.footer-legal{display:flex;flex-wrap:wrap;gap:0 24px;list-style:none;margin:0;padding:0}
.footer-legal a{font-size:15px;color:rgba(255,250,243,.6)}
.footer-social{display:flex;flex-wrap:wrap;gap:8px 16px;list-style:none;margin:0;padding:0}
.footer-social a{display:inline-grid;place-items:center start;width:44px;height:44px;border-radius:var(--radius-sm);color:var(--c-cream)}
.footer-social svg{width:30px;height:30px}
.footer-contact__title{font-size:20px;font-weight:700;line-height:1.3;margin:10px 0 6px;color:var(--c-cream)}
.footer-contact__list{list-style:none;margin:0;padding:0}
.footer-contact__list a{font-size:16px;color:rgba(255,250,243,.85);min-height:44px}
.footer-contact__hours{font-size:16px;line-height:1.5;color:rgba(255,250,243,.6);max-width:30em;margin-top:4px}
.footer-meta{margin-top:4px;font-size:15px;line-height:1.6;color:rgba(255,250,243,.6)}
.footer-meta p{margin:0}
@media (hover:hover){.site-footer a:hover{color:var(--c-lime)}}
.site-footer a:active{opacity:.75}
.site-footer a:focus-visible{outline:3px solid var(--c-cream);outline-offset:2px}
/* Mega type marquee: Figma "THE METAPHORIC MIND" Black 900, cream 7%, clipped at the bottom edge */
.footer-mega{position:relative;margin-top:24px;height:calc(var(--mega-size) * .66);overflow:hidden;--mega-size:88px}
.footer-mega .marquee{--marquee-gap:.35em;--marquee-duration:45s;font-size:var(--mega-size);font-weight:900;line-height:1.265;letter-spacing:-.04em;color:rgba(255,250,243,.07);white-space:nowrap;text-transform:uppercase}
.footer-mega .marquee__group{gap:var(--marquee-gap)}
.footer-mega .marquee__group span{display:block}
@media (prefers-reduced-motion:reduce){.footer-mega .marquee{flex-wrap:nowrap}}
@media (min-width:768px){
  .site-footer{padding-top:56px}
  .footer-cols{grid-template-columns:minmax(0,1fr) minmax(0,1fr);grid-template-areas:"brand contact" "nav contact" "bottom contact";align-items:start}
  .footer-contact{grid-row:1 / span 3}
  .footer-brand__name{font-size:26px}
  .footer-contact__title{font-size:24px;margin-top:0}
  .footer-mega{--mega-size:140px;margin-top:32px}
}
@media (min-width:1200px){
  .site-footer{padding-top:60px}
  .footer-cols{grid-template-columns:minmax(0,1fr) 400px;gap:0 64px}
  .footer-bottom{gap:0}
  .footer-bottom .footer-legal{order:1}.footer-bottom .footer-social{order:2}.footer-bottom .footer-meta{order:3}
  .footer-brand__name{font-size:24px;line-height:1.265;letter-spacing:.01em}
  .footer-brand__tagline{margin-top:10px;font-size:17px;line-height:1.7;color:rgba(255,250,243,.6)}
  .footer-nav{margin-top:34px}
  .footer-nav .menu{gap:0 30px}
  .footer-nav .menu a{font-size:17px;font-weight:400;color:rgba(255,250,243,.75);min-height:32px}
  .footer-legal{gap:0 28px}
  .footer-legal a{font-size:15px;min-height:32px}
  .footer-social{margin-top:10px;gap:28px}
  .footer-contact__title{font-size:24px;line-height:1.265;margin-bottom:10px}
  .footer-contact__list a{font-size:17px;color:rgba(255,250,243,.75);min-height:35px}
  .footer-contact__hours{font-size:16px;line-height:1.7;color:rgba(255,250,243,.55)}
  .footer-meta{font-size:15px}
  .footer-mega{--mega-size:200px;margin-top:20px;height:167px}
  .footer-mega .marquee{letter-spacing:-8px}
}
/* Back to top */
.back-to-top{position:fixed;right:16px;bottom:16px;z-index:90;display:inline-grid;place-items:center;width:48px;height:48px;border-radius:var(--radius-pill);background:var(--c-ink);color:var(--c-lime);box-shadow:0 10px 24px -12px rgba(var(--c-ink-rgb),.6);text-decoration:none;opacity:0;visibility:hidden;transform:translateY(8px);transition:opacity .2s var(--ease),transform .2s var(--ease),visibility 0s linear .2s,background-color .2s}
.back-to-top .icon{width:22px;height:22px}
.back-to-top.is-visible,.back-to-top:focus-visible,.no-js .back-to-top{opacity:1;visibility:visible;transform:none;transition:opacity .2s var(--ease),transform .2s var(--ease),visibility 0s}
@media (hover:hover){.back-to-top:hover{background:var(--c-green-soft);color:var(--c-ink)}}
.back-to-top:active{transform:scale(.94)}
.back-to-top:focus-visible{outline:3px solid var(--c-ink);outline-offset:3px}
@media (min-width:1200px){.back-to-top{right:24px;bottom:24px;width:52px;height:52px}}

/* ---------- 14. Utilities ---------- */
.sr-only,.screen-reader-text{position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}
.br-desktop,.br-tablet{display:none}
@media (min-width:768px){.br-tablet{display:inline}.br-mobile{display:none}}
@media (min-width:1200px){.br-desktop{display:inline}}
.nowrap{white-space:nowrap}
.media{position:relative;overflow:hidden;border-radius:var(--radius-card)}
.media > img,.media > video{width:100%;height:100%;object-fit:cover;object-position:var(--pos,50% 25%)}
.ratio-4x3{aspect-ratio:4/3}.ratio-16x9{aspect-ratio:16/9}.ratio-1x1{aspect-ratio:1}.ratio-3x4{aspect-ratio:3/4}.ratio-9x16{aspect-ratio:9/16}
.media-placeholder{display:grid;place-content:center;gap:6px;min-height:200px;padding:24px;border-radius:var(--radius-card);background:repeating-linear-gradient(135deg,rgba(var(--c-ink-rgb),.04) 0 12px,transparent 12px 24px),var(--c-cream);border:1px solid rgba(var(--c-ink-rgb),.1);color:rgba(var(--c-ink-rgb),.72);text-align:center;font-weight:600}
.media-placeholder small{font-weight:400;opacity:.8}
.placeholder-icon{font-size:24px}
.alignwide,.alignfull{max-width:100%}

/* ---------- 15. Motion: scroll reveal (progressive enhancement) ----------
   Markup: [data-reveal] rises as one block; [data-reveal="stagger"] rises child by child.
   Content is visible by default. main.js only adds .reveal-pending (hidden) when JS runs, motion is allowed and the
   element starts below the viewport; IntersectionObserver swaps it for .is-revealing (a keyframe on opacity + `translate`,
   so hover `transform`s and component transitions are untouched). No JS, print and reduced motion: always visible. */
[data-reveal].reveal-pending:not([data-reveal="stagger"],[data-reveal="mask"],[data-reveal="lines"]),[data-reveal="stagger"].reveal-pending > *{opacity:0;translate:0 24px}
[data-reveal].is-revealing:not([data-reveal="stagger"],[data-reveal="mask"],[data-reveal="lines"]),[data-reveal="stagger"].is-revealing > *{animation:tmm-reveal .7s var(--ease) both}
[data-reveal="stagger"].is-revealing > :nth-child(2){animation-delay:.1s}
[data-reveal="stagger"].is-revealing > :nth-child(3){animation-delay:.2s}
[data-reveal="stagger"].is-revealing > :nth-child(n+4){animation-delay:.3s}
@keyframes tmm-reveal{from{opacity:0;translate:0 24px}to{opacity:1;translate:0 0}}
@media print{
  [data-reveal].reveal-pending,[data-reveal].reveal-pending > *,[data-reveal].is-revealing,[data-reveal].is-revealing > *{opacity:1!important;translate:none!important;animation:none!important}
}
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}
  [data-reveal].reveal-pending,[data-reveal].reveal-pending > *,[data-reveal].is-revealing,[data-reveal].is-revealing > *{opacity:1!important;translate:none!important;animation:none!important}
}

/* ---------- 16. 404 (v2: editorial band, display-type "404" from the existing eyebrow) ---------- */
.error-404{display:flex;align-items:center;min-height:clamp(440px,72vh,780px)}
.error-404__inner{position:relative;z-index:1;max-width:44rem}
.error-404__rule{max-width:22rem;margin-bottom:28px}
.error-404__title{margin:0}
.error-404__desc{margin-top:16px;max-width:36em;font-size:var(--fs-body);line-height:1.6;color:rgba(var(--c-ink-rgb),.78)}
.error-404__actions{margin-top:32px}
/* The "404" outline sits wholly inside a lime parallelogram slab (the site's diagonal cut) that bleeds off the
   bottom-right corner; the slab is sized from the type so its slanted edge always clears the glyphs. */
.error-404{--e404-dt:clamp(150px,38vw,300px);--e404-pad:clamp(32px,8vw,120px)}
.error-404__display{right:-.06em;bottom:-.14em;--dt-size:var(--e404-dt)}
@media (max-width:767.98px){.error-404{padding-bottom:calc(var(--section-y) + 110px)}}
@media (min-width:1200px){.error-404{--e404-dt:clamp(300px,26vw,440px)}}
.error-404__field{right:0;bottom:0;--shape-w:calc(var(--e404-dt) * 1.92 + var(--e404-pad));--shape-h:calc(var(--e404-dt) * .66 + var(--e404-pad) * .6);--slab-cut:var(--e404-pad)}
@media (min-width:1200px){.error-404__desc{margin-top:20px}.error-404__actions{margin-top:40px}.error-404__rule{margin-bottom:40px}}

/* =====================================================================================================================
   17. VISUAL SYSTEM v2 (client, 24 Sep): editorial, premium, print-inspired. No glow / blur / rings / doodles.
   17.1 bands · 17.2 texture · 17.3 display type · 17.4 marks · 17.5 colour fields · 17.6 photo treatments
   17.7 scroll (reveal mask/lines, parallax, progress, stack, reading progress) · 17.8 hover/press · 17.9 header/footer
   17.10 media slots. Every scroll effect: no scroll listeners, static + visible under reduced motion and without JS.
   ===================================================================================================================== */

/* ---- 17.1 Bands: full-bleed colour rhythm. <section class="band band--ink [band--lip|band--cut]"> > .container ----
   Each band sets tokens its children read: --band-bg/-fg, --band-em (heading <em>), --band-eyebrow, --band-accent
   (links, marks, progress), --band-rule (hairlines), --band-focus (focus ring), --dt-stroke/--dt-fill (display type). */
.band,:is(.surface-cream,.surface-lime-50,.surface-lime){--band-fg:var(--c-ink);--band-em:var(--c-green);--band-eyebrow:var(--c-green);--band-accent:var(--c-green);--band-rule:rgba(var(--c-ink-rgb),.16);--band-focus:var(--c-ink);--band-pattern:var(--c-ink);--dt-stroke:rgba(var(--c-green-rgb),.6);--dt-fill:rgba(var(--c-ink-rgb),.05);--hl-c:var(--c-lime)}
.band{--band-bg:var(--c-cream);position:relative;isolation:isolate;overflow:hidden;overflow:clip;padding-block:var(--section-y);background:var(--band-bg);color:var(--band-fg)}
.band > .container{position:relative;z-index:1}
.band--cream{--band-bg:var(--c-cream)}
/* Warm band = lime since 24 Sep: it takes the lime band's contrast tokens (ink accents, olive marker bar). */
.band--warm{--band-bg:var(--c-cream-warm);--band-em:var(--c-ink);--band-eyebrow:var(--c-ink);--band-accent:var(--c-ink);--band-rule:rgba(var(--c-ink-rgb),.2);--dt-stroke:rgba(var(--c-ink-rgb),.2);--dt-fill:rgba(var(--c-ink-rgb),.06);--hl-c:var(--c-green)}
.band--warm :is(h1,h2,h3,h4,.h1,.h2,.h3,.h4) em:not(.hl-swipe){font-weight:900}
.band--lime50{--band-bg:var(--c-cream-warm)} /* mint retired: legacy class renders warm cream */
/* Olive on lime is 2.0:1, so on the lime band highlights, eyebrows and accents turn ink (13:1); the marker bar turns olive. */
.band--lime{--band-bg:var(--c-cream-warm);--band-em:var(--c-ink);--band-eyebrow:var(--c-ink);--band-accent:var(--c-ink);--band-rule:rgba(var(--c-ink-rgb),.2);--dt-stroke:rgba(var(--c-ink-rgb),.2);--dt-fill:rgba(var(--c-ink-rgb),.06);--hl-c:var(--c-green)}
.band--ink,.surface-dark{--band-bg:var(--c-ink);--band-fg:var(--c-cream);--band-em:var(--c-lime);--band-eyebrow:var(--c-lime);--band-accent:var(--c-lime);--band-rule:rgba(255,250,243,.16);--band-focus:var(--c-cream);--band-pattern:var(--c-cream);--dt-stroke:rgba(var(--c-lime-glow),.32);--dt-fill:rgba(255,250,243,.05);--hl-c:var(--c-lime)}
/* Ink on olive is 6.6:1; cream on olive is only 2.2:1, so the olive band is ink-on-olive throughout. */
.band--olive{--band-bg:var(--c-green);--band-em:var(--c-ink);--band-eyebrow:var(--c-ink);--band-accent:var(--c-ink);--band-rule:rgba(var(--c-ink-rgb),.24);--dt-stroke:rgba(var(--c-ink-rgb),.3);--dt-fill:rgba(var(--c-ink-rgb),.07);--hl-c:var(--c-lime)}
.band :is(h1,h2,h3,h4,.h1,.h2,.h3,.h4,.lead-form__title) em:not(.hl-swipe){color:var(--band-em)}
/* On lime/olive the (ink) highlight keeps a distinction: heavier weight + a thin rule under it. */
.band:is(.band--lime,.band--olive) :is(h1,h2,h3,h4,.h1,.h2,.h3,.h4) em:not(.hl-swipe){font-weight:900;text-decoration:underline;text-decoration-thickness:.06em;text-underline-offset:.16em}
.band .eyebrow{color:var(--band-eyebrow)}
.band :focus-visible{outline-color:var(--band-focus)}
.band :is(.prose a,.faq__a a,.form__note a,.link-arrow,.btn--link){text-decoration-color:var(--band-accent)}
.band .link-arrow .icon-arrow{color:var(--band-accent)}
.band--ink .link-arrow,.band--ink .btn--link{color:var(--c-cream)}
.band--ink .text-muted,.band--ink .section-heading__desc{color:rgba(255,250,243,.8)}
/* Buttons per band (text always ≥ 4.5:1; the hover sweep colour always differs from the band). */
.band--ink .btn:not(.btn--ghost,.btn--link,.btn--dark,.btn--lime){--btn-bg-h:var(--c-lime);--btn-fg-h:var(--c-ink);--btn-bd-h:var(--c-lime)}
.band--ink .btn--dark{--btn-bg:var(--c-cream);--btn-fg:var(--c-ink);--btn-bd:var(--c-cream);--btn-bg-h:var(--c-lime);--btn-fg-h:var(--c-ink);--btn-bd-h:var(--c-lime)}
.band--ink .btn--ghost{--btn-fg:var(--c-cream);--btn-bd:var(--c-cream);--btn-bg-h:var(--c-cream);--btn-fg-h:var(--c-ink);--btn-bd-h:var(--c-cream)}
.band--olive .btn:not(.btn--ghost,.btn--link,.btn--lime){--btn-bg:var(--c-ink);--btn-fg:var(--c-lime);--btn-bd:var(--c-ink);--btn-bg-h:var(--c-cream);--btn-fg-h:var(--c-ink);--btn-bd-h:var(--c-cream)}
.band--olive .btn--ghost{--btn-fg:var(--c-ink);--btn-bd:var(--c-ink);--btn-bg-h:var(--c-ink);--btn-fg-h:var(--c-lime);--btn-bd-h:var(--c-ink)}
.band--lime .btn--lime{--btn-bg:var(--c-ink);--btn-fg:var(--c-lime);--btn-bd:var(--c-ink);--btn-bg-h:var(--c-green);--btn-fg-h:var(--c-ink);--btn-bd-h:var(--c-green)}
/* Top edges. --lip: rounded top corners that overlap the previous section; --cut: a clean diagonal top edge.
   Both overlap by --band-overlap and sit above the previous section (z-index 1); the previous section gets the same
   amount of extra bottom padding so its content is never covered. --cut-rev slopes the other way. */
.band--lip,.band--cut{--band-overlap:28px;z-index:1;margin-top:calc(var(--band-overlap) * -1)}
.band--lip{border-radius:var(--band-overlap) var(--band-overlap) 0 0}
.band--cut{--band-overlap:clamp(32px,5vw,72px);padding-top:calc(var(--section-y) + var(--band-overlap));clip-path:polygon(0 var(--band-overlap),100% 0,100% 100%,0 100%)}
.band--cut-rev{clip-path:polygon(0 0,100% var(--band-overlap),100% 100%,0 100%)}
:is(.section,.band,main > section):has(+ .band--lip){padding-bottom:calc(var(--section-y) + 28px)}
:is(.section,.band,main > section):has(+ .band--cut){padding-bottom:calc(var(--section-y) + clamp(32px,5vw,72px))}
@media (min-width:768px){
  .band--lip{--band-overlap:40px}
  :is(.section,.band,main > section):has(+ .band--lip){padding-bottom:calc(var(--section-y) + 40px)}
}

/* ---- 17.2 Texture & pattern (aria-hidden pseudo-elements, clipped to the element) ----
   .grain (§8b) · .pattern-hairline: 1px parallel hairlines at 10px pitch, ~6% · --diag (45°) · --fade (fades out
   toward the left, so the pattern sits on the right-hand side only). One patterned band per page. */
.pattern-hairline{position:relative;isolation:isolate}
.pattern-hairline::before{content:"";position:absolute;inset:0;z-index:0;pointer-events:none;border-radius:inherit;opacity:.06;background:repeating-linear-gradient(180deg,var(--band-pattern,currentColor) 0 1px,transparent 1px 10px)}
.pattern-hairline--diag::before{background:repeating-linear-gradient(135deg,var(--band-pattern,currentColor) 0 1px,transparent 1px 10px)}
.pattern-hairline--fade::before{-webkit-mask-image:linear-gradient(to left,#000 0,#000 25%,transparent 70%);mask-image:linear-gradient(to left,#000 0,#000 25%,transparent 70%)}
.pattern-hairline > .container{position:relative;z-index:1}
@media print{.pattern-hairline::before{display:none}}

/* ---- 17.3 Editorial display type: tmm_display_type( $text, 'my__dt', 'outline'|'solid' ) ----
   Absolute, positioned by the caller class (top/right/bottom/left), cropped by the band (overflow clip). Size via
   --dt-size. Colour follows the band tokens. Put it as a direct child of the band/section, before .container. */
.display-type{position:absolute;z-index:0;pointer-events:none;user-select:none;-webkit-user-select:none;display:block;margin:0;font-family:var(--font);font-size:var(--dt-size,clamp(120px,24vw,320px));font-style:normal;font-weight:900;line-height:.8;letter-spacing:-.045em;white-space:nowrap;text-transform:none;text-wrap:nowrap}
.display-type--outline{color:transparent;-webkit-text-stroke:var(--dt-stroke-w,1.5px) var(--dt-stroke,rgba(var(--c-green-rgb),.6))}
.display-type--solid{color:var(--dt-fill,rgba(var(--c-ink-rgb),.05))}
@supports not (-webkit-text-stroke:1px #000){.display-type--outline{color:var(--dt-fill,rgba(var(--c-ink-rgb),.05))}}
@media (min-width:1200px){.display-type--outline{--dt-stroke-w:2px}}
/* Marquee variant (brand name): a full-width strip that runs slowly sideways, like the footer mega type. */
.display-type--marquee{left:0;right:0;width:auto}
.display-type--marquee .marquee{--marquee-gap:.4em;overflow:hidden}
.display-type--marquee .marquee__group span{display:block;padding-inline-end:0}
@media (prefers-reduced-motion:reduce){.display-type--marquee .marquee{flex-wrap:nowrap}}

/* ---- 17.4 Precise print marks: tmm_mark( 'crop'|'caret'|'insert'|'pilcrow'|'rule-index', $class ) ----
   Geometric 1.5px strokes, olive by default (band accent: ink on lime/olive, lime on ink). Modifiers:
   tmm-mark--ink | --olive | --cream | --lime · tmm-mark--draw (draws in on first view). Small marks are absolute
   when the caller positions them (next to a heading, in whitespace, never over text); .has-mark = position:relative. */
.has-mark{position:relative}
.tmm-mark{pointer-events:none;flex:none;color:var(--band-accent,var(--c-green))}
svg.tmm-mark{display:inline-block;width:24px;height:auto;max-width:none;overflow:visible;vertical-align:middle}
.tmm-mark--insert,.tmm-mark--pilcrow{width:20px}
.tmm-mark path{fill:none;stroke:currentColor;vector-effect:non-scaling-stroke}
@media (min-width:1200px){svg.tmm-mark{width:30px}.tmm-mark--insert,.tmm-mark--pilcrow{width:24px}}
.tmm-mark--ink{color:var(--c-ink)}.tmm-mark--olive{color:var(--c-green)}.tmm-mark--cream{color:var(--c-cream)}.tmm-mark--lime{color:var(--c-lime)}
/* Crop / registration marks: <div class="has-crop-marks"><img …><?= tmm_mark('crop') ?></div>. The wrapper reserves
   --crop padding on every side, so the marks sit in that gutter just outside the photo corners (never outside the
   wrapper, never over the photo). */
.has-crop-marks{--crop:14px;position:relative;display:block;padding:var(--crop)}
.has-crop-marks > :not(.tmm-mark){position:relative;z-index:1}
@media (min-width:1200px){.has-crop-marks{--crop:18px}}
.tmm-mark--crop{position:absolute;inset:0;z-index:2;display:block}
.tmm-mark__corner{position:absolute;display:block;width:calc(var(--crop,14px) * 2);height:calc(var(--crop,14px) * 2)}
.tmm-mark__corner svg{display:block;width:100%;height:100%;overflow:visible}
.tmm-mark__corner--tl{top:0;left:0}
.tmm-mark__corner--tr{top:0;right:0;scale:-1 1}
.tmm-mark__corner--br{right:0;bottom:0;scale:-1 -1}
.tmm-mark__corner--bl{bottom:0;left:0;scale:1 -1}
/* Index rule: hairline + short thick accent segment at its start. Block-level, full width of its box. */
.tmm-mark--rule-index{position:relative;display:block;width:100%;height:4px}
.tmm-mark__hair{position:absolute;left:0;right:0;top:50%;height:1px;margin-top:-.5px;background:var(--band-rule,rgba(var(--c-ink-rgb),.16));transform-origin:left center}
.tmm-mark__seg{position:absolute;left:0;top:.5px;width:clamp(36px,10%,72px);height:3px;background:currentColor;transform-origin:left center;transition:width .7s cubic-bezier(.65,0,.35,1)}
/* Client, 24 Sep: the segment runs the full length of the rule when its card is hovered / focused (not a fixed stub). */
@media (hover:hover){:is(.card-hover,.card-link,.row-hover,.rule-host):hover .tmm-mark--rule-index .tmm-mark__seg{width:100%}}
:is(.card-hover,.card-link,.row-hover,.rule-host):focus-within .tmm-mark--rule-index .tmm-mark__seg{width:100%}
/* Draw-in (main.js measures --dash per path; without it the declarations are invalid and the mark simply shows). */
.deco-draw-armed .tmm-mark--draw path{stroke-dasharray:var(--dash) var(--dash)}
.deco-draw-armed .tmm-mark--draw:not(.is-drawn) path{stroke-dashoffset:var(--dash)}
.deco-draw-armed .tmm-mark--draw.is-drawn path{stroke-dashoffset:0;transition:stroke-dashoffset .8s cubic-bezier(.45,.05,.25,1) .15s}
.deco-draw-armed .tmm-mark--draw.is-drawn path + path{transition-delay:.4s}
.deco-draw-armed .tmm-mark--draw.is-done path{stroke-dasharray:none;stroke-dashoffset:0;transition:none}
.deco-draw-armed .tmm-mark--rule-index.tmm-mark--draw:not(.is-drawn) :is(.tmm-mark__hair,.tmm-mark__seg){transform:scaleX(0)}
.deco-draw-armed .tmm-mark--rule-index.tmm-mark--draw.is-drawn .tmm-mark__hair{transition:transform 1.1s cubic-bezier(.45,.05,.25,1)}
.deco-draw-armed .tmm-mark--rule-index.tmm-mark--draw.is-drawn .tmm-mark__seg{transition:transform .6s cubic-bezier(.45,.05,.25,1) .35s}
.tmm-mark.is-drawn-now *{transition:none!important}
@media (prefers-reduced-motion:reduce){.tmm-mark path{stroke-dasharray:none!important;stroke-dashoffset:0!important}.tmm-mark *{transform:none!important;transition:none!important}}
@media print{.tmm-mark path{stroke-dasharray:none!important;stroke-dashoffset:0!important}.tmm-mark *{transform:none!important}}

/* Marker highlight (v2): a flat lime bar with straight edges behind the lower half of the words, drawing left → right
   on first view (main.js). <em class="hl-swipe">…</em>. Text stays ink on every band. */
.hl-swipe{font-style:normal;color:var(--c-ink);padding:0 .06em;margin:0;background-image:linear-gradient(var(--hl-c,var(--c-lime)),var(--hl-c,var(--c-lime)));background-repeat:no-repeat;background-position:0 88%;background-size:100% .5em;-webkit-box-decoration-break:clone;box-decoration-break:clone}
h1 .hl-swipe,h2 .hl-swipe,h3 .hl-swipe,h4 .hl-swipe,.h1 .hl-swipe,.h2 .hl-swipe,.h3 .hl-swipe,.h4 .hl-swipe,:is(.surface-dark,.band--ink) .hl-swipe,:is(.surface-dark,.band--ink) :is(h1,h2,h3,h4,.h1,.h2,.h3,.h4,.lead-form__title) .hl-swipe{color:var(--c-ink)}
/* On ink the text is ink too, so the bar covers the full line height there (a clean lime block, straight edges). */
:is(.surface-dark,.band--ink) .hl-swipe{--hl-h:1.1em;background-position:0 60%}
.hl-swipe{background-size:100% var(--hl-h,.5em)}
.deco-draw-armed .hl-swipe:not(.is-drawn){background-size:0% var(--hl-h,.5em)}
.deco-draw-armed .hl-swipe.is-drawn{transition:background-size .9s cubic-bezier(.65,0,.35,1) .15s}
.hl-swipe.is-drawn-now{transition:none!important}
@media (prefers-reduced-motion:reduce){.hl-swipe{background-size:100% var(--hl-h,.5em)!important;transition:none!important}}
@media print{.hl-swipe{background-size:100% var(--hl-h,.5em)!important}}

/* ---- 17.5 Colour fields: tmm_shape( 'field-1'…'field-4', 'my__field shape--lime' ) ----
   Large flat crisp-edged shapes (currentColor). Direct child of a band/section (before .container, which is z1), the
   caller class positions it to bleed off an edge; width via --shape-w. Inside an isolated wrapper use .shape--behind. */
.shape{position:absolute;z-index:0;pointer-events:none;display:block;max-width:none;width:var(--shape-w,clamp(280px,48vw,720px));height:auto;color:var(--c-lime)}
.shape--behind{z-index:-1}
.shape--lime{color:var(--c-lime)}.shape--lime50{color:var(--c-cream-warm)}.shape--warm{color:var(--c-cream-warm)}.shape--olive{color:var(--c-green)}.shape--ink{color:var(--c-ink)}
.shape--flip{scale:-1 1}.shape--flip-y{scale:1 -1}
/* 'slab': stretchable parallelogram cut (the .band--cut diagonal). Caller sets width + height (or inset). */
.shape--slab{height:var(--shape-h,auto);background:currentColor;clip-path:polygon(var(--slab-cut,18%) 0,100% 0,calc(100% - var(--slab-cut-end,0%)) 100%,0 100%)}
.shape--slab-rev{clip-path:polygon(0 0,calc(100% - var(--slab-cut,18%)) 0,100% 100%,0 100%)}

/* ---- 17.6 Photo treatments: .frame-offset (§8b) · .duotone · .img-zoom ---- */
/* Ink/olive duotone for secondary imagery (class screenshots stay natural): greyscale photo multiplied onto olive, then
   an ink layer "lightens" the shadows. Without blend-mode support the photo is simply greyscale. .duotone--hover
   returns to natural colour on hover/focus. */
.duotone{position:relative;isolation:isolate;overflow:hidden;background-color:var(--c-green)}
.duotone :is(img,video){filter:grayscale(1) contrast(1.08) brightness(1.04);mix-blend-mode:multiply;transition:filter .5s var(--ease)}
.duotone::after{content:"";position:absolute;inset:0;z-index:1;pointer-events:none;border-radius:inherit;background:var(--c-ink);mix-blend-mode:lighten;transition:opacity .5s var(--ease)}
@supports not (mix-blend-mode:lighten){.duotone::after{display:none}.duotone :is(img,video){mix-blend-mode:normal}}
@media (hover:hover){.duotone--hover:hover :is(img,video){filter:none;mix-blend-mode:normal}.duotone--hover:hover::after{opacity:0}}
.duotone--hover:focus-within :is(img,video){filter:none;mix-blend-mode:normal}
.duotone--hover:focus-within::after{opacity:0}
/* Image zoom inside its frame: put .img-zoom on the frame (it clips); zooms on its own hover or its card/link hover. */
.img-zoom{overflow:hidden}
.img-zoom :is(img,video){transition:transform .8s var(--ease);transform-origin:50% 50%}
@media (hover:hover){.img-zoom:hover :is(img,video),:is(.card-hover,.card-link,a):hover .img-zoom :is(img,video){transform:scale(1.04)}}
:is(.card-hover,.card-link,a):focus-visible .img-zoom :is(img,video),.card-hover:has(:focus-visible) .img-zoom :is(img,video){transform:scale(1.04)}

/* ---- 17.7 Scroll ---- */
/* data-reveal="mask": clip-path wipe left → right (images, cards). The CHILDREN are clipped, not the observed element
   (a fully clipped target would never report an intersection). Put it on the frame/wrapper around the image or card. */
[data-reveal="mask"].reveal-pending > *{clip-path:inset(0 100% 0 0)}
[data-reveal="mask"].is-revealing > *{animation:tmm-mask 1.1s cubic-bezier(.65,0,.35,1) both}
@keyframes tmm-mask{from{clip-path:inset(0 100% 0 0)}to{clip-path:inset(0 0 0 0)}}
/* data-reveal="lines": main.js wraps each word in .rl-w > .rl-i after fonts load (text stays in the DOM, same wrapping)
   and sets --line; each line rises out of its own mask, line by line. */
.rl-w{display:inline-block;clip-path:inset(-.45em -.3em -.4em -.3em)}
.rl-i{display:inline-block}
[data-reveal="lines"].reveal-pending .rl-i{translate:0 145%}
[data-reveal="lines"].is-revealing .rl-i{animation:tmm-line .9s var(--ease) both;animation-delay:calc(var(--line,0) * 90ms)}
@keyframes tmm-line{from{translate:0 145%}to{translate:0 0}}
@media print,(prefers-reduced-motion:reduce){
  [data-reveal="mask"].reveal-pending > *,[data-reveal="mask"].is-revealing > *{clip-path:none!important;animation:none!important}
  [data-reveal="lines"] .rl-i{translate:none!important;animation:none!important}
}
/* Parallax (decoration and photos, ≤ 24px): CSS scroll-driven animation; static where unsupported / reduced motion.
   Uses `translate` — don't combine with [data-reveal] on the same element (wrap it). */
/* Parallax retired (client, 24 Sep round 4: scroll effects read as dated). .parallax is now inert. */
@keyframes tmm-parallax{from{translate:0 var(--parallax,24px)}to{translate:0 calc(var(--parallax,24px) * -1)}}
/* [data-progress]: a hairline track that fills as the viewport centre moves through it. Vertical by default (caller
   sets its height/position, e.g. along a timeline); data-progress="x" is horizontal. Scroll-driven where supported,
   else main.js fills it once on view (html.progress-io); reduced motion / no JS: shown full. */
[data-progress]{position:relative;display:block;background:var(--band-rule,rgba(var(--c-ink-rgb),.16));view-timeline:--tmm-progress block;view-timeline-inset:50% 50%}
[data-progress]:not([data-progress="x"]){width:2px}
[data-progress="x"]{height:2px;view-timeline:--tmm-progress block}
[data-progress]::after{content:"";position:absolute;inset:0;background:var(--band-accent,var(--c-green));transform-origin:50% 0}
[data-progress="x"]::after{transform-origin:0 50%}
/* Scroll-driven fill retired (round 4): the progress line is drawn full, static. */
@keyframes tmm-progress-y{from{transform:scaleY(0)}to{transform:scaleY(1)}}
@keyframes tmm-progress-x{from{transform:scaleX(0)}to{transform:scaleX(1)}}
/* .stack-cards: children stick under the header and stack as you scroll; covered cards scale down slightly (main.js
   adds .is-covered via IntersectionObserver). Tablet/desktop only; off under reduced motion. Children need an opaque
   background. Sticky works because .section/.band clip with overflow:clip (not a scroll container). */
/* Sticky stack retired (round 4): kept behind a never-matching query so the cards simply follow each other. */
@media (min-width:99999px){
  .stack-cards > *{position:sticky;top:calc(var(--header-h) + 24px + var(--stack-i,0) * 16px);transform-origin:50% 0;transition:scale .45s var(--ease),filter .45s var(--ease)}
  .stack-cards > :nth-child(2){--stack-i:1}.stack-cards > :nth-child(3){--stack-i:2}.stack-cards > :nth-child(4){--stack-i:3}
  .stack-cards > :nth-child(5){--stack-i:4}.stack-cards > :nth-child(6){--stack-i:5}.stack-cards > :nth-child(n+7){--stack-i:6}
  .stack-cards > .is-covered{scale:.96;filter:brightness(.97)}
}
/* Reading progress (long pages): thin olive bar at the top of the viewport, scroll-driven; nothing where unsupported
   or under reduced motion. Output by header.php on the legal pages (filter 'tmm_reading_progress'). */
.reading-progress{display:none;position:fixed;left:0;right:0;top:0;z-index:101;height:3px;background:var(--c-green);transform-origin:0 50%;transform:scaleX(0);pointer-events:none}
.admin-bar .reading-progress{top:var(--wp-admin--admin-bar--height,0px)}
@media (prefers-reduced-motion:no-preference){
  @supports (animation-timeline:scroll()){.reading-progress{display:block;animation:tmm-read linear both;animation-timeline:scroll(root block)}}
}
@keyframes tmm-read{to{transform:scaleX(1)}}
@media print{.reading-progress{display:none!important}}

/* ---- 17.8 Hover & press (pointer hover only; every hover has a matching :focus-visible; press = scale) ---- */
.card-hover{transition:transform .35s var(--ease),box-shadow .35s var(--ease),border-color .35s var(--ease)}
@media (hover:hover){.card-hover:hover{transform:translateY(-4px);box-shadow:0 2px 4px rgba(var(--c-ink-rgb),.03),0 28px 56px -30px rgba(var(--c-ink-rgb),.32);border-color:rgba(var(--c-green-rgb),.7)}}
.card-hover:has(:focus-visible),.card-hover:focus-visible{transform:translateY(-4px);box-shadow:0 2px 4px rgba(var(--c-ink-rgb),.03),0 28px 56px -30px rgba(var(--c-ink-rgb),.32);border-color:rgba(var(--c-green-rgb),.7)}
.card-hover:active{transform:translateY(-1px) scale(.98);transition-duration:.12s}
@media (hover:hover){.band--ink .card-hover:hover,.surface-dark .card-hover:hover{box-shadow:0 12px 32px -12px rgba(0,0,0,.5);border-color:var(--c-lime)}}
/* Rows in lists / tables: a faint ink tint + olive left rule (no mint). On tables put .row-hover on the <tr>. */
.row-hover{transition:background-color .25s var(--ease),box-shadow .25s var(--ease)}
@media (hover:hover){
  .row-hover:hover{background-color:rgba(var(--c-ink-rgb),.035);box-shadow:inset 3px 0 0 var(--c-green)}
  tr.row-hover:hover > :is(td,th){background-color:rgba(var(--c-ink-rgb),.035)}
  tr.row-hover:hover{box-shadow:none}
  tr.row-hover:hover > :first-child{box-shadow:inset 3px 0 0 var(--c-green)}
  :is(.band--ink,.surface-dark) .row-hover:hover,:is(.band--ink,.surface-dark) tr.row-hover:hover > :is(td,th){background-color:rgba(255,250,243,.06)}
  :is(.band--ink,.surface-dark) .row-hover:hover,:is(.band--ink,.surface-dark) tr.row-hover:hover > :first-child{box-shadow:inset 3px 0 0 var(--c-lime)}
}
.row-hover:has(:focus-visible){background-color:rgba(var(--c-ink-rgb),.035);box-shadow:inset 3px 0 0 var(--c-green)}
:is(.band--ink,.surface-dark) .row-hover:has(:focus-visible){background-color:rgba(255,250,243,.06);box-shadow:inset 3px 0 0 var(--c-lime)}
/* Press on controls */
.carousel-btn:active:not(:disabled),.menu-toggle:active,.back-to-top:active{transform:scale(.96)}

/* ---- 17.9 Header condense + footer v2 ---- */
/* After ~120px of scroll (IntersectionObserver sentinel in main.js, no scroll listener) the header gets shorter and a
   hairline shadow. The height it loses is given back as margin-bottom, so the content below never jumps. */
:root{--header-shrink:12px}
@media (min-width:1200px){:root{--header-shrink:28px}}
.site-header{transition:box-shadow .3s var(--ease),margin-bottom .3s var(--ease),background-color .3s var(--ease)}
.header-inner{transition:min-height .3s var(--ease)}
.site-header.is-scrolled{margin-bottom:var(--header-shrink);box-shadow:0 1px 0 rgba(var(--c-ink-rgb),.04),0 12px 28px -22px rgba(var(--c-ink-rgb),.45)}
.site-header.is-scrolled .header-inner{min-height:calc(var(--header-h) - var(--header-shrink))}
@media (prefers-reduced-motion:reduce){.site-header,.header-inner{transition:none}}
.header-sentinel{position:absolute;top:0;left:0;width:1px;height:120px;pointer-events:none;visibility:hidden}
/* Footer: ink band, index rule, hairline-structured columns, sliding lime underline on links, outline mega marquee. */
.footer-rule{margin-bottom:32px}
.footer-bottom{border-top:1px solid rgba(255,250,243,.14);padding-top:16px;margin-top:12px}
.footer-nav .menu a,.footer-legal a,.footer-contact__list a{position:relative}
.footer-nav .menu a::after,.footer-legal a::after,.footer-contact__list a::after{content:"";position:absolute;left:0;right:0;top:calc(50% + .8em);height:1px;background:var(--c-lime);transform:scaleX(0);transform-origin:right center;transition:transform .35s var(--ease)}
@media (hover:hover){.footer-nav .menu a:hover::after,.footer-legal a:hover::after,.footer-contact__list a:hover::after{transform:scaleX(1);transform-origin:left center}}
.footer-nav .menu a:focus-visible::after,.footer-legal a:focus-visible::after,.footer-contact__list a:focus-visible::after{transform:scaleX(1)}
.footer-social a{transition:color .2s var(--ease),transform .2s var(--ease)}
@media (hover:hover){.footer-social a:hover{transform:translateY(-2px)}}
.footer-social a:active{transform:scale(.96)}
.footer-mega{border-top:1px solid rgba(255,250,243,.14)}
.footer-mega .marquee{color:transparent;-webkit-text-stroke:1.5px rgba(var(--c-lime-glow),.34)}
@supports not (-webkit-text-stroke:1px #000){.footer-mega .marquee{color:rgba(255,250,243,.07)}}
@media (min-width:1200px){.footer-rule{margin-bottom:48px}.footer-bottom{margin-top:28px;padding-top:24px}.footer-mega .marquee{-webkit-text-stroke-width:2px}}

/* ---- 17.10 Media slots: tmm_media_slot( $key, $args ) (docs/MEDIA-SLOTS.md) ---- */
.media-slot{display:block;width:100%;height:auto;object-fit:cover;border-radius:inherit}
.media-slot--empty{display:grid;place-items:center;background:repeating-linear-gradient(135deg,rgba(var(--c-ink-rgb),.035) 0 1px,transparent 1px 10px),var(--c-cream);border:1px solid rgba(var(--c-ink-rgb),.1);color:rgba(var(--c-ink-rgb),.3)}
.media-slot__icon{width:36px;height:36px}
:is(.band--ink,.surface-dark) .media-slot--empty{background:repeating-linear-gradient(135deg,rgba(255,250,243,.05) 0 1px,transparent 1px 10px),rgba(255,250,243,.04);border-color:rgba(255,250,243,.12);color:rgba(255,250,243,.3)}

/* =====================================================================================================================
   18. ROUND 4 (client, 24 Sep): no scroll-triggered animation. Motion moves to navigation (view transitions), touch
   (swipe rails), hover (tilt) and an app-like mobile action bar.
   ===================================================================================================================== */
/* 18.1 Page-to-page: cross-document view transitions (Chrome/Edge/Safari 18.2+; others navigate normally). The header
   and footer stay put; the page content cross-fades with a short lift. */
@view-transition{navigation:auto}
.site-header{view-transition-name:site-header}
::view-transition-old(root){animation:tmm-vt-out .22s var(--ease) both}
::view-transition-new(root){animation:tmm-vt-in .42s var(--ease) both}
@keyframes tmm-vt-out{to{opacity:0;transform:translateY(-8px)}}
@keyframes tmm-vt-in{from{opacity:0;transform:translateY(14px)}}
@media (prefers-reduced-motion:reduce){::view-transition-old(root),::view-transition-new(root){animation:none}}

/* 18.2 Swipe rails — opt-in since round 6 (.rail--swipe: course lists, CEFR levels, careers jobs). (phones/tablets < 1024): a card list becomes a horizontal, snap-scrolling rail that bleeds to the
   screen edges with the next card peeking; a thin progress track under it follows the swipe (main.js). ≥1024 the
   list keeps its own grid. Markup: add .rail--swipe to the list; optional --rail-w (card width). */
@media (max-width:1023.98px){
  .rail--swipe{display:grid!important;grid-auto-flow:column;grid-template-columns:none!important;grid-auto-columns:var(--rail-w,min(80%,340px));gap:12px!important;
    margin-inline:calc(var(--gutter) * -1)!important;padding:4px var(--gutter) 8px!important;overflow-x:auto;overscroll-behavior-x:contain;scroll-snap-type:x mandatory;scroll-padding-inline:var(--gutter);scrollbar-width:none;-webkit-overflow-scrolling:touch}
  .rail--swipe::-webkit-scrollbar{display:none}
  .rail--swipe > *{scroll-snap-align:start;min-width:0}
  .rail--swipe:focus-visible{outline:3px solid var(--band-focus,var(--c-ink));outline-offset:2px}
  .rail-meter{position:relative;height:3px;margin:14px 0 0;border-radius:3px;background:var(--band-rule,rgba(var(--c-ink-rgb),.14));overflow:hidden}
  .rail-meter__bar{position:absolute;top:0;bottom:0;left:0;width:var(--rail-size,30%);border-radius:inherit;background:var(--band-accent,var(--c-green));transform:translateX(var(--rail-x,0%));transition:transform .12s linear}
}
@media (min-width:768px) and (max-width:1023.98px){.rail--swipe{--rail-w:min(46%,360px)}}
@media (min-width:1024px){.rail-meter{display:none}}

/* 18.3 Mobile action bar: a floating pill bar (primary CTA + call) at the bottom of the screen on phones/tablets. */
.m-cta{position:fixed;left:12px;right:12px;bottom:calc(12px + env(safe-area-inset-bottom,0px));z-index:95;display:flex;gap:8px;padding:6px;border-radius:var(--radius-pill);background:var(--c-ink);box-shadow:0 18px 40px -16px rgba(var(--c-ink-rgb),.6);transform:translateY(calc(100% + 24px));opacity:0;visibility:hidden;transition:transform .45s var(--ease),opacity .3s var(--ease),visibility 0s linear .45s}
.m-cta.is-shown{transform:none;opacity:1;visibility:visible;transition:transform .45s var(--ease),opacity .3s var(--ease),visibility 0s}
.m-cta__main{flex:1;min-height:48px;--btn-bg:var(--c-lime);--btn-bd:var(--c-lime);--btn-fg:var(--c-ink);--btn-bg-h:var(--c-cream);--btn-bd-h:var(--c-cream);--btn-fg-h:var(--c-ink)}
.m-cta__main:focus-visible,.m-cta__call:focus-visible{outline:3px solid var(--c-cream);outline-offset:2px}
.m-cta__call{display:inline-grid;place-items:center;flex:none;width:48px;height:48px;border:1px solid rgba(255,250,243,.3);border-radius:var(--radius-pill);color:var(--c-lime);transition:background-color .2s var(--ease),transform .15s var(--ease)}
.m-cta__call .icon{width:22px;height:22px}
.m-cta__call:active{transform:scale(.94);background:rgba(255,250,243,.1)}
@media (min-width:1024px){.m-cta{display:none}}
/* The bar replaces the back-to-top button on phones/tablets. */
@media (max-width:1023.98px){.js .back-to-top{display:none}body{padding-bottom:0}}
@media (prefers-reduced-motion:reduce){.m-cta{transition:none}}
@media print{.m-cta{display:none}}

/* 18.4 Hover tilt (pointer desktops): cards lean a few degrees toward the cursor (main.js sets --tilt-x/--tilt-y). */
@media (hover:hover) and (pointer:fine) and (prefers-reduced-motion:no-preference){
  .card-hover.is-tilting{transform:perspective(1000px) rotateX(var(--tilt-y,0deg)) rotateY(var(--tilt-x,0deg)) translateY(-4px);transition:transform .12s linear,box-shadow .35s var(--ease),border-color .35s var(--ease)}
}

/* 18.5 FAQs (round 4): each question is its own rounded card; the open one turns ink with a lime "−". */
.faq__list{border-top:0;display:grid;gap:10px;counter-reset:faq}
.faq__item{counter-increment:faq;border:1px solid rgba(var(--c-ink-rgb),.1);border-bottom:1px solid rgba(var(--c-ink-rgb),.1);border-radius:var(--radius-card);background:var(--c-cream);transition:background-color .35s var(--ease),border-color .35s var(--ease),box-shadow .35s var(--ease)}
.band--cream .faq__item{background:var(--c-cream-warm)}
.faq__btn{align-items:center;padding:18px 20px;background:none!important;border-radius:inherit}
.faq__btn::before{content:counter(faq,decimal-leading-zero);position:static;flex:none;order:-1;width:auto;background:none;transform:none;font-size:13px;font-weight:800;letter-spacing:.06em;color:var(--c-green);font-variant-numeric:tabular-nums}
.faq__q-text{flex:1}
.faq__icon{display:grid;place-items:center;width:36px;height:36px;margin:0;border-radius:var(--radius-pill);background:var(--c-ink);color:var(--c-lime);transition:transform .45s var(--ease),background-color .3s var(--ease)}
.faq__icon::before,.faq__icon::after{left:11px;right:11px}
.faq__a{padding:0 64px 22px 54px}
@media (hover:hover){.faq__btn:hover{padding-left:20px;padding-inline-end:20px}.faq__item:not(.is-open):hover{border-color:rgba(var(--c-green-rgb),.8);box-shadow:0 20px 40px -30px rgba(var(--c-ink-rgb),.45)}.faq__item:not(.is-open) .faq__btn:hover .faq__icon{transform:rotate(90deg)}}
.faq__btn:focus-visible{padding-left:20px;padding-inline-end:20px;outline-offset:2px;border-radius:var(--radius-card)}
.faq__item.is-open{background:var(--c-ink);border-color:var(--c-ink)}
.faq__item.is-open .faq__q{color:var(--c-cream)}
.faq__item.is-open .faq__a{color:rgba(255,250,243,.82)}
.faq__item.is-open .faq__btn::before{color:var(--c-lime)}
.faq__item.is-open .faq__icon{background:var(--c-lime);color:var(--c-ink)}
.faq__item.is-open .faq__btn:focus-visible{outline-color:var(--c-cream)}
.faq__item.is-open .faq__a a{text-decoration-color:var(--c-lime)}
@media (max-width:767.98px){.faq__btn{gap:14px;padding:16px}.faq__a{padding:0 16px 20px 16px}.faq__icon{width:32px;height:32px}.faq__icon::before,.faq__icon::after{left:9px;right:9px}}
@media (min-width:1200px){.faq__btn{min-height:76px;padding:20px 26px;gap:22px}.faq__btn:hover,.faq__btn:focus-visible{padding-left:26px;padding-inline-end:26px}.faq__a{padding:0 80px 26px 72px}}
:is(.surface-dark,.band--ink) .faq__item{background:rgba(255,250,243,.05);border-color:rgba(255,250,243,.14)}

/* 18.6 Focus carousel (testimonials; client 24 Sep): the centre slide is always the largest; neighbours scale down
   with their distance from the centre (--focus 1 → 0, set per frame by main.js), so the focus follows a drag. The
   track bleeds to the screen edges and pads half a viewport on each side, so the first and last slides can centre. */
.fcar{--fw:min(72vw,320px);--fmin:.74;position:relative}
.fcar.fcar [data-carousel-track]{display:flex;align-items:center;gap:calc(var(--fw) * -.1);margin-inline:calc(50% - 50vw);padding:28px calc(50vw - var(--fw) / 2) 36px;scroll-snap-type:x mandatory;scroll-padding-inline:0;overflow-x:auto;scrollbar-width:none;cursor:grab}
.fcar [data-carousel-track]::-webkit-scrollbar{display:none}
.fcar [data-carousel-track].is-dragging{cursor:grabbing;scroll-snap-type:none;user-select:none}
.fcar .carousel-slide{--slide-w:var(--fw);flex:0 0 var(--fw);width:var(--fw);scroll-snap-align:center;transform:scale(calc(var(--fmin) + (1 - var(--fmin)) * var(--focus,0)));opacity:calc(.62 + .38 * var(--focus,0));transform-origin:50% 50%;will-change:transform}
.fcar:not(.is-focus-ready) .carousel-slide{--focus:1}
.fcar .carousel-slide img{pointer-events:none;-webkit-user-drag:none;border-radius:var(--radius-card);transition:box-shadow .4s var(--ease)}
.fcar .carousel-slide.is-centre img{box-shadow:0 2px 6px rgba(var(--c-ink-rgb),.06),0 40px 70px -40px rgba(var(--c-ink-rgb),.55)}
.fcar .carousel-slide:not(.is-centre){cursor:pointer}
.fcar .carousel__controls{justify-content:center}
@media (min-width:768px){.fcar{--fw:380px}}
@media (min-width:1200px){.fcar{--fw:440px;--fmin:.7}}
@media (prefers-reduced-motion:reduce){.fcar .carousel-slide{will-change:auto}}

/* 18.7 Button system (client, 24 Sep): three kinds only.
   A · light — white fill, green text (+ a fine green outline so it reads on cream).        .btn--ghost, .btn--lime
   B · green — green fill, white text. The olive #92B93C gives white text only 2.2:1, so the fill is the olive's
       deep shade --c-green-deep (4.6:1), the same hue.                                      .btn (default)
   C · ink   — ink fill, lime text (or white).                                              .btn--dark
   Hovers move to the next kind (A → B, B → C, C → B), so every hover is a clear colour change. */
.btn:not(.btn--link,.btn--ghost,.btn--dark,.btn--lime){--btn-bg:var(--c-green-deep);--btn-bd:var(--c-green-deep);--btn-fg:#fff;--btn-bg-h:var(--c-ink);--btn-bd-h:var(--c-ink);--btn-fg-h:var(--c-lime)}
.btn.btn--ghost,.btn.btn--lime{--btn-bg:#fff;--btn-bd:rgba(var(--c-green-rgb),.6);--btn-fg:var(--c-green-deep);--btn-bg-h:var(--c-green-deep);--btn-bd-h:var(--c-green-deep);--btn-fg-h:#fff}
.btn.btn--dark{--btn-bg:var(--c-ink);--btn-bd:var(--c-ink);--btn-fg:var(--c-lime);--btn-bg-h:var(--c-green-deep);--btn-bd-h:var(--c-green-deep);--btn-fg-h:#fff}
/* On the olive band the green fill would not separate from the band: the main button is ink there. */
.band--olive .btn:not(.btn--link,.btn--ghost,.btn--lime){--btn-bg:var(--c-ink);--btn-bd:var(--c-ink);--btn-fg:var(--c-lime);--btn-bg-h:#fff;--btn-bd-h:#fff;--btn-fg-h:var(--c-green-deep)}
/* On ink, an ink button would vanish: it becomes the light kind. */
:is(.band--ink,.surface-dark) .btn.btn--dark{--btn-bg:#fff;--btn-bd:#fff;--btn-fg:var(--c-green-deep);--btn-bg-h:var(--c-green-deep);--btn-bd-h:var(--c-green-deep);--btn-fg-h:#fff}
:is(.band--ink,.surface-dark) .btn:not(.btn--link,.btn--ghost,.btn--dark,.btn--lime){--btn-bg-h:#fff;--btn-bd-h:#fff;--btn-fg-h:var(--c-green-deep)}
:is(.band--ink,.surface-dark) .btn.btn--ghost{--btn-bg:#fff;--btn-bd:#fff;--btn-fg:var(--c-green-deep)}
.btn{font-weight:700}
.m-cta__main.btn{--btn-bg:var(--c-green-deep);--btn-bd:var(--c-green-deep);--btn-fg:#fff;--btn-bg-h:#fff;--btn-bd-h:#fff;--btn-fg-h:var(--c-green-deep)}
@media (max-width:1023.98px){.primary-nav .header-cta.btn{--btn-bg:var(--c-green-deep);--btn-bd:var(--c-green-deep);--btn-fg:#fff;--btn-bg-h:var(--c-ink);--btn-bd-h:var(--c-ink);--btn-fg-h:var(--c-lime)}}

/* 18.8 Glass, used sparingly (client, 24 Sep: allowed when it is tasteful). Only floating layers get it — the scrolled
   header, the mobile action bar, the home teacher credential, the teacher-profile backdrop and the course tabs.
   Content surfaces stay solid. Each has a solid fallback where backdrop-filter is unsupported. */
@supports ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .site-header.is-scrolled{background:rgba(252,253,239,.74);-webkit-backdrop-filter:saturate(1.6) blur(18px);backdrop-filter:saturate(1.6) blur(18px);border-bottom-color:rgba(var(--c-ink-rgb),.08)}
  .m-cta{background:rgba(50,35,31,.84);-webkit-backdrop-filter:saturate(1.4) blur(16px);backdrop-filter:saturate(1.4) blur(16px);box-shadow:inset 0 0 0 1px rgba(255,250,243,.08),0 18px 40px -16px rgba(var(--c-ink-rgb),.6)}
  .tprofile::backdrop{background:rgba(var(--c-ink-rgb),.38);-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px)}
  .cx-tabs__list{background:rgba(255,250,243,.7);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px)}
}
@media (min-width:1024px) and (hover:hover){
  @supports ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
    .hp-teacher .hp-teacher__cred{background:rgba(50,35,31,.78);-webkit-backdrop-filter:saturate(1.3) blur(14px);backdrop-filter:saturate(1.3) blur(14px);box-shadow:inset 0 0 0 1px rgba(255,250,243,.1),0 18px 40px -20px rgba(var(--c-ink-rgb),.6)}
  }
}
@media (prefers-reduced-transparency:reduce){
  .site-header.is-scrolled{background:var(--c-header);backdrop-filter:none;-webkit-backdrop-filter:none}
  .m-cta,.hp-teacher .hp-teacher__cred{background:var(--c-ink);backdrop-filter:none;-webkit-backdrop-filter:none}
  .cd-toc,.cx-tabs__list{background:var(--c-cream);backdrop-filter:none;-webkit-backdrop-filter:none}
}

/* 18.9 Reveals, renewed (round 5). One observer (main.js) still drives [data-reveal]; the looks are new:
   default → focus-in (blur 10px → 0, 14px rise) · stagger → cards rise with a slight backwards tilt, one after another ·
   mask → a diagonal wipe (the site's cut angle) while the image settles from 1.08 · lines → a heading focus-in with a
   touch of scale (no word splitting). Only elements that start below the fold animate; reduced motion shows all. */
@media (prefers-reduced-motion:no-preference){
  [data-reveal].reveal-pending:not([data-reveal="stagger"],[data-reveal="mask"]),[data-reveal="stagger"].reveal-pending > *{opacity:0;translate:none}
  [data-reveal].is-revealing:not([data-reveal="stagger"],[data-reveal="mask"],[data-reveal="lines"]){animation:tmm-focus-in .9s var(--ease) both}
  [data-reveal="lines"].is-revealing{animation:tmm-head-in 1s var(--ease) both}
  [data-reveal="stagger"].is-revealing > *{animation:tmm-tilt-rise .9s var(--ease) both}
  [data-reveal="stagger"].is-revealing > :nth-child(2){animation-delay:.09s}
  [data-reveal="stagger"].is-revealing > :nth-child(3){animation-delay:.18s}
  [data-reveal="stagger"].is-revealing > :nth-child(4){animation-delay:.27s}
  [data-reveal="stagger"].is-revealing > :nth-child(n+5){animation-delay:.34s}
  [data-reveal="mask"].reveal-pending > *{clip-path:polygon(0 0,0 0,0 100%,0 100%)}
  [data-reveal="mask"].is-revealing > *{animation:tmm-diag-wipe 1.15s cubic-bezier(.65,0,.25,1) both}
  [data-reveal="mask"].is-revealing :is(img,video){animation:tmm-settle 1.4s var(--ease) both}
}
@keyframes tmm-focus-in{from{opacity:0;filter:blur(10px);translate:0 14px}to{opacity:1;filter:blur(0);translate:0 0}}
@keyframes tmm-head-in{from{opacity:0;filter:blur(12px);scale:.97}to{opacity:1;filter:blur(0);scale:1}}
@keyframes tmm-tilt-rise{from{opacity:0;filter:blur(6px);transform:perspective(900px) rotateX(14deg) translateY(34px)}to{opacity:1;filter:blur(0);transform:none}}
@keyframes tmm-diag-wipe{0%{clip-path:polygon(0 0,0 0,0 100%,0 100%)}60%{clip-path:polygon(0 0,112% 0,88% 100%,0 100%)}100%{clip-path:polygon(0 0,100% 0,100% 100%,0 100%)}}
@keyframes tmm-settle{from{scale:1.08;filter:blur(4px)}to{scale:1;filter:blur(0)}}

/* 18.10 Deep green used sparingly (client, 24 Sep: "đừng dùng nhiều… hơi nặng nề"). Kind B buttons use the brand olive
   itself with bold white text (a faint ink text-shadow keeps the letters crisp); hover → ink with lime text.
   Deep green remains only as text colour (light buttons, the brand name in the audience heading). */
.btn:not(.btn--link,.btn--ghost,.btn--dark,.btn--lime){--btn-bg:var(--c-green);--btn-bd:var(--c-green);--btn-fg:#fff;text-shadow:0 1px 1px rgba(var(--c-ink-rgb),.28)}
.btn:not(.btn--link,.btn--ghost,.btn--dark,.btn--lime):is(:hover,:focus-visible){text-shadow:none}
.btn.btn--ghost,.btn.btn--lime{--btn-bg-h:var(--c-green);--btn-bd-h:var(--c-green);--btn-fg-h:#fff}
.btn.btn--dark{--btn-bg-h:var(--c-green);--btn-bd-h:var(--c-green)}
.m-cta__main.btn{--btn-bg:var(--c-green);--btn-bd:var(--c-green);text-shadow:0 1px 1px rgba(var(--c-ink-rgb),.28)}
@media (max-width:1023.98px){.primary-nav .header-cta.btn{--btn-bg:var(--c-green);--btn-bd:var(--c-green);text-shadow:0 1px 1px rgba(var(--c-ink-rgb),.28)}}
.cd-form .form-submit.btn{--btn-bg:var(--c-green);--btn-bd:var(--c-green)}
/* Readability: white on the brand olive is 2.2:1 (fails even for large text), so the green button carries ink text
   (6.6:1) — brand olive kept, no deep green. */
.btn:not(.btn--link,.btn--ghost,.btn--dark,.btn--lime),.m-cta__main.btn,.cd-form .form-submit.btn{--btn-fg:var(--c-ink);text-shadow:none}
@media (max-width:1023.98px){.primary-nav .header-cta.btn{--btn-fg:var(--c-ink);text-shadow:none}}
.btn.btn--ghost,.btn.btn--lime,.btn.btn--dark{--btn-fg-h:var(--c-ink)}
/* Empty feedback slots inside the focus carousel: a quiet 1:1 frame with a centred image glyph. */
.fcar .carousel-slide .media-slot--empty{display:grid;place-items:center;width:100%;aspect-ratio:1;border-radius:var(--radius-card);background:repeating-linear-gradient(135deg,rgba(var(--c-green-rgb),.2) 0 1px,transparent 1px 10px),var(--c-cream);border:1px solid rgba(var(--c-ink-rgb),.12);color:var(--c-green)}
.fcar .carousel-slide .media-slot--empty .media-slot__icon{width:44px;height:44px}
.fcar .carousel-slide.is-centre .media-slot--empty{box-shadow:0 40px 70px -40px rgba(var(--c-ink-rgb),.4)}
/* Video focus carousel ("Lớp học có gì vui?"): portrait 9:16 slides. */
.hp-carousel--video.fcar{--fw:min(66vw,270px)}
@media (min-width:768px){.hp-carousel--video.fcar{--fw:290px}}
@media (min-width:1200px){.hp-carousel--video.fcar{--fw:320px;--fmin:.78}}
.fcar .carousel-slide.is-clone{pointer-events:auto}

/* =====================================================================================================================
   19. ROUND 5 FOUNDATION (24 Sep, QA + approved research): spring motion and tactile press, surface-aware floating
   glass, editorial mobile menu, one "proofreading" lead form, gliding FAQ, rolling digits, carousel controls, lightbox,
   shared-element view transitions, edge-to-edge rails, lazy image settle. Tokens: tokens.css (--spring…).
   19.1 press · 19.2 header / action bar / back-to-top · 19.3 nav + mobile sheet · 19.4 forms · 19.5 FAQ · 19.6 digits
   19.7 carousels · 19.8 magnetic · 19.9 lightbox · 19.10 view transitions · 19.11 rails · 19.12 lazy images
   ===================================================================================================================== */

/* ---- 19.1 Tactile press (research P1). Press uses the `scale` property, so it composes with whatever transform a
   component already uses (hover lift, tilt, magnetic drift): in over 80ms, back on the spring (~480ms). Each element
   declares --press-t (its scale transition) and lists it last in its transition. Reduced motion: no scale, colour only.
   Page styles that restate `transition` on these elements can append `, var(--press-t)` to keep the spring back. */
:is(.btn,.carousel-btn,.menu-toggle,[role="tab"],.card-hover,.lightbox__btn,.m-cta__call,.back-to-top){--press-t:scale var(--dur-spring) var(--spring)}
:is(.btn,.carousel-btn,.menu-toggle,[role="tab"],.card-hover,.lightbox__btn,.m-cta__call,.back-to-top):active{--press-t:scale var(--dur-press) var(--ease-out-quick);scale:.97}
:is(.btn,.carousel-btn,.menu-toggle,.lightbox__btn,.m-cta__call,.back-to-top):disabled{scale:none}
.btn{transition:background-size .4s var(--ease),background-color .2s var(--ease),color .25s var(--ease),border-color .25s var(--ease),box-shadow .2s var(--ease),transform .15s var(--ease),opacity .2s var(--ease),translate var(--dur-spring) var(--spring),clip-path .45s var(--spring-snappy),var(--press-t)}
.btn:active,.carousel-btn:active:not(:disabled),.menu-toggle:active,.back-to-top:active,.m-cta__call:active{transform:none}
/* Press also shows the hover colour, so touch (no hover) and reduced motion get a clear colour response. */
.btn:active:not(:disabled){background-size:100% 100%;color:var(--btn-fg-h);border-color:var(--btn-bd-h)}
.carousel-btn{transition:background-color .2s var(--ease),color .2s var(--ease),border-color .2s var(--ease),opacity .2s,var(--press-t)}
.menu-toggle{transition:background-color .2s var(--ease),color .35s var(--ease),var(--press-t)}
.m-cta__call{transition:background-color .2s var(--ease),color .3s var(--ease),border-color .3s var(--ease),var(--press-t)}
.card-hover{transition:transform .35s var(--ease),box-shadow .35s var(--ease),border-color .35s var(--ease),var(--press-t)}
.card-hover:active{transform:none;transition:transform .35s var(--ease),box-shadow .35s var(--ease),border-color .35s var(--ease),var(--press-t)}
@media (hover:hover){.card-hover:not(.is-tilting):active{transform:translateY(-4px)}}
@media (hover:hover) and (pointer:fine) and (prefers-reduced-motion:no-preference){
  .card-hover.is-tilting{transition:transform .12s linear,box-shadow .35s var(--ease),border-color .35s var(--ease),var(--press-t)}
}
@media (prefers-reduced-motion:reduce){
  :is(.btn,.carousel-btn,.menu-toggle,[role="tab"],.card-hover,.lightbox__btn,.m-cta__call,.back-to-top,[class*="__chip"]):active{scale:none;transform:none}
  .card-hover:active{border-color:rgba(var(--c-green-rgb),.8)}
}

/* ---- 19.2 Surface-aware floating glass (QA P1-1/2/3, research P8). main.js sets data-surface (cream | lime | olive |
   ink) from the band under the header / under the bottom bar. Glass = that surface at .86 + blur(16px) saturate(1.3) +
   a 1px top highlight, so it never turns muddy grey; solid surface colour without backdrop-filter or with reduced
   transparency. Over ink the header is ink glass with cream brand/links (the ghost CTA stays white: readable). */
.site-header{--hdr-bg:var(--c-cream);--hdr-rgb:255,250,243;--hdr-fg:var(--c-ink);--hdr-accent:var(--c-green);--hdr-line:rgba(var(--c-ink-rgb),.1);--hdr-hi:rgba(255,255,255,.6);--hdr-focus:var(--c-ink);
  background:var(--hdr-bg);color:var(--hdr-fg);border-bottom-color:var(--hdr-line);
  transition:background-color .35s var(--ease),color .35s var(--ease),border-color .35s var(--ease),box-shadow .3s var(--ease),margin-bottom .3s var(--ease)}
.site-header[data-surface="lime"]{--hdr-bg:var(--c-cream-warm);--hdr-rgb:228,252,182;--hdr-accent:var(--c-ink);--hdr-line:rgba(var(--c-ink-rgb),.12)}
.site-header[data-surface="olive"]{--hdr-bg:var(--c-green);--hdr-rgb:146,185,60;--hdr-accent:var(--c-ink);--hdr-line:rgba(var(--c-ink-rgb),.18);--hdr-hi:rgba(255,255,255,.28)}
.site-header[data-surface="ink"]{--hdr-bg:var(--c-ink);--hdr-rgb:50,35,31;--hdr-fg:var(--c-cream);--hdr-accent:var(--c-lime);--hdr-line:rgba(255,250,243,.12);--hdr-hi:rgba(255,250,243,.1);--hdr-focus:var(--c-cream)}
.site-header .brand,.site-header .menu-toggle{color:var(--hdr-fg);transition:color .35s var(--ease)}
@media (hover:hover){.site-header .brand:hover{color:var(--hdr-accent)}.site-header[data-surface="ink"] .menu-toggle:hover{background:rgba(255,250,243,.1)}}
.site-header :focus-visible{outline-color:var(--hdr-focus)}
.site-header .btn:focus-visible{outline-color:var(--hdr-focus)}
.site-header.is-scrolled{background:var(--hdr-bg);box-shadow:inset 0 1px 0 var(--hdr-hi),0 12px 28px -22px rgba(var(--c-ink-rgb),.45);border-bottom-color:var(--hdr-line)}
@supports ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .site-header.is-scrolled{background:rgba(var(--hdr-rgb),.86);-webkit-backdrop-filter:saturate(1.3) blur(16px);backdrop-filter:saturate(1.3) blur(16px);border-bottom-color:var(--hdr-line)}
}
/* While the mobile sheet is open the header is solid cream, joined to the sheet. */
.site-header.is-menu-open,.site-header.is-menu-open.is-scrolled{background:var(--c-cream);-webkit-backdrop-filter:none;backdrop-filter:none;box-shadow:none;border-bottom-color:rgba(var(--c-ink-rgb),.1)}
/* Mobile action bar: the same surface-aware glass; ≤ 480px wide, centred on tablets (QA P2-14). */
.m-cta{--mc-rgb:50,35,31;--mc-hi:rgba(255,250,243,.1);--mc-line:rgba(255,250,243,.1);--mc-fg:var(--c-lime);--mc-call-bd:rgba(255,250,243,.3);--mc-focus:var(--c-cream);
  max-width:480px;margin-inline:auto;background:rgb(var(--mc-rgb));box-shadow:inset 0 1px 0 var(--mc-hi),0 0 0 1px var(--mc-line),0 18px 40px -16px rgba(var(--c-ink-rgb),.5)}
.m-cta[data-surface="cream"]{--mc-rgb:255,250,243;--mc-hi:rgba(255,255,255,.7);--mc-line:rgba(var(--c-ink-rgb),.1);--mc-fg:var(--c-ink);--mc-call-bd:rgba(var(--c-ink-rgb),.25);--mc-focus:var(--c-ink)}
.m-cta[data-surface="lime"]{--mc-rgb:228,252,182;--mc-hi:rgba(255,255,255,.6);--mc-line:rgba(var(--c-ink-rgb),.12);--mc-fg:var(--c-ink);--mc-call-bd:rgba(var(--c-ink-rgb),.25);--mc-focus:var(--c-ink)}
.m-cta[data-surface="olive"]{--mc-rgb:146,185,60;--mc-hi:rgba(255,255,255,.3);--mc-line:rgba(var(--c-ink-rgb),.14);--mc-fg:var(--c-ink);--mc-call-bd:rgba(var(--c-ink-rgb),.35);--mc-focus:var(--c-ink)}
/* On olive the olive main button would vanish: it becomes the ink kind (lime text). */
.m-cta[data-surface="olive"] .m-cta__main.btn{--btn-bg:var(--c-ink);--btn-bd:var(--c-ink);--btn-fg:var(--c-lime);--btn-bg-h:var(--c-cream);--btn-bd-h:var(--c-cream);--btn-fg-h:var(--c-ink)}
.m-cta__call{color:var(--mc-fg);border-color:var(--mc-call-bd)}
.m-cta__main:focus-visible,.m-cta__call:focus-visible{outline-color:var(--mc-focus)}
@supports ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .m-cta{background:rgba(var(--mc-rgb),.86);-webkit-backdrop-filter:saturate(1.3) blur(16px);backdrop-filter:saturate(1.3) blur(16px);box-shadow:inset 0 1px 0 var(--mc-hi),0 0 0 1px var(--mc-line),0 18px 40px -16px rgba(var(--c-ink-rgb),.45)}
}
@media (prefers-reduced-transparency:reduce){
  .site-header.is-scrolled{background:var(--hdr-bg);-webkit-backdrop-filter:none;backdrop-filter:none}
  .m-cta{background:rgb(var(--mc-rgb));-webkit-backdrop-filter:none;backdrop-filter:none}
}
/* Back to top over the ink footer / ink bands: lime fill + ink arrow (QA P1-3), cream ring on focus. */
.back-to-top[data-surface="ink"]{background:var(--c-lime);color:var(--c-ink);box-shadow:0 10px 24px -12px rgba(0,0,0,.6)}
.back-to-top[data-surface="ink"]:focus-visible{outline-color:var(--c-cream)}
@media (hover:hover){.back-to-top[data-surface="ink"]:hover{background:var(--c-cream);color:var(--c-ink)}}

/* ---- 19.3 Navigation. Desktop (≥1024): links follow the header surface; focus ring 4px off, radius 12 (QA P2-7).
   Below 1024 (research P15): an editorial full-height cream sheet — links at h2 size on thin index rules (the current
   page's olive segment runs the full length), staggered in 40ms apart on the spring, CTA + hotline pinned at the
   bottom; burger morphs to ✕; scrim ink .4. Without JS the plain wrapped link row stays (see §12). */
.primary-nav .menu a{color:var(--hdr-fg,var(--c-ink));transition:color .35s var(--ease)}
.primary-nav .menu a::after{background:var(--hdr-accent,var(--c-green))}
.primary-nav .current-menu-item > a::after,.primary-nav .current_page_item > a::after,.primary-nav .menu a[aria-current="page"]::after{background:var(--hdr-fg,var(--c-ink))}
.primary-nav a:focus-visible{outline:3px solid var(--hdr-focus,var(--c-ink));outline-offset:4px;border-radius:12px}
.primary-nav__foot{display:flex;align-items:center;flex:none;gap:16px}
.header-hotline{display:none}
.menu-toggle__bars span{transition:transform var(--dur-spring) var(--spring),top var(--dur-snappy) var(--spring-snappy),opacity .15s var(--ease)}
.nav-backdrop{background:rgba(var(--c-ink-rgb),.4)}
@media (max-width:1023.98px){
  .site-header.nav-enhanced .primary-nav{top:100%;height:calc(100vh - var(--nav-top,var(--header-h)) + 1px);height:calc(100dvh - var(--nav-top,var(--header-h)) + 1px);max-height:none;display:flex;flex-direction:column;overflow-y:auto;overscroll-behavior:contain;
    padding:8px var(--gutter) calc(20px + env(safe-area-inset-bottom,0px));background:var(--c-cream);border-bottom:0;box-shadow:none;
    clip-path:inset(0 0 100% 0);opacity:1;transform:none;transition:clip-path var(--dur-spring) var(--spring-snappy),visibility 0s linear var(--dur-spring)}
  .site-header.nav-enhanced .primary-nav.is-open{clip-path:inset(0 0 0 0);opacity:1;transform:none;transition:clip-path var(--dur-spring) var(--spring-snappy),visibility 0s}
  .site-header.nav-enhanced .primary-nav .menu{flex-direction:column;gap:0;margin:0;border-bottom:1px solid rgba(var(--c-ink-rgb),.14)}
  .site-header.nav-enhanced .primary-nav .menu li{position:relative;border-top:1px solid rgba(var(--c-ink-rgb),.14)}
  .site-header.nav-enhanced .primary-nav .menu li::before{content:"";position:absolute;left:0;top:-1px;width:40px;height:3px;background:var(--c-green);transition:width .6s var(--spring-snappy)}
  .site-header.nav-enhanced .primary-nav .menu li:is(.current-menu-item,.current_page_item,:has(> a[aria-current="page"]))::before{width:100%}
  .site-header.nav-enhanced .primary-nav .menu a{display:flex;width:100%;min-height:64px;padding-block:14px 12px;font-size:var(--fs-h2);font-weight:700;line-height:1.2;letter-spacing:-.012em;color:var(--c-ink)}
  .site-header.nav-enhanced .primary-nav .menu a::after{content:none}
  .site-header.nav-enhanced .primary-nav .menu a:focus-visible{outline-offset:-3px}
  @media (hover:hover){.site-header.nav-enhanced .primary-nav .menu li:hover::before{width:100%}}
  .site-header.nav-enhanced .primary-nav .menu li:focus-within::before{width:100%}
  .site-header.nav-enhanced .primary-nav__foot{flex-direction:column;align-items:stretch;gap:8px;margin-top:auto;padding-top:28px}
  .site-header.nav-enhanced .primary-nav .header-cta{margin-top:0;width:100%}
  .site-header.nav-enhanced .header-hotline{display:inline-flex;align-items:center;justify-content:center;gap:10px;min-height:48px;font-size:16px;font-weight:600;color:var(--c-ink);text-decoration:underline;text-decoration-color:var(--c-green);text-decoration-thickness:2px;text-underline-offset:.3em}
  .header-hotline .icon{width:20px;height:20px;color:var(--c-green-deep)}
  /* Stagger: rows rise 14px and fade in, 40ms apart, on the spring; the pinned foot follows. */
  .site-header.nav-enhanced .primary-nav :is(.menu > li,.primary-nav__foot){opacity:0;translate:0 14px;transition:opacity .2s var(--ease),translate var(--dur-spring) var(--spring)}
  .site-header.nav-enhanced .primary-nav.is-open :is(.menu > li,.primary-nav__foot){opacity:1;translate:none;transition:opacity .3s var(--ease) calc(var(--i,0) * 40ms + 60ms),translate var(--dur-spring) var(--spring) calc(var(--i,0) * 40ms + 60ms)}
  .primary-nav .menu > li:nth-child(2){--i:1}.primary-nav .menu > li:nth-child(3){--i:2}.primary-nav .menu > li:nth-child(4){--i:3}
  .primary-nav .menu > li:nth-child(5){--i:4}.primary-nav .menu > li:nth-child(n+6){--i:5}.primary-nav__foot{--i:6}
  @media (hover:hover){.header-hotline:hover{text-decoration-color:var(--c-ink)}}
}
@media (max-width:1023.98px) and (prefers-reduced-motion:reduce){
  .site-header.nav-enhanced .primary-nav,.site-header.nav-enhanced .primary-nav.is-open{transition:none!important}
  .site-header.nav-enhanced .primary-nav :is(.menu > li,.primary-nav__foot){translate:none!important;transition:none!important}
}
@media (prefers-reduced-motion:reduce){.menu-toggle__bars span{transition:none}}

/* ---- 19.4 Lead forms: ONE form (QA P1-21, P2-9/10, research P6). Cream card (lime + card surfaces), ≤ 720px when it
   stands alone on its band; the submit is the ink kind with lime text on every form; floated labels 12–13px, no
   tracking; errors left-aligned. "Proofreading" feedback: focus draws an olive underline left → right (280ms); a valid
   entry gets a small geometric tick at the field end; an invalid one a caret ‸ and an ink rule (the error text stays).
   The submit button morphs pill → spinner (form[aria-busy]) → tick (.is-sent) → pill. Inputs are 16px on phones. */
div.lead-form[class*="lead-form--"]:is(.lead-form--card,.lead-form--lime){background:var(--c-cream)}
.band--olive div.lead-form.lead-form--lime{max-width:720px;margin-inline:auto}
/* In the 720px card the h2-size title is scaled to .8 on desktop so two-line titles keep whole phrases per line. */
@media (min-width:1200px){.band--olive div.lead-form.lead-form--lime .lead-form__title{font-size:calc(var(--fs-h2) * .8)}}
.lead-form:not(.lead-form--dark) [data-consultation-form] .form-submit.btn[data-lead-submit]{--btn-bg:var(--c-ink);--btn-bd:var(--c-ink);--btn-fg:var(--c-lime);--btn-bg-h:var(--c-green);--btn-bd-h:var(--c-green);--btn-fg-h:var(--c-ink);text-shadow:none}
.form-submit{position:relative;isolation:isolate}
.form-submit > *{transition:opacity .2s var(--ease)}
.form-submit::after{content:"";position:absolute;left:50%;top:50%;width:20px;height:20px;margin:-10px 0 0 -10px;border:2px solid var(--btn-fg);border-right-color:transparent;border-radius:50%;opacity:0;pointer-events:none;transition:opacity .2s var(--ease)}
.form[aria-busy="true"] .form-submit,.form.is-sent .form-submit{clip-path:inset(0 calc(50% - 27px) round 999px);opacity:1;background-size:0% 100%;color:var(--btn-fg)}
.form[aria-busy="true"] .form-submit > *,.form.is-sent .form-submit > *{opacity:0}
.form[aria-busy="true"] .form-submit::after{opacity:1;animation:tmm-spin .8s linear infinite}
.form.is-sent .form-submit::after{width:16px;height:9px;margin:-7px 0 0 -8px;border:0;border-left:2px solid var(--btn-fg);border-bottom:2px solid var(--btn-fg);border-radius:0;transform:rotate(-45deg);opacity:1;clip-path:inset(0 0 0 0);animation:tmm-tick-draw .32s var(--ease-out-quick) .08s both}
@keyframes tmm-spin{to{transform:rotate(360deg)}}
@keyframes tmm-tick-draw{from{clip-path:inset(0 100% 0 0)}to{clip-path:inset(0 0 0 0)}}
/* The submit label stays on one line in narrow cards too (e.g. the two-column contact card at 768): the button tightens
   with the width of its form (container query on the lead-form wrapper). */
.lead-form{container:lead-form / inline-size}
.lead-form .form__actions .btn > span{white-space:nowrap}
@container lead-form (max-width:360px){.form__actions .btn{font-size:15px;padding-inline:14px;gap:8px}}
@container lead-form (max-width:290px){.form__actions .btn{font-size:14px;padding-inline:10px;gap:6px}}
/* Labels: 16px resting, 12px floated (13 on desktop), no letter-spacing. */
.field__label{letter-spacing:0}
@media (min-width:1200px){
  .field:focus-within .field__label,.field.is-filled .field__label,.field:has(input.field__control:not(:placeholder-shown)) .field__label,.field:has(textarea.field__control:not(:placeholder-shown)) .field__label,
  input.field__control:focus + .field__label,textarea.field__control:focus + .field__label,input.field__control:not(:placeholder-shown) + .field__label,textarea.field__control:not(:placeholder-shown) + .field__label{transform:translateY(-14px) scale(.76)}
}
.field__error{text-align:left;text-wrap:pretty}
/* Focus: 1px ink rule + a 2px olive bar drawn left → right just above it (lime on dark forms); the label turns ink. */
.field{--field-accent:var(--c-green);--field-ok:var(--c-green-deep);--field-bad:#b3261e;--field-rule:var(--c-ink)}
:is(.surface-dark,.band--ink,.lead-form--dark) .field{--field-accent:var(--c-lime);--field-ok:var(--c-lime);--field-bad:#ffb4ab;--field-rule:var(--c-cream)}
.lead-form:is(.lead-form--lime,.lead-form--card) .field{--field-accent:var(--c-green);--field-ok:var(--c-green-deep);--field-bad:#b3261e;--field-rule:var(--c-ink)}
.field__control{background-image:linear-gradient(var(--field-accent),var(--field-accent));background-repeat:no-repeat;background-position:0 100%;background-size:0% 2px;
  transition:border-color .18s var(--ease),box-shadow .18s var(--ease),background-size 280ms var(--ease-out-quick)}
.field__control:focus,.field__control:focus-visible{border-bottom-color:var(--field-rule);box-shadow:none;background-size:100% 2px}
input.field__control,textarea.field__control{padding-right:28px}
/* Valid tick / invalid caret at the field end (inputs and the textarea; the select keeps its chevron). */
.field:has(> :is(input,textarea).field__control)::after{content:"";position:absolute;right:4px;top:30px;width:13px;height:7px;border-left:2px solid var(--field-ok);border-bottom:2px solid var(--field-ok);transform:rotate(-45deg);clip-path:inset(0 100% 0 0);opacity:0;pointer-events:none;transition:clip-path .28s var(--ease-out-quick),opacity .12s linear}
.field.is-valid:has(> :is(input,textarea).field__control)::after{clip-path:inset(0 0 0 0);opacity:1}
.field.is-invalid:has(> :is(input,textarea).field__control)::after{top:45px;width:8px;height:8px;border:0;border-left:2px solid var(--field-bad);border-top:2px solid var(--field-bad);transform:rotate(45deg);clip-path:inset(0 0 0 0);opacity:1}
.field.is-invalid .field__control,.field.is-invalid .field__control:focus{border-bottom-color:var(--field-rule);box-shadow:0 1px 0 0 var(--field-rule)}
.field.is-valid .field__control:not(:focus){border-bottom-color:rgba(var(--c-ink-rgb),.35)}
:is(.surface-dark,.band--ink,.lead-form--dark) .field.is-valid .field__control:not(:focus){border-bottom-color:rgba(255,250,243,.4)}
@media (min-width:1200px){.field:has(> :is(input,textarea).field__control)::after{top:33px}.field.is-invalid:has(> :is(input,textarea).field__control)::after{top:48px}}
@media (prefers-reduced-motion:reduce){.field__control,.field::after,.form-submit,.form-submit::after{transition:none!important;animation:none!important}}

/* ---- 19.5 FAQ (research P11, QA P2-8): the answer glides open (grid 0fr → 1fr, 360ms spring) and fades in; the + turns
   into − on the spring; the open card's ink fill grows out of the icon as a clip-path circle (main.js measures the
   icon centre → --faq-ix/--faq-iy). The answer text starts exactly under the question text (pad + number + gap). */
.faq__item{--faq-pad:20px;--faq-gap:20px;--faq-num:20px;isolation:isolate}
.faq__item::before{content:"";position:absolute;inset:-1px;z-index:-1;border-radius:inherit;background:var(--c-ink);pointer-events:none;clip-path:circle(0 at var(--faq-ix,calc(100% - 38px)) var(--faq-iy,38px));transition:clip-path .5s var(--spring-snappy)}
.faq__item.is-open{background:var(--c-cream)}
.band--cream .faq__item.is-open{background:var(--c-cream-warm)}
:is(.surface-dark,.band--ink) .faq__item.is-open{background:rgba(255,250,243,.05)}
.faq__item.is-open::before{clip-path:circle(150% at var(--faq-ix,calc(100% - 38px)) var(--faq-iy,38px))}
.faq__btn{gap:var(--faq-gap);padding-inline:var(--faq-pad)}
.faq__btn::before{min-width:var(--faq-num);text-align:left}
@media (hover:hover){.faq__btn:hover{padding-left:var(--faq-pad);padding-inline-end:var(--faq-pad)}}
.faq__btn:focus-visible{padding-left:var(--faq-pad);padding-inline-end:var(--faq-pad)}
.faq__a{padding-left:calc(var(--faq-pad) + var(--faq-num) + var(--faq-gap))}
.faq__q,.faq__a,.faq__btn::before{transition:color .3s var(--ease) .06s}
.faq__icon{transition:transform var(--dur-spring) var(--spring),background-color .3s var(--ease)}
.faq__icon::before,.faq__icon::after{transition:transform var(--dur-spring) var(--spring)}
.js .faq__panel{transition:grid-template-rows 360ms var(--spring),visibility 0s linear 360ms}
.js .faq__item.is-open .faq__panel{transition:grid-template-rows 360ms var(--spring),visibility 0s}
.js .faq__panel .faq__a{opacity:0;translate:0 -6px;transition:opacity .2s var(--ease),translate 360ms var(--spring),color .3s var(--ease)}
.js .faq__item.is-open .faq__panel .faq__a{opacity:1;translate:none;transition:opacity .3s var(--ease) .08s,translate 360ms var(--spring),color .3s var(--ease) .06s}
@media (max-width:767.98px){.faq__item{--faq-pad:16px;--faq-gap:14px}.faq__a{padding-right:16px}}
@media (min-width:1200px){.faq__item{--faq-pad:26px;--faq-gap:22px}}
@media (prefers-reduced-motion:reduce){.faq__item::before,.js .faq__panel .faq__a{transition:none!important}}
@media print{.js .faq__panel .faq__a{opacity:1;translate:none}}

/* ---- 19.6 Rolling digits (research P7, QA P1-6; main.js). The real figure is laid out invisibly (.roll__ghost) and the
   columns sit over it, so width and alignment never change; each column is a 1-digit window onto a 0–9 strip. */
.roll{position:relative;display:inline-block;font-variant-numeric:tabular-nums}
.roll__ghost{visibility:hidden}
.roll__cols{position:absolute;inset:0;display:flex;overflow:hidden}
.roll__col,.roll__static{display:flex;flex-direction:column;height:100%}
.roll__col > span{flex:none;display:block;height:100%}
.roll__col{transform:translateY(calc(var(--to,0) * -100%));will-change:transform}
.roll.is-rolling .roll__col{transition:transform 1.15s var(--spring) calc(var(--i,0) * 50ms)}

/* ---- 19.7 Carousel controls (QA P2-6): every prev/next is the same — ink outline, ink arrow; hover/focus ink fill,
   lime arrow. On ink bands: cream outline; hover lime fill, ink arrow. */
.carousel__controls .carousel-btn.carousel-btn{background:transparent;border:1px solid var(--c-ink);color:var(--c-ink)}
@media (hover:hover){.carousel__controls .carousel-btn.carousel-btn:hover:not(:disabled){background:var(--c-ink);border-color:var(--c-ink);color:var(--c-lime)}}
.carousel__controls .carousel-btn.carousel-btn:focus-visible{background:var(--c-ink);color:var(--c-lime)}
:is(.band--ink,.surface-dark) .carousel__controls .carousel-btn.carousel-btn{border-color:rgba(255,250,243,.7);color:var(--c-cream)}
@media (hover:hover){:is(.band--ink,.surface-dark) .carousel__controls .carousel-btn.carousel-btn:hover:not(:disabled){background:var(--c-lime);border-color:var(--c-lime);color:var(--c-ink)}}
:is(.band--ink,.surface-dark) .carousel__controls .carousel-btn.carousel-btn:focus-visible{background:var(--c-lime);border-color:var(--c-lime);color:var(--c-ink);outline-color:var(--c-cream)}
/* Focus carousel: slides beyond the unique range (few slides + loop clones) fade out (main.js --vis / .is-far). */
.fcar .carousel-slide{opacity:calc((.62 + .38 * var(--focus,0)) * var(--vis,1))}
.fcar .carousel-slide.is-far{visibility:hidden}
.fcar.has-lightbox .carousel-slide.is-centre:has(img:not(.media-slot--empty)){cursor:zoom-in}

/* ---- 19.8 Magnetic primary buttons (research P10; main.js, mouse only): drift via `translate`, arrow 3px further. */
[data-magnetic].is-magnet{translate:var(--mag-x,0) var(--mag-y,0);transition-duration:.4s,.2s,.25s,.25s,.2s,.15s,.2s,.18s,.45s,var(--dur-spring)}
[data-magnetic] .icon-arrow{transition:transform .2s var(--ease),translate var(--dur-spring) var(--spring)}
[data-magnetic].is-magnet .icon-arrow{translate:var(--mag-ax,0) var(--mag-ay,0);transition:transform .2s var(--ease),translate .18s var(--ease-out-quick)}
@media (prefers-reduced-motion:reduce){[data-magnetic].is-magnet,[data-magnetic].is-magnet .icon-arrow{translate:none}}

/* ---- 19.9 Lightbox (research P4; main.js makeLightbox): native <dialog>, image contained ≤ 90vh, controls on ink. */
html.lightbox-open{overflow:hidden}
.lightbox{position:fixed;inset:0;width:100%;max-width:none;height:100%;max-height:none;margin:0;padding:0;border:0;background:transparent;color:var(--c-cream);overflow:hidden}
.lightbox[open]{display:block}
.lightbox::backdrop{background:rgba(var(--c-ink-rgb),.92)}
.lightbox__figure{position:absolute;inset:0;display:grid;place-items:center;margin:0;padding:72px 16px 96px;touch-action:pinch-zoom;translate:0 var(--lb-y,0);opacity:calc(1 - min(var(--lb-y,0px) / 400px, .6));transition:translate var(--dur-spring) var(--spring),opacity .3s var(--ease)}
.lightbox__figure.is-dragging{transition:none}
.lightbox__img{display:block;width:auto;height:auto;max-width:min(92vw,1080px);max-height:calc(100vh - 168px);max-height:calc(100dvh - 168px);object-fit:contain;border-radius:var(--radius-card);touch-action:pinch-zoom;user-select:none;-webkit-user-drag:none;box-shadow:0 40px 90px -40px rgba(0,0,0,.7)}
.lightbox[open] .lightbox__img.is-swapping{animation:tmm-lb-in .5s var(--spring) both}
@keyframes tmm-lb-in{from{opacity:0;scale:.94}to{opacity:1;scale:1}}
.lightbox__btn{display:inline-grid;place-items:center;width:48px;height:48px;border:1px solid rgba(255,250,243,.6);border-radius:var(--radius-pill);background:rgba(var(--c-ink-rgb),.5);color:var(--c-cream);transition:background-color .2s var(--ease),color .2s var(--ease),border-color .2s var(--ease),var(--press-t)}
.lightbox__btn .icon{width:22px;height:22px}
@media (hover:hover){.lightbox__btn:hover{background:var(--c-lime);border-color:var(--c-lime);color:var(--c-ink)}}
.lightbox__btn:focus-visible{outline:3px solid var(--c-cream);outline-offset:3px;background:var(--c-lime);border-color:var(--c-lime);color:var(--c-ink)}
.lightbox__close{position:absolute;top:calc(16px + env(safe-area-inset-top,0px));right:16px;z-index:2}
.lightbox__nav{position:absolute;left:0;right:0;bottom:calc(20px + env(safe-area-inset-bottom,0px));z-index:2;display:flex;align-items:center;justify-content:center;gap:16px}
.lightbox__status{min-width:4em;margin:0;text-align:center;font-size:var(--fs-small);font-weight:600;font-variant-numeric:tabular-nums;color:rgba(255,250,243,.8)}
@media (min-width:1024px){
  .lightbox__figure{padding:5vh 96px}
  .lightbox__img{max-height:90vh;max-width:calc(100vw - 192px)}
  .lightbox__nav{top:50%;bottom:auto;left:24px;right:24px;justify-content:space-between;translate:0 -50%;pointer-events:none}
  .lightbox__nav > *{pointer-events:auto}
  .lightbox__status{position:absolute;left:50%;top:calc(45vh + 12px);translate:-50% 0;display:none}
  .lightbox__close{top:24px;right:24px}
}
@media (prefers-reduced-motion:reduce){.lightbox__figure{transition:none}.lightbox[open] .lightbox__img.is-swapping{animation:none}}

/* ---- 19.10 Shared-element view transitions (research P3; main.js + header.php). Elements named by the data-vt /
   data-vt-src → data-vt-dest contract carry view-transition-class "vt-morph": they morph on the spring in ~0.5s. */
::view-transition-group(*.vt-morph){animation-duration:.5s;animation-timing-function:var(--spring)}
::view-transition-old(*.vt-morph),::view-transition-new(*.vt-morph){animation-duration:.3s;height:100%;object-fit:cover}
@media (prefers-reduced-motion:reduce){::view-transition-group(*.vt-morph),::view-transition-old(*.vt-morph),::view-transition-new(*.vt-morph){animation:none}}

/* ---- 19.11 Rails bleed to the screen edges at every width below 1024 (QA P2-13), whatever wraps them: the rail spans
   the full viewport width and pads its first/last card back to the column edge (also the snap edge). */
@media (max-width:1023.98px){
  /* scroll-padding % would resolve against the rail's own (full-bleed) width, so the snap edge uses the gutter. */
  .rail--swipe{margin-inline:calc(50% - 50vw)!important;padding-inline:calc(50vw - 50%)!important;scroll-padding-inline:var(--gutter)}
}

/* ---- 19.12 Lazy images (research P16; main.js): an image still loading is held transparent, then fades in and settles
   from 1.03 once decoded (360ms). Opacity/scale only; without JS or with reduced motion nothing is held back. */
@media (prefers-reduced-motion:no-preference){
  img.lazy-pending{opacity:0}
  img.lazy-in{animation:tmm-lazy-in 360ms var(--spring-snappy) both}
}
@keyframes tmm-lazy-in{from{opacity:0;scale:1.03}to{opacity:1;scale:1}}
@media print{img.lazy-pending{opacity:1}}

/* Count-up (24 Sep): the real figure keeps the layout (invisible); the counting figure sits on top, left-aligned. */
[data-count]{position:relative;display:inline-block;font-variant-numeric:tabular-nums}
[data-count] .count__ghost{visibility:hidden}
[data-count] .count__live{position:absolute;left:0;top:0}
