/* ==========================================================================
   IDENTITY GRAFIX — Design System
   Brand source: Dylan's shop signage + vinyl logo panel (see _source-assets/)
   Direction: precision fabrication / motorsport. Slant, signal, steel.
   NOT dark-glow-glass. Depth comes from material + type, not blur.
   ========================================================================== */

/* --- 1. TOKENS ---------------------------------------------------------- */
:root {
  /* Brand — sampled + calibrated from real vinyl under two lighting conditions */
  --lime:        #A3D53B;   /* primary signal */
  --lime-hot:    #B8F02A;   /* highlight only — never body text */
  --lime-deep:   #6E9422;   /* lime that passes AA on white */
  --steel:       #68736F;   /* logo fill, gray-green */
  --steel-lt:    #A4ABA7;
  --steel-dk:    #3A433E;

  --ink:         #0E1110;   /* near-black, cool */
  --ink-2:       #171B1A;
  --ink-3:       #222827;
  --hairline:    #2C3331;

  --paper:       #F4F6F4;
  --paper-2:     #E6EAE7;
  --white:       #FFFFFF;

  /* Type */
  --f-display: 'Saira Condensed', 'Arial Narrow', sans-serif;
  --f-body:    'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
  --f-mono:    'JetBrains Mono', ui-monospace, 'SF Mono', monospace;

  /* Scale — fluid, clamps tuned so nothing reflows awkwardly at 390px */
  --t-hero:  clamp(2.9rem, 8.4vw, 8.2rem);
  --t-h1:    clamp(2.6rem, 7vw, 6rem);
  --t-h2:    clamp(2rem, 4.6vw, 3.9rem);
  --t-h3:    clamp(1.3rem, 2.2vw, 1.85rem);
  --t-body:  clamp(1rem, 1.05vw, 1.115rem);
  --t-sm:    0.875rem;
  --t-xs:    0.72rem;

  /* Space */
  --gut: clamp(1.25rem, 5vw, 5.5rem);
  --sec: clamp(4.5rem, 11vh, 10rem);
  --max: 1560px;

  /* Motion */
  --slant: -8deg;              /* the logo's forward lean, used system-wide */
  --e-out: cubic-bezier(.16,1,.3,1);
  --e-in:  cubic-bezier(.7,0,.84,0);
  --dur:   .78s;
}

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

body {
  margin: 0;
  background: var(--ink);
  color: var(--paper);
  font-family: var(--f-body);
  font-size: var(--t-body);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}
img, svg, video { display: block; max-width: 100%; height: auto; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
h1,h2,h3,h4 { margin: 0; line-height: .92; font-weight: 800; }
p { margin: 0; }
/* `ol` matters here, not just `ul`: every ordered list on this site is a
   breadcrumb laid out with display:flex. Without the reset they kept their
   decimal markers, and flex jammed the markers into the adjacent item —
   "1. HOME3/ SERVICES" on all four inner pages. */
ul, ol { margin: 0; padding: 0; list-style: none; }

/* Focus — visible, on-brand, never removed */
:focus-visible {
  outline: 3px solid var(--lime);
  outline-offset: 3px;
  border-radius: 2px;
}

/* Skip link */
.skip {
  position: absolute; left: -9999px; top: 0; z-index: 999;
  background: var(--lime); color: var(--ink);
  padding: .9rem 1.4rem; font-weight: 700;
}
.skip:focus { left: 1rem; top: 1rem; }

/* --- 3. PRIMITIVES ------------------------------------------------------ */
.wrap { width: 100%; max-width: var(--max); margin-inline: auto; padding-inline: var(--gut); }
.pad  { padding-block: var(--sec); }

.display {
  font-family: var(--f-display);
  font-weight: 800;
  font-style: italic;
  letter-spacing: -.015em;
  text-transform: uppercase;
}

/* Technical label — the "spec sheet" voice */
.tag {
  font-family: var(--f-mono);
  font-size: var(--t-xs);
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--lime);
  display: inline-flex; align-items: center; gap: .6rem;
}
.tag::before {
  content: ''; width: 26px; height: 2px; background: var(--lime);
  transform: skewX(var(--slant));
}
.tag.on-light { color: var(--lime-deep); }
.tag.on-light::before { background: var(--lime-deep); }

.lede { font-size: clamp(1.05rem,1.5vw,1.3rem); line-height: 1.6; color: var(--steel-lt); max-width: 56ch; }
.on-light .lede, .light .lede { color: var(--steel-dk); }

/* --- 4. THE SLANT — brand signature ------------------------------------- */
/* Every accent leans forward like the logo. Consistency is what makes it read
   as a system rather than decoration. */
.slant { transform: skewX(var(--slant)); }
.slant > * { transform: skewX(calc(var(--slant) * -1)); }

/* --- 5. BUTTONS --------------------------------------------------------- */
.btn {
  --bg: var(--lime); --fg: var(--ink);
  position: relative; display: inline-flex; align-items: center; gap: .7rem;
  padding: 1.05rem 2.1rem;
  font-family: var(--f-mono); font-size: var(--t-xs); font-weight: 700;
  letter-spacing: .16em; text-transform: uppercase;
  color: var(--fg); background: var(--bg);
  transform: skewX(var(--slant));
  transition: transform .4s var(--e-out), background .3s, color .3s;
  overflow: hidden; isolation: isolate;
}
.btn > * { transform: skewX(calc(var(--slant) * -1)); }
.btn::after {
  content: ''; position: absolute; inset: 0; z-index: -1;
  background: var(--ink); transform: translateX(-101%);
  transition: transform .5s var(--e-out);
}
.btn:hover { color: var(--lime); }
.btn:hover::after { transform: translateX(0); }
.btn:active { transform: skewX(var(--slant)) scale(.97); }

.btn--ghost { --bg: transparent; --fg: var(--paper); box-shadow: inset 0 0 0 1.5px var(--hairline); }
.btn--ghost::after { background: var(--lime); }
.btn--ghost:hover { color: var(--ink); box-shadow: inset 0 0 0 1.5px var(--lime); }

.btn--dark { --bg: var(--ink); --fg: var(--lime); }
.btn--dark::after { background: var(--lime); }
.btn--dark:hover { color: var(--ink); }

/* --- 6. NAV ------------------------------------------------------------- */
.nav {
  position: fixed; inset: 0 0 auto 0; z-index: 200;
  display: flex; align-items: center; justify-content: space-between;
  gap: 1.5rem;
  padding: 1rem var(--gut);
  background: color-mix(in srgb, var(--ink) 82%, transparent);
  backdrop-filter: blur(14px) saturate(1.3);
  border-bottom: 1px solid transparent;
  transition: background .4s, border-color .4s, padding .4s;
}
.nav.is-stuck { background: var(--ink); border-bottom-color: var(--hairline); padding-block: .65rem; }

/* The lockup is the whole logo now — no monogram + re-typed text. Sized by
   height so the 5.82:1 artwork keeps its ratio, and deliberately larger than
   the old ~20px stand-in: this is the brand's main placement. */
