/* =====================================================================
   form industries — landing page
   ---------------------------------------------------------------------
   One column on a dark ground, read as an article: prose that lights word
   by word as you scroll (the same reveal airform.space uses), a timeline of
   what exists and what follows, and the glyph field scrolling behind it all.
   ===================================================================== */

@font-face {
  font-family: 'Lufga';
  src: url('../assets/fonts/LufgaLight.ttf') format('truetype');
  font-weight: 300;
  font-display: swap;
}
@font-face {
  font-family: 'Lufga';
  src: url('../assets/fonts/LufgaRegular.ttf') format('truetype');
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: 'Lufga';
  src: url('../assets/fonts/LufgaMedium.ttf') format('truetype');
  font-weight: 500;
  font-display: swap;
}
@font-face {
  font-family: 'Lufga';
  src: url('../assets/fonts/LufgaSemiBold.ttf') format('truetype');
  font-weight: 600;
  font-display: swap;
}

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

:root {
  --font: 'Lufga', 'Poppins', 'Century Gothic', Futura, -apple-system, sans-serif;

  /* The ground. Kept in sync with the engine palette in site.js so the
     glyph field sits on exactly this colour with no seam. */
  --ground: #0f1012;
  --ink: #f2efe8;          /* fully lit text */
  --ink-dim: #6c7178;      /* secondary copy */

  /* The elemental trio, carried over from the earlier Form Industries site
     where these three terms lit as you scrolled the manifesto. Water leads,
     and it is also the colour the field's signal runs in — the pipework
     growing behind this page is carrying water. */
  --water: #4a90e2;
  --power: #d6993e;
  --data:  #6ba87e;

  --signal: var(--water);

  --page-inline: 24px;
  --measure: 29rem;        /* the single column everything sits in */

  --hair: rgba(242, 239, 232, 0.08);   /* every rule on the page */

  /* One easing for everything, and it settles rather than stops — motion
     here should feel unhurried, never eager. */
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
}

@media (min-width: 768px) {
  :root { --page-inline: 56px; }
}
@media (min-width: 1280px) {
  :root { --page-inline: 80px; }
}

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

body {
  background: var(--ground);
  color: var(--ink);
  font-family: var(--font);
  font-weight: 300;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* clip, not hidden: `hidden` here would promote body to its own scroll
     container, which breaks anchor navigation and fights the scroll
     listener the reveal runs on. Nothing should overflow sideways anyway
     — the oversized glyph board is clipped at its own layer. */
  overflow-x: clip;
  /* The scrolling glyph field is positioned against body. */
  position: relative;
}

/* ── The glyph field ──
   Absolute, not fixed: the field scrolls with the page as one continuous
   board spanning the whole document, so the lattice travels with the copy
   instead of sitting still behind it. The engine generates it to the
   window's width (which sets the cell size) and the document's height,
   and rebuilds when either changes — its own height is set in JS. */
#glyf-field {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  z-index: 0;
  display: grid;
  place-items: start center;
  pointer-events: none;
  /* The board over-provisions by up to a glyph in each direction so it
     reaches every edge; the excess is cropped here. */
  overflow: hidden;
  /* The field fades up once the first board is painted, so the page
     never flashes a half-built lattice. */
  opacity: 0;
  transition: opacity 2s var(--ease);
}
#glyf-field.is-live { opacity: 1; }
#glyf-field svg { display: block; }

/* A gentle wash over the field. Enough to keep the narrative column the
   brightest thing on screen, light enough that the lattice and the
   signal running through it stay visible.
   Two very soft stops rather than one tight ellipse, so no oval edge
   shows against the flat ground. */
#glyf-veil {
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  /* From the left, where the column reads. A signal stream can wander
     anywhere on the board, so the copy needs a guaranteed floor of contrast,
     while the open right keeps the field and its pipework at full strength. */
  background:
    linear-gradient(to right,
      rgba(15, 16, 18, 0.94) 0%,
      rgba(15, 16, 18, 0.92) 30%,
      rgba(15, 16, 18, 0.78) 48%,
      rgba(15, 16, 18, 0.3) 72%,
      rgba(15, 16, 18, 0.02) 100%);
}

/* Narrow screens: the column is the whole width, so an even wash is the
   only thing that keeps copy legible. */
@media (max-width: 767px) {
  #glyf-veil {
    background: linear-gradient(to bottom,
      rgba(15, 16, 18, 0.78), rgba(15, 16, 18, 0.72));
  }
}

/* ── Wordmark ── */
.wordmark {
  position: fixed;
  top: var(--page-inline);
  left: var(--page-inline);
  z-index: 20;
  display: flex;
  flex-direction: column;
  font-size: 15px;
  font-weight: 500;
  line-height: 1.1;
  letter-spacing: 0.005em;
  color: var(--ink);
  text-decoration: none;
}

