/* ==========================================================================
   PHOTON SPEAR
   --------------------------------------------------------------------------
   Design system modelled on anduril.com/space/space. Token values (colors,
   type scale, fluid root sizing) were read from their published stylesheet so
   the proportions match rather than approximate.

   Contents
     01  Fonts
     02  Tokens
     03  Reset
     04  Fluid root sizing + type scale
     05  Layout grid
     06  Utilities: labels, rules, reveals
     07  Navigation
     08  Hero
     09  Thesis
     10  Capability pillars
     11  Approach
     12  Statement
     13  Contact
     14  Footer
     15  Media treatment
     16  Reduced motion
   ========================================================================== */


/* 01  Fonts ================================================================
   Inter Tight stands in for Helvetica Now Display; Space Grotesk for Elios.
   Both are variable fonts under the SIL Open Font License, self-hosted.
   To swap in licensed originals, replace the src URLs below — nothing else
   in this file references font filenames.
   ------------------------------------------------------------------------- */

@font-face {
  font-family: "Inter Tight";
  src: url("../fonts/inter-tight-latin.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Inter Tight";
  src: url("../fonts/inter-tight-latin-ext.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
    U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
    U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "Space Grotesk";
  src: url("../fonts/space-grotesk-latin.woff2") format("woff2");
  font-weight: 300 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Space Grotesk";
  src: url("../fonts/space-grotesk-latin-ext.woff2") format("woff2");
  font-weight: 300 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
    U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
    U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}


/* 02  Tokens =============================================================== */

:root {
  /* Color — taken from PhotonSpear_v0.pptx so the site and the deck read as
     one identity. The hex values below are the ones the deck actually uses.

     --accent is the deck's violet, the colour behind "No limit." on the title
     slide and the border on its callout pills. At 3.1:1 against black it is
     too dark to carry small text on its own, so it does the structural work —
     fills, borders, rules — and --accent-bright, the same hue lifted to 5.1:1,
     carries the small uppercase labels and icons. Anything sitting ON an
     accent fill takes --accent-ink. Changing the brand colour means changing
     these three together. */
  --bg: #010101;
  --text: #ffffff;
  --muted: #b7b7bf;
  --accent: #5843c6;
  --accent-bright: #7d6ae8;
  --accent-ink: #ffffff;
  --rule: rgba(255, 255, 255, 0.16);
  --rule-strong: rgba(255, 255, 255, 0.28);
  --panel: #1f1b32;

  /* Type */
  --font-display: "Inter Tight", Helvetica, Arial, sans-serif;
  --font-label: "Space Grotesk", "Inter Tight", Helvetica, Arial, sans-serif;

  /* Layout — gutters scale with the viewport, as on the reference site */
  --gutter: 1.5rem;
  --grid-gap: 1.5rem;
  --band: 8rem; /* vertical rhythm between major sections */

  /* Motion */
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --dur: 0.8s;
}

@media (min-width: 768px) {
  :root {
    --gutter: 3rem;
    --band: 10rem;
  }
}

@media (min-width: 1280px) {
  :root {
    --gutter: 3.5rem;
    --band: 12rem;
  }
}


/* 03  Reset ================================================================ */

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

html {
  -webkit-text-size-adjust: 100%;
  scrollbar-gutter: stable;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-display);
  overflow-x: clip;
}

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

img {
  -webkit-user-drag: none;
  user-select: none;
}

a {
  color: inherit;
  text-decoration: none;
}

ul {
  margin: 0;
  padding: 0;
  list-style: none;
}

::selection {
  background: rgba(223, 241, 64, 0.333);
}

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

/* Lenis requires these — taken from the library's own stylesheet */
html.lenis,
html.lenis body {
  height: auto;
}

.lenis.lenis-smooth [data-lenis-prevent] {
  overscroll-behavior: contain;
}

.lenis.lenis-stopped {
  overflow: clip;
}

.skip-link {
  position: absolute;
  top: -100%;
  left: var(--gutter);
  z-index: 100;
  padding: 0.75rem 1rem;
  background: var(--accent);
  color: var(--accent-ink);
  font-family: var(--font-label);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.03rem;
  text-transform: uppercase;
}

.skip-link:focus {
  top: 1rem;
}


/* 04  Fluid root sizing + type scale =======================================
   The signature mechanic of the reference site: below 1280px the root is a
   fixed 14px, above it the root tracks viewport width, so every rem-based
   heading scales continuously. All type below is expressed in rem to inherit
   that behaviour.
   ------------------------------------------------------------------------- */

html {
  font-size: 14px;
}

@media (min-width: 1280px) {
  html {
    font-size: calc(1.25vw - 2px);
  }
}

/* Cap the growth on ultra-wide displays so headlines stay readable */
@media (min-width: 2200px) {
  html {
    font-size: 25.5px;
  }
}

h1,
h2,
h3,
h4,
h5,
h6,
.t-h7,
p {
  margin: 0;
  font-family: var(--font-display);
  font-style: normal;
  text-wrap: balance;
}

h1 {
  font-size: 3rem;
  font-weight: 700;
  line-height: 105%;
  letter-spacing: -0.06rem;
}

h2 {
  font-size: 2.5rem;
  font-weight: 400;
  line-height: 105%;
  letter-spacing: -0.05rem;
}

h3 {
  font-size: 2.25rem;
  font-weight: 400;
  line-height: 110%;
  letter-spacing: -0.045rem;
}

h4 {
  font-size: 1.75rem;
  font-weight: 400;
  line-height: 115%;
  letter-spacing: -0.03rem;
}

h5 {
  font-size: 1.5rem;
  font-weight: 400;
  line-height: 115%;
  letter-spacing: -0.02rem;
}

h6 {
  font-size: 1.25rem;
  font-weight: 400;
  line-height: 115%;
  letter-spacing: -0.015rem;
}

.t-h7 {
  font-size: 1.125rem;
  font-weight: 400;
  line-height: 115%;
  letter-spacing: -0.013rem;
}

p {
  font-size: 1.05rem;
  font-weight: 400;
  line-height: 120%;
  letter-spacing: -0.009rem;
  text-wrap: pretty;
}

/* Full display scale, verbatim from the reference stylesheet. Held back until
   there is enough width for -0.1rem tracking at 5rem to read correctly. */
@media (min-width: 768px) {
  h1 {
    font-size: 5rem;
    letter-spacing: -0.1rem;
  }
  h2 {
    font-size: 4rem;
    letter-spacing: -0.08rem;
  }
  h3 {
    font-size: 3.5rem;
    letter-spacing: -0.07rem;
  }
  h4 {
    font-size: 2.5rem;
    letter-spacing: -0.05rem;
  }
  h5 {
    font-size: 2rem;
    letter-spacing: -0.02rem;
  }
  h6 {
    font-size: 1.5rem;
    letter-spacing: -0.015rem;
  }
  .t-h7 {
    font-size: 1.25rem;
  }
}

.t-muted {
  color: var(--muted);
}

.t-measure {
  max-width: 34rem;
}


/* 05  Layout grid ==========================================================
   12 columns, viewport-relative gutters. Sections opt into the grid so
   individual elements can claim specific column spans.
   ------------------------------------------------------------------------- */

.wrap {
  width: 100%;
  max-width: 100%;
  padding-inline: var(--gutter);
}

.grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--grid-gap);
}

