/* ═══════════════════════════════════════════════════════════════════════════
   DIRT WORK BID SCHOOL — shared design system
   Every page on this site loads this file and adds nothing but page-specific
   layout. If a rule is needed twice, it belongs here.

   The palette and display face are inherited from the original sales page and
   deliberately kept: warm paper, steel green, amber, and a condensed industrial
   display face read as trade, not as SaaS. What changed is execution — depth,
   media, scale and rhythm.
   ═══════════════════════════════════════════════════════════════════════════ */

@import url('https://fonts.googleapis.com/css2?family=Big+Shoulders+Display:wght@500;600;700;800;900&family=Public+Sans:ital,wght@0,400;0,500;0,600;0,700;1,400&family=IBM+Plex+Mono:wght@400;500;600&display=swap');

:root{
  /* Ink — warm near-black. Never pure #000: it reads cheap against warm paper. */
  --ink:        #0E0D0B;
  --ink-2:      #16151220;
  --ink-raised: #1A1815;
  --ink-line:   rgba(242,235,220,.10);
  --ink-line-2: rgba(242,235,220,.18);

  /* Paper */
  --paper:      #F2EBDC;
  --paper-2:    #FFFDF8;
  --paper-3:    #E7DEC9;
  --line:       #DED2B4;

  /* Text on paper */
  --text:       #181510;
  --text-2:     #56503E;
  --text-3:     #6E674F;

  /* Text on ink */
  --on-ink:     #F4EEE1;
  --on-ink-2:   rgba(244,238,225,.72);
  --on-ink-3:   rgba(244,238,225,.58);

  /* Accent — amber is the ONLY call-to-action colour on this site. */
  --amber:      #E07B10;
  --amber-lift: #F59022;
  --amber-deep: #A85808;
  --amber-ink:  #2E1703;

  /* Secondary — steel green, for labels and quiet affirmation. Never a CTA. */
  --steel:      #3D6759;
  --steel-light:#9CBBB0;

  --display:'Big Shoulders Display',ui-sans-serif,system-ui,sans-serif;
  --body:'Public Sans',ui-sans-serif,system-ui,-apple-system,sans-serif;
  --mono:'IBM Plex Mono',ui-monospace,SFMono-Regular,monospace;

  --maxw:1200px;
  --gutter:24px;
  --ease:cubic-bezier(.16,1,.3,1);
}

*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth}
@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}}

body{
  background:var(--paper);
  color:var(--text);
  font-family:var(--body);
  font-size:17px;
  line-height:1.6;
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
  overflow-x:hidden;
}
img,video{max-width:100%;display:block}
a{color:inherit;text-decoration:none}
::selection{background:var(--amber);color:var(--amber-ink)}

.wrap{max-width:var(--maxw);margin:0 auto;padding-inline:var(--gutter)}
.wrap-narrow{max-width:760px;margin:0 auto;padding-inline:var(--gutter)}

/* ── Type scale ────────────────────────────────────────────────────────────
   Display is condensed, so it can run far larger than a normal face without
   shouting. Tracking tightens as size grows.                                */
.display-xl{font-family:var(--display);font-weight:800;font-size:clamp(2.5rem,6.2vw,5.4rem);line-height:.94;letter-spacing:-.018em}
.display-l {font-family:var(--display);font-weight:800;font-size:clamp(2.4rem,5.5vw,4.6rem);line-height:.96;letter-spacing:-.015em}
.display-m {font-family:var(--display);font-weight:700;font-size:clamp(1.9rem,3.6vw,3rem);line-height:1.02;letter-spacing:-.01em}
.display-s {font-family:var(--display);font-weight:700;font-size:clamp(1.4rem,2.2vw,1.9rem);line-height:1.1}

.eyebrow{
  font-family:var(--mono);font-size:.72rem;font-weight:500;
  letter-spacing:.18em;text-transform:uppercase;color:var(--steel);
}
.on-ink .eyebrow{color:var(--steel-light)}

.lede{font-size:clamp(1.1rem,1.6vw,1.35rem);line-height:1.55;color:var(--text-2)}
.on-ink .lede{color:var(--on-ink-2)}
.prose p+p{margin-top:1.1em}
.prose strong{font-weight:700;color:var(--text)}

/* Tabular figures everywhere a number is compared to another number. */
.num{font-variant-numeric:tabular-nums;font-feature-settings:"tnum" 1}

