/* ==========================================================================
   Quality Fiber — page blocks
   ========================================================================== */

/* ============ FIXED LEFT RAIL ============ */
.rail {
  position: fixed;
  inset: 0 auto 0 0;
  z-index: 60;
  width: var(--rail);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-between;
  padding: 1.6rem 0;
  background: var(--ink);
  color: var(--bone);
}

.rail-logo img { width: 40px; height: auto; }

.rail-mid { display: grid; justify-items: center; gap: 1.6rem; }

/* vertical wordmark */
.rail-vert {
  writing-mode: vertical-rl;
  font-size: .68rem;
  font-weight: 600;
  letter-spacing: .42em;
  text-transform: uppercase;
  color: var(--sage);
}

.rail-menu {
  display: grid;
  gap: 6px;
  padding: 1rem .8rem;
  width: 100%;
  justify-items: center;
}
.rail-menu i {
  display: block;
  width: 26px; height: 1.5px;
  background: var(--bone);
  transition: transform .4s var(--ease), width .4s var(--ease), opacity .3s;
}
.rail-menu:hover i:nth-child(1) { width: 18px; }
.rail-menu:hover i:nth-child(3) { width: 18px; }
.rail-menu[aria-expanded="true"] i:nth-child(1) { transform: translateY(7.5px) rotate(45deg); width: 26px; }
.rail-menu[aria-expanded="true"] i:nth-child(2) { opacity: 0; }
.rail-menu[aria-expanded="true"] i:nth-child(3) { transform: translateY(-7.5px) rotate(-45deg); width: 26px; }

.rail-foot { display: grid; justify-items: center; gap: 1.2rem; }
.rail-call {
  width: 42px; height: 42px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--clay);
  color: var(--bone);
  transition: transform .3s var(--ease);
}
.rail-call:hover { transform: scale(1.1); }
.rail-call .icn { width: 18px; height: 18px; }

/* mobile: rail becomes a top bar */
.topbar { display: none; }

@media (max-width: 900px) {
  .rail { display: none; }
  .topbar {
    position: fixed;
    inset: 0 0 auto 0;
    z-index: 60;
    height: 68px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding-inline: var(--gut);
    background: var(--ink);
    color: var(--bone);
  }
  .topbar img { width: 34px; }
  .topbar .rail-menu { width: auto; padding: .5rem; }
}

/* ============ FULLSCREEN MENU ============ */
.menu {
  position: fixed;
  inset: 0;
  z-index: 55;
  background: var(--ink);
  color: var(--bone);
  padding: clamp(2rem, 4vw, 4rem) clamp(1.5rem, 5vw, 6rem) 3rem calc(var(--rail) + clamp(1.5rem, 5vw, 6rem));
  display: grid;
  grid-template-columns: 1.5fr 1fr;
  grid-template-rows: auto auto;
  gap: clamp(1.5rem, 3vw, 3rem);
  /* "safe" matters: with plain centring, content taller than the viewport
     overflows equally in both directions and the part above the scroll origin
     cannot be reached at all. safe centring falls back to start once it stops
     fitting, so nothing is ever stranded. */
  align-content: safe center;
  overflow-y: auto;
  overscroll-behavior: contain;
  clip-path: circle(0 at 46px 50%);
  visibility: hidden;
  transition: clip-path .8s var(--ease2), visibility .8s;
}
.menu.open { clip-path: circle(150% at 46px 50%); visibility: visible; }

/* The product list holds the left column across both rows; the contact block
   and the audience links stack in the right one, which was standing empty. */
.menu-list    { grid-column: 1; grid-row: 1 / span 2; display: grid; gap: .1rem; align-content: center; }
.menu-aside   { grid-column: 2; grid-row: 1; }

/* On a short desktop window the right column becomes the constraint, not the
   nav: the contact block plus the audience links stop fitting and a chip drops
   off the bottom. The address, phone and socials appear in the footer of every
   page, so on a short screen the column gives its whole height to the links. */
@media (min-width: 901px) and (max-height: 680px) {
  .menu-aside   { display: none; }
  .menu-sectors { grid-row: 1 / span 2; align-self: center; border-top: 0; padding-top: 0; }
}
.menu-sectors { grid-column: 2; grid-row: 2; }
.menu-item {
  display: flex;
  align-items: baseline;
  gap: 1.2rem;
  padding: .22rem 0;
  font-family: 'Bricolage', sans-serif;
  /* Sized against the viewport's HEIGHT as well as its width. A width-only
     clamp reads fine on a tall screen and far too large on a short one — and on
     a short one the twelve items stop fitting, which is how "Terms &
     Conditions" ended up cut off the bottom. 4.6svh per item keeps all twelve
     plus the menu's padding inside the screen at any height. */
  font-size: min(clamp(1.3rem, .8rem + 1.9vw, 2.6rem), 4.6svh);
  font-weight: 500;
  line-height: 1.05;
  letter-spacing: -.03em;
  color: var(--bone);
  opacity: .55;
  transition: opacity .35s var(--ease), transform .45s var(--ease), color .35s;
}
.menu-item:hover { opacity: 1; color: var(--clay); transform: translateX(14px); }
.menu-item .num { font-size: .75rem; opacity: .6; }

/* audience pages — a secondary tier under the main nav, deliberately smaller
   so it reads as a cross-cut of the product list rather than a rival to it */
