/* ============================================================
   ALPANTEK — precision metal fabrication
   ------------------------------------------------------------
   Near-monochrome by rule, not by mood. Depth comes from small
   steps between white, off-white and light grey rather than
   from shadow, the way a printed engineering catalogue does.
   --cut is the only chromatic value and marks nothing but
   keyboard focus. Don't spend it on buttons or headings.
   ============================================================ */

:root{
  /* grounds — deliberately close together */
  --void:      #f5f5f3;   /* page */
  --floor:     #ffffff;   /* main content sections */
  --floor-hi:  #eeeeec;   /* secondary panels and cards */
  --bar:       #fbfbfa;   /* top bar and the paper panels under it */
  --paper:     #fafaf9;   /* the warmer end of the same paper */
  --paper-2:   #f1f1ef;   /* its shaded edge, for the stage gradients */

  /* Accent — deep teal. One chromatic value for the whole interface,
     spent only on state: focus, active, selected. Never a background,
     never a heading, never decoration. */
  --accent:      #003f47;
  --accent-ink:  #ffffff;   /* 11.6:1 on the accent — AAA */
  --accent-soft: rgba(0,63,71,.10);

  /* retired: the orange marked applied heat, which the dark theme needed
     and the light one does not. Aliased so nothing breaks. */
  --cut:       var(--accent);

  /* type */
  --ink:       #171717;   /* headings */
  --ink-strong:#181a1b;   /* technical values: specs, references, counters */
  --ink-2:     #4f5356;   /* body */
  --ink-3:     #5f6468;   /* secondary technical labels — 5.6:1 on page, AA */

  --line:      #d5d7d8;   /* borders */
  --line-2:    #e3e5e6;   /* dividers, the quieter of the two */

  --display: "IBM Plex Sans Condensed", "Arial Narrow", Helvetica, sans-serif;
  --body:    "IBM Plex Sans", system-ui, -apple-system, Segoe UI, sans-serif;
  --mono:    "IBM Plex Mono", ui-monospace, Consolas, monospace;
}

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

html{ scroll-behavior:smooth; }

body{
  margin:0;
  background:var(--void);
  color:var(--ink);
  font-family:var(--body);
  font-size:16px;
  line-height:1.55;
  -webkit-font-smoothing:antialiased;
  overflow-x:hidden;
}

h1,h2,h3,h4{ margin:0; font-family:var(--display); font-weight:700; letter-spacing:-.015em; }
p{ margin:0; }
a{ color:inherit; }

.sr{ position:absolute; width:1px; height:1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; }

.skip{
  position:fixed; top:8px; left:50%; transform:translate(-50%,-160%);
  z-index:200; background:var(--ink); color:var(--void);
  padding:.6rem 1rem; font:500 .8rem/1 var(--mono); letter-spacing:.08em;
  text-transform:uppercase; text-decoration:none; border-radius:2px;
  transition:transform .2s;
}
.skip:focus{ transform:translate(-50%,0); }

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

/* every measurement on this site is monospaced, like a machine readout */
.eyebrow{
  font:500 .6875rem/1 var(--mono);
  letter-spacing:.2em; text-transform:uppercase; color:var(--ink-3);
  margin:0 0 1.1rem;
}

/* ============================ top bar ============================ */

/* Solid and light. The old bar relied on mix-blend-mode:difference to
   invert itself against whatever it passed over; on a light page that
   inverts the wrong way, so the bar now carries its own ground. */
/* ------------------------- the cut -------------------------
   The mark is not laid on the bar; it is taken out of it. The bar's surface
   lives on ::before and is masked with the logo silhouette subtracted, so
   the shape is an actual hole and whatever the header passes over shows
   through it. A hairline drawn on the same geometry scores the edge, which
   is what keeps the cut readable on the inner pages, where the ground
   behind the bar is nearly the bar's own value.

   Both layers are driven by the same offset the bar is padded by, so the
   hole and the line stay registered without either being hand-placed. */
/* The cut scales with the viewport. Held at a fixed size it took a third of the
   width of a phone and hung 41px into the content below; the floor keeps it
   legible at the narrow end, the ceiling stops it growing past the size the
   header was designed around. Hole and hairline both read this, so they
   scale together and stay registered. */
:root{
  --cut-size:clamp(78px, 14vw, 140px);
  --cut-x:clamp(1rem,3.5vw,2.6rem);
  --bar-pb:1.1rem;
}