.nav-logo { display: flex; align-items: center; flex-shrink: 0; }
.nav-logo img { height: 40px; width: auto; }
.nav.is-stuck .nav-logo img { height: 34px; }
.nav-links { display: flex; gap: clamp(1.2rem, 2.4vw, 2.6rem); }
.nav-links a {
  font-family: var(--f-mono); font-size: var(--t-xs); font-weight: 600;
  letter-spacing: .15em; text-transform: uppercase; color: var(--steel-lt);
  position: relative; padding-block: .4rem; transition: color .3s;
}
.nav-links a::after {
  content: ''; position: absolute; left: 0; bottom: 0; height: 2px; width: 100%;
  background: var(--lime); transform: skewX(var(--slant)) scaleX(0);
  transform-origin: left; transition: transform .45s var(--e-out);
}
.nav-links a:hover, .nav-links a[aria-current="page"] { color: var(--paper); }
.nav-links a:hover::after, .nav-links a[aria-current="page"]::after { transform: skewX(var(--slant)) scaleX(1); }

.nav-right { display: flex; align-items: center; gap: 1.1rem; flex-shrink: 0; }
.nav-tel {
  font-family: var(--f-mono); font-size: var(--t-sm); font-weight: 700;
  letter-spacing: .04em; color: var(--paper); white-space: nowrap;
}
.nav-tel:hover { color: var(--lime); }