.menu-sectors { border-top: 1px solid rgba(243,240,231,.14); padding-top: clamp(1.2rem, 2vw, 1.8rem); align-self: start; }
.menu-sectors h4 {
  font-size: .68rem; font-weight: 600; letter-spacing: .22em;
  text-transform: uppercase; color: var(--clay); margin-bottom: .9rem;
}
.menu-sectors ul { display: flex; flex-wrap: wrap; gap: .5rem .55rem; }
.menu-sectors a {
  display: inline-block;
  padding: .5rem .95rem;
  font-size: .8rem;
  font-weight: 500;
  letter-spacing: .02em;
  color: var(--sage);
  box-shadow: inset 0 0 0 1px rgba(243,240,231,.18);
  transition: color .3s, background .3s, box-shadow .3s;
}
.menu-sectors a:hover { color: var(--ink); background: var(--bone); box-shadow: none; }
.menu-sectors a[aria-current="page"] { color: var(--bone); background: var(--clay); box-shadow: none; }

.menu-aside { align-self: center; display: grid; gap: clamp(1.1rem, 2.4vh, 2rem); align-content: center; }
.menu-aside h4 { font-size: .7rem; letter-spacing: .22em; text-transform: uppercase; color: var(--clay); font-weight: 600; margin-bottom: .7rem; }
.menu-aside p, .menu-aside a { color: var(--sage); font-size: .98rem; line-height: 1.7; }
.menu-aside a:hover { color: var(--bone); }
.menu-socials { display: flex; gap: 1.4rem; }
.menu-socials a { font-size: .78rem; letter-spacing: .14em; text-transform: uppercase; }

@media (max-width: 900px) {
  .menu {
    grid-template-columns: 1fr;
    grid-template-rows: auto auto;
    padding: 92px var(--gut) 4.5rem;
    align-content: start;
    clip-path: circle(0 at 92% 34px);
  }
  .menu.open { clip-path: circle(160% at 92% 34px); }
  /* single column: the list, then the audience links; the contact block is
     already in the footer on a phone */
  .menu-list    { grid-column: 1; grid-row: 1; }
  .menu-sectors { grid-column: 1; grid-row: 2; }
  .menu-aside   { display: none; }
}

/* ============ HERO ============ */
.hero {
  position: relative;
  min-height: 100svh;
  display: grid;
  align-items: end;
  overflow: hidden;
  isolation: isolate;
}
.hero-bg { position: absolute; inset: 0; z-index: -2; overflow: hidden; }
.hero-bg img {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  opacity: 0;
  /* Slow, overlapping cross-fade. At .35s the change registered as a cut
     rather than a dissolve; paired with the eight-second hold in app.js this
     makes the slide change something you notice afterwards, not during. */
  transition: opacity 1.5s var(--ease2);
}
.hero-bg img.show { opacity: 1; }
/* The visible frame is marked with a class rather than :first-child. Once the
   images are wrapped in <picture> for the mobile crop, every img is the first
   child of its own <picture> and a :first-child rule reveals all of them at
   once. The server marks the initial frame; app.js moves the class from there. */

/* Every full-bleed <picture> is transparent to layout. Its <img> is absolutely
   positioned, so the wrapper has nothing to contribute but an inline box — and
   that box was adding a band of section background above the image. */
.hero-bg picture,
picture.bleed { display: contents; }

/* slide indicators — vertical, right edge, clear of the WhatsApp button */
.hero-dots {
  position: absolute;
  right: var(--gut);
  top: 50%;
  translate: 0 -50%;
  display: grid;
  gap: .7rem;
  z-index: 2;
}
.hero-dots button {
  display: block;
  width: 2px;
  height: 26px;
  background: rgba(243,240,231,.32);
  transition: background .35s, height .35s var(--ease);
}
.hero-dots button:hover { background: rgba(243,240,231,.6); }
.hero-dots button[aria-selected="true"] { background: var(--clay); height: 42px; }

@media (max-width: 900px) { .hero-dots { display: none; } }
.hero::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  /* The client asked for the hero type to read better over the photograph. The
     weak point was the 18% band at a third of the way down, which is exactly
     where a two-line headline starts on a laptop, and a left wash that ran out
     at 55% while the headline runs past 60ch. Both stops move; the middle of
     the frame is still the lightest part, so the photograph is not flattened. */
  background:
    linear-gradient(180deg, rgba(7,11,9,.80) 0%, rgba(7,11,9,.34) 30%, rgba(7,11,9,.66) 64%, rgba(7,11,9,.92) 100%),
    linear-gradient(90deg, rgba(7,11,9,.74) 0%, rgba(7,11,9,.30) 52%, transparent 80%);
}

.hero-inner {
  /* Sits above the absolutely-positioned hero decoration rather than under it,
     so a link in the breadcrumb or the CTA row is always the top-most element
     at its own coordinates. */
  position: relative;
  z-index: 1;
  width: 100%;
  padding: clamp(2rem, 4vw, 4rem) var(--gut) clamp(2.5rem, 4vw, 4.5rem);
  display: grid;
  gap: clamp(1.5rem, 3vw, 2.5rem);
  color: var(--bone);
}
.hero h1 { color: var(--bone); max-width: 15ch; }
.hero h1 em { font-family: 'Editorial', Georgia, serif; font-style: italic; font-weight: 400; letter-spacing: -.01em; color: var(--clay); }

.hero-row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 2rem;
  border-top: 1px solid rgba(243,240,231,.22);
  padding-top: 1.6rem;
}
.hero-row p { color: rgba(243,240,231,.8); max-width: 40ch; font-size: 1.02rem; }
.hero-actions { display: flex; flex-wrap: wrap; gap: .8rem; }

/* top meta strip */
.hero-meta {
  position: absolute;
  top: 0; left: 0; right: 0;
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding: clamp(1.6rem, 3vw, 2.6rem) var(--gut);
  /* Absolutely positioned, so it paints above the in-flow .hero-inner and its
     ~90px padding box reached down over the breadcrumb and swallowed the click
     on "Home". Nothing in this strip is interactive, so let clicks through. */
  pointer-events: none;
  font-size: .7rem;
  font-weight: 600;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: rgba(243,240,231,.7);
}