@media (min-width: 768px) {
  .grid {
    grid-template-columns: repeat(12, minmax(0, 1fr));
  }
}

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

.section--tight {
  padding-block: calc(var(--band) * 0.55);
}

.section--flush {
  padding-block: 0;
}


/* 06  Utilities: labels, rules, reveals ====================================
   Note the tracking inversion that defines this design language: display type
   carries negative tracking, small uppercase labels carry positive tracking.
   ------------------------------------------------------------------------- */

.label {
  font-family: var(--font-label);
  font-size: 0.75rem;
  font-weight: 500;
  line-height: 105%;
  letter-spacing: 0.03rem;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0;
}

.label--accent {
  color: var(--accent-bright);
}

.label--index {
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
}

.label--index .num {
  color: var(--accent-bright);
}

.label--index .sep {
  color: var(--rule-strong);
}

.rule {
  height: 1px;
  border: 0;
  margin: 0;
  background: var(--rule);
}

/* Scroll reveal. Applied by main.js only when motion is allowed; the .is-in
   class is what animates, so content is visible by default if JS never runs. */
.js .reveal {
  opacity: 0;
  transform: translateY(1.5rem);
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
  will-change: opacity, transform;
}

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

.js .reveal[data-delay="1"] { transition-delay: 0.08s; }
.js .reveal[data-delay="2"] { transition-delay: 0.16s; }
.js .reveal[data-delay="3"] { transition-delay: 0.24s; }


