﻿/* ==========================================================================
   MOBILSISTEMI — sections.css
   Homepage choreography, section by section.
   ========================================================================== */

/* ==========================================================================
   01 — HERO
   ========================================================================== */

.hero{
  position: relative;
  min-height: 100svh;          /* svh, not vh — no jump when iOS hides its bar */
  display: flex;
  flex-direction: column;
  justify-content: flex-end;   /* the whole composition is grounded, not centred */
  padding-block: clamp(7rem, 14vh, 10rem) clamp(24px, 4vh, 56px);
  background: var(--dark);
  color: var(--on-dark);
  overflow: hidden;
  isolation: isolate;
  touch-action: pan-y;         /* swipe reads horizontal; the page still scrolls */
}
@media (max-width: 768px){
  /* max(), not a bare 560px — a plain min-height here *replaces* the 100svh
     above rather than acting as a floor under it, and the hero collapsed to
     560px on an 844px screen. */
  .hero{ min-height: max(560px, 100svh); }
}
.hero__media{
  position: absolute;
  inset: 0;
  z-index: -2;
  /* The slides rest at scale(1.08) and the entrance settles from 1.06, so
     their boxes are wider than the viewport. .hero clips visually, but the
     scaled boxes still counted toward the document's scroll width and gave
     320px screens 66px of overflow. Clipping here contains them. */
  overflow: hidden;
}
.hero__media picture{ display: block; width: 100%; height: 100%; }
.hero__media img{ width: 100%; height: 100%; object-fit: cover; }

/* --------------------------------------------------------------------------
   Hero slider
   --------------------------------------------------------------------------
   A cross-fade with a counter-scale: the outgoing slide drifts out to 1.05
   while the incoming one settles back from 1.08. Nothing slides sideways —
   the headline sits on top of this and must not appear to move.

   The transition lives on .is-active and .is-leaving, never on the base
   class. That is what lets a finished slide be reset to its resting state by
   removing .is-leaving: with no transition declared on the base, it snaps
   back to opacity 0 / scale 1.08 instead of animating there in front of us. */

.hero__slide{
  position: absolute;
  inset: 0;
  opacity: 0;
  transform: scale(1.08);
}
.hero__slide.is-active{
  opacity: 1;
  /* --drag is written by the swipe handler so the slide follows the finger.
     It is a separate translate rather than a change to the scale, so the
     cross-fade's own transform is untouched by the gesture. */
  transform: translateX(var(--drag, 0px)) scale(1);
  transition: opacity var(--hero-fade) var(--ease-out),
              transform var(--hero-fade) var(--ease-out);
}
/* While a finger is down the slide must track it exactly; a transition here
   would make the drag lag behind the thumb. */
.hero__slide.is-dragging{ transition: none; }
.hero__slide.is-leaving{
  opacity: 0;
  transform: scale(1.05);
  transition: opacity var(--hero-fade) var(--ease-out),
              transform var(--hero-fade) var(--ease-out);
}

/* The idle drift rides the <picture>, not the slide, so it cannot fight the
   transform the cross-fade is already using. */
.hero__slide.is-active picture{
  animation: hero-drift 8000ms var(--ease-out) forwards;
}
@keyframes hero-drift{
  from{ transform: scale(1); }
  to{ transform: scale(1.06); }
}

/* --------------------------------------------------------------------------
   Control cluster — bottom-right, in the rail
   --------------------------------------------------------------------------
   Previously the arrows were edge-anchored and vertically centred. That only
   works when the middle band of the hero is empty; here the headline is
   left-aligned and spans most of the width, so the middle band is the busiest
   part of the composition and the left arrow landed inside the M of
   MODULARNI. Vertical centring was the wrong anchor for this layout.

   SAFE ZONE. The cluster now lives in .hero__rail, a flow sibling *below*
   .hero__inner. That is the enforcement — normal flow cannot produce an
   overlap, so there is no collision test to get wrong and no viewport at
   which it can regress. If the hero runs short, the rail pushes down and the
   headline goes with it; it never slides underneath. */