.burger { display: none; width: 44px; height: 44px; position: relative; }
.burger span {
  position: absolute; left: 10px; width: 24px; height: 2px; background: var(--paper);
  transition: transform .4s var(--e-out), opacity .3s;
}
.burger span:nth-child(1) { top: 16px; }
.burger span:nth-child(2) { top: 22px; }
.burger span:nth-child(3) { top: 28px; }
.burger[aria-expanded="true"] span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.burger[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.burger[aria-expanded="true"] span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

.drawer {
  position: fixed; inset: 0; z-index: 190;
  background: var(--ink); padding: 6rem var(--gut) 2rem;
  display: flex; flex-direction: column; justify-content: center; gap: .4rem;
  clip-path: inset(0 0 100% 0); transition: clip-path .6s var(--e-out);
}
.drawer[data-open="true"] { clip-path: inset(0 0 0 0); }
.drawer a {
  font-family: var(--f-display); font-style: italic; font-weight: 800;
  font-size: clamp(2.2rem,9vw,3.4rem); text-transform: uppercase;
  color: var(--paper); padding-block: .35rem; border-bottom: 1px solid var(--hairline);
}
.drawer a:hover { color: var(--lime); }

@media (max-width: 1000px) {
  .nav-links, .nav-tel { display: none; }
  .burger { display: block; }
}
/* Below ~620px the logo + CTA + burger no longer fit, and the burger — the only
   way into the menu — gets pushed off-screen. Drop the nav CTA; the fixed
   mobile action bar already carries "Start a Project". */
@media (max-width: 620px) {
  .nav-right .btn { display: none; }
  /* 582x100 artwork at 34px is 198px wide — with the burger (44px) and the
     gutter that still fits a 375px viewport with room to spare. */
  .nav-logo img { height: 34px; }
  .nav.is-stuck .nav-logo img { height: 30px; }
}

/* --- 7. HERO — VIDEO-FIRST ----------------------------------------------
   The film carries the fold; the headline supports it. Two arrangements:

   ≥1000px  copy column + a tall film column standing at the film's NATIVE
            9:16. The source is a 720x1280 phone clip — the one thing we must
            not do is blow it across a 1920 viewport, which is a 2.7x upscale
            of a 720x405 crop and looks soft on the exact surface that is
            meant to prove craftsmanship. Height-constrained to the fold, the
            frame lands ~400-460 CSS px wide, i.e. still downscaling. Sharp.

   <1000px  the film IS the fold, full-bleed. A phone is already 9:16, so
            this is where the footage finally fills a screen at 1:1 — the
            layout the source actually wants. Copy sits on a scrim over it.

   Breakpoint matches the nav's (1000px) so the layout never changes shape
   in between two adjacent components.
   ---------------------------------------------------------------------- */
.hero {
  position: relative; min-height: 100svh;
  display: flex; align-items: center;
  padding-block: 7.5rem 3rem;
  overflow: hidden;
  background: var(--ink);
}
/* Material field — brushed steel + halftone. Still the backdrop behind and
   around the film column on desktop; on mobile the film covers it. */
.hero-field {
  position: absolute; inset: 0; z-index: 0;
  background:
    repeating-linear-gradient(98deg, rgba(255,255,255,.020) 0 2px, transparent 2px 5px),
    radial-gradient(120% 90% at 78% 8%, rgba(163,213,59,.16), transparent 58%),
    radial-gradient(90% 70% at 12% 92%, rgba(104,115,111,.24), transparent 60%),
    linear-gradient(168deg, #121615 0%, var(--ink) 48%, #090B0A 100%);
}
/* Diagonal rule sweep — echoes the wrap stripe on the Barrier Tahoe */
.hero-stripe {
  position: absolute; z-index: 1; left: -12%; right: -12%; height: 26vh;
  top: 42%; transform: skewY(-7deg);
  background: linear-gradient(90deg, transparent, rgba(163,213,59,.055) 38%, rgba(163,213,59,.10) 52%, transparent 88%);
  border-block: 1px solid rgba(163,213,59,.16);
  pointer-events: none;
}
.hero-inner { position: relative; z-index: 3; width: 100%; }
.hero-copy { position: relative; z-index: 3; }

.hero h1 {
  font-family: var(--f-display); font-style: italic; font-weight: 800;
  font-size: var(--t-hero); text-transform: uppercase;
  letter-spacing: -.022em; line-height: .855;
  margin-block: 1.1rem 1.4rem;
  max-width: 16ch;
}
.hero h1 em { font-style: italic; color: var(--lime); position: relative; }

.hero-sub { max-width: 46ch; color: var(--steel-lt); font-size: clamp(1rem,1.35vw,1.22rem); }
.hero-cta { display: flex; flex-wrap: wrap; gap: .9rem; margin-top: 2.4rem; }

/* Everything below is scoped to .hero--film — the homepage fold. The inner
   pages (about/services/portfolio/work/contact) share this same .hero and
   .hero-inner markup with no film in them, so an unscoped grid here would
   drop their headline and body copy into two side-by-side columns, and an
   unscoped type change would resize four headers nobody asked to touch.
   Removing `hero--film` from the <header> is also the film-pulled fallback —
   see §7c. */
.hero--film h1 {
  /* Smaller than the full-width hero on purpose: the headline now shares the
     fold with the film and sits in a narrower column. At the inherited 8.4vw
     it wrapped mid-word against the film at 1280px. */
  font-size: clamp(2.6rem, 5.6vw, 6.4rem);
  max-width: 14ch;
}
.hero--film .hero-sub {
  max-width: 44ch;
  /* --steel-lt is tuned for text on flat --ink. Over a full-bleed film it is
     the dimmest thing on the page and measured 3.88:1 against the eased
     scrim — a fail. Lifting just this line to a brighter steel buys the
     margin back without re-darkening the scrim and burying the footage
     again, and it stays clearly secondary to the headline. */
  color: #CBD2CE;
}

/* --- 7a. HERO — the film is the fold, full-bleed, at every size ---------
   Previously the desktop layout was a copy column beside a portrait video
   panel, because a 9:16 phone clip cannot be stretched across a desktop
   without a 2.7x upscale. With a landscape re-cut that constraint is gone, so
   the fold becomes what it should always have been: the film edge to edge with
   the copy over it.

   The crop is ART-DIRECTED, not scaled. Landscape viewports get the 16:9 cut;
   portrait ones get a 4:5 cut of the same segment, so a tall screen is filled
   by a tall frame instead of letterboxing a wide one.

   Selection keys off viewport SHAPE, not width. A width-only rule sent a
   portrait tablet (820x1180) the landscape cut, which then had to be cropped
   so hard that barely a door panel was left. The condition is
   `min-width: 820px AND min-aspect-ratio: 1/1` — wide enough for the copy to
   sit beside the vehicle, and actually landscape. js/ig.js matches the same
   query so the file that loads always agrees with the scrim painted over it. */
.hero--film { align-items: center; }
/* .hero-inner is position:relative + z-index:3 on the inner pages so the copy
   clears the material field. Both break a full-bleed film:
     - position:relative makes it the film's containing block, so `inset: 0`
       resolves to the height of the COPY, not the fold (measured: 473px inside
       an 844px viewport).
     - z-index:3 opens a stacking context that would trap the film (z1) and the
       scrim (z2) inside it and paint the film over the scrim.
   `static` removes both; film < scrim < copy then resolve in .hero. */
.hero--film .hero-inner { position: static; }
.hero--film .hero-stripe { display: none; }   /* the film covers it */
.hero--film .hero-film { position: absolute; inset: 0; z-index: 1; margin: 0; }
.hero--film .hero-film .film {
  position: absolute; inset: 0;
  width: 100%; height: 100%; aspect-ratio: auto;
  border: 0; clip-path: none;      /* the corner notch reads as damage full-bleed */
}
/* Wide enough for the two CTAs to sit on ONE line. At 42ch the primary
   ("Request a Brand Consultation", ~315px) plus the secondary wrapped, which
   made the fold read as a stacked form rather than a headline with actions.
   The sub-paragraph keeps its own narrower measure below. */
.hero--film .hero-copy { max-width: 34rem; }

/* --- scrim ---
   The re-cut changed the contrast problem completely. The old loop sat on a
   dark charcoal trailer; this one opens on a WHITE van panel in direct sun, so
   the scrim has to do considerably more work. Wide viewports get a horizontal
   ramp (opaque behind the copy on the left, clearing to the right so the wrap
   stays visible) plus a mild global darkening; phones get a vertical ramp with
   a solid floor under the copy. Both are measured, not eyeballed — see
   _source-assets/hero-contrast.mjs. */
@media (min-width: 820px) and (min-aspect-ratio: 1/1) {
  .hero--film .film-scrim {
    display: block; position: absolute; inset: 0; z-index: 2; pointer-events: none;
    background:
      linear-gradient(96deg,
        rgba(8,10,9,.94) 0%,
        rgba(8,10,9,.90) 30%,
        rgba(8,10,9,.62) 48%,
        rgba(8,10,9,.24) 68%,
        rgba(8,10,9,.10) 100%),
      linear-gradient(to top, rgba(8,10,9,.48), rgba(8,10,9,0) 42%);
  }
  .hero--film .film-labels { inset: auto 0 0 auto; padding-right: var(--gut); text-align: right; }
}
@media (max-width: 819px), (max-aspect-ratio: 1/1) {
  /* The 2.6rem on top of the 2.5rem reserves the credit line that sits
     absolutely at the bottom. Without it the CTAs cleared it by 7px at
     820x1180 and visually collided. */
  .hero--film { align-items: flex-end; padding-block: 6rem calc(2.5rem + 2.6rem); }
  .hero--film .film-scrim {
    display: block; position: absolute; inset: 0; z-index: 2; pointer-events: none;
    background: linear-gradient(to top,
      rgba(8,10,9,.93) 0%,
      rgba(8,10,9,.88) 58%,
      rgba(8,10,9,.60) 78%,
      rgba(8,10,9,.12) 100%);
  }
  .hero--film h1 { font-size: clamp(2.6rem, 10vw, 4.6rem); max-width: 12ch; }
  .hero--film .hero-sub { max-width: 40ch; }
  .hero--film .film-labels { display: none; }
  .hero--film .film-credit {
    position: absolute; z-index: 3; bottom: .9rem;
    left: var(--gut); right: var(--gut);
    margin: 0; color: var(--steel-lt);
  }
}
/* The fixed action bar overlays the bottom 54px; the hero's own 2.5rem bottom
   padding is less than that, so the CTAs would sit underneath it. The extra
   2.6rem reserves the two lines the credit wraps to at 390px. */
@media (max-width: 760px) {
  /* plus the fixed action bar, which overlays the bottom 54px */
  .hero--film { padding-bottom: calc(2.5rem + 2.6rem + 54px); }
  .hero--film .film-credit { bottom: calc(54px + .7rem); }
}
/* Desktop credit sits under the film, bottom-left, clear of the copy. */
@media (min-width: 820px) and (min-aspect-ratio: 1/1) {
  .hero--film .film-credit {
    position: absolute; z-index: 3; bottom: 1.4rem; left: var(--gut);
    /* --steel-lt, matching the portrait variant above and the base rule. At
       --steel this sat at 3.86:1 over the scrim — and unlike the two credits
       under a film, this one lies over moving footage, so it needs the margin
       more, not less. */
    margin: 0; color: var(--steel-lt);
  }
}

/* --- 7c. HERO FALLBACK — film pulled ------------------------------------
   If Dylan does not clear the Unknown Industries footage: remove `hero--film`
   from the <header> and delete the <figure class="hero-film">. Because every
   rule above is scoped to .hero--film, the fold falls back to the plain
   material-field hero the inner pages already use — full-size headline, no
   orphaned scrim. js/ig.js exits early on the missing #hero-film.
   See the gate note at the top of index.html. */

/* --- 7d. SPEC STRIP ----------------------------------------------------- */
/* Lifted out of the fold. Same markup, now its own band under the hero. */
.spec-band { background: var(--ink); }
.spec-band .spec { margin-top: 0; }

/* Spec strip — trades fake stats for verifiable facts */
.spec {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(180px,1fr));
  gap: 1px; margin-top: clamp(2rem,4.5vh,3.4rem);
  background: var(--hairline); border-block: 1px solid var(--hairline);
}
.spec > div { background: var(--ink); padding: 1.5rem 1.4rem; }
/* Without the <small> descriptions each cell is just a label and a value, so
   the tall padding left a lot of dead space under the fold. */
.spec--tight > div { padding: 1.15rem 1.4rem; }
.spec dt {
  font-family: var(--f-mono); font-size: var(--t-xs); letter-spacing: .18em;
  text-transform: uppercase; color: var(--steel); margin-bottom: .5rem;
}
.spec dd {
  margin: 0; font-family: var(--f-display); font-style: italic; font-weight: 800;
  font-size: clamp(1.25rem,2vw,1.6rem); text-transform: uppercase; color: var(--paper);
  line-height: 1;
}
.spec dd small { display: block; font-family: var(--f-body); font-style: normal;
  font-weight: 400; font-size: .74rem; letter-spacing: 0; text-transform: none;
  color: var(--steel-lt); margin-top: .45rem; line-height: 1.4; }

/* --- 8. SECTION HEADS --------------------------------------------------- */
.sec-head { max-width: 46ch; margin-bottom: clamp(2.5rem,5vh,4.5rem); }
.sec-head h2 {
  font-family: var(--f-display); font-style: italic; font-weight: 800;
  font-size: var(--t-h2); text-transform: uppercase; letter-spacing: -.018em;
  margin-block: 1.1rem .9rem;
}
.sec-head h2 em { font-style: italic; color: var(--lime); }
.light .sec-head h2 em { color: var(--lime-deep); }

/* --- 9. LIGHT SECTION --------------------------------------------------- */
.light { background: var(--paper); color: var(--ink); }
.light .lede, .light p { color: var(--steel-dk); }

/* --- 10. SERVICES ------------------------------------------------------- */
.svc-list { border-top: 1px solid var(--paper-2); }
.svc {
  position: relative; display: grid;
  grid-template-columns: 5.5rem 1fr auto;
  gap: clamp(1rem,3vw,2.5rem); align-items: center;
  padding: clamp(1.6rem,3.2vh,2.5rem) 0;
  border-bottom: 1px solid var(--paper-2);
  transition: color .4s;
}
.svc::before {
  content: ''; position: absolute; inset: 0 -1.5rem; z-index: 0;
  background: var(--ink); transform: skewX(var(--slant)) scaleX(0);
  transform-origin: left; transition: transform .55s var(--e-out);
}
.svc > * { position: relative; z-index: 1; }
.svc:hover::before { transform: skewX(var(--slant)) scaleX(1); }
.svc:hover { color: var(--paper); }
.svc:hover .svc-no { color: var(--lime); }
.svc:hover .svc-desc { color: var(--steel-lt); }

.svc-no { display: block; font-family: var(--f-mono); font-size: var(--t-xs); letter-spacing: .16em; color: var(--steel); transition: color .4s; }
.svc-name {
  display: block;
  font-family: var(--f-display); font-style: italic; font-weight: 800;
  font-size: clamp(1.5rem,3.4vw,2.5rem); text-transform: uppercase; line-height: 1;
}
.svc-desc { display: block; font-size: var(--t-sm); color: var(--steel); margin-top: .5rem; max-width: 54ch; transition: color .4s; }
/* Homepage variant: names only. The rows get shorter, so the vertical rhythm
   has to come down with them or the list reads as eight empty bands. */
.svc-list--compact .svc { padding-block: clamp(1.05rem, 2.1vh, 1.5rem); }
.svc-go { font-family: var(--f-mono); font-size: 1.4rem; opacity: 0; transform: translateX(-12px); transition: .45s var(--e-out); }
.svc:hover .svc-go { opacity: 1; transform: translateX(0); color: var(--lime); }
@media (max-width: 720px) {
  .svc { grid-template-columns: 3.2rem 1fr; }
  .svc-go { display: none; }
}

/* --- 11. WORK / PORTFOLIO ---------------------------------------------- */
.work-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%,340px),1fr)); gap: clamp(1rem,2vw,1.6rem); }