.scroll-cue {
  position: absolute;
  pointer-events: none;   /* decorative, and it overlaps the hero CTA row */
  right: var(--gut);
  bottom: clamp(2.5rem, 4vw, 4.5rem);
  display: grid;
  justify-items: center;
  gap: .8rem;
  font-size: .62rem;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: rgba(243,240,231,.6);
}
.scroll-cue span { display: block; width: 1px; height: 54px; background: rgba(243,240,231,.35); position: relative; overflow: hidden; }
.scroll-cue span::after {
  content: ''; position: absolute; inset: 0; background: var(--clay);
  animation: cue 2.4s var(--ease) infinite;
}
@keyframes cue { 0% { transform: translateY(-100%); } 60%,100% { transform: translateY(100%); } }

/* the fixed top bar would sit on top of the meta strip on small screens */
@media (max-width: 900px) {
  .hero { min-height: 92svh; }
  .hero-meta { display: none; }
}

/* ============ TEXT MARQUEE ============ */
.ticker {
  overflow: hidden;
  border-block: 1px solid var(--bone-3);
  padding-block: 1.15rem;
  background: var(--bone);
}
.ticker-track {
  display: flex;
  align-items: center;
  gap: 2.5rem;
  width: max-content;
  animation: run 42s linear infinite;
}
.ticker:hover .ticker-track { animation-play-state: paused; }
.ticker b {
  font-family: 'Bricolage', sans-serif;
  font-size: clamp(1.1rem, .9rem + .9vw, 1.7rem);
  font-weight: 500;
  letter-spacing: -.02em;
  white-space: nowrap;
  color: var(--ink);
}
.ticker i { width: 7px; height: 7px; border-radius: 50%; background: var(--clay); flex: none; }
@keyframes run { to { transform: translateX(-50%); } }

/* ============ HORIZONTAL PRODUCT RAIL ============ */
.rail-head {
  display: flex;
  flex-wrap: wrap;
  /* The side slot used to sit on flex-end, which left a block of empty beige
     above it whenever the heading ran to three or four lines. Centring the
     side content against the heading balances the row at every heading
     length, and applies to both shapes that occupy the slot: the lede
     paragraph on the homepage rails and the .tlink on a product page. */
  align-items: center;
  justify-content: space-between;
  gap: 2rem;
  padding: 0 var(--gut) clamp(2rem, 3vw, 3rem);
}
.rail-head h2 { max-width: 16ch; }

.hscroll {
  display: flex;
  gap: 1px;
  overflow-x: auto;
  /* Explicit, and load-bearing on touch. `overflow-x: auto` alone makes the
     browser compute overflow-y from `visible` to `auto`, and the cards are ~18px
     taller than the box — so a vertical swipe that started on a card dragged the
     images up and down inside the rail and never reached the page. */
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  padding-inline: 0;
}
.hscroll::-webkit-scrollbar { display: none; }

/* ---- pinned mode (added by JS on wide screens only) ---- */
.hpin { position: relative; }

.hpin.on .hpin-stage {
  position: sticky;
  top: 0;
  height: 100svh;
  display: grid;
  align-content: center;
  overflow: hidden;
}
/* While pinned the hint is lifted out of the flow and parked at the foot of the
   sticky viewport, so it rides along with the cards instead of occupying a strip
   of page of its own. Unpinned it stays an ordinary paragraph under the rail. */
.hpin.on .hpin-stage { position: sticky; }
.hpin.on .hint {
  position: absolute;
  /* Parked in the strip of empty stage above the cards. Below them it either
     sat behind the sticky enquiry bar or, once lifted clear of it, on top of
     the photographs — and this space was going to waste anyway. */
  inset: clamp(.9rem, 2.2vh, 1.8rem) 0 auto;
  margin: 0;
  text-align: center;
}
/* the page scroll drives scrollLeft, so snapping would fight it */
.hpin.on .hscroll {
  overflow: hidden;
  scroll-snap-type: none;
}
.hpin.on .pcard {
  flex: 0 0 auto;
  width: auto;
  /* Was 74svh, which left 234px of the pinned viewport empty above and below
     the cards — dead page you scroll through for nothing. */
  height: min(84svh, 780px);
  aspect-ratio: 3/4;
}

.pcard {
  position: relative;
  flex: 0 0 clamp(280px, 34vw, 520px);
  aspect-ratio: 3/4;
  scroll-snap-align: start;
  overflow: hidden;
  isolation: isolate;
  color: var(--bone);
}
.pcard img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  z-index: -2;
  /* Was saturate(1.08) — adding punch to photographs that are already very
     saturated pushed them over. */
  filter: saturate(.98) contrast(1.03);
  transition: transform 1.1s var(--ease2);
}
/* light scrim only: enough to anchor the glass panel, not enough to dull the photo */
.pcard::after {
  content: '';
  position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(180deg, rgba(7,11,9,.12) 0%, transparent 32%, rgba(7,11,9,.55) 100%);
  transition: background .5s;
}
.pcard:hover img { transform: scale(1.05); }
.pcard:hover::after { background: linear-gradient(180deg, rgba(7,11,9,.1) 0%, transparent 30%, rgba(7,11,9,.66) 100%); }

