/* ============================================================
   Young Guns Studio — site style layer (2026)
   Visual fixes only: hero scale, final-CTA readability, focus
   styles, skip link, footer. No redesign.
   ============================================================ */

/* ============================================================
   Brand tokens — the ONE place the studio red is defined.
   main.css is the only sheet every page loads, so a token set
   here reaches the hand-authored pages (/404, /privacy, /terms,
   /accessibility, /wechat, the outcome profiles) that used to
   declare their own `--red` in an inline <style>. Those local
   declarations shipped a second, lighter red plus a hard-coded
   hover shade, so the same .btn measured a different colour
   depending on which page it was on. Both are gone; #c8283f is
   the site red the shared chrome (nav CTA, drawer, social hover,
   focus rings) has always used.
   ============================================================ */
:root{
  --red:#c8283f;       /* studio red — 5.5:1 on white */
  --red-deep:#a82135;  /* its hover/pressed state */
}

/* ---------- hero headline scale (homepage) ---------- */
[data-og="hero-h1"] h1.yg-text{
  font-size:clamp(48px,5vw,76px)!important;
  line-height:1.06!important;
  text-shadow:0 2px 24px rgba(0,0,0,.5);
}
[data-og="hero-sub"] .yg-text{
  font-size:clamp(24px,2.4vw,36px)!important;
  line-height:1.25!important;
  text-shadow:0 2px 20px rgba(0,0,0,.6),0 1px 3px rgba(0,0,0,.5);
}
@media (max-width:809px){
  /* headline is the focal point on a phone: was clamp(34px,8.5vw,48px) —
     8.5vw reads timid against a full-bleed hero video. Subtitle drops to
     roughly half the headline size so it reads as a supporting line
     funnelling into the CTA, not a second, equally-loud headline. */
  [data-og="hero-h1"] h1.yg-text{font-size:clamp(36px,11vw,48px)!important}
  [data-og="hero-sub"] .yg-text{font-size:clamp(17px,4.5vw,20px)!important;margin-top:2px!important}
}

/* ---------- final CTA section: make the copy readable on the
   noisy red/blue gradient ---------- */
.yg-1s0mrl8 .yg-text{
  text-shadow:0 2px 28px rgba(8,10,22,.65),0 1px 4px rgba(8,10,22,.5);
}
.yg-h5gk3r .yg-text{
  color:#ffffff!important;
  text-shadow:0 2px 28px rgba(8,10,22,.65),0 1px 4px rgba(8,10,22,.5);
}
.yg-6f6u2q .yg-text{
  font-size:16px!important;
  letter-spacing:.01em;
}

/* ---------- keyboard focus visibility ---------- */
a:focus-visible,button:focus-visible,[role="button"]:focus-visible,
[role="link"]:focus-visible,[tabindex]:focus-visible{
  outline:3px solid #fff!important;
  outline-offset:3px;
  box-shadow:0 0 0 6px rgba(12,14,28,.6)!important;
  /* no border-radius here: this rule outranks the components' own radius
     (a:focus-visible is 0,1,1) and squared the ring off every pill - the
     outline already follows each element's real radius */
}

/* ---------- skip link ---------- */
.og-skip{
  position:fixed;left:16px;top:-64px;z-index:10000;
  background:#101223;color:#fff;padding:12px 20px;border-radius:8px;
  font:600 14px/1 "Inter Variable",Inter,system-ui,sans-serif;
  text-decoration:none;transition:top .15s ease;
}
.og-skip:focus{top:16px}

/* ============================================================
   Site footer: the custom artwork footer is static markup in every
   page's HTML — the generator's original footer markup was deleted
   from all pages. This guard once kept the old runtime's client-
   rendered footer copy dead; the runtime itself is gone now, so the
   rule is a harmless belt-and-braces leftover.
   ============================================================ */
.yg-1w1kwye{display:none!important}

/* ---------- icon socials row ---------- */
.og-social-row{display:flex;gap:10px;margin-top:8px;align-self:flex-start}
/* Sized by padding + min-width/min-height rather than a fixed 40×40: the
   padding alone already places the 17px icon within half a pixel of centre,
   so the circle no longer depends on flex alignment resolving to look right
   (icons pinned to the top-left rim is the reported failure — see #52), and
   44px meets the minimum tap target. Equal padding keeps it a circle. */
.og-social-row a{
  display:flex;align-items:center;justify-content:center;
  min-width:44px;min-height:44px;padding:13px;flex-shrink:0;
  border-radius:50%;color:#fff;
  border:1px solid rgba(255,255,255,.35);
  transition:background .2s ease,border-color .2s ease,transform .2s ease;
}
/* Every hover block on this site sits behind (hover:hover) and
   (pointer:fine). A touch browser with no real pointer still matches
   :hover after a tap and holds it until the visitor taps something
   else, so an unguarded lift/tint stays stuck on the last thing the
   thumb touched. The guard costs nothing on a mouse and removes the
   state entirely on a phone. */
@media (hover:hover) and (pointer:fine){
  .og-social-row a:hover{background:var(--red);border-color:var(--red);transform:translateY(-2px)}
}
.og-social-row svg{width:17px;height:17px;display:block}

/* ============================================================
   Site footer (all pages) — the studio's noise artwork (captured
   from the footer canvas as og-footer-art.webp) showing through
   cleanly, with only a soft gradient scrim for text legibility.
   The capture is 1920×381: on desktop the footer is a wide short
   band and the artwork renders near 1:1, but under 1200px the
   footer grows much taller than 381px and cover-scaling blows the
   noise up into blur — so below 1200px we draw the same look
   procedurally (gradient + crisp SVG grain) at native resolution.
   ============================================================ */
