/* ============================================================
   ORiQ — oriqdryfire.com
   Tokens, then layout, then components. Dark theme locked.
   Radius is 0 everywhere by design: the wordmark and the
   machined/printed parts are all square geometry.
   ============================================================ */

/* Archivo, not Space Grotesk: an industrial grotesque with squared terminals
   that sits closer to the geometry of the ORiQ wordmark, and one that has not
   been worn flat by every other product page. */
@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/archivo-400.woff2') format('woff2');
}
@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('../fonts/archivo-500.woff2') format('woff2');
}
@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('../fonts/archivo-700.woff2') format('woff2');
}
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/jetbrains-mono-400.woff2') format('woff2');
}
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('../fonts/jetbrains-mono-500.woff2') format('woff2');
}

:root {
  --bg:        #0a0a0a;
  --bg-raise:  #121212;
  --bg-sunk:   #060606;
  --plate:     #e9e9e9;  /* light tile for the black hardware shots — see .tile-photo */
  --plate-ink: #1a1a1a;
  --line:      rgba(255, 255, 255, 0.10);
  --line-firm: rgba(255, 255, 255, 0.20);
  --ink:       #f5f5f5;
  --ink-mute:  #999999;
  --accent:    #c6ff00;

  --sans: 'Archivo', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
  --mono: 'JetBrains Mono', ui-monospace, 'SFMono-Regular', Menlo, Consolas, monospace;

  --shell: 1400px;
  --gut:   clamp(1.25rem, 4vw, 3.5rem);
  --band:  clamp(4.5rem, 9vw, 8rem);   /* vertical section rhythm */

  /* The built-in CSS easings are too weak to read as intentional. These are the
     stronger custom curves; ease-in is never used, because starting slow makes
     the interface feel sluggish at exactly the moment the user is watching. */
  --ease: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
  --ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);

  /* Feedback stays under 200ms. Narrative reveals are allowed to be slower. */
  --t-press: 140ms;
  --t-hover: 180ms;
  --t-reveal: 560ms;
  --t-wipe: 820ms;
}

*,
*::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(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 1rem;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img,
svg { display: block; max-width: 100%; }

img { height: auto; }

a { color: inherit; }

::selection {
  background: var(--accent);
  color: #0a0a0a;
}

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

/* ---------- type ---------- */

h1, h2, h3, p, ul, ol, figure, blockquote { margin: 0; }

/* Capped at 3.5rem rather than 4.5rem: the hero headline sits in a half-width
   column, and above this size "The digital shooting range." breaks to three
   lines. Headline stays at two lines maximum. */
.h1 {
  font-size: clamp(2.5rem, 4.6vw, 3.5rem);
  font-weight: 700;
  line-height: 1.04;
  letter-spacing: -0.035em;
}

.h2 {
  font-size: clamp(1.875rem, 3.8vw, 3rem);
  font-weight: 700;
  line-height: 1.06;
  letter-spacing: -0.028em;
}

.h3 {
  font-size: 1.0625rem;
  font-weight: 500;
  line-height: 1.35;
  letter-spacing: -0.01em;
}

.lede {
  font-size: clamp(1.0625rem, 1.45vw, 1.25rem);
  line-height: 1.55;
  color: var(--ink-mute);
  max-width: 46ch;
}

.body {
  color: var(--ink-mute);
  max-width: 62ch;
}

/* Spacing utilities, kept deliberately few. They exist so the markup can stay
   free of inline style attributes, which would force 'unsafe-inline' into the
   style-src directive of the CSP in _headers. */
.mt-md { margin-top: 1.25rem; }
.mt-lg { margin-top: 1.5rem; }

.label {
  font-family: var(--mono);
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent);
}

.label--mute { color: var(--ink-mute); }

.num {
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.1em;
  color: var(--ink-mute);
}

/* ---------- shell ---------- */

.shell {
  max-width: var(--shell);
  margin-inline: auto;
  padding-inline: var(--gut);
}

.band { padding-block: var(--band); }

/* The nav is sticky, so an anchored section would otherwise land underneath it.
   Matters more now that the primary hero CTA is an in-page anchor. */
main > section[id] { scroll-margin-top: 84px; }

.band--line { border-top: 1px solid var(--line); }

