/* ============================================================
   Гложекс Консалтинг — light editorial
   ONE typeface (IBM Plex Sans), ONE accent (--accent), ONE vertical
   rhythm (--sp). Every colour flows through a token in :root; sections
   that sit on a photograph re-tokenise those same names to light values.
   ============================================================ */

:root {
  /* --bg-rgb is the single source of truth for the paper colour: the nav
     blur and the hero wash need it at partial alpha, so it must never be
     duplicated as a literal. */
  --bg-rgb: 247, 244, 238;
  --bg: rgb(var(--bg-rgb));
  --bg-2: #efeae1;
  --ink: #17140f;
  --ink-dim: #5f574c;
  --accent: #a4161a;        /* from the logo's red — 7.1:1 on --bg */
  --line: rgba(23, 20, 15, 0.12);
  --photo-bg: #1a1611;      /* ground under every full-bleed photo/video */

  --font-sans: "IBM Plex Sans", "Helvetica Neue", Arial, system-ui, sans-serif;

  --sp: 5rem;               /* vertical section rhythm */
  --gutter: 3rem;
  /* THE page edge for everything below the hero: sections, band captions and
     the footer all measure from this one line. The hero deliberately keeps its
     own, slightly tighter inset (5vw), so this sits a step to the right of it. */
  --edge: max(var(--gutter), 7vw);
  --maxw: 1280px;
}

/* Sections that sit on a photograph: light type over a dark scrim.
   Custom properties cascade, so every descendant rule that reads
   --ink / --ink-dim / --line / --accent resolves to these automatically. */
.cband, .project {
  --ink: #f7f2e8;
  --ink-dim: rgba(247, 242, 232, 0.76);
  --line: rgba(247, 242, 232, 0.22);
  --accent: #f0ece3;
  color: var(--ink);
}

::selection { background: var(--ink); color: var(--bg); }

* { margin: 0; padding: 0; box-sizing: border-box; }

/* Native scrolling. A JS smooth-scroll library used to sit on top of this and
   ease toward the wheel position, which on a trackpad (already carrying OS
   momentum) read as lag. The browser's own scroll runs on the compositor. */
html { scroll-behavior: smooth; }
/* anchor targets clear the fixed nav */
section[id], header[id], footer[id], .footer__inner[id] { scroll-margin-top: 72px; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-sans);
  font-weight: 400;
  line-height: 1.6;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
}

img { display: block; width: 100%; height: 100%; object-fit: cover; }
a { color: inherit; text-decoration: none; }
/* <em> marks the key phrase in a heading; same face, same weight */
em { font-style: normal; font-weight: inherit; color: inherit; }

/* reading progress */
.progress {
  position: fixed; top: 0; left: 0; height: 2px; width: 100%; z-index: 70;
  background: var(--accent); transform-origin: 0 50%; transform: scaleX(0);
}

/* ============ nav ============ */
.nav {
  position: fixed; top: 0; left: 0; right: 0; z-index: 50;
  display: flex; align-items: center; justify-content: space-between;
  padding: 1.1rem var(--gutter);
  transition: background 0.4s, padding 0.4s, border-color 0.4s;
  border-bottom: 1px solid transparent;
}
/* Opaque, not backdrop-filter: blurring the backdrop re-filters whatever is
   behind the bar on every scrolled frame, which is expensive over photos. */
.nav.is-scrolled {
  background: var(--bg);
  padding: 0.75rem var(--gutter); border-bottom-color: var(--line);
}
.nav__logo { display: flex; align-items: center; }
.nav__logo img { height: 32px; width: auto; display: block; }
.nav__links { display: flex; gap: 2rem; align-items: center; }
.nav__links a {
  font-size: 0.76rem; letter-spacing: 0.16em; text-transform: uppercase; color: var(--ink-dim);
  transition: color 0.3s;
}
.nav__links a:hover,
.nav__links a.is-active,
.nav__links a.is-current { color: var(--accent); }
.nav__item--drop { position: relative; }
.nav__drop {
  position: absolute; top: 100%; left: -1.2rem; padding-top: 1rem;
  opacity: 0; visibility: hidden; transform: translateY(8px); transition: 0.25s;
}
.nav__item--drop:hover .nav__drop, .nav__item--drop:focus-within .nav__drop {
  opacity: 1; visibility: visible; transform: translateY(0);
}
.nav__drop a {
  display: block; white-space: nowrap; padding: 0.6rem 1.4rem;
  background: #fff; border-left: 1px solid var(--line);
  border-right: 1px solid var(--line); text-transform: none; letter-spacing: 0.04em;
  font-size: 0.85rem;
}
.nav__drop a:first-child { border-top: 1px solid var(--line); border-radius: 6px 6px 0 0; padding-top: 0.85rem; }
.nav__drop a:last-child { border-bottom: 1px solid var(--line); border-radius: 0 0 6px 6px; padding-bottom: 0.85rem; }
.nav__drop a.is-current { color: var(--accent); }
.nav__cta {
  font-size: 0.76rem; letter-spacing: 0.14em; text-transform: uppercase;
  border: 1px solid rgba(23, 20, 15, 0.32); color: var(--ink);
  padding: 0.6em 1.4em; border-radius: 99px; white-space: nowrap;
  transition: background 0.3s, color 0.3s, border-color 0.3s;
}
.nav__cta:hover { background: var(--ink); border-color: var(--ink); color: var(--bg); }