/* 07  Navigation =========================================================== */

.nav {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  padding: 1.25rem var(--gutter);
  background: transparent;
  border-bottom: 1px solid transparent;
  transition: background 0.4s var(--ease), border-color 0.4s var(--ease),
    backdrop-filter 0.4s var(--ease);
}

.nav.is-scrolled {
  background: rgba(1, 1, 1, 0.82);
  backdrop-filter: blur(12px);
  border-bottom-color: var(--rule);
}

.nav__brand {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  font-family: var(--font-display);
  font-size: 0.9375rem;
  font-weight: 700;
  letter-spacing: 0.02rem;
  text-transform: uppercase;
  color: var(--text);
}

/* The logo mark is a tall, narrow spear (roughly 0.45:1), so it is sized by
   height and left to find its own width. The source PNG is a white glow on
   transparency — see assets/brand/ for how it was derived — which is why it
   needs no background treatment here. */
.nav__mark {
  height: 1.5rem;
  width: auto;
  flex: none;
  /* The glow already carries the mark; a touch of restraint keeps it from
     outshining the wordmark beside it. */
  opacity: 0.92;
}

.nav__links {
  display: none;
  align-items: center;
  gap: 2rem;
}

@media (min-width: 768px) {
  .nav__links {
    display: flex;
  }
}

.nav__link {
  position: relative;
  font-family: var(--font-label);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.03rem;
  text-transform: uppercase;
  color: var(--muted);
  transition: color 0.25s var(--ease);
  padding-block: 0.25rem;
}

.nav__link::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 1px;
  background: var(--accent-bright);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.35s var(--ease);
}

.nav__link:hover {
  color: var(--text);
}

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

.nav__cta {
  font-family: var(--font-label);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.03rem;
  text-transform: uppercase;
  color: var(--text);
  border: 1px solid var(--rule-strong);
  padding: 0.625rem 1.125rem;
  transition: border-color 0.3s var(--ease), color 0.3s var(--ease),
    background 0.3s var(--ease);
}

.nav__cta:hover {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
}


/* 08  Hero ================================================================= */

.hero {
  position: relative;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding-block: 6rem 3rem;
  overflow: hidden;
  isolation: isolate;
}

/* Compound selector on purpose. This element also carries .media, which sets
   position: relative for its grid overlay; a plain .hero__media rule ties on
   specificity and loses to whichever comes later in the file. Matching both
   classes settles it regardless of source order. */
.hero__media.media {
  position: absolute;
  inset: 0;
  z-index: -2;
}

.hero__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Vignette: darkens the base of the frame so the headline always holds
   contrast regardless of the photograph behind it. */
.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(
      to top,
      rgba(1, 1, 1, 0.95) 0%,
      rgba(1, 1, 1, 0.55) 38%,
      rgba(1, 1, 1, 0.2) 70%,
      rgba(1, 1, 1, 0.45) 100%
    );
  pointer-events: none;
}

/* Brand mark in the hero. z-index -1 places it above the photograph (-2) but
   below the headline, so text always wins. Sized in svh so it scales with the
   viewport the way the type does. */
.hero__logo {
  position: absolute;
  z-index: -1;
  height: 34svh;
  width: auto;
  top: 16%;
  left: 50%;
  transform: translateX(-50%);
  opacity: 0.85;
  pointer-events: none;
}

/* Once there is width, move it out of the headline's column and let it run
   taller — the mark and the text then occupy separate halves of the frame. */
@media (min-width: 768px) {
  .hero__logo {
    height: 66svh;
    top: 50%;
    left: auto;
    right: 8%;
    transform: translateY(-50%);
  }
}

.hero__inner {
  position: relative;
  width: 100%;
}

.hero__eyebrow {
  grid-column: 1 / -1;
  margin-bottom: 1.5rem;
}

.hero__title {
  grid-column: 1 / -1;
  max-width: 18ch;
}