/* ── Surfaces ──────────────────────────────────────────────────────────── */
.on-ink{background:var(--ink);color:var(--on-ink)}
.on-paper{background:var(--paper);color:var(--text)}
.on-paper-2{background:var(--paper-2);color:var(--text)}
section{position:relative}
.sec{padding-block:clamp(72px,10vw,140px)}
.sec-tight{padding-block:clamp(48px,6vw,84px)}

/* Hairline that reads as a drawn rule, not a border. */
.rule{height:1px;background:var(--line);border:0}
.on-ink .rule{background:var(--ink-line)}

/* ── Checker motif — the brand mark, used sparingly as a divider ───────── */
.checker{height:10px;width:100%;
  background-image:linear-gradient(90deg,var(--ink) 25%,transparent 25% 50%,var(--ink) 50% 75%,transparent 75%);
  background-size:20px 10px;opacity:.9}
.on-ink .checker{background-image:linear-gradient(90deg,var(--paper) 25%,transparent 25% 50%,var(--paper) 50% 75%,transparent 75%)}

/* ── Buttons ───────────────────────────────────────────────────────────── */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:.6em;
  font-family:var(--display);font-weight:700;font-size:1.15rem;letter-spacing:.01em;
  padding:1.05em 2em;border:0;border-radius:2px;cursor:pointer;
  transition:transform .18s var(--ease),box-shadow .18s var(--ease),background .18s var(--ease);
  text-align:center;
}
.btn-primary{background:var(--amber);color:var(--amber-ink);box-shadow:0 1px 0 var(--amber-deep),0 12px 30px -10px rgba(224,123,16,.55)}
.btn-primary:hover{background:var(--amber-lift);transform:translateY(-2px);box-shadow:0 1px 0 var(--amber-deep),0 18px 40px -12px rgba(224,123,16,.7)}
.btn-primary:active{transform:translateY(0)}
.btn-ghost{background:transparent;color:var(--on-ink);box-shadow:inset 0 0 0 1.5px var(--ink-line-2)}
.btn-ghost:hover{box-shadow:inset 0 0 0 1.5px var(--on-ink-3);transform:translateY(-2px)}
.on-paper .btn-ghost{color:var(--text);box-shadow:inset 0 0 0 1.5px var(--line)}
.btn:focus-visible{outline:3px solid var(--steel-light);outline-offset:3px}
/* On a phone, two stacked CTAs at their natural widths read as a mistake.
   Scoped to stages so inline links in body copy are untouched. */
@media (max-width:560px){.stage-inner .btn{width:100%}}

/* ── Navigation ────────────────────────────────────────────────────────── */
.nav{
  position:fixed;inset:0 0 auto 0;z-index:60;
  display:flex;align-items:center;gap:28px;
  padding:16px var(--gutter);
  transition:background .3s var(--ease),backdrop-filter .3s var(--ease),box-shadow .3s var(--ease);
}
/* THE BLUR LIVES ON A SEPARATE LAYER, NOT ON .nav ITSELF.
   backdrop-filter makes an element the containing block for its
   position:fixed descendants. With it on .nav, the mobile panel's inset:0
   resolved against the 77px nav bar instead of the viewport, so
   translateY(-100%) cleared only 77px and the closed menu - including the
   amber CTA - sat on top of the page at all times once scrolled.
   Measured at 393px scrolled to 1400: panelTop -77, panelHeight 77,
   ctaTop 74, ctaVisible true. Moving the filter to ::before removes the
   containing block and the panel resolves against the viewport again. */
.nav::before{content:'';position:absolute;inset:0;z-index:-1;opacity:0;
  transition:opacity .3s var(--ease);
  background:rgba(14,13,11,.92);backdrop-filter:blur(14px) saturate(1.4);
  box-shadow:0 1px 0 var(--ink-line)}
.nav.solid::before{opacity:1}
.nav-brand{display:flex;align-items:center;gap:11px;font-family:var(--display);font-weight:800;
  font-size:1rem;letter-spacing:.04em;text-transform:uppercase;color:var(--on-ink);white-space:nowrap}
.nav-flag{display:grid;grid-template-columns:repeat(3,6px);grid-auto-rows:6px;gap:2px}
.nav-flag i{background:var(--paper);display:block}
.nav-flag i:nth-child(2),.nav-flag i:nth-child(4),.nav-flag i:nth-child(6){background:transparent}
.nav-brand em{font-style:normal;color:var(--amber)}
.nav-links{display:flex;gap:26px;margin-left:auto;align-items:center}
.nav-links a{font-family:var(--mono);font-size:.78rem;letter-spacing:.1em;text-transform:uppercase;
  color:var(--on-ink-2);transition:color .18s var(--ease);padding-block:6px}