/* frosted panel holding the card text */
.pcard-body {
  position: absolute;
  inset: auto clamp(.7rem, 1vw, 1rem) clamp(.7rem, 1vw, 1rem);
  padding: clamp(1rem, 1.4vw, 1.4rem);
  display: grid;
  gap: .55rem;
  border-radius: 16px;
  /* A frosted panel over a saturated playground photograph turns it into a
     rainbow smear — the blur samples a dozen strong hues and averages them into
     mud, and the type loses contrast at the same time. A flat dark panel keeps
     the photo readable behind it and the caption readable on top. */
  background: rgba(9, 15, 12, .72);
  border: 1px solid rgba(243, 240, 231, .14);
  box-shadow: 0 10px 34px rgba(7, 11, 9, .34);
  transition: background .4s var(--ease), border-color .4s;
}
.pcard:hover .pcard-body,
.pcard:focus-within .pcard-body {
  background: rgba(9, 15, 12, .84);
  border-color: rgba(243, 240, 231, .26);
}

.pcard-body .num { color: #F0A98B; }
.pcard-body h3 { color: var(--bone); }
.pcard-body p {
  font-size: .95rem;
  line-height: 1.55;
  color: rgba(243,240,231,.86);
  max-height: 0;
  opacity: 0;
  overflow: hidden;
  transition: max-height .55s var(--ease), opacity .45s;
}
.pcard:hover p, .pcard:focus-within p { max-height: 8rem; opacity: 1; }
.pcard .tlink { color: var(--bone); }

.hint {
  padding: 1.2rem var(--gut) 0;
  font-size: .68rem;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--fg-muted);
}

/* ============ WHO WE BUILD FOR ============ */
/* Audience pages surfaced in the body of the homepage. They were only in the
   menu and the footer before, which meant nobody scrolling the page ever saw
   they existed. */
.sgrid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: clamp(.8rem, 1.2vw, 1.3rem);
}

.stile {
  position: relative;
  display: block;
  overflow: hidden;
  aspect-ratio: 4/5;
  background: var(--ink);
  isolation: isolate;
}
.stile img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  z-index: -2;
  transition: transform 1.1s var(--ease2);
}
.stile::after {
  content: '';
  position: absolute; inset: 0; z-index: -1;
  /* The caption block starts around 60% of the tile and runs to the bottom, so
     the scrim has to be near-opaque from 60% down. The old ramp only reached
     .55 at the midpoint, which left the title sitting on open sky. */
  background: linear-gradient(180deg,
    rgba(7,11,9,.05)  0%,
    rgba(7,11,9,.30) 30%,
    rgba(7,11,9,.78) 50%,
    rgba(7,11,9,.96) 100%);
  transition: background .45s;
}
.stile:hover img { transform: scale(1.06); }
.stile:hover::after {
  background: linear-gradient(180deg,
    rgba(7,11,9,.12)  0%,
    rgba(16,38,28,.44) 30%,
    rgba(7,11,9,.84) 50%,
    rgba(7,11,9,.98) 100%);
}

.stile-in {
  position: absolute;
  inset: auto 0 0 0;
  display: grid;
  gap: .55rem;
  padding: clamp(1.1rem, 1.6vw, 1.6rem);
  color: var(--bone);
}
.stile-in b {
  font-family: 'Bricolage', sans-serif;
  font-size: clamp(1.02rem, .95rem + .35vw, 1.28rem);
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: -.02em;
}
.stile-in > span { font-size: .9rem; line-height: 1.55; color: rgba(243,240,231,.88); }
.stile-in .tlink { color: var(--clay); font-size: .74rem; margin-top: .2rem; }

@media (max-width: 1100px) { .sgrid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 520px) {
  .sgrid { grid-template-columns: 1fr; }
  .stile { aspect-ratio: 16/10; }
  /* The caption is the same height but the tile is shorter, so it starts around
     40% instead of 55% — the ramp has to arrive sooner. */
  .stile::after {
    background: linear-gradient(180deg,
      rgba(7,11,9,.08)  0%,
      rgba(7,11,9,.44) 22%,
      rgba(7,11,9,.84) 44%,
      rgba(7,11,9,.97) 100%);
  }
}

/* ============ FILM / VIDEO ============ */
.film { background: var(--ink); color: var(--sage); }
.film-grid {
  display: grid;
  grid-template-columns: 1.85fr 1fr;
  gap: clamp(1.5rem, 2.5vw, 3rem);
  align-items: start;
}

/* stage */
.stage {
  position: relative;
  aspect-ratio: 16/9;
  overflow: hidden;
  background: #000;
  cursor: pointer;
}
.stage img {
  width: 100%; height: 100%;
  object-fit: cover;
  transition: transform 1.2s var(--ease2), opacity .4s;
}
.stage::after {
  content: '';
  position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(7,11,9,.34) 0%, rgba(7,11,9,.12) 45%, rgba(7,11,9,.34) 100%);
  transition: opacity .4s;
  pointer-events: none;
}
.stage iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.stage.playing::after, .stage.playing .stage-play { opacity: 0; pointer-events: none; }

.stage-play {
  position: absolute;
  top: 50%; left: 50%;
  translate: -50% -50%;
  width: clamp(64px, 6vw, 88px);
  aspect-ratio: 1;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--clay);
  color: var(--bone);
  /* deliberately no movement on hover — the button stays put, only its ring
     and colour respond, so the cursor never chases a shifting target */
  transition: background .3s, box-shadow .35s var(--ease), opacity .3s;
  box-shadow: 0 0 0 0 rgba(243,240,231,0);
}
.stage-play .icn { width: 32%; height: 32%; margin-left: 8%; }
.stage:hover .stage-play,
.stage:focus-visible .stage-play {
  background: var(--clay-2);
  box-shadow: 0 0 0 10px rgba(210,101,60,.28);
}

/* caption sits below the frame — these clips have burned-in subtitles and a
   contact banner, so text overlaid on the poster is never reliably legible */
.stage-cap {
  display: grid;
  gap: .35rem;
  padding-top: 1.1rem;
  margin-top: 1.1rem;
  border-top: 1px solid rgba(243,240,231,.14);
}
.stage-cap b {
  font-family: 'Bricolage', sans-serif;
  font-size: clamp(1.05rem, .9rem + .6vw, 1.45rem);
  font-weight: 600;
  letter-spacing: -.02em;
  color: var(--bone);
}
.stage-cap span { font-size: .9rem; color: rgba(243,240,231,.72); }