.skip {
  position: absolute;
  left: -9999px;
}
.skip:focus {
  left: var(--gut);
  top: 0.75rem;
  z-index: 100;
  padding: 0.6rem 1rem;
  background: var(--accent);
  color: #0a0a0a;
  font-family: var(--mono);
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
}

/* ---------- nav ---------- */

.nav {
  position: sticky;
  top: 0;
  z-index: 50;
  height: 68px;
  display: flex;
  align-items: center;
  border-bottom: 1px solid var(--line);
  background: rgba(10, 10, 10, 0.82);
  backdrop-filter: blur(14px);
}

@supports not (backdrop-filter: blur(1px)) {
  .nav { background: #0a0a0a; }
}

.nav__inner {
  display: flex;
  align-items: center;
  gap: clamp(1rem, 3vw, 2.5rem);
  width: 100%;
}

.nav__mark {
  display: flex;
  align-items: center;
  color: var(--accent);
  text-decoration: none;
  flex: none;
}

.nav__mark img { height: 24px; width: auto; }

.nav__cta--end { margin-left: auto; }

.nav__links {
  display: flex;
  align-items: center;
  gap: clamp(1rem, 2.4vw, 2rem);
  margin-left: auto;
  list-style: none;
  padding: 0;
}

.nav__links a {
  font-size: 0.875rem;
  color: var(--ink-mute);
  text-decoration: none;
  white-space: nowrap;
  transition: color 0.2s var(--ease);
}

.nav__links a:hover { color: var(--ink); }

@media (max-width: 720px) {
  .nav__links { display: none; }
  /* The links carry the margin-left:auto that pushes this group right, so with
     them hidden the CTA has to take that job over. */
  .nav__inner .btn { margin-left: auto; }
}

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

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 0.8125rem 1.375rem;
  font-family: var(--mono);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  border: 1px solid transparent;
  cursor: pointer;
  transition: transform 0.18s var(--ease), background-color 0.18s var(--ease),
              border-color 0.18s var(--ease), color 0.18s var(--ease);
}

.btn:hover { transform: translateY(-1px); }
.btn:active { transform: scale(0.985); }

.btn--solid {
  background: var(--accent);
  color: #0a0a0a;
}

.btn--solid:hover { background: #d4ff3d; }

.btn--ghost {
  background: transparent;
  color: var(--ink);
  border-color: var(--line-firm);
}

.btn--ghost:hover { border-color: var(--ink); }

.btn--sm { padding: 0.5625rem 0.9375rem; }

/* ---------- 1. hero ---------- */

.hero {
  display: grid;
  grid-template-columns: minmax(0, 6fr) minmax(0, 6fr);
  gap: clamp(2rem, 5vw, 4.5rem);
  align-items: center;
  padding-top: clamp(3rem, 6vw, 6rem);
  padding-bottom: clamp(3.5rem, 7vw, 6.5rem);
}

.hero__copy > * + * { margin-top: 1.5rem; }

.hero__title { margin-top: 1.125rem; }

.hero__cta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
}

.hero__media { position: relative; }

/* Clips the slight overscale that gives the parallax room to move without
   exposing an edge. Sits on its own wrapper rather than on the figure, so the
   overscale cannot bleed down over the caption. */
.hero__frame {
  overflow: hidden;
  line-height: 0;
}

.hero__media img {
  width: 100%;
  filter: contrast(1.04) saturate(0.94);
}

.hero__caption {
  margin-top: 0.875rem;
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.06em;
  color: var(--ink-mute);
}

@media (max-width: 900px) {
  .hero {
    grid-template-columns: 1fr;
    gap: 2.25rem;
  }
}

/* ---------- 2. capability: 1 wide + 2 stacked ---------- */

.cap {
  display: grid;
  grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  gap: clamp(1.75rem, 3.5vw, 3rem);
  margin-top: clamp(2.25rem, 4vw, 3.5rem);
}

.cap__lead {
  border-top: 2px solid var(--accent);
  padding-top: 1.5rem;
}

.cap__lead .h3 { font-size: clamp(1.25rem, 2.2vw, 1.75rem); }

.cap__rest {
  display: grid;
  gap: clamp(1.5rem, 2.5vw, 2rem);
  align-content: start;
}

.cap__item {
  border-top: 1px solid var(--line);
  padding-top: 1.25rem;
}