/* Image-led variant — homepage only (the portfolio page's placeholder grid
   must stay an even 3-up). There are only three photographs of Dylan's work,
   so the answer to "make the front end image-heavy" is to run them BIG rather
   than to pad the row out: the lead shot takes the full width as a cinematic
   band, the other two sit beside it. */
@media (min-width: 761px) {
  .work-grid--feature { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .work-grid--feature > .work:first-child { grid-column: 1 / -1; aspect-ratio: 2 / 1; }
}
@media (max-width: 760px) {
  .work-grid--feature > .work:first-child { aspect-ratio: 3 / 2; }
}
.work {
  position: relative; display: block; overflow: hidden;
  background: var(--ink-2); border: 1px solid var(--hairline);
  aspect-ratio: 4/3;
}
.work img { width: 100%; height: 100%; object-fit: cover; transition: transform 1.1s var(--e-out), filter .6s; filter: saturate(.92); }
.work:hover img { transform: scale(1.055); filter: saturate(1.08); }
.work-meta {
  position: absolute; inset: auto 0 0 0; z-index: 2;
  padding: clamp(1.4rem,2.4vw,1.9rem) clamp(1.2rem,2.4vw,1.9rem) clamp(1.2rem,2.4vw,1.9rem);
  /* Scrim must be opaque under the TEXT, not just at the very bottom edge.
     These photos have blown-out highlights (white truck, sunlit sign) exactly
     where the kicker sits, so the solid floor extends to ~62% of the overlay. */
  background: linear-gradient(to top,
    rgba(8,10,9,.97) 0%, rgba(8,10,9,.94) 88%, rgba(8,10,9,.55) 96%, transparent 100%);
}
.work-kind { font-family: var(--f-mono); font-size: var(--t-xs); letter-spacing: .18em; text-transform: uppercase; color: var(--lime); }
.work-title {
  font-family: var(--f-display); font-style: italic; font-weight: 800;
  font-size: clamp(1.2rem,2vw,1.6rem); text-transform: uppercase; margin-top: .3rem; color: var(--white);
}
.work-note { font-size: .8rem; color: var(--steel-lt); margin-top: .3rem; }

/* Placeholder tile — honestly marked, not a fake photo */
.ph {
  position: relative; width: 100%; height: 100%;
  background:
    repeating-linear-gradient(var(--slant-deg,-8deg), rgba(255,255,255,.028) 0 12px, transparent 12px 24px),
    linear-gradient(160deg, var(--ink-3), var(--ink-2));
  display: grid; place-items: center; text-align: center; padding: 1.5rem;
}
.ph-badge {
  font-family: var(--f-mono); font-size: var(--t-xs); letter-spacing: .18em;
  text-transform: uppercase; color: var(--steel);
  border: 1px dashed var(--hairline); padding: .55rem 1rem;
}
.ph-badge b { color: var(--lime); font-weight: 700; }

/* --- 11b. IMAGE BUG ------------------------------------------------------
   The mark sits in the DOM, not baked into the pixels.

   Burning it into the JPEG looked fine in isolation and broke on the page:
   `object-fit: cover` crops each tile differently, so the 2:1 lead tile lost
   the bug completely, the 830 tile showed the bottom half of it, and Harris
   lost it too. A fixed 20%-of-image bug cannot survive a responsive crop.

   As an overlay it lands in the same place on every tile whatever the crop,
   scales with the tile rather than with the source raster, stays vector-crisp
   at any DPR, and never needs a photo re-encoded when a layout changes.

   Legibility comes from a drop-shadow that follows the artwork's own alpha
   rather than a rectangular plate — the mark reads on sky, white bodywork and
   dark pavement without ever looking like a sticker.

   NB: the burned-in `*-id.*` files are still in /assets and still used for the
   OG card, which social platforms serve uncropped and which genuinely does
   need the mark travelling with the pixels. */
.shot { position: relative; display: block; }
.shot-bug {
  /* Bottom-right and half the previous size (2026-08-23, Dylan's note: the
     mark read as redundant at the old scale). `inset` rather than `top`, so
     the .work override below only has to move one edge. */
  position: absolute; z-index: 3; inset: auto 4% 4% auto;
  /* clamp keeps the mark a consistent optical size across a 1294px lead tile
     and a 633px secondary one, instead of tracking each photo's raster */
  width: clamp(39px, 7.5%, 84px);
  aspect-ratio: 701.89 / 404.94;      /* the stacked lockup's own ratio */
  background: url('/assets/id-logo.svg') center / contain no-repeat;
  pointer-events: none;
  filter:
    drop-shadow(0 1px 2px rgba(8,10,9,.92))
    drop-shadow(0 0 10px rgba(8,10,9,.75))
    drop-shadow(0 0 26px rgba(8,10,9,.55));
}
/* .work tiles carry .work-meta along the bottom — an opaque scrim with the
   client name over it. The bug still sits bottom-right, but lifted clear of
   that band rather than landing on top of the title. 8.5rem is the tallest the
   overlay gets (kicker + title + note at the top of its clamp range). */
.work .shot-bug { bottom: 8.5rem; right: 4.5%; }
@media (max-width: 620px) {
  .shot-bug { width: clamp(33px, 10%, 60px); }
  .work .shot-bug { bottom: 7rem; }
}

/* --- 11b-i. HERO MOTION CONTROL ------------------------------------------
   Top-right, because every other edge of the film is spoken for: the credit
   and the CTAs own the bottom-left, .film-labels the bottom-right, and the
   nav the top-left. Sits above .film-scrim (z-index 2) and the labels.

   Deliberately a real <button> with a text label rather than a bare glyph —
   a lone pause icon over moving footage is easy to miss, and this exists
   precisely for people who are struggling with the motion. */
.film-toggle {
  position: absolute; z-index: 5;
  /* Clears the fixed nav, which is ~76px unstuck (1rem padding either side of
     a 44px CTA). Measured in the browser, not derived — the nav has no height
     variable to key off, and it shrinks on scroll rather than growing. */
  top: 5.75rem; right: var(--gut);
  display: inline-flex; align-items: center; gap: .55rem;
  /* 44px minimum target on the short axis, for a control people reach for
     in a hurry. */
  min-height: 44px; padding: .5rem .95rem;
  font-family: var(--f-mono); font-size: var(--t-xs);
  letter-spacing: .16em; text-transform: uppercase;
  color: var(--white); background: rgba(8,10,9,.62);
  border: 1px solid rgba(255,255,255,.28);
  backdrop-filter: blur(7px); -webkit-backdrop-filter: blur(7px);
  cursor: pointer; transition: background .18s ease, border-color .18s ease;
}
.film-toggle:hover { background: rgba(8,10,9,.82); border-color: var(--lime); }
.film-toggle:focus-visible { outline: 2px solid var(--lime); outline-offset: 3px; }

/* Two bars (pause) that become one triangle (play) when aria-pressed flips.
   Drawn in CSS so it costs no request and inherits currentColor.

   The bars are a GRADIENT, not a clip-path. A single clip-path polygon cannot
   describe two disjoint rectangles — the seam between them has to be traced,
   which rendered as one notched bar rather than a pause glyph. The triangle is
   genuinely one shape, so that one stays a clip-path. */
.film-toggle-icon {
  width: 11px; height: 12px; flex: none;
  background: linear-gradient(to right,
    currentColor 0 35%, transparent 35% 65%, currentColor 65% 100%);
}
.film-toggle[aria-pressed="true"] .film-toggle-icon {
  background: currentColor;
  clip-path: polygon(0 0, 100% 50%, 0 100%);
}

@media (max-width: 620px) {
  /* Label off on phones — the bar is tight and the icon plus the accessible
     name carry it. */
  .film-toggle { padding: .5rem .7rem; }
  .film-toggle-label { position: absolute; width: 1px; height: 1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
}

/* --- 11c. FILM PLAYER — the long walk-around ----------------------------
   Click-to-play, deliberately. The hero loop is ambient wallpaper; this is a
   22s cut someone chooses to watch, and it is the heaviest asset on the page.
   Shipping it as a poster plus a button means it costs zero bytes until asked
   for, which is also why it can afford to be the longest, best-quality file
   here. */
.film-band { background: var(--ink-2); border-block: 1px solid var(--hairline); }

.film-player {
  position: relative; margin: 0;
  aspect-ratio: 16 / 9;
  background: var(--ink);
  border: 1px solid var(--hairline);
  overflow: hidden;
  /* the corner notch from the brand's forward lean */
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 30px), calc(100% - 30px) 100%, 0 100%);
}
.film-player img,
.film-player video {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover;
}
.film-player video { opacity: 0; transition: opacity .45s var(--e-out); }
.film-player.is-playing video { opacity: 1; }
.film-player.is-playing .film-play { opacity: 0; pointer-events: none; }