@media (min-width: 768px) {
  .wordmark { font-size: 17px; }
}

/* The opening carries the brand at full size, so the corner mark stays out
   of the way until that lockup has scrolled off — then it takes over as the
   persistent identity. */
.wordmark {
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.8s var(--ease);
}
body.is-scrolled .wordmark {
  opacity: 1;
  pointer-events: auto;
}

/* ── Narrative ── */
main {
  position: relative;
  z-index: 10;
}

/* The media sections join the same flow. */
.chapter {
  padding: 1.7em var(--page-inline);
}

/* One left axis, shared by every section. */
.chapter-inner,
.story-inner {
  width: 100%;
  max-width: var(--measure);
}

@media (min-width: 1024px) {
  .chapter-inner,
  .story-inner {
    /* --axis-nudge is set once by JS to seat the column on the module grid,
       so the mark aligns with the text beneath it. */
    margin-left: calc((100vw - var(--measure)) * 0.13 + var(--axis-nudge, 0px));
  }
}

@media (max-width: 767px) {
  .story--open { padding-top: 12vh; }
  .story--close { padding-bottom: 12vh; }
}

/* ── The opening lockup ──
   The mark is cut from the same geometry as the tiles behind it (the engine
   builds it from LOGO_MARK with the same shape routine), sitting above the
   wordmark. It is the first thing on screen. */
.lockup {
  margin-bottom: 3.2em;
}

/* Size and offset are written by JS: the mark has to land exactly on the
   module the board reserved for it. */
.lockup-mark {
  width: 72px;
  height: 72px;
  margin-bottom: 0.9em;
  color: var(--ink);
}
.lockup-mark svg {
  display: block;
  width: 100%;
  height: 100%;
}

.lockup-word {
  display: flex;
  flex-direction: column;
  font-size: clamp(30px, 3.4vw, 48px);
  font-weight: 500;
  line-height: 0.98;
  letter-spacing: -0.03em;
}

/* ── Load choreography ──
   The lockup first, then the field building outward from it, then the network,
   then the words. Each stage is a class on <body>; the delays that stagger the
   tiles outward are written per-glyph in JS. */
.lockup {
  opacity: 0;
  transition: opacity 1.4s var(--ease);
}
body.intro-mark .lockup { opacity: 1; }

/* Nothing but the lockup is on screen until the field has built and its first
   nodes are moving water. Every section is held back, not just the opening —
   otherwise the paragraphs below the fold sit there dimly through the whole
   intro. visibility, not opacity, so the layout never moves. */
main > section { visibility: hidden; }
body.intro-text main > section { visibility: visible; }

/* The opening is the exception: it carries the lockup, which comes first. */
.story--open { visibility: visible; }
.story--open .story-body { visibility: hidden; }
body.intro-text .story--open .story-body { visibility: visible; }

/* Each piece ignites rather than fades: up to full, a dip, then settling.
   Only opacity moves, so it stays cheap enough to run on thousands of pieces
   at once, and it reads as something powering on rather than a wipe passing
   over. The per-piece delay is written inline by JS. */
@keyframes glyf-ignite {
  0%   { opacity: 0; }
  30%  { opacity: 1; }
  56%  { opacity: 0.62; }
  100% { opacity: 1; }
}

/* Colour changes ease rather than snap, so a node lights up and the water
   flows into it instead of both switching state between frames. Short enough
   that a travelling signal keeps its edge. */
#glyf-field path,
#glyf-field circle { transition: fill 0.5s ease; }

#glyf-field.is-spawning g[data-glyph] > * { opacity: 0; animation: none; }
#glyf-field.is-igniting g[data-glyph] > * {
  animation: glyf-ignite 1.25s var(--ease) both;
}

/* ── The reading column ── */
/* One continuous article: sections are paragraphs' worth of space apart, not
   screens apart, so the reading runs from the opening straight through to the
   software without a break.

   Padding, never margin: a top margin here collapses through <main> and
   <body>, which shifts the body box and takes the absolutely positioned glyph
   field with it — leaving a bare band across the top of the page. */
.story {
  position: relative;
  padding: 1.7em var(--page-inline);
}
.story--open { padding-top: 16vh; }
.story--close { padding-bottom: 16vh; }

/* Measured off op.al: ~21px at desktop, leading a shade over 1.1, tracked
   very slightly open, ranged left in a narrow column. Large type set tight
   is what gives that page its voice. */