.cap__item .h3 { margin-bottom: 0.5rem; }

.cap__lead .h3 { margin-bottom: 0.75rem; }

@media (max-width: 860px) {
  .cap { grid-template-columns: 1fr; }
}

/* ---------- 3. hardware: exactly five cells ---------- */

.rig {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: 1px;
  margin-top: clamp(2.25rem, 4vw, 3.5rem);
  background: var(--line);
  border: 1px solid var(--line);
}

.tile {
  background: var(--bg);
  padding: clamp(1.25rem, 2vw, 1.75rem);
  display: flex;
  flex-direction: column;
  gap: 1rem;
  min-width: 0;
}

.tile--a { grid-column: span 5; }
.tile--b { grid-column: span 4; }
.tile--c { grid-column: span 3; }
.tile--d { grid-column: span 7; }
.tile--e { grid-column: span 5; }

.tile__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
}

.tile__name {
  font-family: var(--mono);
  font-size: 0.8125rem;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.tile__body {
  font-size: 0.9375rem;
  line-height: 1.55;
  color: var(--ink-mute);
}

.tile__body--lead { margin-top: 1rem; }

/* The hardware is matte black. On a near-black page it would read as a void,
   so photo tiles get a light plate. This is legibility, not decoration —
   the page theme itself stays dark throughout. */
.tile__plate {
  margin-top: auto;
  background: var(--plate);
  overflow: hidden;
}

.tile__plate img {
  width: 100%;
  transition: transform 0.5s var(--ease);
}

.tile:hover .tile__plate img { transform: scale(1.03); }

.tile--spec {
  justify-content: space-between;
  background: var(--bg-raise);
}

.tile__spec {
  margin-top: auto;
  display: grid;
  gap: 0.5rem;
  list-style: none;
  padding: 0;
}

.tile__spec li {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding-top: 0.5rem;
  border-top: 1px solid var(--line);
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-mute);
}

.tile__spec b {
  color: var(--ink);
  font-weight: 500;
  text-align: right;
}

@media (max-width: 1040px) {
  .tile--a, .tile--b, .tile--d { grid-column: span 6; }
  .tile--c, .tile--e { grid-column: span 6; }
}

@media (max-width: 680px) {
  .tile--a, .tile--b, .tile--c, .tile--d, .tile--e { grid-column: span 12; }
}

/* ---------- 4. how it works ---------- */

.flow {
  margin-top: clamp(2.25rem, 4vw, 3.5rem);
  display: grid;
  gap: clamp(2rem, 4vw, 3rem);
}

.flow__media {
  position: relative;
  background: var(--bg-sunk);
}

.flow__media img {
  width: 100%;
  filter: contrast(1.04) saturate(0.94);
}

.flow__steps {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1px;
  background: var(--line);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

.step {
  background: var(--bg);
  padding: clamp(1.375rem, 2.2vw, 2rem);
  display: grid;
  gap: 0.625rem;
  align-content: start;
}

.step__n {
  font-family: var(--mono);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.12em;
  color: var(--accent);
}

.step p {
  font-size: 0.9375rem;
  color: var(--ink-mute);
}

@media (max-width: 780px) {
  .flow__steps { grid-template-columns: 1fr; }
}

/* ---------- 5. status ---------- */

/* Background comes from the surface-* class in the markup, not from here. */
.status__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(2rem, 5vw, 4rem);
  align-items: start;
}

.status__note {
  margin-top: 1.5rem;
  padding: 1.125rem 1.25rem;
  border-left: 2px solid var(--accent);
  background: rgba(198, 255, 0, 0.04);
  font-size: 0.9375rem;
  color: var(--ink);
}

.track {
  list-style: none;
  padding: 0;
  display: grid;
  gap: 0;
}

.track li {
  display: grid;
  grid-template-columns: 7.5rem minmax(0, 1fr);
  gap: 1.25rem;
  padding-block: 1.125rem;
  border-top: 1px solid var(--line);
  font-size: 0.9375rem;
}

.track li:last-child { border-bottom: 1px solid var(--line); }

.track__state {
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-mute);
  padding-top: 0.2rem;
}

.track__state--now { color: var(--accent); }