.nav-links a:hover,.nav-links a[aria-current="page"]{color:var(--on-ink)}
.nav-links a[aria-current="page"]{box-shadow:inset 0 -2px 0 var(--amber)}
.nav-cta{font-family:var(--display)!important;font-size:.95rem!important;letter-spacing:0!important;
  text-transform:none!important;background:var(--amber);color:var(--amber-ink)!important;
  padding:.62em 1.2em!important;border-radius:2px}
.nav-cta:hover{background:var(--amber-lift)}
.nav-toggle{display:none;margin-left:auto;background:none;border:0;cursor:pointer;padding:8px;color:var(--on-ink)}

@media (max-width:860px){
  .nav-links{position:fixed;inset:0;height:100svh;flex-direction:column;justify-content:center;gap:28px;
    background:var(--ink);
    transform:translateY(-100%);transition:transform .38s var(--ease);margin:0}
  .nav.open .nav-links{transform:translateY(0)}
  .nav-links a{font-size:1.05rem}
  .nav-toggle{display:block;z-index:2}
  .nav.open .nav-toggle{position:fixed;right:var(--gutter);top:16px}
}

/* ── Video stage ───────────────────────────────────────────────────────────
   The poster carries the first paint; the video fades in only once it can
   actually play, so a slow connection sees a sharp still rather than black.  */
.stage{position:relative;overflow:hidden;background:var(--ink)}
.stage-media{position:absolute;inset:0}
/* Both layers must be absolutely positioned and stacked ON each other. As
   normal-flow blocks they sat one ABOVE the other, so .stage-media ran to
   200% height and the video was clipped out of view entirely - the poster was
   the only thing ever rendered. */
.stage-media img,.stage-media video{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.stage-media video{opacity:0;transition:opacity .9s var(--ease)}
.stage-media video.ready{opacity:1}
/* The scrim has one job: make text legible while STILL SHOWING THE FOOTAGE.
   The first draft stacked two near-opaque gradients and hid the video almost
   entirely, which defeated the point of shooting a background at all. These
   values are the lightest that still clear 4.5:1 on the hero text. */
.stage-scrim{position:absolute;inset:0;
  background:
    linear-gradient(180deg,rgba(14,13,11,.58) 0%,rgba(14,13,11,.26) 30%,rgba(14,13,11,.60) 72%,rgba(14,13,11,.97) 100%),
    linear-gradient(100deg,rgba(14,13,11,.76) 0%,rgba(14,13,11,.32) 54%,rgba(14,13,11,0) 84%);
}
/* Over moving footage, type needs its own floor of contrast. */
.stage-inner h1,.stage-inner h2,.stage-inner .lede{text-shadow:0 2px 24px rgba(14,13,11,.55)}
.stage-grain{position:absolute;inset:0;opacity:.055;pointer-events:none;mix-blend-mode:overlay;
  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='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E")}
.stage-inner{position:relative;z-index:2}
/* .on-ink is a SURFACE class - it paints a solid background. Inside a video
   stage that background covers the footage for exactly the content width,
   which looked like a broken gradient (dark centre, video only in the
   margins). Inside a stage we want its colours, never its fill. */
.stage-inner.on-ink,.stage .stage-inner.on-ink{background:transparent}

@media (prefers-reduced-motion:reduce){
  .stage-media video{display:none}
}

/* ── Spec row — the data strip. Fixes the original page's dark-on-dark bug
   by giving every figure a real surface and a label that meets contrast.   */
.spec{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));
  border-top:1px solid var(--ink-line);border-left:1px solid var(--ink-line)}
.spec div{padding:22px 20px;border-right:1px solid var(--ink-line);border-bottom:1px solid var(--ink-line);
  background:rgba(244,238,225,.035)}
.spec dt{font-family:var(--mono);font-size:.68rem;letter-spacing:.16em;text-transform:uppercase;
  color:var(--on-ink-3);margin-bottom:8px}
.spec dd{font-family:var(--display);font-weight:800;font-size:clamp(1.9rem,3.2vw,2.6rem);
  line-height:1;color:var(--on-ink);font-variant-numeric:tabular-nums}