/* playlist */
.plist { display: grid; align-content: start; }
.pitem {
  display: grid;
  grid-template-columns: 92px 1fr;
  gap: 1rem;
  align-items: center;
  width: 100%;
  text-align: left;
  padding: .85rem 0;
  border-top: 1px solid rgba(243,240,231,.14);
  transition: opacity .3s;
  opacity: .62;
}
.plist .pitem:last-child { border-bottom: 1px solid rgba(243,240,231,.14); }
.pitem:hover { opacity: 1; }
.pitem[aria-current="true"] { opacity: 1; }
.pitem[aria-current="true"] .pthumb::after { opacity: 1; }

.pthumb { position: relative; aspect-ratio: 16/9; overflow: hidden; background: #000; }
.pthumb img { width: 100%; height: 100%; object-fit: cover; }
.pthumb::after {
  content: '';
  position: absolute; inset: 0;
  box-shadow: inset 0 0 0 2px var(--clay);
  opacity: 0;
  transition: opacity .3s;
}
.pmeta b {
  display: block;
  font-size: .96rem;
  font-weight: 600;
  line-height: 1.3;
  color: var(--bone);
}
.pmeta span { display: block; margin-top: .2rem; font-size: .82rem; color: var(--sage); }

@media (max-width: 900px) {
  .film-grid { grid-template-columns: 1fr; }
  .pitem { grid-template-columns: 76px 1fr; }
}

/* ============ STATS OVER IMAGE ============ */
.figures {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  padding-block: clamp(4rem, 3rem + 5vw, 8rem);
  color: var(--bone);
}
.figures img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: -2; }
.figures::after { content: ''; position: absolute; inset: 0; z-index: -1; background: rgba(7,11,9,.78); }

.fig-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: clamp(2rem, 3vw, 3.5rem);
}
.fig b {
  display: block;
  font-family: 'Bricolage', sans-serif;
  font-size: clamp(3rem, 2rem + 4.4vw, 6rem);
  font-weight: 600;
  line-height: .9;
  letter-spacing: -.05em;
  color: var(--bone);
}
.fig span {
  display: block;
  margin-top: .9rem;
  padding-top: .9rem;
  border-top: 1px solid rgba(243,240,231,.22);
  font-size: .74rem;
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--sage);
}

/* ============ SPLIT (about) ============ */
.split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: stretch;
  min-height: 78vh;
}
.split-img { position: relative; overflow: hidden; min-height: 46vh; }
.split-img img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.split-txt {
  display: grid;
  align-content: center;
  gap: 1.6rem;
  padding: clamp(3rem, 2rem + 4vw, 6rem) clamp(1.5rem, 4vw, 5rem);
}
.split-txt .lede { max-width: 52ch; }

.rules { display: grid; }
.rules li {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 1.2rem;
  align-items: baseline;
  padding: 1.05rem 0;
  border-top: 1px solid var(--bone-3);
  font-size: 1rem;
}
.rules li:last-child { border-bottom: 1px solid var(--bone-3); }
.dark .rules li, .forest .rules li { border-color: rgba(243,240,231,.16); }

@media (max-width: 900px) { .split { grid-template-columns: 1fr; } }

/* ============ EDGE-TO-EDGE GALLERY ============ */
.mosaic {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 2px;
}
.mtile { position: relative; overflow: hidden; cursor: zoom-in; background: var(--bone-2); aspect-ratio: 1; }
/* These are site photographs of brightly coloured plastic equipment shot in
   full sun — several come out close to clipping, and a wall of them reads as
   loud rather than as work. A small pull on saturation calms the grid without
   draining it; the client asked for the lead tile toned down and this is
   gentler than dropping the photograph. */
.mtile img {
  width: 100%; height: 100%;
  object-fit: cover;
  filter: saturate(.88) contrast(1.02);
  transition: transform 1s var(--ease2), filter .5s;
}
.mtile:hover img { filter: saturate(.98) contrast(1.02); }
.mtile:hover img { transform: scale(1.07); }
.mtile--wide { grid-column: span 2; aspect-ratio: 2/1; }
.mtile--tall { grid-row: span 2; aspect-ratio: 1/2; }

/* Every mosaic tile carries a label from the client's document, on the homepage
   and on the four sector pages alike, so the caption scrim lives here rather
   than in sectors.css. */
.mosaic figcaption {
  position: absolute;
  inset: auto 0 0 0;
  padding: 2.4rem 1.1rem .95rem;
  font-size: .74rem;
  font-weight: 600;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--bone);
  background: linear-gradient(transparent, rgba(7, 11, 9, .82));
  pointer-events: none;
}

@media (max-width: 900px) {
  .mosaic { grid-template-columns: repeat(2, 1fr); }
  .mtile--wide { grid-column: span 2; }
  .mtile--tall { grid-row: span 1; aspect-ratio: 1; }
}

/* lightbox */
.lb {
  position: fixed; inset: 0; z-index: 200;
  display: grid; place-items: center;
  padding: 4vmin;
  background: rgba(7,11,9,.94);
  opacity: 0; visibility: hidden;
  transition: opacity .4s, visibility .4s;
}
.lb.open { opacity: 1; visibility: visible; }
.lb img { max-width: 100%; max-height: 88vh; }
.lb-x {
  position: absolute; top: 1.5rem; right: 1.5rem;
  font-size: .72rem; letter-spacing: .2em; text-transform: uppercase;
  color: var(--bone); padding: .6rem .9rem;
  box-shadow: inset 0 0 0 1px rgba(243,240,231,.4);
}
.lb-x:hover { background: var(--bone); color: var(--ink); }