.film-play {
  position: absolute; inset: 0; z-index: 3;
  display: block; width: 100%; padding: 0; border: 0;
  cursor: pointer; background: none;
  transition: opacity .45s var(--e-out);
}
/* Scrim so the button and the meta line read over any frame of the poster. */
.film-play::after {
  content: ''; position: absolute; inset: 0;
  background:
    radial-gradient(38% 38% at 50% 50%, rgba(8,10,9,.62), rgba(8,10,9,0) 70%),
    linear-gradient(to top, rgba(8,10,9,.72), rgba(8,10,9,0) 38%);
}
.film-play-btn {
  position: absolute; z-index: 2; top: 50%; left: 50%;
  translate: -50% -50%;
  width: clamp(64px, 7vw, 92px); aspect-ratio: 1;
  background: var(--lime);
  transform: skewX(var(--slant));
  transition: transform .4s var(--e-out), background .3s;
  display: grid; place-items: center;
}
/* the play triangle, un-skewed so it stays a clean isosceles */
.film-play-btn::before {
  content: ''; display: block;
  width: 0; height: 0;
  border-left: clamp(17px, 1.9vw, 25px) solid var(--ink);
  border-block: clamp(11px, 1.2vw, 16px) solid transparent;
  transform: skewX(calc(var(--slant) * -1)) translateX(12%);
}
.film-play:hover .film-play-btn { transform: skewX(var(--slant)) scale(1.08); background: var(--lime-hot); }
.film-play:focus-visible .film-play-btn { outline: 3px solid var(--paper); outline-offset: 4px; }

