/* ===========================================================
   Mecantek Automation — design system

   Direction: white page, drafting-sheet structure. The frame
   sequence was shot on a seamless backdrop that has been lifted
   to pure white, so the part appears to float directly on the
   page instead of sitting inside a video box. Everything else
   stays out of its way: navy type, hairline rules, lots of air.

   Dark is spent in exactly one place — the footer.
=========================================================== */
:root {
  /* --- color --- */
  --paper: #ffffff;
  --bone: #f6f7f9;        /* section tint, barely there        */
  --ink: #0e1a24;         /* headings                          */
  --navy: #113c5f;        /* brand — 11.5:1 on paper           */
  --navy-600: #2c5c82;    /* mono labels — 6.6:1 on paper      */
  --navy-900: #0a2436;    /* footer ground                     */
  --steel: #4a6478;       /* body copy — 6.2:1 on paper        */
  --steel-200: #ccd8e0;
  /* Accent sampled from the roller in the sequence itself, then
     deepened until it holds on white (5.2:1). Used for the logo
     panel and small marks — never for long-form copy. */
  --accent: #2e6fb0;
  --accent-deep: #17497f;
  --rule: rgba(17, 60, 95, 0.12);
  --rule-dark: rgba(255, 255, 255, 0.14);

  /* --- type --- */
  --font-display: "Archivo", "Segoe UI", system-ui, sans-serif;
  --font-body: "Inter", "Segoe UI", system-ui, sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, "SFMono-Regular", monospace;

  /* --fs-hero is sized to the sequence's side column, where the
     headline shares the stage with the part. --fs-display is the
     home page, where it has the whole width. */
  --fs-display: clamp(2.35rem, 5vw, 4rem);
  --fs-hero: clamp(1.8rem, 2.9vw, 3rem);
  --fs-h2: clamp(2rem, 4vw, 3.25rem);
  --fs-h3: clamp(1.1rem, 1.5vw, 1.35rem);
  --fs-lead: clamp(1.05rem, 1.3vw, 1.2rem);
  --fs-body: 1rem;
  --fs-small: 0.875rem;
  --fs-mono: 0.6875rem;

  /* --- layout --- */
  --max-w: 1280px;
  --gutter: clamp(1.5rem, 5vw, 4.5rem);
  --sp-section: clamp(5.5rem, 11vw, 9.5rem);
  --sp-block: clamp(2.5rem, 5vw, 4.5rem);
  --radius: 14px;
  --ease: cubic-bezier(0.16, 1, 0.3, 1);
}

/* ---------- reset ---------- */
* { margin: 0; padding: 0; box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  font-family: var(--font-body);
  color: var(--ink);
  background: var(--paper);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
img, svg, canvas { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
ul, ol { list-style: none; }
button { font: inherit; cursor: pointer; }

h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: 700;
  line-height: 1.0;
  letter-spacing: -0.03em;
}
h3 { line-height: 1.15; letter-spacing: -0.02em; }

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

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

/* ---------- primitives ---------- */
.wrap { max-width: var(--max-w); margin-inline: auto; padding-inline: var(--gutter); }

.eyebrow {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--navy-600);
}

.lead { font-size: var(--fs-lead); line-height: 1.5; color: var(--steel); max-width: 46ch; }

.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.8rem 1.5rem;
  border-radius: 999px;
  font-weight: 600;
  font-size: var(--fs-small);
  letter-spacing: -0.01em;
  border: 1px solid transparent;
  transition: background-color 0.3s var(--ease), border-color 0.3s var(--ease),
              transform 0.3s var(--ease);
}
.btn--primary { background: var(--navy); color: var(--paper); }
.btn--primary:hover { background: var(--navy-600); transform: translateY(-1px); }
.btn--ghost { border-color: var(--rule); color: var(--navy); }
.btn--ghost:hover { border-color: var(--navy-600); background: rgba(17, 60, 95, 0.04); }

.s-head { max-width: 54ch; }
.s-head h2 { font-size: var(--fs-h2); margin-top: 1.1rem; }
.s-head .lead { margin-top: 1.4rem; }

/* ===========================================================
   Home — the five-second read

   The governing rule of the site: someone who lands here and
   neither scrolls nor clicks must leave knowing what we make and
   how to reach us. So the hero states the trade in one sentence,
   names the three service lines underneath it, and the three
   doors sit immediately below — on a laptop the doors are already
   half on screen, which is what pulls the scroll.

   No saturated ground here. The one dark surface on the site is
   the footer, and the single accent colour is spent on the
   primary action, nothing else.
=========================================================== */
.hero {
  padding-block: clamp(3rem, 7vw, 5.5rem) clamp(2rem, 4vw, 3rem);
  border-bottom: 1px solid var(--rule);
}
.hero h1 {
  font-size: var(--fs-display);
  margin-top: 1.1rem;
  max-width: 24ch;
  text-wrap: balance;
}