@media (min-width: 768px) {
  .hero__title {
    grid-column: 1 / 10;
  }
}

.hero__sub {
  grid-column: 1 / -1;
  margin-top: 1.75rem;
  color: var(--muted);
  max-width: 42ch;
}

@media (min-width: 768px) {
  .hero__sub {
    grid-column: 1 / 6;
  }
}

.hero__foot {
  margin-top: 4rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
}

.hero__scroll {
  display: inline-flex;
  align-items: center;
  gap: 0.75rem;
}

.hero__scroll svg {
  width: 0.75rem;
  height: 0.75rem;
  color: var(--accent-bright);
  animation: nudge 2.4s var(--ease) infinite;
}

@keyframes nudge {
  0%,
  100% {
    transform: translateY(0);
    opacity: 1;
  }
  50% {
    transform: translateY(0.3rem);
    opacity: 0.5;
  }
}


/* 08b  Section stepping ====================================================
   A control at the foot of each section that advances to the next one. Built
   at build time rather than by script, so it works with JavaScript disabled
   and the anchors are real links.
   ------------------------------------------------------------------------- */

.section-next-wrap {
  display: flex;
  justify-content: flex-end;
  margin-top: 5rem;
}

/* The pillars section already ends on a hairline rule, so it needs less. */
.pillars .section-next-wrap {
  margin-top: 3.5rem;
  padding-bottom: 1rem;
}

.statement .section-next-wrap {
  justify-content: center;
  margin-top: 3rem;
}

.section-next {
  display: inline-flex;
  align-items: center;
  gap: 1rem;
  color: var(--muted);
  transition: color 0.3s var(--ease);
}

.section-next__icon {
  width: 2.75rem;
  height: 2.75rem;
  flex: none;
  display: grid;
  place-items: center;
  border: 1px solid var(--rule-strong);
  transition: border-color 0.3s var(--ease), background 0.3s var(--ease);
}

.section-next svg {
  width: 0.75rem;
  height: 0.75rem;
  color: var(--accent-bright);
  transition: color 0.3s var(--ease), transform 0.3s var(--ease);
}

.section-next:hover {
  color: var(--text);
}

.section-next:hover .section-next__icon {
  border-color: var(--accent);
  background: var(--accent);
}

/* On hover the arrow inverts against the filled square and nudges the way it
   is about to travel. */
.section-next:hover svg {
  color: var(--accent-ink);
  transform: translateY(0.15rem);
}

.section-next[href="#top"]:hover svg {
  transform: translateY(-0.15rem);
}


/* 09  Thesis =============================================================== */

.thesis__label {
  grid-column: 1 / -1;
  margin-bottom: 2.5rem;
}

@media (min-width: 768px) {
  .thesis__label {
    grid-column: 1 / 3;
    margin-bottom: 0;
  }
}

.thesis__body {
  grid-column: 1 / -1;
}

@media (min-width: 768px) {
  .thesis__body {
    grid-column: 4 / 12;
  }
}

/* Direct children only, so both body paragraphs get the same treatment —
   including the first one, which follows the h3 rather than another p. */
.thesis__body > p {
  margin-top: 2rem;
  color: var(--muted);
  max-width: 46ch;
}


/* 10  Capability pillars =================================================== */

.pillars {
  border-top: 1px solid var(--rule);
}

.pillar {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--grid-gap);
  align-items: center;
  padding-block: calc(var(--band) * 0.6);
  border-bottom: 1px solid var(--rule);
}

@media (min-width: 768px) {
  .pillar {
    grid-template-columns: repeat(12, minmax(0, 1fr));
  }
}

.pillar__media {
  grid-column: 1 / -1;
  aspect-ratio: 4 / 3;
  overflow: hidden;
}

.pillar__body {
  grid-column: 1 / -1;
}

@media (min-width: 768px) {
  .pillar__media {
    grid-column: 1 / 7;
  }
  .pillar__body {
    grid-column: 8 / 13;
  }

  /* Alternate sides so the eye travels down the page rather than in a column */
  .pillar:nth-child(even) .pillar__media {
    grid-column: 7 / 13;
    grid-row: 1;
  }
  .pillar:nth-child(even) .pillar__body {
    grid-column: 1 / 6;
    grid-row: 1;
  }
}