/* ============ language switch ============
   A quiet pill beside the call to action. It is NOT hidden with .nav__links on
   small screens — a visitor on a phone must still be able to change language. */
.nav__lang {
  margin-left: 0.9rem; font-size: 0.7rem; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--ink-dim); border: 1px solid var(--line); border-radius: 99px; padding: 0.32em 0.75em;
  transition: color 0.3s, border-color 0.3s; flex-shrink: 0;
}
.nav__lang:hover { color: var(--accent); border-color: var(--accent); }
.mobilemenu a.mobilemenu__lang { color: var(--ink-dim); }

/* ============ mobile menu ============ */
.nav__burger {
  display: none; flex-direction: column; gap: 6px; padding: 10px 4px;
  background: none; border: none; cursor: pointer; z-index: 56;
}
.nav__burger span { width: 26px; height: 1.5px; background: var(--ink); transition: 0.3s; display: block; }
body.menu-open .nav__burger span:first-child { transform: translateY(7.5px) rotate(45deg); }
body.menu-open .nav__burger span:last-child { transform: translateY(-7.5px) rotate(-45deg); }
.mobilemenu {
  position: fixed; inset: 0; z-index: 48; background: var(--bg);
  display: flex; flex-direction: column; justify-content: center; gap: 0.1rem;
  padding: 4.5rem 2rem 2rem; opacity: 0; visibility: hidden;
  transition: opacity 0.35s, visibility 0.35s; overflow-y: auto;
}
body.menu-open .mobilemenu { opacity: 1; visibility: visible; }
/* the overlay covers the page; letting the page scroll behind it is disorienting */
body.menu-open { overflow: hidden; }
.mobilemenu a {
  font-size: 1.15rem; font-weight: 500; padding: 0.45rem 0;
  border-bottom: 1px solid var(--line); transition: color 0.3s, padding-left 0.3s;
}
.mobilemenu a:hover, .mobilemenu a.is-current { color: var(--accent); }
.mobilemenu a small {
  display: block; font-size: 0.64rem; letter-spacing: 0.25em; text-transform: uppercase;
  color: var(--ink-dim); margin-bottom: 0.05rem;
}

/* ============ photo hero ============ */
.hero {
  position: relative; height: 92vh; min-height: 560px; max-height: 900px;
  overflow: hidden; background: var(--bg);
}
.hero__bg { position: absolute; inset: 0; }
/* slides are stacked; js/main.js crossfades to the next one every 15 s */
.hero__slides { position: absolute; inset: 0; }
/* No filter and no transform here: this is a ~92vh image, and five of them are
   stacked. A filter forces a full repaint of that surface on every frame.

   object-position anchors the crop to the BOTTOM of the photograph. `cover`
   otherwise trims top and bottom equally, and on a short wide screen that ate
   the machinery, which is what these pictures are of. */
.hero__slide {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: 50% 100%;
  opacity: 0; transition: opacity 1.6s ease;
}
.hero__slide.is-active { opacity: 1; }
/* only the visible slide is a composited layer */
.hero__slide:not(.is-active) { will-change: auto; }
/* a restrained wash: enough behind the headline, a whisper at the top for
   the nav, and a short fade into the page colour at the bottom */
.hero__scrim {
  position: absolute; inset: 0;
  background:
    linear-gradient(100deg, rgba(var(--bg-rgb), 0.72) 0%, rgba(var(--bg-rgb), 0.48) 34%, rgba(var(--bg-rgb), 0.05) 62%, rgba(var(--bg-rgb), 0) 100%),
    linear-gradient(180deg, rgba(var(--bg-rgb), 0.34) 0%, rgba(var(--bg-rgb), 0) 18%),
    linear-gradient(0deg, rgba(var(--bg-rgb), 0.88) 0%, rgba(var(--bg-rgb), 0) 26%);
}
.hero__content {
  position: absolute; left: max(var(--gutter), 5vw); right: auto; top: 26vh;
  text-align: left; z-index: 5; max-width: 52rem; padding: 0 1.5rem 0 0;
}
/* --ink, never --accent: this sits on an arbitrary photograph behind only a
   partial wash, so it needs the highest-contrast ink on every slide */
.hero__eyebrow {
  font-size: 0.78rem; letter-spacing: 0.36em; text-transform: uppercase; color: var(--ink);
  margin-bottom: 1.6rem;
}
.hero__title {
  font-size: clamp(2.1rem, 4.6vw, 4rem);
  line-height: 1.07; font-weight: 600; letter-spacing: -0.02em;
}
.line { display: block; overflow: hidden; padding-bottom: 0.08em; margin-bottom: -0.08em; }
.line__inner { display: inline-block; }
.hero__sub { max-width: 30rem; margin: 1.6rem 0 0; color: var(--ink-dim); font-size: 1rem; }
.hero__title, .hero__sub, .hero__eyebrow { text-shadow: 0 0 24px rgba(var(--bg-rgb), 0.55); }
.hero__scrollcue {
  position: absolute; bottom: 5vh; left: 0; right: 0;
  z-index: 5; text-align: center; color: var(--ink-dim);
  font-size: 0.68rem; letter-spacing: 0.3em; text-transform: uppercase;
}
.hero__scrollcue span { display: inline-block; padding-left: 0.3em; }
.hero__scrollline { width: 1px; height: 48px; margin: 0.7rem auto 0; background: var(--line); position: relative; overflow: hidden; }
.hero__scrollline i { position: absolute; left: 0; top: -40%; width: 100%; height: 40%; background: var(--ink-dim); animation: scrolldrop 1.8s ease-in-out infinite; }
@keyframes scrolldrop { 0% { top: -40%; } 60%, 100% { top: 110%; } }