.spec dd small{display:block;font-family:var(--body);font-weight:400;font-size:.8rem;
  letter-spacing:0;color:var(--on-ink-2);margin-top:8px;line-height:1.4}

/* ── Cards ─────────────────────────────────────────────────────────────── */
.card{background:var(--paper-2);border:1px solid var(--line);border-radius:3px;padding:26px;
  transition:transform .22s var(--ease),box-shadow .22s var(--ease)}
.card-link:hover{transform:translateY(-3px);box-shadow:0 18px 40px -22px rgba(24,21,16,.45)}
.on-ink .card{background:var(--ink-raised);border-color:var(--ink-line)}

.grid{display:grid;gap:22px}
.grid-2{grid-template-columns:repeat(auto-fit,minmax(300px,1fr))}
.grid-3{grid-template-columns:repeat(auto-fit,minmax(260px,1fr))}

/* ── Module / lesson list ──────────────────────────────────────────────── */
.mod{display:grid;grid-template-columns:auto 1fr;gap:20px;padding-block:26px;border-top:1px solid var(--line)}
.on-ink .mod{border-color:var(--ink-line)}
.mod-n{font-family:var(--display);font-weight:800;font-size:2.4rem;line-height:.9;color:var(--amber);
  font-variant-numeric:tabular-nums}
.mod h3{font-family:var(--display);font-weight:700;font-size:1.5rem;line-height:1.12;margin-bottom:6px}

/* A module we cannot honestly write yet is marked, not hidden. */
.tag{display:inline-block;font-family:var(--mono);font-size:.64rem;letter-spacing:.14em;
  text-transform:uppercase;padding:4px 9px;border-radius:2px;vertical-align:middle}
.tag-ready{background:rgba(74,122,108,.16);color:var(--steel)}
.on-ink .tag-ready{background:rgba(156,187,176,.16);color:var(--steel-light)}
.tag-filming{background:rgba(224,123,16,.15);color:var(--amber-deep)}
.on-ink .tag-filming{background:rgba(224,123,16,.2);color:var(--amber-lift)}

/* ── Pull quote — his own words, set as evidence ───────────────────────── */
.quote{border-left:3px solid var(--amber);padding:6px 0 6px 24px;margin-block:28px}
.quote p{font-family:var(--display);font-weight:600;font-size:clamp(1.3rem,2.4vw,1.9rem);
  line-height:1.2;letter-spacing:-.005em}
.quote cite{display:block;margin-top:12px;font-family:var(--mono);font-size:.72rem;font-style:normal;
  letter-spacing:.12em;text-transform:uppercase;color:var(--text-3)}
.on-ink .quote cite{color:var(--on-ink-3)}

/* ── Footer ────────────────────────────────────────────────────────────── */
.foot{background:var(--ink);color:var(--on-ink-2);padding-block:56px 40px;font-size:.9rem}
.foot a:hover{color:var(--on-ink)}
.foot-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:32px;margin-bottom:36px}
.foot h4{font-family:var(--mono);font-size:.68rem;letter-spacing:.16em;text-transform:uppercase;
  color:var(--on-ink-3);margin-bottom:14px}
.foot li{list-style:none;margin-bottom:9px}
.foot-fine{font-family:var(--mono);font-size:.7rem;letter-spacing:.04em;color:var(--on-ink-3);line-height:1.7}

/* ── Scroll reveal ─────────────────────────────────────────────────────── */
/* PROGRESSIVE ENHANCEMENT, NOT A GAMBLE.
   The first version set .reveal{opacity:0} unconditionally and relied on an
   IntersectionObserver to bring it back. That means: no JS, slow JS, a failed
   observer, a crawler, or a reader-mode pass = a completely blank page. A
   full-page screenshot caught it with every section empty.
   Content is visible by DEFAULT. It is only hidden once the inline head
   script has proved JS is running, and the observer is there to reveal it. */
.reveal{opacity:1;transform:none;transition:opacity .7s var(--ease),transform .7s var(--ease)}
.js .reveal{opacity:0;transform:translateY(22px)}
.js .reveal.in{opacity:1;transform:none}
@media (prefers-reduced-motion:reduce){
  .reveal,.js .reveal{opacity:1;transform:none;transition:none}
}

/* Skip link — keyboard users land here first. */
.skip{position:absolute;left:-9999px;top:0;z-index:100;background:var(--amber);color:var(--amber-ink);
  padding:12px 18px;font-family:var(--display);font-weight:700}
.skip:focus{left:8px;top:8px}