.og-footer{
  position:relative;overflow:hidden;color:#fff;
  background:#232342 url("/assets/media/images/og-footer-art.webp") center/cover no-repeat;
  font-family:"Inter Variable",Inter,system-ui,sans-serif;
}
/* seamless legibility scrim — deepens gently toward the bottom */
.og-footer::before{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(180deg,rgba(26,26,48,.12),rgba(26,26,48,.5));
}
@media (max-width:1199.98px){
  .og-footer{
    background:
      radial-gradient(900px 480px at 8% 0%,rgba(140,44,66,.5),transparent 65%),
      radial-gradient(1100px 620px at 92% 110%,rgba(30,34,80,.85),transparent 72%),
      linear-gradient(112deg,#3d3a5c 0%,#343457 46%,#252542 100%);
  }
  /* film-grain overlay: same character as the artwork's noise but
     tiled at native resolution, so it stays crisp at any size */
  .og-footer::after{
    content:"";position:absolute;inset:0;pointer-events:none;opacity:.4;
    background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3CfeComponentTransfer%3E%3CfeFuncA type='linear' slope='0.16'/%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
  }
  /* the gradient is already deep — keep only a light bottom scrim */
  .og-footer::before{
    background:linear-gradient(180deg,transparent,rgba(26,26,48,.35));
  }
}
.og-footer>*{position:relative;z-index:1}
.og-f-main{padding:56px 40px 60px}
.og-f-grid{
  display:grid;grid-template-columns:1.6fr .8fr .8fr .8fr .8fr;
  gap:48px;align-items:start;max-width:1520px;
}
/* brand column: logo + the studio's own one-line description, sitting
   in the same row as the link columns */
.og-f-brand{display:grid;gap:20px;justify-items:start}
.og-f-logo-img{display:block;width:250px;max-width:100%;height:auto;margin:0}
.og-f-brand p{
  margin:0;font-size:15px;line-height:1.65;
  color:rgba(255,255,255,.85);max-width:44ch;
}
.og-f-h{
  font-family:"Cooper Hewitt Bold Italic","Inter Variable",Inter,sans-serif;
  font-weight:712;font-style:italic;font-size:17px;letter-spacing:.01em;
  color:#fff;margin:0 0 18px;text-transform:uppercase;
}
.og-f-col{display:grid;gap:12px;justify-items:start;font-size:16px}
.og-f-col a{color:#fff;text-decoration:none}
@media (hover:hover) and (pointer:fine){
  .og-f-col a:hover{text-decoration:underline;text-underline-offset:3px}
}
.og-f-base{background:rgba(16,16,34,.35);padding:20px 40px;font-size:16px;
  text-align:center}
.og-f-base p{margin:0}
@media (max-width:1199px){
  .og-f-grid{grid-template-columns:1fr 1fr;gap:40px}
  .og-f-brand{grid-column:1 / -1}
}
@media (max-width:809px){
  /* centered brand block reads better once everything is one column */
  .og-f-main{padding:44px 24px 48px}
  .og-f-grid{grid-template-columns:1fr;gap:36px}
  .og-f-brand{justify-items:center;text-align:center}
  .og-f-base{padding:18px 24px;font-size:14px}
}
/* Wider than the footer's design width — center the content instead of
   letting it hug the left edge. The content column is the grid's 1520px
   cap; on monitors wider than ~1600px keep the artwork edge-to-edge and
   center all three bands (logo, grid, copyright) by growing the side
   gutters. At 1600px the gutter resolves to the original 40px, so the
   transition is seamless and normal/smaller desktops are untouched.
   Same technique as the homepage's 1440-canvas centering in home.css. */
@media (min-width:1600px){
  .og-f-main,.og-f-base{padding-inline:calc((100vw - 1520px) / 2)}
}

@media (prefers-reduced-motion:reduce){
  .og-skip{transition:none}
  .og-social-row a{transition:none}
  .og-social-row a:hover{transform:none}
}

/* ============================================================
   Homepage Build Club band (.og-bc) — the studio-door poster teaser.
   Rebuilt 2026-07-15 to speak the rest of the site's language instead
   of a flat radial fill: the layered navy/red base coat + SVG grain +
   drifting og-ambient blobs the dark hero/CTA bands use, plus the red
   -45deg hatch and slash-divided meta strip carried straight off the
   /build-club poster this links to. The section ships opacity:0 so it
   rides the shared scroll reveal. Static markup lives in index.html
   just before .og-footer; the wordmark asset is shared with the poster.
   ============================================================ */
.og-bc{
  position:relative;overflow:hidden;isolation:isolate;color:#fff;
  background-color:#0a0c1a;
  background-repeat:repeat,no-repeat,no-repeat,no-repeat;
  background-size:160px 160px,auto,auto,auto;
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3CfeComponentTransfer%3E%3CfeFuncA type='linear' slope='0.22'/%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E"),
    radial-gradient(62% 95% at 84% -10%, rgba(200,40,63,.30) 0%, rgba(150,25,50,.14) 42%, rgba(150,25,50,0) 72%),
    radial-gradient(52% 82% at 2% 112%, rgba(46,74,168,.30) 0%, rgba(28,44,105,0) 62%),
    linear-gradient(150deg, #14162e 0%, #0d0f22 55%, #070812 100%);
  /* per-section blob palette for the shared og-ambient engine (red / blue / deep navy) */
  --og-amb-1:radial-gradient(closest-side,rgba(200,40,63,.62),transparent 72%);
  --og-amb-2:radial-gradient(closest-side,rgba(46,74,168,.5),transparent 72%);
  --og-amb-3:radial-gradient(closest-side,rgba(10,12,26,.92),transparent 74%);
  font-family:"Inter Variable",Inter,system-ui,sans-serif;
}
.og-bc-inner{
  position:relative;z-index:1;
  display:flex;align-items:center;justify-content:space-between;
  gap:clamp(32px,5vw,80px);
  max-width:1240px;margin:0 auto;
  padding:clamp(64px,8vw,104px) 40px;
}
.og-bc-copy{min-width:0;max-width:640px}
/* the italic serif wordmark treatment lifted from the poster's title —
   Cooper Hewitt is already loaded on the home page (see home.css) */
.og-bc h2{
  font-family:"Cooper Hewitt Bold Italic","Source Sans Pro Bold",Georgia,serif;
  font-weight:700;letter-spacing:.008em;text-transform:uppercase;
  font-size:clamp(34px,5vw,54px);line-height:.98;
  color:#fff;margin:0;
}
.og-bc-lede{
  font-size:clamp(16px,1.8vw,18px);line-height:1.65;color:#c9cddc;
  max-width:52ch;margin:20px 0 0;
}
/* the poster's slash-divided meta strip, top+bottom hairlines and all */
.og-bc-strip{
  display:flex;flex-wrap:wrap;align-items:center;
  margin:26px 0 0;padding:12px 0;
  border-top:1px solid rgba(255,255,255,.18);
  border-bottom:1px solid rgba(255,255,255,.18);
  font-family:"Inter Variable",Inter,sans-serif;
  font-variation-settings:"opsz" 20,"wght" 600;
  font-size:11.5px;letter-spacing:.16em;text-transform:uppercase;color:#eef0f5;
}
.og-bc-strip .d{color:#e0607a;margin:0 10px}
.og-bc-cta{
  display:inline-flex;align-items:center;gap:10px;
  min-height:48px;padding:5px 26px;border-radius:999px;margin-top:28px;
  background:linear-gradient(100deg,var(--red),#55395f);
  color:#fff;text-decoration:none;
  font-family:"Inter Variable",Inter,sans-serif;
  font-variation-settings:"opsz" 20,"wght" 600;font-size:15px;letter-spacing:-.15px;
  box-shadow:0 10px 26px rgba(200,40,63,.35);
  transition:filter .2s ease,transform .2s ease;
}
.og-bc-cta svg{width:17px;height:17px;transition:transform .2s ease}
@media (hover:hover) and (pointer:fine){
  .og-bc-cta:hover{filter:brightness(1.1);transform:translateY(-2px)}
  .og-bc-cta:hover svg{transform:translateX(3px)}
}
/* the wordmark sits in a pinned studio flyer rather than a bare sticker:
   a dark poster mat framing a white chip. The chip pads a tight-cropped
   copy of the wordmark (…-tight.png, transparent margins trimmed) evenly
   on every side, so the mark reads centred with equal white all around. */
.og-bc-markcard{
  flex:0 0 auto;position:relative;
  width:clamp(230px,26vw,340px);
  display:flex;
  background:linear-gradient(180deg,#171a36,#0f1128);
  border:1px solid rgba(255,255,255,.16);border-radius:3px;
  box-shadow:0 2px 0 rgba(0,0,0,.35), 0 34px 70px -24px rgba(0,0,0,.7);
  padding:clamp(16px,1.8vw,22px);
  transform:rotate(2.2deg);
  transition:transform .25s ease;
}
.og-bc-mc-chip{
  flex:1 1 auto;
  background:#fff;border-radius:2px;
  padding:clamp(26px,3vw,40px) clamp(22px,2.4vw,30px);
  display:flex;align-items:center;justify-content:center;
}
.og-bc-mc-chip img{width:100%;height:auto;display:block}
@media (hover:hover) and (pointer:fine){
  .og-bc-markcard:hover{transform:rotate(0deg) scale(1.02)}
}
@media (max-width:809.98px){
  .og-bc-inner{flex-direction:column-reverse;align-items:flex-start;padding:64px 24px 68px;gap:36px}
  .og-bc-copy{max-width:none}
  .og-bc-markcard{width:min(300px,78vw);transform:none}
}
@media (prefers-reduced-motion:reduce){
  .og-bc-cta,.og-bc-cta svg,.og-bc-markcard{transition:none}
  .og-bc-cta:hover{transform:none}
  .og-bc-cta:hover svg{transform:none}
  .og-bc-markcard:hover{transform:rotate(2.2deg)}
}

/* ---------- nav typography faces: the shared .og-topnav below is styled
   here (links in "Source Sans Pro Bold", CTA in "Source Sans 3" / "Source
   Sans Pro Regular"), but main.css is the ONLY sheet on every page — the
   @font-face registrations otherwise live in each page's own CSS. Every
   per-page sheet registers these EXCEPT programs/programs.css, so on the
   programs-family pages (/programs, /build-club, /programs/*) the nav fell
   back to a wider system sans-serif, inflating the row until the CTA
   overflowed the pill in the 1200–1280px band. Registering them here (same
   families/sources the other sheets use — harmless, idempotent duplicates)
   makes the shared nav self-sufficient in its own fonts at every width.
   The brand display face rides along for the same reason: the footer's
   giant wordmark (.og-f-mark) and column headings (.og-f-h) are styled
   here and appear on EVERY page, including the ones that load no sheet
   but this one (/privacy, /terms, /wechat, the outcome profiles, /404,
   /accessibility and their /zh twins). Without the registration those
   footers fell back to Inter — off-brand. ---------- */
@font-face{font-family:"Cooper Hewitt Bold Italic";src:url("/assets/media/assets/nkYS5GLtI8UZeuhvRXx6KQt2so.woff2");font-display:swap;font-style:normal;font-weight:712}
@font-face{font-family:"Source Sans Pro Bold";src:url("/assets/media/assets/mfJIi4ZsBj1zFYUodmv4QSL2jnU.woff2");font-display:swap;font-style:normal;font-weight:700}
@font-face{font-family:"Source Sans Pro Regular";src:url("/assets/media/assets/alL3C3eWkOZCVYCmuhkboeZkZk.woff2");font-display:swap;font-style:normal;font-weight:400}
@font-face{font-family:"Source Sans 3";font-style:italic;font-weight:700;font-display:swap;src:url(/assets/fonts/s/sourcesans3/v19/nwpDtKy2OAdR1K-IwhWudF-R3woAa8opPOrG97lwqF5JxCkSmg.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}

/* ---------- navigation bar: every page hides the original generated nav
   and uses only .og-topnav — the one nav design for the whole site, home
   and inner pages alike. Its shape differs by width: ≥1200px it's the
   floating rounded pill that docks into a square full-width bar on
   scroll; below 1200px (mobile/tablet, see the max-width:1199.98px
   block) it's a fixed, square, edge-to-edge bar the whole time — a pill
   there would be structural chrome borrowing the shape this site
   reserves for buttons (the CTA, badges), not a header.

   ALL nav-hiding rules key on .yg-w2UcW — the nav COMPONENT class that is in
   the static server-rendered markup of every page and survives hydration —
   NOT on the .og-navbar/.og-navwrap tags enhance.js adds. The tags arrive
   only after JS runs (and ~250ms after every SPA re-render), so any rule
   that needs them flashes the original italic styling first. The tags are
   kept in enhance.js only as a fallback hook for browsers without :has().
   ---------- */
/* the native generated bar is hidden at every width now (this used to be
   home-only, gated behind the JS-set body.og-home class — which meant it
   could never take effect before enhance.js ran and flashed the original
   italic nav on inner pages). .yg-w2UcW is static SSR markup present on
   first paint on every page, so this hides it with no JS and no flash.
   The :has() wrapper selectors hide the nav's containing div too (some
   pages' wrapper carries its own padding/background — belt-and-braces
   against an empty bar-shaped gap surviving the child being hidden). */
.yg-w2UcW,
div:has(> .yg-w2UcW),
div[class*="-container"]:has(.yg-w2UcW){
  display:none!important;
}

/* homepage: its nav is absolutely positioned over the hero and simply
   scrolls away — enhance.js injects this floating pill (same look as
   the inner-page nav) and shows it once the visitor scrolls */
.og-topnav{
  position:fixed;top:14px;left:0;right:0;z-index:9999;
  display:flex;justify-content:center;
  padding:0 clamp(16px,3vw,40px);
  opacity:0;transform:translateY(-140%);pointer-events:none;
  transition:opacity .25s ease,transform .25s ease,
    top .25s ease,padding .25s ease;
}
.og-topnav.og-show{opacity:1;transform:none;pointer-events:auto}
.og-topnav-bar{
  display:flex;align-items:center;gap:clamp(20px,2.5vw,40px);
  width:100%;max-width:1520px;height:64px;padding:0 10px 0 26px;
  border-radius:999px;background:#fff;
  box-shadow:0 12px 32px rgba(16,18,40,.16),0 2px 6px rgba(16,18,40,.08);
  transition:max-width .3s ease,border-radius .25s ease,
    box-shadow .25s ease,background .25s ease;
}
/* scrolled: the pill docks — fused to the viewport top, full width,
   square corners, the native inner-page bar's flat shadow. enhance.js
   toggles og-docked from the scroll position. */
.og-topnav.og-docked{top:0;padding:0}
.og-topnav.og-docked .og-topnav-bar{
  max-width:100vw;border-radius:0;
  background:#fff;
  box-shadow:0 2px 8px rgba(16,18,40,.08);
}
.og-tn-logo{display:flex;align-items:center;flex:none}
.og-tn-logo img{height:40px;width:auto;display:block}
.og-tn-links{display:flex;gap:clamp(12px,1.6vw,30px);flex:1;justify-content:center}
/* every top link stays on one line — without this the Programs link's inline
   caret wraps below the word in the tight 1200-1279 zone, breaking the row */
.og-tn-links a{white-space:nowrap}
/* SPECIFICITY GUARD — the .og-topnav prefix on the colour-bearing rules is
   deliberate. The nav is baked into every page, but each page also carries its
   own inline <style> that loads AFTER main.css. A page-scoped rule like
   `.wc a{color:inherit}` or `.ax a{color:inherit}` (specificity 0,1,1) would
   otherwise TIE a bare `.og-tn-links a` and win on source order — inheriting a
   dark page's white body text and painting the nav links white-on-white on the
   white pill. Prefixing with .og-topnav raises these to 0,2,1 so no realistic
   body-scoped page rule can clobber the shared nav's colours. Do NOT drop the
   prefix. (See roadmap.md "Shared-nav specificity" note.) */
.og-topnav .og-tn-links a{
  /* "Source Sans 3" is registered on this site as an ITALIC-ONLY face, so
     font-style:normal still renders slanted with it — use the true upright
     bold face instead. */
  font:700 16px/1 "Source Sans Pro Bold","Source Sans Pro Regular",sans-serif;
  letter-spacing:.02em;color:#343459;text-decoration:none;text-transform:uppercase;
  transition:color .18s ease,border-bottom-color .18s ease;
  border-bottom:2px solid transparent;
  /* top padding mirrors the 2px underline padding + 2px border below, so
     the label sits dead-centre of its 24px min-height tap-target box */
  padding:4px 0 2px;
}
@media (hover:hover) and (pointer:fine){
  .og-topnav .og-tn-links a:hover{
    color:var(--red);
    border-bottom-color:var(--red);
  }
}
/* the nav's home link had no hover state at all — the one link in the bar
   that gave no feedback (issue #89). Dimming the wordmark matches the
   restraint of the rest of the bar without tinting the brand mark. */
.og-tn-logo img{transition:opacity .18s ease}
@media (hover:hover) and (pointer:fine){
  .og-tn-logo:hover img{opacity:.72}
}
@media (prefers-reduced-motion:reduce){
  .og-tn-logo img{transition:none}
}
/* the CTA is an <a> too, so the same body-scoped page rules can override its
   colour; it stays readable (white on the red gradient) but harden it anyway
   so the whole nav is immune to page-level anchor rules */
.og-topnav .og-tn-cta{color:#fff}
/* remove the original nav link hover zoom/scale effects on inner pages */
.yg-w2UcW,
.yg-w2UcW *{
  --yg-transform:none!important;
}
.yg-w2UcW a,
.yg-w2UcW [role="link"],
.yg-w2UcW .yg-text{
  transform:none!important;
  scale:1!important;
  transition:color .18s ease,border-bottom-color .18s ease !important;
}
/* nav labels (APPROACH … GALLERY, preset 1wicq5s): upright, not the
   original italic. font-style alone is not enough — the site's
   "Source Sans 3" family ships only an italic 700 face, so the family
   itself must change to the true upright bold. Underline reserve space
   goes on the <a> ONLY — its parent <p> is also .yg-text, and
   putting border+padding on both stacked 8px of dead space under the
   text, floating the labels above the bar's vertical centre. */
.yg-w2UcW a.yg-styles-preset-1wicq5s{
  font-style:normal!important;
  font-family:"Source Sans Pro Bold","Source Sans Pro Regular",sans-serif!important;
  border-bottom:2px solid transparent;
  padding-bottom:2px;
  /* the nav's line boxes leave the labels ~1.5px above the bar's
     centre (logo and CTA sit at 0) — nudge without affecting layout */
  position:relative;top:1.5px;
}
/* :active stays outside the pointer guard — it is the touch press state,
   and the transform/scale resets have to land on every device */
.yg-w2UcW a:active,
.yg-w2UcW [role="link"]:active{
  transform:none!important;
  scale:1!important;
  border-bottom-color:var(--red);
  color:var(--red)!important;
}
@media (hover:hover) and (pointer:fine){
  .yg-w2UcW a:hover,
  .yg-w2UcW [role="link"]:hover,
  .yg-w2UcW .yg-text:hover{
    transform:none!important;
    scale:1!important;
    border-bottom-color:var(--red);
    color:var(--red)!important;
  }
}
.og-tn-cta{
  position:relative;overflow:hidden;
  display:flex;flex-direction:row;align-items:center;justify-content:center;
  flex:none;min-height:46px;padding:0 10px 0 22px;border-radius:999px;
  background:linear-gradient(100deg,var(--red),#55395f);
  background-size:170% 100%;background-position:0% 50%;
  color:#fff;text-decoration:none;font-style:italic;text-transform:uppercase;
  font-family:"Source Sans 3","Source Sans Pro Regular",sans-serif;
  box-shadow:0 2px 10px rgba(200,40,63,.28);
  transition:background-position .5s ease,box-shadow .3s ease,
             transform .3s cubic-bezier(.2,.8,.2,1);
}
/* shine sweep that travels across the pill on hover */
.og-tn-cta::before{
  content:"";position:absolute;top:0;left:-60%;width:45%;height:100%;
  background:linear-gradient(100deg,transparent,rgba(255,255,255,.38),transparent);
  transform:skewX(-18deg);pointer-events:none;
  transition:left .55s ease;
}
@media (hover:hover) and (pointer:fine){
  .og-tn-cta:hover{
    background-position:100% 50%;
    transform:translateY(-2px);
    box-shadow:0 9px 24px rgba(200,40,63,.42),0 3px 9px rgba(85,57,95,.36);
  }
  .og-tn-cta:hover::before{left:120%}
}
.og-tn-cta:active{transform:translateY(0)}
/* arrow rides in a translucent chip on the right, then launches forward */
.og-tn-cta-icon{
  flex:none;width:30px;height:30px;padding:6px;border-radius:999px;
  margin-left:11px;
  background:rgba(255,255,255,.15);
  transition:background .3s ease,transform .35s cubic-bezier(.2,.8,.2,1);
}
@media (hover:hover) and (pointer:fine){
  .og-tn-cta:hover .og-tn-cta-icon{
    background:rgba(255,255,255,.3);
    transform:translateX(3px);
  }
}
.og-tn-cta .l1,
.og-tn-cta .l2{font-weight:800;font-size:14px;letter-spacing:.05em}
/* a single word-space between the two words (the flex gap is gone so the
   words don't drift apart, and the icon keeps its own margin) */
.og-tn-cta .l1{margin-right:.34em}
@media (prefers-reduced-motion:reduce){
  .og-tn-cta,.og-tn-cta::before,.og-tn-cta-icon{transition:none}
  .og-tn-cta:hover{transform:none}
  .og-tn-cta:hover::before{left:-60%}
  .og-tn-cta:hover .og-tn-cta-icon{transform:none}
  .og-tnd-cta:hover .og-tn-cta-icon{transform:none}
}

/* ---------- Programs dropdown (pill nav .og-tn-item + native bar
   .og-nav-prog share the same menu panel). Pure CSS disclosure: opens
   on hover and on keyboard focus within, so it needs no JS state; the
   top-level "Programs" is itself a real link to the /programs/ hub,
   which doubles as the touch path. ---------- */
.og-tn-item,.og-nav-prog{position:relative}
.og-tn-item>a{display:inline-block;white-space:nowrap}
/* dropdown chevron: rides inside the Programs link, inherits its
   colour, and flips 180° whenever the menu is open (hover or
   keyboard focus — the same states that show the panel) */
.og-tn-caret{
  width:10px;height:7px;margin-left:6px;
  display:inline-block;vertical-align:middle;
  transform:translateY(-1px);
  transition:transform .18s ease;
}
.og-tn-item:focus-within>a .og-tn-caret,
.og-nav-prog:focus-within .og-tn-caret{
  transform:translateY(-1px) rotate(180deg);
}
@media (hover:hover) and (pointer:fine){
  .og-tn-item:hover>a .og-tn-caret,
  .og-nav-prog:hover .og-tn-caret{
    transform:translateY(-1px) rotate(180deg);
  }
}
@media (prefers-reduced-motion:reduce){
  .og-tn-caret{transition:none}
}
.og-tn-menu{
  position:absolute;left:50%;top:100%;transform:translateX(-50%) translateY(10px);
  min-width:236px;padding:10px;z-index:10000;
  display:grid;gap:2px;
  background:#fff;border-radius:18px;
  box-shadow:0 16px 40px rgba(16,18,40,.2),0 3px 8px rgba(16,18,40,.1);
  opacity:0;visibility:hidden;pointer-events:none;
  transition:opacity .18s ease,transform .18s ease,visibility 0s linear .18s;
}
/* invisible bridge so the pointer can cross the gap without closing */
.og-tn-menu::before{
  content:"";position:absolute;left:0;right:0;top:-14px;height:14px;
}
/* the panel opens on focus-within everywhere (keyboard, and a touch tap
   that focuses a link inside it) and additionally on hover only where a
   real pointer exists — on a phone the top-level Programs link is itself
   the path to the hub, so nothing is lost and no menu is left hanging
   open after a tap */
.og-tn-item:focus-within>.og-tn-menu,
.og-nav-prog:focus-within .og-tn-menu{
  opacity:1;visibility:visible;pointer-events:auto;
  transform:translateX(-50%) translateY(4px);
  transition:opacity .18s ease,transform .18s ease,visibility 0s;
}
@media (hover:hover) and (pointer:fine){
  .og-tn-item:hover>.og-tn-menu,
  .og-nav-prog:hover .og-tn-menu{
    opacity:1;visibility:visible;pointer-events:auto;
    transform:translateX(-50%) translateY(4px);
    transition:opacity .18s ease,transform .18s ease,visibility 0s;
  }
}
/* menu items override the generic .og-tn-links a / preset link styling.
   .og-tn-menu is nested inside .og-tn-links (.og-tn-links > .og-tn-item >
   .og-tn-menu > a), so the SPECIFICITY GUARD rule above (.og-topnav
   .og-tn-links a, specificity 0,2,1) also matches these menu items and was
   winning over the plain .og-tn-menu a (0,1,1) — only the !important
   properties (color, border-bottom) came through; padding/font-size/
   line-height silently fell back to the top-level link's cramped
   4px-0-2px/16px/1 values instead of the menu's own 10px-14px/14px/1.2.
   Prefixing with .og-topnav ties the specificity (0,2,1) and wins on
   source order (declared later), same fix pattern as the guard itself. */
.og-topnav .og-tn-menu a{
  display:block;padding:10px 14px;border-radius:12px;
  font:700 14px/1.2 "Source Sans Pro Bold","Source Sans Pro Regular",sans-serif;
  font-style:normal!important;letter-spacing:.02em;text-transform:uppercase;
  color:#343459!important;text-decoration:none;border-bottom:none!important;
  transition:background .15s ease,color .15s ease;
}
.og-tn-menu a span{
  display:block;margin-top:3px;
  font:600 11px/1 "Inter Variable",Inter,system-ui,sans-serif;
  letter-spacing:.06em;color:#5a5a7a;text-transform:uppercase;
}
.og-topnav .og-tn-menu a:focus-visible{
  background:#f4e9eb;color:var(--red)!important;
}
.og-tn-menu a:focus-visible span{color:#a2404f}
@media (hover:hover) and (pointer:fine){
  .og-topnav .og-tn-menu a:hover{
    background:#f4e9eb;color:var(--red)!important;
  }
  .og-tn-menu a:hover span{color:#a2404f}
}
@media (prefers-reduced-motion:reduce){
  .og-tn-menu{transition:none}
}

/* ---------- native bar CTA on one line: the generator stacks
   "START<br>YOUR JOURNEY" with an 18px/11px size split. Drop the <br>
   and unify the sizes so it reads as one phrase. Scoped to the nav —
   the same component elsewhere on the pages keeps its stacked look. */
.yg-w2UcW .yg-usOLM br{display:none}
/* !important is required: the generator sets these vars as inline
   styles on the same elements */
.yg-w2UcW .yg-usOLM p.yg-text{--yg-font-size:13px!important;white-space:nowrap}
.yg-w2UcW .yg-usOLM p.yg-text span.yg-text{--yg-font-size:13px!important}
/* the button's container is sized for the stacked layout — let the
   one-liner breathe instead of clipping. The visible gradient pill is
   the nested .yg-1egba9c span, also sized for the stacked button, so
   the wider text spilled past it: stretch the span to the anchor and
   lift the text above it. */
.yg-w2UcW .yg-hlnwr7-container{width:auto!important;min-width:0}
.yg-w2UcW .yg-usOLM{position:relative;padding:0 18px}
.yg-w2UcW .yg-usOLM .yg-s0ukm0{position:relative;z-index:2!important}
.yg-w2UcW .yg-usOLM .yg-1egba9c{
  position:absolute!important;inset:0!important;
  width:auto!important;height:auto!important;
  z-index:1!important;
}
/* centre the links in the bar (they ship right-packed against the
   button). The row holds links + CTA together and the logo image is
   absolutely positioned (its flex container computes ~0 wide), so:
   logo container gives up its greedy flex:1, the row takes the full
   bar and centres its links, and the CTA leaves the flow to pin to
   the bar's right edge — link-group centre = bar centre, same
   geometry as the pill nav. */
.yg-w2UcW .yg-1a0c9gy{flex:0 0 auto!important}
.yg-w2UcW .yg-1qxkp2j{position:relative}
.yg-w2UcW .yg-12tn0r6{flex:1 1 auto!important;justify-content:center!important}
.yg-w2UcW .yg-hlnwr7-container{
  position:absolute!important;right:0;top:50%;transform:translateY(-50%);
}

/* ---------- navbar hover zoom fix: disable yg-motion whileHover
   animation. Scale 1.2 is applied via yg-motion's whileHover variant
   object (xt) to the link's parent [data-yg-component-type=
   "RichTextContainer"] div (class yg-1q0gkoa / yg-fm4xvw / etc.),
   not to the <a> itself. Prior CSS overrides targeted the wrong element.
   ------*/
.yg-w2UcW [data-yg-component-type="RichTextContainer"]{
  transform:none!important;
}

/* ---------- mobile nav: below 1200px the pill acts as a compact fixed
   header. The burger opens the .og-tn-drawer panel (static markup next
   to the bar, inside .og-topnav; enhance.js only toggles it) over a
   blurred .og-tn-scrim. ---------- */
.og-tn-burger{
  display:none;flex-direction:column;justify-content:center;gap:5px;
  width:44px;height:44px;padding:11px;flex:none;
  background:none;border:0;border-radius:6px;cursor:pointer;
  transition:background .18s ease;
}
@media (hover:hover) and (pointer:fine){
  .og-tn-burger:hover{background:rgba(52,52,89,.08)}
}
.og-tn-burger:active{background:rgba(52,52,89,.13)}
.og-tn-burger span{
  display:block;height:3px;width:100%;background:#343459;border-radius:1px;
  transition:transform .22s cubic-bezier(.2,.8,.2,1),opacity .18s ease,background .18s ease;
  /* iOS Safari can skip repainting the middle bar when .og-open toggles off,
     leaving only the two outer bars on screen (reads as "two dots"). Promoting
     each bar to its own compositor layer forces a clean repaint on every
     toggle; no visual change where repaint already works. */
  -webkit-backface-visibility:hidden;backface-visibility:hidden;transform:translateZ(0);
}
.og-topnav.og-open .og-tn-burger span{background:var(--red)}
.og-topnav.og-open .og-tn-burger span:nth-child(1){transform:translateY(8px) rotate(45deg)}
.og-topnav.og-open .og-tn-burger span:nth-child(2){opacity:0}
.og-topnav.og-open .og-tn-burger span:nth-child(3){transform:translateY(-8px) rotate(-45deg)}
/* drawer + scrim exist on desktop too (enhance.js injects them once);
   they are mobile-only UI */
.og-tn-scrim,.og-tn-drawer{display:none}
/* the drawer locks the page scroll while open */
html.og-nav-open{overflow:hidden!important}

/* below 1200px the pill (with its burger drawer) is the site's one
   header. This used to cut over at 810px, but the native bar's Tablet
   variant (810–1199.98) ships only a logo and a runtime-driven burger —
   dead markup since the de-Framer — so tablet visitors had no working
   navigation at all. The original site used burger nav at these widths
   too; the pill's drawer restores that. */
@media (max-width:1199.98px){
  /* the floating glass pill was borrowed template chrome — on this site
     a pill shape means "button" (the CTA, badges), not "header". Below
     1200px the header is standard fixed chrome instead: flush to the
     viewport, square, solid, always the same shape whether you're at
     the top of the page or scrolled — no shape-shift to track. The only
     change on scroll is .og-docked's existing shadow/opacity bump
     (shared with desktop), which reads as normal elevation, not a
     redesign mid-scroll. */
  .og-topnav{
    display:flex;opacity:1;transform:none;pointer-events:auto;
    top:0;padding:0;
  }
  .og-topnav-bar{
    position:relative;z-index:2;
    width:100%;height:60px;padding:0 18px;border-radius:0;
    background:#fff;box-shadow:none;
    -webkit-backdrop-filter:none;backdrop-filter:none;
  }
  .og-tn-logo img{height:30px}
  .og-tn-burger{display:flex;margin-left:auto}
  .og-tn-links,.og-tn-cta{display:none}

  /* .og-docked's elevation shadow (main.css ~320) is a scroll-position cue
     for the bar sitting over page content — it doesn't know the drawer is
     open. With the opaque full-screen drawer up, that shadow has nothing
     to elevate over; it just reads as a stray line across the top of the
     Programs card. Drop it whenever the drawer is open, docked or not. */
  .og-topnav.og-open .og-topnav-bar{box-shadow:none}

  /* scrim: dims + blurs the page behind the open drawer */
  .og-tn-scrim{
    display:block;position:fixed;inset:0;z-index:0;
    background:rgba(16,14,36,.45);
    -webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);
    opacity:0;visibility:hidden;pointer-events:none;
    transition:opacity .25s ease,visibility 0s linear .25s;
  }
  .og-topnav.og-open .og-tn-scrim{
    opacity:1;visibility:visible;pointer-events:auto;
    transition:opacity .25s ease,visibility 0s;
  }

  /* drawer panel: a full-screen takeover, not a dropdown card — the bar
     (z-index:2) stays above it so the burger-turned-X keeps working as
     the close control. The bar is a fixed 56px everywhere now (no more
     floating-vs-docked offset to track), so the top clearance is fixed
     too — bar height plus a fixed breathing gap. */
  /* flex here buys one thing only — the CTA's margin-top:auto bottom-anchor.
     Deliberately no `gap`: the separation between the list and the CTA is
     carried by ordinary margins instead, so it survives even if this
     container is laid out as a block (see the flex-independence note on
     the rows below). */
  .og-tn-drawer{
    display:flex;flex-direction:column;
    position:fixed;inset:0;z-index:1;
    overflow-y:auto;overscroll-behavior:contain;
    padding:80px 20px calc(28px + env(safe-area-inset-bottom));
    background:#fff;
    opacity:0;visibility:hidden;pointer-events:none;
    transform:translateY(16px);
    transition:opacity .22s cubic-bezier(.2,.8,.2,1),
      transform .22s cubic-bezier(.2,.8,.2,1),visibility 0s linear .22s;
  }
  .og-topnav.og-open .og-tn-drawer{
    opacity:1;visibility:visible;pointer-events:auto;transform:none;
    transition:opacity .22s cubic-bezier(.2,.8,.2,1),
      transform .22s cubic-bezier(.2,.8,.2,1),visibility 0s;
  }
  /* the two content groups cascade in behind the panel */
  .og-tn-drawer>*{
    opacity:0;transform:translateY(10px);
    transition:opacity .3s ease,transform .3s ease;
  }
  .og-topnav.og-open .og-tn-drawer>*{opacity:1;transform:none}
  .og-topnav.og-open .og-tn-drawer>:nth-child(1){transition-delay:.05s}
  .og-topnav.og-open .og-tn-drawer>:nth-child(2){transition-delay:.11s}

  /* main page links: app-style rows with right chevrons. One flat list of
     six destinations plus the language row — the tinted Programs card that
     used to sit above it (PROGRAMS / VIEW ALL over ArtLab · Foundation
     Studio · Portfolio Track) is gone, and /programs is now just the first
     row like every other section. That buys the vertical room this type
     scale needs: 26px rows, ~66px tall, well past the 44px minimum target
     size, and readable at arm's length without zooming. */
  /* rows read as pills (12px radius + a tinted press/hover state), so they
     need air between them - butted together they looked like one slab.
     The air is per-row `margin-bottom`, not a grid `gap`, for the same
     flex-independence reason as the row rule below. */
  .og-tnd-links{padding:0 8px;margin-bottom:20px}

  /* ---- rows: one row = one <div>, on purpose ----
     Two prior attempts here both relied on the row *anchor* carrying a
     specific `display` value — first `flex`, then `block` — and each time a
     device field report showed exactly the failure you'd get if that
     specific `display` value were silently not applying (chevron and label
     splitting onto separate lines under `flex`; two whole destinations
     sharing one line under `block`, which is the anchor's un-styled default
     of `inline`). Nothing in this stylesheet ever overrode `display` on
     these anchors, no engine available here has ever reproduced either
     failure, and the deployed CSS was current both times — the trigger was
     never identified, twice.
     So this round doesn't add a third guess at *which* display value is
     safe. It stops the layout's core guarantee — one destination per
     line — from depending on the anchor's `display` at all. Each `<a>` is
     now wrapped in a plain `<div class="og-tnd-row">` (markup in
     bake_static.py's DRAWER constant). A `<div>` is block-level by nothing
     more than the UA stylesheet every browser ships, not by any rule in
     this file, so nothing here can fail to apply it. Block boxes always
     stack on separate lines regardless of what's inside them, so even in
     the worst case — the anchor reverting all the way to inline — the
     next destination is a sibling inside its OWN div and can never share
     the first one's line. The anchor keeps `display:block` for the normal
     case (full-width pill, chevron lane, tap target), it's just no longer
     the thing standing between one destination and the next.
     `overflow:hidden` is the same belt-and-suspenders logic applied to the
     cosmetic edge case: if the anchor's background/padding ever did paint
     as inline (Safari still allows a background to bleed above/below an
     inline line box), the row div clips it at its own edge instead of
     bleeding into its neighbour. */
  .og-tnd-row{margin-bottom:8px;border-radius:12px;overflow:hidden}
  .og-tnd-row:last-child{margin-bottom:0}
  .og-topnav .og-tnd-links a{
    display:block;position:relative;
    padding:20px 44px 20px 16px;
    border-radius:12px;text-decoration:none;
    /* line-height 1.15, not 1: at this size a flat 1 clips the CJK glyphs
       on the /zh drawer rows (课程, 教学理念) top and bottom */
    font:700 26px/1.15 "Source Sans Pro Bold","Source Sans Pro Regular",sans-serif;
    letter-spacing:.02em;color:#343459;text-transform:uppercase;
    transition:background .15s ease,color .15s ease;
  }
  /* chevron scaled with the label so it still reads as an affordance next to
     26px type. Centred by pinning the top edge to the row's midpoint and
     pulling back half its own height, then rotating about its centre. */
  .og-tnd-links a::after{
    content:"";position:absolute;right:18px;top:50%;
    width:11px;height:11px;
    border-right:2.5px solid rgba(52,52,89,.35);
    border-top:2.5px solid rgba(52,52,89,.35);
    transform:translateY(-50%) rotate(45deg);
  }
  /* :active is the touch press — it must stay unguarded, and it is also
     what stops the tint sticking once the finger lifts */
  .og-tnd-links a:active{background:#f6f0f1;color:var(--red)}
  .og-tnd-links a:active::after{border-color:var(--red)}
  @media (hover:hover) and (pointer:fine){
    .og-tnd-links a:hover{background:#f6f0f1;color:var(--red)}
    .og-tnd-links a:hover::after{border-color:var(--red)}
  }
  /* the page the visitor is on */
  .og-tn-drawer a[aria-current="page"]{color:var(--red)}
  .og-tnd-links a[aria-current="page"]::after{border-color:var(--red)}

  /* full-width gradient CTA — sized up with the rows so it still reads as
     the loudest thing in the drawer.
     `margin-top:auto` parks it against the drawer's bottom padding instead
     of letting it sit right under the last row with the leftover height
     dumped below it: the primary action belongs in the thumb zone, and on
     a tall phone that dead space was most of the lower third. It collapses
     to nothing once the rows fill the drawer, so short viewports are
     unaffected. */
  /* `text-align:center` backs up the flex centring, and the arrow chip is
     inline-block + vertical-align, so the button still reads correctly if
     this box is laid out as a block. margin-top:auto simply computes to 0
     there, leaving the CTA under the list — the list's own margin-bottom
     keeps them apart either way. */
  .og-topnav .og-tnd-cta{
    display:flex;align-items:center;justify-content:center;text-align:center;
    flex:none;min-height:68px;margin:auto 8px 6px;padding:5px 24px;
    border-radius:999px;text-decoration:none;
    background:linear-gradient(100deg,var(--red),#55395f);
    box-shadow:0 10px 24px rgba(200,40,63,.3);
    color:#fff;font-style:italic;text-transform:uppercase;
    font-family:"Source Sans 3","Source Sans Pro Regular",sans-serif;
    font-weight:800;font-size:21px;letter-spacing:.05em;
    transition:filter .2s ease;
  }
  @media (hover:hover) and (pointer:fine){
    .og-tnd-cta:hover{filter:brightness(1.12)}
  }
  /* the drawer CTA carries the same arrow chip as the desktop pill */
  .og-topnav .og-tnd-cta .og-tn-cta-icon{
    display:inline-block;vertical-align:middle;
    width:30px;height:30px;padding:6px;margin-left:12px;
  }
  @media (hover:hover) and (pointer:fine){
    .og-tnd-cta:hover .og-tn-cta-icon{background:rgba(255,255,255,.28);transform:translateX(3px)}
  }
}
/* the drawer runs all the way to 1199px, so on a tablet or a landscape
   phone a full-bleed row is ~780px wide — the chevron ends up a hand's
   width from the label it belongs to, and the eye has nothing to track.
   Cap the content column at a normal reading measure and centre it.
   Selectors carry the .og-topnav prefix to outrank the base CTA rule's
   own margin shorthand above; only the inline margins are restated, so
   its margin-top:auto bottom-anchoring survives. */
@media (max-width:1199.98px) and (min-width:560px){
  .og-topnav .og-tnd-links,
  .og-topnav .og-tnd-cta{
    width:100%;max-width:560px;margin-left:auto;margin-right:auto;
  }
}

/* short viewports (common phones in portrait — 375x667, 320x568 — and
   any landscape phone): the drawer's default vertical rhythm (80px top
   clearance + 20px gaps + generous row padding) is taller than a 667px-high
   screen, so the CTA — the whole point of the drawer — sat below the fold
   with no visual hint it was scrollable. Compact the rhythm instead of
   relying on overflow-y:auto to save it. Dropping the Programs card freed
   three rows' worth of height, so the compacted scale can stay large: 23px
   rows here, still well above the 19px the full-height drawer used to run
   before this pass. */
@media (max-width:1199.98px) and (max-height:700px){
  /* 68px, not the old 56px: the bar is 60px tall, so 56px tucked the first
     row's top 4px (and its press tint) under it. Dropping the Programs card
     freed the height to clear the bar properly. */
  .og-tn-drawer{padding-top:68px;padding-bottom:14px}
  /* prefixed to match the .og-topnav-guarded base rule's specificity above,
     so this short-viewport padding override still wins. margin-bottom lives
     on .og-tnd-row now, not the anchor — see the rows comment above. */
  .og-topnav .og-tnd-links a{padding-top:14px;padding-bottom:14px;font-size:23px}
  .og-tnd-row{margin-bottom:5px}
  .og-topnav .og-tnd-cta{min-height:60px;font-size:19px}
  .og-tnd-links{margin-bottom:10px}
}
@media (prefers-reduced-motion:reduce){
  .og-topnav,.og-topnav-bar,.og-tn-scrim,.og-tn-drawer,.og-tn-drawer>*{transition:none}
  .og-tn-drawer,.og-tn-drawer>*{transform:none}
  .og-tn-burger span{transition:none}
}

/* ---------- scroll-reveal failsafe: sections ship at inline opacity:0
   (the old engine's entrance states) and enhance.js animates them in on
   scroll. If enhance.js ever fails to run (html never gets .og-js), force
   everything visible after a beat so no content can be lost. ---------- */
@keyframes og-force-visible{to{opacity:1;transform:none}}
html:not(.og-js) [style*="opacity:0"],
html:not(.og-js) [style*="opacity: 0"]{
  animation:og-force-visible .6s ease 1.2s forwards;
}
/* the footer mark is static markup revealed by enhance.js — same deal */
html:not(.og-js) .og-f-mark{opacity:1;transform:none}

/* ---------- engine-drawn section backgrounds, redrawn in CSS. The
   removed React runtime painted these three full-bleed bands with its
   BlurGradient component; without it they were white with invisible
   white text. Same recipe as the mobile footer artwork: layered
   gradients + an SVG grain tile (colored noise, like the original).
   .yg-92jzxg  = homepage CTA band ("A boutique art & design studio")
   .yg-1ezo1vh = /approach hero ("OUR APPROACH")
   .yg-4veble  = /lets-chat hero ("YOUR JOURNEY STARTS HERE")
   .yg-10bma76 = /about-us hero ("ABOUT US")
   .yg-pe8j1z  = /schedule hero ("EVENTS + WORKSHOPS")
   .yg-rmvr16  = /outcomes hero ("OUTCOMES")
   .yg-1xpoeol = /gallery hero ("GALLERY")
   These static coats paint instantly and with no JS; the og-ambient
   engine below animates on top of them. ---------- */
.yg-92jzxg,
.yg-1ezo1vh,
.yg-4veble,
.yg-10bma76,
.yg-pe8j1z,
.yg-rmvr16,
.yg-1xpoeol{
  background-color:#0c1124;
  background-repeat:repeat,no-repeat,no-repeat,no-repeat;
  background-size:160px 160px,auto,auto,auto;
  position:relative;
  isolation:isolate;
}
.yg-pe8j1z,
.yg-rmvr16,
.yg-1xpoeol{
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3CfeComponentTransfer%3E%3CfeFuncA type='linear' slope='0.22'/%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E"),
    radial-gradient(65% 125% at 38% 10%, rgba(46,74,168,.55) 0%, rgba(28,44,105,.3) 45%, rgba(12,18,40,0) 74%),
    radial-gradient(45% 70% at 4% 90%, rgba(30,66,60,.3) 0%, rgba(15,33,40,0) 65%),
    linear-gradient(115deg, #0c142b 0%, #081020 45%, #03050b 100%);
}
/* The CTA band is the one coat carrying a drifting dust field over it
   (og-field), and dust made of light only reads on something dark — the
   band shipped bright enough (a near-white sky wash across the bottom-left
   third) that fine motes disappeared into it. Same composition, same three
   brand colours, taken down to studio-wall depth: the sky reads as a glow
   in the corner instead of a wash across it, and the red is a bloom rather
   than a field. 2026-08. */
.yg-92jzxg{
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3CfeComponentTransfer%3E%3CfeFuncA type='linear' slope='0.22'/%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E"),
    radial-gradient(62% 95% at 6% 92%, rgba(168,216,242,.40) 0%, rgba(132,186,220,.17) 28%, rgba(120,175,210,0) 56%),
    radial-gradient(58% 85% at 50% 26%, rgba(158,28,50,.62) 0%, rgba(122,20,42,.30) 45%, rgba(110,18,38,0) 74%),
    linear-gradient(100deg, #3f1424 0%, #331229 35%, #0e1433 70%, #05070f 100%);
}
.yg-1ezo1vh{
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3CfeComponentTransfer%3E%3CfeFuncA type='linear' slope='0.22'/%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E"),
    radial-gradient(70% 130% at 32% 0%, rgba(64,102,190,.6) 0%, rgba(38,64,130,.32) 42%, rgba(18,28,58,0) 72%),
    radial-gradient(45% 70% at 6% 96%, rgba(36,84,74,.32) 0%, rgba(18,40,48,0) 65%),
    linear-gradient(115deg, #0f172e 0%, #0a1124 45%, #04060d 100%);
}
.yg-4veble{
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3CfeComponentTransfer%3E%3CfeFuncA type='linear' slope='0.22'/%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E"),
    radial-gradient(55% 95% at 22% 18%, rgba(58,74,64,.42) 0%, rgba(28,38,36,0) 65%),
    radial-gradient(70% 115% at 86% 82%, rgba(28,46,104,.55) 0%, rgba(14,24,58,0) 70%),
    linear-gradient(105deg, #171c1e 0%, #10141f 50%, #0a0f22 100%);
}
.yg-10bma76{
  background-color:#6b1522;
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3CfeComponentTransfer%3E%3CfeFuncA type='linear' slope='0.22'/%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E"),
    radial-gradient(60% 115% at 4% 82%, rgba(226,229,238,.92) 0%, rgba(205,195,208,.45) 22%, rgba(180,160,178,0) 48%),
    radial-gradient(85% 130% at 80% 28%, rgba(148,20,36,.7) 0%, rgba(118,14,30,0) 72%),
    linear-gradient(100deg, #8d1e2d 0%, #7e1826 45%, #5a0f1c 100%);
}

/* ============================================================
   og-ambient — the studio's own ambient background engine.
   The removed runtime animated these bands with a blurred moving
   gradient; this is the dependency-free replacement. enhance.js
   mounts one .og-ambient backdrop per configured section: three
   blurred colour blobs drifting on slow GPU-only transform loops
   under an SVG grain veil. The blobs sit at z-index:-1 inside the
   section's isolated stacking context, so they always paint above
   the static base coat and below every piece of content. Colours
   come from the per-section --og-amb-* palette variables.
   ============================================================ */
.og-ambient{
  position:absolute;inset:0;overflow:hidden;
  pointer-events:none;z-index:-1;
}
.og-ambient i{
  position:absolute;display:block;border-radius:50%;
  filter:blur(80px);opacity:.9;
  will-change:transform;
}
.og-ambient .b1{width:58%;height:135%;left:-14%;top:-24%;background:var(--og-amb-1);animation:og-drift1 13s ease-in-out infinite}
.og-ambient .b2{width:54%;height:122%;left:26%;top:-10%;background:var(--og-amb-2);animation:og-drift2 17s ease-in-out infinite}
.og-ambient .b3{width:64%;height:145%;right:-22%;top:-20%;background:var(--og-amb-3);animation:og-drift3 15s ease-in-out infinite}
/* continuous flowing loops — three stops each so the blobs wander rather
   than shuttle, with scale and opacity breathing for the lava-lamp feel
   the original runtime's animated gradients had */
@keyframes og-drift1{
  0%{transform:translate3d(-8%,-6%,0) scale(1);opacity:.9}
  33%{transform:translate3d(14%,10%,0) scale(1.28);opacity:.75}
  66%{transform:translate3d(2%,-12%,0) scale(.9);opacity:.95}
  100%{transform:translate3d(-8%,-6%,0) scale(1);opacity:.9}
}
@keyframes og-drift2{
  0%{transform:translate3d(6%,9%,0) scale(1.15) rotate(10deg);opacity:.85}
  33%{transform:translate3d(-12%,-9%,0) scale(.88) rotate(-8deg);opacity:1}
  66%{transform:translate3d(9%,-4%,0) scale(1.22) rotate(4deg);opacity:.7}
  100%{transform:translate3d(6%,9%,0) scale(1.15) rotate(10deg);opacity:.85}
}
@keyframes og-drift3{
  0%{transform:translate3d(9%,6%,0) scale(1);opacity:.9}
  33%{transform:translate3d(-9%,-11%,0) scale(1.25);opacity:.8}
  66%{transform:translate3d(-14%,7%,0) scale(.92);opacity:1}
  100%{transform:translate3d(9%,6%,0) scale(1);opacity:.9}
}
@media (prefers-reduced-motion:reduce){
  /* stop the drift and release the compositor-layer hint — a motionless
     blurred blob doesn't need its own retained GPU layer. Selectors are
     per-blob, not `.og-ambient i`: the animations are declared on .b1/.b2/
     .b3, so a one-class selector loses the cascade and the drift survives
     the media query (it did, until 2026-08). */
  .og-ambient .b1,
  .og-ambient .b2,
  .og-ambient .b3{animation:none;will-change:auto}
}

/* ============================================================
   og-field — the moving half of the background (2026-08).
   og-ambient above gives the colour bleed; this gives it a hand.

   A flow field, not a gradient. A few hundred motes of dust are
   carried along the curl of an analytic stream function (three
   sines — no noise library, and smooth everywhere), each leaving a
   short beaded wake behind it. Taking velocity as the curl of a
   scalar makes the field divergence-free: the dust shears and
   swirls but never piles into a drain, so the band stays evenly
   populated without any density bookkeeping. A slow upward bias in
   the mean flow gives it the rise.

   The canvas is created by enhance.js (initField), never by the
   page HTML — a no-JS visitor keeps the plain static coat below and
   loses only motion — and under prefers-reduced-motion the field is
   drawn once and left still. Everything except the scrim lives in
   JS, including the centre-thinning that keeps dust off the copy:
   as a CSS mask that would cost a compositing pass on every frame,
   where in the draw it is one multiply per mote. Colours come from
   the per-section --og-field-* palette variables (raw RGB triplets,
   read once at mount).

   Replaces the blurred ribbon layer that shipped earlier in 2026-08
   (og-flow): at ribbon scale the shapes read as melted stripes
   behind the copy rather than as light.
   ============================================================ */
.og-field{
  position:absolute;inset:0;overflow:hidden;
  pointer-events:none;z-index:-1;
}
.og-field>canvas{
  position:absolute;inset:0;display:block;width:100%;height:100%;
}
/* copy scrim. Moving light under white text is a contrast problem the
   static coat never had: the field swings the luminance behind the band's
   own copy as it drifts, and the sub-headline was already under 3:1 on the
   bottom-left sky blob before any of this existed (2.74:1 measured). A soft
   centre-weighted darkening, painted last inside the layer so it sits over
   the dust and still under all content, holds the copy above AA large-text
   contrast at every moment of the drift without flattening the art at the
   band's edges. */
.og-field::after{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(82% 88% at 50% 50%,
    rgba(5,8,18,.30) 0%,rgba(5,8,18,.17) 56%,rgba(5,8,18,0) 88%);
}

/* ============================================================
   og type motion — the creative-studio animation layer (2026-07).
   Companion JS lives in enhance.js (initScrub / initCharCascade /
   typeMarquee). Everything here is decorative, sits behind or inside
   existing content, and is disabled under prefers-reduced-motion.
   ============================================================ */
/* scroll-scrubbed word reveal: enhance.js wraps each word of a
   [data-og-scrub] element in .og-sw and toggles .on as the visitor
   scrolls through it (both directions). Words listed in the element's
   data-og-accent settle on the brand red instead. The three colours
   are custom properties so other pages/bands can re-tint the effect. */
[data-og-scrub] .og-sw{color:var(--og-scrub-dim,rgba(87,87,87,.22));transition:color .3s linear}
[data-og-scrub] .og-sw.on{color:var(--og-scrub-on,rgb(87,87,87))}
[data-og-scrub] .og-sw.og-sw-acc.on{color:var(--og-scrub-acc,#ca414e)}
/* words inside (or of) an .au-red block (e.g. the /about-us closing
   line) land on the accent colour without per-word accent marks */
[data-og-scrub] .au-red .og-sw.on,
[data-og-scrub].au-red .og-sw.on{color:var(--og-scrub-acc,#eb405a)}
/* per-band scrub palettes: the statement act fills near-black on the
   white page; the CTA band fills white over its dark gradient (its
   own red is already in the backdrop, so no accent there — that hook
   came off in the 2026-07 static pass, palette kept for if it returns) */
.hp-ac{--og-scrub-dim:rgba(17,17,17,.12);--og-scrub-on:#111;--og-scrub-acc:#ca414e}
.hp-cta{--og-scrub-dim:rgba(255,255,255,.22);--og-scrub-on:#fff;--og-scrub-acc:#fff}
/* The statement act is a below-1200px design (home.css ARTCLASS): at
   desktop widths that band reverts to its original two-column layout,
   where the copy is 20px body text and a word-by-word fill would just
   read as broken. enhance.js still splits the words there — the split is
   viewport-independent on purpose, so resizing never has to unwind it —
   so flatten the spans to the paragraph's own colour instead. Needs to
   out-specify the .on/.og-sw-acc rules above, hence the extra classes. */
@media (min-width:1200px){
  .hp-ac [data-og-scrub] .og-sw,
  .hp-ac [data-og-scrub] .og-sw.on,
  .hp-ac [data-og-scrub] .og-sw.og-sw-acc,
  .hp-ac [data-og-scrub] .og-sw.og-sw-acc.on{color:inherit;transition:none}
}

/* intro headline character cascade: the per-character spans (.hp-c)
   ship in index.html; .og-chars (JS-only, so no-JS never hides text)
   arms the hidden state and .og-chars-on staggers the letters in */
.og-chars .hp-c,.og-chars .au-c{opacity:0;transform:translateY(.6em)}
.og-chars.og-chars-on .hp-c,.og-chars.og-chars-on .au-c{
  opacity:1;transform:none;
  transition:opacity .45s ease,transform .55s cubic-bezier(.2,.7,0,1);
  transition-delay:calc(var(--i,0)*.02s)}

/* hero word rotator (enhance.js initWordSpin): the fixed lead line
   sits above a one-line window that the four phrases roll through —
   the current one holds, rolls up and out, and the next rolls in from
   below. Items are absolutely stacked inside the fixed-height window
   so the headline never reflows mid-roll. */
.og-spin-lead{display:block}
/* grid-stacked items: every phrase occupies the same cell, so the
   window is always as wide as the WIDEST phrase (absolute items
   contributed no width and the longest phrase got clipped).
   The padding/negative-margin pair grows the clip window past the
   line box without moving anything: at the headline's tight
   line-height, Inter's descenders (the g in "Intelligence") extend
   below the line box and overflow:hidden was shaving them flat. */
.og-spin{display:grid;justify-items:center;overflow:hidden;
  white-space:nowrap;padding:.14em .1em;margin:-.14em -.1em}
.og-spin-item{grid-area:1/1;transform:translateY(140%);opacity:0}
.og-spin-item.cur{transform:none;opacity:1;
  transition:transform .6s cubic-bezier(.2,.7,0,1),opacity .35s ease}
.og-spin-item.out{transform:translateY(-135%);opacity:0;
  transition:transform .6s cubic-bezier(.55,.06,.68,.19),opacity .5s ease}
@media (prefers-reduced-motion:reduce){
  .og-spin-item{transform:none;opacity:1}
}

/* giant "YOUNG GUNS" wordmark in every footer — injected by
   enhance.js (footerMark) just above the copyright bar, sized off the
   viewport so it nearly spans the band but stays fully visible (no
   glyph cropping — the audience is parents, the lettering has to read
   as branding, not as a glitch). .on (IntersectionObserver) rises it
   in once reached.
   Set in the brand display face (Cooper Hewitt Bold Italic, weight 712 —
   the same recipe as .og-bc h2 in the CTA band directly above and the
   gallery headings), not Inter: the wordmark has to match the brand
   guidelines. The old -.045em tracking was tuning for Inter's tight 800
   cut; on Cooper Hewitt's geometric caps .01em keeps "YOUNG GUNS" within
   ~1% of its previous width at every step of the clamp, so the mark still
   sits fully inside the band (.og-footer is overflow:hidden — it clips)
   down to a 320px viewport. */
.og-f-mark{
  text-align:center;white-space:nowrap;pointer-events:none;user-select:none;
  margin:56px 0 42px;
  font-family:"Cooper Hewitt Bold Italic","Source Sans Pro Bold",Georgia,serif;
  font-weight:712;
  font-size:clamp(50px,11.4vw,172px);line-height:.95;letter-spacing:.01em;
  color:rgba(255,255,255,.96);
  opacity:0;transform:translateY(18%);
}
.og-f-mark.on{opacity:1;transform:none;
  transition:opacity .7s ease,transform .9s cubic-bezier(.2,.7,0,1)}
@media (prefers-reduced-motion:reduce){
  .og-f-mark{opacity:1;transform:none}
  .og-chars .hp-c{opacity:1;transform:none}
}

/* ---------- carousels (og-scroll): native horizontal scroll without a
   visible scrollbar, plus grab/drag affordance — the drag itself is
   bound in enhance.js ---------- */
ul[data-og-scroll]{
  scrollbar-width:none;
  -ms-overflow-style:none;
  cursor:grab;
}
ul[data-og-scroll]::-webkit-scrollbar{display:none}
ul[data-og-scroll] a,
ul[data-og-scroll] img{
  -webkit-user-drag:none;
  user-select:none;
}
ul[data-og-scroll].og-dragging{
  cursor:grabbing;
  scroll-snap-type:none;
  scroll-behavior:auto;
  user-select:none;
}
/* /outcomes alumni carousel: the generator shipped every slide with
   inline visibility:hidden (its engine flipped them visible at hydration
   and looped over 4 copies of each card). Static build: show the
   aria-visible set of 7, collapse the loop clones — hidden slides still
   occupy layout space, so left in place the strip reads as an empty band.
   Static selectors on SSR markup: applies from first paint, no JS needed.
   enhance.js then re-opens two clone sets as loop buffers
   (data-og-loop="pre"/"post") so the strip scrolls like the original
   endless belt: buffers get every rule the real cards get, and the pre
   set moves in front of the real cards with flex order — set on the card
   <div>s because the display:contents <li>s generate no boxes (which is
   also why scroll-snap-align rides along here instead of on the li). */
[data-og-page="outcomes"] ul[draggable="false"] li[aria-hidden="true"]:not([data-og-loop]){display:none!important}
[data-og-page="outcomes"] ul[draggable="false"] li[aria-hidden="false"]>div,
[data-og-page="outcomes"] ul[draggable="false"] li[data-og-loop]>div{
  visibility:visible!important;
  scroll-snap-align:center;
}
[data-og-page="outcomes"] ul[draggable="false"] li[data-og-loop="pre"]>div{order:-1}
/* desktop + tablet: the original strip overflowed its 575×744 track,
   so a centred card showed with neighbours bleeding off both viewport
   edges (the engine animated a transform, no clipping). A native
   scroller must clip at its border box, so widen the scrollport to the
   full band and pad the track back down — cards stay 575px (width:100%
   resolves against the padded content box) and the first/last card can
   still reach dead centre. The height pin matters: the component sizes
   its height off its width, so an unpinned 100%-wide container balloons
   the track vertically. */
@media (min-width:810px){
  [data-og-page="outcomes"] .yg-1xeap0e-container{
    width:100%;
    height:744px!important;
  }
  [data-og-page="outcomes"] ul[draggable="false"]{
    box-sizing:border-box;
    padding-inline:calc((100% - 575px)/2)!important;
  }
}
/* phone: breathing room around the card (the original ran it hard
   edge-to-edge) and the dots pill moved off the artwork onto the dark
   band below it. The card keeps its baked 1545:2000 aspect so the text
   near its edges survives the narrower slot. */
@media (max-width:809px){
  [data-og-page="outcomes"] ul[draggable="false"]{
    box-sizing:border-box;
    padding-inline:24px!important;
  }
  [data-og-page="outcomes"] ul[draggable="false"] li[aria-hidden="false"]>div,
  [data-og-page="outcomes"] ul[draggable="false"] li[data-og-loop]>div{
    height:auto!important;
    aspect-ratio:1545/2000;
  }
  [data-og-page="outcomes"] fieldset.yg--slideshow-controls div:has(>button[aria-label^="Scroll to page"]){
    bottom:-46px!important;
  }
  /* room on the band for the relocated dots */
  [data-og-page="outcomes"] .yg-hwcit4{padding-bottom:64px}
}
/* page dots: dim the inactive ones; enhance.js moves .og-on with the
   scroll (the dots ship inline opacity:1 with no active-state styling —
   the engine used to restyle them) */
button[aria-label^="Scroll to page"]>div{opacity:.45!important;transition:opacity .3s}
button[aria-label^="Scroll to page"].og-on>div{opacity:1!important}

/* per-section blob palettes */
.yg-92jzxg{
  /* dialled down with the base coat above, and for the same reason: these
     blobs were most of the band's brightness, and the dust has to be able
     to show against them */
  --og-amb-1:radial-gradient(closest-side,rgba(158,210,238,.30),transparent 70%);
  --og-amb-2:radial-gradient(closest-side,rgba(176,30,54,.44),transparent 72%);
  --og-amb-3:radial-gradient(closest-side,rgba(14,20,58,.9),transparent 74%);
  /* dust palette: two of the band's own colours (the sky blue it opens
     with bottom-left, and the studio red it closes on), as raw RGB
     triplets — enhance.js composes them at per-mote alpha, so they can
     only be stated here as components */
  --og-field-a:206,236,252;
  --og-field-b:255,192,201;
}
.yg-10bma76{
  --og-amb-1:radial-gradient(closest-side,rgba(228,230,238,.6),transparent 70%);
  --og-amb-2:radial-gradient(closest-side,rgba(186,28,48,.7),transparent 72%);
  --og-amb-3:radial-gradient(closest-side,rgba(84,12,26,.85),transparent 74%);
}
.yg-1ezo1vh,
.yg-pe8j1z,
.yg-rmvr16,
.yg-1xpoeol{
  --og-amb-1:radial-gradient(closest-side,rgba(66,104,205,.5),transparent 70%);
  --og-amb-2:radial-gradient(closest-side,rgba(28,48,118,.6),transparent 72%);
  --og-amb-3:radial-gradient(closest-side,rgba(2,4,12,.9),transparent 74%);
}
.yg-4veble{
  --og-amb-1:radial-gradient(closest-side,rgba(62,86,78,.5),transparent 70%);
  --og-amb-2:radial-gradient(closest-side,rgba(34,56,126,.55),transparent 72%);
  --og-amb-3:radial-gradient(closest-side,rgba(4,6,16,.85),transparent 74%);
}

/* ---------- homepage hero video: paint its own first frame while the
   file downloads instead of a white hole. The poster attribute lives in
   the static markup only, so this CSS backdrop also survives hydration
   re-rendering the <video>. !important beats the inline transparent
   background-color set inline on the element. ---------- */
video[src*="young-guns-studio-hero"]{
  background:#17171f url("/assets/media/assets/young-guns-studio-hero-poster.jpg") center/cover no-repeat!important;
}

/* ---------- mobile/tablet fixed-height fixes (inherited from the
   source design; confirmed present on the original production site).
   Three homepage components carry desktop-scale fixed pixel heights
   into small breakpoints, leaving sparse content adrift in empty
   space. The CTA band's content is absolutely positioned, so it gets
   an explicit compact height (auto would collapse it); the tier and
   testimonial cards are flex containers with flowing content, so
   they can size to content. ---------- */
@media (max-width:1199px){
  /* CTA band ("A boutique art & design studio…"): 900–1100px fixed */
  .yg-92jzxg{height:640px!important}
  /* its content column is hard-positioned for the original 1100px band
     (absolute, top:-540px, height:1100px) — pin it to the band and let
     the existing flex centering do the rest */
  .yg-92jzxg .yg-1gym4ok{
    top:0!important;bottom:0!important;height:100%!important;
    justify-content:center!important;align-items:center!important;
    padding:0 24px!important;
  }
}
@media (max-width:809px){
  .yg-92jzxg{height:560px!important}
  /* the fixed heights actually live on the section WRAPPERS, which
     flex-stretch the cards to fill them — release the wrappers first */
  .yg-1k3rsj1,
  .yg-xtjgct,
  .yg-15tbi9u{height:auto!important;min-height:0!important;aspect-ratio:auto!important}
  /* program tier cards (Tier 1/2/3): the source design gives them
     aspect-ratio 0.885, so their height balloons with their width
     (779px tall at 764px viewport) — drop the ratio, size to content */
  .yg-1twiv2u,
  .yg-g6e38r,
  .yg-qtrrmp{
    aspect-ratio:auto!important;
    height:auto!important;min-height:0!important;flex:none!important;
    padding-top:30px!important;padding-bottom:30px!important;
  }
  /* testimonial cards: same aspect-ratio ballooning (~794px per card) */
  .yg-uq7aso,
  .yg-y9mepr,
  .yg-1qb7vpj{
    aspect-ratio:auto!important;
    height:auto!important;min-height:0!important;flex:none!important;
    padding-top:30px!important;padding-bottom:30px!important;
  }
}

/* ---------- homepage rhythm: the source design tied several content
   bands to the viewport height, leaving big navy voids at every size.
   .yg-mvp5b  = pull-quote band under the hero (100vh — 844px of band
                for ~200px of copy on a phone)
   .yg-eo048p = stats band (900+ / $10M+ / 17+ Years; 50vh, content
                ~90px tall pinned to its top)
   .yg-92jzxg = CTA band above the footer (100vh on desktop; the
                640/560px caps below 1200px shipped earlier)
   Heights go content-based; horizontal padding stays the source's
   per-breakpoint values. ---------- */
.yg-voUA0 .yg-mvp5b{height:auto!important;min-height:0!important}
@media (min-width:1200px){
  body[data-og-page="home"] .yg-mvp5b{padding:120px 300px!important}
}
.yg-voUA0 .yg-eo048p{
  height:auto!important;min-height:0!important;
  padding-top:100px!important;padding-bottom:100px!important;
}
@media (max-width:809px){
  .yg-voUA0 .yg-mvp5b{padding:64px 30px!important}
  .yg-voUA0 .yg-eo048p{
    gap:48px!important;
    padding-top:64px!important;padding-bottom:64px!important;
  }
}
/* desktop CTA band: same treatment the sub-1200 breakpoints already
   got — compact band, content column pinned to it and centered
   (.yg-1ap763u is the desktop variant's column; .yg-1gym4ok covers
   any variant that shows it at this width) */
@media (min-width:1200px){
  .yg-voUA0 .yg-92jzxg{height:560px!important}
  .yg-voUA0 .yg-1gym4ok,
  .yg-voUA0 .yg-1ap763u{
    top:0!important;bottom:0!important;height:100%!important;
    justify-content:center!important;
    padding:80px 100px!important;
  }
}

/* ---------- homepage testimonial cards ("Loved by families"): the
   source gave the first card a different fixed width than the other
   two (323px vs 362px) and centered the row, so heights and tops all
   differed. Equal flexible widths, equal stretched heights, names
   pinned to the card bottoms. ---------- */
@media (min-width:1200px){
  .yg-voUA0 .yg-xtjgct{align-items:stretch!important;height:auto!important}
  .yg-voUA0 .yg-uq7aso,
  .yg-voUA0 .yg-y9mepr,
  .yg-voUA0 .yg-1qb7vpj{
    flex:1 1 0!important;width:auto!important;
    aspect-ratio:auto!important;height:auto!important;min-height:0!important;
    justify-content:space-between!important;
  }
}
/* tablet: cards are stacked full-width; height to content instead of
   the source's 1.74 aspect ratio (phone already fixed further down) */
@media (min-width:810px) and (max-width:1199.98px){
  .yg-voUA0 .yg-uq7aso,
  .yg-voUA0 .yg-y9mepr,
  .yg-voUA0 .yg-1qb7vpj{
    aspect-ratio:auto!important;height:auto!important;min-height:0!important;
  }
}

/* ---------- homepage consultation buttons: one geometry. The source
   shipped the same 194×37 pill three times with 14px text in the hero
   and 16px in the CTA band — and 37px is under the 44px tap-target
   minimum. Colours stay per section (red on the hero video, steel
   blue on the red band). ---------- */
.yg-voUA0 .yg-xpyekj{
  width:auto!important;min-width:200px;
  height:auto!important;min-height:44px;
  padding:10px 26px!important;
}
/* both the var and the direct property: the band button's label gets
   its size from a plain font-size rule, not the .yg-text var formula */
.yg-voUA0 .yg-xpyekj .yg-text{--yg-font-size:15px!important;font-size:15px!important}

/* ============================================================
   Gallery — center the six discipline titles (FASHION DESIGN,
   ARCHITECTURE, …). Their RichTextContainers are already
   width:100%, so alignment alone does it.
   ============================================================ */
.yg-1f4wlqg h1.yg-text,
.yg-1fhn2k5 h1.yg-text,
.yg-177yzp0 h1.yg-text,
.yg-1jw88ls h1.yg-text,
.yg-11ztvpc h1.yg-text,
.yg-1n9c7sf h1.yg-text{text-align:center!important}

/* ============================================================
   Approach — "Development Pathways" cards + program detail sheet
   (.yg-frfz8 component: cards .yg-l537ta/.yg-7ybjmh/
   .yg-kujtgi, sheet variants .yg-1ky8w0y/.yg-1qjkfv6/
   .yg-1jc24gy render into the #overlay portal)
   ============================================================ */

/* card media is too bright for the white copy — scrim it, heavier
   behind the title (top) and grade label (bottom) */
.yg-frfz8 .yg-l537ta::after,
.yg-frfz8 .yg-7ybjmh::after,
.yg-frfz8 .yg-kujtgi::after{
  content:"";position:absolute;inset:0;z-index:1;pointer-events:none;
  border-radius:inherit;
  background:linear-gradient(180deg,
    rgba(16,18,35,.55) 0%,rgba(16,18,35,.24) 42%,
    rgba(16,18,35,.24) 68%,rgba(16,18,35,.5) 100%);
}
/* keep the card copy (and the expand button) above the scrim; the
   unclassed media div and the *-container video embeds stay below */
.yg-frfz8 .yg-l537ta>div[class^="yg-"]:not([class$="-container"]),
.yg-frfz8 .yg-7ybjmh>div[class^="yg-"]:not([class$="-container"]),
.yg-frfz8 .yg-kujtgi>div[class^="yg-"]:not([class$="-container"]){
  position:relative;z-index:2;
}

/* ---------- program detail sheet ----------
   Ships as a bare white fixed panel (no backdrop, 100px voids,
   off-centre). Restyle into a deliberate bottom sheet. */
.yg-frfz8.yg-1ky8w0y,
.yg-frfz8.yg-1qjkfv6,
.yg-frfz8.yg-1jc24gy{
  inset:96px 40px 0!important;
  max-width:1160px!important;
  margin-inline:auto!important;
  gap:56px!important;
  padding:72px clamp(24px,7vw,96px) 72px!important;
  border-top-left-radius:28px!important;
  border-top-right-radius:28px!important;
  box-shadow:0 -24px 80px rgba(8,10,22,.5);
  overscroll-behavior:contain;
  scrollbar-width:thin;
}
@media (max-width:809px){
  .yg-frfz8.yg-1ky8w0y,
  .yg-frfz8.yg-1qjkfv6,
  .yg-frfz8.yg-1jc24gy{
    inset:56px 10px 0!important;
    gap:44px!important;
    padding:56px 20px 56px!important;
    border-top-left-radius:20px!important;
    border-top-right-radius:20px!important;
  }
}

/* close button: keep it visible while the sheet scrolls. It's the
   first flex item (order:-1, 38px tall); the negative bottom margin
   swallows its 38px + the 56px column gap so layout doesn't shift. */
.yg-frfz8 .yg-16uqwi-container,
.yg-frfz8 .yg-pf1o3w-container,
.yg-frfz8 .yg-19tcqv7-container{
  position:sticky!important;top:0!important;right:auto!important;
  order:-1;align-self:flex-end;z-index:20;
  margin:-44px -8px -94px 0;
}
@media (max-width:809px){
  .yg-frfz8 .yg-16uqwi-container,
  .yg-frfz8 .yg-pf1o3w-container,
  .yg-frfz8 .yg-19tcqv7-container{margin:-32px 0 -82px 0}
}
.yg-frfz8 .yg-16uqwi-container>div,
.yg-frfz8 .yg-pf1o3w-container>div,
.yg-frfz8 .yg-19tcqv7-container>div{
  cursor:pointer;transition:background-color .2s ease,transform .2s ease;
}
@media (hover:hover) and (pointer:fine){
  .yg-frfz8 .yg-16uqwi-container>div:hover,
  .yg-frfz8 .yg-pf1o3w-container>div:hover,
  .yg-frfz8 .yg-19tcqv7-container>div:hover{
    background-color:var(--red)!important;transform:scale(1.08);
  }
}

/* /approach program cards: enhance.js overlays an anchor to the
   program's own page (the runtime that opened the detail sheet is
   gone). The maximaize icon underneath stays purely decorative. */
a.og-prog-link{
  position:absolute;inset:0;z-index:5;
  border-radius:inherit;cursor:pointer;
}
a.og-prog-link:focus-visible{outline:3px solid var(--red);outline-offset:3px}
/* the card link covered the whole tile and changed nothing on hover
   (issue #89) — a ring in the card's own radius plus a light red wash
   reads as "this whole card is the link" without touching the artwork */
a.og-prog-link{transition:box-shadow .2s ease,background-color .2s ease}
@media (hover:hover) and (pointer:fine){
  a.og-prog-link:hover{
    box-shadow:inset 0 0 0 2px var(--red);
    background-color:rgba(200,40,63,.10);
  }
}
@media (prefers-reduced-motion:reduce){
  a.og-prog-link{transition:none}
}

/* backdrop behind the sheet (injected by enhance.js) */
.og-scrim{
  position:fixed;inset:0;z-index:9;cursor:pointer;
  background:rgba(12,14,28,.62);
  backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);
  animation:og-fade .25s ease;
}
@keyframes og-fade{from{opacity:0}}
html.og-modal-open{overflow:hidden!important}
@media (prefers-reduced-motion:reduce){
  .og-scrim{animation:none}
  .yg-frfz8 .yg-16uqwi-container>div,
  .yg-frfz8 .yg-pf1o3w-container>div,
  .yg-frfz8 .yg-19tcqv7-container>div{transition:none}
  .yg-frfz8 .yg-16uqwi-container>div:hover,
  .yg-frfz8 .yg-pf1o3w-container>div:hover,
  .yg-frfz8 .yg-19tcqv7-container>div:hover{transform:none}
}

/* ---------- homepage: the two photo-and-copy sections ("Why
   families choose YG", "AI era positioning") gave their photo an
   aspect-ratio tied to viewport width but fixed the section height
   (600px / 85vh), so on wide screens the photo grows past the
   section and overlaps the student-testimonial cards above and the
   copy below. Let the sections grow with their photo instead; the
   vertical padding keeps a gap to the neighbouring sections once
   the photo is the tallest thing in the row. ---------- */
@media (min-width:1496px){
  .yg-voUA0 .yg-y35eg8{
    height:auto!important;min-height:600px!important;
    padding:50px 100px!important;
  }
  .yg-voUA0 .yg-1j1619k{
    height:auto!important;min-height:85vh!important;
    padding:60px 100px!important;
  }
}
@media (min-width:1200px) and (max-width:1495.98px){
  .yg-voUA0 .yg-y35eg8{
    height:auto!important;min-height:75vh!important;
    padding:50px 75px!important;
  }
}

/* ---------- blog deferred (see /roadmap.md) ----------
   Blog section pulled from the live site for now. Hides every blog
   entry point (top nav + footer nav) site-wide; survives the runtime
   re-hydration because it's a rule, not a DOM edit. Remove this block
   when the blog comes back. */
a[href="/blog"],
a[href="/blog/"],
a[href^="/blog/"],
a[href="./blog"],
a[href^="./blog/"]{display:none!important}
/* ---------- /outcomes: static alumni profile links ----------
   The alumni carousel's profile anchors are enhance.js-injected overlays,
   so without JS (and for crawlers) the seven profile pages were unreachable
   from their hub — the names lived only in image alt text. This strip is
   the always-there path. It is the last child INSIDE the oc-dark band (not a
   sibling after it — see the seam note in outcomes.css) and keeps the same
   navy so it reads as that band's baseboard rather than a new
   section; links are white with the brand-red underline on hover/focus,
   sized as comfortable tap targets, wrapping to multiple rows on phones. */
.og-alumni-links{background:#323455;padding:0 32px 56px;text-align:center}
.og-alumni-links p{
  margin:0 0 8px;color:#8fb3c6;font-size:15px;line-height:1;
  font-family:"Inter Variable",Inter,system-ui,sans-serif;
  font-variation-settings:"opsz" 14,"wght" 600;
}
.og-alumni-links ul{
  list-style:none;margin:0 auto;padding:0;max-width:900px;
  display:flex;flex-wrap:wrap;justify-content:center;column-gap:8px;
}
.og-alumni-links a{
  display:inline-block;padding:12px 12px;color:#fff;font-size:16px;line-height:1;
  font-family:"Inter Variable",Inter,system-ui,sans-serif;
  font-variation-settings:"opsz" 14,"wght" 600;
  text-decoration:underline;text-decoration-color:rgba(255,255,255,.35);
  text-decoration-thickness:1px;text-underline-offset:5px;
  transition:text-decoration-color .2s ease;
}
.og-alumni-links a:focus-visible{
  text-decoration-color:#e54e5e;text-decoration-thickness:2px;
}
@media (hover:hover) and (pointer:fine){
  .og-alumni-links a:hover{
    text-decoration-color:#e54e5e;text-decoration-thickness:2px;
  }
}

/* ---------- auto-mover pause controls (WCAG 2.2.2) ----------
   Injected by enhance.js (ogPauseToggle): .og-car-pause sits in the
   /outcomes carousel's controls fieldset on the dark navy band;
   .og-ticker-pause floats over each /gallery marquee strip's corner
   (light page background). aria-pressed="true" = paused → swap the
   pause glyph for the play glyph. Keyboard focus rides the global
   focus-visible rule. Neither button exists under
   prefers-reduced-motion — the movers themselves never start — so
   only the hover transition needs stilling there. */
/* ONE geometry for all three pause controls (issue #88): the carousel and
   ticker buttons used to be 32×32 with a 14px glyph and the homepage hero's
   .og-hero-pause 40×40 with a 15px one. They are now the same 44×44 button
   with the same 15px glyph and the same 20px inset from their corner — the
   44px also clears the touch-target minimum, which 32px never did (#89).
   Only the colour treatment still varies (light strip vs dark band). */
.og-car-pause,.og-ticker-pause{
  display:flex;align-items:center;justify-content:center;
  width:44px;height:44px;padding:0;border-radius:50%;cursor:pointer;
  transition:background-color .2s,border-color .2s,color .2s;
}
.og-car-pause svg,.og-ticker-pause svg{display:block;width:15px;height:15px}
.og-car-pause .og-pp-play,.og-ticker-pause .og-pp-play{display:none}
.og-car-pause[aria-pressed="true"] .og-pp-pause,
.og-ticker-pause[aria-pressed="true"] .og-pp-pause{display:none}
.og-car-pause[aria-pressed="true"] .og-pp-play,
.og-ticker-pause[aria-pressed="true"] .og-pp-play{display:block}
/* carousel: centred under the arrows/dots on the #323455 band */
.og-car-pause{
  margin:14px auto 0;color:#fff;
  border:1.5px solid rgba(255,255,255,.55);background:rgba(255,255,255,.12);
}
@media (hover:hover) and (pointer:fine){
  .og-car-pause:hover{background:var(--red);border-color:var(--red)}
}
/* ticker: anchored to the strip's bottom-right corner; the strip's
   images run beneath it, so a translucent white chip keeps the glyph
   legible on any frame */
.ga-car{position:relative}
.og-ticker-pause{
  position:absolute;right:20px;bottom:20px;z-index:2;color:#343459;
  border:1.5px solid rgba(52,52,89,.35);background:rgba(255,255,255,.85);
}
@media (hover:hover) and (pointer:fine){
  .og-ticker-pause:hover{background:#343459;border-color:#343459;color:#fff}
}
@media (prefers-reduced-motion:reduce){
  .og-car-pause,.og-ticker-pause{transition:none}
}

/* ============================================================
   Internationalisation — Simplified Chinese (feature #63)
   ============================================================ */
/* /zh pages inherit the reader's system CJK face for Chinese glyphs; Latin
   runs (brand names, numbers) keep the brand fonts via per-glyph fallback.
   Zero added page weight, no webfont, no CSP change.

   These are keyed on `[lang|="zh"]`, the language-range match, not on the
   exact `zh-Hans` the pages are authored with. Browsers do rewrite that
   attribute — Brave was observed serving our `zh-Hans` page with a live
   `lang="zh-CN"` — and an exact-value selector silently stops matching,
   which drops the CJK stack and brings back the faux-oblique CJK the rule
   below exists to prevent. `|=` also covers zh-Hant, zh-SG and friends.
   enhance.js already tests the same way (indexOf('zh') === 0). */
html[lang|="zh"]{
  font-family:"PingFang SC","Hiragino Sans GB","Microsoft YaHei",
    "Noto Sans SC","Source Han Sans SC","Source Han Sans CN",sans-serif;
}
/* Chinese has no spaces, so long headings/labels must be allowed to wrap;
   the desktop nav row keeps its no-wrap links but the language pill and
   drawer copy break normally. */
html[lang|="zh"] .lc-hero-title,
html[lang|="zh"] .lc-h1{white-space:normal}

/* ---------- language toggle (EN <-> 中文) ----------
   Lives as the last item of .og-tn-links (desktop) and .og-tnd-links
   (drawer), so it hides with them below 1200px. Styled as a soft chip -
   the nav's Source Sans faces have no CJK glyphs, so the chip carries its
   own stack ending in the system CJK sans faces; without it the 中文
   label falls back to a serif face and looks broken. */
/* selectors carry the `.og-tn-links a` prefix on purpose: the generic nav
   link rules (font shorthand, padding, letter-spacing, hover colour) are
   0,2,1-specific and silently flattened a bare `.og-tn-lang` version of
   this chip - hover went red-on-red and the CJK stack never applied. */
.og-tn-links a.og-tn-lang{
  align-self:center;text-transform:none;
  display:inline-flex;align-items:center;justify-content:center;gap:7px;
  padding:8px 15px;border-radius:999px;border:none;
  background:#f6f0f1;color:#343459;
  font:700 14px/1 "Source Sans Pro Bold","PingFang SC","Hiragino Sans GB",
    "Microsoft YaHei","Noto Sans SC",sans-serif;
  letter-spacing:.04em;
  transition:background .18s ease,color .18s ease;
}
.og-tn-lang svg{width:14px;height:14px;display:block;opacity:.75;
  transition:opacity .18s ease}
@media (hover:hover) and (pointer:fine){
  .og-tn-links a.og-tn-lang:hover{
    background:var(--red);color:#fff;border-bottom-color:transparent;
  }
  .og-tn-lang:hover svg{opacity:1}
}
/* the drawer row inherits the standard .og-tnd-links a row treatment
   (full-width row, chevron, hover tint) - just fix its CJK face */
.og-tnd-links a.og-tnd-lang{
  font-family:"Source Sans Pro Bold","PingFang SC","Hiragino Sans GB",
    "Microsoft YaHei","Noto Sans SC",sans-serif;font-weight:800;
  }
  /* the globe replaces the row chevron: this row switches language, it does
     not navigate deeper, so the "go deeper" affordance would misread. With
     no chevron the row doesn't need the reserved right lane either. */
  .og-tnd-links a.og-tnd-lang{padding-right:16px}
  .og-tnd-links a.og-tnd-lang::after{content:none}
  /* the globe rides the label as inline content rather than as a flex item
     with a `gap`, so it sits beside the text in plain block flow too — see
     the flex-independence note on the drawer rows in main.css */
  /* sized to the 26px row label, not the old 19px one */
  .og-tnd-lang svg{
    display:inline-block;vertical-align:middle;margin-right:12px;
    width:24px;height:24px;opacity:.55;
  }
@media (prefers-reduced-motion:reduce){
  .og-tn-lang,.og-tn-lang svg{transition:none}
}
/* The 1200-1359 band barely fits the full link row once the language chip
   is in it (issue #60). This used to be paid for out of the link row —
   gap crushed to 9px — which left the links visually colliding and STILL
   let the CTA spill past the pill: the media query matches on a viewport
   width that includes the scrollbar, so at a 1200px window the row is
   laid out in ~1183-1194px of content and the 9px was never enough.

   Take the space out of the CTA instead. It carries its label in two
   spans, so dropping "your journey" leaves "Start" plus the arrow chip —
   still a complete call to action — and frees ~106px, far more than the
   row needs. The links keep a readable gap and the chip keeps its globe. */
@media (min-width:1200px) and (max-width:1359.98px){
  /* the bar's own logo/links/CTA gap is clamp(20px,2.5vw,40px), ~30px here;
     holding it at 20px buys back the ~10px the row needs so the CTA keeps
     the pill's full 10px right padding even at the narrowest content width
     this band can produce (a 1200px window minus a ~17px scrollbar) */
  .og-topnav-bar{gap:20px}
  .og-tn-links{gap:clamp(14px,1.4vw,20px)}
  /* Chinese is not compacted: 开启你的旅程 measures 168px against the English
     220px and already clears the pill, and "开启" on its own is too bare to
     read as a call to action the way "Start" does. */
  html:not([lang|="zh"]) .og-tn-cta{padding:0 10px 0 18px}
  html:not([lang|="zh"]) .og-tn-cta .l2{display:none}
  html:not([lang|="zh"]) .og-tn-cta .l1{margin-right:.5em}
}
/* CJK never renders faux-oblique: the italic eyebrow treatment is a Latin
   design decision, and slanted CJK glyphs read as a rendering glitch */
html[lang|="zh"] .og-f-h,
html[lang|="zh"] .foot-eyebrow{font-style:normal}

/* ============================================================
   WeChat contact modal (feature #64) — footer icon opens this.
   The footer icon degrades to a link to /lets-chat#wechat with JS off;
   enhance.js builds this dialog and wires it up. QR is a self-hosted
   placeholder until the studio supplies the real image + ID.
   ============================================================ */
.og-wx-modal{
  position:fixed;inset:0;z-index:10001;display:flex;align-items:center;
  justify-content:center;padding:20px;
}
.og-wx-modal[hidden]{display:none}
.og-wx-scrim{position:absolute;inset:0;background:rgba(20,20,35,.6);
  backdrop-filter:blur(2px)}
.og-wx-card{
  position:relative;z-index:1;background:#fff;border-radius:18px;
  padding:28px 28px 24px;max-width:340px;width:100%;text-align:center;
  box-shadow:0 24px 60px rgba(0,0,0,.32);
  display:flex;flex-direction:column;align-items:center;gap:12px;
}
.og-wx-close{
  position:absolute;top:10px;right:10px;width:34px;height:34px;
  display:flex;align-items:center;justify-content:center;
  border:none;border-radius:50%;background:transparent;cursor:pointer;
  font-size:22px;line-height:1;color:#343459;
}
@media (hover:hover) and (pointer:fine){
  .og-wx-close:hover{background:#f6f0f1;color:var(--red)}
}
.og-wx-h{margin:0;color:#343459;
  font:700 19px/1.3 "Source Sans Pro Bold","PingFang SC","Hiragino Sans GB","Microsoft YaHei","Noto Sans SC",sans-serif}
.og-wx-qr{display:block;width:220px;height:220px;max-width:100%;border-radius:8px}
.og-wx-id{margin:0;color:#555;font:400 14px/1.4 "Source Sans Pro Regular","PingFang SC","Hiragino Sans GB","Microsoft YaHei","Noto Sans SC",sans-serif}
.og-wx-id b{color:#000}
@media (prefers-reduced-motion:no-preference){
  .og-wx-card{animation:og-wx-in .22s cubic-bezier(.22,.7,.3,1) both}
  @keyframes og-wx-in{from{opacity:0;transform:translateY(12px) scale(.98)}to{opacity:1;transform:none}}
}

/* ---------- /zh translation notice (Oasis of Change pattern) ----------
   .og-zh-note: static disclaimer line baked into every /zh footer by
   tools/build_zh.py (visible without JS). .og-zh-modal: the first-visit
   关于翻译 dialog enhance.js layers on top, reusing the .og-wx-* shell.
   No font-family overrides here - the html[lang|="zh"] CJK stack
   must keep supplying the Chinese glyphs. */
.og-zh-note{margin:0 0 10px;color:rgba(255,255,255,.72);font-size:13px;line-height:1.7}
.og-zh-note a{color:#fff;text-decoration:underline;text-underline-offset:2px}
@media (hover:hover) and (pointer:fine){
  .og-zh-note a:hover{color:#eea7ad}
}
.og-zh-modal .og-wx-card{max-width:400px}
.og-zh-p{margin:0;color:#333;font-size:15px;line-height:1.8;text-align:left}
.og-zh-ok{
  align-self:stretch;margin-top:4px;padding:13px 20px;border:none;border-radius:999px;
  background:#343459;color:#fff;font-size:15px;font-weight:700;line-height:1;
  letter-spacing:.06em;cursor:pointer;transition:background .2s ease;
}
@media (hover:hover) and (pointer:fine){
  .og-zh-ok:hover{background:var(--red)}
}
.og-zh-en{color:#555;font-size:13px;line-height:1;
  text-decoration:underline;text-underline-offset:2px}
@media (hover:hover) and (pointer:fine){
  .og-zh-en:hover{color:var(--red)}
}
@media (prefers-reduced-motion:reduce){.og-zh-ok{transition:none}}

/* ============================================================
   .btn — the one pill button for the hand-authored dark pages
   (/programs and its three sheets, /build-club, /404, /privacy,
   /terms, /accessibility, /wechat, the seven outcome profiles).
   It used to exist as nine copies of `.btn` (programs.css plus
   eight inline <style> blocks, 14px 28px) and as `.wc-btn` /
   `.ax-btn` (13px 26px) — the same button under a page-family
   prefix. Measured heights ran 49–51px. One definition here, on
   the wider of the two paddings, so the same action is the same
   size everywhere. `--display` is declared by each of those
   pages; the fallback keeps the face right if one ever isn't.
   ============================================================ */
.btn{
  font-family:var(--display,'Cooper Hewitt','Source Sans Pro',Georgia,serif);
  font-weight:600;font-size:15.5px;text-decoration:none;
  padding:14px 28px;border-radius:999px;
  /* explicit line-height, because an inherited one was the last thing
     keeping these apart: identical padding still measured 51px on
     /privacy and 49px on /404 purely from the page's body line-height.
     1.2 keeps room for descenders and a wrapped second line, and stays
     under the 48px floor so min-height — not the type — sets the box. */
  line-height:1.2;
  min-height:48px;display:inline-flex;align-items:center;gap:8px;
  transition:transform .16s ease,background .16s ease,border-color .16s ease;
}
.btn.primary{background:var(--red);color:#fff}
.btn.ghost{border:1.5px solid rgba(255,255,255,.28);color:#fff}
@media (hover:hover) and (pointer:fine){
  .btn.primary:hover{background:var(--red-deep);transform:translateY(-2px)}
  .btn.ghost:hover{border-color:rgba(255,255,255,.6);transform:translateY(-2px)}
}
@media (prefers-reduced-motion:reduce){
  .btn{transition:none}
  .btn.primary:hover,.btn.ghost:hover{transform:none}
}

/* ============================================================
   Shared hover treatments (issue #89).
   The Framer-derived page families each ship their own CTA pill
   and card link and none of them had a hover state — every
   hand-built component on the site does. Rather than repeat the
   same three declarations in six page stylesheets, the shared
   treatment lives here (main.css is the only sheet on every
   page). Two hover languages, matching what the hand-built
   components already do: pills lift, card links take a ring.
   ============================================================ */
/* the six CTA-band pills — white on a photo, so a brightness bump
   would be invisible; they lift and cast instead */
.au-pill,.ap-cta-pill,.dt-pill,.ga-pill,.oc-pill,.sc-pill{
  transition:transform .18s ease,box-shadow .18s ease;
}
@media (hover:hover) and (pointer:fine){
  .au-pill:hover,.ap-cta-pill:hover,.dt-pill:hover,
  .ga-pill:hover,.oc-pill:hover,.sc-pill:hover{
    transform:translateY(-2px);
    box-shadow:0 10px 24px rgba(16,18,40,.32);
  }
}
/* the /outcomes carousel slide — a bare overlay anchor over the card
   artwork; the ring uses the card's own radius via border-radius:inherit */
.og-alumni-link{transition:box-shadow .2s ease}
@media (hover:hover) and (pointer:fine){
  .og-alumni-link:hover{box-shadow:inset 0 0 0 3px var(--red)}
}
/* the outcome profiles' footer wordmark */
.foot-brand .lockup img{transition:opacity .18s ease}
@media (hover:hover) and (pointer:fine){
  .foot-brand .lockup:hover img{opacity:.72}
}
@media (prefers-reduced-motion:reduce){
  .au-pill,.ap-cta-pill,.dt-pill,.ga-pill,.oc-pill,.sc-pill,
  .og-alumni-link,.foot-brand .lockup img{transition:none}
  .au-pill:hover,.ap-cta-pill:hover,.dt-pill:hover,
  .ga-pill:hover,.oc-pill:hover,.sc-pill:hover{transform:none}
}