.hero__rail{
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-5);
  width: 100%;
  margin-top: clamp(20px, 3.5vh, 44px);
  /* One shared baseline for the scroll cue on the left and the cluster on the
     right. That shared line is what makes it read as designed. */
  min-height: 44px;
  /* Reserved corridor for the assistant launcher, which is fixed to this same
     corner. --assistant-w is measured from the launcher itself by
     assistant.js, so this stays correct whether the pill shows its label or
     has collapsed to a square. Applied at every width, not just on phones:
     the launcher is bottom-left on desktop too, and the scroll cue was
     landing on top of it at 1024, 1280 and 1440. */
  padding-left: calc(var(--assistant-w, 0px) + var(--fixed-gap));
}

.hero-controls{
  display: flex;
  align-items: center;
  gap: var(--s-3);
  margin-left: auto;
}

/* ---- indicator rules: never hidden ----
   They carry position, not just control. A slider whose position indicator
   disappears reads as broken, so only the arrows and the pause toggle take
   part in the idle-hide. */
.hero__dots{
  display: flex;
  align-items: center;
  gap: var(--s-2);
}
.hero__dot{
  position: relative;
  width: 44px;
  height: 44px;               /* the target; the rule itself is 3px */
  display: grid;
  align-items: center;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}
.hero__dot::before,
.hero__dot::after{
  content: "";
  grid-area: 1 / 1;
  height: 3px;
  width: 100%;
}
.hero__dot::before{ background: rgb(var(--paper-rgb) / .3); }
.hero__dot::after{
  background: var(--red);
  transform: scaleX(0);
  transform-origin: left center;
}
.hero__dot[aria-current="true"]::after{
  animation: hero-dot var(--hero-dwell, 6000ms) linear forwards;
  animation-play-state: var(--hero-play, running);
}
@keyframes hero-dot{ to{ transform: scaleX(1); } }

/* Autoplay off: the rule shows position, not elapsed time. */
.hero__dots.is-static .hero__dot[aria-current="true"]::after{
  animation: none;
  transform: scaleX(1);
}

/* ---- pause + arrows: these do hide ---- */
.hero__pause,
.hero__arrows{
  opacity: 0;
  pointer-events: none;
  /* Fade only. Movement at rest pulls the eye to the wrong part of the
     composition, and this cluster is deliberately the quietest thing in it. */
  transition: opacity var(--dur-md) var(--ease-out);
}
.hero.controls-visible .hero__pause,
.hero.controls-visible .hero__arrows,
.hero__pause:focus-visible,
.hero__arrows:focus-within{
  opacity: 1;
  pointer-events: auto;
  transition-duration: var(--dur-sm);
}

.hero__pause{
  min-width: 44px;
  min-height: 44px;
  padding: 0 var(--s-2);
  border: 0;
  background: none;
  cursor: pointer;
  font: 500 .6875rem/1 var(--font-mono);
  letter-spacing: var(--track-mono-label);
  text-transform: uppercase;
  color: var(--on-dark-2);
  transition: opacity var(--dur-md) var(--ease-out), color var(--dur-sm) var(--ease-soft);
}
.hero__pause:hover{ color: var(--paper); }

.hero__divider{
  width: 1px;
  height: 20px;
  background: var(--line-dark);
  opacity: 0;
  transition: opacity var(--dur-md) var(--ease-out);
}
.hero.controls-visible .hero__divider{ opacity: 1; }

/* One split pill, not two floating buttons. Each half is notched on its own
   outer bottom corner, so the pair reads as a single cut form — the same
   gesture as the CTA button pair. */
.hero__arrows{
  position: relative;
  display: flex;
}
.hero__nav{
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  background: rgb(var(--ink-rgb) / .8);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  color: var(--paper);
  cursor: pointer;
  border-radius: var(--r-xs);           /* fallback before notch.js */
  transition: background-color var(--dur-sm) var(--ease-soft),
              color var(--dur-sm) var(--ease-soft);
}
.hero__nav--next{ transform: rotate(180deg); }   /* one chevron, mirrored */
.hero__nav:hover{ background: var(--orange); color: var(--ink); }
.hero__nav:active{ background: var(--orange-deep); color: var(--paper); }
.hero__nav svg{ width: 16px; height: 16px; display: block; }