/* ============ marquee ============ */
/* a thin ticker, not a band — it should read as a rule between sections */
.marquee {
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
  padding: 0.5rem 0; overflow: hidden; white-space: nowrap; background: var(--bg-2);
}
/* js/main.js fills each half past the viewport and sets the duration, so the
   loop is seamless at any width or font size — it must never look like it ends. */
.marquee__inner { display: inline-flex; will-change: transform; animation: marq 34s linear infinite; }
/* Pause on hover ONLY where a real hover exists. On iOS a touch makes :hover
   stick until something else is tapped, so this rule, unscoped, froze the strip
   the moment a thumb brushed it while scrolling. */
@media (hover: hover) and (pointer: fine) {
  .marquee:hover .marquee__inner { animation-play-state: paused; }
}
.marquee__inner span {
  font-size: 0.66rem; letter-spacing: 0.22em; text-transform: uppercase; color: var(--ink-dim);
}
@keyframes marq { from { transform: translateX(0); } to { transform: translateX(-50%); } }

/* ============ shared section bits ============ */
section { position: relative; }
/* The section label ("03 — Алсын хараа") is the ONE place red is used as
   standing text. Everywhere else red is reserved for interactive states and
   the current timeline year. */
.section-eyebrow {
  font-size: 0.72rem; letter-spacing: 0.3em; text-transform: uppercase; color: var(--accent);
  margin-bottom: 1rem; font-weight: 500;
}
/* One colour, and no width cap: the 22ch cap was what broke every heading onto
   a second line. Headings now run on one line wherever they fit. The full-bleed
   band captions keep their own cap, which is the wrap that works there. */
.section-title {
  font-size: clamp(1.45rem, 2.2vw, 1.95rem); line-height: 1.18; font-weight: 600;
  letter-spacing: -0.015em; margin-bottom: 1.4rem;
}
.section-title em { color: inherit; }
.cband .section-title em, .cband__title em { color: inherit; opacity: 0.82; }

/* Revealed by one IntersectionObserver in js/main.js (see .is-in). The
   transition runs on the compositor — there is no per-scroll JS behind it. */
[data-reveal] {
  opacity: 0; transform: translateY(24px);
  transition: opacity 0.7s ease-out, transform 0.7s ease-out;
}
[data-reveal].is-in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  [data-reveal] { opacity: 1; transform: none; transition: none; }
}

/* ============ stats ============ */
.stats { padding: var(--sp) var(--edge); }
.stats__grid {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.5rem;
}
.stat { border-left: 1px solid var(--line); padding-left: 1.4rem; }
.stat__num {
  display: block; font-size: clamp(2.2rem, 4vw, 3.6rem); font-weight: 600; color: var(--ink);
  line-height: 1; letter-spacing: -0.02em;
}
.stat__label {
  display: block; margin-top: 0.7rem; font-size: 0.75rem; letter-spacing: 0.18em;
  text-transform: uppercase; color: var(--ink-dim);
}

/* ============ story ============ */
.story {
  display: grid; grid-template-columns: 1fr 1fr; gap: 3.5rem; align-items: center;
  padding: var(--sp) var(--edge);
}
.story__text p:not(.section-eyebrow) { color: var(--ink-dim); margin-bottom: 1.1rem; max-width: 34rem; font-size: 0.95rem; }
.link-arrow {
  display: inline-block; margin-top: 0.6rem; color: var(--ink);
  font-size: 0.8rem; letter-spacing: 0.18em; text-transform: uppercase;
  border-bottom: 1px solid var(--line); padding-bottom: 0.35em;
  transition: border-color 0.3s, color 0.3s;
}
.link-arrow:hover { border-color: var(--accent); color: var(--accent); }
.link-arrow span { display: inline-block; transition: transform 0.3s; }
.link-arrow:hover span { transform: translateX(8px); }
/* quarry.jpg is 640x893 — use its own ratio so nothing is cut off. The photo
   used to be shifted by a scroll parallax, which slid it up and left a band of
   this background showing underneath; there is no transform on it now. */
.story__frame { aspect-ratio: 640 / 893; overflow: hidden; border-radius: 6px; background: var(--bg-2); }
.story__caption {
  margin-top: 0.8rem; font-size: 0.72rem; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--ink-dim);
}

/* ============ awards ============ */
.awards { padding: var(--sp) var(--edge); }
.awards__grid {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 1px;
  background: var(--line); border: 1px solid var(--line); margin-top: 1.5rem;
}
.award { background: var(--bg); padding: 1.7rem 1.5rem; transition: background 0.4s; }
.award:hover { background: var(--bg-2); }
.award__year {
  font-weight: 600; color: var(--ink);
  font-size: 1.5rem; line-height: 1; display: block; margin-bottom: 0.7rem;
}
.award h3 { font-size: 0.98rem; font-weight: 600; margin-bottom: 0.4rem; line-height: 1.4; }
.award p { color: var(--ink-dim); font-size: 0.82rem; }

