/* SPIRIN TECH front site. One stylesheet, no build step, mobile-first.
   Everything below is plain CSS - edit it directly.

   Theme: orange on black.
   Motion: CSS does the looping/idle animation, static/js/motion.js feeds it
   scroll state through custom properties on <html>:

     --scroll    0..1   how far down the page we are
     --sv       -1..1   smoothed scroll velocity, signed (down = positive)
     [data-dir]         "down" or "up", the current scroll direction
     --p         0..1   per-element scroll progress on [data-scrub] elements
     --py        px     per-element parallax offset on [data-parallax]

   Nothing here requires JS: without it every reveal is simply already
   visible (the .js gate on <html> is what arms the hidden states). */

:root {
  --ink: #08080b;          /* page black */
  --ink-2: #0e0e12;        /* alternating band */
  --panel: #15151a;
  --panel-2: #1b1b21;
  --line: #2a2a31;
  --text: #f5f2ef;
  --muted: #a39d97;
  --orange: #ff6a00;       /* primary */
  --orange-lit: #ff9838;   /* highlight end of the gradient */
  --orange-deep: #b53c00;  /* shadow end */
  --on-orange: #140600;    /* text on an orange fill */
  --sweep: linear-gradient(100deg, var(--orange-deep) 0%, var(--orange) 42%, var(--orange-lit) 100%);
  --radius: 14px;
  --wrap: 72rem;
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  color-scheme: dark;
}

*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  background: var(--ink);
}

body {
  margin: 0;
  background: var(--ink);
  color: var(--text);
  font: 400 1rem/1.65 system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden; /* parallax layers overhang on purpose */
}

h1, h2, h3 { line-height: 1.08; letter-spacing: -0.025em; margin: 0 0 0.6em; font-weight: 700; }
p { margin: 0 0 1rem; }
img, video { max-width: 100%; height: auto; display: block; }

a { color: var(--orange-lit); text-decoration-thickness: 1px; text-underline-offset: 3px; }

:focus-visible { outline: 2px solid var(--orange); outline-offset: 3px; border-radius: 4px; }

.wrap { width: 100%; max-width: var(--wrap); margin: 0 auto; padding: 0 1.25rem; }

.skip {
  position: absolute; left: -9999px;
  background: var(--orange); color: var(--on-orange);
  padding: 0.75rem 1rem; border-radius: 0 0 var(--radius) 0; font-weight: 700; z-index: 20;
}
.skip:focus { left: 0; top: 0; }

/* ==================================================== motion primitives

   Markup contract:
     data-reveal="up|down|left|right|scale|mask"   what it animates from
     data-delay="120"                               ms of stagger
   motion.js adds .is-in when the element first enters the viewport, and
   sets data-from to the scroll direction at that moment, so a section you
   scroll back up into animates downward instead of upward. */

[data-reveal] { --rev-delay: 0ms; transition-delay: var(--rev-delay); }

.js [data-reveal] {
  opacity: 0;
  transition:
    opacity 0.7s var(--ease-out) var(--rev-delay),
    transform 0.9s var(--ease-out) var(--rev-delay);
  will-change: opacity, transform;
}

.js [data-reveal="up"]    { transform: translate3d(0, 2.2rem, 0); }
.js [data-reveal="down"]  { transform: translate3d(0, -2.2rem, 0); }
.js [data-reveal="left"]  { transform: translate3d(-2.2rem, 0, 0); }
.js [data-reveal="right"] { transform: translate3d(2.2rem, 0, 0); }
.js [data-reveal="scale"] { transform: scale(0.94); }

/* Entered while scrolling up: come from the other side. */
.js [data-from="up"][data-reveal="up"] { transform: translate3d(0, -2.2rem, 0); }

.js [data-reveal].is-in {
  opacity: 1;
  transform: none;
  will-change: auto;
}

/* Note: the hidden state deliberately does NOT use clip-path. An element
   clipped to zero height reports itself as not intersecting, so an
   IntersectionObserver watching it never fires and it stays hidden
   forever. Clipping is only ever applied to .word wrappers below, which
   are not the observed elements. */

/* Word-by-word headline reveal. motion.js wraps each word of a
   [data-split] element in .word > span so the span can slide out from
   under a clipped parent - the classic kinetic-type move. */
