/* ==========================================================================
   IFT LEARNINGS — design system
   --------------------------------------------------------------------------
   Palette : Crimson Scholar   crimson #FF3D57 · gold #FFC24B on #0E0407
   Type    : Syne 700/800 (display) · Space Grotesk 400/500/600 (body)
   Motion  : one easing curve everywhere — var(--ease)

   Loaded by every page. Course detail pages additionally load course.css.
   ========================================================================== */

/* ============================== 1. TOKENS ============================== */
:root{
  /* surfaces */
  --bg:#0E0407;
  --bg-2:#160610;
  --panel:#1A0810;
  --dark:#080205;

  /* ink */
  --fg:#F3E9EB;
  --muted:#9A8189;

  /* brand */
  --accent:#FF3D57;          /* crimson — primary */
  --accent-2:#FFC24B;        /* gold — achievement / accent */
  --accent-soft:rgba(255,61,87,.13);

  --line:rgba(243,233,235,.13);
  --line-2:rgba(243,233,235,.24);

  /* the six course tracks. Each course page overrides --ca with its own,
     so every accent-coloured rule can just read var(--ca). */
  --c-ai:#FFC24B;
  --c-cyber:#FF3D57;
  --c-full:#4ADEDE;
  --c-wp:#7C8CFF;
  --c-seo:#4ADE80;
  --c-design:#FF6FD8;
  --c-uiux:#B57BFF;
  --ca:var(--accent);

  --ease:cubic-bezier(.19,1,.22,1);
  --ease-out:cubic-bezier(.16,1,.3,1);

  --font-d:'Syne',Impact,'Arial Black',sans-serif;
  --font-b:'Space Grotesk',Helvetica,Arial,sans-serif;
  --font-m:ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,monospace;

  --nav-h:78px;
}

/* Scroll-triggered light section (the "statement" band). Only the tokens
   flip — every component below reads them, so nothing else needs to know. */
body.light{
  --bg:#F6EEF0;
  --fg:#0E0407;
  --muted:#6B5257;
  --line:rgba(14,4,7,.16);
  --line-2:rgba(14,4,7,.28);
  --accent:#D01B33;
  --accent-2:#B88400;
}

