/* ==========================================================================
   Mullins IT — atmosphere layer
   Loaded AFTER each page's inline <style>, so equal-specificity rules win.
   Adds material, depth and one orchestrated motion moment. It deliberately
   changes no colour, typeface or layout: every value below is derived from
   the existing tokens (--bg/--ink/--green/--ln, Fraunces/Inter/JetBrains).
   Everything here degrades to "exactly the old site" under
   prefers-reduced-motion.
   ========================================================================== */

/* --- 1. Paper grain ------------------------------------------------------
   The flat cream field is what makes the site read as a template. Noise gives
   it a surface.

   Applied as the BODY BACKGROUND, not a fixed overlay element, for two
   reasons. Design: texture belongs to the paper the page is printed on, not
   smeared over the photographs — grain on top of images just muddies them.
   Practical: a full-viewport fixed overlay is the top hit-test target for the
   whole page, and pointer-events:none is the only thing standing between that
   and swallowed input. Not worth the risk for a texture.

   Do NOT add background-attachment:fixed here. These pages set
   `body{overflow-x:hidden}`, which already makes body its own scroll
   container; a fixed background on top of that stops the wheel from
   scrolling the document in Chrome (keyboard still works, which makes it easy
   to miss). Verified live on 2026-08-06. The tile is 260px of fractal noise —
   scrolling with the content is imperceptible at this opacity anyway.

   Only background-image is set, so each page's `background: var(--bg)` still
   supplies the colour and dark mode keeps working. */
body {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='260' height='260'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='.035'/%3E%3C/svg%3E");
  background-repeat: repeat;
}
/* Grain reads heavier against a dark field — pull it back. */
html.dark body {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='260' height='260'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='.05'/%3E%3C/svg%3E");
}

/* --- 2. Hero: slow drift -------------------------------------------------
   The hero photo is the only full-bleed image on most pages. A very slow
   drift keeps it alive without ever being perceptible as motion.

   This animates BACKGROUND-POSITION, not transform. Do not "optimise" it to
   a transform: animating transform (with or without will-change) on this
   full-bleed .bg layer promotes it to a compositor layer that swallows wheel
   events, and the whole site stops scrolling by mouse — while keyboard
   scrolling keeps working, so it looks fine in a quick check. Cost a real
   debugging session on 2026-08-06. background-position repaints instead of
   compositing, which is fine at 34s on a single element.

   pointer-events:none is belt-and-braces: this layer is decorative and should
   never be an input target. */
.hero .bg {
  pointer-events: none;
  animation: mit-drift 34s ease-in-out infinite alternate;
}
@keyframes mit-drift {
  from { background-position: 48% 48%; }
  to   { background-position: 54% 53%; }
}

/* --- 3. Hero: orchestrated entrance --------------------------------------
   The signature moment. One sequence on load, then the page is still.
   Eyebrow → headline → lead → actions, reading order, ~90ms apart. */
.hero .lbl,
.hero h1,
.hero .lead,
.hero .row {
  animation: mit-enter .78s cubic-bezier(.2, .7, .2, 1) both;
}
.hero .lbl  { animation-delay: .06s; }
.hero h1    { animation-delay: .15s; }
.hero .lead { animation-delay: .26s; }
.hero .row  { animation-delay: .36s; }
@keyframes mit-enter {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: none; }
}

/* --- 4. Scroll reveals: sequence, not scatter ----------------------------
   The page already has .rise + an IntersectionObserver. It fades everything
   on the same curve at the same instant, which is what makes it feel
   incidental. atmos.js sets --mit-d per element so a group resolves in
   order; the easing here matches the hero so the whole site moves alike. */
.rise {
  transition-property: opacity, transform;
  transition-duration: .62s;
  transition-timing-function: cubic-bezier(.2, .7, .2, 1);
  transition-delay: var(--mit-d, 0ms);
}

/* --- 5. Photo cards: fix the scrim --------------------------------------
   .go{margin-top:auto} pushes the link to the card floor, which floats the
   heading to the TOP — where the original `transparent 30%` scrim gives it
   no protection at all. On the Trades card that put white body copy over a
   bright wall and a hi-vis shirt. Darken both ends, keep the middle open so
   the photograph still reads. */
.b.pic::before {
  background: linear-gradient(
    180deg,
    rgba(13, 15, 9, .62) 0%,
    rgba(13, 15, 9, .34) 34%,
    rgba(13, 15, 9, .52) 62%,
    rgba(13, 15, 9, .88) 100%
  );
}
.b.pic p { color: #eceee4; }

/* --- 6. Cards: hover ------------------------------------------------------
   These are links to real pages, so hover should read as "this moves you
   somewhere": the rule warms to green and the arrow leans that way. */
.b {
  transition: background .18s ease, box-shadow .22s ease, transform .22s ease;
}
.b:hover {
  box-shadow: inset 0 0 0 1px var(--green);
  transform: translateY(-2px);
}
.b .go {
  transition: transform .22s cubic-bezier(.2, .7, .2, 1), color .18s ease;
  will-change: transform;
}
.b:hover .go { transform: translateX(4px); }
.b.pic::before { transition: opacity .3s ease; }
.b.pic:hover::before { opacity: .88; }

/* --- 7. Quality floor ----------------------------------------------------
   The site had no visible keyboard focus at all. Green ring, offset so it
   never sits on top of the thing it is marking. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid var(--green-soft, #6f9136);
  outline-offset: 3px;
  border-radius: 2px;
}

/* --- 8. Reduced motion ---------------------------------------------------
   Everything above collapses to the original static site. Grain stays: it
   is texture, not movement. */
@media (prefers-reduced-motion: reduce) {
  .hero .bg { animation: none; }
  .hero .lbl,
  .hero h1,
  .hero .lead,
  .hero .row { animation: none; opacity: 1; transform: none; }
  .rise { transition-delay: 0ms !important; }
  .b:hover { transform: none; }
  .b:hover .go { transform: none; }
}