.js [data-split]:not(.is-split) { opacity: 0; }
[data-split] .word {
  display: inline-block; overflow: hidden;
  vertical-align: bottom; padding-bottom: 0.08em; margin-bottom: -0.08em;
}
[data-split] .word > span {
  display: inline-block;
  transform: translate3d(0, 110%, 0) rotate(3deg);
  transition: transform 1s var(--ease-out) var(--word-delay, 0ms);
}
[data-split].is-in .word > span { transform: none; }

/* --------------------------------------------------------- page chrome */

/* Film grain. One fixed composited layer, deliberately faint. */
.grain {
  position: fixed; inset: -20%;
  z-index: 15; pointer-events: none;
  opacity: 0.05;
  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='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
  animation: grain-shift 0.6s steps(4) infinite;
}
@keyframes grain-shift {
  0%   { transform: translate3d(0, 0, 0); }
  25%  { transform: translate3d(-2%, 1%, 0); }
  50%  { transform: translate3d(1%, -2%, 0); }
  75%  { transform: translate3d(-1%, -1%, 0); }
  100% { transform: translate3d(0, 0, 0); }
}

/* Scroll progress rail across the very top. */
.progress {
  position: fixed; inset: 0 0 auto 0; height: 2px; z-index: 12;
  background: rgba(255, 255, 255, 0.06); pointer-events: none;
}
.progress-fill {
  display: block; height: 100%; transform-origin: 0 50%;
  transform: scaleX(var(--scroll, 0));
  background: var(--sweep);
  box-shadow: 0 0 12px rgba(255, 106, 0, 0.7);
}

/* ------------------------------------------------------------- top bar */

.topbar {
  position: sticky; top: 0; z-index: 10;
  background: rgba(8, 8, 11, 0.72);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid transparent;
  transition: border-color 0.3s ease, background-color 0.3s ease;
}
/* motion.js sets this once you have scrolled off the hero. */
.topbar.is-stuck { border-bottom-color: var(--line); background: rgba(8, 8, 11, 0.92); }

.topbar-inner {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; min-height: 4rem;
}

.brand {
  display: inline-flex; align-items: center; gap: 0.7rem;
  color: var(--text); text-decoration: none; font-weight: 800;
  letter-spacing: 0.06em; font-size: 1.02rem; text-transform: uppercase;
}
.brand-mark { width: 1.6rem; height: 1.6rem; flex: none; display: block; }
.brand-mark svg { width: 100%; height: 100%; overflow: visible; }

.mark-ring {
  fill: none; stroke: var(--orange); stroke-width: 1.6;
  stroke-dasharray: 10 7; opacity: 0.85;
  transform-origin: 50% 50%;
  animation: spin 9s linear infinite;
}
.mark-core {
  fill: var(--orange); transform-origin: 50% 50%;
  animation: spin-back 6s cubic-bezier(0.6, 0, 0.4, 1) infinite;
}
.mark-dot { fill: var(--ink); }
@keyframes spin      { to { transform: rotate(360deg); } }
@keyframes spin-back { to { transform: rotate(-360deg); } }

.brand-thin { font-weight: 300; color: var(--muted); margin-left: 0.3em; letter-spacing: 0.14em; }

/* ------------------------------------------------------------- buttons */

.btn {
  position: relative; isolation: isolate; overflow: hidden;
  display: inline-block;
  background: var(--orange); color: var(--on-orange);
  padding: 0.85rem 1.4rem; border-radius: 10px;
  font-weight: 700; text-decoration: none; border: 1px solid transparent;
  transition: transform 0.25s var(--ease-out), box-shadow 0.25s ease, background-color 0.2s ease;
  box-shadow: 0 0 0 0 rgba(255, 106, 0, 0.45);
}
/* Light sweeping across the fill on hover. */
.btn::after {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(100deg, transparent 20%, rgba(255, 255, 255, 0.55) 50%, transparent 80%);
  transform: translateX(-120%);
  transition: transform 0.6s var(--ease-out);
}
.btn:hover { background: var(--orange-lit); transform: translateY(-2px); box-shadow: 0 10px 30px -8px rgba(255, 106, 0, 0.6); }
.btn:hover::after { transform: translateX(120%); }
.btn:active { transform: translateY(0); }

.btn-ghost {
  background: transparent; color: var(--text); border-color: var(--line);
  box-shadow: none;
}
.btn-ghost:hover { background: var(--panel); border-color: var(--orange); color: var(--orange-lit); box-shadow: none; }