/* The hairline that splits the pill. Drawn over the pair rather than as a
   border, because a border on a clipped element is cut away with the corner. */
.hero__arrows::after{
  content: "";
  position: absolute;
  top: 8px; bottom: 8px; left: 50%;
  width: 1px;
  background: var(--line-dark);
  pointer-events: none;
}

/* ---- mobile ---------------------------------------------------------------
   The arrows are not styled away here — hero-slider.js does not build them at
   all below this width or on any touch device, so there are no dead nodes and
   no listeners on a phone. What is left moves to the left of the rail. */
@media (max-width: 768px){
  .hero__rail{ justify-content: flex-start; }
  .hero-controls{ margin-left: 0; }
  .hero__pause,
  .hero__divider{ opacity: 1; pointer-events: auto; }
}

/* ---- the one-time swipe hint ---- */
.hero__hint{
  position: absolute;
  left: 50%;
  bottom: 22%;
  transform: translateX(-50%);
  display: flex;
  gap: var(--s-4);
  font: 400 1.5rem/1 var(--font-body);
  color: var(--paper);
  opacity: 0;
  pointer-events: none;
  text-shadow: 0 2px 12px rgb(var(--ink-rgb) / .6);
}
.hero__hint.is-on{ animation: hero-hint 2600ms var(--ease-in-out) forwards; }
@keyframes hero-hint{
  0%, 100%{ opacity: 0; }
  14%, 40%{ opacity: .9; }
  27%{ opacity: .25; }
  56%, 82%{ opacity: .9; }
  69%{ opacity: .25; }
}

@media (prefers-reduced-motion: reduce){
  .hero__slide.is-active picture{ animation: none; }
  .hero__dot[aria-current="true"]::after{ animation: none; transform: scaleX(1); }
  .hero__pause, .hero__arrows, .hero__divider{
    opacity: 1; pointer-events: auto; transition: none;
  }
  .hero__hint{ display: none; }
}

/* The brief specifies a flat rgba(20,20,20,.45) overlay. A flat 45% is not
   enough to guarantee AA for the display type against an unknown photograph,
   so the scrim keeps .45 across the upper frame and deepens toward the bottom
   where the type actually sits. */
.hero__veil{
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(
    to top,
    rgb(20 20 20 / .88) 0%,
    rgb(20 20 20 / .72) 38%,
    var(--veil) 72%,
    var(--veil) 100%
  );
}
/* The red rule closes the hero — the same gesture as the logo's underline. */
.hero::after{
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 5px;
  background: var(--red);
  transform: scaleX(0);
  transform-origin: left center;
}
.hero.is-revealed::after{
  transform: scaleX(1);
  transition: transform var(--dur-xl) var(--ease-out) var(--dur-lg);
}

.hero__inner{
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: end;
  gap: var(--s-6);
  width: 100%;
}
@media (max-width: 900px){
  .hero__inner{ grid-template-columns: 1fr; }
}

/* Three lines of .display-xl at its 11rem ceiling overflow a 900px-tall
   viewport, so the hero runs its own slightly shorter scale. */
.hero__title{
  margin-block: var(--s-5) var(--s-6);
  font-size: clamp(2.75rem, 8vw, 8rem);
  letter-spacing: var(--track-display-xl);
}

/* Each line wipes in from its own baseline, so the type reveals rather than
   fades — closer to a printing press than a cross-dissolve.

   Leading of 0.82 makes the line box shorter than the glyphs it holds, so the
   caron on Š and the descenders sit outside it. The padding grows the clip
   box back to the real glyph height and the negative margin gives the visual
   leading back, otherwise the wipe shaves the diacritics off. */
.hero__line{
  display: block;
  padding-block: .18em .12em;
  margin-block: -.18em -.12em;
}
/* --------------------------------------------------------------------------
   B6 — the entrance sequence
   --------------------------------------------------------------------------
   The first three seconds set the perception of the whole site, so the hero
   arrives in an order rather than all at once:

     0            curtain up, image already at 1.06
     0 -> 1200    image settles to 1                     --hero-settle
     200          eyebrow wipes in                       --hero-t-eyebrow
     320/400/480  three headline lines, 80ms apart       --hero-t-line
     700          paragraph rises 12px                   --hero-t-lede
     820 / 880    CTA, then the + button                 --hero-t-cta
     1000         bottom rail                            --hero-t-rail
     then         autoplay is allowed to start

   Every resting state below is gated on html.js, and has to be. Only
   JavaScript adds .is-revealed, so an ungated clip would leave the largest
   text on the site clipped to nothing for anyone without it. */