.film-play-meta {
  position: absolute; z-index: 2; left: clamp(1rem,2.4vw,1.9rem); bottom: clamp(1rem,2.4vw,1.6rem);
  font-family: var(--f-mono); font-size: var(--t-xs); font-weight: 600;
  letter-spacing: .18em; text-transform: uppercase; color: var(--white);
}

.film-band .film-credit { margin-top: .9rem; }
/* The film player's own bug sits clear of the play button and the meta line. */
.film-player .shot-bug { z-index: 4; inset: auto 4% 5% auto; }

@media (prefers-reduced-motion: reduce) {
  .film-play-btn, .film-player video { transition: none; }
}

/* --- 12. DYLAN / MAKER -------------------------------------------------- */
.maker { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(2rem,5vw,5rem); align-items: center; }
@media (max-width: 900px) { .maker { grid-template-columns: 1fr; } }
.maker-portrait { position: relative; aspect-ratio: 4/5; overflow: hidden; background: var(--ink-2); border: 1px solid var(--hairline); }
/* Quote-only band (no portrait tile) — the quote is the whole section, so it
   gets room to run rather than sitting in a half-width column. */
.maker-band .maker-quote { max-width: 20ch; font-size: clamp(2rem, 4.4vw, 3.6rem); }
.maker-band .lede { max-width: 52ch; }
.maker-quote {
  font-family: var(--f-display); font-style: italic; font-weight: 800;
  font-size: clamp(1.6rem,3vw,2.6rem); text-transform: uppercase; line-height: 1.02;
  margin-block: 1.2rem 1.4rem;
}
.maker-quote em { font-style: italic; color: var(--lime); }

/* --- 13. PROCESS -------------------------------------------------------- */
.steps { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%,230px),1fr)); gap: 1px; background: var(--hairline); border: 1px solid var(--hairline); }
.step { background: var(--ink); padding: clamp(1.6rem,3vw,2.4rem); }
.step-no {
  font-family: var(--f-display); font-style: italic; font-weight: 800;
  font-size: 2.6rem; color: var(--hairline); line-height: 1;
  -webkit-text-stroke: 1px var(--steel-dk);
}
.step h3 { font-family: var(--f-display); font-style: italic; font-size: var(--t-h3); text-transform: uppercase; margin-block: .9rem .6rem; }
.step p { font-size: var(--t-sm); color: var(--steel-lt); }

/* --- 14. MARQUEE -------------------------------------------------------- */
.marquee { overflow: hidden; border-block: 1px solid var(--hairline); padding-block: 1.1rem; background: var(--ink-2); }
.marquee-track { display: flex; gap: 3.5rem; width: max-content; animation: slide 34s linear infinite; }
.marquee span {
  font-family: var(--f-display); font-style: italic; font-weight: 800;
  font-size: clamp(1.1rem,2vw,1.7rem); text-transform: uppercase;
  color: var(--steel-dk); white-space: nowrap; display: flex; align-items: center; gap: 3.5rem;
}
.marquee span::after { content: '◆'; font-size: .55em; color: var(--lime); }
@keyframes slide { to { transform: translateX(-50%); } }
@media (prefers-reduced-motion: reduce) { .marquee-track { animation: none; } }

/* --- 15. CTA ------------------------------------------------------------ */
.cta { position: relative; background: var(--lime); color: var(--ink); overflow: hidden; }
.cta::before {
  content: ''; position: absolute; inset: -30% -10%;
  background: repeating-linear-gradient(-8deg, rgba(0,0,0,.045) 0 30px, transparent 30px 60px);
}
.cta .wrap { position: relative; z-index: 1; }
.cta h2 { font-family: var(--f-display); font-style: italic; font-weight: 800; font-size: var(--t-h1); text-transform: uppercase; letter-spacing: -.02em; }
.cta p { color: rgba(14,17,16,.74); margin-block: 1rem 2.2rem; font-size: 1.1rem; max-width: 48ch; }

/* --- 16. FORM ----------------------------------------------------------- */
.field { margin-bottom: 1.4rem; }
.field label {
  display: block; font-family: var(--f-mono); font-size: var(--t-xs);
  letter-spacing: .16em; text-transform: uppercase; color: var(--steel-lt); margin-bottom: .55rem;
}
.field input, .field select, .field textarea {
  width: 100%; padding: .95rem 1.1rem;
  background: var(--ink-2); border: 1px solid var(--hairline);
  color: var(--paper); font-family: var(--f-body); font-size: 1rem;
  transition: border-color .3s, background .3s;
}
.field input:focus, .field select:focus, .field textarea:focus {
  border-color: var(--lime); background: var(--ink-3); outline: none;
}
.field textarea { min-height: 140px; resize: vertical; }
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 1.1rem; }
@media (max-width: 640px) { .field-row { grid-template-columns: 1fr; } }
.req { color: var(--lime); }

.drop {
  border: 1.5px dashed var(--hairline); padding: 1.9rem 1.2rem; text-align: center;
  transition: border-color .3s, background .3s; cursor: pointer; display: block;
}
.drop:hover, .drop:focus-within { border-color: var(--lime); background: var(--ink-2); }
.drop small { display: block; color: var(--steel); font-size: .78rem; margin-top: .4rem; }

/* --- 17. FOOTER --------------------------------------------------------- */
.foot { background: var(--ink-2); border-top: 1px solid var(--hairline); }
.foot-grid { display: grid; grid-template-columns: 1.6fr 1fr 1fr 1.2fr; gap: clamp(2rem,4vw,3.5rem); }
@media (max-width: 900px) { .foot-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 560px) { .foot-grid { grid-template-columns: 1fr; } }
.foot h4 { font-family: var(--f-mono); font-size: var(--t-xs); letter-spacing: .18em; text-transform: uppercase; color: var(--lime); margin-bottom: 1.1rem; font-weight: 600; }
.foot li { margin-bottom: .55rem; }
.foot a { color: var(--steel-lt); font-size: var(--t-sm); transition: color .3s; }
.foot a:hover { color: var(--paper); }
.foot-bar {
  margin-top: clamp(2.5rem,5vh,4rem); padding-top: 1.6rem; border-top: 1px solid var(--hairline);
  display: flex; flex-wrap: wrap; gap: 1rem; justify-content: space-between;
  font-size: .78rem; color: var(--steel);
}

/* --- 18. SCROLL REVEAL -------------------------------------------------- */
[data-reveal] {
  opacity: 0; transform: translateY(26px);
  transition: opacity .85s var(--e-out), transform .85s var(--e-out);
  transition-delay: var(--d, 0ms);
}
[data-reveal].in { opacity: 1; transform: none; }