.btn-small { padding: 0.55rem 0.95rem; font-size: 0.9rem; border-radius: 8px; }
.btn-big { font-size: clamp(1.05rem, 3.6vw, 1.4rem); padding: 1.1rem 1.9rem; }

/* ---------------------------------------------------------------- hero */

/* Sized so the primary CTA clears the fold on a laptop: measured at
   1280x757 the button lands around 580px. Raising the type scale or the
   top padding pushes it under, which is the one thing this section
   cannot afford. */
.hero {
  position: relative; isolation: isolate; overflow: hidden;
  padding: clamp(2rem, 6vh, 3.5rem) 0 clamp(2.5rem, 6vh, 4rem);
  border-bottom: 1px solid var(--line);
}

.hero-layers { position: absolute; inset: -10% -5%; z-index: -1; pointer-events: none; }
.hero-layer { position: absolute; inset: 0; transform: translate3d(0, var(--py, 0px), 0); }

/* Grid floor, fading out as it recedes. */
.hero-grid {
  position: absolute; inset: 0;
  background-image:
    linear-gradient(to right, rgba(255, 106, 0, 0.14) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(255, 106, 0, 0.14) 1px, transparent 1px);
  background-size: 3.5rem 3.5rem;
  -webkit-mask-image: radial-gradient(60% 60% at 25% 20%, #000 0%, transparent 75%);
  mask-image: radial-gradient(60% 60% at 25% 20%, #000 0%, transparent 75%);
  animation: grid-drift 24s linear infinite;
}
@keyframes grid-drift {
  to { background-position: 3.5rem 3.5rem, 3.5rem 3.5rem; }
}

/* Two slow orange glows, breathing out of phase. */
.hero-glow {
  position: absolute; border-radius: 50%; filter: blur(10px);
}
.hero-glow-a {
  width: 46rem; height: 28rem; top: -8rem; left: -12rem;
  background: radial-gradient(closest-side, rgba(255, 106, 0, 0.34), transparent 72%);
  animation: breathe 13s ease-in-out infinite;
}
.hero-glow-b {
  width: 34rem; height: 24rem; top: 6rem; right: -10rem;
  background: radial-gradient(closest-side, rgba(255, 152, 56, 0.22), transparent 70%);
  animation: breathe 17s ease-in-out infinite reverse;
}
@keyframes breathe {
  0%, 100% { transform: translate3d(0, 0, 0) scale(1); opacity: 0.85; }
  50%      { transform: translate3d(2.5rem, 1.5rem, 0) scale(1.14); opacity: 1; }
}

/* Radar rings: concentric pulses, like a rotor spinning up. */
.hero-rings { position: absolute; top: 14%; right: 8%; width: 24rem; height: 24rem; }
/* On a phone they sit directly behind the headline, so pull them back. */
@media (max-width: 48rem) {
  .hero-rings { opacity: 0.4; width: 16rem; height: 16rem; top: 44%; right: -3rem; }
}
.hero-rings circle {
  fill: none; stroke: var(--orange); stroke-width: 0.6; opacity: 0;
  transform-origin: 50% 50%;
  animation: ring-out 6s ease-out infinite;
}
.hero-rings circle:nth-child(2) { animation-delay: 1.5s; }
.hero-rings circle:nth-child(3) { animation-delay: 3s; }
.hero-rings circle:nth-child(4) { animation-delay: 4.5s; }
@keyframes ring-out {
  0%   { transform: scale(0.15); opacity: 0; }
  12%  { opacity: 0.55; }
  100% { transform: scale(1); opacity: 0; }
}

/* Drone flight path, drawn on load. Kept faint: it crosses the headline,
   so it has to read as atmosphere rather than as a line through the type. */
.hero-path { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0.55; }
.hero-path path {
  fill: none; stroke: url(#pathGrad); stroke-width: 1.5;
  stroke-linecap: round;
  stroke-dasharray: 1400;
  stroke-dashoffset: 1400;
  animation: draw 3.2s var(--ease-out) 0.3s forwards;
}
@keyframes draw { to { stroke-dashoffset: 0; } }
.hero-path .ping {
  fill: var(--orange-lit);
  offset-path: path("M -40 300 C 220 120, 420 420, 700 180 S 1100 60, 1480 240");
  animation: fly 11s cubic-bezier(0.45, 0, 0.55, 1) infinite;
  filter: drop-shadow(0 0 6px rgba(255, 152, 56, 0.9));
}
@keyframes fly {
  0%   { offset-distance: 0%;   opacity: 0; }
  8%   { opacity: 1; }
  92%  { opacity: 1; }
  100% { offset-distance: 100%; opacity: 0; }
}

.eyebrow {
  display: inline-flex; align-items: center; gap: 0.6rem;
  text-transform: uppercase; letter-spacing: 0.18em;
  font-size: 0.74rem; font-weight: 700; color: var(--orange-lit);
  margin: 0 0 1.4rem;
}
.eyebrow::before {
  content: ""; width: 2.2rem; height: 1px; background: var(--orange);
  transform-origin: 0 50%;
  animation: rule-in 1.1s var(--ease-out) 0.2s both;
}
@keyframes rule-in { from { transform: scaleX(0); } }

.pulse-dot {
  width: 0.45rem; height: 0.45rem; border-radius: 50%; background: var(--orange);
  box-shadow: 0 0 0 0 rgba(255, 106, 0, 0.7);
  animation: pulse 2.4s ease-out infinite;
}
@keyframes pulse {
  0%   { box-shadow: 0 0 0 0 rgba(255, 106, 0, 0.7); }
  70%  { box-shadow: 0 0 0 0.7rem rgba(255, 106, 0, 0); }
  100% { box-shadow: 0 0 0 0 rgba(255, 106, 0, 0); }
}

.hero h1 {
  font-size: clamp(2.15rem, 7vw, 4rem);
  font-weight: 800; letter-spacing: -0.035em;
  max-width: 20ch;
  margin-bottom: 1.1rem;
  text-wrap: balance;
}
.hero h1 .accent {
  display: block;
  background: var(--sweep);
  background-size: 220% 100%;
  -webkit-background-clip: text; background-clip: text; color: transparent;
  animation: sweep 7s ease-in-out infinite;
}
@keyframes sweep {
  0%, 100% { background-position: 0% 50%; }
  50%      { background-position: 100% 50%; }
}

.lede { font-size: clamp(1.03rem, 2.3vw, 1.22rem); color: var(--muted); max-width: 56ch; }
.lede strong { color: var(--text); font-weight: 600; }

.cta-row { display: flex; flex-wrap: wrap; gap: 0.8rem; margin: 1.7rem 0 1.1rem; }
.cta-row .btn { flex: 1 1 auto; text-align: center; }
@media (min-width: 30rem) {
  .cta-row .btn { flex: 0 0 auto; }
}

.hero-note { font-size: 0.92rem; color: var(--muted); max-width: 48ch; margin: 0; }

/* Honest counters - these are facts about how the work is sold, not
   invented client numbers. Keep them that way. */
.stats {
  display: grid; grid-template-columns: repeat(2, 1fr);
  gap: 1.5rem 1rem; margin: 3rem 0 0;
  padding-top: 2rem; border-top: 1px solid var(--line);
  list-style: none;
}
@media (min-width: 46rem) {
  .stats { grid-template-columns: repeat(3, 1fr); gap: 2rem; }
}
.stat-n {
  display: block; font-size: clamp(2rem, 6vw, 2.9rem); font-weight: 800;
  letter-spacing: -0.04em; line-height: 1;
  background: var(--sweep); -webkit-background-clip: text; background-clip: text; color: transparent;
  font-variant-numeric: tabular-nums;
}
.stat-l { display: block; margin-top: 0.5rem; color: var(--muted); font-size: 0.92rem; line-height: 1.4; }

.scroll-cue {
  display: flex; align-items: center; gap: 0.7rem;
  margin: 2.5rem 0 0; color: var(--muted);
  font-size: 0.74rem; text-transform: uppercase; letter-spacing: 0.18em;
}
.scroll-cue::after {
  content: ""; width: 1px; height: 2.4rem;
  background: linear-gradient(to bottom, var(--orange), transparent);
  animation: cue 2.2s ease-in-out infinite;
  transform-origin: 50% 0;
}
@keyframes cue {
  0%, 100% { transform: scaleY(0.35); opacity: 0.4; }
  50%      { transform: scaleY(1); opacity: 1; }
}

/* -------------------------------------------------------------- ticker

   Scrolls on its own; motion.js speeds it up and flips it with the page
   scroll direction, so the band reacts to the wheel. */

.ticker {
  position: relative; overflow: hidden;
  background: var(--orange); color: var(--on-orange);
  padding: 0.85rem 0;
  border-block: 1px solid var(--orange-deep);
  transform: skewY(calc(var(--sv, 0) * -0.25deg));
  transition: transform 0.2s linear;
}
.ticker-track {
  display: flex; width: max-content; gap: 0;
  transform: translate3d(var(--tx, 0px), 0, 0);
  will-change: transform;
}
.ticker-group {
  display: flex; align-items: center; gap: 2.5rem; padding-right: 2.5rem;
  font-weight: 700; text-transform: uppercase; letter-spacing: 0.1em;
  font-size: 0.82rem; white-space: nowrap;
}
.ticker-group span { display: inline-flex; align-items: center; gap: 2.5rem; }
.ticker-group span::after {
  content: ""; width: 0.35rem; height: 0.35rem; border-radius: 50%;
  background: var(--on-orange); opacity: 0.55;
}

/* ------------------------------------------------------------ sections */

.section { position: relative; padding: clamp(3.5rem, 9vh, 6rem) 0; }
.section-alt { background: var(--ink-2); border-bottom: 1px solid var(--line); }

.section-head { max-width: 58ch; margin-bottom: 2.5rem; }
.section h2 {
  font-size: clamp(1.75rem, 5.6vw, 2.9rem); font-weight: 800; letter-spacing: -0.03em;
}
.section-kicker {
  display: block; text-transform: uppercase; letter-spacing: 0.18em;
  font-size: 0.72rem; font-weight: 700; color: var(--orange);
  margin-bottom: 0.9rem;
}
.section-lede { color: var(--muted); max-width: 58ch; margin: 0; }

/* ------------------------------------------------------- service cards */

.cards { display: grid; gap: 1rem; }
@media (min-width: 46rem) {
  .cards { grid-template-columns: repeat(2, 1fr); gap: 1.25rem; }
}

.card {
  position: relative; isolation: isolate; overflow: hidden;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.6rem;
  transition: transform 0.4s var(--ease-out), border-color 0.3s ease, background-color 0.3s ease;
}
/* Orange wash that rises out of the bottom edge on hover. */
.card::before {
  content: ""; position: absolute; inset: auto 0 0 0; height: 100%; z-index: -1;
  background: radial-gradient(120% 90% at 50% 120%, rgba(255, 106, 0, 0.22), transparent 70%);
  opacity: 0; transition: opacity 0.4s ease;
}
.card:hover { border-color: rgba(255, 106, 0, 0.5); transform: translateY(-4px); background: var(--panel-2); }
.card:hover::before { opacity: 1; }

.card-top { display: flex; align-items: center; justify-content: space-between; gap: 1rem; margin-bottom: 1.1rem; }
.card-ix {
  font-size: 0.74rem; font-weight: 700; letter-spacing: 0.16em;
  color: var(--orange); font-variant-numeric: tabular-nums;
}
.card-icon { width: 2.6rem; height: 2.6rem; flex: none; }
.card-icon svg { width: 100%; height: 100%; }
.card-icon [stroke] {
  fill: none; stroke: var(--orange); stroke-width: 1.6;
  stroke-linecap: round; stroke-linejoin: round;
  stroke-dasharray: 120; stroke-dashoffset: 120;
  transition: stroke-dashoffset 1.3s var(--ease-out) 0.15s;
}
/* The line art draws itself when the card scrolls into view. */
.card.is-in .card-icon [stroke] { stroke-dashoffset: 0; }

.card h3 { font-size: 1.2rem; margin-bottom: 0.55rem; }
.card p { color: var(--muted); margin: 0; font-size: 0.98rem; }

/* -------------------------------------------------------------- steps

   The rail between the step numbers is scrubbed by scroll position:
   --p comes from motion.js and drives scaleY, so the line draws down
   as you read and retracts if you scroll back up. */

.process { position: relative; }
.steps { list-style: none; margin: 0; padding: 0; display: grid; gap: 2.25rem; position: relative; }

/* Wide screens: the heading parks on the left while the timeline scrolls
   past it on the right. Narrow screens stay stacked. */
@media (min-width: 64rem) {
  .process .wrap { display: grid; grid-template-columns: 20rem 1fr; gap: 0 4rem; align-items: start; }
  .process .section-head { position: sticky; top: 6.5rem; margin-bottom: 0; }
}

/* ::before is the unlit track, ::after is the orange fill. Pseudo-elements
   rather than markup, so the <ol> contains nothing but <li>. */
.steps::before,
.steps::after {
  content: ""; position: absolute; left: 1.05rem; top: 0.4rem; bottom: 0.4rem; width: 2px;
}
.steps::before { background: var(--line); }
.steps::after {
  background: var(--sweep);
  transform: scaleY(var(--p, 0)); transform-origin: 50% 0;
  box-shadow: 0 0 14px rgba(255, 106, 0, 0.55);
}

.steps li { display: flex; gap: 1.15rem; align-items: flex-start; position: relative; }

.step-n {
  flex: none; position: relative; z-index: 1;
  width: 2.1rem; height: 2.1rem;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--ink);
  border: 1px solid var(--line);
  color: var(--muted);
  font-weight: 800; font-size: 0.95rem;
  font-variant-numeric: tabular-nums;
  transition: color 0.4s ease, border-color 0.4s ease, box-shadow 0.4s ease, transform 0.5s var(--ease-out);
}
.section-alt .step-n { background: var(--ink-2); }
/* Lights up as its step arrives. */
.steps li.is-in .step-n {
  color: var(--on-orange); background: var(--orange); border-color: var(--orange);
  box-shadow: 0 0 0 5px rgba(255, 106, 0, 0.14);
  transform: scale(1.06);
}
.steps h3 { font-size: 1.12rem; margin-bottom: 0.35rem; }
.steps p { color: var(--muted); margin: 0; font-size: 0.98rem; max-width: 56ch; }

/* Deliberately one column at every width: the rail only reads as a
   timeline if the four steps sit on a single line down the page. */

/* ------------------------------------------------------------- gallery */

.gallery { display: grid; gap: 1rem; margin-top: 2rem; }
@media (min-width: 38rem) {
  .gallery { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 62rem) {
  .gallery { grid-template-columns: repeat(3, 1fr); }
}

.shot {
  margin: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--panel);
  transition: transform 0.4s var(--ease-out), border-color 0.3s ease;
}
.shot:hover { transform: translateY(-4px); border-color: rgba(255, 106, 0, 0.5); }
.shot img, .shot video {
  width: 100%; aspect-ratio: 4 / 3; object-fit: cover; background: #101014;
  transition: transform 0.7s var(--ease-out), filter 0.5s ease;
  filter: saturate(0.92);
}
.shot:hover img, .shot:hover video { transform: scale(1.04); filter: saturate(1.08); }
.shot figcaption { padding: 0.85rem 1rem; font-size: 0.88rem; color: var(--muted); }
.shot figcaption strong { color: var(--text); font-weight: 650; display: block; }

/* ------------------------------------------------------------- contact */

/* isolation matters here: it makes this section its own stacking context,
   which is what lets the z-index:-1 glow paint above the section's own
   background instead of disappearing behind it. */
.section-contact {
  position: relative; isolation: isolate; overflow: hidden; text-align: center;
  background: var(--ink);
  border-top: 1px solid var(--line);
}
.section-contact .section-head { margin-inline: auto; }
.section-contact .section-lede { margin-inline: auto; }

/* Slow orange sunrise behind the call to action. The wrapper takes the
   parallax offset and the pseudo-element takes the loop, so the two
   transforms never fight over the same property. */
.contact-glow {
  position: absolute; left: 50%; bottom: -24rem; width: 54rem; height: 54rem;
  margin-left: -27rem; z-index: -1; pointer-events: none;
  transform: translate3d(0, var(--py, 0px), 0);
}
.contact-glow::before {
  content: ""; position: absolute; inset: 0; border-radius: 50%;
  background: radial-gradient(closest-side, rgba(255, 106, 0, 0.3), transparent 70%);
  animation: breathe 15s ease-in-out infinite;
}

.contact-main { position: relative; display: inline-block; margin: 2.4rem 0 1.5rem; }
/* Ring that keeps expanding out of the button. */
.contact-main::before {
  content: ""; position: absolute; inset: -0.4rem; border-radius: 14px;
  border: 1px solid var(--orange); opacity: 0;
  animation: ring-pop 3s ease-out infinite;
}
@keyframes ring-pop {
  0%   { transform: scale(0.96); opacity: 0.6; }
  100% { transform: scale(1.1); opacity: 0; }
}

.contact-note { color: var(--muted); font-size: 0.92rem; max-width: 46ch; margin-inline: auto; }

/* --------------------------------------------------- contact form

   The enquiry form writes straight into the lead pipeline. Left-aligned
   inside a centred section on purpose: labels and errors above a field are
   far easier to scan than centred ones, and this is the one place on the
   page where the visitor has work to do. */

.contact-form {
  position: relative;
  max-width: 44rem;
  margin: 2.6rem auto 1.5rem;
  padding: 1.8rem 1.6rem 1.5rem;
  text-align: left;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
/* Orange hairline along the top edge, picking up the section's accent. */
.contact-form::before {
  content: ""; position: absolute; inset: 0 0 auto; height: 2px;
  border-radius: var(--radius) var(--radius) 0 0;
  background: var(--sweep);
}

.field-grid { display: grid; gap: 1.1rem 1.2rem; grid-template-columns: 1fr; }
@media (min-width: 40rem) {
  .field-grid { grid-template-columns: 1fr 1fr; }
  /* The long fields get the full width. */
  .field-business_name, .field-message { grid-column: 1 / -1; }
}

.field { margin: 0; display: flex; flex-direction: column; gap: 0.4rem; }
.field label {
  font-size: 0.82rem; font-weight: 600; letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--muted);
}
.field .req { color: var(--orange); }

.field input,
.field textarea {
  width: 100%;
  font: inherit;
  color: var(--text);
  background: var(--ink);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 0.7rem 0.85rem;
  transition: border-color 0.2s var(--ease-out), box-shadow 0.2s var(--ease-out);
}
.field textarea { resize: vertical; min-height: 6rem; }
.field input::placeholder,
.field textarea::placeholder { color: #6c6761; }

.field input:focus,
.field textarea:focus {
  outline: none;
  border-color: var(--orange);
  box-shadow: 0 0 0 3px rgba(255, 106, 0, 0.18);
}

.field.has-error input,
.field.has-error textarea { border-color: #e2583c; }

.field-error { color: #ff8f78; font-size: 0.85rem; margin: 0; }

.form-actions { margin: 1.5rem 0 0.6rem; }
.form-note { color: var(--muted); font-size: 0.85rem; margin: 0; }

.contact-alt { color: var(--muted); font-size: 0.95rem; margin: 0 0 1.2rem; }

/* Server-side messages (submitted, or something needs fixing). */
.flash { list-style: none; margin: 1.8rem auto 0; padding: 0; max-width: 44rem; text-align: left; }
.flash li {
  padding: 0.8rem 1rem; border-radius: 10px; margin-bottom: 0.6rem;
  border: 1px solid var(--line); background: var(--panel-2); font-size: 0.95rem;
}
.flash li.flash-success { border-color: rgba(255, 106, 0, 0.5); }
.flash li.flash-success::before { content: "✓ "; color: var(--orange); font-weight: 700; }
.flash li.flash-error { border-color: rgba(226, 88, 60, 0.6); }

/* -------------------------------------------------------------- footer */

.footer { border-top: 1px solid var(--line); padding: 2rem 0; font-size: 0.88rem; color: var(--muted); }
.footer-inner { display: flex; flex-wrap: wrap; gap: 0.5rem 1.5rem; justify-content: space-between; align-items: center; }

/* ============================================ reduced motion / no-JS

   One switch. Everything that loops stops, everything that slides in is
   simply there, and nothing depends on scroll position to be readable. */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *, *::before, *::after {
    animation-duration: 0.001s !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001s !important;
  }

  .js [data-reveal] { opacity: 1; transform: none; }
  .js [data-split]:not(.is-split) { opacity: 1; }
  [data-split] .word > span { transform: none; }

  .grain { display: none; }
  .ticker { transform: none; }
  .ticker-track { transform: none; }
  .steps::after { transform: scaleY(1); }
  .steps li .step-n {
    color: var(--on-orange); background: var(--orange); border-color: var(--orange);
  }
  .card .card-icon [stroke] { stroke-dashoffset: 0; }
  .hero-path path { stroke-dashoffset: 0; }
  .hero-path .ping { display: none; }
  .hero-rings { display: none; }
}