/* ============ BIG QUOTE OVER IMAGE ============ */
.testi {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  padding-block: clamp(4rem, 3rem + 6vw, 9rem);
  color: var(--bone);
}
.testi img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: -2; }
.testi::after { content: ''; position: absolute; inset: 0; z-index: -1; background: linear-gradient(90deg, rgba(7,11,9,.92) 20%, rgba(16,38,28,.72)); }

.quote-wrap { max-width: 60ch; display: grid; gap: 2rem; }
.quote-wrap blockquote {
  font-family: 'Bricolage', sans-serif;
  font-size: clamp(1.5rem, 1.1rem + 1.8vw, 2.6rem);
  font-weight: 500;
  line-height: 1.22;
  letter-spacing: -.03em;
  color: var(--bone);
}
.quote-wrap blockquote em { font-family: 'Editorial', Georgia, serif; font-style: italic; color: var(--clay); }
.quote-meta { display: flex; align-items: center; gap: 1rem; font-size: .78rem; letter-spacing: .14em; text-transform: uppercase; color: var(--sage); }
.quote-meta b { color: var(--bone); font-weight: 600; }

.q-nav { display: flex; gap: .6rem; margin-top: .5rem; }
.q-dot {
  width: 34px; height: 2px; background: rgba(243,240,231,.25);
  transition: background .3s;
}
.q-dot[aria-selected="true"] { background: var(--clay); }

/* ============ FAQ ============ */
.faq { background: var(--bone-2); }
.faq-grid {
  display: grid;
  grid-template-columns: .85fr 1.15fr;
  gap: clamp(2rem, 4vw, 5rem);
  align-items: start;
}
.faq-intro { display: grid; gap: 1.4rem; justify-items: start; position: sticky; top: 3rem; }
.faq-intro .lede { max-width: 34ch; }

.faq-list { display: grid; }

.faq-item {
  border-top: 1px solid rgba(7,11,9,.14);
}
.faq-item:last-child { border-bottom: 1px solid rgba(7,11,9,.14); }

.faq-item summary {
  display: grid;
  grid-template-columns: auto 1fr auto;
  gap: 1rem;
  align-items: baseline;
  padding: 1.35rem .25rem;
  cursor: pointer;
  list-style: none;
  transition: color .25s;
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary:hover { color: var(--clay-2); }

.faq-q {
  font-family: 'Bricolage', sans-serif;
  font-size: clamp(1.02rem, .95rem + .35vw, 1.25rem);
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: -.02em;
}

/* + / − built from two bars so it needs no icon */
.faq-sign {
  position: relative;
  width: 15px; height: 15px;
  flex: none;
  align-self: center;
}
.faq-sign::before, .faq-sign::after {
  content: '';
  position: absolute;
  inset: 50% 0 auto 0;
  height: 1.5px;
  background: var(--clay);
  transition: transform .3s var(--ease), opacity .3s;
}
.faq-sign::after { transform: rotate(90deg); }
.faq-item[open] .faq-sign::after { transform: rotate(0deg); opacity: 0; }

.faq-a {
  padding: 0 2.6rem 1.5rem 2.6rem;
  max-width: 68ch;
}
.faq-a p { color: var(--fg-muted); font-size: 1rem; line-height: 1.72; }

@media (max-width: 900px) {
  .faq-grid { grid-template-columns: 1fr; }
  .faq-intro { position: static; }
  .faq-a { padding-inline: 0 0; }
}

/* ============ CTA ============ */
.cta {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  min-height: 68svh;
  /* min-height alone is not padding. Once the heading, the paragraph and two
     buttons are taller than 68svh — which they are on any short window — the
     content ran edge to edge with the eyebrow touching the top border and the
     WhatsApp button touching the bottom. Matches the rhythm of .figures. */
  padding-block: clamp(3.5rem, 2.5rem + 4.5vw, 7rem);
  display: grid;
  align-items: center;
  color: var(--bone);
}
.cta img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: -2; }
.cta::after { content: ''; position: absolute; inset: 0; z-index: -1; background: rgba(7,11,9,.72); }
/* These three lines used to live in sectors.css, which the homepage does not
   load. Without them .cta-in fell back to max-width:24ch measured in its own
   17px font — a 236px column — so the closing heading broke into six lines and
   pushed the band to 985px tall. The photograph behind it is cut for a 2.2:1
   slot, so it was then being scaled up to fill a 1.37:1 box. */
.cta-in { display: grid; gap: 1.8rem; max-width: 52ch; }
.cta-in h2 { color: var(--bone); font-size: clamp(1.9rem, 1.2rem + 2.4vw, 3.3rem); max-width: 20ch; }
.cta-in p { color: rgba(243,240,231,.8); max-width: 52ch; }
.cta .label--light { margin-bottom: -.4rem; }
.cta-actions { display: flex; flex-wrap: wrap; gap: .8rem; }

/* ============ FOOTER ============ */
.foot { background: var(--ink); color: var(--sage); padding-top: clamp(3rem, 2rem + 4vw, 5.5rem); }
.foot-top {
  display: grid;
  grid-template-columns: 1.5fr 1fr 1.1fr .9fr 1.2fr;
  gap: clamp(1.6rem, 2.4vw, 3.2rem);
  padding-bottom: clamp(3rem, 2rem + 3vw, 4.5rem);
}
.foot h4 { font-size: .7rem; font-weight: 600; letter-spacing: .22em; text-transform: uppercase; color: var(--clay); margin-bottom: 1.3rem; }
.foot a { transition: color .25s; }
.foot a:hover { color: var(--bone); }
.foot-links { display: grid; gap: .8rem; font-size: .98rem; }
.foot-mark { display: grid; gap: 1.2rem; }
.foot-mark img { width: 54px; }
.foot-mark p { font-size: .97rem; line-height: 1.7; max-width: 34ch; }
.foot-contact { display: grid; gap: 1rem; font-size: .98rem; }
.foot-contact li { display: grid; grid-template-columns: auto 1fr; gap: .8rem; align-items: start; }
.foot-contact .icn { color: var(--clay); margin-top: .3rem; }