@media (max-width: 860px) {
  .status__grid { grid-template-columns: 1fr; }
  .track li { grid-template-columns: 1fr; gap: 0.375rem; }
}

/* ---------- 6. contact + verification ---------- */

.reach {
  display: grid;
  grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  gap: clamp(2rem, 5vw, 4rem);
  align-items: start;
}

.reach__mail {
  display: inline-block;
  margin-top: 1.25rem;
  font-size: clamp(1.375rem, 3.4vw, 2.375rem);
  font-weight: 700;
  letter-spacing: -0.03em;
  color: var(--accent);
  text-decoration: none;
  border-bottom: 2px solid transparent;
  transition: border-color 0.2s var(--ease);
  word-break: break-word;
}

.reach__mail:hover { border-bottom-color: var(--accent); }

.reach__aside {
  border: 1px solid var(--line);
  padding: clamp(1.25rem, 2vw, 1.75rem);
  background: var(--bg-raise);
}

.proof {
  list-style: none;
  padding: 0;
  margin-top: 1rem;
  display: grid;
}

.proof li { border-top: 1px solid var(--line); }

.proof a {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding-block: 0.9375rem;
  font-size: 0.9375rem;
  text-decoration: none;
  color: var(--ink);
  transition: color 0.2s var(--ease);
}

.proof a:hover { color: var(--accent); }

.proof span {
  font-family: var(--mono);
  font-size: 0.625rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-mute);
}

@media (max-width: 860px) {
  .reach { grid-template-columns: 1fr; }
}

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

.foot {
  border-top: 1px solid var(--line);
  padding-block: clamp(2.5rem, 5vw, 4rem) 2rem;
  background: var(--bg-sunk);
}

.foot__grid {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(1.75rem, 4vw, 3rem);
}

.foot__mark {
  color: var(--accent);
  display: block;
}

.foot__mark img { height: 26px; width: auto; }

.foot__tag {
  margin-top: 1rem;
  font-size: 0.875rem;
}

.foot h2 {
  font-family: var(--mono);
  font-size: 0.625rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-mute);
  margin-bottom: 0.875rem;
}

.foot ul {
  list-style: none;
  padding: 0;
  display: grid;
  gap: 0.5rem;
}

.foot a {
  font-size: 0.875rem;
  color: var(--ink-mute);
  text-decoration: none;
  transition: color 0.2s var(--ease);
}

.foot a:hover { color: var(--ink); }

.foot__legal {
  margin-top: clamp(2.25rem, 4vw, 3.5rem);
  padding-top: 1.5rem;
  border-top: 1px solid var(--line);
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.5rem;
  justify-content: space-between;
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.05em;
  color: var(--ink-mute);
}

/* Modifier must follow the base rule to win at equal specificity. */
.foot__legal--bare {
  margin-top: 0;
  padding-top: 0;
  border-top: 0;
}

@media (max-width: 780px) {
  .foot__grid { grid-template-columns: 1fr 1fr; }
  .foot__brand { grid-column: 1 / -1; }
}

/* ---------- legal / prose pages ---------- */

.doc {
  max-width: 68ch;
  padding-block: clamp(3rem, 6vw, 5rem) var(--band);
}

.doc__title { margin-block: 1rem 0.75rem; }

.doc__meta {
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-mute);
}

.doc h2 {
  font-size: 1.1875rem;
  font-weight: 700;
  letter-spacing: -0.015em;
  margin-top: 2.75rem;
  margin-bottom: 0.75rem;
}

.doc h3 {
  font-size: 1rem;
  font-weight: 500;
  margin-top: 1.75rem;
  margin-bottom: 0.5rem;
}

.doc p,
.doc li {
  color: var(--ink-mute);
}

.doc p + p { margin-top: 1rem; }

.doc ul,
.doc ol {
  margin-top: 0.875rem;
  padding-left: 1.25rem;
  display: grid;
  gap: 0.5rem;
}