.pillar__index {
  margin-bottom: 1.25rem;
}

.pillar__title {
  margin-bottom: 1.25rem;
}

.pillar__text {
  color: var(--muted);
  max-width: 40ch;
}


/* 11  Approach ============================================================= */

.approach__head {
  grid-column: 1 / -1;
  margin-bottom: 4rem;
}

.approach__title {
  margin-top: 2rem;
}

@media (min-width: 768px) {
  .approach__head {
    grid-column: 1 / 8;
  }
}

.approach__list {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
  gap: 0;
  border-top: 1px solid var(--rule);
}

.approach__item {
  padding-block: 2.5rem;
  border-bottom: 1px solid var(--rule);
}

@media (min-width: 768px) {
  .approach__item {
    padding: 2.5rem var(--grid-gap) 3.5rem 0;
    border-bottom: 0;
  }
  .approach__item + .approach__item {
    padding-left: var(--grid-gap);
    border-left: 1px solid var(--rule);
  }
}

.approach__item h5 {
  margin-block: 1.25rem 1rem;
}

/* :not(.label) matters here — a bare `.approach__item p` is (0,1,1) and would
   outrank .label--accent (0,1,0), silently repainting the accent eyebrows
   muted. Excluding labels keeps the modifier class in charge of its own color. */
.approach__item p:not(.label) {
  color: var(--muted);
}


/* 12  Statement ============================================================ */

.statement {
  position: relative;
  min-height: 80svh;
  display: grid;
  place-items: center;
  overflow: hidden;
  isolation: isolate;
  text-align: center;
}

/* Compound selector for the same reason as .hero__media.media above. */
.statement__media.media {
  position: absolute;
  inset: 0;
  z-index: -2;
}

.statement__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.statement::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: rgba(1, 1, 1, 0.6);
}

.statement__inner {
  position: relative;
  padding: var(--band) var(--gutter);
  max-width: 60rem;
}

.statement__inner .label {
  margin-bottom: 2rem;
}


/* 13  Contact ============================================================== */

.contact {
  border-top: 1px solid var(--rule);
}

.contact__label {
  grid-column: 1 / -1;
  margin-bottom: 2.5rem;
}

@media (min-width: 768px) {
  .contact__label {
    grid-column: 1 / 3;
    margin-bottom: 0;
  }
}

.contact__body {
  grid-column: 1 / -1;
}

@media (min-width: 768px) {
  .contact__body {
    grid-column: 4 / 12;
  }
}

.contact__body p {
  margin-top: 1.75rem;
  color: var(--muted);
  max-width: 44ch;
}

.contact__form {
  margin-top: 2.5rem;
  max-width: 34rem;
}

.contact__field {
  margin-bottom: 1.5rem;
}

.contact__field label {
  display: block;
  font-family: var(--font-label);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.03rem;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 0.625rem;
}

.contact__field input,
.contact__field textarea {
  display: block;
  width: 100%;
  background: transparent;
  border: 1px solid var(--rule);
  border-radius: 0;
  padding: 0.75rem 0.875rem;
  font-family: var(--font-display);
  font-size: 1rem;
  color: var(--text);
  transition: border-color 0.3s var(--ease);
}

.contact__field textarea {
  resize: vertical;
  min-height: 7rem;
}

.contact__field input:focus,
.contact__field textarea:focus {
  outline: none;
  border-color: var(--accent-bright);
}

.contact__field input::placeholder,
.contact__field textarea::placeholder {
  color: var(--muted);
}

/* Honeypot: off-screen rather than display:none, since some bots skip fields
   they detect as hidden. Real visitors, sighted or not, never reach it. */
.contact__hp {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  left: -9999px;
}

.contact__foot {
  display: flex;
  align-items: center;
  gap: 1.25rem;
  margin-top: 0.5rem;
}

.contact__submit {
  font-family: var(--font-label);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.03rem;
  text-transform: uppercase;
  color: var(--text);
  background: transparent;
  border: 1px solid var(--rule-strong);
  padding: 0.75rem 1.5rem;
  cursor: pointer;
  transition: border-color 0.3s var(--ease), color 0.3s var(--ease),
    background 0.3s var(--ease);
}

.contact__submit:hover,
.contact__submit:focus-visible {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
}