/* ============ vision ============ */
.vision {
  position: relative; isolation: isolate; overflow: hidden; background: var(--bg-2);
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
}
.vision__inner { position: relative; z-index: 2; padding: var(--sp) var(--edge); }
.vision__cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.2rem; margin-top: 1.5rem; }
.vision__card {
  border: 1px solid var(--line); border-radius: 8px; padding: 1.6rem 1.5rem;
  background: #fff; box-shadow: 0 1px 2px rgba(23, 20, 15, 0.04);
}
.vision__card h3 {
  font-size: 0.8rem; letter-spacing: 0.2em; text-transform: uppercase;
  color: var(--ink); margin-bottom: 0.7rem; font-weight: 600;
}
.vision__card p { color: var(--ink-dim); font-size: 0.9rem; }

/* ============ team ============ */
.team { padding: var(--sp) var(--edge); }
.team__grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.8rem 1.4rem; margin-top: 1.5rem; }
.member { text-align: center; }
.member__photo {
  width: 100%; aspect-ratio: 1; border-radius: 8px; overflow: hidden;
  background: var(--bg-2); border: 1px solid var(--line); margin-bottom: 0.9rem;
}
.member__photo img {
  width: 100%; height: 100%; object-fit: cover;
  filter: grayscale(0.35); transition: filter 0.4s, transform 0.5s;
}
.member:hover .member__photo img { filter: none; transform: scale(1.04); }
.member__photo--initials { display: flex; align-items: center; justify-content: center; }
.member__photo--initials span { font-weight: 600; font-size: 2.4rem; color: var(--ink-dim); opacity: 0.7; }
.member h3 { font-size: 0.98rem; font-weight: 600; margin-bottom: 0.2rem; }
.member p { color: var(--ink-dim); font-size: 0.82rem; }
.team__note { margin-top: 2rem; text-align: center; color: var(--ink-dim); font-size: 0.88rem; }

/* ============ full-bleed cinematic bands (photo or video) ============
   A fixed cinematic band height, so the caption is ALWAYS overlaid on the
   media at every width and the band never eats a whole screen of scroll.
   The media is cropped to the band with object-fit: cover. */
/* The photograph is shown WHOLE — full width, natural height, never cropped
   top or bottom. The caption is overlaid on it at every width. */
.cband {
  position: relative; isolation: isolate; overflow: hidden; background: var(--photo-bg);
}
.cband__img {
  width: 100%; height: auto; display: block;
  /* the band keeps the shape of the photograph it shipped with, whatever gets
     uploaded later — a portrait would otherwise stretch the band to its full
     height; cover crops instead */
  aspect-ratio: 2048 / 1143; object-fit: cover;
}
/* The box must match the clip EXACTLY (1280x714, not 16:9) — a box even a
   fraction off leaves a letterbox bar of the near-black --photo-bg above and
   below the picture. `cover` then has nothing to crop. */
.cband__video {
  width: 100%; height: auto; aspect-ratio: 1280 / 714; display: block; object-fit: cover;
}
.cband__scrim {
  position: absolute; inset: 0; pointer-events: none; z-index: 1;
  background:
    linear-gradient(180deg, rgba(8,6,3,0.12) 0%, rgba(8,6,3,0) 38%, rgba(8,6,3,0.84) 100%),
    linear-gradient(90deg, rgba(8,6,3,0.46) 0%, rgba(8,6,3,0) 62%);
}
.cband__inner {
  position: absolute; left: var(--edge); right: var(--edge);
  bottom: 2.2rem; z-index: 2;
}
.cband__title {
  font-size: clamp(1.5rem, 3.4vw, 3rem); line-height: 1.08; font-weight: 600;
  letter-spacing: -0.02em; max-width: 22ch; text-shadow: 0 2px 30px rgba(0,0,0,0.55);
}
.cband .section-eyebrow { margin-bottom: 0.6rem; }

/* The video band carries no colour grade of its own, so it only needs enough
   scrim at the bottom to carry the caption — no overall tint. */
.cband--video .cband__scrim {
  background:
    linear-gradient(180deg, rgba(8,6,3,0) 0%, rgba(8,6,3,0) 48%, rgba(8,6,3,0.78) 100%),
    linear-gradient(90deg, rgba(8,6,3,0.2) 0%, rgba(8,6,3,0) 50%);
}

/* ============ map ============ */
.mapsec { padding: var(--sp) var(--edge); }
.mapsec__head { margin-bottom: 1.6rem; }
.mapsec__grid { display: grid; grid-template-columns: 290px 1fr; gap: 1.2rem; }
.map-list {
  display: flex; flex-direction: column; gap: 0; max-height: 440px; overflow-y: auto;
  border: 1px solid var(--line); border-radius: 10px; background: var(--bg-2);
}
/* a <button>, so it does NOT inherit the page font — set it explicitly */
.map-list__item {
  display: flex; align-items: baseline; justify-content: space-between; gap: 1rem;
  text-align: left; width: 100%; padding: 0.8rem 1.1rem; background: none; border: none;
  border-bottom: 1px solid var(--line); border-radius: 0; cursor: pointer;
  color: var(--ink); font-family: var(--font-sans); transition: background 0.2s, color 0.2s;
}
.map-list__item:last-child { border-bottom: none; }
.map-list__item span { font-size: 0.9rem; font-weight: 500; letter-spacing: 0; text-transform: none; }
.map-list__item small { font-size: 0.7rem; color: var(--ink-dim); letter-spacing: 0.06em; white-space: nowrap; }
.map-list__item:hover, .map-list__item:focus-visible { background: var(--bg); color: var(--accent); }
.mapsec__mapwrap { border: 1px solid var(--line); border-radius: 10px; overflow: hidden; background: var(--bg-2); }
#home_map { width: 100%; height: 440px; }