html.js .hero__line,
html.js .hero .eyebrow{ clip-path: inset(100% 0 0 0); }
html.js .hero__lede{ opacity: 0; transform: translateY(12px); }
/* --enter, not transform: the button composes four movers into one transform
   (see .btn in components.css) and writing one here would cancel the rest. */
html.js .hero__aside .btn--primary,
html.js .hero__aside .btn--icon{ opacity: 0; --enter: 12px; }
html.js .hero__rail{ opacity: 0; }

.hero__line > span{ display: block; }

/* Display type wipes; it never fades. Fading large type in reads cheap —
   a wipe reads printed. */
html.js .hero.is-revealed .hero__line,
html.js .hero.is-revealed .eyebrow{
  clip-path: inset(0 0 0 0);
  transition: clip-path var(--dur-xl) var(--ease-out) var(--d, 0ms);
}
html.js .hero.is-revealed .eyebrow{ --d: var(--hero-t-eyebrow); }

html.js .hero.is-revealed .hero__lede{
  opacity: 1;
  transform: none;
  transition: opacity var(--dur-lg) var(--ease-out) var(--d, 0ms),
              transform var(--dur-lg) var(--ease-out) var(--d, 0ms);
}
html.js .hero.is-revealed .hero__aside .btn--primary,
html.js .hero.is-revealed .hero__aside .btn--icon{
  opacity: 1;
  --enter: 0px;
  transition: opacity var(--dur-lg) var(--ease-out) var(--d, 0ms),
              transform var(--dur-lg) var(--ease-out) var(--d, 0ms);
}
html.js .hero.is-revealed .hero__lede{ --d: var(--hero-t-lede); }
html.js .hero.is-revealed .hero__aside .btn--primary{ --d: var(--hero-t-cta); }
html.js .hero.is-revealed .hero__aside .btn--icon{ --d: calc(var(--hero-t-cta) + 60ms); }

html.js .hero.is-revealed .hero__rail{
  opacity: 1;
  transition: opacity var(--dur-lg) var(--ease-out) var(--hero-t-rail);
}

/* The image settles rather than drifting, for the first slide only. Once the
   sequence has finished, .is-sequenced hands it over to the idle drift. */
html.js .hero:not(.is-sequenced) .hero__slide.is-active picture{
  animation: hero-settle var(--hero-settle) var(--ease-out) forwards;
}
@keyframes hero-settle{
  from{ transform: scale(1.06); }
  to{ transform: scale(1); }
}

/* Reduced motion: every one of the above is at its end state at 0ms. */
@media (prefers-reduced-motion: reduce){
  html.js .hero__line,
  html.js .hero .eyebrow{ clip-path: none; }
  html.js .hero__lede{ opacity: 1; transform: none; }
  html.js .hero__aside .btn--primary,
  html.js .hero__aside .btn--icon{ opacity: 1; --enter: 0px; }
  html.js .hero__rail{ opacity: 1; }
  html.js .hero:not(.is-sequenced) .hero__slide.is-active picture{ animation: none; }
  html.js .hero.is-revealed .hero__line,
  html.js .hero.is-revealed .eyebrow,
  html.js .hero.is-revealed .hero__lede,
  html.js .hero.is-revealed .hero__aside .btn--primary,
  html.js .hero.is-revealed .hero__aside .btn--icon,
  html.js .hero.is-revealed .hero__rail{ transition: none; }
}

.hero__lede{
  max-width: 46ch;
  color: var(--on-dark);
  opacity: .88;
}

/* ---- A5: mobile recomposition -------------------------------------------
   The mobile hero was a tall column with a void between the top of the image
   and the headline. Everything is now anchored to the bottom so the block
   reads as grounded rather than floating. */