/* The three service lines, set as a list rather than a sentence:
   a maintenance lead scanning the page reads three nouns, not a
   clause they have to parse. Mono, because on this site mono is
   the drafting voice used for structural labels. */
.hero__lines {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.75rem;
  margin-top: 1.6rem;
  font-family: var(--font-mono);
  font-size: var(--fs-small);
  letter-spacing: -0.005em;
  color: var(--steel);
}
.hero__lines li { display: flex; align-items: baseline; gap: 0.55rem; }
.hero__lines li::before {
  content: "";
  width: 6px;
  height: 6px;
  flex: none;
  background: var(--accent);
  transform: translateY(-1px);
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-top: clamp(1.75rem, 3vw, 2.5rem);
}

/* The lead time is a positioning statement, not an apology, so it
   reads as a fact on its own rule rather than as fine print. */
.hero__delay {
  margin-top: clamp(1.75rem, 3vw, 2.25rem);
  padding-top: 1.15rem;
  border-top: 1px solid var(--rule);
  max-width: 52ch;
  font-size: var(--fs-small);
  color: var(--steel);
}
.hero__delay strong { color: var(--navy); font-weight: 600; }

/* ---------- the three doors ----------
   Titled from the client's side ("you already have a drawing"),
   never from ours ("machining services"). Someone recognises
   their own situation faster than they recognise a service name. */
.doors { padding-block: clamp(3rem, 6vw, 5rem); background: var(--bone); border-bottom: 1px solid var(--rule); }
.doors__grid {
  margin-top: var(--sp-block);
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(1rem, 2vw, 1.75rem);
}
.door {
  /* Anchor for the stretched link below. */
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: clamp(1.5rem, 2.4vw, 2.1rem);
  transition: border-color 0.3s var(--ease), transform 0.3s var(--ease);
}
.door:hover { border-color: rgba(17, 60, 95, 0.3); transform: translateY(-2px); }
.door svg { width: 100%; height: auto; aspect-ratio: 3 / 2; margin-bottom: 1.3rem; }
.door h3 { font-size: var(--fs-h3); margin-bottom: 0.6rem; }
.door p { font-size: var(--fs-small); color: var(--steel); }
.door__go {
  margin-top: auto;
  padding-top: 1.4rem;
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-size: var(--fs-small);
  font-weight: 600;
  color: var(--accent);
}
.door__go::after { content: "→"; transition: transform 0.3s var(--ease); }
.door:hover .door__go::after { transform: translateX(3px); }

/* --- the whole card is the link ---
   A pseudo-element on the anchor is stretched over the card, so the
   entire box is the hit area while the accessibility tree still sees
   exactly one link with the anchor's own text as its name. Wrapping
   the card in an <a> instead would swallow the heading into the link
   name; adding a click handler would leave it unreachable by keyboard
   and invisible to "open in new tab".

   The cost is that text inside the card can no longer be selected with
   the mouse. That is the accepted trade for this pattern, and these
   cards carry three lines of summary, not content worth copying. */
.door__go::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: var(--radius);
}

/* Focus has to land on the CARD, not on the six-word link at the bottom
   of it — the outline is the only thing telling a keyboard user which
   of the three doors they are on.

   Both rules are inside the @supports on purpose. Suppressing the
   link's own ring is only safe where the card can take it over; a
   browser without :has() keeps the default ring from the global
   :focus-visible rule instead of losing focus indication entirely. */
@supports selector(:has(*)) {
  .door:has(.door__go:focus-visible) {
    outline: 2px solid var(--navy-600);
    outline-offset: 3px;
    border-color: rgba(17, 60, 95, 0.3);
  }
  .door:has(.door__go:focus-visible) .door__go { outline: none; }
}

/* The pointer belongs to the whole box now, not just the last line. */
.door { cursor: pointer; }

/* ---------- experience ----------
   Credibility by enumeration: the equipment actually worked on,
   set as a plain list. No numbers we cannot substantiate. */
.experience { padding-block: var(--sp-section); }
.experience__grid {
  margin-top: var(--sp-block);
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
  gap: clamp(2rem, 5vw, 5rem);
  align-items: start;
}
.experience__list {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.65rem 1.5rem;
}
.experience__list li {
  font-size: var(--fs-small);
  color: var(--steel);
  padding-left: 1.1rem;
  position: relative;
}
.experience__list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.62em;
  width: 5px;
  height: 1px;
  background: var(--navy-600);
}

/* ---------- equipment strip ----------
   Deliberately lighter than the doors: the weighing line exists,
   and must be findable, but it is not what the company sells. One
   rule, one line of text, one link — no card, no illustration. */
.equip-strip { border-block: 1px solid var(--rule); background: var(--bone); }
.equip-strip .wrap {
  padding-block: clamp(1.5rem, 3vw, 2.25rem);
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem 1.5rem;
  justify-content: space-between;
}
.equip-strip p { font-size: var(--fs-small); color: var(--steel); max-width: 62ch; }
.equip-strip strong { color: var(--navy); font-weight: 600; }
.equip-strip a {
  font-size: var(--fs-small);
  font-weight: 600;
  color: var(--accent);
  white-space: nowrap;
}
.equip-strip a::after { content: " →"; }