/* mapbox chrome inherits the site face (its own sheet sets Helvetica) */
.mapboxgl-map { font-family: var(--font-sans); }
.mapboxgl-scroll-zoom-blocker, .mapboxgl-touch-pan-blocker { font-family: var(--font-sans); }
.mapboxgl-popup-content {
  background: #fff; color: var(--ink); border: 1px solid var(--line);
  border-radius: 8px; padding: 0.8rem 1rem; font-family: var(--font-sans);
}
.mapboxgl-popup-tip { border-top-color: #fff !important; border-bottom-color: #fff !important; }
.map-pop { display: flex; flex-direction: column; gap: 0.15rem; }
.map-pop strong { font-size: 0.95rem; font-weight: 600; }
.map-pop span { font-size: 0.8rem; color: var(--ink-dim); }
.map-pop em { font-size: 0.7rem; color: var(--ink-dim); letter-spacing: 0.08em; text-transform: uppercase; margin-top: 0.2rem; }
.mapboxgl-ctrl-group { background: #fff !important; }
.mapboxgl-ctrl-group button + button { border-top: 1px solid var(--line) !important; }

/* ============ news ============ */
.news { padding: var(--sp) var(--edge); }
.news__grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.5rem; margin-top: 1.5rem; }
.news-card { display: block; }
.news-card__img {
  aspect-ratio: 16 / 10; border-radius: 8px; overflow: hidden;
  border: 1px solid var(--line); background: var(--bg-2); margin-bottom: 0.9rem;
}
.news-card__img img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.6s; }
.news-card:hover .news-card__img img { transform: scale(1.05); }
.news-card time { font-size: 0.7rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--ink-dim); }
.news-card h3 { font-size: 0.95rem; font-weight: 500; line-height: 1.5; margin-top: 0.4rem; transition: color 0.3s; }
.news-card:hover h3 { color: var(--accent); }
.news__more { margin-top: 1.8rem; text-align: center; }

/* ============ зураг төсөл ============
   Borrows the news-card measurements exactly — a photo at 16:10, a line of
   metadata and a name — so nothing new enters the card language. The home page
   shows three; zurag-tosol.php shows them all, each with its description. */
.dprojects { padding: var(--sp) var(--edge); }
.dprojects__head { margin-bottom: 1.5rem; }
.dgrid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.5rem; margin-top: 1.5rem; }
.dcard { display: block; }
.dcard__img {
  aspect-ratio: 16 / 10; border-radius: 8px; overflow: hidden;
  border: 1px solid var(--line); background: var(--bg-2); margin-bottom: 0.9rem;
}
.dcard__img img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.6s; }
a.dcard:hover .dcard__img img { transform: scale(1.05); }
.dcard__meta { font-size: 0.7rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--ink-dim); }
.dcard h3 { font-size: 0.95rem; font-weight: 500; line-height: 1.5; margin-top: 0.4rem; transition: color 0.3s; }
a.dcard:hover h3 { color: var(--accent); }
.dcard > p:not(.dcard__meta) { color: var(--ink-dim); font-size: 0.88rem; line-height: 1.65; margin-top: 0.5rem; }
.dprojects__more { margin-top: 1.8rem; text-align: center; }
.dprojects__btn { padding: 0.85em 1.9em; font-size: 0.78rem; }
.dgrid--page { margin-top: 0.5rem; gap: 1.8rem 1.5rem; }

/* an optional paragraph under a section title, set from the admin */
.section-intro { color: var(--ink-dim); max-width: 44rem; margin: -0.4rem 0 1.4rem; font-size: 0.92rem; }

/* ============ careers ============ */
.careers { background: var(--bg-2); border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }
/* A recruitment strip between two large sections — it should read as a note,
   not as a headline act, so its type is a step down from a section title. */
.careers__inner {
  padding: 2.25rem var(--edge);
  display: flex; align-items: center; justify-content: space-between; gap: 2rem;
}
.careers__inner .section-eyebrow { margin-bottom: 0.5rem; }
.careers__inner .section-title {
  font-size: clamp(1.45rem, 2.2vw, 1.95rem); margin-bottom: 0.5rem;
}
.careers__inner p:not(.section-eyebrow) { color: var(--ink-dim); max-width: 44rem; font-size: 0.9rem; }
.careers__btn { flex-shrink: 0; padding: 0.85em 1.9em; font-size: 0.78rem; }

/* ============ subpages (projects.php, news.php, team.php, article.php) ============ */
.page-hero { padding: calc(var(--sp) + 3.5rem) var(--edge) 1.5rem; }
/* article.php only: its heading is centred to the same measure as the prose.
   projects.php and team.php keep the full width their table and grid need. */