@media (max-width: 768px){
  .hero{ padding-block: clamp(5rem, 12vh, 7rem) clamp(20px, 3vh, 32px); }

  /* 0.82 is too tight below this width — the caron on Š meets the descenders
     of the line above. */
  .hero__title{ line-height: .92; }

  .hero__lede{
    max-width: 38ch;
    font-size: .95rem;
  }
}

/* A genuinely dark base under the copy, weighted to the bottom where the type
   actually sits. */
@media (max-width: 768px){
  .hero__veil{
    background: linear-gradient(
      180deg,
      rgb(20 20 20 / .25) 0%,
      rgb(20 20 20 / .35) 45%,
      rgb(20 20 20 / .88) 100%
    );
  }
}

.hero__aside{
  display: flex;
  flex-direction: column;
  gap: var(--s-5);
  align-items: flex-start;
}
@media (max-width: 900px){
  .hero__aside{ flex-direction: row; align-items: center; flex-wrap: wrap; }
}

.hero__cue{ color: var(--on-dark); opacity: .75; }

/* ==========================================================================
   02 — THREE BUSINESS LINES
   ========================================================================== */

.lines__grid{
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(1.5rem, 3vw, 3rem);
}
@media (max-width: 900px){
  .lines__grid{ grid-template-columns: 1fr; gap: var(--s-7); }
}

/* ==========================================================================
   03 — EDITORIAL STATEMENT + FLOATING POLYGON FIELD
   ========================================================================== */

.statement{
  position: relative;
  overflow: clip;
}
.statement__inner{
  position: relative;
  z-index: 2;
  text-align: center;
  padding-block: clamp(2rem, 6vw, 5rem);
}
/* The 24ch measure has to sit on the element that carries the editorial
   font-size — on the wrapper, ch resolves against 17px body text and the
   statement collapses to two words a line. */
.statement__text{
  max-width: 24ch;
  margin-inline: auto;
  text-wrap: balance;
}

/* The field of images drifting behind the statement at differing depths. */
.polyfield{
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
}
.polyfield__item{
  position: absolute;
  width: var(--w, 14vw);
  aspect-ratio: var(--ar, 3 / 4);
  overflow: hidden;
  /* will-change is managed by parallax.js, for the same reason. */
  opacity: .92;
}
.polyfield__item img{ width: 100%; height: 100%; object-fit: cover; }

/* Placed by hand so the composition reads, rather than scattered randomly.
   Each carries its own irregular polygon — the resting state of the same
   geometry clip-morph.js animates in the feature block, so the two sections
   speak the same language. */
.polyfield__item:nth-child(1){ --w: 13vw; --ar: 3/4;  top:  6%; left:  2%;
  clip-path: polygon(6% 0%, 100% 4%, 94% 88%, 12% 100%, 0% 46%); }
.polyfield__item:nth-child(2){ --w: 9vw;  --ar: 1/1;  top: 30%; left: 12%;
  clip-path: polygon(0% 8%, 88% 0%, 100% 72%, 40% 100%, 4% 82%); }
.polyfield__item:nth-child(3){ --w: 11vw; --ar: 4/5;  top: 64%; left:  4%;
  clip-path: polygon(10% 2%, 96% 0%, 100% 60%, 74% 100%, 0% 92%, 2% 34%); }
.polyfield__item:nth-child(4){ --w: 14vw; --ar: 3/2;  top: 82%; left: 20%;
  clip-path: polygon(0% 0%, 92% 6%, 100% 84%, 22% 100%, 4% 52%); }
.polyfield__item:nth-child(5){ --w: 10vw; --ar: 1/1;  top:  4%; right: 8%;
  clip-path: polygon(4% 6%, 100% 0%, 90% 94%, 16% 100%); }
.polyfield__item:nth-child(6){ --w: 12vw; --ar: 4/5;  top: 34%; right: 2%;
  clip-path: polygon(0% 4%, 84% 0%, 100% 48%, 92% 100%, 8% 90%); }
.polyfield__item:nth-child(7){ --w: 10vw; --ar: 3/4;  top: 66%; right: 12%;
  clip-path: polygon(8% 0%, 100% 10%, 96% 100%, 0% 86%, 4% 30%); }