.foot-bar {
  border-top: 1px solid rgba(243,240,231,.12);
  padding-block: 1.5rem;
  display: flex;
  flex-wrap: wrap;
  gap: .8rem 2rem;
  justify-content: space-between;
  font-size: .8rem;
  letter-spacing: .04em;
}

@media (max-width: 1100px) { .foot-top { grid-template-columns: 1fr 1fr 1fr; } }
@media (max-width: 900px)  { .foot-top { grid-template-columns: 1fr 1fr; } }
@media (max-width: 560px)  { .foot-top { grid-template-columns: 1fr; } }

/* ============ FLOATING WHATSAPP ============ */
.wa {
  position: fixed;
  right: clamp(1rem, 2vw, 1.8rem);
  bottom: clamp(1rem, 2vw, 1.8rem);
  z-index: 50;
  width: 54px; height: 54px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: #1E4632;
  color: var(--bone);
  box-shadow: 0 10px 30px rgba(7,11,9,.35);
  transition: transform .3s var(--ease), background .3s;
}
.wa:hover { transform: scale(1.08); background: var(--clay); }
.wa .icn { width: 26px; height: 26px; }

/* ============ SCROLL REVEAL ============
   The .has-reveal class is set by app.js only after IntersectionObserver is
   confirmed and reduced-motion is confirmed off, so these rules never apply
   unless something is actually going to remove them again. */
.has-reveal .reveal {
  opacity: 0;
  transform: translateY(18px);
  transition:
    opacity .7s var(--ease2) var(--reveal-delay, 0ms),
    transform .7s var(--ease2) var(--reveal-delay, 0ms);
}
.has-reveal .reveal.is-in { opacity: 1; transform: none; }

/* A reveal must never clip a sticky or absolutely-positioned child, and it must
   not fight the hero, which has its own entrance. */
.has-reveal .hero .reveal { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .has-reveal .reveal { opacity: 1; transform: none; transition: none; }
}

/* ============ CLIENT LOGO WALL ============
   The source logos are 114x64 — the only unwatermarked copies that exist — so
   they are shown at roughly native size rather than scaled up. Greyscale at
   rest keeps two dozen competing brand palettes from shouting over the page and
   hides what softness is left; colour returns on hover. */
.sec--tight { padding-block: clamp(3rem, 5vw, 5rem); }
.sec-head--center { text-align: center; justify-items: center; margin-inline: auto; }
.sec-head--center .label::before { display: none; }

.logowall {
  overflow: hidden;
  margin-top: clamp(1.6rem, 3vw, 2.6rem);
  /* fade the ends so logos enter and leave instead of being chopped */
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}
.logowall-track {
  gap: clamp(2.4rem, 4vw, 4.5rem);
  animation-duration: 64s;
}
.logowall:hover .logowall-track { animation-play-state: paused; }

.logo {
  flex: none;
  display: grid;
  place-items: center;
  /* The source files are 114x64. Painting them any larger than this is pure
     upscaling; at 96px they are close to 1:1 even on a retina screen, and a
     logo wall reads better small anyway. */
  width: 96px;
  height: 54px;
}
.logo img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  filter: grayscale(1);
  opacity: .55;
  mix-blend-mode: multiply;      /* drops the white plate on the bone background */
  transition: filter .4s var(--ease), opacity .4s var(--ease);
}
.logo:hover img { filter: none; opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .logowall-track { animation: none; }
  /* With the animation off the duplicated half would simply run off the edge,
     so wrap instead and show the set as a static grid. */
  .logowall { -webkit-mask-image: none; mask-image: none; }
  .logowall-track { flex-wrap: wrap; width: auto; justify-content: center; row-gap: 1.5rem; }
}

/* ============ CERTIFICATION MARKS ============ */
.certs {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(1.4rem, 3vw, 3rem);
  margin-top: clamp(2rem, 4vw, 3.4rem);
  padding-top: clamp(1.8rem, 3vw, 2.6rem);
  border-top: 1px solid rgba(243, 240, 231, .16);
}
.cert {
  display: flex;
  align-items: center;
  gap: .9rem;
  flex: 1 1 210px;
}
/* Six marks at a 210px basis lay out 5 + 1, stranding "Make in India" on a row
   of its own. A third-width basis balances them 3 + 3. */
@media (min-width: 900px) { .certs > .cert { flex-basis: 28%; } }
.cert img {
  width: 54px;
  height: 54px;
  object-fit: contain;
  background: var(--bone);
  border-radius: 10px;
  padding: 5px;
  flex: none;
}
.cert figcaption { display: grid; gap: .15rem; }
.cert b {
  font-family: 'Bricolage', sans-serif;
  font-size: .98rem;
  font-weight: 600;
  letter-spacing: -.01em;
  color: var(--bone);
}
.cert span { font-size: .82rem; line-height: 1.4; color: rgba(243, 240, 231, .62); }

/* On a light section the marks need the opposite treatment. */
.certs--light { border-top-color: var(--bone-3); }
.certs--light .cert b { color: var(--ink); }
.certs--light .cert span { color: var(--fg-muted); }
.certs--light .cert img { background: var(--bone-2); }