.page-hero--article { max-width: calc(68ch + 2 * var(--edge)); margin-inline: auto; }
.page-hero .section-title { margin-bottom: 0.8rem; }
.page-hero p { color: var(--ink-dim); max-width: 38rem; }
.page-body { padding: 0 var(--edge) var(--sp); }

.filterbar { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 1.6rem 0; }
.chip {
  font: 500 0.76rem var(--font-sans); letter-spacing: 0.08em;
  padding: 0.5em 1.1em; border-radius: 99px; cursor: pointer;
  border: 1px solid var(--line); background: none; color: var(--ink-dim); transition: 0.25s;
}
.chip:hover { border-color: var(--accent); color: var(--accent); }
.chip.active { background: var(--ink); border-color: var(--ink); color: var(--bg); }

.ptable { width: 100%; border-collapse: collapse; }
.ptable th {
  text-align: left; font-size: 0.7rem; letter-spacing: 0.2em; text-transform: uppercase;
  color: var(--ink-dim); font-weight: 600; padding: 0.8rem 1rem; border-bottom: 1px solid var(--line);
}
.ptable td { padding: 0.85rem 1rem; border-bottom: 1px solid var(--line); font-size: 0.9rem; }
.ptable td:first-child { font-weight: 600; }
.ptable td:nth-child(2), .ptable td:nth-child(3) { color: var(--ink-dim); }
.ptable tr { transition: background 0.25s; }
.ptable tbody tr:hover { background: var(--bg-2); }
.ptable .badge {
  font-size: 0.7rem; letter-spacing: 0.08em; padding: 0.3em 0.85em; border-radius: 99px;
  border: 1px solid var(--line); color: var(--ink-dim); white-space: nowrap;
}
.ptable__count { color: var(--ink-dim); font-size: 0.82rem; margin-bottom: 0.8rem; }

.news-list { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0 2rem; margin-top: 0.5rem; }
.news-row { display: flex; gap: 1.2rem; align-items: baseline; padding: 1.1rem 0; border-bottom: 1px solid var(--line); }
.news-row time { font-size: 0.72rem; letter-spacing: 0.14em; color: var(--ink-dim); white-space: nowrap; }
.news-row h3 { font-size: 0.95rem; font-weight: 500; line-height: 1.5; }
.news-row { transition: color 0.25s; }
a.news-row:hover h3 { color: var(--accent); }

/* ============ a single news article ============ */
/* A measure with no centring pinned the column to the left edge of a wide
   screen and left the right half empty. .article is used only by article.php. */
.article { max-width: 68ch; margin-inline: auto; }
.article__figure { margin: 0 0 1.8rem; border-radius: 8px; overflow: hidden; border: 1px solid var(--line); }
.article__figure img { width: 100%; height: auto; display: block; }
.article p { color: var(--ink-dim); margin-bottom: 1rem; font-size: 0.98rem; line-height: 1.7; }
.article__back { margin-top: 2rem; }
.article__back .link-arrow { color: var(--ink); }

/* ============ timeline — a plain grid: no slider, no pinning ============ */
.timeline { padding: var(--sp) var(--edge); }
.timeline__head { margin-bottom: 1.5rem; }
.timeline__grid {
  list-style: none; display: grid; grid-template-columns: repeat(3, 1fr); gap: 1px;
  background: var(--line); border: 1px solid var(--line);
}
.tl-card { background: var(--bg); padding: 1.6rem 1.5rem; transition: background 0.4s; }
.tl-card:hover { background: var(--bg-2); }
/* the years read as plain ink; only the current one (2026) takes the accent */
.tl-card__year {
  display: block; font-size: 1.5rem; font-weight: 600; color: var(--ink);
  line-height: 1; margin-bottom: 0.7rem; letter-spacing: -0.01em;
}
.tl-card h3 { font-size: 0.98rem; font-weight: 600; margin-bottom: 0.4rem; line-height: 1.4; }
.tl-card p { color: var(--ink-dim); font-size: 0.82rem; }
.tl-card--now { background: var(--bg-2); }
.tl-card--now .tl-card__year { color: var(--accent); }

/* ============ projects — a compact 2-up grid of photo cards ============ */
.projects { padding: var(--sp) var(--edge); }
.projects__head { margin-bottom: 1.5rem; }
.projects__grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1.2rem; }

.project {
  position: relative; isolation: isolate; overflow: hidden; border-radius: 10px;
  min-height: 380px; display: flex; align-items: flex-end; background: var(--photo-bg);
}
.project__media { position: absolute; inset: 0; overflow: hidden; background: var(--photo-bg); z-index: 0; }
.project__media::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(11,12,12,0.16) 0%, rgba(11,12,12,0.1) 34%, rgba(11,12,12,0.9) 100%);
}
.project__media img { transform: scale(1.06); transition: transform 0.8s; }
.project:hover .project__media img { transform: scale(1.12); }
.project__info { position: relative; z-index: 2; padding: 1.8rem 1.6rem; width: 100%; }
.project__index {
  display: block; font-size: 0.72rem; font-weight: 600; letter-spacing: 0.3em;
  color: var(--ink-dim); margin-bottom: 0.5rem;
}
.project__name {
  font-size: clamp(1.4rem, 2.2vw, 2rem); font-weight: 600; letter-spacing: -0.02em;
  line-height: 1.08; margin-bottom: 0.7rem;
}
.project__meta { display: flex; flex-wrap: wrap; gap: 0.3rem 0; list-style: none; margin-bottom: 0.7rem; }
.project__meta li { font-size: 0.68rem; letter-spacing: 0.16em; text-transform: uppercase; color: var(--ink); opacity: 0.8; }
.project__meta li + li::before { content: "/"; margin: 0 0.7rem; color: var(--ink-dim); }
.project__info > p { max-width: 40rem; color: var(--ink-dim); font-size: 0.88rem; }

