/* =====================================================================
   CS Masterclass — design system
   Loaded by every prototype page. Phase 3 will port these to Tailwind v4
   tokens + a few raw CSS layers in the Next.js + Payload build.
   ===================================================================== */

html { scroll-behavior: smooth; background: #0F0F12; }
body {
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
  color: #F5F0E8;
  background: #0F0F12;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}
.font-display { font-family: 'Fraunces', serif; font-optical-sizing: auto; }
.font-mono { font-family: 'JetBrains Mono', monospace; }

/* Massive display treatments */
.display-colossal {
  font-family: 'Fraunces', serif;
  font-weight: 300;
  letter-spacing: -0.045em;
  line-height: 0.88;
  font-variation-settings: "SOFT" 100, "WONK" 0, "opsz" 144;
}
.display-huge {
  font-family: 'Fraunces', serif;
  font-weight: 400;
  letter-spacing: -0.04em;
  line-height: 0.94;
  font-variation-settings: "SOFT" 80, "opsz" 144;
}
.display-italic {
  font-family: 'Fraunces', serif;
  font-style: italic;
  font-weight: 300;
  letter-spacing: -0.03em;
  /* WONK 1 turns on Fraunces' eccentric italic glyphs (whimsical g, k, y).
     Visible mainly on words like "meets", "10-year pro", "transformations"
     that are wrapped in <em>. Adds personality without blowing up layout. */
  font-variation-settings: "SOFT" 100, "WONK" 1, "opsz" 144;
}
em, .display-colossal em, .display-huge em {
  font-variation-settings: "SOFT" 100, "WONK" 1, "opsz" 144;
}

/* Subtle gold ambient — a soft radial wash that lifts a section's corner
   without painting it. Use it on key chapter sections (instructor,
   final CTA) so the page stops feeling uniformly flat. */
.gold-ambient { position: relative; }
.gold-ambient::before {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(
    ellipse 1200px 600px at 90% -10%,
    rgba(212, 181, 123, 0.06) 0%,
    rgba(212, 181, 123, 0.02) 35%,
    transparent 70%
  );
  pointer-events: none;
  z-index: 0;
}
.gold-ambient > * { position: relative; z-index: 1; }
.gold-ambient-bl::before {
  background: radial-gradient(
    ellipse 1100px 500px at 5% 110%,
    rgba(212, 181, 123, 0.07) 0%,
    rgba(212, 181, 123, 0.02) 40%,
    transparent 75%
  );
}

/* Editorial chapter rule — a thin gold gradient that fades from invisible
   on the edges to gold in the centre. Replaces flat border-t for emphasis
   between major sections. */
.section-rule {
  height: 1px;
  background: linear-gradient(
    to right,
    transparent 0%,
    rgba(212, 181, 123, 0.45) 50%,
    transparent 100%
  );
  border: none;
  margin: 0;
}

.mono-caption {
  font-family: 'JetBrains Mono', monospace;
  font-size: 12px;
  letter-spacing: 0.18em;
  line-height: 1.5;
  text-transform: uppercase;
  font-weight: 500;
}

/* Editorial eyebrow: dash + label. Wraps the pattern repeated across pages. */
.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 1rem;
}
.eyebrow-line {
  display: inline-block;
  width: 2.5rem;
  height: 1px;
  background: #D4B57B;
}
.eyebrow-label {
  font-family: 'JetBrains Mono', monospace;
  font-size: 12px;
  letter-spacing: 0.18em;
  line-height: 1.5;
  text-transform: uppercase;
  font-weight: 500;
  color: #D4B57B;
}

/* Section vertical rhythm — replaces py-20/24/28/32 drift across pages. */
.section-pad { padding-top: 6rem; padding-bottom: 6rem; }
@media (min-width: 1024px) {
  .section-pad { padding-top: 8rem; padding-bottom: 8rem; }
}

/* Body copy hierarchy. Use these instead of inline text-[14/15/16/17px]. */
.body-prose { font-size: 16px; line-height: 1.7; color: #C9C3B8; }
@media (min-width: 1024px) {
  .body-prose { font-size: 17px; }
}
.body-small { font-size: 14px; line-height: 1.6; color: #C9C3B8; }

/* Ambient grain — desktop only. A fixed, full-screen mix-blend layer forces
   phones to repaint the whole page on every scroll frame (scroll jank). */
@media (min-width: 1024px) and (pointer: fine) {
  .grain::before {
    content: '';
    position: fixed;
    inset: 0;
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='200' height='200'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.18 0'/></filter><rect width='200' height='200' filter='url(%23n)'/></svg>");
    background-size: 200px 200px;
    opacity: 0.6;
    mix-blend-mode: overlay;
    pointer-events: none;
    z-index: 9999;
  }
}

/* ============================= */
/* Scroll reveals                 */
/* Driven by src/components/RevealObserver.tsx. Content is visible by
   default; only elements below the fold get `.is-revealing` (hidden) until
   they scroll in and get `.in`. Nothing here uses clip-path on the observed
   element — Chrome's IntersectionObserver treats a fully clipped element as
   invisible forever, which is what left images blank before. */
/* ============================= */
.reveal.is-revealing {
  opacity: 0;
  transform: translateY(40px);
  transition:
    opacity 1200ms cubic-bezier(0.19, 1, 0.22, 1),
    transform 1200ms cubic-bezier(0.19, 1, 0.22, 1);
}
.reveal.is-revealing.in { opacity: 1; transform: none; }

.r-d1 { transition-delay: 120ms; }
.r-d2 { transition-delay: 240ms; }
.r-d3 { transition-delay: 360ms; }
.r-d4 { transition-delay: 480ms; }
.r-d5 { transition-delay: 600ms; }

/* Headline lines. Padding gives room for ascenders and italic descenders so
   `overflow: hidden` never clips a glyph; negative margins pull the lines
   back to tight display spacing. */
.line {
  display: block;
  overflow: hidden;
  line-height: 1;
  padding-top: 0.18em;
  padding-bottom: 0.24em;
  margin-top: -0.22em;
  margin-bottom: -0.26em;
}
.line:first-child { margin-top: 0; }
.line:last-child { margin-bottom: 0; }
.display-colossal .line { margin-top: -0.30em; margin-bottom: -0.34em; }
.display-colossal .line:first-child { margin-top: 0; }
.display-colossal .line:last-child { margin-bottom: 0; }
.line > .line-inner { display: block; }

.line-group.is-revealing > .line > .line-inner {
  transform: translateY(115%);
  transition: transform 1400ms cubic-bezier(0.19, 1, 0.22, 1);
}
.line-group.is-revealing.in > .line > .line-inner,
.reveal.is-revealing.in .line-group.is-revealing > .line > .line-inner { transform: none; }
.line-group > .line:nth-child(2) > .line-inner { transition-delay: 120ms; }
.line-group > .line:nth-child(3) > .line-inner { transition-delay: 240ms; }
.line-group > .line:nth-child(4) > .line-inner { transition-delay: 360ms; }
.line-group > .line:nth-child(5) > .line-inner { transition-delay: 480ms; }

/* Image curtain — an overlay lifts off the photo. The observed element itself
   is never clipped, so the observer always sees it. */
.media { position: relative; overflow: hidden; }
.media.is-revealing::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 25;
  background: #22222A;
  transform-origin: top center;
  transform: scaleY(1);
  transition: transform 1300ms cubic-bezier(0.77, 0, 0.175, 1);
  pointer-events: none;
}
.media.is-revealing.in::after { transform: scaleY(0); }
.media.is-revealing > img,
.media.is-revealing > video {
  transform: scale(1.12);
  transition: transform 2000ms cubic-bezier(0.19, 1, 0.22, 1);
}
.media.is-revealing.in > img,
.media.is-revealing.in > video { transform: none; }

@media (prefers-reduced-motion: reduce) {
  .reveal.is-revealing, .line-group.is-revealing > .line > .line-inner,
  .media.is-revealing > img, .media.is-revealing > video { opacity: 1; transform: none; transition: none; }
  .media.is-revealing::after { display: none; }
  .kb, .hero-slide.is-active > img, .hero-slide.is-active > video, .marq-track { animation: none !important; }
}

/* Nav */
.nav-blur {
  backdrop-filter: saturate(140%) blur(20px);
  -webkit-backdrop-filter: saturate(140%) blur(20px);
  background-color: rgba(15,15,18,0.5);
  border-bottom: 1px solid rgba(255,255,255,0.06);
}

/* Buttons — base typography lives here so callsites don't need text-[Npx] tracking uppercase. */
.btn-gold,
.btn-ghost,
.btn-bone {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  transition: all 280ms cubic-bezier(0.16,1,0.3,1);
  position: relative;
  overflow: hidden;
}
.btn-gold {
  background: #D4B57B;
  color: #0F0F12;
}
.btn-gold:hover { background: #E8D9BC; transform: translateY(-1px); box-shadow: 0 12px 40px -8px rgba(212,181,123,0.45); }
.btn-ghost {
  border: 1px solid rgba(245,240,232,0.2);
  color: #F5F0E8;
  background: transparent;
}
.btn-ghost:hover { border-color: #F5F0E8; background: rgba(245,240,232,0.04); }
.btn-bone {
  background: #F5F0E8;
  color: #0F0F12;
}
.btn-bone:hover { background: #FFFFFF; transform: translateY(-1px); }

/* Ken Burns */
@keyframes kb { 0% { transform: scale(1.06); } 100% { transform: scale(1.18); } }
.kb { animation: kb 24s ease-in-out infinite alternate; }

/* Marquee */
@keyframes marq { from { transform: translateX(0); } to { transform: translateX(-50%); } }
.marq-track { animation: marq 26s linear infinite; }

/* Logo class kept (referenced from Nav/Footer/admin Logo+Icon) but the
   filter is no longer needed — both the static fallback at
   /assets/cs-monogram.png AND the Payload Media upload are white-on-
   transparent. Inverting it would turn the mark black. If a black
   variant ever needs lifting, re-add filter: invert(1) but only on the
   fallback path. */
.logo-white { filter: none; }

/* Cards */
.card { transition: all 500ms cubic-bezier(0.16,1,0.3,1); }
.card:hover { transform: translateY(-6px); }
.card-img { transition: transform 1200ms cubic-bezier(0.16,1,0.3,1), filter 900ms; }
.card:hover .card-img { transform: scale(1.06); }

/* Glow */
.glow {
  box-shadow:
    0 0 0 1px rgba(212,181,123,0.3),
    0 20px 60px -20px rgba(212,181,123,0.25),
    inset 0 1px 0 rgba(212,181,123,0.15);
  background:
    radial-gradient(1200px circle at 30% 0%, rgba(212,181,123,0.08) 0%, transparent 50%),
    #16161A;
}

/* Hairline */
.hl { border-color: rgba(245,240,232,0.08); }

/* Hero gradient */
.hero-grad { background: linear-gradient(180deg, rgba(15,15,18,0.35) 0%, rgba(15,15,18,0.55) 55%, rgba(15,15,18,0.95) 100%); }

/* Big number outline */
.bignum {
  font-family: 'Fraunces', serif;
  font-weight: 200;
  letter-spacing: -0.08em;
  font-variation-settings: "SOFT" 100, "opsz" 144;
  line-height: 0.85;
  color: transparent;
  -webkit-text-stroke: 1px rgba(212,181,123,0.4);
}

/* Accordion */
details > summary { list-style: none; cursor: pointer; }
details > summary::-webkit-details-marker { display: none; }
details[open] .plus { transform: rotate(45deg); }
.plus { transition: transform 400ms cubic-bezier(0.16,1,0.3,1); }

/* Press logos */
.press-item { color: #8A847A; transition: color 300ms; }
.press-item:hover { color: #F5F0E8; }

/* Gold hairline */
.gold-rule { height: 1px; background: linear-gradient(90deg, transparent 0%, #D4B57B 50%, transparent 100%); }

/* Sticky CTA */
.sticky-cta { box-shadow: 0 -10px 40px rgba(0,0,0,0.6); background: rgba(10,10,10,0.9); backdrop-filter: blur(20px); }

/* Scroll indicator */
.scroll-line {
  width: 1px;
  height: 48px;
  background: linear-gradient(180deg, transparent 0%, #F5F0E8 50%, transparent 100%);
  animation: scrollPulse 2.6s ease-in-out infinite;
}
@keyframes scrollPulse {
  0%,100% { transform: translateY(0); opacity: 0.4; }
  50% { transform: translateY(8px); opacity: 1; }
}

/* =============== Hero rotator =============== */
.hero-slide { position: absolute; inset: 0; opacity: 0; transition: opacity 1600ms cubic-bezier(0.19, 1, 0.22, 1); pointer-events: none; }
.hero-slide.is-active { opacity: 1; }
.hero-slide > img, .hero-slide > video { width: 100%; height: 100%; object-fit: cover; }
.hero-slide.is-active > img, .hero-slide.is-active > video { animation: kb 26s ease-in-out infinite alternate; }

/* Every slide's copy sits in the SAME grid cell, so the column is always as
   tall as the longest slide: nothing below it (the "At a glance" box on
   phones) can be drawn over the headline, and switching slides never moves
   the layout. Inactive slides are hidden from view and from screen readers. */
.hero-stack { display: grid; }
.hero-stack > .hero-content { grid-area: 1 / 1; }
.hero-content {
  opacity: 0;
  visibility: hidden;
  transition: opacity 700ms cubic-bezier(0.19, 1, 0.22, 1), visibility 0s linear 700ms;
}
.hero-content.is-active {
  opacity: 1;
  visibility: visible;
  transition: opacity 900ms cubic-bezier(0.19, 1, 0.22, 1) 150ms, visibility 0s linear 0s;
}
.hero-content .line > .line-inner {
  transform: translateY(115%);
  transition: transform 1300ms cubic-bezier(0.19, 1, 0.22, 1);
}
.hero-content.is-active .line > .line-inner { transform: none; }
.hero-content.is-active .line:nth-child(2) > .line-inner { transition-delay: 120ms; }
.hero-content.is-active .line:nth-child(3) > .line-inner { transition-delay: 240ms; }
.hero-content.is-active .line:nth-child(4) > .line-inner { transition-delay: 360ms; }
/* First paint: the opening slide is already in place — no animation. */
.hero-first .hero-content.is-active .line > .line-inner { transition: none; }
@media (prefers-reduced-motion: reduce) {
  .hero-content .line > .line-inner { transform: none; transition: none; }
}

.hero-dot {
  position: relative;
  width: 36px;
  height: 2px;
  background: rgba(245, 240, 232, 0.25);
  border: none;
  cursor: pointer;
  padding: 0;
  overflow: visible;
  transition: background 400ms;
}
/* Bigger tap target than the 2px line it draws. */
.hero-dot::before { content: ''; position: absolute; inset: -14px -2px; }
.hero-dot:hover { background: rgba(245, 240, 232, 0.5); }
.hero-dot::after { content: ''; position: absolute; inset: 0; background: #D4B57B; transform: scaleX(0); transform-origin: left center; }
.hero-dot.active { background: rgba(245, 240, 232, 0.15); }
.hero-dot.active::after { animation: heroDot var(--slide-duration, 7000ms) linear forwards; }
.hero-paused .hero-dot.active::after { animation-play-state: paused; }
@keyframes heroDot { from { transform: scaleX(0); } to { transform: scaleX(1); } }

.hero-counter { font-family: 'JetBrains Mono', monospace; font-size: 12px; letter-spacing: 0.18em; color: #C9C3B8; }
.hero-counter .num { display: inline-block; min-width: 1.4em; text-align: center; color: #D4B57B; }

/* =============== Page-specific helpers =============== */
/* VSL video frame */
.vsl-frame {
  position: relative;
  border-radius: 4px;
  overflow: hidden;
  box-shadow:
    0 0 0 1px rgba(212,181,123,0.18),
    0 60px 120px -30px rgba(0,0,0,0.7),
    0 0 240px -40px rgba(212,181,123,0.15);
}

/* Curriculum row stripe */
.row-curric { transition: background 380ms ease; }
.row-curric:hover { background: rgba(212,181,123,0.04); }

/* Portfolio masonry-style */
.masonry-col { break-inside: avoid; margin-bottom: 24px; }

/* Form input dark */
.input-dark {
  width: 100%;
  height: 52px;
  padding: 0 18px;
  background: #16161A;
  border: 1px solid rgba(245,240,232,0.12);
  color: #F5F0E8;
  font-size: 14px;
  font-family: 'IBM Plex Sans', sans-serif;
  transition: border-color 240ms;
}
.input-dark:focus { outline: none; border-color: #D4B57B; }
.input-dark::placeholder { color: rgba(245,240,232,0.3); }
textarea.input-dark { padding-top: 16px; height: auto; min-height: 120px; resize: vertical; }