/* ============ PROCESS STEPS ============ */
.steps {
  display: flex;
  flex-wrap: wrap;
  gap: 1px;
  margin-top: clamp(2rem, 4vw, 3.2rem);
  background: var(--bone-3);
  border: 1px solid var(--bone-3);
  list-style: none;
  counter-reset: none;
}
.step {
  flex: 1 1 240px;
  background: var(--bone);
  padding: clamp(1.6rem, 2.4vw, 2.4rem);
  display: grid;
  align-content: start;
  gap: .6rem;
  transition: background .4s var(--ease);
}
.step:hover { background: var(--bone-2); }
.step-n {
  font-family: 'Editorial', Georgia, serif;
  font-style: italic;
  font-size: 1.1rem;
  color: var(--clay);
}
.step h3 { font-size: 1.16rem; letter-spacing: -.02em; }
.step p { font-size: .96rem; line-height: 1.65; color: var(--fg-muted); }

/* ============ STICKY ENQUIRY BAR ============
   Off-screen rather than display:none so it can slide, and pointer-events are
   dropped while it is down so it cannot swallow clicks on the page behind it. */
.quickbar {
  position: fixed;
  inset: auto 0 0 0;
  z-index: 60;
  padding: .75rem var(--gut);
  background: rgba(9, 15, 12, .96);
  border-top: 1px solid rgba(243, 240, 231, .14);
  color: var(--bone);
  transform: translateY(110%);
  pointer-events: none;
  transition: transform .5s var(--ease2);
}
.quickbar.is-up { transform: none; pointer-events: auto; }
/* ...but never over the open menu. */
.menu-open .quickbar { transform: translateY(110%); pointer-events: none; }

.quickbar-in {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: .8rem 1.6rem;
  max-width: var(--max);
  margin-inline: auto;
}
.quickbar p { display: grid; gap: .1rem; margin: 0; }
.quickbar b {
  font-family: 'Bricolage', sans-serif;
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: -.01em;
}
.quickbar p span { font-size: .86rem; color: rgba(243, 240, 231, .68); }
.quickbar-acts { display: flex; flex-wrap: wrap; gap: .55rem; }
.quickbar .btn--line { color: var(--bone); border-color: rgba(243, 240, 231, .3); }

.btn--sm { padding: .62rem 1.05rem; font-size: .76rem; }

@media (max-width: 860px) {
  /* The supporting line and the phone button are redundant next to the two
     primary actions once the bar is a single row on a phone. */
  .quickbar p span, .quickbar-tel { display: none; }
  .quickbar-in { justify-content: center; }
}
@media (max-width: 560px) {
  /* On a phone the heading pushed the buttons onto two more rows and the bar
     covered a fifth of the screen. Keep only the two actions, side by side. */
  .quickbar { padding-block: .6rem calc(.6rem + env(safe-area-inset-bottom, 0px)); background: rgb(9, 15, 12); }
  .quickbar p { display: none; }
  .quickbar-acts { flex-wrap: nowrap; width: 100%; }
  .quickbar-acts .btn { flex: 1 1 0; justify-content: center; gap: .45rem; padding-inline: .5rem; letter-spacing: .08em; white-space: nowrap; }
}

/* The floating WhatsApp button sits above the bar while the bar is up, rather
   than underneath it. --qb-h is the bar's height, set by app.js. */
.wa { transition: transform .3s var(--ease), background .3s, bottom .5s var(--ease2); }
.qb-up .wa { bottom: calc(var(--qb-h, 0px) + .75rem); }
@media (max-width: 560px) {
  /* keep the last footer line clear of the floating button */
  .foot-bar { padding-bottom: calc(1.5rem + 70px); }
}
@media (prefers-reduced-motion: reduce) { .quickbar { transition: none; } }

/* ============ CARD HOVER ============
   The grids were entirely static; a small lift and a border shift is enough to
   say "this is a link" without turning the page into a toybox. */
.scard, .ucase, .bcard {
  transition: transform .45s var(--ease2), box-shadow .45s var(--ease2);
}
.scard:hover, .bcard:hover {
  transform: translateY(-4px);
  box-shadow: 0 18px 44px rgba(7, 11, 9, .12);
}
.ptile { transition: box-shadow .45s var(--ease2); }
.ptile:hover { box-shadow: 0 14px 34px rgba(7, 11, 9, .12); }

@media (prefers-reduced-motion: reduce) {
  .scard, .ucase, .bcard, .ptile { transition: none; }
  .scard:hover, .bcard:hover { transform: none; }
}

/* Section that directly follows the pinned rail. The rail releases with the
   foot of its sticky stage still on screen, so the normal section padding lands
   on top of space that already reads as empty. */
.sec--tight-top { padding-top: clamp(2rem, 3vw, 3.5rem); }

/* ============ WHY IT LASTS (homepage section 6) ============ */
/* The client's note on this section: "too much empty space at the top right …
   shift the six points slightly upward." The heading keeps its own line and
   loses two thirds of its bottom margin, which is what closes the gap — the
   two-column head used elsewhere is what opened it. */
.sec-head--tight { max-width: none; margin-bottom: clamp(1.4rem, .9rem + 1.4vw, 2.2rem); }
/* At display size this heading broke into four short lines inside the 62ch box,
   which is what pushed the six points down and left the top right of the
   section empty. One step smaller and a wider measure puts it on two lines. */
.sec-head--tight h2 {
  font-size: clamp(1.9rem, 1.1rem + 2.8vw, 3.6rem);
  max-width: 28ch;
}

.wgrid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: clamp(1.5rem, 2.5vw, 3rem);
}

/* ============ BARE PRODUCT CARD ============ */
/* The document's WHAT WE MANUFACTURE list is six names and nothing else, so
   these cards have no description to reveal on hover. Without the paragraph the
   panel is short enough to sit on its own baseline. */
.pcard--bare .pcard-body { gap: .4rem; }