/* ===========================================================
   Page head — interior pages

   Every interior page opens the same way: eyebrow, h1, one lead
   paragraph, and the action. Same shape everywhere so the site
   reads as one document, and so the essential line is always
   above the fold whatever page you arrive on from a search.
=========================================================== */
.page-head { padding-block: clamp(3rem, 6vw, 5rem) clamp(2.5rem, 4vw, 3.5rem); border-bottom: 1px solid var(--rule); }
.page-head h1 { font-size: var(--fs-hero); margin-top: 1.1rem; max-width: 22ch; text-wrap: balance; }
.page-head .lead { margin-top: 1.4rem; }
.page-head__actions { display: flex; flex-wrap: wrap; gap: 0.75rem; margin-top: clamp(1.5rem, 2.5vw, 2rem); }

/* The reassurance line on /conception. It is the strongest
   argument on the whole site, so it sits inside the page head,
   above the fold, not further down with the detail. */
.page-head__pledge {
  margin-top: clamp(1.5rem, 2.5vw, 2rem);
  padding: 1.1rem 1.3rem;
  border: 1px solid var(--rule);
  border-left: 3px solid var(--accent);
  border-radius: 0 var(--radius) var(--radius) 0;
  background: var(--bone);
  max-width: 56ch;
  font-size: var(--fs-small);
  color: var(--steel);
}
.page-head__pledge strong { color: var(--navy); font-weight: 600; }

/* ===========================================================
   Spec — the two-column technical lists the interior pages use
   for materials, file formats, tooling types.
=========================================================== */
.spec { padding-block: var(--sp-section); }
.spec--tint { background: var(--bone); border-block: 1px solid var(--rule); }
.spec__grid {
  margin-top: var(--sp-block);
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: clamp(1.5rem, 3vw, 2.5rem);
}
.spec__col h3 { font-size: var(--fs-h3); margin-bottom: 0.9rem; }
.spec__col ul { display: grid; gap: 0.5rem; }
.spec__col li {
  font-size: var(--fs-small);
  color: var(--steel);
  padding-left: 1.1rem;
  position: relative;
}
.spec__col li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.62em;
  width: 5px;
  height: 1px;
  background: var(--navy-600);
}
.spec__note { margin-top: 1.1rem; font-size: var(--fs-small); color: var(--steel); }

/* The single still on /equipements/. That page carries no sequence —
   it must not compete with the three doors — so one frame of the clip
   does the work the scrub used to. Same lifted white as the page, so
   it floats rather than sitting in a box. */
.spec__figure {
  width: 100%;
  height: auto;
  max-width: 860px;
  margin: 0 auto clamp(2rem, 4vw, 3.25rem);
  background: var(--paper);
}

/* ===========================================================
   Header — identical on every page

   Five links and the call to action, which stays visible at every
   width: a maintenance lead who has decided to write should never
   have to open a menu to find the button. The links collapse into
   a disclosure below 900px; the button never does.
=========================================================== */
.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: rgba(255, 255, 255, 0.9);
  backdrop-filter: blur(14px) saturate(1.4);
  border-bottom: 1px solid var(--rule);
}
.site-header .wrap {
  min-height: 72px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}
.site-header__logo img { height: 30px; width: auto; }

.site-header__nav { display: flex; align-items: center; gap: clamp(0.75rem, 2vw, 2rem); }
.site-header__links { display: flex; align-items: center; gap: clamp(0.75rem, 1.9vw, 1.9rem); }
.site-header__links a {
  font-size: var(--fs-small);
  font-weight: 500;
  letter-spacing: -0.01em;
  color: var(--navy);
  opacity: 0.72;
  white-space: nowrap;
  transition: opacity 0.25s var(--ease);
}
.site-header__links a:hover { opacity: 1; }

/* The current page is named, not just highlighted — the underline
   tells you where you are without relying on colour alone. */
.site-header__links a[aria-current="page"] {
  opacity: 1;
  font-weight: 600;
  box-shadow: inset 0 -2px 0 var(--accent);
}

/* ---------- mobile disclosure ---------- */
.site-header__toggle {
  display: none;
  align-items: center;
  gap: 0.5rem;
  padding: 0.55rem 0.85rem;
  border: 1px solid var(--rule);
  border-radius: 999px;
  background: transparent;
  color: var(--navy);
  font-size: var(--fs-small);
  font-weight: 600;
}
.site-header__toggle svg { width: 14px; height: 14px; }

/* Six links plus the button no longer fit a laptop row. Measured with
   the current labels: the button holds one line at 1080px and wraps to
   two at 1040px, which doubles the header. So the links collapse at
   1100px — and js/main.js reads THIS rule (via the toggle's computed
   display) rather than repeating the number. */
