/* ===== Home ===== */

/* ============================================================
   HERO — nine real installation photographs, rotating one by one.
   Rebuilt 2026-08-07 to Anurag's own layout: centred company name, catalogue
   tagline, two-line introduction, the three office countries.

   Contracts this block keeps:
   - FAIL-OPEN. Slide 1 is authored in index.html with class .is-on; the other
     eight exist only after js/rotate.js runs. Script dead => one photo, all
     text, correct layout. Nothing here may hide authored content.
   - M2. The crossfade is opacity only. No transform, filter or shadow is
     animated anywhere in this hero.
   - CONTRAST. The photographs include BRIGHT frames (white sheds, lit floors),
     so the scrim is load-bearing: white copy needs the radial centre at .62
     over the worst (brightest) slide to clear AA at display sizes. Do not
     thin it by eye on a dark slide.
   - The stage welds into the flat #0d3568 plinth below, same as the old hero.
   ============================================================ */
.home-hero {
  position: relative;
  color: #e7eefb;
  background: #0d3568;
  padding: 0;
}

.home-hero__stage {
  position: relative;
  isolation: isolate;
  overflow: hidden;              /* Safari < 16 */
  overflow: clip;
  display: grid;
  align-content: center;
  justify-items: center;
  min-height: clamp(36rem, 92vh, 56rem);
  min-height: clamp(36rem, 92svh, 56rem);
  padding: clamp(4.5rem, 4rem + 2vw, 6.5rem) 0 clamp(3rem, 2.5rem + 2vw, 4.5rem);
}

/* ---- layer 0: the photographs ---- */
.home-hero__media { position: absolute; inset: 0; z-index: 0; overflow: hidden; overflow: clip; }
/* Gradient plate under the images: the hero is never flat black while a slide
   loads, and the first paint reads as room light, not as a broken image. */