.story p,
.story-body p {
  font-size: clamp(20px, 1.55vw, 23px);
  font-weight: 300;
  line-height: 1.28;
  letter-spacing: 0.004em;
  text-align: left;
  /* Ranged left needs no hyphenation, and breaking words at this size looks
     careless rather than considered. */
  hyphens: none;
  -webkit-hyphens: none;
}

.story p + p,
.story-body p + p { margin-top: 1.15em; }

/* The opening sits a step above the rest — the only size change on the page. */
/* Qualified with p so it outranks `.story p` without reaching for !important. */
/* The opening sentence is the same size as the prose — one voice — but held
   apart from it. */
.story-body p.story-open { margin-bottom: 1.9em; }



/* Section labels: a hairline and a whisper. Colour is reserved for the
   manifesto's elemental terms and the field's signal, so it would be spent
   carelessly here. */
/* A section heading, given space rather than a band to separate it. */
.label {
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(242, 239, 232, 0.5);
  margin-bottom: 2.6em;
}

/* ── The ecosystem ──
   An index, not a pitch: eleven products read as a spec list, name against
   description, so the section stays dense enough to scan in one pass. */
.chapter--line {
  min-height: auto;
  padding-block: 16vh;
  position: relative;
}

.label--spaced { margin-top: 5.5em; }

.eco-group { margin-bottom: 6.5em; }
.eco-group:last-child { margin-bottom: 0; }

.eco-scale {
  font-size: clamp(23px, 2.3vw, 31px);
  font-weight: 300;
  letter-spacing: -0.022em;
  color: var(--ink);
}

/* Group lead-ins, carried over from the earlier site's Consumer / MultiHome /
   Industrial pages. */


.eco-lede {
  font-size: clamp(16px, 1.4vw, 19px);
  font-weight: 300;
  line-height: 1.6;
  letter-spacing: -0.004em;
  color: rgba(242, 239, 232, 0.7);
  max-width: 27rem;
  margin: 0.85em auto 0;
  text-wrap: pretty;
}

/* Prose set between runs of the timeline: the story voice, at reading size,
   carrying the argument from one group of nodes to the next. */
.story-body--tight {
  margin-bottom: 3em;
}
.line + .story-body--tight { margin-top: 4.5em; }

/* ── The line ──
   A timeline read downward in one column: name against year, a sentence, and
   a render only where there is something worth showing. The hairline above
   each entry is the line itself. */
.line { list-style: none; }

.line-item {
  border-top: 1px solid var(--hair);
  padding: 2.1em 0 2.4em;
}
.line:last-of-type .line-item:last-child { border-bottom: 1px solid var(--hair); }

.line-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1.5em;
  margin-bottom: 0.7em;
}

.line-head h3 {
  font-size: clamp(17px, 1.35vw, 19px);
  font-weight: 400;
  letter-spacing: -0.012em;
}

.line-head a {
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid rgba(242, 239, 232, 0.28);
  transition: border-color 0.4s var(--ease), color 0.4s var(--ease);
}
.line-head a:hover {
  color: var(--water);
  border-bottom-color: var(--water);
}

/* Tabular so the years line up down the right edge, like a ledger. */
.line-year {
  flex: none;
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  font-variant-numeric: tabular-nums;
  color: rgba(242, 239, 232, 0.42);
}

.line-item > p {
  font-size: clamp(14px, 1.1vw, 15px);
  font-weight: 300;
  line-height: 1.68;
  color: rgba(242, 239, 232, 0.6);
  text-wrap: pretty;
}

.line-figure {
  position: relative;
  margin-top: 1.8em;
  aspect-ratio: 16 / 10;
  overflow: hidden;
  border-radius: 10px;
  background: #14161a;
}

/* Sources that are square, or nearly so, get a square frame: at 16/10 the
   render was cropped through the middle of the unit. */
.line-figure--square { aspect-ratio: 1; }

.line-figure img,
.line-figure video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Several renders on one entry crossfade between them. */
.line-figure[data-gallery] img {
  opacity: 0;
  transition: opacity 1.5s var(--ease);
}
.line-figure[data-gallery] img.is-active { opacity: 1; }

/* ── Manufacturing ── */
.chapter--factory { min-height: auto; padding-block: 16vh; }

.factory-media {
  display: grid;
  grid-template-columns: 1fr;
  gap: 18px;
  margin-top: 4em;
}



.factory-film,
.factory-portrait {
  position: relative;
  border: 1px solid var(--hair);
  border-radius: 16px;
  overflow: hidden;
  background: #14161a;
}

.factory-film { aspect-ratio: 16 / 9; }
.factory-portrait { aspect-ratio: 3 / 4; }



.factory-film video,
.factory-portrait video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.factory-portrait figcaption {
  position: absolute;
  left: 1.1em;
  bottom: 0.9em;
  z-index: 2;
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(242, 239, 232, 0.82);
  text-shadow: 0 1px 14px rgba(0, 0, 0, 0.7);
}