@media (max-width: 1100px) {
  .site-header__toggle { display: inline-flex; }
  .site-header__links {
    position: absolute;
    inset: 100% 0 auto 0;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding: 0.5rem var(--gutter) 1.1rem;
    background: var(--paper);
    border-bottom: 1px solid var(--rule);
    box-shadow: 0 18px 30px -22px rgba(10, 36, 54, 0.5);
  }
  .site-header__links[hidden] { display: none; }
  .site-header__links a {
    padding: 0.85rem 0;
    opacity: 1;
    border-bottom: 1px solid var(--rule);
  }
  .site-header__links a:last-child { border-bottom: 0; }
  .site-header__links a[aria-current="page"] { box-shadow: none; color: var(--accent); }
}

@media (max-width: 560px) {
  /* The call to action keeps its place at every width, but not its
     full label: "soumission" alone still says what the button does. */
  .site-header .btn__long { display: none; }
  .site-header .btn { text-transform: capitalize; }
  .site-header .wrap { gap: 0.5rem; }
  .site-header__nav { gap: 0.5rem; }
  .site-header .btn { padding: 0.6rem 0.9rem; }
  .site-header__logo img { height: 26px; }
}

/* ===========================================================
   Sequence — the scroll-scrubbed centrepiece

   .seq is tall; .seq__stage sticks to the viewport for its whole
   height. js/main.js maps scroll progress onto both the drawn
   frame and which .seq__note is active, then hands over to the
   logo outro before releasing the page.
=========================================================== */
.seq {
  position: relative;
  height: 620vh;            /* pin length — the scroll budget */
  background: var(--paper);
}

/* A shorter pin, for /equipements/. That page is the secondary offering
   and must stay lighter than the three service pages, so its sequence
   gets less of the visitor's scroll. 520vh over 78 frames still leaves
   about 5.4vh per frame — roughly two frames per wheel notch, which is
   where this scrub stops feeling stepped. Going much shorter starts to
   look like the under-sampling fault the weighing clip already had. */
.seq--short { height: 520vh; }
.seq__stage {
  position: sticky;
  top: 0;
  height: 100vh;
  overflow: hidden;
  background: var(--paper);
}

/* --- the part: centred and large, the stage's real subject --- */
.seq__canvas {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 1;
  /* Sized against the measured content extents of the sequence:
     the part spans 18.8%–75.0% of the frame while assembled and
     11.9%–87.0% at full explosion. These widths keep both ends of
     that range clear of the side panels. */
  width: min(60vw, 880px);
  height: min(66vh, 540px);
  background: var(--paper);
}

/* --- the commentary: one panel at a time, alternating sides ---
   Text sits above the canvas so the white it paints can never
   cover a caption; the part is held to the middle of the stage
   so the two never actually meet. */
.seq__intro,
.seq__note {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 2;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

/* Intro is wider than the notes — it carries the headline while
   the part is still assembled and compact. */
.seq__intro {
  left: var(--gutter);
  width: min(26vw, 330px);
  transition: opacity 0.5s var(--ease), transform 0.5s var(--ease);
}
.seq__intro.is-hidden { opacity: 0; transform: translateY(-50%) translateX(-24px); pointer-events: none; }
.seq__intro h1 { font-size: var(--fs-hero); margin-top: 1.4rem; text-wrap: balance; }
.seq__intro p { margin-top: 1.5rem; font-size: var(--fs-lead); color: var(--steel); }

/* Each note enters from its own side and leaves the same way, so
   a fading pair never crosses over the middle — or each other. */
.seq__note {
  width: clamp(220px, 20vw, 290px);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.5s var(--ease), transform 0.55s var(--ease);
}
.seq__note[data-side="left"] { left: var(--gutter); transform: translateY(-50%) translateX(-30px); }
.seq__note[data-side="right"] { right: var(--gutter); transform: translateY(-50%) translateX(30px); text-align: right; }
.seq__note.is-active {
  opacity: 1;
  transform: translateY(-50%) translateX(0);
  pointer-events: auto;
}
.seq__note h2 { font-size: clamp(1.3rem, 1.9vw, 1.75rem); margin: 0.9rem 0 0.8rem; }
.seq__note p { color: var(--steel); font-size: var(--fs-small); }

/* --- outro: the brand resolve ---
   Unlike the rest of the sequence this block is driven by inline
   styles from js/main.js, because the mark has to land on the logo
   the video draws, and that position depends on the canvas's
   contain-fit box. CSS only sets what does not need measuring. */
.seq__outro {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
}

/* The saturated ground — still the only one on the page. */
.seq__outro-bg {
  position: absolute;
  inset: 0;
  background: linear-gradient(150deg, var(--accent-deep) 0%, var(--navy-900) 78%);
  opacity: 0;
}

/* Registered on the frame's logo, then travels to its slot inside
   the full lockup. Both copies share the wrapper's box so the
   navy → white change is a cross-fade, not a repaint. */
.seq__outro-mark { position: absolute; opacity: 0; }
.seq__outro-mark img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
.seq__outro-mark-light { filter: brightness(0) invert(1); opacity: 0; }

/* The finished lockup. Its mark sits exactly where the travelling
   mark ends, so the hand-off is invisible.

   Only /outillage-production carries the sequence now, and it runs
   without this outro — the one saturated surface on the site is the
   footer. The block is kept because the engine still supports the
   brand resolve and a future page may want it. */
.seq__outro-brand {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: clamp(200px, 24vw, 340px);
  height: auto;
  filter: brightness(0) invert(1);
  opacity: 0;
}

.seq__outro-cue {
  position: absolute;
  left: 50%;
  bottom: 2rem;
  transform: translateX(-50%);
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--steel-200);
  opacity: 0;
}