.doc a {
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.doc strong { color: var(--ink); }

.callout {
  margin-top: 1.5rem;
  padding: 1.25rem 1.375rem;
  border: 1px solid var(--line);
  border-left: 2px solid var(--accent);
  background: var(--bg-raise);
}

.callout p { color: var(--ink); }

/* ---------- 404 ---------- */

.oops {
  min-height: calc(100dvh - 68px);
  display: grid;
  align-content: center;
  gap: 1.25rem;
  padding-block: var(--band);
}

/* ---------- scroll reveal ----------
   Only armed when JS is present, so the page is fully readable
   if the script never runs. */

.js .reveal {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 0.6s var(--ease), transform 0.6s var(--ease);
}

.js .reveal.is-in {
  opacity: 1;
  transform: none;
}

/* The safety-net state, applied by site.js when IntersectionObserver never
   fires. It must not rely on the transition: in a document that is hidden at
   load time the animation timeline is paused, so a transitioned opacity would
   stay stuck at 0. This sets the end state outright. */
.js .reveal.is-shown {
  opacity: 1;
  transform: none;
  transition: none;
}

/* ============================================================
   SURFACES

   Section backgrounds vary to break the flat single-tone scroll, but every
   value stays inside the same near-black family so the page never reads as
   two different websites. The rhythm is
   hero(base) product(base) hardware(raise) flow(sunk) status(raise) contact(base),
   so no two adjacent sections share a tone.
   ============================================================ */

.surface-raise { background: var(--bg-raise); }
.surface-sunk  { background: var(--bg-sunk); }

/* Triangular lattice lifted from the brand background asset. Two crossed
   hairline gradients approximate it without shipping another image. */
.tex { position: relative; isolation: isolate; }

.tex::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-image:
    repeating-linear-gradient(60deg,  rgba(255, 255, 255, 0.032) 0 1px, transparent 1px 44px),
    repeating-linear-gradient(-60deg, rgba(255, 255, 255, 0.032) 0 1px, transparent 1px 44px);
  -webkit-mask-image: radial-gradient(120% 90% at 50% 0%, #000 15%, transparent 78%);
          mask-image: radial-gradient(120% 90% at 50% 0%, #000 15%, transparent 78%);
}

.tex > .shell,
.tex > * { position: relative; z-index: 1; }

/* A wide, low wash rather than a glow blob: reads as light spilling in from
   the frame edge, not as a decorative orb behind the headline. */
.hero-wrap { position: relative; overflow: hidden; }

.hero-wrap::after {
  content: '';
  position: absolute;
  right: -8%;
  bottom: -45%;
  width: 62%;
  height: 90%;
  z-index: 0;
  pointer-events: none;
  background: radial-gradient(closest-side, rgba(198, 255, 0, 0.055), transparent 72%);
}

.hero-wrap > .shell { position: relative; z-index: 1; }

/* ============================================================
   MOTION

   Raised to a mid intensity on request. Every rule here follows the same
   constraints: strong custom easing, never ease-in, feedback under 200ms,
   only transform / opacity / clip-path animated, hover gated behind a fine
   pointer, and reduced motion keeps the fades while dropping the movement.
   ============================================================ */

/* --- staggered entrance ---------------------------------------------------
   Reuses the reveal mechanism, so the safety net in site.js covers these too.
   Delays stay short: long stagger makes an interface feel slow. */

.js .reveal .stagger {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity var(--t-reveal) var(--ease-out),
              transform var(--t-reveal) var(--ease-out);
}

.js .reveal.is-in .stagger {
  opacity: 1;
  transform: none;
}

.js .reveal.is-in .stagger:nth-child(1) { transition-delay: 0ms; }
.js .reveal.is-in .stagger:nth-child(2) { transition-delay: 70ms; }
.js .reveal.is-in .stagger:nth-child(3) { transition-delay: 140ms; }
.js .reveal.is-in .stagger:nth-child(4) { transition-delay: 210ms; }
.js .reveal.is-in .stagger:nth-child(5) { transition-delay: 280ms; }

.js .reveal.is-shown .stagger {
  opacity: 1;
  transform: none;
  transition: none;
}

/* --- hero image wipe -----------------------------------------------------
   clip-path from the bottom edge, so the photo builds rather than fades in.
   ease-in-out because the element is moving on screen, not entering from
   outside it. */

.js .reveal .hero__media img {
  clip-path: inset(0 0 100% 0);
  transition: clip-path var(--t-wipe) var(--ease-in-out);
}

.js .reveal.is-in .hero__media img {
  clip-path: inset(0 0 0 0);
  transition-delay: 120ms;
}

.js .reveal.is-shown .hero__media img {
  clip-path: inset(0 0 0 0);
  transition: none;
}

/* The parallax offset is written to this variable by site.js, so the
   transform itself stays a single composited property. */
.hero__media {
  --par-x: 0px;
  --par-y: 0px;
}

.hero__media img {
  transform: translate3d(var(--par-x), var(--par-y), 0) scale(1.03);
  will-change: transform;
}

/* --- nav ----------------------------------------------------------------- */

/* At the very top the nav has nothing to separate from, so the rule only
   appears once the page has actually moved. State indication, not decoration. */
.nav {
  border-bottom-color: transparent;
  transition: border-color var(--t-hover) ease,
              background-color var(--t-hover) ease;
}

.nav.is-scrolled { border-bottom-color: var(--line); }

.nav__progress {
  position: absolute;
  left: 0;
  bottom: -1px;
  height: 2px;
  width: 100%;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: 0 50%;
  pointer-events: none;
}

.nav__links a {
  position: relative;
}

.nav__links a::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: -5px;
  height: 1px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform var(--t-hover) var(--ease-out);
}

/* --- hover states --------------------------------------------------------
   Gated: touch devices fire hover on tap, which produces states that stick. */

@media (hover: hover) and (pointer: fine) {
  .nav__links a:hover::after { transform: scaleX(1); }

  .tile { transition: background-color var(--t-hover) ease; }
  .tile:hover { background: var(--bg-raise); }
  .surface-raise .tile:hover { background: #171717; }

  /* The accent rule draws the eye to the tile under the cursor. */
  .tile::after {
    content: '';
    position: absolute;
    inset: 0 auto auto 0;
    height: 2px;
    width: 100%;
    background: var(--accent);
    transform: scaleX(0);
    transform-origin: 0 50%;
    transition: transform 220ms var(--ease-out);
  }

  .tile:hover::after { transform: scaleX(1); }

  .cap__item { transition: border-color var(--t-hover) ease; }
  .cap__item:hover { border-top-color: var(--accent); }

  .step { transition: background-color var(--t-hover) ease; }
  .step:hover { background: var(--bg-raise); }
  .step:hover .step__n { color: var(--accent); }

  .track li { transition: background-color var(--t-hover) ease; }
  .track li:hover { background: rgba(255, 255, 255, 0.025); }

  .proof a:hover .proof__go { transform: translateX(4px); }

  .reach__mail:hover::after { transform: scaleX(1); }
}

.tile { position: relative; }

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

/* Replaces the border-colour swap with an underline that grows from the left,
   so the direction of the gesture matches the reading direction. */
.reach__mail {
  position: relative;
  border-bottom: 0;
}

.reach__mail::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0.05em;
  height: 2px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform 260ms var(--ease-out);
}

.proof__go {
  display: inline-block;
  color: var(--ink-mute);
  transition: transform var(--t-hover) var(--ease-out),
              color var(--t-hover) ease;
}

.proof a:hover .proof__go { color: var(--accent); }

/* --- press feedback ------------------------------------------------------
   Buttons scale down on press so the interface confirms it heard the click.
   Kept subtle, and faster than the hover so release feels snappy. */

.btn {
  transition: transform var(--t-press) var(--ease-out),
              background-color var(--t-hover) ease,
              border-color var(--t-hover) ease,
              color var(--t-hover) ease;
}

.btn:active { transform: scale(0.97); }

/* ============================================================
   REDUCED MOTION
   Fewer and gentler, not zero: the fades that aid comprehension stay,
   everything that moves is removed.
   ============================================================ */

@media (prefers-reduced-motion: reduce) {
  .js .reveal {
    opacity: 1;
    transform: none;
    transition: none;
  }

  .js .reveal .stagger,
  .js .reveal.is-in .stagger {
    opacity: 1;
    transform: none;
    transition: opacity 200ms ease;
    transition-delay: 0ms;
  }

  .js .reveal .hero__media img,
  .js .reveal.is-in .hero__media img {
    clip-path: inset(0 0 0 0);
    transition: none;
  }

  .hero__media img {
    transform: none;
    will-change: auto;
  }

  .btn:hover,
  .btn:active,
  .tile:hover .tile__plate img,
  .proof a:hover .proof__go { transform: none; }

  .nav__progress { display: none; }
}