.home-hero__media::before {
  content: "";
  position: absolute; inset: 0;
  background:
    radial-gradient(80% 70% at 50% 42%, #1657b0 0%, #12468c 38%, transparent 72%),
    linear-gradient(115deg, #0a2a52 0%, #0d3568 46%, #14509f 100%);
}
/* Every slide fills the stage; only .is-on is visible. The 1.15s opacity
   transition IS the rotator's whole animation (M2: opacity only). */
.home-hero__photo {
  position: absolute; inset: 0;
  width: 100%; height: 100%; display: block;
  object-fit: cover;
  object-position: 50% 42%;
  opacity: 0;
  transition: opacity 1.15s var(--ease);
}
.home-hero__photo.is-on { opacity: 1; }

/* ---- layer 1: the scrim (static, no animation, no blur) ----
   Radial centre carries the headline zone; the bottom ramp ends ON #0d3568 so
   the photograph welds into the plinth with no seam. */
.home-hero__scrim {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background:
    radial-gradient(95% 82% at 50% 44%, rgba(5,15,33,.62) 0%, rgba(5,15,33,.42) 58%, rgba(5,15,33,.24) 100%),
    linear-gradient(to top, #0d3568 0%, rgba(8,27,58,.72) 9%, rgba(8,27,58,0) 30%);
}

/* ---- layer 2: the copy, centred ---- */
.home-hero .wrap { position: relative; z-index: 2; }
.home-hero__center {
  display: grid; justify-items: center; text-align: center;
  max-width: 62rem;
}
.home-hero__brand {
  margin: 0;
  color: #fff;
  font-size: clamp(2.5rem, 1.2rem + 5.2vw, 5rem);
  line-height: 1.02;
  letter-spacing: .015em;
  text-wrap: balance;
  text-shadow: 0 2px 28px rgba(5,15,33,.6);
}
.home-hero__tag {
  margin: .9rem 0 0;
  color: #c9e0ff;
  font-size: clamp(.95rem, .8rem + .7vw, 1.3rem);
  font-weight: 700;
  font-style: italic;
  letter-spacing: .1em;
  text-transform: uppercase;
  text-shadow: 0 1px 18px rgba(5,15,33,.6);
}
.home-hero__intro {
  margin: 1.3rem 0 0;
  max-width: 62ch;
  color: #dce7f7;
  font-size: clamp(1rem, .95rem + .3vw, 1.16rem);
  line-height: 1.6;
  text-shadow: 0 1px 16px rgba(5,15,33,.55);
}
.home-hero__intro b { color: #fff; font-weight: 700; }
.home-hero__cta { display: flex; gap: .8rem; flex-wrap: wrap; justify-content: center; margin-top: 2rem; }
/* three buttons: the two browse routes sit together, enquiry leads */
.home-hero__cta .btn { flex: 0 1 auto; }

/* On a photograph the outline button has no ground of its own — it gets one. */
.home-hero .btn--outline-light { background: rgba(8,27,58,.62); border-color: rgba(255,255,255,.55); }
.home-hero .btn--outline-light:hover { background: rgba(8,27,58,.86); border-color: #fff; }
.home-hero .btn--light:hover { box-shadow: none; }

/* Solid header on the homepage: a blurred bar composited over a crossfading
   photograph every 5s is the most expensive thing the page could do. */
body[data-page="home"] .site-header { background: #fff; backdrop-filter: none; }

/* M3 — focus on a photograph takes the white ring. */
.home-hero a:focus-visible,
.home-hero button:focus-visible {
  outline: 3px solid #fff;
  outline-offset: 3px;
  border-radius: var(--r);
}
.home-hero .btn--light:focus-visible { outline-offset: 4px; }

/* Entrance — CSS at parse time, runs with JS disabled, cannot hide anything
   permanently because every animation ENDS at the authored state. */
@keyframes hero-in { from { opacity: 0; transform: translate3d(0, 12px, 0); } to { opacity: 1; transform: none; } }
.home-hero__center > * { animation: hero-in .55s var(--ease) both; }
.home-hero__center > .home-hero__tag       { animation-delay: .08s; }
.home-hero__center > .home-hero__intro     { animation-delay: .16s; }
.home-hero__center > .home-hero__cta       { animation-delay: .32s; }

/* M4 — kill switches. .lite (set by js/layout.js) and reduced motion both pin
   the finished frame: first photo on, all copy in place, no crossfade.
   js/rotate.js checks the same gates and never starts. */
.lite .home-hero__center > * { animation: none !important; }
.lite .home-hero__photo { transition: none; }
@media (prefers-reduced-motion: reduce) {
  .home-hero__center > * { animation: none !important; transform: none !important; opacity: 1 !important; }
  .home-hero__photo { transition: none; }
}

/* ============================================================
   RESPONSIVE — 1050 / 640, the file's existing ladder.
   ============================================================ */
@media (max-width: 1050px) {
  .home-hero__stage {
    min-height: clamp(32rem, 90vh, 48rem);
    min-height: clamp(32rem, 90svh, 48rem);
  }
}
@media (max-width: 640px) {
  .home-hero__stage { padding-top: 3.4rem; }
  /* Fits the 12-char MECHATRONICS on a 320px phone with the page padding on:
     .9rem + 4.6vw => 29px at 320, 32px at 388, capped 2.2rem. The name wraps
     to two balanced lines rather than clipping in the stage. */
  .home-hero__brand { font-size: clamp(1.8rem, .9rem + 4.6vw, 2.2rem); }
  .home-hero__tag { letter-spacing: .06em; }
  .home-hero__intro { font-size: 1rem; max-width: 34ch; }
  .home-hero__cta { margin-top: 1.6rem; width: 100%; }
  .home-hero__cta .btn { width: 100%; justify-content: center; padding: .95rem 1.4rem; font-size: 1rem; }
}


/* Project brief */
.project-brief { scroll-margin-top:90px; color:#dfe9f7; background:linear-gradient(160deg,var(--blue),var(--navy-2)); }
.project-brief__grid { display:grid; grid-template-columns:.8fr 1.2fr; gap:clamp(2.5rem,6vw,6rem); align-items:start; }
.project-brief h2 { color:#fff; max-width:15ch; }
.project-brief__intro > p { color:#c8d6e9; max-width:52ch; }
.project-brief__promise { display:grid; grid-template-columns:auto 1fr; gap:.7rem 1rem; margin-top:2rem; }
.project-brief__promise span { color:#6aaaf1; font-family:var(--font-mono); font-size:.76rem; }
.project-brief__promise b { color:#edf4fc; font-size:.88rem; }
.project-form {
  display:grid;
  grid-template-columns:1fr 1fr;
  gap:1rem;
  padding:clamp(1.4rem,3vw,2.2rem);
  border:1px solid rgba(255,255,255,.16);
  border-radius:var(--r-lg);
  background:rgba(255,255,255,.06);
  box-shadow:0 24px 64px rgba(1,8,22,.22);
}
.project-form label { display:grid; gap:.42rem; color:#f3f7fc; font-size:.82rem; font-weight:750; }
.project-form input,.project-form textarea {
  width:100%;
  min-height:48px;
  border:1px solid rgba(255,255,255,.22);
  border-radius:var(--r);
  background:#fff;
  color:var(--ink);
  font:inherit;
  padding:.78rem .85rem;
}
.project-form textarea { min-height:112px; resize:vertical; }
.project-form input::placeholder,.project-form textarea::placeholder { color:#718097; }
.project-form__wide,.project-form .btn,.project-form small { grid-column:1/-1; }
/* the two send routes share a row and wrap together on narrow screens */
.project-brief__wa { margin-top:2rem; display:grid; gap:.5rem; justify-items:start; }
.project-brief__wa small { color:#c8d6e9; font-size:.8rem; }
@media (max-width:620px){
  .project-brief__wa { justify-items:stretch; }
  .project-brief__wa .btn { width:100%; justify-content:center; }
}
.project-form .btn { justify-content:center; width:fit-content; min-width:210px; }
.project-form small { color:#c9d5e7; }

/* Section heads and actions */
.sec-head { max-width:60ch; margin-bottom:2rem; }
.sec-head.center { margin-inline:auto; text-align:center; }

/* Industries */
.industry-grid { display:grid; grid-template-columns:repeat(4,1fr); gap:1px; background:var(--line); border:1px solid var(--line); border-radius:var(--r-lg); overflow:hidden; }
.industry-grid a {
  min-height:96px;
  padding:1.25rem;
  background:#fff;
  color:var(--ink);
  font-weight:750;
  display:flex;
  align-items:flex-end;
  transition:color .18s var(--ease),background .18s var(--ease);
}
.industry-grid a:hover { color:var(--blue); background:var(--blue-soft); }


@media (max-width:1050px) {
  .industry-grid { grid-template-columns:1fr 1fr; }
}
@media (max-width:860px) {
  .project-brief__grid { grid-template-columns:1fr; }
}
@media (max-width:640px) {
  .industry-grid { grid-template-columns:1fr; }
  .project-form { grid-template-columns:1fr; }
  .project-form label,.project-form__wide,.project-form .btn,.project-form small { grid-column:1; }
  .project-form .btn { width:100%; }
  .industry-grid a { min-height:76px; }
}

/* ============================================================
   CREDENTIALS STRIP — the catalogue cover's navy band, rebuilt for the web.
   Full-bleed royal blue, six cells: white icon medallion + number + label,
   thin dividers. 6 across -> 3x2 (1050) -> 2x3 (640), everything always
   visible (Anurag chose the grid over a swipe strip). The four numbers keep
   the js/count.js contract: <b data-count> is the finished state, nowrap,
   tabular figures, width locked by the script at arm time.
   ============================================================ */
.stat-band { padding: 0 0 1.4rem; }
.stat-strip {
  background: linear-gradient(180deg, #1657b0 0%, #0f3e7c 100%);
  border-block: 1px solid #12468c;
}
.stat-strip__grid {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(6, 1fr);
}
.stat-strip__grid li {
  display: flex; align-items: center; gap: .7rem;
  padding: 1.05rem .85rem;
  border-right: 1px solid rgba(255,255,255,.28);
}
.stat-strip__grid li:last-child { border-right: 0; }
.stat-strip__ic {
  flex: 0 0 auto; width: 50px; height: 50px; border-radius: 50%;
  background: #fff; color: #0f3e7c;
  display: grid; place-items: center;
}
.stat-strip__ic svg { width: 27px; height: 27px; }
.stat-strip__tx { display: grid; gap: .18rem; min-width: 0; }
.stat-strip__tx b {
  font-size: clamp(1.25rem, 1.7vw, 1.7rem);
  line-height: 1; font-weight: 700; color: #fff;
  letter-spacing: -.01em; white-space: nowrap;
  font-variant-numeric: tabular-nums lining-nums;
}
.stat-strip__tx b span { color: #fff; }
.stat-strip__tx b.stat-strip__word { font-size: clamp(.98rem, 1.25vw, 1.22rem); text-transform: uppercase; letter-spacing: .02em; }
.stat-strip__tx > span {
  /* .75rem = 12px — .66rem measured 10.6px, under the readable floor */
  font-size: .75rem; font-weight: 700; letter-spacing: .07em;
  text-transform: uppercase; color: #CFE0F7; line-height: 1.35;
}
/* The six-column strip switches to three at 1200px, not 1050px (fixed
   2026-08-22). `1fr` is `minmax(auto, 1fr)`, and that auto floor is min-content
   — a track can never shrink below the widest word plus the 50px icon, so when
   six tracks do not fit the grid does not compress, it OVERHANGS. Measured on
   this content: the six columns need 1099px, plus the 38px .wrap gutter = a
   1138px viewport. The old 1050px breakpoint therefore left 1051–1137px broken,
   which is exactly where the homepage scrolled sideways by ~17px at 1101px.
   1200px keeps ~60px of slack for wider font rendering on Windows/Linux, where
   the same text measures a few per cent longer than it does here. Re-measure if
   a stat's wording gets longer. */
@media (max-width: 1200px) {
  .stat-strip__grid { grid-template-columns: repeat(3, 1fr); }
  .stat-strip__grid li:nth-child(3n) { border-right: 0; }
  .stat-strip__grid li:nth-child(-n+3) { border-bottom: 1px solid rgba(255,255,255,.28); }
}
@media (max-width: 640px) {
  .stat-strip__grid { grid-template-columns: repeat(2, 1fr); }
  .stat-strip__grid li { border-right: 0; padding: .95rem .6rem; }
  .stat-strip__grid li:nth-child(odd) { border-right: 1px solid rgba(255,255,255,.28); }
  .stat-strip__grid li:nth-child(-n+4) { border-bottom: 1px solid rgba(255,255,255,.28); }
  .stat-strip__ic { width: 42px; height: 42px; }
  .stat-strip__ic svg { width: 23px; height: 23px; }
}
/* Phones: the icon moves ABOVE the text instead of beside it (added
   2026-08-22, same min-content bug as the six-column case above). Two columns
   on a 375px screen give each cell 168px, but the widest cell needs 186px —
   the icon (42) and its gap (11) eat the width the label "Equipment
   installations" needs, and `1fr` cannot shrink below that, so the strip
   overhung the viewport by 3px. Stacking returns those 53px to the text:
   the cell then needs ~115px, which still fits a 320px iPhone SE (141px per
   cell) with room to spare. Trimming padding instead was measured too and
   only just fitted 375px while still failing a 360px Android. */
@media (max-width: 430px) {
  .stat-strip__grid li { flex-direction: column; align-items: flex-start; gap: .5rem; }
}




/* ============================================================
   Customer testimonials — seamless ticker.
   Built on the lab's marquee pattern: the track wraps as a normal
   list until JS adds --live. So with JS off, a dead animation or a
   low tier, the reviews are still a readable set rather than a
   frozen strip. That fallback is the point of the pattern.
   ============================================================ */
.ts-sec { background: var(--mist); overflow: hidden; }
.ts-head { text-align: center; margin-bottom: 2.2rem; }
.ts-head h2 { margin: .3rem 0 0; }

.pk-marquee { position: relative; }
.pk-marquee__track {
  display: flex; flex-wrap: wrap; align-items: stretch;
  gap: var(--pk-marquee-gap, 1.1rem);
  margin: 0; padding: 0; list-style: none;
}
.pk-marquee__item { flex: 0 0 auto; }
/* --live is added by JS once the track is measured and running */
.pk-marquee--live { overflow: hidden; }
.pk-marquee--live .pk-marquee__track {
  flex-wrap: nowrap; width: max-content; will-change: transform;
}
.pk-marquee--live.pk-marquee--fade {
  mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
}
.pk-marquee--live :focus-visible { outline-offset: -3px; }
/* not live: a wrapped, padded list that reads correctly on its own */
.pk-marquee:not(.pk-marquee--live) .pk-marquee__track { row-gap: 1.1rem; padding-inline: var(--pad, 1.5rem); }

.ts-rail { --pk-marquee-gap: 1.1rem; }
.ts-card {
  width: 380px; background: #fff; border: 1px solid var(--line);
  border-radius: var(--r-lg); padding: 1.5rem 1.6rem 1.4rem;
  display: flex; flex-direction: column; gap: .9rem;
}
.ts-card__top { display: flex; align-items: center; gap: .85rem; }
.ts-card__face {
  width: 54px; height: 54px; flex: 0 0 54px; border-radius: 50%;
  object-fit: cover; background: var(--blue-soft);
}
.ts-card__who b { display: block; color: var(--ink); font-size: .96rem; line-height: 1.25; }
.ts-card__who span { display: block; color: var(--steel); font-size: .82rem; margin-top: .1rem; }
.ts-card__quote {
  margin: 0; color: var(--ink); font-size: .97rem; line-height: 1.62; flex: 1;
}
.ts-card__sector {
  margin: 0; padding-top: .85rem; border-top: 1px solid var(--line);
  color: var(--steel); font-size: .8rem;
}

@media (max-width: 620px) { .ts-card { width: 300px; } }
@media (prefers-reduced-motion: reduce) {
  /* honest degradation: stop the rail and let it wrap as a list */
  .pk-marquee--live { overflow: visible; }
  .pk-marquee--live .pk-marquee__track {
    flex-wrap: wrap; width: auto; will-change: auto; transform: none !important;
  }
  .pk-marquee--live.pk-marquee--fade { mask-image: none; }
  .pk-marquee--live [aria-hidden="true"] { display: none; }
}









/* ============================================================
   NETWORK — 5 / 9 / 3 / 14 on navy, then the three lists.
   ============================================================ */
.net-band .sec-head .eyebrow { color: #bdd7f6; }
.net-band h2, .net-band h3 { color: #fff; }
.net-counts {
  list-style: none; margin: 0 0 2.2rem; padding: 0;
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem;
  text-align: center;
}
.net-counts li { display: grid; gap: .3rem; justify-items: center; }
.net-counts b {
  font-size: clamp(2.2rem, 4vw, 3.2rem); line-height: 1; font-weight: 700; color: #fff;
  white-space: nowrap; font-variant-numeric: tabular-nums lining-nums;
}
.net-counts span { font-size: .84rem; color: #c8d6e9; }
/* ---- levels tree: root -> countries -> counted groups ---- */
.net-tree { margin-top: .6rem; }
.net-tree__root {
  width: fit-content; margin: 0 auto;
  padding: .7rem 1.6rem; border-radius: 99px;
  background: #fff; color: var(--navy);
  font-size: 1.02rem; letter-spacing: .04em;
  position: relative; z-index: 1;
}
.net-tree__root b { letter-spacing: .06em; }
/* stem below the root — its 28px must equal the branches' padding-top so it
   lands exactly ON the horizontal rule the branches draw. */
.net-tree__root::after {
  content: ""; position: absolute; left: 50%; top: 100%; margin-left: -1px;
  width: 2px; height: 28px; background: #606F88;
}
.net-tree__tier {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.6rem;
}
/* CONNECTORS, correct by construction — no guessed percentages.
   Each branch draws its own horizontal segment at its top edge:
     first  : from its own centre rightwards (plus the grid gap)
     middle : its full width plus both gaps
     last   : from the left edge (plus gap) to its own centre
   Joined, they form one rule that STARTS on stem 1 and ENDS on stem 3,
   whatever the column widths. Each branch then drops its stem from that rule
   to its country node (top 0 -> 28px = padding-top). The root's 28px stem
   lands at container-centre, which lies inside the middle segment. */
.net-tree__branch { position: relative; padding-top: 28px; }
.net-tree__branch::before {
  content: ""; position: absolute; top: 0; height: 2px;
  background: #606F88;
}
/* Segments ABUT, never overlap: each one bridges only the gap to its right,
   the next starts at its own left edge. Same solid colour everywhere, so even
   sub-pixel rounding cannot draw a brighter splice. */
.net-tree__branch:nth-child(1)::before { left: 50%; right: -1.6rem; }
.net-tree__branch:nth-child(2)::before { left: 0; right: -1.6rem; }
.net-tree__branch:nth-child(3)::before { left: 0; right: 50%; }
.net-tree__branch::after {
  content: ""; position: absolute; top: 0; left: 50%; margin-left: -1px;
  width: 2px; height: 28px; background: #606F88;
}
.net-tree__country {
  display: grid; gap: .1rem; justify-items: center; text-align: center;
  padding: .65rem 1rem; border-radius: var(--r-lg);
  border: 1px solid rgba(106,170,241,.6);
  background: rgba(8,27,58,.6);
  color: #fff; font-weight: 800; font-size: 1.15rem; letter-spacing: .03em;
}
.net-tree__country span { font-weight: 500; font-size: .72rem; letter-spacing: .06em; color: #bdd7f6; text-transform: uppercase; }
.net-tree__groups { display: grid; gap: .8rem; margin-top: .9rem; }
.net-tree__g {
  position: relative; padding: .85rem 1rem .9rem 1.15rem;
  border-left: 2px solid rgba(106,170,241,.55);
  background: rgba(255,255,255,.05);
  border-radius: 0 var(--r) var(--r) 0;
}
.net-tree__g h3 {
  margin: 0 0 .35rem; color: #fff; font-size: .86rem;
  letter-spacing: .05em; text-transform: uppercase;
}
.net-tree__g h3 b { color: #6aaaf1; font-size: 1.15rem; margin-right: .25rem; font-variant-numeric: tabular-nums; }
.net-tree__g p { margin: 0; color: #dfe9f7; font-size: .88rem; line-height: 1.6; }
.net-tree__cta { margin-top: 1.8rem; text-align: center; }
@media (max-width: 900px) {
  .net-counts { grid-template-columns: repeat(2, 1fr); gap: 1.4rem .8rem; }
  /* the tree stacks: connector geometry becomes a single left rail */
  .net-tree__tier { grid-template-columns: 1fr; gap: 1.2rem; }
  .net-tree__root::after { display: none; }
  .net-tree__branch { padding-top: 0; }
  .net-tree__branch::before, .net-tree__branch::after { display: none; }
  .net-tree__g p { font-size: .95rem; }
}

/* ============================================================
   RECENT PROJECTS — the same rail as Customers (client, 2026-08-16): same
   380px card, same white surface and border, same marquee. Only the contents
   differ (photograph + caption instead of quote), and the direction: this one
   runs left while the testimonials run right.
   ============================================================ */
.rp-sec { overflow: hidden; }
.rp-head { text-align: center; margin-bottom: 2.2rem; }
.rp-head h2 { margin: 0; }
.rp-rail { --pk-marquee-gap: 1.1rem; }
.rp-card {
  width: 380px; background: var(--white); border: 1px solid var(--line);
  border-radius: var(--r-lg); overflow: hidden;
  display: flex; flex-direction: column;
}
.rp-card figure { margin: 0; }
.rp-card img {
  display: block; width: 100%; aspect-ratio: 4/3; object-fit: cover;
  /* height:auto is load-bearing, not tidying. The markup carries
     height="720" for layout stability, and that attribute is a
     presentational hint the browser applies as a real height — which makes
     the box definite and aspect-ratio a no-op. Without this line the card
     rendered 809px tall against the Customers card's 267px. */
  height: auto;
}
.rp-card__tx { padding: 1.1rem 1.3rem 1.3rem; display: grid; gap: .4rem; }
.rp-card__cat {
  display: flex; align-items: center; gap: .45rem;
  font-size: .74rem; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--steel);
}
.rp-card__cat i { width: 7px; height: 7px; border-radius: 50%; background: var(--accent); }
.rp-card h3 { margin: 0; font-size: 1.12rem; line-height: 1.3; }
.rp-note { margin: 1.4rem 0 0; font-size: .8rem; color: var(--steel); text-align: center; }
@media (max-width: 620px) { .rp-card { width: 300px; } }

/* --- Recent Projects rails (the cards themselves) --- */
/* #recent-projects is a link target we hand to the client, and the header is
   sticky — without this the heading lands underneath it. Same treatment the
   other anchor targets on the site get. */
#recent-projects { scroll-margin-top: 90px; }
.rp-card { width: 297px; }
.rp-rail--b { margin-top: 1.1rem; }
.rp-card__hit {
  display: flex; flex-direction: column; width: 100%; padding: 0;
  border: 0; background: none; text-align: left; cursor: pointer; color: inherit;
  font: inherit; text-decoration: none;
}
.rp-card__hit img { transition: transform .4s var(--ease); }
.rp-card__hit:hover img { transform: scale(1.03); }
.rp-card__hit:focus-visible { outline: 3px solid var(--blue-bright); outline-offset: 2px; }
.rp-all { margin-top: 1.6rem; text-align: center; }
@media (max-width: 1100px) { .rp-card { width: 320px; } }
@media (max-width: 620px)  { .rp-card { width: 300px; } }

/* Caption under each Recent Projects rail photo — founder (2026-08-27),
   restyled to Yash's three choices (2026-08-28): two-line lockup, machine
   name bold navy, "Installed in <country>" in brand blue with a location pin.
   The min-height reserves the tallest case (a two-line machine name) so every
   card in the moving row keeps the same height and the rail's bottom edge
   stays straight — ragged card bottoms on a marquee read as broken. */
.rp-cap { display: grid; gap: .18rem; align-content: start;
  padding: .6rem .9rem .8rem; text-align: center; line-height: 1.3;
  /* 4.8rem, not 3.9: the reserve must cover a TWO-line machine name plus the
     place line. Windows renders this face ~5-8% wider than macOS, so names
     that fit one line on the Mac ("Roof-mounted dust collection system")
     wrapped to two on the founder's HP — the card grew and the rail bottoms
     went ragged, which is exactly the misalignment he filmed (2026-08-28).
     Uniform means reserving the worst case on every OS, not the Mac's case. */
  min-height: 4.8rem; }
.rp-cap__m { font-size: .84rem; font-weight: 700; color: var(--navy); }
.rp-cap__p { display: inline-flex; align-items: center; justify-content: center;
  gap: .32rem; font-size: .74rem; font-weight: 600; color: var(--blue); }
.rp-cap__p svg { width: .9em; height: .9em; flex: 0 0 auto; }

/* ============================================================
   SHORT LAPTOP SCREENS (founder's HP, 2026-08-28) — his 1366x768 panel at
   125% Windows zoom leaves a ~540-610px viewport, so the heading plus TWO
   full-size rails could never share his window; he only ever saw one row.
   Under 860px of height the cards compact so heading + both rails + button
   fit one screen; under 700px they compact further. Width-based rules stay
   untouched — a tall phone keeps full-size cards. Yash's Mac (900px+) is
   unaffected.
   ============================================================ */
/* min-width 900px guard: height alone would also catch phones (an iPhone SE is
   667px tall, many Androids ~800px) and silently shrink the approved mobile
   cards. Compact mode is for short LAPTOP windows only. */
@media (max-height: 860px) and (min-width: 900px) {
  #recent-projects.section { padding: 2.2rem 0; }
  #recent-projects .rp-head { margin-bottom: 1.1rem; }
  .rp-card { width: 224px; }
  .rp-cap { padding: .45rem .6rem .55rem; min-height: 4.1rem; }
  .rp-cap__m { font-size: .76rem; }
  .rp-cap__p { font-size: .68rem; }
  .rp-rail--b { margin-top: .8rem; }
  .rp-all { margin-top: 1rem; }
}
@media (max-height: 700px) and (min-width: 900px) {
  #recent-projects.section { padding: 1.5rem 0; }
  #recent-projects .rp-head { margin-bottom: .8rem; }
  #recent-projects .sec-head__sub { margin-top: .3rem; }
  .rp-card { width: 186px; }
  .rp-cap { min-height: 3.9rem; }
  .rp-all { margin-top: .8rem; }
}