.seq__cue {
  position: absolute;
  bottom: 2rem;
  left: 50%;
  transform: translateX(-50%);
  /* Below the outro (z-index 3): the cue stays lit for the whole
     scrub, so the logo panel has to be able to cover it during the
     crossfade rather than have navy text sit on a navy ground. */
  z-index: 2;
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--navy);
  opacity: 0.9;
  transition: opacity 0.4s var(--ease);
  animation: bob 2.4s ease-in-out infinite;
}
.seq__cue.is-hidden { opacity: 0; }

/* --- reversed sequence: the resolved state is the STARTING state ---
   js/main.js drives the outro through inline styles, but it only runs
   at the end of <body>. On a page that opens on the brand rather than
   ending on it, that left a blank stage on screen for the first paint.
   These two are the state at p = 0; the first update() overrides them
   from inline, and the intro and the white-stage cue open carrying
   .is-hidden in the markup for the same reason. */
.seq--reverse .seq__outro-bg,
.seq--reverse .seq__outro-brand { opacity: 1; }
@keyframes bob {
  0%, 100% { transform: translate(-50%, 0); }
  50% { transform: translate(-50%, 5px); }
}

/* Reduced motion: unpin, stack, show everything at once. */
.seq.is-static { height: auto; }
.seq.is-static .seq__stage {
  position: static;
  height: auto;
  overflow: visible;
  display: grid;
  gap: var(--sp-block);
  max-width: var(--max-w);
  margin-inline: auto;
  padding: var(--sp-section) var(--gutter) var(--sp-block);
}
.seq.is-static .seq__intro,
.seq.is-static .seq__note {
  position: static;
  width: auto;
  max-width: 60ch;
  opacity: 1;
  transform: none;
  text-align: left;
  /* A reversed page ships .seq__intro already carrying .is-hidden, so
     the first paint is not a headline over a blank stage. This layout
     overrides its opacity and transform by specificity, but not the
     pointer-events that came with it — without this the static
     headline is there and unselectable. */
  pointer-events: auto;
}
.seq.is-static .seq__canvas {
  position: static;
  transform: none;
  width: 100%;
  height: 58vh;
  order: -1;
}
.seq.is-static .seq__outro,
.seq.is-static .seq__cue { display: none; }

/* ===========================================================
   Formats — integrating a new product on an existing line.
   This is the core of the business, so it gets the widest
   section and the most room on the page.
=========================================================== */
.formats { background: var(--bone); padding-block: var(--sp-section); border-block: 1px solid var(--rule); }
.formats__grid {
  margin-top: var(--sp-block);
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(1rem, 2vw, 1.5rem);
}
.format {
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: clamp(1.4rem, 2.2vw, 1.9rem);
  display: flex;
  flex-direction: column;
  transition: border-color 0.3s var(--ease), transform 0.3s var(--ease);
}
.format:hover { border-color: rgba(17, 60, 95, 0.28); transform: translateY(-2px); }
.format svg { width: 100%; height: auto; aspect-ratio: 3 / 2; margin-bottom: 1.4rem; }
.format h3 { font-size: var(--fs-h3); margin-bottom: 0.55rem; }
.format p { font-size: var(--fs-small); color: var(--steel); }

/* ===========================================================
   Process — datum axis, drawn on white
=========================================================== */
.process { background: var(--paper); padding-block: var(--sp-section); }
.process__axis { position: relative; margin-top: clamp(3.5rem, 7vw, 6rem); }
/* Full-bleed datum line: crosses the container margins so the
   section is tied to the page edges instead of floating in it. */
.process__axis::before {
  content: "";
  position: absolute;
  top: 2.35rem;
  left: 0;
  right: 0;
  height: 1px;
  background: var(--rule);
}
/* The station count varies by page — four on /usinage-sur-mesure/,
   five on /conception/ — and they all hang off one datum rule, so they
   have to sit on ONE row. A fixed four-column grid wrapped the fifth
   station onto a second row below the axis, where it read as an
   afterthought rather than a step. Columns are counted from the
   markup instead, via the count set on the element. */