/* the closing "300+ projects" card is a flat panel, not a photo */
.project--more {
  --ink: #17140f; --ink-dim: #5f574c; --line: rgba(23, 20, 15, 0.12); --accent: #a4161a;
  color: var(--ink);
  background: var(--bg-2); border: 1px solid var(--line);
  align-items: center; justify-content: center; text-align: center;
}
.project--more .project__info { padding: 2.5rem 2rem; }
.project--more .project__info > p { margin: 0 auto 1rem; }

/* ============ services ============ */
.services { padding: var(--sp) var(--edge); }
.services__grid {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 1px;
  background: var(--line); border: 1px solid var(--line); margin-top: 1.5rem;
}
.svc { background: var(--bg); padding: 1.8rem 1.5rem; transition: background 0.4s; }
.svc:hover { background: var(--bg-2); }
.svc span { font-weight: 600; letter-spacing: 0.2em; color: var(--ink-dim); font-size: 0.78rem; }
.svc h3 { font-size: 1.05rem; font-weight: 600; margin: 0.7rem 0 0.5rem; }
.svc p { color: var(--ink-dim); font-size: 0.87rem; }

/* ============ partners ============ */
/* Partner logos sit in a bordered grid of equal white tiles, the same language
   as the awards and services grids. Every file in img/pl-*.png was trimmed of
   its whitespace and re-centred at a common visual weight, so the marks read at
   one size regardless of how much padding the original artwork carried. */
.partners { padding: var(--sp) var(--edge) 2.5rem; }
.partners__row {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 1px;
  background: var(--line); border: 1px solid var(--line);
  margin-top: 1.5rem;
}
.partners__row img {
  width: 100%; height: auto; aspect-ratio: 440 / 170; object-fit: contain;
  background: #fff; transition: transform 0.35s;
}
.partners__row img:hover { transform: scale(1.05); }

/* ============ quote ============ */
.quote { padding: 2.75rem var(--edge) var(--sp); text-align: center; }
.quote__text, .quote__attr { max-width: 62ch; margin-left: auto; margin-right: auto; }
/* Solid ink, italic. It used to fade in word by word from 16% opacity on a
   scroll scrub, which meant it read as grey unless you happened to stop at the
   end of the effect. */
.quote__text {
  font-style: italic; font-weight: 500; color: var(--ink);
  font-size: clamp(1.1rem, 1.9vw, 1.55rem); line-height: 1.45; letter-spacing: -0.01em;
}
.quote__text .w { opacity: 1; }
.quote__attr {
  margin-top: 1.2rem; font-size: 0.72rem; letter-spacing: 0.24em; text-transform: uppercase;
  color: var(--ink);
}
/* the team page has no top-level nav item, so this is its way in from the home page */
.quote__more { margin-top: 0.9rem; }

/* ============ footer ============
   On the home page the closing photograph IS the footer: the call to action and
   every contact detail sit on top of it, so nothing is stated twice. The plain
   .footer rule below is still used by the subpages, which carry only a base line. */
.footer { background: var(--bg-2); border-top: 1px solid var(--line); padding: var(--sp) var(--edge) 1.8rem; }

.cband--footer { display: flex; align-items: flex-end; }
/* The closing photograph is a BAND, not a full plate. Held at its own 3:2
   shape it stood 1280px tall on a 1920 screen, so the #contact anchor landed on
   the top of the photo and left every contact detail below the fold.
   min-height, NOT max-height or aspect-ratio: .cband is overflow:hidden and this
   panel is align-items:flex-end, so a hard cap would clip the top of the text if
   it ever grew. A floor shrinks the photo but can never crop the words. */
@media (min-width: 901px) { .cband--footer { min-height: min(76vh, 66vw); } }
/* The excavator sits between roughly 34% and 83% of the frame with empty sky
   above it. 75% takes 22-33% of the crop off the TOP — the sky — across every
   desktop size while leaving the whole machine in frame; the 7-11% it trims
   from the bottom is foreground under the darkest part of the scrim, where the
   contact text sits. A flat 100% would shave the top off the boom. */