/* Above-the-fold content must never depend on the scroll observer — if JS is
   slow, blocked or absent, the hero would otherwise paint empty. These animate
   on load instead; the observer adding .in later is harmless. */
.hero [data-reveal] { animation: rise .9s var(--e-out) both; animation-delay: var(--d, 0ms); }
@keyframes rise { from { opacity: 0; transform: translateY(26px); } to { opacity: 1; transform: none; } }
/* Belt and braces: no JS at all → everything visible. */
.no-js [data-reveal] { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  [data-reveal] { opacity: 1; transform: none; transition: none; }
  * { animation-duration: .001ms !important; transition-duration: .001ms !important; }
}

/* --- 19. MOBILE ACTION BAR --------------------------------------------- */
.mobile-bar {
  position: fixed; inset: auto 0 0 0; z-index: 180;
  display: none; gap: 1px; background: var(--hairline);
  border-top: 1px solid var(--hairline);
}
.mobile-bar a {
  flex: 1; text-align: center; padding: 1rem .5rem; background: var(--ink);
  font-family: var(--f-mono); font-size: var(--t-xs); font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase;
}
.mobile-bar a.primary { background: var(--lime); color: var(--ink); }
@media (max-width: 760px) {
  .mobile-bar { display: flex; }
  body { padding-bottom: 54px; }
}

/* --- 20. UTIL ----------------------------------------------------------- */
.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;
}
.rule { height: 1px; background: var(--hairline); border: 0; margin: 0; }

/* --- 21. THE FILM — hero showcase ---------------------------------------
   Was its own mid-page section; moved into the hero, where it is now the
   primary conversion element. The section it came from was deleted rather
   than kept, because playing the same 14.6s clip twice on one page reads as
   a shortage of work, not an abundance of it.

   The film is a 720x1280 PORTRAIT phone clip and is presented at its native
   aspect on purpose — see §7 for the arithmetic on why a full-bleed desktop
   crop is not on the table.
   -------------------------------------------------------------------- */

/* --- film frame --- */
.film {
  position: relative; aspect-ratio: 9 / 16; overflow: hidden;
  background: var(--ink); border: 1px solid var(--hairline);
  /* corner notch echoing the logo's forward lean */
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 26px), calc(100% - 26px) 100%, 0 100%);
}
.film video,
.film .film-poster {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover;
  /* Slight over-scale so the scroll zoom never reveals an edge. */
  transform: scale(1.03);
  will-change: transform;
}
.film .film-poster { z-index: 1; transition: opacity .5s var(--e-out); }
.film.is-playing .film-poster { opacity: 0; }

/* --- masked reveal on load --- */
/* The frame wipes open from the bottom as the page paints. It lives on the
   FIGURE, not on .film: .film's clip-path is already spent on the corner
   notch, and a second clip-path on the same element would replace it. */
@media (prefers-reduced-motion: no-preference) {
  .hero-film { animation: film-reveal .95s var(--e-out) .1s both; }
}
@keyframes film-reveal {
  from { clip-path: inset(0 0 100% 0); }
  to   { clip-path: inset(0 0 0 0); }
}

/* --- scrim --- */
/* Only the full-bleed (<1000px) hero needs this; on desktop the copy sits on
   the material field, well clear of the frame. Kept in the DOM at all sizes so
   the mobile rule is a display flip rather than a structural change. */
.film-scrim { display: none; }

/* --- light sweep, once, on load --- */
/* A single specular pass across the frame. Deliberately NOT a glass/blur
   treatment — this design system takes its depth from material and type, and
   a permanent glow would fight the footage rather than introduce it. */
.film-sheen {
  position: absolute; inset: -25% -40%; z-index: 2; pointer-events: none;
  background: linear-gradient(100deg,
    transparent 40%, rgba(255,255,255,.10) 48%,
    rgba(163,213,59,.11) 53%, transparent 61%);
  transform: translateX(-115%);
  animation: film-sheen 1.6s var(--e-out) .6s 1 both;
}
@keyframes film-sheen { to { transform: translateX(115%); } }
@media (prefers-reduced-motion: reduce) { .film-sheen { display: none; } }

/* Scroll-driven push-in. Runs off the main thread where supported; browsers
   without scroll timelines simply get the static 1.03 scale above — no JS
   scroll listener anywhere, so this cannot cost frames.
   Range is `exit` only: the film starts in view now, so the old
   `entry 15%` range was already behind the scroll position at load and the
   frame would have opened mid-zoom. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .hero-film .film video, .hero-film .film .film-poster {
      animation: film-push linear both;
      animation-timeline: view();
      animation-range: exit 0% exit 100%;
    }
  }
}
@keyframes film-push { from { transform: scale(1.03); } to { transform: scale(1.13); } }

/* --- label cycle over the film --- */
.film-labels {
  position: absolute; z-index: 2; inset: auto 0 0 0;
  padding: 1.1rem 1.25rem 1.35rem;
  /* Same scrim shape as .work-meta, for the same reason: the footage has
     blown-out highlights (white trailer panels, sunlit concrete) that pass
     directly behind this text as the camera moves. A linear ramp measured only
     4.5:1–6:1 at its worst frame; a solid floor to ~88% holds it above 12:1
     for every frame in the clip. */
  background: linear-gradient(to top,
    rgba(8,10,9,.97) 0%, rgba(8,10,9,.94) 88%, rgba(8,10,9,.55) 96%, transparent 100%);
  font-family: var(--f-mono); font-size: var(--t-xs);
  letter-spacing: .2em; text-transform: uppercase; color: var(--white);
  display: grid; /* all labels stack in one cell; only one is visible at a time */
}
.film-labels b { grid-area: 1 / 1; font-weight: 500; opacity: 0; }
.film-labels b::before { content: '◆'; color: var(--lime); font-size: .6em; margin-right: .7rem; vertical-align: middle; }
.is-live .film-labels b { animation: label-cycle 16s var(--e-out) infinite; }
.is-live .film-labels b:nth-child(2) { animation-delay: 4s; }
.is-live .film-labels b:nth-child(3) { animation-delay: 8s; }
.is-live .film-labels b:nth-child(4) { animation-delay: 12s; }
@keyframes label-cycle {
  0%, 1%   { opacity: 0; transform: translateY(6px); }
  4%, 22%  { opacity: 1; transform: none; }
  26%, 100%{ opacity: 0; transform: translateY(-4px); }
}
@media (prefers-reduced-motion: reduce) {
  .is-live .film-labels b { animation: none; }
  .film-labels b:first-child { opacity: 1; }
}
/* Without JS the cycle never starts, so show the first label rather than none. */
.no-js .film-labels b:first-child { opacity: 1; }

.film-credit {
  margin-top: .9rem; font-family: var(--f-mono); font-size: var(--t-xs);
  letter-spacing: .14em; text-transform: uppercase;
  /* --steel (#68736F) on --ink-2 is 3.86:1 — under AA for text this small.
     This one never got caught because the credit used to render inside the
     clipped .film-player box, behind the play scrim, and was invisible. Now
     that it sits under the film where it was always meant to, it has to pass:
     --steel-lt is 7.3:1 on --ink-2 and 7.9:1 on --ink. */
  color: var(--steel-lt);
}