.polyfield__item:nth-child(8){ --w: 13vw; --ar: 3/2;  top: 88%; right: 4%;
  clip-path: polygon(0% 10%, 90% 0%, 100% 70%, 46% 100%, 6% 88%); }

/* Below 900px the drifting field costs more than it gives: it collapses to a
   plain two-column stagger under the statement. */
@media (max-width: 900px){
  .polyfield{
    position: static;
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: var(--s-4);
    margin-top: var(--s-7);
  }
  .polyfield__item{
    position: static;
    width: 100%;
    transform: none !important;
  }
  .polyfield__item:nth-child(even){ margin-top: var(--s-6); }
  .polyfield__item:nth-child(n+7){ display: none; }
}

/* ==========================================================================
   04 — TRUST BAR
   ========================================================================== */

.trust__grid{
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(1.5rem, 3vw, 3rem);
}
@media (max-width: 900px){
  .trust__grid{ grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s-6); }
}

/* ==========================================================================
   05–07 — THE SIGNATURE FEATURE BLOCK
   ========================================================================== */

.feature{
  position: relative;
  padding-block: clamp(4rem, 9vw, 8rem);
}
.feature__inner{
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
  gap: clamp(2rem, 5vw, 5rem);
  align-items: center;
}
.feature--flip .feature__inner{ direction: rtl; }
.feature--flip .feature__inner > *{ direction: ltr; }

@media (max-width: 900px){
  .feature__inner,
  .feature--flip .feature__inner{ grid-template-columns: 1fr; direction: ltr; }
}

.feature__body{ display: flex; flex-direction: column; gap: var(--s-5); align-items: flex-start; }
/* align-items: flex-start shrink-wraps children; the spec table has to keep
   the full column width or it collapses to one cell a row. */
.feature__body .meta-row{ align-self: stretch; }
.feature__title{ margin-block: var(--s-3) var(--s-2); }
.feature__text{ max-width: 40ch; color: var(--fg-2); }
.feature__media{ position: relative; }

.feature__figure{
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  /* will-change is set by clip-morph.js while the figure is in the scroll
     window and removed once it has resolved. Declaring it here would hold a
     compositor layer open for every figure on the page, permanently. */
}
.feature__figure img{ width: 100%; height: 100%; object-fit: cover; }
.feature__figure picture{ display: block; width: 100%; height: 100%; }

.feature__more{ margin-top: clamp(2.5rem, 5vw, 4rem); }

/* ==========================================================================
   08 — FACTORY
   ========================================================================== */

.factory{
  background: var(--dark);
  color: var(--on-dark);
  position: relative;
}
.factory__inner{
  display: grid;
  grid-template-columns: minmax(0, 320px) minmax(0, 1fr);
  gap: clamp(2rem, 5vw, 5rem);
  align-items: start;
}
@media (max-width: 900px){
  .factory__inner{ grid-template-columns: 1fr; }
}

.factory__sticky{
  position: sticky;
  top: clamp(6rem, 16vh, 9rem);
  display: flex;
  flex-direction: column;
  gap: var(--s-5);
  align-items: flex-start;
}
@media (max-width: 900px){
  .factory__sticky{ position: static; }
}

.process{ border-top: 1px solid var(--line-dark); }
.process__row{
  display: grid;
  grid-template-columns: 4rem minmax(0, 1fr) auto;
  gap: var(--s-5);
  align-items: baseline;
  padding-block: clamp(1.5rem, 3vw, 2.5rem);
  border-bottom: 1px solid var(--line-dark);
  cursor: default;
  transition: color var(--dur-sm) var(--ease-soft),
              padding-inline var(--dur-sm) var(--ease-soft);
}
.process__num{
  font: 500 0.6875rem/1 var(--font-mono);
  letter-spacing: var(--track-mono-label);
  color: var(--on-dark-2);
}
.process__title{
  font: 800 clamp(1.35rem, 2.6vw, 2.25rem)/1.05 var(--font-display);
  letter-spacing: var(--track-display-m);
  text-transform: uppercase;
}
.process__text{
  grid-column: 2;
  font-size: .9375rem;
  line-height: 1.55;
  color: var(--on-dark-2);
  max-width: 52ch;
  /* The grid row gap already separates it from the title. */
  margin-top: calc(var(--s-5) * -0.5);
}
.process__mark{
  color: var(--on-dark-2);
  opacity: 0;
  transition: opacity var(--dur-sm) var(--ease-soft);
}
@media (hover: hover){
  .process__row:hover{ color: var(--orange); padding-inline: var(--s-4); }
  .process__row:hover .process__mark{ opacity: 1; }
}