.cband--footer .cband__img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: 50% 75%;
}
/* a deeper wash than the other bands — it carries far more text */
.cband--footer .cband__scrim {
  background: linear-gradient(180deg,
    rgba(8,6,3,0.22) 0%, rgba(8,6,3,0.42) 26%, rgba(8,6,3,0.72) 58%, rgba(8,6,3,0.92) 100%);
}
.footer__inner {
  position: relative; z-index: 2; width: 100%;
  padding: var(--sp) var(--edge) 1.6rem;
}
.footer__inner .cband__title { margin-bottom: 2.5rem; }
.footer__cols a { border-bottom: 1px solid var(--line); transition: color 0.3s, border-color 0.3s; }
.footer__cols a:hover { color: var(--accent); border-color: var(--accent); }
.footer__cols {
  margin: 0 0 2rem; display: grid;
  grid-template-columns: repeat(4, 1fr); gap: 1.5rem;
}
.footer__cols h4 {
  font-size: 0.72rem; letter-spacing: 0.22em; text-transform: uppercase; color: var(--ink-dim);
  margin-bottom: 0.6rem; font-weight: 600;
}
.footer__cols p { color: var(--ink-dim); font-size: 0.86rem; }
.footer__nav {
  margin: 0 0 1.4rem; display: flex; flex-wrap: wrap; gap: 0.8rem 2rem;
  padding-bottom: 1.4rem; border-bottom: 1px solid var(--line);
}
.footer__nav a {
  font-size: 0.74rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-dim);
  transition: color 0.3s;
}
.footer__nav a:hover { color: var(--accent); }
.footer__base {
  display: flex; flex-wrap: wrap; gap: 0.4rem 1.5rem;
  font-size: 0.72rem; letter-spacing: 0.1em; color: var(--ink-dim);
}
/* the subpages still use .footer, where the base line needs its own rule */
.footer .footer__base { border-top: 1px solid var(--line); padding-top: 1.2rem; }

/* ============ responsive ============ */
@media (max-width: 1100px) {
  .team__grid { grid-template-columns: repeat(3, 1fr); }
  .stats__grid { gap: 1rem; }
  .mapsec__grid { grid-template-columns: 250px 1fr; }
}

@media (max-width: 900px) {
  :root { --sp: 3.25rem; --gutter: 1.4rem; }

  .nav { padding: 1rem var(--gutter); }
  /* .nav.is-scrolled (0,2,0) outranks .nav (0,1,0), so restate it here */
  .nav.is-scrolled { padding: 0.65rem var(--gutter); }
  .nav__links { display: none; }
  /* scope the hide to the nav bar — .nav__cta is reused for the careers button */
  .nav > .nav__cta { display: none; }
  .nav__burger { display: flex; }

  .marquee { padding: 0.4rem 0; }
  .marquee__inner span { font-size: 0.6rem; letter-spacing: 0.18em; }

  .hero { height: 88vh; min-height: 520px; }
  .hero__content { top: 22vh; }

  .stats__grid { grid-template-columns: repeat(2, 1fr); gap: 1.6rem 1.2rem; }
  .story { grid-template-columns: 1fr; gap: 2rem; }
  /* two columns, not one — collapsing straight to a single column at this
     width made the page roughly 50% taller than it needs to be */
  .awards__grid, .vision__cards, .news__grid, .services__grid,
  .timeline__grid, .projects__grid, .dgrid { grid-template-columns: repeat(2, 1fr); }
  .partners__row { grid-template-columns: repeat(2, 1fr); }
  .mapsec__grid { grid-template-columns: 1fr; }
  .map-list { max-height: 220px; }
  #home_map { height: 360px; }
  .team__grid { grid-template-columns: repeat(2, 1fr); gap: 1.4rem 1rem; }
  .careers__inner { flex-direction: column; align-items: flex-start; padding: var(--sp) var(--edge); }
  .news-list { grid-template-columns: 1fr; }
  .ptable th, .ptable td { padding: 0.8rem 0.7rem; }
  .ptable td:nth-child(2), .ptable th:nth-child(2),
  .ptable td:nth-child(3), .ptable th:nth-child(3) { display: none; }
  .footer__cols { grid-template-columns: repeat(2, 1fr); }
  .footer__base { flex-direction: column; gap: 0.4rem; }

  /* The caption STAYS over the photo at every width — only the gutter and
     the type scale change. It used to become a flat box below the image. */
  .cband__inner { bottom: 1.2rem; }
  .cband__title { font-size: clamp(1.3rem, 5.2vw, 2.2rem); max-width: 20ch; }

  .project { min-height: 320px; }
  .project__info { padding: 1.5rem 1.3rem; }

  .footer__inner { padding-top: calc(var(--sp) + 1rem); }
  .footer__cols { grid-template-columns: repeat(2, 1fr); gap: 1.4rem 1rem; }
}

/* phones: single column, tighter cards */
@media (max-width: 620px) {
  .awards__grid, .vision__cards, .news__grid, .services__grid,
  .timeline__grid, .projects__grid, .dgrid { grid-template-columns: 1fr; }
  .award, .tl-card, .svc, .vision__card { padding: 1.25rem 1.2rem; }
  .project { min-height: 260px; }
  .project__info { padding: 1.2rem 1.1rem; }
  .footer__cols { grid-template-columns: 1fr 1fr; }
}

@media (max-width: 480px) {
  .cband__title { font-size: 1.15rem; max-width: 18ch; }
  .cband__inner { bottom: 0.9rem; }
  .cband .section-eyebrow { font-size: 0.64rem; letter-spacing: 0.22em; }
  .stats__grid, .team__grid { grid-template-columns: repeat(2, 1fr); }
}

@media (prefers-reduced-motion: reduce) {
  /* the marquee is deliberately NOT stopped here — js/main.js slows it to a
     third instead, so a phone with Reduce Motion on never shows a strip that
     looks broken (the owner's call) */
  .hero__scrollline i { animation: none; }
  .hero__slide { transition: none; }
}