.topbar{
  position:fixed; inset:0 0 auto 0; z-index:100;
  display:flex; align-items:center; justify-content:space-between;
  padding:1.1rem var(--cut-x) var(--bar-pb);
  background:transparent;          /* the surface moved to ::before */
}
.topbar::before{
  content:''; position:absolute; inset:0; z-index:-1;
  padding:inherit;                 /* content box mirrors the bar's own */
  background:var(--bar);
  border-bottom:1px solid var(--line-2);
  -webkit-mask-image:url("media/logos/mark-cut-mask.svg"), linear-gradient(#000,#000);
          mask-image:url("media/logos/mark-cut-mask.svg"), linear-gradient(#000,#000);
  -webkit-mask-repeat:no-repeat, no-repeat;
          mask-repeat:no-repeat, no-repeat;
  /* Hole and hairline are both hung from the bar's lower edge at the same
     left offset the bar is padded by, so they stay registered without either
     being placed by hand — including on the inner pages, whose bar carries
     more padding below than above. */
  -webkit-mask-origin:border-box, border-box;
          mask-origin:border-box, border-box;
  -webkit-mask-position:var(--cut-x) top, 0 0;
          mask-position:var(--cut-x) top, 0 0;
  -webkit-mask-size:var(--cut-size) var(--cut-size), 100% 100%;
          mask-size:var(--cut-size) var(--cut-size), 100% 100%;
  /* subtract the silhouette from the surface */
  -webkit-mask-composite:xor;
          mask-composite:exclude;
}
/* the scored edge, sitting exactly over the hole */
/* Masking is the whole effect; where it is unsupported the bar stays solid
   and the mark reads as a drawn outline rather than a cut. Nothing breaks. */
@supports not ((mask-composite:exclude) or (-webkit-mask-composite:xor)){
  .topbar{ background:var(--bar); border-bottom:1px solid var(--line-2); }
  .topbar::before{ display:none; }
}

/* The cut is the whole mark now; the link's name comes from its aria-label,
   which is what carries "Alpantek" to assistive technology.

   The mark reserves its width in the row but takes no height: the cut is
   absolutely placed, so a 60px silhouette can sit taller than the bar's
   content box without the bar growing to fit it. */
.mark{
  position:relative;
  text-decoration:none;
  flex:none;
  width:var(--cut-size);
  align-self:stretch;
  /* stretch reaches the content box; this carries it the rest of the way to
     the bar's lower edge, which is where the cut is hung from */
  margin-bottom:calc(-1 * var(--bar-pb));
}
/* Hung from the bar's top edge rather than its bottom: at 120px the mark is
   taller than the 72px bar, so anchoring it to the lower edge would push its
   top 48px above the viewport and cut the crown off. */
.mark__cut{
  position:absolute; left:0; top:calc(-1 * 1.1rem); bottom:auto;
  width:var(--cut-size); height:var(--cut-size);
  /* The line art is a mask, not an image, so the scored edge takes its
     colour from the token rather than from a hex baked into the SVG. The
     asset carried a literal copy of --ink-3, which would have drifted the
     first time the token changed. */
  background-color:var(--ink-3);
  -webkit-mask:url("media/logos/mark-cut-line.svg") center / contain no-repeat;
          mask:url("media/logos/mark-cut-line.svg") center / contain no-repeat;
}
/* 1.6vw rather than 1.9vw: the bar was already flush against its right
   padding at the narrow end of desktop, so the cut had to be paid for
   somewhere. It still opens to the same 1.7rem once there is room. */
.topnav{ display:flex; align-items:center; gap:clamp(.85rem,1.6vw,1.7rem); }
.topnav a{
  font:500 .75rem/1 var(--mono); white-space:nowrap;
  letter-spacing:.12em; text-transform:uppercase; text-decoration:none;
  color:var(--ink-2); transition:color .2s;
}
.topnav a:hover{ color:var(--ink); }
.topnav__cta{
  color:var(--ink) !important; white-space:nowrap;
  border:1px solid currentColor; padding:.55rem .9rem; border-radius:2px;
}
/* ---------------------------- mobile navigation ----------------------------
   Below 1000px the row cannot hold five labels, the language codes and the
   CTA, so the nav becomes a panel under the bar. A mono word rather than a
   hamburger: this interface labels everything else in words, and a drawn
   glyph would be the only icon on the site. */
.navtoggle{ display:none; }
@media (max-width:1000px){
  .topbar{ position:fixed; }
  .navtoggle{
    display:block;
    background:none; border:1px solid var(--line); border-radius:2px;
    padding:.72rem 1rem; min-height:44px;
    font:500 .75rem/1 var(--mono);
    letter-spacing:.12em; text-transform:uppercase;
    color:var(--ink); cursor:pointer;
    transition:border-color .2s, color .2s;
  }
  .navtoggle:hover{ border-color:var(--accent); color:var(--accent); }

  /* The panel hangs below the bar rather than sitting in its flow, and moves
     on opacity and transform only. Animating its height instead would run
     layout on every frame of the open, which is exactly the work a phone
     has least of. Links leave the tab order via inert, not via display. */
  .topnav{
    position:absolute; top:100%; left:0; right:0;
    flex-direction:column; align-items:stretch;
    gap:0;
    padding:0 clamp(1rem,3.5vw,2.6rem) 1.2rem;
    background:var(--bar);
    border-bottom:1px solid var(--line-2);
    opacity:0; visibility:hidden;
    transform:translateY(-6px);
    transition:opacity .24s ease, transform .24s cubic-bezier(.22,.61,.36,1),
               visibility 0s linear .24s;
  }
  .topbar.is-open .topnav{
    opacity:1; visibility:visible; transform:none;
    transition:opacity .24s ease, transform .24s cubic-bezier(.22,.61,.36,1);
  }
  .topnav a{
    display:flex; align-items:center; min-height:48px;
    border-top:1px solid var(--line-2);
    font-size:.8125rem;
  }
  .topnav a:first-of-type{ border-top:0; }
  /* the CTA keeps its outline but spans the panel */
  .topnav__cta{
    justify-content:center; border:1px solid currentColor;
    margin:1.1rem 0 1.2rem; border-top-width:1px;
  }
}
@media (prefers-reduced-motion:reduce){
  .topnav{ transition:none; }
}

/* ============================ hero film ============================
   Pinned section whose scroll distance maps onto the video timeline.
   JS sets the height from the clip duration once metadata arrives. */

/* The footage is daylight-bright, so the hero joins the light theme rather
   than sitting on it as the one dark block. No local token scope: the page
   tokens apply here as everywhere, and the type is charcoal like the rest
   of the site. The ground is the page ground, never a solid panel, so the
   video is what the visitor sees behind the content. */
/* .cinema spans the hero and Capabilities. Its backdrop sticks for the whole
   run, so both sections scroll over one continuous picture instead of the
   video ending where the hero does. */
.cinema{ position:relative; background:var(--void); }
.cinema__bg{
  /* Sticks for the whole run, but the negative bottom margin cancels its
     contribution to flow, so the sections below start at the top of .cinema
     and scroll over it. The previous approach pulled siblings up with
     margin-top:-100vh, which broke the sticky offset — Capabilities was
     already 2733px past the viewport before the film finished. */
  position:sticky; top:0; z-index:0;
  height:100vh; margin-bottom:-100vh;
  overflow:hidden;
  pointer-events:none;
  will-change:opacity;   /* JS fades this out as Capabilities ends */
}

/* everything in the run sits above the backdrop and stays transparent —
   an opaque section here would blank the video out */
.cinema > section{ position:relative; z-index:1; background:transparent; }

/* the hero's own scroll length still drives how far the film is stretched */
.filmstrip{
  position:relative;
  height:220vh;            /* the hero holds while Capabilities comes up */
}
.filmstrip__stage{
  position:sticky; top:0;
  height:100vh;
  background:transparent;
  pointer-events:none;     /* the film is a backdrop, not a click target */
}
.cinema__img{
  position:absolute; inset:0;
  width:100%; height:100%;
  object-fit:cover;
  opacity:0; transition:opacity .6s;
  /* the hall is already bright and close to the palette; only a touch of
     saturation comes off so charcoal type stays the darkest thing in frame */
  filter:saturate(.88) contrast(1.02);
  object-position:center 42%;   /* keeps the cranes in frame as it crops */
}
.cinema__img{ opacity:1; }

/* A light wash, not a curtain: enough to carry charcoal type over a moving
   picture, little enough that the shop is still plainly visible through it.
   Weakest in the centre where the footage does its work, stronger at the
   edges, with a band under the header and a fade into the page below. */
.filmstrip__scrim{
  position:absolute; inset:0;
  background:
    radial-gradient(88% 74% at 50% 46%,
      rgba(245,245,243,.22) 0%, rgba(245,245,243,.40) 62%, rgba(238,238,236,.58) 100%),
    linear-gradient(180deg,
      rgba(251,251,250,.72) 0%, rgba(245,245,243,.14) 20%,
      rgba(245,245,243,.20) 66%, var(--void) 100%);
}

/* transport readout — the film's own position, in the page's own vocabulary */
.filmstrip__meta{
  position:absolute; left:50%; bottom:clamp(1.8rem,5vh,3.2rem);
  transform:translateX(-50%);
  width:min(420px,62vw);
  opacity:0; transition:opacity .5s;
}
.filmstrip.is-ready .filmstrip__meta{ opacity:1; }
.filmstrip__bar{
  height:1px; background:var(--line);
  position:relative; overflow:hidden;
}
.filmstrip__bar i{
  position:absolute; inset:0 auto 0 0;
  width:0%; display:block;
}
/* how much of the film has downloaded — scrubbing is held inside this */
.filmstrip__buf{ background:var(--ink-3); }
/* where the scroll has taken it */
.filmstrip__pos{ background:var(--ink); }
.filmstrip__tc{
  margin-top:.7rem; text-align:center;
  font:400 .625rem/1 var(--mono);
  letter-spacing:.2em; color:var(--ink-3);
}

/* ============================ overlay copy ============================ */

.hero{
  position:absolute; left:50%; top:50%;
  transform:translate(-50%,-50%);
  width:min(1100px,88vw); text-align:center;
  z-index:6;
}
.hero__title{
  display:flex; flex-direction:column;
  font-size:clamp(2.5rem,8.2vw,7rem);
  line-height:.94; letter-spacing:-.025em;
  text-transform:uppercase;
  /* Resolved here, not inherited: body computes color from the root --ink
     once, and an inherited computed value cannot pick up the lighter --ink
     scoped to .filmstrip. Without this the headline goes near-black on the
     dark footage. */
  color:var(--ink);
  /* A soft halo carried by the type itself. Darkening the whole scrim would
     hold the type up too, but it would flatten the cut spark — and the spark
     is the reason this footage works. */
  text-shadow:0 1px 0 rgba(255,255,255,.6), 0 2px 30px rgba(255,255,255,.85);
}
.hero__title span:last-child{ color:var(--ink-2); }
.hero__sub{
  margin:0 0 1.7rem;
  font:400 .8125rem/1 var(--mono);
  letter-spacing:.2em; text-transform:uppercase; color:var(--ink-2);
  /* stacked, tight-to-wide: thin mono glyphs need a dense halo close in,
     or they dissolve into a bright frame */
  text-shadow:0 0 3px rgba(255,255,255,.95), 0 1px 8px rgba(255,255,255,1), 0 0 22px rgba(255,255,255,.9);
}
.hero__cue{
  margin:0 0 1.1rem;
  display:flex; align-items:center; justify-content:center; gap:.9rem;
  font:400 .6875rem/1 var(--mono);
  letter-spacing:.2em; text-transform:uppercase; color:var(--ink-2);
  /* stacked, tight-to-wide: thin mono glyphs need a dense halo close in,
     or they dissolve into a bright frame */
  text-shadow:0 0 3px rgba(255,255,255,.95), 0 1px 8px rgba(255,255,255,1), 0 0 22px rgba(255,255,255,.9);
}
.hero__cueline{
  display:block; width:52px; height:1px; background:var(--ink-3);
  transform-origin:left; animation:cue 2.4s ease-in-out infinite;
}
@keyframes cue{ 0%,100%{ transform:scaleX(.25); opacity:.4 } 50%{ transform:scaleX(1); opacity:1 } }

/* ============================ content blocks ============================ */

.block{
  padding:clamp(4.5rem,13vh,9rem) clamp(1.2rem,5vw,4rem);
  border-top:1px solid var(--line-2);
}
.block--alt{ background:var(--floor); }

.block__head{ max-width:56rem; margin-bottom:clamp(2.6rem,5vw,4rem); }
.block__title{
  font-size:clamp(1.9rem,4.6vw,3.3rem);
  line-height:1.04; text-transform:uppercase; margin-bottom:1.15rem;
}
.block__lead{ color:var(--ink-2); max-width:52ch; font-size:1.0625rem; }

/* capabilities */
.caps{
  display:grid; gap:1px;
  grid-template-columns:repeat(auto-fit,minmax(255px,1fr));
}
/* Hairlines are drawn per card rather than bled through from the container,
   so a part-filled last row leaves an empty space, not a lit panel. */
.cap{
  position:relative;   /* anchors the machine-maker mark to the corner */
  background:var(--void);
  box-shadow:0 0 0 1px var(--line-2);
  padding:2rem 1.7rem 1.7rem;
  display:flex; flex-direction:column;
}
.cap__name{ font-size:1.32rem; margin-bottom:.75rem; }
/* Machine-maker mark, in the card's top-right corner. It sits in the top
   padding band rather than in the heading row, so the title keeps the full
   column and never wraps around it — Italian and Albanian run longer.
   Height matches the padding exactly, which is what squares the mark with
   the card's top edge. */
.cap__logo{
  position:absolute; top:0; right:1.7rem;
  height:2rem; width:auto;
}
/* dropped clear of the card edge. The heading it then sits beside is short
   enough that the two never meet across the column. */
.cap__logo--low{ top:15px; }
/* A very wide lockup set to the shared height runs most of the way across the
   column and outweighs the rest of the row. These are held to width instead. */
.cap__logo--wide{ height:1rem; }
.cap__body{ color:var(--ink-2); font-size:.9375rem; margin-bottom:1.5rem; }
/* specs sit on the baseline of the card, so they line up across a row
   however long the description above them runs */
.spec{ margin:auto 0 0; border-top:1px solid var(--line-2); padding-top:1rem; }
.spec div{
  display:flex; justify-content:space-between; align-items:baseline;
  gap:1rem; padding:.34rem 0;
}
.spec dt{
  font:400 .625rem/1 var(--mono); letter-spacing:.14em;
  text-transform:uppercase; color:var(--ink-3);
}
.spec dd{ margin:0; font:500 .8125rem/1 var(--mono); color:var(--ink-strong); }
/* a term carrying several values stacks them under one another, still ranged
   right so the column edge holds. line-height:1 is set for single values and
   would collide once they stack. */
.spec__multi{ list-style:none; margin:0; padding:0; text-align:right; }
.spec__multi li{ line-height:1.65; }

/* ============================ about ============================ */

/* the film gives the homepage header something to sit on; inner pages need
   their own ground, and difference blending has nothing to invert against */
/* Opaque behind the links themselves, fading only below them. The homepage
   header floats over film; here it sits over prose, and text scrolling under
   a half-transparent bar reads as a printing fault. */
/* The ground is carried by .topbar::before on every page now, so this must
   not paint its own — a background here would fill the cut back in. */
/* .topbar--solid is gone. It existed to undo a mix-blend-mode the bar no
   longer uses, and carried extra bottom padding that made the inner-page
   headers taller than the homepage. One header, one height, every page. */
.topnav a.is-here{ color:var(--ink); }

/* --- scroll reveal --- */
[data-reveal]{
  opacity:0;
  transform:translateY(16px);
  transition:opacity .7s cubic-bezier(.22,.61,.36,1), transform .7s cubic-bezier(.22,.61,.36,1);
}
[data-reveal].is-in{ opacity:1; transform:none; }
/* nothing may stay hidden if the observer never runs */
.no-js [data-reveal]{ opacity:1; transform:none; }

.masthead{
  padding:clamp(8rem,20vh,13rem) clamp(1.2rem,5vw,4rem) clamp(3.5rem,9vh,6rem);
  max-width:64rem;
}
.masthead__title{
  display:flex; flex-direction:column;
  font-size:clamp(2.6rem,8vw,6.4rem);
  line-height:.95; letter-spacing:-.025em; text-transform:uppercase;
  margin-bottom:2.2rem;
}
.masthead__title span:last-child{ color:var(--ink-2); }
.masthead__lead{
  font-size:clamp(1.0625rem,1.7vw,1.32rem);
  line-height:1.6; color:var(--ink-2); max-width:48ch;
}

/* --- an essay block: number rail, prose column, then a plate --- */
.essay{
  padding:clamp(3.5rem,10vh,7rem) clamp(1.2rem,5vw,4rem);
  border-top:1px solid var(--line-2);
}
.essay--alt{ background:var(--floor); }

.essay__inner{
  display:grid;
  grid-template-columns:12rem minmax(0,1fr);
  gap:0 clamp(2rem,5vw,5rem);
  align-items:start;
  max-width:74rem;
}
.essay__n{
  font:500 .6875rem/1.6 var(--mono);
  letter-spacing:.2em; text-transform:uppercase; color:var(--ink-3);
  padding-top:.7rem;
  border-top:1px solid var(--line);
}
.essay__title{
  font-size:clamp(1.7rem,3.6vw,2.9rem);
  line-height:1.06; text-transform:uppercase;
  margin-bottom:1.6rem; max-width:20ch;
}
.essay__text p{
  font-size:1.0625rem; line-height:1.68; color:var(--ink-2);
  max-width:56ch;
}
.essay__text p + p{ margin-top:1.15rem; }

@media (max-width:820px){
  .essay__inner{ grid-template-columns:1fr; }
  .essay__n{ margin-bottom:1.6rem; }
}

/* --- the seven monitored stages --- */
.stages{
  max-width:74rem;
  margin-top:clamp(2.4rem,5vw,3.6rem);
}
.stages__head{
  font:500 .625rem/1 var(--mono);
  letter-spacing:.2em; text-transform:uppercase; color:var(--ink-3);
  margin-bottom:1.2rem;
}
.stages__list{
  list-style:none; margin:0; padding:0;
  display:grid; gap:1px;
  grid-template-columns:repeat(auto-fit,minmax(190px,1fr));
  background:var(--line-2);
  border:1px solid var(--line-2);
}
.stages__list li{
  background:var(--floor);
  padding:1.05rem 1.1rem;
  font-size:.9375rem; color:var(--ink-strong);
  display:flex; align-items:baseline; gap:.85rem;
}
.essay:not(.essay--alt) .stages__list li{ background:var(--void); }
.stages__list span{
  font:500 .625rem/1 var(--mono);
  letter-spacing:.14em; color:var(--ink-3);
}

/* --- image plate ---
   Fixed 16:9 frame. Dropping a file at the img src replaces the technical
   placeholder without moving anything around it. */
.plate{
  margin:clamp(2.6rem,6vw,4.5rem) 0 0;
  max-width:74rem;
}
.plate__frame{
  position:relative;
  aspect-ratio:16/9;
  border:1px solid var(--line-2);
  overflow:hidden;
  background:
    radial-gradient(120% 90% at 50% 6%, rgba(23,23,23,.03), transparent 62%),
    repeating-linear-gradient(94deg, rgba(23,23,23,.022) 0 2px, transparent 2px 5px),
    linear-gradient(162deg,var(--paper-2),var(--bar) 68%,var(--paper));
}
.plate__img, .plate__ph{
  position:absolute; inset:0;
  width:100%; height:100%;
}
/* hidden until proven loaded, so a missing file never flashes a broken icon */
.plate__img{
  object-fit:cover; opacity:0; transition:opacity .45s;
  /* Shop photography is daylight — bright, warm and far more saturated than
     this page. Ungraded it sits forward of everything around it. Same
     correction the hero footage needs, and for the same reason. */
  filter:saturate(.92) contrast(1.02) brightness(.98);
}
.plate__frame.has-image .plate__img{ opacity:1; }

/* Blown-out roof lights are the brightest thing in a shop photo and pull the
   eye off the page. A vignette settles the frame into the surrounding dark. */
.plate__frame.has-image::after{
  content:""; position:absolute; inset:0; pointer-events:none;
  background:
    radial-gradient(108% 84% at 50% 46%, transparent 42%, rgba(23,23,23,.16) 88%),
    linear-gradient(180deg, rgba(7,8,10,.42) 0%, transparent 22%, transparent 74%, rgba(7,8,10,.34) 100%);
}
.plate__ph{ color:var(--ink-3); padding:6% 12%; opacity:.75; }
.plate__frame.has-image .plate__ph{ display:none; }
.plate__img.is-missing{ display:none; }
/* ---------------------------- image slider ----------------------------
   One frame at a time, held until the visitor moves it. No autoplay and
   no timers anywhere in this component. */

.slider__frame{ position:relative; }

.slider__track{
  list-style:none; margin:0; padding:0;
  position:relative;
}
.slider__slide{
  position:absolute; inset:0;
  opacity:0; visibility:hidden;
  transition:opacity .45s ease, visibility 0s linear .45s;
}
.slider__slide.is-on{
  position:relative;
  opacity:1; visibility:visible;
  transition:opacity .45s ease;
}
.slider__cap{ display:none; }   /* surfaced in the bar below the frame */

/* Deliberately small. A hairline square that sits over the image edge and
   firms up on hover — the picture is the content, not the control. */
.slider__arrow{
  position:absolute; top:50%; z-index:3;
  width:34px; height:44px;
  transform:translateY(-50%);
  display:grid; place-items:center;
  background:rgba(255,255,255,.9);
  border:1px solid var(--line);
  color:var(--ink-2);
  font:400 1.1rem/1 var(--mono);
  cursor:pointer;
  transition:background .2s, color .2s, border-color .2s;
}
.slider__arrow:hover{
  background:var(--floor);
  border-color:var(--ink-3);
  color:var(--ink);
}
.slider__arrow--prev{ left:0; border-left:0; }
.slider__arrow--next{ right:0; border-right:0; }
.slider__arrow:disabled{ opacity:.3; cursor:default; }
/* display:grid above outranks the user-agent rule for [hidden], so a slider
   holding a single frame kept showing arrows that did nothing. The gallery
   arrows already carried this guard; these did not. */
.slider__arrow[hidden], .slider__count[hidden]{ display:none; }

.slider__bar{
  display:flex; align-items:baseline; justify-content:space-between;
  gap:1.4rem; margin-top:.85rem;
}
.slider__label{
  font:400 .625rem/1.5 var(--mono);
  letter-spacing:.18em; text-transform:uppercase; color:var(--ink-3);
}
.slider__count{
  font:500 .625rem/1.5 var(--mono);
  letter-spacing:.14em; color:var(--ink-2); flex:none;
}

/* swiping shouldn't also drag the image ghost around */
.slider__frame img{ -webkit-user-drag:none; user-select:none; }
.slider__frame{ touch-action:pan-y; }

@media (max-width:560px){
  .slider__arrow{ width:30px; height:38px; }
  .slider__bar{ flex-direction:column; gap:.35rem; }
}

@media (prefers-reduced-motion:reduce){
  .slider__slide{ transition:none; }
}

.plate figcaption{
  margin-top:.85rem;
  font:400 .625rem/1 var(--mono);
  letter-spacing:.18em; text-transform:uppercase; color:var(--ink-3);
}
.plate__frame.has-image + figcaption{ color:var(--ink-3); }

@media (prefers-reduced-motion:reduce){
  [data-reveal]{ opacity:1; transform:none; transition:none; }
}

/* ============================ portfolio gallery ============================
   Sits directly under the open panel and swaps content with the category.
   Images are shown whole — contain, never cover — so a tall render is
   letterboxed rather than cropped through. */

.gal{
  margin-top:1px;
  padding:1.6rem 1.7rem 1.9rem;
  background:linear-gradient(180deg,var(--bar),var(--paper));
  box-shadow:0 0 0 1px var(--line-2);
}
.gal[hidden]{ display:none; }

/* ties the gallery back to the card that opened it */
.gal__head{
  display:flex; align-items:baseline; gap:.85rem;
  font:500 .625rem/1 var(--mono);
  letter-spacing:.2em; text-transform:uppercase; color:var(--ink-3);
  margin-bottom:1.1rem;
}
.gal__n{ color:var(--ink-2); }

.gal__frame{
  position:relative;
  aspect-ratio:16/9;
  background:
    repeating-linear-gradient(45deg, rgba(23,23,23,.022) 0 14px, transparent 14px 28px),
    var(--void);
  border:1px solid var(--line-2);
  overflow:hidden;
  touch-action:pan-y;
}
.gal__track{ list-style:none; margin:0; padding:0; }
.gal__shot{
  position:absolute; inset:0;
  opacity:0; visibility:hidden;
  transition:opacity .4s ease, visibility 0s linear .4s;
  display:grid; place-items:center;
  /* One track, definite, floored at zero. Left auto, the row takes the
     image's own height and a tall photograph pushes the frame's clip. */
  grid-template:minmax(0,1fr) / minmax(0,1fr);
}
.gal__shot.is-on{
  opacity:1; visibility:visible;
  transition:opacity .4s ease;
}
/* Whole image, original proportions, no crop.
   The box is the frame and object-fit does the letterboxing. Sizing the
   image itself instead (max-height:100%) does not hold: the percentage
   resolves against the grid area, which layout treats as indefinite, so
   a portrait photograph overflows and the frame clips it. */
.gal__shot img{
  width:100%; height:100%;
  object-fit:contain;
  cursor:zoom-in;
  -webkit-user-drag:none; user-select:none;
}
/* a slot whose photograph has not been supplied yet */
.gal__todo{
  font:400 .625rem/1.8 var(--mono);
  letter-spacing:.2em; text-transform:uppercase; color:var(--ink-3);
  text-align:center; padding:1rem;
}

.gal__arrow{
  position:absolute; top:50%; z-index:3;
  width:34px; height:44px; transform:translateY(-50%);
  display:grid; place-items:center;
  background:rgba(255,255,255,.9);
  border:1px solid var(--line);
  color:var(--ink-2);
  font:400 1.1rem/1 var(--mono); cursor:pointer;
  transition:background .2s, color .2s, border-color .2s;
}
.gal__arrow:hover{ background:var(--floor); border-color:var(--ink-3); color:var(--ink); }
.gal__arrow--prev{ left:0; border-left:0; }
.gal__arrow--next{ right:0; border-right:0; }
.gal__arrow[hidden]{ display:none; }

.gal__bar{
  display:flex; align-items:baseline; justify-content:space-between;
  gap:1.4rem; margin-top:.85rem;
}
.gal__cap{
  font:400 .625rem/1.5 var(--mono);
  letter-spacing:.16em; text-transform:uppercase; color:var(--ink-3);
}
.gal__count{
  font:500 .625rem/1.5 var(--mono);
  letter-spacing:.14em; color:var(--ink-2); flex:none;
}

@media (max-width:560px){
  .gal{ padding:1.2rem 1.1rem 1.5rem; }
  /* arrows stay reachable rather than shrinking away on a phone */
  .gal__arrow{ width:38px; height:48px; }
  .gal__bar{ flex-direction:column; gap:.35rem; }
}

/* ---------------------------- lightbox ---------------------------- */

.lightbox{ position:fixed; inset:0; z-index:300; display:flex; }
.lightbox[hidden]{ display:none; }
.lightbox__backdrop{
  position:absolute; inset:0;
  background:rgba(245,245,243,.94); backdrop-filter:blur(3px);
}
.lightbox__panel{
  position:relative; margin:auto;
  width:min(1400px,94vw); height:min(92vh,1000px);
  display:grid; place-items:center;
  /* definite track, so the image's max-height:100% has something to
     resolve against — otherwise a portrait shot overruns the viewport */
  grid-template:minmax(0,1fr) / minmax(0,1fr);
}
.lightbox__img{
  max-width:100%; max-height:100%;
  width:auto; height:auto; object-fit:contain;
  box-shadow:0 0 0 1px var(--line);
}
.lightbox__close{
  position:absolute; top:-2px; right:-2px;
  width:36px; height:36px;
  display:grid; place-items:center;
  background:var(--floor); border:1px solid var(--line);
  color:var(--ink-2); cursor:pointer; font:400 .9rem/1 var(--mono);
  transition:background .2s, color .2s;
}
/* Ink, not red. Closing a viewer is not destructive, and the palette carries
   one chromatic value — a second one here read as a warning that isn't. */
.lightbox__close:hover{ background:var(--ink); color:var(--void); }
.lightbox__cap{
  position:absolute; left:0; bottom:-1.8rem;
  font:400 .625rem/1.5 var(--mono);
  letter-spacing:.16em; text-transform:uppercase; color:var(--ink-3);
}

@media (prefers-reduced-motion:reduce){
  .gal__shot{ transition:none; }
}

/* ============================ certifications ============================ */

.certs-wrap{ padding:0 clamp(1.2rem,5vw,4rem) clamp(4.5rem,12vh,8rem); }

.certs{
  display:grid; gap:1px;
  grid-template-columns:repeat(3,1fr);
  max-width:88rem;
}
@media (max-width:1000px){ .certs{ grid-template-columns:repeat(2,1fr); } }
@media (max-width:640px){  .certs{ grid-template-columns:1fr; } }

/* the whole card is the button */
.cert{
  display:flex; flex-direction:column;
  text-align:left; width:100%;
  padding:1.3rem 1.3rem 1.5rem;
  background:linear-gradient(168deg,var(--floor),var(--paper) 62%,var(--void));
  box-shadow:0 0 0 1px var(--line-2);
  color:inherit; font:inherit; cursor:pointer;
  transition:transform .35s cubic-bezier(.22,.61,.36,1),
             box-shadow .35s, background .35s;
}
.cert:hover, .cert:focus-visible{
  transform:translateY(-3px);
  box-shadow:0 0 0 1px var(--line), 0 4px 14px rgba(23,23,23,.07);
  background:linear-gradient(168deg,var(--paper-2),var(--bar) 62%,var(--paper));
}
.cert[disabled]{ cursor:default; opacity:.72; }
.cert[disabled]:hover{ transform:none; box-shadow:0 0 0 1px var(--line-2); }

/* --- document preview --- */
.cert__doc{
  position:relative;
  aspect-ratio:3/2;
  margin-bottom:1.25rem;
  border:1px solid var(--line-2);
  overflow:hidden;
  background:
    radial-gradient(120% 90% at 50% 4%, rgba(23,23,23,.03), transparent 60%),
    linear-gradient(162deg,var(--void),var(--bar) 70%,var(--paper));
  display:grid; place-items:center;
}
/* the document's own first page, drawn from the PDF */
.cert__page{
  position:absolute; inset:0;
  width:100%; height:100%; object-fit:cover;
  opacity:0; transition:opacity .5s;
}
.cert__doc.has-page .cert__page{ opacity:1; }
.cert__doc.has-page .cert__sheet{ display:none; }
/* the page top carries the letterhead — anchor there, not at the centre */
.cert__doc.has-page{ background:var(--floor); }

/* a page, unmistakably: A4 proportion, ruled, with a seal */
.cert__sheet{
  position:relative;
  height:78%; aspect-ratio:1/1.414;
  background:linear-gradient(168deg,var(--floor),var(--void));
  box-shadow:0 2px 8px rgba(23,23,23,.10), inset 0 0 0 1px var(--line);
  transition:transform .4s cubic-bezier(.22,.61,.36,1);
}
.cert:hover .cert__sheet{ transform:translateY(-2px) rotate(-.6deg); }
.cert__sheet::before{
  content:""; position:absolute; left:14%; right:14%; top:16%; height:34%;
  background:repeating-linear-gradient(180deg,
    rgba(23,23,23,.22) 0 1px, transparent 1px 7px);
}
.cert__sheet::after{
  content:""; position:absolute; left:50%; bottom:14%;
  width:22%; aspect-ratio:1; transform:translateX(-50%);
  border:1px solid rgba(23,23,23,.2); border-radius:50%;
}

.cert__badge{
  position:absolute; top:.6rem; left:.6rem;
  font:500 .5625rem/1 var(--mono);
  letter-spacing:.16em; text-transform:uppercase; color:var(--ink-3);
  background:rgba(255,255,255,.88); padding:.34rem .5rem; border-radius:2px;
}

/* --- card text --- */
.cert__n{
  font:500 .625rem/1 var(--mono);
  letter-spacing:.2em; text-transform:uppercase; color:var(--ink-3);
  margin-bottom:.6rem;
}
/* long official titles have to wrap without pushing the card out of line —
   the grid stretches all cards to the tallest, and the button is pinned to
   the bottom, so an extra line here costs nothing */
.cert__title{
  font-size:1.06rem; line-height:1.22;
  margin-bottom:.5rem;
}
.cert__std{
  font:500 .6875rem/1.4 var(--mono);
  letter-spacing:.06em; color:var(--ink-2);
  margin-bottom:1rem;
}
.cert__meta{ margin:0 0 1.3rem; border-top:1px solid var(--line-2); padding-top:.85rem; }
.cert__meta div{
  display:flex; justify-content:space-between; align-items:baseline;
  gap:1rem; padding:.3rem 0;
}
.cert__meta dt{
  font:400 .5625rem/1.4 var(--mono); letter-spacing:.14em;
  text-transform:uppercase; color:var(--ink-3); flex:none;
}
.cert__meta dd{
  margin:0; font:500 .75rem/1.4 var(--mono); color:var(--ink-strong);
  text-align:right; word-break:break-word;
}
.cert__meta dd.is-pending{ color:var(--ink-3); font-weight:400; }

.cert__cta{
  margin-top:auto;
  display:flex; align-items:center; justify-content:space-between; gap:.8rem;
  padding-top:.9rem; border-top:1px solid var(--line-2);
  font:500 .625rem/1 var(--mono);
  letter-spacing:.14em; text-transform:uppercase; color:var(--ink-2);
  transition:color .25s;
}
.cert:hover .cert__cta{ color:var(--ink); }
.cert__cta span:last-child{ transition:transform .3s cubic-bezier(.22,.61,.36,1); }
.cert:hover .cert__cta span:last-child{ transform:translateX(4px); }

.certs__note{
  margin-top:2.4rem; max-width:52ch;
  font-size:.9375rem; color:var(--ink-3);
}
.certs__note a{ color:var(--ink-2); }

/* ============================ pdf viewer ============================ */

.pdfv{ position:fixed; inset:0; z-index:300; display:flex; }
.pdfv[hidden]{ display:none; }
.pdfv__backdrop{
  position:absolute; inset:0;
  background:rgba(245,245,243,.94);
  backdrop-filter:blur(3px);
  animation:pdfvFade .25s ease-out;
}
@keyframes pdfvFade{ from{ opacity:0 } to{ opacity:1 } }

.pdfv__panel{
  position:relative; margin:auto;
  width:min(1180px,94vw); height:min(92vh,1000px);
  display:flex; flex-direction:column;
  background:var(--floor);
  box-shadow:0 0 0 1px var(--line), 0 12px 40px rgba(23,23,23,.14);
  animation:pdfvIn .3s cubic-bezier(.22,.61,.36,1);
}
@keyframes pdfvIn{ from{ opacity:0; transform:translateY(12px) scale(.99) } to{ opacity:1; transform:none } }

.pdfv__bar{
  display:flex; align-items:center; justify-content:space-between;
  gap:1.2rem; flex-wrap:wrap;
  padding:.9rem 1.1rem;
  border-bottom:1px solid var(--line-2);
  background:linear-gradient(180deg,var(--paper-2),var(--floor));
}
.pdfv__title{ font-size:1.02rem; line-height:1.2; }
.pdfv__sub{
  margin-top:.3rem;
  font:400 .5625rem/1 var(--mono);
  letter-spacing:.16em; text-transform:uppercase; color:var(--ink-3);
}
.pdfv__tools{ display:flex; align-items:center; gap:.6rem; flex-wrap:wrap; }
.pdfv__group{
  display:flex; align-items:center; gap:1px;
  background:var(--line-2); border:1px solid var(--line-2);
}
.pdfv__btn{
  min-width:32px; height:30px;
  display:inline-flex; align-items:center; justify-content:center;
  padding:0 .5rem;
  background:var(--floor); color:var(--ink-2);
  border:0; border-radius:0; cursor:pointer; text-decoration:none;
  font:500 .6875rem/1 var(--mono); letter-spacing:.1em; text-transform:uppercase;
  transition:background .2s, color .2s;
}
.pdfv__btn--wide{ min-width:auto; padding:0 .75rem; }
.pdfv__btn:hover{ background:var(--ink); color:var(--void); }
.pdfv__btn:disabled{ opacity:.35; cursor:default; }
.pdfv__btn:disabled:hover{ background:var(--floor); color:var(--ink-2); }
/* no override: the close button hovers like every other control in the bar */
.pdfv__page{
  padding:0 .7rem; background:var(--floor);
  font:500 .6875rem/30px var(--mono); color:var(--ink); white-space:nowrap;
}

/* full wording, kept out of the cards where it would overflow */
.pdfv__detail{
  display:grid; gap:1px 2rem;
  grid-template-columns:repeat(auto-fit,minmax(270px,1fr));
  padding:.9rem 1.1rem;
  border-bottom:1px solid var(--line-2);
  background:var(--void);
  max-height:11rem; overflow-y:auto;
}
.pdfv__detail div{ padding:.28rem 0; }
.pdfv__detail dt{
  font:400 .5625rem/1.4 var(--mono); letter-spacing:.14em;
  text-transform:uppercase; color:var(--ink-3);
}
.pdfv__detail dd{ margin:0; font-size:.8125rem; line-height:1.5; color:var(--ink-2); }
@media (max-width:760px){ .pdfv__detail{ max-height:8rem; } }

.pdfv__stage{
  flex:1; overflow:auto;
  padding:1.4rem;
  display:grid; place-items:start center;
  background:
    repeating-linear-gradient(45deg, rgba(23,23,23,.022) 0 12px, transparent 12px 24px),
    var(--void);
}
.pdfv__stage:focus-visible{ outline:2px solid var(--cut); outline-offset:-2px; }
.pdfv__canvas{
  max-width:100%; height:auto;
  box-shadow:0 0 0 1px var(--line);
  background:var(--floor);
}
.pdfv__canvas[hidden]{ display:none; }
.pdfv__frame{ width:100%; height:100%; border:0; background:var(--floor); }
.pdfv__status{
  grid-area:1/1; align-self:center;
  font:400 .6875rem/1.7 var(--mono);
  letter-spacing:.16em; text-transform:uppercase; color:var(--ink-3);
  text-align:center; max-width:40ch;
}
.pdfv__status[hidden]{ display:none; }

@media (max-width:760px){
  .pdfv__panel{ width:100vw; height:100vh; }
  .pdfv__bar{ padding:.7rem .8rem; }
  .pdfv__title{ font-size:.9rem; }
  .pdfv__stage{ padding:.7rem; }
}

@media (prefers-reduced-motion:reduce){
  .cert, .cert__sheet, .cert__cta span:last-child{ transition:none; }
  .cert:hover{ transform:none; }
  .cert:hover .cert__sheet{ transform:none; }
  .pdfv__backdrop, .pdfv__panel{ animation:none; }
}

/* ============================ portfolio ============================ */

.pf{
  display:grid; gap:1px;
  grid-template-columns:repeat(3,1fr);
}
@media (max-width:900px){ .pf{ grid-template-columns:1fr; } }

.pf__card{
  position:relative;
  padding:1.6rem 1.6rem 1.9rem;
  background:linear-gradient(168deg,var(--floor),var(--paper) 60%,var(--void));
  box-shadow:0 0 0 1px var(--line-2);
  perspective:900px;
  display:flex; flex-direction:column;
  transition:background .5s;
}
.pf__card.is-open{ background:linear-gradient(168deg,var(--paper-2),var(--bar) 62%,var(--paper)); }

/* --- the 3D stage ---
   Holds its own aspect ratio, so dropping the finished render in at the
   img src changes nothing about the layout around it. */
.pf__stage{
  position:relative;
  aspect-ratio:16/10;
  margin-bottom:1.5rem;
  border:1px solid var(--line-2);
  background:
    radial-gradient(120% 90% at 50% 8%, rgba(23,23,23,.035), transparent 62%),
    repeating-linear-gradient(94deg, rgba(23,23,23,.022) 0 2px, transparent 2px 5px),
    linear-gradient(162deg,var(--paper-2),var(--bar) 68%,var(--void));
  overflow:hidden;
  transform-style:preserve-3d;
  transform:rotateX(var(--rx,0deg)) rotateY(var(--ry,0deg));
  transition:transform .5s cubic-bezier(.22,.61,.36,1), border-color .4s;
  will-change:transform;
}
.pf__card:hover .pf__stage{ border-color:rgba(255,255,255,.16); }

/* specular sweep across the metal, parked off-frame until hover */
.pf__stage::after{
  content:""; position:absolute; inset:-30%;
  background:linear-gradient(104deg, transparent 42%, rgba(255,255,255,.07) 50%, transparent 58%);
  transform:translateX(-64%);
  transition:transform 1.1s cubic-bezier(.22,.61,.36,1);
  pointer-events:none;
}
.pf__card:hover .pf__stage::after{ transform:translateX(64%); }

.pf__img, .pf__ph{
  position:absolute; inset:0;
  width:100%; height:100%;
  transform:translateZ(26px) scale(var(--z,1));
  transition:transform .5s cubic-bezier(.22,.61,.36,1);
}
/* hidden until proven loaded, so a missing file never flashes a broken icon */
.pf__img{ object-fit:cover; opacity:0; }
.pf__stage.has-image .pf__img{ opacity:1; }
.pf__card:hover .pf__img, .pf__card:hover .pf__ph{ --z:1.035; }

/* the placeholder: a technical line drawing, not a photograph.
   Hidden the moment a real image loads. */
.pf__ph{
  color:var(--ink-3);
  padding:11%;
  opacity:.8;
}
.pf__card:hover .pf__ph{ color:var(--ink-2); }
.pf__stage.has-image .pf__ph{ display:none; }
.pf__img.is-missing{ display:none; }

.pf__meta{
  display:flex; align-items:baseline; gap:.8rem;
  font:500 .625rem/1 var(--mono);
  letter-spacing:.2em; text-transform:uppercase; color:var(--ink-3);
  margin-bottom:.7rem;
}
.pf__n{ color:var(--ink-2); }
.pf__name{
  font-size:1.32rem; line-height:1.15;
  margin-bottom:1.4rem;
}

/* Category button — held at the exact size specified. */
.pf__btn{
  width:228.25px; height:32.75px;
  margin-top:auto;
  display:inline-flex; align-items:center; justify-content:center;
  padding:0 .7rem; max-width:100%;
  font:500 .625rem/1 var(--mono);
  letter-spacing:.12em; text-transform:uppercase;
  color:var(--ink-2);
  background:transparent;
  border:1px solid var(--line);
  border-radius:2px;
  cursor:pointer;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
  transition:color .25s, background .25s, border-color .25s;
}
.pf__btn:hover{ color:var(--ink); border-color:var(--ink-3); }
.pf__btn[aria-selected="true"]{
  background:var(--ink); border-color:var(--ink); color:var(--void);
}

/* --- panel --- */
.pf__panel{
  margin-top:1px;
  padding:2.2rem 1.7rem 2.4rem;
  background:linear-gradient(180deg,var(--floor),var(--void));
  box-shadow:0 0 0 1px var(--line-2);
  animation:pfIn .45s cubic-bezier(.22,.61,.36,1);
}
@keyframes pfIn{ from{ opacity:0; transform:translateY(7px) } to{ opacity:1; transform:none } }
.pf__panel-lead{
  color:var(--ink-2); max-width:60ch; font-size:1rem;
  margin-bottom:1.7rem;
}
.pf__list{
  list-style:none; margin:0; padding:0;
  display:grid; gap:1px 2rem;
  grid-template-columns:repeat(auto-fit,minmax(240px,1fr));
}
.pf__list li{
  padding:.72rem 0 .72rem 1.6rem;
  border-top:1px solid var(--line-2);
  font-size:.9375rem; color:var(--ink-strong);
  position:relative;
}
.pf__list li::before{
  content:""; position:absolute; left:0; top:1.22rem;
  width:9px; height:1px; background:var(--ink-3);
}

@media (prefers-reduced-motion:reduce){
  .pf__stage, .pf__img, .pf__ph, .pf__stage::after{ transition:none; }
  .pf__panel{ animation:none; }
}


/* route list */
.route-list{
  list-style:none; margin:0; padding:0;
  display:grid; gap:1px; background:var(--line-2);
  border-block:1px solid var(--line-2);
}
.route-list li{
  background:var(--void); padding:1.8rem 0;
  display:grid; grid-template-columns:5.5rem 1fr; gap:0 1.5rem; align-items:baseline;
}
.route-list__n{
  font:500 .75rem/1 var(--mono); letter-spacing:.16em; color:var(--ink-3);
}
.route-list h3{ font-size:1.3rem; margin-bottom:.45rem; }
.route-list p{ grid-column:2; color:var(--ink-2); max-width:58ch; font-size:.9375rem; }
@media (max-width:640px){
  .route-list li{ grid-template-columns:1fr; gap:.4rem; }
  .route-list p{ grid-column:1; }
}

/* cta */
.cta{
  padding:clamp(5rem,15vh,10rem) clamp(1.2rem,5vw,4rem);
  border-top:1px solid var(--line-2);
  background:
    radial-gradient(90% 120% at 50% 130%, var(--floor-hi), var(--void) 62%);
}
.cta__inner{ max-width:46rem; }
.cta__title{
  font-size:clamp(2.2rem,6vw,4.4rem);
  line-height:1; text-transform:uppercase; margin-bottom:1.2rem;
}
.cta__lead{ color:var(--ink-2); font-size:1.0625rem; max-width:48ch; }
.cta__actions{ display:flex; flex-wrap:wrap; gap:.8rem; margin:2.4rem 0 3rem; }
.btn{
  display:inline-block; text-decoration:none;
  background:var(--ink); color:var(--void);
  padding:.95rem 1.6rem; border-radius:2px;
  font:500 .75rem/1 var(--mono); letter-spacing:.14em; text-transform:uppercase;
  border:1px solid var(--ink);
  transition:background .2s, color .2s;
}
.btn:hover{ background:transparent; color:var(--ink); }
.btn--ghost{ background:transparent; color:var(--ink); border-color:var(--line); }
.btn--ghost:hover{ background:var(--ink); color:var(--void); border-color:var(--ink); }
.cta__meta{
  display:flex; flex-wrap:wrap; gap:2.4rem;
  margin:0; padding-top:1.8rem; border-top:1px solid var(--line-2);
}
.cta__meta dt{
  font:400 .625rem/1 var(--mono); letter-spacing:.16em;
  text-transform:uppercase; color:var(--ink-3); margin-bottom:.45rem;
}
.cta__meta dd{ margin:0; color:var(--ink-strong); font:500 .8125rem/1 var(--mono); }

/* closing statement */
.statement{
  padding:clamp(5rem,16vh,10rem) clamp(1.2rem,5vw,4rem);
  border-top:1px solid var(--line-2);
  text-align:center;
}
.statement__title{
  display:flex; flex-direction:column;
  font-size:clamp(1.9rem,6vw,4.6rem);
  line-height:1; letter-spacing:-.02em; text-transform:uppercase;
}
.statement__title span:last-child{ color:var(--ink-2); }

/* footer */
.foot{
  padding:clamp(3rem,7vh,5rem) clamp(1.2rem,5vw,4rem) 2.4rem;
  border-top:1px solid var(--line-2);
  display:grid; gap:2.4rem;
  grid-template-columns:2fr 1fr 1fr;
}
.foot__brand p{ color:var(--ink-3); font-size:.875rem; margin-top:.9rem; max-width:30ch; }
.foot__col h3{
  font:500 .625rem/1 var(--mono); letter-spacing:.18em;
  text-transform:uppercase; color:var(--ink-3); margin-bottom:.85rem;
}
.foot__col p{ color:var(--ink-2); font-size:.875rem; }
/* Address and phone are links, so they need a hittable target, not just a
   text run. 18px tall failed WCAG 2.2 target size; the padding is vertical
   only so the column's left edge stays aligned with the labels above it. */
.foot__col a{
  text-decoration:none;
  display:inline-flex; align-items:center; min-height:28px;
}
.foot__col a:hover{ color:var(--ink); text-decoration:underline; }
.foot__legal{
  grid-column:1/-1; padding-top:2rem; border-top:1px solid var(--line-2);
  font:400 .6875rem/1 var(--mono); letter-spacing:.1em; color:var(--ink-3);
}
@media (max-width:700px){ .foot{ grid-template-columns:1fr; } }

/* ============================ reduced motion ============================ */

@media (prefers-reduced-motion:reduce){
  html{ scroll-behavior:auto; }
  .filmstrip{ height:auto !important; }
  .filmstrip__stage{ position:relative; height:80vh; }
  .filmstrip__meta{ display:none; }
  .hero__cueline{ animation:none; }
}

/* ---------------------------- language ----------------------------
   Three codes and two hairline separators — the same weight as the
   nav links it sits beside, so it reads as part of the bar rather
   than as a control bolted onto it. */
.lang{ display:flex; align-items:center; gap:.42rem; }
.lang__opt{
  background:none; border:0; padding:0; cursor:pointer;
  font:500 .75rem/1 var(--mono);
  letter-spacing:.12em; text-transform:uppercase;
  color:var(--ink-3);
  transition:color .2s;
}
.lang__opt:hover{ color:var(--ink-2); }
.lang__opt.is-on{ color:var(--ink); }
.lang__sep{ font:400 .75rem/1 var(--mono); color:var(--line); }
/* In the drawer the codes sit on their own row above the CTA, at a size a
   thumb can actually hit. They used to be dropped entirely below 430px,
   which removed the whole trilingual switch from every phone. */
@media (max-width:1000px){
  .lang{
    gap:.2rem;
    border-top:1px solid var(--line-2);
    padding:.35rem 0;
  }
  .lang__opt{ min-height:44px; min-width:44px; justify-content:center; display:flex; align-items:center; }
}

/* ------------------ Capabilities over the film ------------------
   The section is transparent so the picture reads through it; only the
   cards carry a translucent panel, and only as much as the specification
   text needs to stay legible over moving footage. */
.cinema #capabilities{ border-top:0; }

.cinema .caps{ background:transparent; }
.cinema .cap{
  background:rgba(255,255,255,.78);
  -webkit-backdrop-filter:saturate(1.1) blur(7px);
  backdrop-filter:saturate(1.1) blur(7px);
  box-shadow:0 0 0 1px rgba(23,23,23,.07);
}
/* the heading block needs less: large charcoal type carries itself */
.cinema .block__head{
  background:linear-gradient(180deg, rgba(245,245,243,.72), rgba(245,245,243,.44));
  padding:1.4rem 1.5rem 1.5rem;
  /* Bleeds to the section edge, never past it. A flat -1.5rem overran the
     section's own padding once that padding dropped to 1.2rem on phones,
     and pushed 5px of horizontal scroll onto the document. */
  --bleed:min(1.5rem, clamp(1.2rem,5vw,4rem));
  margin-left:calc(-1 * var(--bleed));
  margin-right:calc(-1 * var(--bleed));
  backdrop-filter:blur(4px);
}
@supports not (backdrop-filter: blur(1px)){
  .cinema .cap{ background:rgba(255,255,255,.93); }
  .cinema .block__head{ background:rgba(245,245,243,.9); }
}

/* the run ends: the backdrop is faded out by JS, and the page returns to
   its ordinary light ground for everything below */

/* ---------------------------- brand mark ----------------------------
   Isolated on the far left with clear space to its right, so it reads as
   a mark rather than as the first item in the navigation. Separation is
   spacing only — no box, border or panel behind it. */
.topbar .mark{
  /* the cut now occupies part of the clear space this margin used to
     provide, so it gives back what the mark took */
  margin-right:clamp(1rem,2.6vw,4rem);
}
.topbar .topnav{ margin-left:auto; }

/* The header carries the name typographically. The logo image that used to
   sit here was removed; the artwork itself is still shipped as the favicon
   at media/logos/alpantek-mark.svg. */

/* The header carries its own light ground on every page, so one dark mark
   serves throughout. If a light version is ever needed over the film,
   add .mark__logo--light and swap on .filmstrip proximity — not needed
   while the bar stays opaque. */

/* ------------------------- hit areas -------------------------
   WCAG 2.5.8: the language codes measured 17x12px. Padding is
   negated by equal negative margin, so the target grows to a
   comfortable size while the bar looks exactly as before. */
.lang__opt{
  padding:.62rem .4rem;
  margin:-.62rem -.4rem;
}
.lang{ gap:1.2rem; }
.skip, .topnav__cta{ padding-top:.72rem; padding-bottom:.72rem; }

/* inner section headers are not page banners */

/* ------------------------- category read-more -------------------------
   The link out of a portfolio panel to its own page. Same mono vocabulary
   as the technical labels, so it reads as a note rather than a button. */
.pf__more{ margin-top:1.6rem; }
.pf__more a{
  font:500 .625rem/1 var(--mono);
  letter-spacing:.14em; text-transform:uppercase; color:var(--ink-2);
}
.pf__more a:hover{ color:var(--ink); }

/* capability preview: a list, not cards — nothing here is a product */
.prev{
  list-style:none; margin:0; padding:0; max-width:74rem;
  display:grid; gap:1px;
  grid-template-columns:repeat(auto-fit,minmax(260px,1fr));
  border-top:1px solid var(--line-2);
}
.prev li{
  padding:.95rem 0 .95rem 1.6rem;
  border-bottom:1px solid var(--line-2);
  font-size:.9375rem; color:var(--ink-strong);
  position:relative;
}
.prev li::before{
  content:""; position:absolute; left:0; top:1.45rem;
  width:9px; height:1px; background:var(--ink-3);
}

/* ---------------------------- accent states ----------------------------
   The teal appears only where the interface reports state back to the
   visitor. Everything else stays white / off-white / grey / charcoal. */

/* selected portfolio category — the fill was charcoal, which read as a
   button rather than as a selection */
.pf__btn[aria-selected="true"]{
  background:var(--accent);
  border-color:var(--accent);
  color:var(--accent-ink);
}
/* and the open card carries a hairline so the pairing is legible at a glance */
.pf__card.is-open{ box-shadow:inset 3px 0 0 var(--accent), 0 0 0 1px var(--line-2); }

/* active language and current page */
.lang__opt.is-on{ color:var(--accent); }
.topnav a.is-here{ color:var(--accent); }

/* pressed state on the primary controls */
.btn:active,
.pf__btn:active,
.gal__arrow:active,
.slider__arrow:active,
.pdfv__btn:active{ background:var(--accent); border-color:var(--accent); color:var(--accent-ink); }

.gal__arrow:hover, .slider__arrow:hover{ border-color:var(--accent); color:var(--accent); }