/* Below 480 the three-column row leaves the title about 140px, and a single
   uppercase word — KONSTRUKCIJA — needs 180. It was the only real source of
   horizontal overflow at 320px on the whole site. The number moves above the
   title instead of beside it, which gives the heading the full column. */
@media (max-width: 480px){
  .process__row{
    grid-template-columns: minmax(0, 1fr) auto;
    gap: var(--s-2) var(--s-4);
  }
  .process__num{ grid-column: 1; grid-row: 1; }
  .process__mark{ grid-column: 2; grid-row: 1; }
  .process__title{ grid-column: 1; grid-row: 2; font-size: clamp(1.15rem, 5.6vw, 1.5rem); }
  .process__text{ grid-column: 1; grid-row: 3; }
}

/* ==========================================================================
   09 — HOW ORDERING WORKS
   ========================================================================== */

.howto__inner{
  display: grid;
  grid-template-columns: minmax(0, 280px) minmax(0, 1fr);
  gap: clamp(2rem, 5vw, 5rem);
  align-items: start;
}
@media (max-width: 900px){
  .howto__inner{ grid-template-columns: 1fr; }
}
.howto__sticky{ position: sticky; top: clamp(6rem, 16vh, 9rem); }
@media (max-width: 900px){ .howto__sticky{ position: static; } }

.howto__steps{
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(1.25rem, 2.5vw, 2.5rem);
}
@media (max-width: 900px){
  .howto__steps{ grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 520px){
  .howto__steps{ grid-template-columns: 1fr; }
}

/* ==========================================================================
   10 — REFERENCES
   ========================================================================== */

.refs{ background: var(--paper-steel); }

/* ==========================================================================
   11 — RENTAL
   Lighter on purpose: a different buyer, a different journey.
   ========================================================================== */

.rental{ background: var(--paper-light); }
.rental__inner{
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(2rem, 5vw, 5rem);
  align-items: center;
}
@media (max-width: 900px){ .rental__inner{ grid-template-columns: 1fr; } }

.rental__list{
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s-5);
  margin-top: var(--s-5);
}
@media (max-width: 520px){ .rental__list{ grid-template-columns: 1fr; } }

.rental__item{
  display: flex; flex-direction: column; gap: 6px;
  padding-top: var(--s-4);
  border-top: 1px solid var(--line);
}
.rental__item strong{
  font: 700 1.05rem/1.2 var(--font-display);
  letter-spacing: var(--track-heading);
}
.rental__item span{ font-size: .875rem; color: var(--fg-2); }

/* ==========================================================================
   12 — CONTACT
   The oversized word sits behind the images, not beside them.
   ========================================================================== */

.contact{ position: relative; overflow: clip; }
.contact__word{
  position: relative;
  z-index: 1;
  text-align: center;
  color: var(--fg);
  pointer-events: none;
  margin-bottom: clamp(-4rem, -6vw, -2rem);
}
.contact__media{
  position: relative;
  z-index: 2;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(1.5rem, 4vw, 4rem);
  max-width: 62rem;
  margin-inline: auto;
}
.contact__media .frame:nth-child(1){ margin-top: clamp(2rem, 6vw, 5rem); }
.contact__media .frame:nth-child(2){ margin-bottom: clamp(2rem, 6vw, 5rem); }

.contact__lines{
  position: relative;
  z-index: 2;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--s-4);
  margin-top: clamp(3rem, 6vw, 5rem);
}
@media (max-width: 900px){
  .contact__lines{ grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 480px){
  .contact__lines{ grid-template-columns: 1fr; }
}

.contact__foot{
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-5) var(--s-7);
  align-items: center;
  justify-content: space-between;
  margin-top: clamp(2.5rem, 5vw, 4rem);
  padding-top: var(--s-5);
  border-top: 1px solid var(--line);
}