.contact__submit:disabled {
  opacity: 0.6;
  cursor: default;
}

.contact__status {
  font-family: var(--font-label);
  font-size: 0.75rem;
  letter-spacing: 0.03rem;
  color: var(--muted);
  margin: 0;
}

.contact__status[data-state="success"] {
  color: var(--accent-bright);
}

.contact__status[data-state="error"] {
  color: var(--text);
}

.contact__alt {
  margin-top: 2rem;
  color: var(--muted);
}

.contact__alt a {
  color: var(--text);
  border-bottom: 1px solid var(--rule-strong);
  padding-bottom: 0.1rem;
  transition: color 0.3s var(--ease), border-color 0.3s var(--ease);
}

.contact__alt a:hover {
  color: var(--accent-bright);
  border-bottom-color: var(--accent-bright);
}


/* 14  Footer =============================================================== */

.footer {
  border-top: 1px solid var(--rule);
  padding: 3rem var(--gutter) 2.5rem;
}

.footer__top {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: 2.5rem;
  padding-bottom: 3rem;
}

.footer__cols {
  display: flex;
  flex-wrap: wrap;
  gap: 3rem 4rem;
}

.footer__col .label {
  margin-bottom: 1rem;
  color: var(--rule-strong);
}

.footer__col li + li {
  margin-top: 0.625rem;
}

.footer__col a,
.footer__col span {
  font-family: var(--font-label);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.03rem;
  text-transform: uppercase;
  color: var(--muted);
  transition: color 0.25s var(--ease);
}

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

.footer__bottom {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding-top: 2rem;
  border-top: 1px solid var(--rule);
}

.footer__bottom p {
  font-family: var(--font-label);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.03rem;
  text-transform: uppercase;
  color: var(--rule-strong);
}


/* 15  Media treatment =====================================================
   A single shared treatment applied to every photograph. Desaturating and
   lifting contrast uniformly makes images from mixed sources read as one
   deliberate set. The gradient underneath shows through if a file is absent,
   so a missing image looks like an intentional dark panel rather than a
   broken asset.
   ------------------------------------------------------------------------- */

.media {
  position: relative;
  background: linear-gradient(135deg, #121212 0%, #060606 55%, #0e0e0e 100%);
  overflow: hidden;
}

.media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: grayscale(1) contrast(1.08) brightness(0.92);
  transition: filter 0.6s var(--ease), transform 0.9s var(--ease);
}

/* The hero is the exception. Its photograph is the title slide of
   PhotonSpear_v0.pptx — a single authored brand asset rather than one of the
   mixed-source images the rule above exists to reconcile, and its warm dome
   against the violet accent is the pairing the deck itself makes. It keeps the
   contrast and brightness grade so it still sits in the same family, and drops
   only the desaturation. Compound selector because `.media img` below ties it
   on specificity and would win on source order. To put the hero back in
   monochrome, delete this block. */
.hero__media.media img {
  filter: contrast(1.06) brightness(0.95);
}

/* Fine grid overlay — reads as technical/engineering rather than decorative */
.media::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: linear-gradient(
      to right,
      var(--rule) 1px,
      transparent 1px
    ),
    linear-gradient(to bottom, var(--rule) 1px, transparent 1px);
  background-size: 4rem 4rem;
  opacity: 0.25;
}

.pillar__media.media:hover img {
  filter: grayscale(0.7) contrast(1.05) brightness(1);
  transform: scale(1.02);
}

/* Empty state — applied by main.js when an image file is missing or fails.
   A broken <img> otherwise renders the browser's broken-icon glyph, which
   looks like a mistake. Hiding it and strengthening the panel underneath makes
   the gap read as an intentional dark surface, so the page is presentable
   before real photography lands (and if a filename is ever mistyped in
   production, it degrades quietly instead of visibly breaking). */
.media--empty img {
  display: none;
}

.media--empty {
  background: linear-gradient(135deg, #17181a 0%, #0b0c0d 55%, #131417 100%);
}

.media--empty::after {
  opacity: 0.5;
}


/* 16  Reduced motion ======================================================
   Lenis is disabled entirely in main.js when this preference is set; these
   rules cover the CSS-driven animation.
   ------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }

  .js .reveal {
    opacity: 1;
    transform: none;
  }
}