.process__stations {
  display: grid;
  grid-template-columns: repeat(var(--stations, 4), minmax(0, 1fr));
  gap: clamp(1.25rem, 3vw, 2.5rem);
}
.station { position: relative; padding-top: 3.5rem; }
.station__idx {
  position: absolute;
  top: 0;
  left: 0;
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  letter-spacing: 0.14em;
  color: var(--navy-600);
}
.station__tick { position: absolute; top: 1.85rem; left: 0; width: 1px; height: 1rem; background: var(--navy-600); opacity: 0.5; }
.station__tick::after {
  content: "";
  position: absolute;
  top: 0.35rem;
  left: -2.5px;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--navy);
}
.station h3 { font-size: var(--fs-h3); }
.station p { margin-top: 0.6rem; font-size: var(--fs-small); color: var(--steel); max-width: 30ch; }

/* ===========================================================
   Offers
=========================================================== */
.offers { background: var(--bone); padding-block: var(--sp-section); border-top: 1px solid var(--rule); }
.offers__grid {
  margin-top: var(--sp-block);
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(1.25rem, 2.5vw, 2rem);
}
.offer {
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: clamp(1.75rem, 3vw, 2.5rem);
  display: flex;
  flex-direction: column;
}
.offer h3 { font-size: clamp(1.25rem, 1.9vw, 1.6rem); margin: 0.9rem 0 0.85rem; }
.offer p { color: var(--steel); max-width: 40ch; }
.offer__list { margin-top: 1.5rem; display: flex; flex-wrap: wrap; gap: 0.5rem; }
.offer__list li {
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  letter-spacing: 0.06em;
  color: var(--navy-600);
  background: var(--bone);
  border: 1px solid var(--rule);
  border-radius: 999px;
  padding: 0.4em 0.85em;
}

/* ===========================================================
   Founder — the credibility the company does not have yet
=========================================================== */
.founder { background: var(--paper); padding-block: var(--sp-section); border-top: 1px solid var(--rule); }
.founder .wrap {
  display: grid;
  grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
  gap: clamp(2rem, 5vw, 4.5rem);
  align-items: start;
}
.founder__body p { color: var(--steel); max-width: 52ch; }
.founder__body p + p { margin-top: 1.1rem; }
.founder__body strong { color: var(--ink); font-weight: 600; }
.founder__tags { margin-top: 1.75rem; display: flex; flex-wrap: wrap; gap: 0.5rem; }
.founder__tags li {
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  letter-spacing: 0.06em;
  color: var(--navy-600);
  background: var(--bone);
  border: 1px solid var(--rule);
  border-radius: 999px;
  padding: 0.4em 0.85em;
}
@media (max-width: 900px) { .founder .wrap { grid-template-columns: 1fr; } }

/* ===========================================================
   Cross-branch link — the same block on both sequence pages
=========================================================== */
.crosslink { background: var(--bone); padding-block: var(--sp-block); border-top: 1px solid var(--rule); }
.crosslink__card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.5rem;
  max-width: 34rem;
  padding: clamp(1.4rem, 2.5vw, 2rem) clamp(1.5rem, 2.5vw, 2.25rem);
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  transition: border-color 0.35s var(--ease), transform 0.35s var(--ease);
}
.crosslink__card:hover { border-color: rgba(17, 60, 95, 0.3); transform: translateY(-2px); }
.crosslink__name {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.25rem, 2vw, 1.6rem);
  letter-spacing: -0.02em;
  line-height: 1.1;
}
.crosslink__note { font-size: var(--fs-small); color: var(--steel); max-width: 44ch; }
.crosslink__go {
  margin-top: 0.4rem;
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--navy);
  display: inline-flex;
  gap: 0.5rem;
}
.crosslink__card:hover .crosslink__go span { transform: translateX(4px); }
.crosslink__go span { transition: transform 0.35s var(--ease); }

/* ===========================================================
   Contact
=========================================================== */
/* Tighter at the bottom than a normal section: this is the only
   place where white meets the dark footer, and a full --sp-section
   there reads as a gap rather than as breathing room. */
.contact { background: var(--paper); padding-block: var(--sp-section) calc(var(--sp-section) * 0.55); }
.contact .wrap {
  display: grid;
  grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
  gap: clamp(2.5rem, 6vw, 5.5rem);
  align-items: start;
}
.contact__intro h2 { font-size: var(--fs-h2); margin-top: 1.1rem; max-width: 14ch; }
/* On /contact/ the page head already carries the h1, so this heading
   is a section label rather than the page title — it can be smaller
   and its column wider. */
.contact--page { padding-block: clamp(3rem, 5vw, 4.5rem) calc(var(--sp-section) * 0.55); }
.contact--page .contact__intro h2 { font-size: var(--fs-hero); max-width: 18ch; }
.contact--page .contact__intro p { max-width: 44ch; font-size: var(--fs-body); }