/* A row of stills that scrolls sideways rather than stacking — the section is
   already long, and these are supporting evidence, not hero images. */
.factory-strip {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 68vw;
  gap: 14px;
  margin-top: 18px;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  padding-bottom: 0.6em;
  scrollbar-width: thin;
  scrollbar-color: rgba(242, 239, 232, 0.2) transparent;
}

@media (min-width: 760px) {
  .factory-strip { grid-auto-flow: row; grid-template-columns: repeat(2, 1fr); overflow-x: visible; }
}

.factory-strip figure {
  position: relative;
  aspect-ratio: 4 / 3;
  border: 1px solid var(--hair);
  border-radius: 12px;
  overflow: hidden;
  background: #14161a;
  scroll-snap-align: start;
}

.factory-strip img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.eco-list { list-style: none; }

.eco-list li {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.45em;
  padding: 1.5em 0;
  border-top: 1px solid var(--hair);
}
.eco-list li:last-child { border-bottom: 1px solid var(--hair); }

@media (min-width: 768px) {
  .eco-list li {
    grid-template-columns: 13rem 1fr;
    gap: 2.5em;
    align-items: baseline;
  }
}

.eco-list h3 {
  font-size: clamp(16px, 1.4vw, 18px);
  font-weight: 500;
  letter-spacing: -0.006em;
  color: var(--ink);
}

.eco-list p {
  font-size: clamp(15px, 1.3vw, 16px);
  font-weight: 300;
  line-height: 1.6;
  color: rgba(242, 239, 232, 0.6);
  max-width: 34rem;
}

/* ── The scroll reveal ──
   Every word starts dim and lights as the scroll line passes it. The
   colour transition is what sells it, so it is the only thing animated. */
/* A word is either written or not yet written. No scroll-linked brightness:
   the type-in is the reveal. */
.reveal-word {
  opacity: 0;
  transition: opacity 0.2s linear;
}
.reveal-word.lit { opacity: 1; }

/* The manifesto's elemental terms take their own colour as they light,
   instead of resolving to plain ink like the rest of the sentence. The
   colour lands on the word inside the term wrapper, because the reveal
   splits text into per-word spans. */

.term {
  hyphens: none;
  -webkit-hyphens: none;
}
.term--water { color: var(--water); }
.term--power { color: var(--power); }
.term--data  { color: var(--data); }

/* Cards and figures reveal as a whole rather than per word. */
[data-reveal-block] {
  opacity: 0;
  transform: translateY(22px);
  transition: opacity 1.4s var(--ease), transform 1.4s var(--ease);
}
[data-reveal-block].is-visible {
  opacity: 1;
  transform: none;
}

/* ── Footer ── */
footer {
  position: relative;
  z-index: 10;
  background: var(--ground);
  border-top: 1px solid var(--hair);
  padding: 12vh var(--page-inline) 6vh;
}

.footer-inner {
  max-width: 68rem;
  margin: 0 auto;
  display: grid;
  gap: 5vh;
}

.footer-brand {
  display: flex;
  flex-direction: column;
  font-size: clamp(34px, 7vw, 76px);
  font-weight: 500;
  line-height: 0.98;
  letter-spacing: -0.03em;
}

/* Four links out, nothing more. */
.footer-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 1.6em 2.4em;
  font-size: 15px;
}

.footer-nav a {
  color: rgba(242, 239, 232, 0.72);
  text-decoration: none;
  border-bottom: 1px solid rgba(242, 239, 232, 0.2);
  transition: color 0.4s var(--ease), border-color 0.4s var(--ease);
}
.footer-nav a:hover {
  color: var(--ink);
  border-bottom-color: var(--ink);
}

/* Restores the link styling that lived with the old footer columns. */
.footer-fine a {
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid rgba(242, 239, 232, 0.2);
  transition: color 0.4s var(--ease), border-color 0.4s var(--ease);
}
.footer-fine a:hover {
  color: var(--ink);
  border-bottom-color: var(--ink);
}

.footer-fine {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6em 2em;
  justify-content: space-between;
  font-size: 13px;
  color: rgba(242, 239, 232, 0.4);
  border-top: 1px solid rgba(242, 239, 232, 0.08);
  padding-top: 2.4em;
}

/* ── Reduced motion ──
   The engine already slows itself; here the reveal simply resolves so no
   copy is left sitting dim. */
@media (prefers-reduced-motion: reduce) {
  .reveal-word { opacity: 1; transition: none; }
  [data-reveal-block] { opacity: 1; transform: none; transition: none; }
  #glyf-field { transition: none; }
}