/* ============================== 2. BASE ============================== */
*,*::before,*::after{margin:0;padding:0;box-sizing:border-box}
html{overflow-x:hidden;scroll-behavior:smooth}
body{
  background:var(--bg);
  color:var(--fg);
  font-family:var(--font-b);
  font-size:16px;
  line-height:1.55;
  overflow-x:hidden;
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
  transition:background-color .8s ease,color .8s ease;
}
/* the preloader owns the viewport until JS says otherwise */
body:not(.loaded){overflow:hidden;height:100vh}
a{color:inherit;text-decoration:none}
img,canvas,svg{display:block;max-width:100%}
button{font:inherit;color:inherit;background:none;border:0}
::selection{background:var(--accent);color:#FFF}
::-webkit-scrollbar{width:7px}
::-webkit-scrollbar-track{background:var(--dark)}
::-webkit-scrollbar-thumb{background:#40161F;border-radius:4px}
::-webkit-scrollbar-thumb:hover{background:var(--accent)}
a:focus-visible,button:focus-visible,input:focus-visible,
select:focus-visible,textarea:focus-visible,summary:focus-visible{
  outline:2px solid var(--accent);outline-offset:3px;border-radius:2px;
}
/* the custom cursor replaces the native one, but only where there is a
   real pointer — never on touch, and never when the user can't see it */
@media(pointer:fine){
  body,a,button,summary,.tilt{cursor:none}
}
.sr-only{
  position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;
}
.skip{
  position:fixed;top:-100px;left:4vw;z-index:300;
  background:var(--accent);color:#FFF;padding:12px 22px;border-radius:0 0 8px 8px;
  font-size:.8rem;letter-spacing:.12em;text-transform:uppercase;font-weight:600;
  transition:top .3s var(--ease);
}
.skip:focus{top:0}

/* ============================== 3. FILM GRAIN ============================== */
.grain{
  position:fixed;inset:-50%;width:200%;height:200%;
  pointer-events:none;z-index:90;opacity:.07;mix-blend-mode:overlay;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='300' height='300'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/><feColorMatrix type='saturate' values='0'/></filter><rect width='300' height='300' filter='url(%23n)' opacity='0.62'/></svg>");
  animation:grain 8s steps(10) infinite;
}
@keyframes grain{
  0%,100%{transform:translate(0,0)}    10%{transform:translate(-5%,-10%)}
  20%{transform:translate(-15%,5%)}    30%{transform:translate(7%,-25%)}
  40%{transform:translate(-5%,25%)}    50%{transform:translate(-15%,10%)}
  60%{transform:translate(15%,0)}      70%{transform:translate(0,15%)}
  80%{transform:translate(3%,25%)}     90%{transform:translate(-10%,10%)}
}

/* ============================== 4. CURSOR ============================== */
.cursor,.cursor-f{position:fixed;top:0;left:0;pointer-events:none;border-radius:50%;z-index:150}
.cursor{width:7px;height:7px;background:var(--accent);margin:-3.5px 0 0 -3.5px}
.cursor-f{
  width:36px;height:36px;margin:-18px 0 0 -18px;
  border:1px solid rgba(243,233,235,.5);
  display:flex;align-items:center;justify-content:center;
  transition:width .35s var(--ease),height .35s var(--ease),margin .35s var(--ease),
             background-color .35s ease,border-color .35s ease;
}
.cursor-f span{
  font-size:9.5px;font-weight:600;letter-spacing:.16em;color:#FFF;
  opacity:0;transition:opacity .25s ease;text-transform:uppercase;
}
.cursor-f.is-hover{width:58px;height:58px;margin:-29px 0 0 -29px;border-color:var(--accent)}
.cursor-f.is-view{
  width:92px;height:92px;margin:-46px 0 0 -46px;
  background:var(--accent);border-color:var(--accent);
}
.cursor-f.is-view span{opacity:1}
@media(pointer:coarse){.cursor,.cursor-f{display:none}}

/* canvas that draws the crimson spark bursts on text hover */
#fx{position:fixed;inset:0;pointer-events:none;z-index:140}

/* ============================== 5. SCROLL PROGRESS ============================== */
.sprog{
  position:fixed;top:0;left:0;right:0;height:2px;z-index:120;
  background:var(--line);pointer-events:none;
}
.sprog i{
  display:block;height:100%;width:100%;transform:scaleX(0);transform-origin:left;
  background:linear-gradient(90deg,var(--accent),var(--accent-2));
}

/* ============================== 6. PRELOADER ============================== */
/* On brand with the logo: the site boots like a terminal. */
#preloader{
  position:fixed;inset:0;z-index:200;background:var(--dark);
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:1.6rem;
  transition:transform 1.05s var(--ease) .15s;
}
body.loaded #preloader{transform:translateY(-101%)}
.pre-line{
  display:flex;align-items:center;gap:.6rem;
  font-family:var(--font-m);font-size:clamp(1rem,3.2vw,1.6rem);color:var(--fg);
}
.pre-line .cx{color:var(--accent);font-weight:700}
.pre-line .tw{white-space:pre}
.pre-line .cur{
  display:inline-block;width:.62em;height:1.05em;background:var(--accent-2);
  animation:curBlink 1s steps(1) infinite;translate:0 .12em;
}
@keyframes curBlink{50%{opacity:0}}
.pre-mark{
  height:clamp(46px,9vw,74px);width:auto;
  opacity:0;transform:translateY(14px) scale(.94);
  animation:preMark .9s var(--ease) .1s forwards;
}
@keyframes preMark{to{opacity:1;transform:none}}
.pre-tag{
  position:absolute;left:4vw;bottom:3.4vw;
  font-size:10.5px;letter-spacing:.3em;text-transform:uppercase;color:var(--muted);
}
.pre-num{
  position:absolute;right:4vw;bottom:2.2vw;
  font-family:var(--font-d);font-weight:800;line-height:1;
  font-size:clamp(3.6rem,12vw,9rem);color:var(--fg);opacity:.9;
  font-variant-numeric:tabular-nums;
}
.pre-bar{
  position:absolute;left:0;bottom:0;width:100%;height:2px;
  background:var(--accent);transform:scaleX(0);transform-origin:left;
}

/* ============================== 7. NAV ============================== */
.nav{
  position:fixed;top:0;left:0;right:0;z-index:110;
  display:flex;justify-content:space-between;align-items:center;
  gap:1.5rem;padding:20px 4vw;height:var(--nav-h);
  transition:background-color .5s ease,backdrop-filter .5s ease,border-color .5s ease;
  border-bottom:1px solid transparent;
}
/* solid once you've scrolled — mix-blend-mode looks great over the hero but
   turns unreadable over mixed content, so we swap to a real backdrop */
.nav.stuck{
  background:color-mix(in srgb,var(--bg) 82%,transparent);
  backdrop-filter:blur(14px) saturate(140%);
  -webkit-backdrop-filter:blur(14px) saturate(140%);
  border-bottom-color:var(--line);
}
@supports not (background:color-mix(in srgb,red,blue)){
  .nav.stuck{background:rgba(14,4,7,.86)}
}

/* the lockup: inlined mark + live Syne text. Sharper than any SVG wordmark
   and it inherits theme colour for free. */
.lockup{display:inline-flex;align-items:center;gap:.66em;font-size:19px}
.lockup .lk-mark{width:2.16em;height:2.16em;flex:none;overflow:visible}
.lockup .lk-txt{display:flex;flex-direction:column;line-height:1}
.lockup .lk-a{font-family:var(--font-d);font-weight:800;font-size:1.58em;letter-spacing:-.014em}
.lockup .lk-b{
  font-family:var(--font-b);font-weight:500;font-size:.55em;
  letter-spacing:.33em;color:var(--accent);margin-top:.36em;
}
.lk-cursor{animation:curBlink 1.1s steps(1) infinite}
.lockup .lk-frame,.lockup .lk-chev{transition:stroke .4s ease}
.lockup:hover .lk-frame{stroke:var(--accent-2)}

.nav-links{display:flex;gap:2.1rem;font-size:.78rem;letter-spacing:.15em;text-transform:uppercase}
.nav-links a{position:relative;padding:5px 0;white-space:nowrap}
.nav-links a::after{
  content:'';position:absolute;left:0;bottom:0;width:100%;height:1px;
  background:var(--accent);transform:scaleX(0);transform-origin:right;
  transition:transform .45s var(--ease);
}
.nav-links a:hover::after,.nav-links a[aria-current="page"]::after{transform:scaleX(1);transform-origin:left}
.nav-links a[aria-current="page"]{color:var(--accent)}
.nav-right{display:flex;align-items:center;gap:1rem}

/* burger — iftsol has no mobile menu at all, so this is new */
.burger{
  display:none;width:44px;height:44px;position:relative;flex:none;
  border:1px solid var(--line-2);border-radius:50%;
}
.burger i{
  position:absolute;left:13px;width:18px;height:1.5px;background:var(--fg);
  transition:transform .45s var(--ease),opacity .3s ease;
}
.burger i:nth-child(1){top:18px}
.burger i:nth-child(2){top:24px}
body.menu-open .burger i:nth-child(1){transform:translateY(3px) rotate(45deg)}
body.menu-open .burger i:nth-child(2){transform:translateY(-3px) rotate(-45deg)}
@media(max-width:900px){
  .nav-links{display:none}
  .burger{display:block}
  /* The CTA used to be display:none here, which left the phone header with no
     way to enrol at all — the burger menu's "Admissions" link was the only
     route and you had to open the menu to find it. Kept, just compact. */
  .nav-cta{padding:11px 17px;font-size:.62rem;letter-spacing:.14em}
  .lockup{font-size:17px}
  .nav{gap:.7rem}
}
/* Below this the lockup, the pill and the burger genuinely stop fitting on one
   line — 150 + 90 + 44 + gaps against a 300px content box. The menu carries it
   from here down. */
@media(max-width:390px){
  .nav-cta{display:none}
}

/* full-screen overlay menu */
.menu{
  position:fixed;inset:0;z-index:105;background:var(--dark);
  display:flex;flex-direction:column;justify-content:center;gap:.2rem;
  padding:var(--nav-h) 8vw 8vh;
  clip-path:circle(0% at calc(100% - 44px) 42px);
  transition:clip-path .8s var(--ease);
  pointer-events:none;overflow-y:auto;
}
body.menu-open .menu{clip-path:circle(150% at calc(100% - 44px) 42px);pointer-events:auto}
.menu a{
  font-family:var(--font-d);font-weight:800;text-transform:uppercase;
  font-size:clamp(1.9rem,8vw,3.4rem);line-height:1.1;
  display:flex;align-items:baseline;gap:.7rem;
  opacity:0;transform:translateY(28px);
  transition:opacity .6s var(--ease),transform .6s var(--ease),color .3s ease;
}
.menu a span{font-family:var(--font-b);font-size:.9rem;font-weight:500;color:var(--accent);letter-spacing:.1em}
.menu a:hover{color:var(--accent)}
body.menu-open .menu a{opacity:1;transform:none}
body.menu-open .menu a:nth-child(1){transition-delay:.22s}
body.menu-open .menu a:nth-child(2){transition-delay:.28s}
body.menu-open .menu a:nth-child(3){transition-delay:.34s}
body.menu-open .menu a:nth-child(4){transition-delay:.40s}
body.menu-open .menu a:nth-child(5){transition-delay:.46s}
body.menu-open .menu a:nth-child(6){transition-delay:.52s}
.menu-foot{
  margin-top:3rem;padding-top:1.6rem;border-top:1px solid var(--line);
  display:flex;gap:2rem;flex-wrap:wrap;color:var(--muted);font-size:.9rem;
  opacity:0;transition:opacity .6s ease .6s;
}
body.menu-open .menu-foot{opacity:1}
.menu-foot a{font-family:var(--font-b);font-size:.9rem;font-weight:400;text-transform:none;opacity:1;transform:none}

/* ============================== 8. LAYOUT ============================== */
#content{position:relative;width:100%}
#content.is-fixed{position:fixed;top:0;left:0;will-change:transform}
main{min-height:60vh}
.wrap{padding-left:4vw;padding-right:4vw}
.sec{padding:15vh 4vw;position:relative}
.sec-sm{padding:9vh 4vw}
.sec-head{
  display:flex;flex-wrap:wrap;justify-content:space-between;align-items:baseline;gap:.6rem 2rem;
  padding-bottom:1.5rem;margin-bottom:6.5vh;position:relative;
}
.sec-head::after{
  content:'';position:absolute;left:0;bottom:0;width:100%;height:1px;background:var(--line);
  transform:scaleX(0);transform-origin:left;transition:transform 1.4s var(--ease) .15s;
}
.sec-head.in-view::after{transform:scaleX(1)}
.sec-title{
  font-family:var(--font-d);font-weight:800;text-transform:uppercase;
  font-size:clamp(1.9rem,5.4vw,4.4rem);line-height:.95;letter-spacing:-.012em;
}
.sec-count{
  font-size:.71rem;letter-spacing:.24em;text-transform:uppercase;
  color:var(--muted);white-space:nowrap;
}
.eyebrow{
  font-size:10.5px;letter-spacing:.3em;text-transform:uppercase;color:var(--muted);
  display:flex;align-items:center;gap:.9em;
}
.eyebrow::before{content:'';width:34px;height:1px;background:var(--ca);flex:none}
.lead{color:var(--muted);font-size:clamp(1rem,1.25vw,1.12rem);line-height:1.75;max-width:58ch}

/* ============================== 9. BUTTONS ============================== */
.btn{
  position:relative;display:inline-flex;align-items:center;gap:.7em;overflow:hidden;
  border:1px solid var(--line-2);border-radius:999px;padding:16px 33px;
  color:var(--fg);background:transparent;
  font-size:.75rem;font-weight:500;letter-spacing:.19em;text-transform:uppercase;
  transition:color .45s var(--ease),border-color .45s ease;
}
.btn::before{
  content:'';position:absolute;inset:0;border-radius:inherit;background:var(--ca);
  transform:translateY(102%);transition:transform .5s var(--ease);
}
.btn:hover::before{transform:translateY(0)}
.btn:hover{color:#FFF;border-color:var(--ca)}
.btn.solid{background:var(--ca);border-color:var(--ca);color:#FFF;font-weight:600}
.btn.solid::before{background:var(--fg)}
.btn.solid:hover{color:var(--bg)}
.btn.sm{padding:12px 24px;font-size:.69rem}
.btn .magnetic-inner{position:relative;z-index:1}
.magnetic-inner{display:inline-block;transition:transform .5s var(--ease)}
.nav-cta{
  border:1px solid var(--line-2);border-radius:999px;padding:11px 23px;
  font-size:.71rem;letter-spacing:.17em;text-transform:uppercase;
  transition:background-color .4s ease,color .4s ease,border-color .4s ease;
}
.nav-cta:hover{background:var(--accent);border-color:var(--accent);color:#FFF}

/* ============================== 10. REVEALS ============================== */
[data-reveal]{
  opacity:0;transform:translateY(44px);
  transition:opacity .95s var(--ease),transform .95s var(--ease);
}
[data-reveal].in-view{opacity:1;transform:none}

/* per-character headline reveal — JS wraps each char in .ch > i */
.split{display:inline-block}
/* Per-word wrapper from site.js. nowrap is the whole point: each character is an
   inline-block and therefore its own break opportunity, so without this a line
   can break in the middle of a word. Spaces between wrappers stay real text
   nodes, so lines still break where they should. */
.split .wd{display:inline-block;white-space:nowrap;vertical-align:top}
.split .ch{display:inline-block;overflow:hidden;vertical-align:top}
.split .ch > i{
  display:inline-block;font-style:normal;
  transform:translateY(104%) rotate(6deg);transform-origin:left top;
  transition:transform 1s var(--ease);
}
.split.in-view .ch > i{transform:none}

/* decode/scramble on section titles — JS swaps glyphs, CSS only tints */
.scr.scrambling{color:var(--ca)}

/* ============================== 11. MARQUEE ============================== */
.marquee{
  border-top:1px solid var(--line);border-bottom:1px solid var(--line);
  padding:1.4rem 0;overflow:hidden;white-space:nowrap;will-change:transform;
}
.marquee-track{display:inline-flex;animation:marq 30s linear infinite}
.marquee-track > span{
  font-family:var(--font-d);font-weight:700;text-transform:uppercase;
  font-size:clamp(1.4rem,3.2vw,2.8rem);letter-spacing:.01em;padding-right:.55em;
}
.mq-dot{color:var(--accent);font-weight:400}
@keyframes marq{to{transform:translateX(-50%)}}
.marquee:hover .marquee-track{animation-play-state:paused}

/* ============================== 11b. HERO (home) ============================== */
/* Deliberately NOT full-viewport. It used to be min-height:100svh with the
   headline pinned to the bottom edge, which meant the first thing a visitor
   could act on — the tracks — was a whole screen away. Now the hero is sized by
   its own content and sits beside the 3D carousel (hero3d.css), so the courses
   are visible without scrolling at all. */
.hero{
  position:relative;overflow:hidden;
  padding:calc(var(--nav-h) + 5vh) 4vw 6vh;
}
.hero::before{
  content:'';position:absolute;inset:0;z-index:0;pointer-events:none;
  background:radial-gradient(58% 62% at 78% 26%,rgba(255,61,87,.15),transparent 62%);
}
/* Two columns: type on the left, the carousel on the right. `min-width:0` on the
   grid children is what stops the carousel's absolutely-positioned cards from
   forcing the track wider than its share. */
.hero-inner{
  position:relative;z-index:2;
  display:grid;grid-template-columns:minmax(0,1.18fr) minmax(0,1fr);
  gap:clamp(1.8rem,3.6vw,4.2rem);align-items:center;
}
.hero-copy{min-width:0;container-type:inline-size}
.hero .eyebrow{margin-bottom:1.5rem}
/* "GET YOU HIRED." is 15.3em wide in Syne 800 uppercase. Sized against the
   viewport it does not fit its own column at any font size a hero wants, and
   it wrapped into seven lines — which is how a hero that was supposed to get
   shorter ended up taller than the one it replaced.
   cqw sizes the headline against the COLUMN instead, so the longest line
   always fits whatever width the grid gives it. The vw clamp above is the
   fallback for browsers without container queries. */
.hero h1{
  font-family:var(--font-d);font-weight:800;text-transform:uppercase;
  font-size:min(3.4rem,3.05vw);line-height:.94;letter-spacing:-.018em;
}
/* 6.8cqw is measured, not guessed: "GET YOU HIRED." is 13.38em wide in Syne
   ExtraBold at -.018em tracking, so 13.38 x 0.068 = 91% of the column. That
   leaves headroom for the 3.4rem cap and for a font that fails to load. */
@supports (font-size:1cqw){
  .hero h1{font-size:min(3.4rem,6.8cqw)}
}
.h1-line{display:block}
.outline{color:transparent;-webkit-text-stroke:2px var(--fg);transition:-webkit-text-stroke-color .5s ease,color .5s ease}
.hero h1:hover .outline{-webkit-text-stroke-color:var(--accent);color:rgba(255,61,87,.07)}
.hero-foot{display:flex;flex-direction:column;align-items:flex-start;gap:1.9rem;margin-top:2.2rem}
.hero-sub{max-width:44ch;color:var(--muted);line-height:1.68;font-size:clamp(.92rem,1.05vw,1rem)}
.hero-cta{display:flex;gap:.85rem;flex-wrap:wrap}

/* the hero's own entrance — held until the preloader has finished */
.hero-fade{opacity:0;transform:translateY(24px);transition:opacity 1s var(--ease),transform 1s var(--ease)}
body.loaded .hero-fade{opacity:1;transform:none}
body.loaded .eyebrow.hero-fade{transition-delay:.5s}
body.loaded .hero-sub.hero-fade{transition-delay:.85s}
body.loaded .hero-cta.hero-fade{transition-delay:.95s}
body.loaded .scroll-hint.hero-fade{transition-delay:1.05s}
/* Outranks `.split.in-view .ch > i` (0,2,3 vs 0,2,2) so the per-character
   headline stays hidden while the preloader is still up, instead of playing
   its animation behind an opaque overlay. */
body:not(.loaded) .hero .split .ch > i{transform:translateY(104%) rotate(6deg)}

/* The scroll hint sits inline in the CTA row now that the hero no longer fills
   the viewport — there is no bottom edge to pin it to. */
.scroll-hint{
  display:flex;align-items:center;gap:11px;
  color:var(--muted);font-size:9.5px;letter-spacing:.3em;text-transform:uppercase;
}
.scroll-hint b{font-weight:400}
.scroll-hint i{width:46px;height:1px;background:var(--line);position:relative;overflow:hidden}
.scroll-hint i::after{
  content:'';position:absolute;top:0;left:-100%;height:100%;width:100%;
  background:var(--accent);animation:drift-x 1.9s var(--ease) infinite;
}
@keyframes drift-x{to{left:100%}}

/* Single column below 1100px: two columns squeeze both the headline and the
   carousel into unusable widths long before the viewport gets narrow. At
   1025px the ring had a 207px half-width stage to put a 178px card in. */
@media(max-width:1100px){
  .hero{padding-top:calc(var(--nav-h) + 4vh)}
  .hero-inner{grid-template-columns:1fr;gap:clamp(2.4rem,6vw,4rem)}
  /* full width again, so the headline can go back up to hero scale */
  /* min(), not clamp(): a clamp floor is a floor the column cannot honour on
     a 360px phone, and the headline wrapped again below ~440px. */
  .hero h1{font-size:min(4.1rem,6.6vw)}
  @supports (font-size:1cqw){ .hero h1{font-size:min(4.1rem,7cqw)} }
  .hero-sub{max-width:52ch}
}
/* ── the hero headline on phones ───────────────────────────────────────────
   Above this width the headline is sized so its longest line never wraps.
   Below it that goal is self-defeating: "GET YOU HIRED." is 13.38em wide, so
   fitting it unwrapped into a 350px column means a 25px hero headline. Let it
   wrap and give it real size instead — four lines of 45px reads as a hero,
   three lines of 25px reads as a mistake. */
@media(max-width:700px){
  /* 8.2vw is not a taste call. Syne ExtraBold at -.018em tracking, with the
     font's real 0.31em space glyph: "SKILLS THAT" is 10.40em and
     "GET YOU HIRED." is 13.38em, so any size in
        col/13.38 < font <= col/10.40   ->   6.9vw .. 8.8vw
     wraps to exactly four lines with the same break points at every width.
     8.2vw sits inside that band with room either side and fills 93% of the
     column: LEARN THE / SKILLS THAT / GET YOU / HIRED.
     11.5vw was outside it and gave six lines, "LEARN" and "THE" among them. */
  .hero h1{font-size:min(3.6rem,8.2vw)}
  @supports (font-size:1cqw){
    .hero h1{font-size:min(3.6rem,8.2vw)}
  }
  .hero{padding-top:calc(var(--nav-h) + 3vh);padding-bottom:5vh}
  .hero-foot{gap:1.5rem;margin-top:1.9rem}
  .hero-sub{font-size:.95rem;line-height:1.65}
}
/* Two pills side by side need ~360px of content box. Below that they wrapped
   at their natural width, which reads as an accident rather than a stack — so
   make the stack deliberate and full width. */
@media(max-width:480px){
  .hero-cta{gap:.6rem}
  .hero-cta .btn{flex:1 1 100%;justify-content:center;padding:15px 20px}
}

/* Kept on phones. It was display:none below 600px, which is backwards — the
   hint tells you the cards are draggable, and swiping is the only way to move
   them on a touch screen. */
@media(max-width:600px){
  .scroll-hint{font-size:9px;letter-spacing:.22em}
  .scroll-hint i{width:34px}
}

/* ============================== 12. MANIFESTO + STATS ============================== */
.manifesto{
  font-family:var(--font-d);font-weight:700;
  font-size:clamp(1.5rem,3.9vw,3.3rem);line-height:1.2;max-width:26ch;
}
.manifesto .w{opacity:.14;transition:opacity .32s ease}
.manifesto .w.on{opacity:1}
.stats{display:grid;grid-template-columns:repeat(4,1fr);gap:2.2rem;margin-top:10vh}
.stat{border-top:1px solid var(--line);padding-top:1.2rem}
.stat b{
  font-family:var(--font-d);font-weight:800;line-height:1;display:block;
  font-size:clamp(2.1rem,4.6vw,3.9rem);font-variant-numeric:tabular-nums;
}
.stat b i{font-style:normal;color:var(--accent)}
/* Direct child ONLY. The counter is also a <span>, but nested inside the <b>:
       <b><span data-count>0</span><i>+</i></b><span>Label</span>
   A plain `.stat span` matches both, and since it applies to the counter
   directly it beats the font-size the counter should be inheriting from <b> —
   which renders the headline number at .74rem, grey, uppercase, and forces the
   +/% suffix onto its own line. */
.stat > span{
  display:block;margin-top:.55rem;color:var(--muted);
  font-size:.74rem;letter-spacing:.15em;text-transform:uppercase;
}
@media(max-width:760px){.stats{grid-template-columns:1fr 1fr;gap:1.6rem}}

/* ============================== 13. COURSE WORLDS (pinned scroll story) ============================== */
/* 6 panels wipe in over one pinned viewport. Height = 6 * 90vh + a lead-in;
   JS reads offsetHeight so changing this number is safe. */
.worlds{position:relative;height:600vh}
.worlds-stage{
  position:absolute;top:0;left:0;width:100%;
  height:100vh;height:100svh;overflow:hidden;will-change:transform;
}
.world{
  position:absolute;inset:0;display:flex;align-items:center;overflow:hidden;
  will-change:clip-path,transform;clip-path:inset(100% 0 0 0);
}
.world:first-child{clip-path:inset(0 0 0 0)}
/* a bright hairline rides the clip edge so the wipe reads as a deliberate
   curtain instead of a jump cut */
.world::before{
  content:'';position:absolute;left:0;right:0;top:0;height:2px;z-index:5;
  background:linear-gradient(90deg,transparent,var(--ca),transparent);
  opacity:0;transition:opacity .3s ease;
}
.world.wiping::before{opacity:1}

.wld-ai    {--ca:#FFC24B;background:radial-gradient(130% 120% at 78% 18%,#4A3208 0%,#1E1404 52%,#0A0703 100%)}
.wld-cyber {--ca:#FF3D57;background:radial-gradient(130% 120% at 20% 80%,#4A0A18 0%,#20050C 52%,#0A0205 100%)}
.wld-full  {--ca:#4ADEDE;background:radial-gradient(130% 120% at 80% 76%,#07373A 0%,#04191C 52%,#02090C 100%)}
.wld-wp    {--ca:#7C8CFF;background:radial-gradient(130% 120% at 22% 24%,#161C50 0%,#0A0E30 52%,#040614 100%)}
.wld-seo   {--ca:#4ADE80;background:radial-gradient(130% 120% at 78% 26%,#0A3D22 0%,#052314 52%,#020D07 100%)}
.wld-design{--ca:#FF6FD8;background:radial-gradient(130% 120% at 24% 22%,#4A1040 0%,#230722 52%,#0C030B 100%)}
.wld-uiux  {--ca:#B57BFF;background:radial-gradient(130% 120% at 76% 22%,#2E1152 0%,#180929 52%,#08040E 100%)}

.w-num{
  position:absolute;top:-4vw;right:-1vw;line-height:1;pointer-events:none;z-index:0;
  font-family:var(--font-d);font-weight:800;font-size:clamp(8rem,26vw,22rem);
  color:transparent;-webkit-text-stroke:1.5px rgba(243,233,235,.12);
}
.w-content{position:relative;z-index:2;max-width:680px}
.w-tier{
  display:inline-flex;align-items:center;gap:.5em;margin-bottom:1.1rem;
  border:1px solid var(--ca);border-radius:999px;padding:6px 14px;
  font-size:.63rem;font-weight:600;letter-spacing:.2em;text-transform:uppercase;
  color:var(--ca);background:color-mix(in srgb,var(--ca) 12%,transparent);
}
@supports not (background:color-mix(in srgb,red,blue)){.w-tier{background:rgba(255,255,255,.06)}}
.w-eyebrow{
  font-size:10.5px;letter-spacing:.3em;text-transform:uppercase;color:var(--ca);
  margin-bottom:1.3rem;display:flex;align-items:center;gap:.9em;
}
.w-eyebrow::before{content:'';width:34px;height:1px;background:var(--ca);flex:none}
.w-title{
  font-family:var(--font-d);font-weight:800;text-transform:uppercase;
  font-size:clamp(2.3rem,7.4vw,6.4rem);line-height:.9;letter-spacing:-.015em;
}
.w-desc{
  margin-top:1.5rem;color:rgba(243,233,235,.76);max-width:46ch;
  font-size:clamp(.94rem,1.15vw,1.08rem);line-height:1.7;
}
.w-foot{margin-top:2rem}
.w-tags{display:flex;gap:.6rem;flex-wrap:wrap}
.w-tags span{
  border:1px solid rgba(243,233,235,.24);border-radius:999px;padding:7px 16px;
  font-size:.69rem;letter-spacing:.14em;text-transform:uppercase;color:rgba(243,233,235,.84);
}
.w-tags span.hot{
  background:var(--ca);border-color:var(--ca);color:#0A0205;font-weight:600;
  animation:hotPulse 2.4s ease-in-out infinite;
}
@keyframes hotPulse{
  0%,100%{box-shadow:0 0 6px color-mix(in srgb,var(--ca) 60%,transparent)}
  50%{box-shadow:0 0 24px color-mix(in srgb,var(--ca) 80%,transparent)}
}
.w-motif{position:absolute;inset:0;pointer-events:none;z-index:1}
.w-progress{
  position:absolute;left:4vw;bottom:4.2vh;z-index:3;
  display:flex;align-items:center;gap:1rem;
  font-family:var(--font-d);font-weight:700;font-size:.82rem;color:rgba(243,233,235,.85);
}
.w-progress i{display:block;width:120px;height:2px;background:rgba(243,233,235,.2);overflow:hidden}
.w-progress b{display:block;width:100%;height:100%;background:var(--ca);transform:scaleX(0);transform-origin:left}
.w-progress .nm{color:var(--ca);letter-spacing:.06em}

/* --- per-course motifs, pure CSS --- */
@keyframes floaty{0%,100%{transform:translateY(0) rotate(-2deg)}50%{transform:translateY(-16px) rotate(2deg)}}
@keyframes spinSlow{to{transform:rotate(360deg)}}
@keyframes blinkc{50%{opacity:0}}

/* AI — orbiting neural ring */
.m-neural{
  position:absolute;right:11vw;top:22%;width:min(320px,34vw);aspect-ratio:1;
  border-radius:50%;border:1px dashed rgba(255,194,75,.3);animation:spinSlow 26s linear infinite;
}
.m-neural::before,.m-neural::after{
  content:'';position:absolute;border-radius:50%;background:#FFC24B;
}
.m-neural::before{width:12px;height:12px;top:-6px;left:calc(50% - 6px);box-shadow:0 0 18px #FFC24B}
.m-neural::after{width:8px;height:8px;bottom:12%;right:6%;box-shadow:0 0 14px #FFC24B}
.m-core{
  position:absolute;right:calc(11vw + min(160px,17vw) - 26px);top:calc(22% + min(160px,17vw) - 26px);
  width:52px;height:52px;border-radius:50%;
  background:radial-gradient(circle,#FFC24B,transparent 70%);animation:hotPulse 2.6s ease-in-out infinite;
}

/* Cyber — radar sweep + blips */
.m-radar{
  position:absolute;right:11vw;top:20%;width:min(330px,35vw);aspect-ratio:1;
  border-radius:50%;border:1px solid rgba(255,61,87,.34);overflow:hidden;
}
.m-radar::before{content:'';position:absolute;inset:24%;border-radius:50%;border:1px dashed rgba(255,61,87,.3)}
.m-radar::after{
  content:'';position:absolute;inset:0;border-radius:50%;
  background:conic-gradient(from 0deg,rgba(255,61,87,.5),rgba(255,61,87,.05) 78deg,transparent 120deg);
  animation:spinSlow 3.4s linear infinite;
}
.m-blip{position:absolute;width:8px;height:8px;border-radius:50%;background:#FF3D57;animation:blipP 2s ease-out infinite}
.m-blip.b1{right:17vw;top:31%}
.m-blip.b2{right:22vw;top:47%;animation-delay:1s}
@keyframes blipP{
  0%{box-shadow:0 0 0 0 rgba(255,61,87,.7);opacity:1}
  100%{box-shadow:0 0 0 26px rgba(255,61,87,0);opacity:.6}
}

/* Full stack — code glyph + caret */
.m-code{
  position:absolute;right:10vw;top:24%;line-height:1;
  font-family:var(--font-d);font-weight:800;font-size:clamp(5rem,15vw,13rem);
  color:transparent;-webkit-text-stroke:1.5px rgba(74,222,222,.4);
  animation:floaty 8s ease-in-out infinite;
}
.m-caret{position:absolute;right:13vw;top:63%;width:22px;height:38px;background:#4ADEDE;animation:blinkc 1.1s steps(1) infinite}

/* WordPress — stacked blocks */
.m-blocks{position:absolute;right:13vw;top:24%;display:grid;gap:12px;width:min(260px,28vw)}
.m-blocks i{
  display:block;height:26px;border-radius:6px;border:1.5px solid rgba(124,140,255,.5);
  background:rgba(124,140,255,.07);animation:floaty 7s ease-in-out infinite;
}
.m-blocks i:nth-child(1){width:100%}
.m-blocks i:nth-child(2){width:74%;animation-delay:-1.2s}
.m-blocks i:nth-child(3){width:88%;animation-delay:-2.4s}
.m-blocks i:nth-child(4){width:60%;animation-delay:-3.6s}

/* SEO — growth bars + arrow */
.m-bars{position:absolute;right:14vw;bottom:18%;display:flex;align-items:flex-end;gap:13px;height:240px}
.m-bars i{
  display:block;width:27px;border-radius:6px 6px 0 0;transform-origin:bottom;
  background:linear-gradient(to top,rgba(74,222,128,.1),#4ADE80);
  animation:growUp 2.6s var(--ease) infinite alternate;
}
.m-bars i:nth-child(1){height:32%}
.m-bars i:nth-child(2){height:52%;animation-delay:.15s}
.m-bars i:nth-child(3){height:70%;animation-delay:.3s}
.m-bars i:nth-child(4){height:96%;animation-delay:.45s}
@keyframes growUp{from{transform:scaleY(.72)}to{transform:scaleY(1)}}
.m-arrow{
  position:absolute;right:8vw;bottom:54%;line-height:1;
  font-family:var(--font-d);font-weight:800;font-size:clamp(3.5rem,8vw,7rem);
  color:transparent;-webkit-text-stroke:1.5px rgba(74,222,128,.45);
  animation:floaty 6s ease-in-out infinite;
}

/* Design — type specimen + swatches */
.m-aa{
  position:absolute;right:12vw;top:21%;line-height:1;
  font-family:var(--font-d);font-weight:800;font-size:clamp(5rem,14vw,12rem);
  color:transparent;-webkit-text-stroke:1.5px rgba(255,111,216,.42);
  animation:floaty 8s ease-in-out infinite;
}
.m-strip{position:absolute;width:14px;border-radius:7px;height:40%}
.m-strip.s1{right:29vw;top:34%;background:linear-gradient(#FF6FD8,transparent)}
.m-strip.s2{right:26vw;top:40%;background:linear-gradient(rgba(243,233,235,.5),transparent)}

/* Track 07 — a wireframe artboard mid-build: an outer frame, a header bar, two
   component blocks and a cursor. Chosen to read as *product* design, so it does
   not repeat the Aa-and-strips motif of the graphic-design track next to it. */
.m-frame{
  position:absolute;right:13vw;top:22%;width:min(24vw,300px);aspect-ratio:3/4;
  border:2px solid rgba(181,123,255,.5);border-radius:10px;
  animation:floaty 9s ease-in-out infinite;
}
.m-frame::before{
  content:'';position:absolute;left:8%;right:8%;top:7%;height:7%;
  border-radius:4px;background:rgba(181,123,255,.34);
}
.m-frame i{
  position:absolute;left:8%;border-radius:4px;
  background:rgba(243,233,235,.14);border:1px solid rgba(181,123,255,.28);
}
.m-frame i:nth-child(1){top:22%;width:52%;height:16%}
.m-frame i:nth-child(2){top:44%;width:84%;height:11%}
.m-frame i:nth-child(3){top:60%;width:66%;height:11%}
.m-cursor{
  position:absolute;right:11.5vw;top:56%;width:0;height:0;
  border-left:11px solid #B57BFF;border-bottom:8px solid transparent;
  border-top:8px solid transparent;
  rotate:-24deg;
  animation:floaty 6.5s ease-in-out infinite reverse;
}

/* ── the panels on narrow screens ──────────────────────────────────────────
   The motifs are positioned against the right edge, and the copy runs full
   width here, so the two used to sit on top of each other — which is why the
   whole motif layer was dropped to opacity .3 and the AI core was switched off
   entirely. Both were losses, not decisions.
   Instead the panel splits: the motif owns the top, the copy the bottom. The
   panel is 100svh and the copy is ~380px of it, so pinning the copy to the
   bottom leaves the top third free for artwork that can then be seen. */
@media(max-width:900px){
  .worlds{height:540vh}
  .w-motif{opacity:.66}
  .w-num{font-size:clamp(6rem,38vw,12rem);top:-2vw}
  .w-content{max-width:100%}

  /* Every motif re-anchored into the top band and scaled to fit it. Sizes are
     vmin-based so a short landscape phone shrinks them too, rather than
     letting a 34vw circle run off the bottom of a 380px-tall viewport. */
  .m-neural{right:6vw;top:4vh;width:min(210px,42vmin)}
  .m-core{
    right:calc(6vw + min(105px,21vmin) - 19px);
    top:calc(4vh + min(105px,21vmin) - 19px);
    width:38px;height:38px;
  }
  .m-radar{right:6vw;top:4vh;width:min(210px,42vmin)}
  .m-blip.b1{right:16vw;top:12vh}
  .m-blip.b2{right:26vw;top:20vh}
  .m-code{right:7vw;top:2vh;font-size:min(7.4rem,30vmin)}
  .m-caret{right:12vw;top:24vh;width:16px;height:28px}
  .m-blocks{right:7vw;top:5vh;width:min(180px,40vw);gap:9px}
  .m-blocks i{height:20px}
  .m-bars{right:8vw;bottom:auto;top:5vh;height:min(190px,26vh);gap:9px}
  .m-bars i{width:20px}
  .m-arrow{right:5vw;bottom:auto;top:3vh;font-size:min(4.4rem,17vmin)}
  .m-aa{right:8vw;top:2vh;font-size:min(7rem,28vmin)}
  .m-strip{height:min(150px,20vh)}
  .m-strip.s1{right:31vw;top:6vh}
  .m-strip.s2{right:26vw;top:9vh}
  .m-frame{right:8vw;top:4vh;width:min(148px,30vw)}
  .m-cursor{right:6vw;top:20vh}
}
/* The top/bottom split itself is height-gated. The motifs occupy y 17..232 of
   an 844px panel and the copy is ~480px, so bottom-aligning the copy leaves a
   clear band for the artwork. On a 390px-tall landscape phone the copy is
   already taller than the panel — bottom-aligning there would clip the
   headline off the top rather than clipping evenly at both ends, which is what
   centring does. */
@media(max-width:900px) and (min-height:620px){
  .world{align-items:flex-end}
  .w-content{padding-bottom:7vh}
}
/* Short viewport: no room to separate them, so keep the copy centred and pull
   the motif back far enough that the overlap stays legible. */
@media(max-width:900px) and (max-height:619px){
  .w-motif{opacity:.34}
}

/* ============================== 14. COURSE CARDS (3D tilt + spotlight) ============================== */
.card-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:1.6rem}
@media(max-width:1000px){.card-grid{grid-template-columns:1fr 1fr}}
@media(max-width:640px){.card-grid{grid-template-columns:1fr}}

.tilt{
  position:relative;display:flex;flex-direction:column;gap:1rem;
  border:1px solid var(--line);border-radius:12px;padding:1.9rem 1.7rem 1.7rem;
  background:var(--panel);overflow:hidden;isolation:isolate;
  transform-style:preserve-3d;
  transition:border-color .45s ease,box-shadow .45s ease;
}
.tilt:hover{border-color:var(--ca);box-shadow:0 22px 60px -30px color-mix(in srgb,var(--ca) 60%,transparent)}
@supports not (background:color-mix(in srgb,red,blue)){.tilt:hover{box-shadow:0 22px 60px -30px rgba(0,0,0,.7)}}
/* cursor-following spotlight; --mx/--my are set by JS in percent */
.tilt::before{
  content:'';position:absolute;inset:0;z-index:-1;pointer-events:none;opacity:0;
  background:radial-gradient(320px circle at var(--mx,50%) var(--my,50%),
             color-mix(in srgb,var(--ca) 16%,transparent),transparent 70%);
  transition:opacity .4s ease;
}
.tilt:hover::before{opacity:1}
.tilt .t-inner{transform:translateZ(34px);display:flex;flex-direction:column;gap:.85rem;height:100%}
.t-top{display:flex;justify-content:space-between;align-items:flex-start;gap:1rem}
.t-num{
  font-family:var(--font-d);font-weight:800;font-size:.78rem;letter-spacing:.22em;color:var(--ca);
}
.t-tier{
  font-size:.6rem;font-weight:600;letter-spacing:.2em;text-transform:uppercase;
  border:1px solid var(--ca);border-radius:999px;padding:4px 10px;color:var(--ca);
}
.t-ico{width:40px;height:40px;color:var(--ca);flex:none}
.tilt h3{font-family:var(--font-d);font-weight:700;font-size:1.32rem;line-height:1.2}
.tilt p{color:var(--muted);font-size:.92rem;line-height:1.62}
.t-meta{
  display:flex;gap:1.2rem;flex-wrap:wrap;margin-top:auto;padding-top:1rem;
  border-top:1px solid var(--line);
  font-size:.7rem;letter-spacing:.13em;text-transform:uppercase;color:var(--muted);
}
.t-meta b{color:var(--fg);font-weight:500}
.t-go{
  display:inline-flex;align-items:center;gap:.5em;margin-top:.4rem;
  font-size:.71rem;letter-spacing:.16em;text-transform:uppercase;color:var(--ca);
}
.t-go svg{width:14px;height:14px;transition:transform .5s var(--ease)}
.tilt:hover .t-go svg{transform:translate(4px,-4px)}

/* ============================== 15. ROADMAP (scroll-drawn path) ============================== */
.roadmap{position:relative;display:grid;grid-template-columns:74px 1fr;gap:2.5vw}
/* The rail spans the full height of the steps column. It deliberately does NOT
   use position:sticky — #content is position:fixed and translated, so there is
   no scrollport inside it for sticky to resolve against and it would simply
   never stick. Instead the SVG stretches to the grid row height with
   preserveAspectRatio="none", and vector-effect="non-scaling-stroke" on the
   path keeps the line 2.5px however far it is stretched. */
.rm-rail{position:relative}
.rm-rail svg{position:absolute;inset:0;width:100%;height:100%}
.rm-rail path{
  fill:none;stroke:var(--accent);stroke-width:2.5;stroke-linecap:round;
  vector-effect:non-scaling-stroke;
  /* pathLength normalises the dash maths so JS can just set a 0-1 value */
  stroke-dasharray:1;stroke-dashoffset:1;
}
.rm-steps{display:flex;flex-direction:column;gap:2px}
.rm-step{
  position:relative;padding:2.6rem 0 2.6rem 2.4rem;border-top:1px solid var(--line);
  transition:padding-left .55s var(--ease);
}
.rm-step:last-child{border-bottom:1px solid var(--line)}
.rm-step:hover{padding-left:3.4rem}
.rm-step::before{
  content:'';position:absolute;left:0;top:calc(2.6rem + .5em);
  width:11px;height:11px;border-radius:50%;
  background:var(--bg);border:2px solid var(--line-2);
  transition:border-color .5s ease,background-color .5s ease,box-shadow .5s ease;
}
.rm-step.lit::before{
  border-color:var(--accent);background:var(--accent);
  box-shadow:0 0 0 5px var(--accent-soft);
}
.rm-step .ph{
  font-size:.68rem;letter-spacing:.22em;text-transform:uppercase;color:var(--accent);
  display:block;margin-bottom:.7rem;
}
.rm-step h3{font-family:var(--font-d);font-weight:800;text-transform:uppercase;font-size:clamp(1.2rem,2.6vw,1.9rem)}
.rm-step p{color:var(--muted);margin-top:.7rem;max-width:56ch;line-height:1.7}
.rm-step ul{margin:.9rem 0 0;padding-left:1.1rem;color:var(--muted);font-size:.92rem}
.rm-step li{margin:.3rem 0}
@media(max-width:760px){
  /* The rail was display:none below this width, so the one thing that makes
     this section feel alive — a crimson path drawing itself as you scroll —
     only ever existed on desktop. It needs a column, not a wide one: the SVG
     is a 0 0 40 520 viewBox stretched with preserveAspectRatio="none", and its
     path only occupies x 6..34. */
  .roadmap{grid-template-columns:34px 1fr;gap:0}
  .rm-step{padding-left:1.5rem}
  .rm-step:hover{padding-left:2.1rem}
}

/* ============================== SECTION LABEL ============================== */
/* Rendered as an <h2>, not a <p>. These labels ARE the section headings — every
   page previously went h1 straight to h3 because the only thing between them was
   this, marked up as a paragraph, so no page had a usable document outline for a
   crawler or a screen reader.
   font-weight is pinned because an h2 defaults to bold and the paragraph was not;
   font-size was already explicit, and the global `*` reset zeroes the UA margin. */
.sec-label{
  font-size:.71rem;font-weight:400;letter-spacing:.24em;text-transform:uppercase;
  color:var(--muted);
  margin-bottom:3.2rem;display:flex;align-items:center;gap:.9em;
}
.sec-label::before{content:'';width:34px;height:1px;background:var(--ca);flex:none}

/* ============================== 16. TESTIMONIALS ============================== */
/* --rail-air / negative margin: see the long note on .vt-rail in media.css.
   Both rails clip horizontally by necessity, so any hover lift or glow has to
   be paid for with padding inside the box. */
.tm-rail{--rail-air:20px;padding-block:var(--rail-air);margin-block:calc(var(--rail-air) * -1);overflow:hidden;position:relative;cursor:grab;-webkit-mask:linear-gradient(90deg,transparent,#000 6%,#000 94%,transparent);mask:linear-gradient(90deg,transparent,#000 6%,#000 94%,transparent)}
.tm-rail.dragging{cursor:grabbing}
.tm-track{display:flex;gap:1.4rem;width:max-content;will-change:transform}
.tm{
  width:min(400px,80vw);flex:none;border:1px solid var(--line);border-radius:12px;
  padding:1.8rem 1.6rem;background:var(--panel);
  display:flex;flex-direction:column;gap:1.1rem;
  transition:border-color .4s ease;
}
.tm:hover{border-color:var(--accent)}
.tm .quote{color:var(--fg);font-size:.98rem;line-height:1.7}
/* Named .tm-who, not .who: course.css already uses .who for a completely
   different two-column grid. They only avoid colliding today because
   course.css is not loaded on the home page — that is luck, not design. */
.tm-who{display:flex;align-items:center;gap:.9rem;margin-top:auto;padding-top:.4rem}
.tm-who .av{
  width:42px;height:42px;border-radius:50%;flex:none;
  display:grid;place-items:center;font-family:var(--font-d);font-weight:800;font-size:.9rem;
  background:var(--accent-soft);color:var(--accent);border:1px solid var(--accent);
}
/* The name/track pair is wrapped in .tm-id and targeted through it. A blanket
   `.tm-who span` would also hit .av — and at (0,2,1) it outranks the (0,2,0)
   avatar rule, stripping its grid centring, accent colour and size. */
.tm-who .tm-id{display:flex;flex-direction:column;gap:.16rem;min-width:0}
.tm-who .tm-id b{font-size:.92rem;font-weight:600}
.tm-who .tm-id span{color:var(--muted);font-size:.76rem;letter-spacing:.08em}
.tm .stars{color:var(--accent-2);font-size:.85rem;letter-spacing:.16em}

/* ============================== 17. FAQ ============================== */
.faq{border-top:1px solid var(--line)}
.faq details{border-bottom:1px solid var(--line)}
.faq summary{
  display:flex;justify-content:space-between;align-items:center;gap:1.5rem;
  padding:1.7rem 0;list-style:none;
  font-family:var(--font-d);font-weight:700;font-size:clamp(1rem,2.1vw,1.28rem);
  transition:color .35s ease;
}
.faq summary::-webkit-details-marker{display:none}
.faq summary:hover{color:var(--accent)}
.faq summary .pm{position:relative;width:16px;height:16px;flex:none}
.faq summary .pm::before,.faq summary .pm::after{
  content:'';position:absolute;background:var(--accent);
  transition:transform .45s var(--ease),opacity .3s ease;
}
.faq summary .pm::before{left:0;top:7.5px;width:16px;height:1.5px}
.faq summary .pm::after{left:7.5px;top:0;width:1.5px;height:16px}
.faq details[open] summary .pm::after{transform:rotate(90deg);opacity:0}
/* grid-template-rows 0fr -> 1fr is the one reliable way to animate to
   auto height without measuring in JS */
.faq .ans{display:grid;grid-template-rows:0fr;transition:grid-template-rows .5s var(--ease)}
.faq details[open] .ans{grid-template-rows:1fr}
.faq .ans > div{overflow:hidden}
.faq .ans p{color:var(--muted);line-height:1.75;padding-bottom:1.7rem;max-width:70ch}

/* ============================== 18. FORMS ============================== */
.form-grid{display:grid;grid-template-columns:1.15fr .85fr;gap:5vw}
@media(max-width:900px){.form-grid{grid-template-columns:1fr;gap:3rem}}
.field{position:relative;margin-bottom:1.5rem}
.field label{
  display:block;font-size:.69rem;letter-spacing:.2em;text-transform:uppercase;
  color:var(--muted);margin-bottom:.6rem;
}
.field .req{color:var(--accent)}
.field input,.field select,.field textarea{
  width:100%;background:transparent;border:0;border-bottom:1px solid var(--line-2);
  padding:12px 0;color:var(--fg);font-family:inherit;font-size:1rem;
  border-radius:0;transition:border-color .4s ease;
}
.field textarea{resize:vertical;min-height:120px}
.field select{cursor:pointer}
.field select option{background:var(--panel);color:var(--fg)}
.field input:focus,.field select:focus,.field textarea:focus{
  outline:none;border-bottom-color:var(--accent);
}
.field input::placeholder,.field textarea::placeholder{color:rgba(154,129,137,.6)}
.field .err{
  display:block;color:var(--accent);font-size:.75rem;margin-top:.5rem;
  opacity:0;transform:translateY(-4px);transition:opacity .3s ease,transform .3s ease;
}
.field.invalid .err{opacity:1;transform:none}
.field.invalid input,.field.invalid select,.field.invalid textarea{border-bottom-color:var(--accent)}
.form-note{color:var(--muted);font-size:.8rem;margin-top:1.2rem;line-height:1.6}
.form-status{
  margin-top:1.4rem;padding:1rem 1.2rem;border-radius:8px;font-size:.92rem;
  border:1px solid var(--line);display:none;
}
.form-status.show{display:block}
.form-status.ok{border-color:var(--accent-2);color:var(--accent-2)}
.form-status.bad{border-color:var(--accent);color:var(--accent)}
.info h3{
  font-size:.69rem;letter-spacing:.24em;text-transform:uppercase;color:var(--muted);
  margin:2rem 0 .7rem;font-weight:500;
}
.info h3:first-child{margin-top:0}
.info a,.info p{font-size:.98rem;line-height:1.7}
.info a{transition:color .3s ease}
.info a:hover{color:var(--accent)}
.avail{display:flex;align-items:center;gap:.6em;color:var(--fg)}
.pulse-dot{
  width:8px;height:8px;border-radius:50%;background:var(--accent-2);flex:none;
  animation:dotP 1.9s ease-in-out infinite;
}
@keyframes dotP{
  0%,100%{box-shadow:0 0 0 0 rgba(255,194,75,.6)}
  50%{box-shadow:0 0 0 8px rgba(255,194,75,0)}
}

/* ============================== 19. STATEMENT (light band) ============================== */
.statement{padding:22vh 4vw}
.st-line{
  display:block;font-family:var(--font-d);font-weight:800;text-transform:uppercase;
  font-size:clamp(2rem,7.6vw,7rem);line-height:.98;letter-spacing:-.01em;
}
.st-outline{color:transparent;-webkit-text-stroke:2px var(--fg)}
.st-outline em{font-style:normal;color:var(--accent);-webkit-text-stroke:0}
.st-sub{margin-top:2.8rem;color:var(--muted);max-width:46ch;line-height:1.7}

/* ============================== 20. FOOTER ============================== */
.footer{padding:18vh 4vw 4vh}
/* Set by partials/footer.blade.php when its CTA is suppressed. The 18vh above
   exists to give that giant CTA air; with no CTA it is just a void, and above a
   `.c-close` (10vh bottom padding of its own) it read as a layout bug. */
.footer-flush{padding-top:9vh}
.f-cta{
  display:inline-block;margin:2.2rem 0 9vh;
  font-family:var(--font-d);font-weight:800;text-transform:uppercase;
  font-size:clamp(2.4rem,11vw,10rem);line-height:.95;letter-spacing:-.015em;
  transition:color .5s ease;
}
.f-cta:hover{color:var(--accent)}
.f-cta .arr{display:inline-block;color:var(--accent);transition:transform .6s var(--ease)}
.f-cta:hover .arr{transform:translate(.08em,-.08em)}
.f-grid{
  display:grid;grid-template-columns:1.3fr 1fr 1fr 1fr;gap:2.4rem;
  border-top:1px solid var(--line);padding-top:2.8rem;
}
.f-grid h3{
  font-size:.68rem;letter-spacing:.26em;text-transform:uppercase;color:var(--muted);
  margin-bottom:1.1rem;font-weight:500;
}
.f-grid a{display:block;margin-bottom:.5rem;font-size:.94rem;transition:color .3s ease}
.f-grid a:hover{color:var(--accent)}
.f-grid p{font-size:.94rem;line-height:1.7;color:var(--muted)}
/* .avail sets color:var(--fg) but is only (0,1,0), so `.f-grid p` at (0,1,1)
   wins and the "Admissions open" status renders muted like body copy. */
.f-grid .avail{color:var(--fg)}
.f-brand .lockup{margin-bottom:1.1rem}
.f-bottom{
  display:flex;justify-content:space-between;gap:1rem;flex-wrap:wrap;margin-top:9vh;
  color:var(--muted);font-size:.7rem;letter-spacing:.16em;text-transform:uppercase;
}
.f-bottom a:hover{color:var(--accent)}
@media(max-width:900px){.f-grid{grid-template-columns:1fr 1fr}}
@media(max-width:560px){.f-grid{grid-template-columns:1fr}}

/* mini footer for interior pages */
.f-mini{
  display:flex;justify-content:space-between;gap:1rem;flex-wrap:wrap;
  padding:3.4vh 4vw;border-top:1px solid var(--line);
  color:var(--muted);font-size:.7rem;letter-spacing:.16em;text-transform:uppercase;
}
.f-mini a:hover{color:var(--accent)}

/* ============================== 21. PAGE HERO (interior pages) ============================== */
.p-hero{padding:calc(var(--nav-h) + 12vh) 4vw 8vh;position:relative;overflow:hidden}
.p-hero::before{
  content:'';position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(56% 52% at 80% 6%,color-mix(in srgb,var(--ca) 14%,transparent),transparent 64%);
}
@supports not (background:color-mix(in srgb,red,blue)){.p-hero::before{background:radial-gradient(56% 52% at 80% 6%,rgba(255,61,87,.14),transparent 64%)}}
.p-hero h1{
  font-family:var(--font-d);font-weight:800;text-transform:uppercase;
  font-size:clamp(1.7rem,8.6vw,7rem);line-height:.92;letter-spacing:-.015em;margin-top:1.5rem;
}
.p-hero .lead{margin-top:1.8rem}

/* ============================== 22. GLITCH (hover FX) ============================== */
/* Paired with the canvas spark burst in site.js. Cheap: two clipped copies
   of the same text nudged along x with the brand hues. */
.glitch{position:relative;display:inline-block}
.glitch::before,.glitch::after{
  content:attr(data-t);position:absolute;left:0;top:0;width:100%;
  clip-path:inset(0 0 0 0);pointer-events:none;opacity:0;
}
.glitch::before{color:var(--accent);transform:translateX(-2px)}
.glitch::after{color:var(--accent-2);transform:translateX(2px)}
.glitch.on::before{animation:gA .42s steps(2,end) 1}
.glitch.on::after{animation:gB .42s steps(2,end) 1}
@keyframes gA{
  0%,100%{opacity:0}
  20%{opacity:.85;clip-path:inset(12% 0 62% 0)}
  45%{opacity:.7;clip-path:inset(66% 0 14% 0)}
  70%{opacity:.8;clip-path:inset(38% 0 44% 0)}
}
@keyframes gB{
  0%,100%{opacity:0}
  28%{opacity:.8;clip-path:inset(58% 0 22% 0)}
  55%{opacity:.65;clip-path:inset(20% 0 58% 0)}
  80%{opacity:.75;clip-path:inset(44% 0 34% 0)}
}

/* ============================== 22b. TOUCH ==============================
   Everything on a card that only appeared on :hover is invisible on a phone —
   not degraded, absent. The accent border and the spotlight were the whole
   signal that a card is a link. Here they get a resting state instead, so a
   touch device gets the same design rather than a plainer one. */
@media(hover:none){
  .tilt{border-color:color-mix(in srgb,var(--ca) 30%,var(--line))}
  @supports not (background:color-mix(in srgb,red,blue)){
    .tilt{border-color:var(--line-2)}
  }
  /* the spotlight has no pointer to follow, so it becomes a fixed corner wash */
  .tilt::before{opacity:.55;--mx:88%;--my:6%}
  .tilt .t-go svg{transform:translate(2px,-2px)}
  /* :hover can never fire, so the padding shift it shares with the resting
     state must not leave the step looking half-animated */
  .rm-step:hover{padding-left:1.5rem}
}
/* A 38px control is under the 44px minimum every touch guideline agrees on. */
@media(pointer:coarse){
  .c3d-nav{width:44px;height:44px}
  .btn{padding:17px 30px}
  .faq summary{padding-top:1.5rem;padding-bottom:1.5rem}
}

/* ============================== 23. REDUCED MOTION ============================== */
/* Everything above degrades to a static, fully legible page. */
@media(prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:.01ms!important;animation-iteration-count:1!important;
    transition-duration:.01ms!important;scroll-behavior:auto!important;
  }
  [data-reveal],.split .ch > i,.hero-fade{opacity:1!important;transform:none!important}
  .grain{display:none}
  .rm-rail path{stroke-dashoffset:0!important}
  .worlds{height:auto}
  .worlds-stage{position:static;height:auto;transform:none!important}
  .world{position:static;clip-path:none!important;opacity:1!important;transform:none!important;min-height:80vh}
  .w-progress{display:none}
  .tm-track{transform:none!important;flex-wrap:wrap;width:auto}
}