/* Coordinates in plain text. A maintenance lead who has decided to
   phone should find the number without hunting, so these are set at
   body size with the label in the mono voice, not squeezed into
   fine print. */
.contact__coords { margin-top: 1.75rem; display: grid; gap: 1rem; }
.contact__coords div { display: grid; gap: 0.2rem; }
.contact__coords dt,
.contact__coords .contact__coords-label {
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--navy-600);
}
.contact__coords a {
  font-size: var(--fs-lead);
  font-weight: 600;
  color: var(--navy);
  text-decoration: underline;
  text-decoration-color: var(--steel-200);
  text-underline-offset: 4px;
}
.contact__coords a:hover { text-decoration-color: var(--accent); color: var(--accent); }
.contact__intro p { margin-top: 1.4rem; color: var(--steel); max-width: 36ch; font-size: var(--fs-lead); }
.contact__location {
  display: block;
  margin-top: 2.75rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--rule);
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--navy-600);
}

.form {
  background: var(--bone);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: clamp(1.75rem, 3vw, 2.5rem);
}
.form__row { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
.field { margin-bottom: 1.15rem; }
.field label {
  display: block;
  font-size: var(--fs-small);
  font-weight: 600;
  letter-spacing: -0.01em;
  margin-bottom: 0.45rem;
  color: var(--navy);
}
.field label span { font-weight: 400; color: var(--steel); }
.field input,
.field select,
.field textarea {
  width: 100%;
  font: inherit;
  padding: 0.8rem 0.95rem;
  border: 1px solid var(--steel-200);
  border-radius: 10px;
  background: var(--paper);
  color: var(--ink);
  transition: border-color 0.25s var(--ease);
}
.field input:focus, .field select:focus, .field textarea:focus { border-color: var(--navy-600); outline: none; }

/* The native select arrow is drawn by the OS and looks nothing like
   the rest of the form, so it is suppressed and replaced by one drawn
   in the same navy as the labels. */
.field select {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8' fill='none'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5' stroke='%232c5c82' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 1rem center;
  background-size: 12px 8px;
  padding-right: 2.6rem;
}

/* The file input keeps its native button — a custom one would have to
   re-implement keyboard access and the chosen-file name for nothing.
   Only the chrome around it is brought into line. */
.field input[type="file"] {
  padding: 0.65rem 0.7rem;
  font-size: var(--fs-small);
  background: var(--paper);
  cursor: pointer;
}
.field input[type="file"]::file-selector-button {
  font: inherit;
  font-weight: 600;
  margin-right: 0.9rem;
  padding: 0.45rem 0.9rem;
  border: 1px solid var(--rule);
  border-radius: 999px;
  background: var(--bone);
  color: var(--navy);
  cursor: pointer;
}
.field input[type="file"]::file-selector-button:hover { border-color: var(--navy-600); }
.field__hint { margin-top: 0.5rem; font-size: var(--fs-small); color: var(--steel); }
/* Points at the field the status line is talking about. Colour is not
   the only signal — the message names the field too. */
/* Full `border` shorthand, matching how the base rule declares it —
   a bare border-color longhand did not take here. */
.field input[aria-invalid="true"],
.field textarea[aria-invalid="true"] { border: 1px solid #b3492f; background: #fdf6f4; }
.field textarea { resize: vertical; min-height: 124px; }
.form__submit { display: flex; align-items: center; gap: 1rem; margin-top: 0.75rem; flex-wrap: wrap; }
.form__status { font-size: var(--fs-small); color: var(--steel); }
.form__status[data-state="ok"] { color: #2f7a4f; }
.form__status[data-state="error"] { color: #b3492f; }

/* ===========================================================
   Footer — the one dark surface on the page
=========================================================== */
.site-footer { background: var(--navy-900); padding-block: clamp(2.5rem, 5vw, 3.5rem); }
.site-footer .wrap {
  display: flex;
  flex-wrap: wrap;
  gap: 1.5rem;
  align-items: center;
  justify-content: space-between;
}
.site-footer__logo img { height: 24px; width: auto; filter: brightness(0) invert(1); opacity: 0.85; }
/* Wraps: this nav now carries cross-branch links, whose labels are far
   longer than the section anchors it used to hold. */
.site-footer__nav { display: flex; flex-wrap: wrap; gap: 0.75rem 1.75rem; }
.site-footer__nav a { font-size: var(--fs-small); color: var(--steel-200); opacity: 0.75; transition: opacity 0.25s var(--ease); }
.site-footer__nav a:hover { opacity: 1; }
.site-footer__meta {
  width: 100%;
  margin-top: 1.75rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--rule-dark);
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--steel-200);
  opacity: 0.7;
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.5rem;
}

/* ===========================================================
   Responsive
=========================================================== */
@media (max-width: 1040px) {
  .formats__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .experience__grid { grid-template-columns: 1fr; }
}

@media (max-width: 900px) {
  /* Three doors do not survive a tablet: below this they stack, and
     stacked they still read in the same order as the sentence in the
     hero that introduces them. */
  .doors__grid { grid-template-columns: 1fr; }
  .contact .wrap { grid-template-columns: 1fr; }
  .offers__grid { grid-template-columns: 1fr; }
  .form__row { grid-template-columns: 1fr; }

  /* Axis rotates from horizontal datum to vertical, keeping the
     drafting device instead of degrading to a plain list. */
  .process__stations { grid-template-columns: 1fr; gap: 0; }
  .process__axis::before { top: 0; bottom: 0; left: 3px; right: auto; width: 1px; height: auto; }
  .station { padding: 0 0 2.25rem 2.25rem; }
  .station:last-child { padding-bottom: 0; }
  .station__idx { position: static; display: block; margin-bottom: 0.35rem; }
  .station__tick { top: 0.5rem; left: -2.25rem; width: 1rem; height: 1px; }
  .station__tick::after { top: -2.5px; left: 0.35rem; }
  .station p { max-width: 46ch; }

}

/* --- Sequence: stacked layout ---
   Side panels need real room. Measured against the part's actual
   extents (18.8%–75.0% assembled, 11.9%–87.0% exploded), the
   columns only clear it from about 1400px up; below that the
   panels would sit on the machined parts. So the part moves up,
   the commentary reads underneath it, and the panels still enter
   from alternating sides — the direction survives the breakpoint
   even though the position does not. */
@media (max-width: 1400px) {
  .seq { height: 560vh; }
  .seq--short { height: 470vh; }
  .seq__canvas {
    top: 36%;
    width: calc(100% - var(--gutter) * 2);
    height: min(46vh, 420px);
  }
  .seq__intro,
  .seq__note {
    top: auto;
    /* Leaves room for the scroll cue, which stays lit until the logo. */
    bottom: 11vh;
    left: var(--gutter);
    right: var(--gutter);
    width: auto;
    max-width: 46rem;
    transform: none;
    text-align: left;
  }
  .seq__intro.is-hidden { transform: translateX(-24px); }
  .seq__note[data-side="left"] { transform: translateX(-26px); }
  .seq__note[data-side="right"] { transform: translateX(26px); text-align: left; }
  .seq__note.is-active { transform: translateX(0); }
}

/* --- .seq--wide: a subject that reaches the frame edge ---
   The weighing machine is a long conveyor. Measured across every
   frame, it spans 0%–89% of the frame width — at its widest sweep it
   touches the left edge outright. Side panels are therefore only
   possible if the canvas itself is held narrower than the viewport by
   more than a panel's width, which is what the sizes below do. That
   costs some size on the machine, and it is the price of putting the
   commentary beside it rather than under it.

   Placed after the stacked block on purpose: it has to win back the
   side layout between 1250px and 1400px. */
@media (min-width: 1251px) {
  .seq--wide .seq__canvas {
    top: 50%;
    width: min(54vw, 780px);
    height: min(70vh, 620px);
  }
  .seq--wide .seq__intro,
  .seq--wide .seq__note {
    top: 50%;
    bottom: auto;
    max-width: none;
    transform: translateY(-50%);
  }
  /* Column widths are set by how far the machine reaches at the frames
     each panel is actually on screen, not by one worst case. The note
     windows are timed so that no left-hand panel is up during the
     widest sweep — that one is on the right, where there is room.

     The intro is the panel the reversed clip moved: it used to play
     over the opening frames, where the machine is compact and its left
     edge sits at 26% of the frame, and it was sized min(30vw, 420px)
     for that. Reversed it plays over frames 63–77 instead — the second
     machine, after the cut — whose left edge reaches 14.6%. At 1251px,
     the tightest viewport that still gets side panels, the old column
     overlapped it by 51px. The clamp below keeps 24px of clearance
     there and 27px at 1440px. */
  .seq--wide .seq__intro {
    left: var(--gutter);
    right: auto;
    width: clamp(260px, 24vw, 360px);
  }
  .seq--wide .seq__note { width: clamp(230px, 22vw, 310px); }
  .seq--wide .seq__intro.is-hidden { transform: translateY(-50%) translateX(-24px); }
  .seq--wide .seq__note[data-side="left"] {
    left: var(--gutter);
    right: auto;
    text-align: left;
    transform: translateY(-50%) translateX(-26px);
  }
  .seq--wide .seq__note[data-side="right"] {
    left: auto;
    right: var(--gutter);
    text-align: right;
    transform: translateY(-50%) translateX(26px);
  }
  .seq--wide .seq__note.is-active { transform: translateY(-50%) translateX(0); }
}

@media (max-width: 560px) {
  .formats__grid { grid-template-columns: 1fr; }
  .experience__list { grid-template-columns: 1fr; }
  .equip-strip .wrap { flex-direction: column; align-items: flex-start; }
  .site-footer .wrap { flex-direction: column; align-items: flex-start; }
  .seq__canvas { top: 30%; height: 34vh; }
}