/* .showcase-caps / .film-kicker / .film-col were removed with the mid-page
   showcase section. Their four capability facts duplicated the spec strip
   (§7d), which is now directly under the fold, and their CTA duplicated the
   hero's. Nothing was lost but the repetition. */

/* --- 21b. SOCIAL LINKS --------------------------------------------------
   Icon is masked rather than drawn, so it takes `currentColor` and inherits
   the same hover treatment as every other link on the site. Meta's gradient
   is deliberately not used: it fights the lime/steel palette and it is not our
   artwork to restyle.

   44px hit target — this is the one control a visitor is most likely to reach
   for on a phone, and it sits next to text links that are much easier to miss.

   ⚠️ These rules are inert until the markup exists. The account URL is NOT
   guessed anywhere — see OPEN-QUESTIONS.md. */
.social { display: flex; align-items: center; gap: .55rem; }
.social a {
  display: grid; place-items: center;
  width: 44px; height: 44px;
  color: var(--steel-lt);
  transition: color .3s, transform .3s var(--e-out);
}
.social a::before {
  content: ''; width: 22px; height: 22px;
  background: currentColor;
  -webkit-mask: var(--icon) center / contain no-repeat;
          mask: var(--icon) center / contain no-repeat;
}
.social a:hover { color: var(--lime); transform: translateY(-2px); }
.social a[data-net="instagram"] { --icon: url('/assets/icon-instagram.svg'); }
/* On the light sections the muted steel drops below AA against paper. */
.light .social a { color: var(--steel-dk); }
.light .social a:hover { color: var(--lime-deep); }

/* --- 21c. INSTAGRAM GALLERY --------------------------------------------
   Written into index.html by scripts/instagram-pull.py between IG markers, so
   these tiles are real HTML rather than something that appears after JS —
   the work stays visible to search and to AI answer engines.

   Tiles are square. The feed mixes 4:5, 1:1 and 16:9, and a grid of mixed
   aspects reads as a scrapbook; the script centre-crops on the way in.

   Deliberately NOT given the .shot-bug overlay: these are already published
   under Dylan's own handle and the tile links back to the post, so stamping
   the mark again is redundant. The bug is for photographs that travel without
   that context. */
.ig-band { background: var(--ink-2); border-block: 1px solid var(--hairline); }
.ig-head {
  display: flex; flex-wrap: wrap; gap: 1.5rem;
  align-items: flex-end; justify-content: space-between;
  margin-bottom: clamp(2rem, 4vh, 3.2rem);
}
.ig-grid {
  display: grid; gap: clamp(.5rem, 1vw, .9rem);
  /* min(50%, 380px) resolves to 3 columns on desktop — Instagram's own grid,
     and big enough that the work reads — while collapsing to a clean 2-up on a
     phone instead of one giant tile per row. A 190px floor gave 6 thumbnails
     in a row that looked like an afterthought in a full-width band. */
  grid-template-columns: repeat(auto-fit, minmax(min(50%, 380px), 1fr));
}
.ig-tile {
  position: relative; display: block; overflow: hidden;
  aspect-ratio: 1; background: var(--ink);
  border: 1px solid var(--hairline);
}
.ig-tile img {
  width: 100%; height: 100%; object-fit: cover;
  filter: saturate(.94);
  transition: transform 1s var(--e-out), filter .5s;
}
.ig-tile::after {
  /* the glyph only on hover — a permanent badge on every tile turns the grid
     into a wall of icons rather than a wall of work */
  content: ''; position: absolute; z-index: 2; inset: auto .7rem .7rem auto;
  width: 22px; height: 22px; background: var(--white);
  -webkit-mask: url('/assets/icon-instagram.svg') center / contain no-repeat;
          mask: url('/assets/icon-instagram.svg') center / contain no-repeat;
  opacity: 0; transform: translateY(6px);
  transition: opacity .35s var(--e-out), transform .35s var(--e-out);
}
.ig-tile:hover img { transform: scale(1.06); filter: saturate(1.06); }
.ig-tile:hover::after, .ig-tile:focus-visible::after { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .ig-tile img, .ig-tile::after { transition: none; }
}

/* --- 22. FOOTER LOCKUP -------------------------------------------------- */
/* The real brand lockup replaces what was a Saira Condensed text stand-in.
   Capped by width so the wordmark stays legible without dominating the
   column; height follows the artwork's own 1.733:1 ratio. */
.foot-logo { width: 100%; max-width: 250px; height: auto; display: block; }

/* --- 23. RECENT-WORK FEED (/portfolio/#recent) --------------------------
   Separate from §22's .ig-grid, which is a 1:1 tile grid built to mirror
   Instagram's own square crop. This set must NOT be square-cropped: it is a mix
   of 1:1, 4:5 and 9:16 phone frames of signs and vehicles, and a centre crop
   cuts the lettering off — the same failure that turned the 830 restriction
   sign into a photograph of the word "DO".

   CSS columns rather than grid, so every frame keeps its own height with no JS.
   break-inside is load-bearing: without it a column break lands mid-photograph
   and the caption ends up in the next column. */
.feed-grid { columns: 4 220px; column-gap: clamp(.7rem, 1.4vw, 1.1rem); }
.feed-card {
  display: block; position: relative; break-inside: avoid;
  margin-bottom: clamp(.7rem, 1.4vw, 1.1rem);
  overflow: hidden; background: var(--ink);
  border: 1px solid var(--hairline);
  transition: border-color .25s ease;
}
.feed-card img { display: block; width: 100%; height: auto; transition: transform .5s cubic-bezier(.2,.7,.3,1); }
.feed-card:hover { border-color: var(--lime); }
.feed-card:hover img { transform: scale(1.04); }
.feed-card:focus-visible { outline: 2px solid var(--lime); outline-offset: 2px; }

/* Same scrim reasoning as .work-meta: these frames have blown highlights —
   white vans, sunlit signs — exactly where the text sits, so the floor has to
   be opaque under the words rather than only at the very edge. */
.feed-meta {
  position: absolute; inset: auto 0 0 0; z-index: 2;
  display: flex; flex-direction: column; gap: .18rem;
  padding: clamp(1.4rem,2.4vw,1.9rem) .9rem .8rem;
  background: linear-gradient(to top,
    rgba(8,10,9,.96) 0%, rgba(8,10,9,.9) 62%, transparent 100%);
}
.feed-kind {
  font-family: var(--f-mono); font-size: .58rem; letter-spacing: .18em;
  text-transform: uppercase; color: var(--lime);
}
.feed-title {
  font-family: var(--f-display); font-style: italic; font-weight: 800;
  text-transform: uppercase; font-size: .95rem; color: var(--white); line-height: 1.15;
}
@media (prefers-reduced-motion: reduce) {
  .feed-card, .feed-card img { transition: none; }
  .feed-card:hover img { transform: none; }
}
