/* =========================================================
   HARIS DAWOOD — QA/QC ENGINEER PORTFOLIO
   v5 — "Corporate Premium": a warm off-white / dark-charcoal
   engineering-consultancy palette. Steel blue is the primary
   "engineering" accent; muted green is reserved specifically for
   quality, reliability and certification content. Plus Jakarta
   Sans display type + Inter body. Corner-bracket photo framing,
   dimension-line labels and the timeline spine carry over from
   the prior system, recoloured into this palette.
   Structure:
   1. Reset & Variables   5. Header / Footer     9.  Case Study      13. Award-level polish
   2. Typography          6. Hero                10. Background / Details
   3. Layout primitives   7. Expertise / Stats    11. Responsive
   4. Buttons & Links     8. Work grid / Cards    12. Accessibility
   ========================================================= */

/* ============ 1. RESET & VARIABLES ============ */
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; max-width: 100%; overflow-x: hidden; }
img { max-width: 100%; display: block; }
ul { margin: 0; padding: 0; }
a { color: inherit; text-decoration: none; }
button { font-family: inherit; cursor: pointer; }
h1, h2, h3, h4, p { margin: 0; }

:root{
  --color-bg:        #F7F5EF;
  --color-surface:   #FFFFFF;
  --color-ink:       #24272C;
  --color-ink-soft:  #585C63;
  --color-ink-faint: #8A8F96;
  --color-line:      #D9D6CF;
  --color-navy:      #1F3E52;
  --color-steel:     #3E6B8A;
  --color-steel-soft: rgba(62,107,138,0.12);
  --color-green:     #4C7A5D;
  --color-green-soft: rgba(76,122,93,0.12);
  --color-charcoal-deep: #1D2024;
  --color-charcoal-line: rgba(255,255,255,0.14);

  --font-display: 'Plus Jakarta Sans', 'Arial', sans-serif;
  --font-body: -apple-system, 'Inter', BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif;
  /* Dedicated "premium" wordmark face for the hero name only (index.html) —
     a distinctive display grotesk, not the site's workhorse display font,
     so "Haris Dawood" reads like a personal mark rather than a heading. */
  --font-name: 'Bricolage Grotesque', var(--font-display);
  /* Shared between .hero-title's own font-size and the .hero-cover margin
     calc below, which needs to know the name's rendered height to position
     it precisely against the avatar — kept as one variable so the two
     never drift out of sync if this size is ever tuned again. */
  --hero-title-fs: clamp(1.9rem, 2.6vw + 1.1rem, 2.9rem);
  /* How much of the avatar hangs below the banner's bottom edge, as a
     fraction of its own size — 0.35 = the client's 65% in / 35% out
     desktop ratio. Overridden to 0.6 on mobile (see the max-width:860px
     block) per the client's own request for more room there. */
  --hero-avatar-overlap: 0.35;

  --fs-display: clamp(2.9rem, 5vw + 1rem, 5.6rem);
  --fs-h2:       clamp(1.9rem, 2.1vw + 1rem, 2.6rem);
  --fs-h3:       clamp(1.25rem, 0.9vw + 0.9rem, 1.55rem);
  --fs-lede:     clamp(1.05rem, 0.5vw + 0.95rem, 1.3rem);
  --fs-body:     1.05rem;
  --fs-small:    0.88rem;

  --space-2xs: 0.4rem;
  --space-xs:  0.75rem;
  --space-sm:  1.25rem;
  --space-md:  2rem;
  --space-lg:  3.5rem;
  --space-xl:  7rem;
  --space-2xl: 10rem;

  --max-width: 1120px;
  --max-width-narrow: 720px;
  --radius: 14px;
  --radius-sm: 9px;

  --shadow-sm: 0 1px 2px rgba(20,22,26,0.05);
  --shadow-md: 0 18px 40px -12px rgba(20,22,26,0.18);
  --shadow-lift: 0 20px 36px -16px rgba(20,22,26,0.22);

  --header-h: 84px;
  /* Scales smoothly with viewport width rather than a single fixed size,
     so it reaches its full (roughly double the old 160px) size on
     typical desktop/laptop widths without ever overflowing the banner's
     own height at narrower "desktop" widths just above the mobile
     breakpoint — see the max-width:860px override for the mobile value. */
  --hero-avatar-size: clamp(140px, 22vw, 320px);
  --transition: 260ms cubic-bezier(.4,0,.2,1);
}

/* ============ 2. TYPOGRAPHY ============ */
body{
  font-family: var(--font-body);
  color: var(--color-ink);
  background: var(--color-bg);
  font-size: var(--fs-body);
  line-height: 1.7;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
h1, h2 { font-family: var(--font-display); font-weight: 600; letter-spacing: -0.02em; line-height: 1.04; color: var(--color-ink); }
h1 { font-size: var(--fs-display); font-weight: 600; }
h2 { font-size: var(--fs-h2); }
h3 { font-size: var(--fs-h3); font-weight: 600; font-family: var(--font-body); letter-spacing: -0.01em; color: var(--color-ink); line-height: 1.2; }
p { color: var(--color-ink-soft); }
strong { color: var(--color-ink); font-weight: 600; }
.lede{ font-size: var(--fs-lede); color: var(--color-ink-soft); max-width: 42ch; }

/* Justified body copy, sitewide — every paragraph of real prose across every
   page and every card, per the client's explicit request: even left/right
   edges everywhere, no word-splitting hyphenation (client explicitly asked
   to avoid it). The browser already leaves each paragraph's own last line
   unstretched by default, so short final lines still read normally.
   Left OUT on purpose — not prose, so justify either does nothing or looks
   wrong on it: headings, the kicker labels, nav links, buttons, pills/tags,
   role/meta lines (name · location · dates), captions, dates, and any
   other single-line value or label. */
.hero-role,
.lede,
.bg-summary p,
.case-body-content p,
.footer-statement p,
.work-feature-card p,
.proof-card-body p,
.redact-note,
.detail-card-body p,
.project-entry-focus,
.featured-project-desc{
  text-align: justify;
}
/* .capability-card p deliberately excluded: its 4-column grid is too
   narrow (~150px per card) for justify to work — with only 2-3 short
   words per line, the browser has to stretch huge gaps between them to
   reach both edges, and hyphenating the words involved doesn't help
   since none of them are long enough for a hyphen break to matter. Left
   left-aligned, the one deliberate exception beyond headings/labels. */

/* Re-verified per the client's explicit request (round 30): justify on
   mobile too, everywhere it reads cleanly — checked visually per
   selector at 320-390px, not assumed. Most of the list above holds up
   fine at mobile column widths once actually tested. The one exception
   kept here is .footer-statement p: a single large, bold, few-words-
   per-line sentence (not body prose), where justify still stretches
   visibly large gaps between words at phone widths — left justified on
   mobile only, unchanged (still justified on the wider desktop/tablet
   column, where it already read fine). */
@media (max-width: 640px){
  .footer-statement p{
    text-align: left;
  }
}

.kicker{
  display: inline-flex; align-items: center; gap: 0.6rem;
  font-size: 0.76rem; letter-spacing: 0.15em; text-transform: uppercase;
  color: var(--color-steel); font-weight: 600; margin-bottom: var(--space-sm);
}
.kicker::before{ content: ""; display: inline-block; width: 18px; height: 1px; background: var(--color-steel); flex: none; }
.kicker.kicker-green{ color: var(--color-green); }
.kicker.kicker-green::before{ background: var(--color-green); }
.eyebrow{ font-size: 0.85rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--color-ink-faint); margin-bottom: var(--space-xs); }

/* ============ 3. LAYOUT PRIMITIVES ============ */
.container{ max-width: var(--max-width); margin: 0 auto; padding: 0 var(--space-md); }
.container-narrow{ max-width: var(--max-width-narrow); margin: 0 auto; padding: 0 var(--space-md); }
/* Items 10/11 (round 30): on background.html, the Professional Summary and
   Foundation & Education sections both use .container-narrow — the ONLY
   two places this class is used sitewide — and the client asked for both
   to align with the nav bar's own left/right edges on desktop. Reusing
   plain .container wouldn't actually do that: the nav pill is deliberately
   NARROWER than .container (width: calc(100% - 2*space-md), capped at
   max-width - 40px — the same formula .hero-cover already uses to line up
   with it), so this desktop-only override gives .container-narrow that
   exact formula, with its own padding zeroed out so the TEXT edge itself
   (not a padding box inset further inward) lands on the nav's edge.
   Left untouched below 861px — mobile wasn't part of this request, and
   the existing 720px-narrow/padded mobile treatment already reads fine
   there. */
@media (min-width: 861px){
  .container-narrow{
    width: calc(100% - 2 * var(--space-md));
    max-width: calc(var(--max-width) - 40px);
    padding: 0;
  }
}
.section{ padding: var(--space-xl) 0; }
.section-tight{ padding: var(--space-lg) 0; }
.divider{ border: none; border-top: 1px solid var(--color-line); margin: 0; }
.skip-link{
  position:absolute; left:-999px; top:0; background:var(--color-navy); color:#fff;
  padding: 0.5rem 1rem; z-index: 200;
}
.skip-link:focus{ left: var(--space-sm); top: var(--space-sm); }

/* ============ 4. BUTTONS & LINKS ============ */
.btn{
  display: inline-flex; align-items: center; justify-content: center;
  gap: 0.5rem; padding: 0.9rem 1.7rem; border-radius: 999px;
  font-weight: 600; font-size: 0.95rem; border: 1px solid transparent;
  transition: transform var(--transition), background var(--transition), color var(--transition), border-color var(--transition);
}
.btn-primary{ background: var(--color-navy); color: #fff; }
.btn-primary:hover{ background: #10192E; transform: translateY(-1px); }
.btn-outline{ border-color: var(--color-ink); color: var(--color-ink); }
.btn-outline:hover{ background: var(--color-ink); color: #fff; transform: translateY(-1px); }
.btn:focus-visible{ outline: 2px solid var(--color-steel); outline-offset: 3px; }

.text-link{
  position: relative; font-weight: 600; color: var(--color-ink);
  padding-bottom: 2px; border-bottom: 1px solid var(--color-ink);
}
.text-link:hover{ color: var(--color-steel); border-color: var(--color-steel); }
.text-link-muted{ color: var(--color-ink-soft); border-bottom: 1px solid var(--color-line); font-weight: 500; }
.text-link-muted:hover{ color: var(--color-ink); border-color: var(--color-ink-faint); }
.arrow{ display: inline-block; transition: transform var(--transition); }
.text-link:hover .arrow{ transform: translateX(3px); }
.contact-links{ display: flex; flex-direction: column; gap: 1rem; align-items: flex-start; }

/* ============ CONTACT FORM ============ */
.contact-grid{ display: grid; grid-template-columns: 0.9fr 1.1fr; gap: var(--space-lg); align-items: start; }
.contact-channels{ display: flex; flex-direction: column; gap: 0.75rem; }
.contact-channel{
  display: flex; align-items: center; gap: 0.9rem;
  background: var(--color-surface); border: 1px solid var(--color-line); border-radius: var(--radius);
  padding: 1rem 1.1rem;
}
.contact-channel-icon{
  width: 38px; height: 38px; border-radius: 50%; flex: none;
  display: flex; align-items: center; justify-content: center;
  background: var(--color-steel-soft); color: var(--color-steel); font-size: 1.05rem;
}
.contact-channel-icon.icon-green{ background: var(--color-green-soft); color: var(--color-green); }
.contact-channel-label{ font-size: 0.78rem; color: var(--color-ink-faint); text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 0.15rem; }
.contact-channel-value{ font-weight: 600; font-size: 0.98rem; }
.contact-channel-value a{ border-bottom: 1px solid var(--color-line); }
.contact-channel-value a:hover{ color: var(--color-steel); border-color: var(--color-steel); }

.contact-form{
  background: var(--color-surface); border: 1px solid var(--color-line); border-radius: var(--radius);
  padding: var(--space-md);
}
.form-row{ display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-sm); }
.form-field{ display: flex; flex-direction: column; gap: 0.45rem; margin-top: var(--space-sm); }
.form-row .form-field{ margin-top: 0; }
.form-field label{ font-size: 0.85rem; font-weight: 600; color: var(--color-ink); }
.form-field input, .form-field textarea, .form-field select{
  font-family: var(--font-body); font-size: 0.95rem; color: var(--color-ink);
  background: var(--color-bg); border: 1px solid var(--color-line); border-radius: var(--radius);
  padding: 0.75rem 0.9rem; width: 100%;
}
.form-field textarea{ resize: vertical; min-height: 110px; }
.form-field input:focus, .form-field textarea:focus, .form-field select:focus{
  outline: none; border-color: var(--color-steel); box-shadow: 0 0 0 3px var(--color-steel-soft);
}
.form-submit-row{ margin-top: var(--space-md); display: flex; align-items: center; gap: var(--space-sm); flex-wrap: wrap; }
.form-note{ font-size: 0.8rem; color: var(--color-ink-faint); }

/* Contact form — submission status message (JS-driven) and honeypot
   anti-spam field. Neither has any effect on the locked visible layout:
   the status line is hidden until JS sets .is-visible on success/error,
   and the honeypot field is taken out of flow with zero footprint. */
.form-status{ display: none; margin-top: var(--space-sm); font-size: 0.85rem; font-weight: 600; }
.form-status.is-visible{ display: block; }
.form-status.is-success{ color: var(--color-green); }
.form-status.is-error{ color: #B23A3A; }
.form-honeypot{
  position: absolute; left: -9999px; top: auto; width: 1px; height: 1px;
  overflow: hidden; opacity: 0; pointer-events: none;
}

/* Contact page — desktop-only spacing tightened so the whole page (heading
   through footer) fits a single desktop viewport without scrolling, per
   the client's explicit request. Scoped entirely to .contact-page-head
   (added on this page's own head section only) so no other page's
   desktop layout is touched — same scoping pattern as .work-page-head
   above. Mobile is untouched: .page-head-pad's own ≤860px override
   (padding-top: var(--space-lg)) still applies as before. Card style,
   typography and the footer are all left exactly as they are; only the
   gaps between elements are reduced. */
.contact-page-head{ position: relative; }
@media (min-width: 861px){
  .contact-page-head.page-head-pad{ padding-top: calc(var(--header-h) + var(--space-sm)); }
  .contact-page-head.section-tight{ padding-bottom: var(--space-xs); }
  .contact-page-head .section-head{ margin-bottom: var(--space-xs); }
  .contact-page-head .contact-form{ padding: var(--space-sm) var(--space-md); }
  .contact-page-head .form-field{ margin-top: var(--space-2xs); }
  .contact-page-head .form-row .form-field{ margin-top: 0; }
  .contact-page-head .form-field textarea{ min-height: 75px; }
  .contact-page-head .form-submit-row{ margin-top: var(--space-2xs); }
}

/* ============ 5. HEADER / FOOTER (shared across pages) ============ */
.site-header{
  position: fixed; top: 0; left: 0; right: 0; z-index: 100;
  height: var(--header-h);
  background: transparent;
  display: flex; align-items: center; justify-content: center;
  border-bottom: 1px solid transparent;
  transition: border-color var(--transition), background var(--transition);
}
.site-header.scrolled{ border-bottom-color: transparent; }

/* The nav is the same floating glass toolbar at every width — a rounded,
   blurred pill that sits inside the reserved header band instead of a
   full-width bar, the "native app chrome" look — rather than switching to
   a different mobile pattern (bottom tab bar/hamburger). Sizing/spacing
   scale down for narrower screens below, but the pill, the wordmark, the
   nav links and the CV action are the same nav bar at every width. */
.navbar{
  --navbar-pad-left: 1.7rem;
  width: calc(100% - 2 * var(--space-md));
  max-width: calc(var(--max-width) - 40px);
  height: 62px;
  margin: 0 auto;
  background: rgba(255,255,255,0.72);
  backdrop-filter: blur(18px) saturate(160%);
  -webkit-backdrop-filter: blur(18px) saturate(160%);
  border: 1px solid var(--color-line);
  border-radius: 999px;
  padding: 0 0.55rem 0 var(--navbar-pad-left);
  box-shadow: 0 10px 28px -16px rgba(20,22,26,0.14);
  transition: box-shadow var(--transition), background var(--transition), border-color var(--transition);
  display: flex; align-items: center; gap: var(--space-sm);
  overflow: hidden;
}
.site-header.scrolled .navbar{
  background: rgba(255,255,255,0.92);
  box-shadow: 0 16px 34px -14px rgba(20,22,26,0.22);
  border-color: rgba(217,214,207,0.8);
}

/* The wordmark sits in its own gradient-filled segment of the pill — a
   badge occupying the bar's left cap, distinct from the frosted-glass
   body the rest of the nav sits on, but still one continuous pill shape
   (clipped by .navbar's own overflow:hidden + border-radius). */
.wordmark{
  font-family: var(--font-display); font-weight: 600; font-size: 1.1rem; letter-spacing: -0.01em;
  color: #fff; margin-right: auto; white-space: nowrap; flex: none;
  display: flex; align-items: center; height: 100%;
  margin-left: calc(var(--navbar-pad-left) * -1);
  padding-left: var(--navbar-pad-left);
  padding-right: 1.1rem;
  background: linear-gradient(120deg, var(--color-navy) 0%, var(--color-steel) 100%);
}
.wordmark span{ color: #BFE0F2; }

.nav-menu{ display: flex; align-items: center; gap: var(--space-lg); min-width: 0; }
.nav-links{
  display: flex; gap: var(--space-md); list-style: none; min-width: 0;
  overflow-x: auto; -ms-overflow-style: none; scrollbar-width: none;
}
.nav-links::-webkit-scrollbar{ display: none; }
.nav-links li{ position: relative; }
/* Thin vertical separators between nav items, centred in the existing
   gap — a visual break between links without splitting them into
   separate bars. */
.nav-links li:not(:last-child)::after{
  content: ""; position: absolute; top: 50%; right: calc(var(--space-md) / -2);
  transform: translateY(-50%);
  width: 1px; height: 14px; background: var(--color-line);
}
.nav-link{
  font-size: 0.94rem; font-weight: 500; color: var(--color-ink-soft);
  position: relative; padding: 0.35rem 0; transition: color var(--transition);
  white-space: nowrap;
}
.nav-link::after{
  content:""; position:absolute; left:0; bottom:-2px; width:0; height:1.5px;
  background: var(--color-navy); transition: width var(--transition);
}
.nav-link:hover, .nav-link.active{ color: var(--color-ink); }
.nav-link:hover::after, .nav-link.active::after{ width: 100%; }
.btn-nav{ padding: 0.65rem 1.3rem; font-size: 0.88rem; white-space: nowrap; }

/* CV action inside the nav: on narrower screens a full "Download CV" text
   button would crowd the pill, so a compact icon button stands in for it
   there (hidden again from 861px up, where the full button is shown).
   `order` puts it after the nav links regardless of markup position, so
   the header markup itself doesn't need to change per breakpoint. */
.navbar-cv-icon{
  display: flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; border-radius: 50%; flex: none;
  background: var(--color-steel-soft); color: var(--color-steel);
  order: 2;
}
.navbar-cv-icon svg{ width: 18px; height: 18px; }

@media (min-width: 861px){
  .navbar-cv-icon{ display: none; }
}

.site-footer{ background: var(--color-charcoal-deep); color: #E7E9EE; padding: var(--space-md) 0; }
/* Statement + CTA sit on one compact row, text against the left edge and
   the CTA against the right — rather than stacking, which is what made
   this section tall. Wraps to two lines only if the width really forces it. */
.footer-statement{
  display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap;
  gap: var(--space-sm) var(--space-md);
  margin-bottom: var(--space-md); padding-bottom: var(--space-md); border-bottom: 1px solid var(--color-charcoal-line);
}
.footer-statement p{
  font-family: var(--font-display); font-weight: 600; letter-spacing: -0.015em;
  font-size: clamp(1.05rem, 0.6vw + 0.95rem, 1.3rem); color: #fff; max-width: 44ch;
  flex: 1 1 280px; margin: 0;
}
.footer-cta{
  display: inline-flex; align-items: center; gap: 0.4rem; flex: none;
  color: #fff; font-weight: 600; font-size: 0.94rem;
  padding-bottom: 2px; border-bottom: 1px solid rgba(255,255,255,0.35);
}
.footer-cta:hover{ color: #7BB3D6; border-color: #7BB3D6; }
.footer-cta:hover .arrow{ transform: translateX(3px); }
/* Just two blocks now (name/role, contact) — a left/right split instead
   of a 3-column grid, since the Site links column was dropped (the
   floating nav already covers that on every page). */
.footer-grid{ display: flex; justify-content: space-between; align-items: flex-start; flex-wrap: wrap; gap: var(--space-md); }
.footer-name{ font-family: var(--font-display); font-weight: 600; font-size: 1.3rem; }
.footer-role{ color: rgba(231,233,238,0.6); margin-top: 0.3rem; font-size: 0.92rem; }
.footer-heading{ font-size: 0.76rem; letter-spacing: 0.1em; text-transform: uppercase; color: rgba(231,233,238,0.45); margin-bottom: 0.9rem; }
.footer-meta{ display: flex; flex-direction: column; gap: 0.6rem; }
.footer-meta a{ display: flex; align-items: center; gap: 0.65rem; color: rgba(231,233,238,0.85); font-size: 0.92rem; }
.footer-meta a:hover{ color: #fff; }
.footer-meta-icon{
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 26px; height: 26px; border-radius: 50%;
  background: rgba(255,255,255,0.08); color: #fff; font-size: 0.78rem;
}
.copyright{ color: rgba(231,233,238,0.4); font-size: 0.8rem; margin-top: var(--space-md); text-align: center; }
.copy-dot{ display: inline-block; margin: 0 0.5em; font-size: 1.4em; line-height: 0; vertical-align: middle; opacity: 0.6; }

/* Replaces the old plain "scroll to top" button: a back-navigation
   affordance in a light accent gradient (the same navy→steel pairing the
   nav wordmark badge uses, for consistency). Behaviour lives in script.js —
   when the visitor arrived from elsewhere on the site it returns them to
   that exact previous page and scroll position (native browser back
   navigation); otherwise it falls back to scrolling to top. */
.back-nav{
  position: fixed; bottom: 24px; right: 24px; width: 44px; height: 44px; border-radius: 50%;
  background: linear-gradient(135deg, var(--color-steel) 0%, var(--color-navy) 100%);
  color: #fff; border: none;
  display: flex; align-items: center; justify-content: center;
  box-shadow: var(--shadow-md); opacity: 0; visibility: hidden; transform: translateY(10px);
  transition: var(--transition); z-index: 90;
}
.back-nav svg{ width: 18px; height: 18px; }
.back-nav.visible{ opacity: 1; visibility: visible; transform: translateY(0); }
@media (hover: hover){
  .back-nav:hover{ transform: translateY(-2px); box-shadow: 0 18px 30px -14px rgba(20,22,26,0.32); }
}

/* Shared top clearance for a page's first section, where the fixed nav
   would otherwise sit over it — same value the hero/case-hero use. */
.page-head-pad{ padding-top: calc(var(--header-h) + var(--space-lg)); }

/* ============ 6. HERO ============ */
.hero{
  position: relative;
  /* On desktop the banner now sits BELOW the floating nav pill rather
     than behind it (the nav no longer needs to float over it now that
     the banner is a self-contained, inset, rounded block rather than a
     full-bleed background) — otherwise the banner's own top lettering
     sits partly hidden under the nav. Mobile keeps padding-top:0 (nav
     sits at the bottom there, see the max-width:860px override). */
  padding-top: calc(var(--header-h) + var(--space-sm));
  padding-bottom: var(--space-md);
}
/* Was capped at 46rem, which (via .container's own auto margins) centered
   the whole text block as a narrower box floating inside the section
   instead of sitting flush against the banner's own left edge. The
   paragraph still wraps at a readable width via .hero-role's own
   max-width:40em below, so widening this doesn't widen the body copy —
   it only lines the eyebrow/name/actions up with the banner.
   The max-width itself is set 24px wider than the site's normal
   --max-width (1120px) to exactly cancel out .hero-cover's own
   -40px inset (it's capped at max-width - 40px, to match the nav
   pill's width, per an earlier round) — so once both boxes are capped
   on very wide screens, their left edges (after .hero-inner's own
   space-md padding) land on the same pixel instead of drifting apart. */
.hero-inner{ max-width: calc(var(--max-width) + 2 * var(--space-md) - 40px); }
.hero-eyebrow{
  /* display:flex, not inline-flex: an inline-level box here sits inside an
     ambient inline line box sized by the parent's inherited line-height
     (1.7), leaving invisible "strut" space above the element itself that
     doesn't show up in its own getBoundingClientRect height — which was
     throwing the margin calc below off by that same amount. It's already
     alone on its own row visually, so switching to a block-level flex
     container changes nothing on screen, only removes that hidden offset. */
  display: flex; align-items: center; gap: 0.6rem;
  font-size: 0.85rem; letter-spacing: 0.12em; text-transform: uppercase; color: var(--color-steel);
  /* Tight line-height (was inheriting the body's 1.7) and a much smaller
     gap before the name (--space-2xs, not --space-sm) — both deliberate:
     the .hero-cover margin calc below needs the tagline+name block to be
     as compact as the avatar's own protrusion allows, so the name's
     bottom edge can land exactly on the avatar's bottom edge without
     the block sprawling down past it. A single-line label loses nothing
     readability-wise from tighter leading. */
  line-height: 1.3;
  margin-bottom: var(--space-2xs); font-weight: 600;
}
.hero-eyebrow::before{ content: ""; display: inline-block; width: 18px; height: 1px; background: var(--color-steel); }
/* The name gets its own compact, letter-spaced treatment instead of the
   site's giant --fs-display heading size — kept deliberately modest (well
   under the avatar's own radius at every breakpoint) so it reads as a
   refined personal wordmark sitting under the tagline, not a competing
   oversized headline. */
.hero-title{
  margin-bottom: var(--space-sm);
  font-family: var(--font-name);
  font-size: var(--hero-title-fs);
  font-weight: 600;
  letter-spacing: 0.04em;
}
.hero-role{
  font-size: var(--fs-lede); color: var(--color-ink-soft); margin-bottom: var(--space-md);
  /* Tighter, more deliberate leading than the body-text default (1.7) —
     a lede paragraph reads more considered at ~1.5, the way a magazine
     standfirst is set rather than a running paragraph. */
  line-height: 1.55; max-width: 40em;
  text-wrap: pretty;
}
.hero-role strong{ color: var(--color-steel); font-weight: 600; }
.hero-actions{ display: flex; align-items: center; gap: var(--space-md); flex-wrap: wrap; }

/* Profile banner + avatar — sits at the very top of the hero, above the
   text, and renders the same way at every width (mobile and desktop),
   per the client's own request for consistency between the two. On
   desktop (>860px) it's inset to the same left/right edges as the
   floating nav pill below it, so the two visually align; on mobile it
   stays full-bleed (see the max-width:860px override further down). The
   avatar deliberately overlaps the banner's own bottom edge — about 65%
   of its height sits on the banner, the remaining ~35% hangs below it
   on the page's own background — a LinkedIn-style profile-photo overlap,
   per the client's explicit ratio. It still never "blends": .hero-cover
   itself is left overflow:visible so the avatar can extend past its
   edge unclipped, while the image is clipped to the rounded corners by
   .hero-cover-media instead, and the avatar (z-index:2) always paints
   above the bottom fade (a pseudo-element on .hero-cover-media, so it's
   clipped to the same rounded corners as the image and never rendered
   below the avatar in stacking order). */
.hero-cover{
  position: relative;
  width: calc(100% - 2 * var(--space-md));
  max-width: calc(var(--max-width) - 40px);
  /* This margin is no longer just "avatar protrusion + a gap" — it's
     solved backwards from an exact requirement: the NAME's own bottom
     edge must land precisely on the avatar's bottom edge (not just below
     it). Working from .hero-cover's bottom edge: the avatar's bottom sits
     at (avatar-size * 0.35) below it; the tagline+name block that follows
     this margin is (eyebrow line-height*font-size) + (its margin-bottom,
     --space-2xs) + (name line-height 1.04 * --hero-title-fs) tall. Setting
     this margin to protrusion-minus-block-height means the block's own
     bottom (the name) lands exactly on the avatar's bottom by construction
     — at every width, since every term is the same responsive value
     driving the visible rendering, not a fixed guess. At the narrowest
     mobile widths this comes out slightly negative (the eyebrow starts a
     few pixels above the banner's own bottom edge) — verified this never
     exceeds the bottom fade's own height, so it only ever lands on the
     fade's fully-faded-to-page-background portion, never on visible photo
     content. Desktop's eyebrow line-height/font-size differ (see the
     max-width:860px override below), so mobile gets its own version of
     this same formula there. */
  margin: 0 auto max(0px, calc(var(--hero-avatar-size) * var(--hero-avatar-overlap) - (0.85rem * 1.3) - var(--space-2xs) - (var(--hero-title-fs) * 1.04)));
}
.hero-cover-media{
  /* The banner graphic itself is a designed 2048x768 (2.667:1) piece with
     its own name/title/location lettering baked in, not a plain photo —
     so it's sized to its exact native ratio rather than an arbitrary
     strip height, showing the whole graphic with zero cropping (so every
     line of its own text stays visible) instead of a different cropped
     slice of it per breakpoint. */
  position: relative; width: 100%; aspect-ratio: 2048 / 768;
  border-radius: var(--radius); overflow: hidden;
}
.hero-cover-media img{ width: 100%; height: 100%; object-fit: cover; object-position: center top; display: block; }
/* A small fade right at the bottom edge only, into the page's own
   background colour — kept short and well clear of where the avatar
   sits on the image, so it never visually washes over it. Lives on
   .hero-cover-media (not .hero-cover) so it's clipped to the same
   rounded corners as the banner image. */
.hero-cover-media::after{
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 14%;
  background: linear-gradient(to bottom, rgba(26,43,56,0) 0%, var(--color-bg) 100%);
  pointer-events: none;
}
/* A container-width layer over the banner, purely so the avatar can align
   to the same right-hand edge every other section's content uses. */
.hero-cover-inner{
  position: absolute; inset: 0; pointer-events: none; z-index: 2;
}
.hero-avatar{
  display: block; position: absolute; right: var(--space-md);
  /* Negative bottom pushes the avatar past the banner's own bottom edge
     by --hero-avatar-overlap of its own height — 0.35 (65% in / 35% out)
     on desktop, 0.6 (40% in / 60% out) on mobile. */
  bottom: calc(var(--hero-avatar-size) * -1 * var(--hero-avatar-overlap));
  width: var(--hero-avatar-size); height: var(--hero-avatar-size);
  border-radius: 50%; overflow: hidden;
  border: 4px solid var(--color-surface);
  box-shadow: 0 0 0 1px var(--color-line), var(--shadow-md);
  pointer-events: auto;
}
/* object-position keeps the crop anchored to the top of the portrait —
   leaves a little headroom above the hair instead of cropping tight
   against it, and keeps the face itself centred and fully visible. */
.hero-avatar img{ width: 100%; height: 100%; object-fit: cover; object-position: center top; }

/* ============ CREDENTIALS STRIP ============
   A thin credibility band immediately under the hero — same PMP/ISO/SCE
   line the hero used to carry at its own base, now a compact section of
   its own so it reads as a continuation of the hero rather than bulking
   the hero out. */
.credentials-strip{
  padding: var(--space-sm) 0;
  border-top: 1px solid var(--color-line);
  border-bottom: 1px solid var(--color-line);
}
.credentials-line{
  font-size: 0.88rem; color: var(--color-ink-faint); letter-spacing: 0.02em;
}
.credentials-line strong{ color: var(--color-ink-soft); font-weight: 500; }

/* ============ CURRENT PROJECT ============
   Reuses the .hero-photo image treatment (frame, curtain-wipe reveal,
   corner brackets) so the photo keeps its existing visual identity, just
   in its own section instead of riding alongside the hero text. */
.current-project{ padding: var(--space-md) 0; }

/* Shared "section break" treatment for every light-background section
   after the credentials strip: a hairline border with a soft blurred
   shadow just beneath it, so sections that now sit much closer together
   still read as clearly separate bands rather than blurring into one
   long scroll. Combined with each section's own (tightened) padding, this
   replaces the old plain <hr> divider and the larger 3.5rem/7rem gaps. */
.section-sep{
  position: relative;
  border-top: 1px solid var(--color-line);
  box-shadow: 0 10px 22px -18px rgba(20,22,26,0.18);
}
.section-tight.section-sep{ padding: var(--space-md) 0; }
.stats-band.section-sep{ padding: var(--space-sm) 0; }
.current-project-inner{
  display: grid; grid-template-columns: 0.9fr 1.1fr; gap: var(--space-lg);
  align-items: center;
}
.current-project-photo img{ aspect-ratio: 16/10; }
.current-project-inner h2{ margin-top: 0.4rem; margin-bottom: var(--space-sm); }
.current-project-inner .lede{ max-width: none; margin-bottom: var(--space-sm); }

.hero-photo{ position: relative; display: block; border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow-md); }
.hero-photo img{
  width: 100%; height: 100%; object-fit: cover; aspect-ratio: 4/5;
  transform: scale(1.08); transition: transform 900ms cubic-bezier(.16,.84,.44,1);
}
.hero-photo.is-visible img{ transform: scale(1); }

/* ============ 7. EXPERTISE / STATS BAND ============ */
.section-head{ max-width: 620px; margin-bottom: var(--space-lg); }
.section-head h2{ margin-top: 0.4rem; }
.nowrap-heading{ white-space: nowrap; }

.expertise-list{
  columns: 2; column-gap: var(--space-lg);
}
.expertise-list li{
  list-style: none; padding: 0.85rem 0; border-bottom: 1px solid var(--color-line);
  font-size: 1.02rem; color: var(--color-ink);
  break-inside: avoid;
}
.expertise-list li:last-child, .expertise-list li:nth-last-child(2){ border-bottom: none; }

/* Grouped Expertise section — Background page (added for the "What he
   handles on the project" copy pass). The .expertise-list rules above are
   left in place even though this page no longer uses them, per the
   desktop-lock policy of only adding new rules, never editing existing
   ones. */
.expertise-groups{ display: flex; flex-direction: column; gap: var(--space-lg); }
.expertise-group-label{
  font-size: 0.8rem; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--color-steel); font-weight: 600; margin-bottom: var(--space-sm);
}
.expertise-group-items{ display: grid; grid-template-columns: repeat(2, 1fr); gap: 0 var(--space-lg); }
.expertise-item{ padding: 0.85rem 0; border-bottom: 1px solid var(--color-line); }
.expertise-item-name{ font-weight: 600; font-size: 1.02rem; margin-bottom: 0.25rem; }
.expertise-item-desc{ font-size: 0.92rem; color: var(--color-ink-soft); }

/* Skills & Languages subsection headings (Background page) — reuses
   .cert-tier-label's styling directly via that class in the markup; this
   just adds the spacing between the two stacked subsections. */
.skills-subsection + .skills-subsection{ margin-top: var(--space-lg); }

/* Home page's condensed 4-group version of the expertise list — the full
   list itself lives on the Background page (.expertise-list above). */
.capability-grid{ display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-md); }
.capability-card{ border-top: 2px solid var(--color-steel); padding-top: var(--space-sm); }
.capability-card h3{ font-size: 1.02rem; margin-bottom: 0.4rem; }
.capability-card p{ font-size: 0.92rem; }

.stats-band{ padding: var(--space-md) 0; }
.stats-grid{ display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-sm); }
.stat-item{
  display: flex; flex-direction: column; gap: 0.6rem;
  background: var(--color-surface); border: 1px solid var(--color-line); border-radius: var(--radius);
  padding: var(--space-md);
  transition: transform var(--transition), box-shadow var(--transition), border-color var(--transition);
}
@media (hover: hover){
  .stat-item:hover{ transform: translateY(-3px); box-shadow: var(--shadow-lift); border-color: rgba(217,214,207,0.6); }
}
.stat-number{
  font-family: var(--font-display); font-weight: 600; letter-spacing: -0.02em;
  font-size: clamp(2.2rem, 3vw + 1rem, 3.2rem); line-height: 1; color: var(--color-ink);
}
.stat-number.accent-steel{ color: var(--color-steel); }
.stat-number.accent-green{ color: var(--color-green); }
.stat-plus{ color: inherit; opacity: 0.7; }
.stat-label{ font-size: 0.85rem; color: var(--color-ink-soft); max-width: 20ch; }

/* ============ 8. WORK GRID / FEATURE CARDS ============ */
.work-feature-grid{ display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: var(--space-md); }
.work-feature-card{ display: block; transition: transform var(--transition); }
.work-feature-card .feature-image{
  position: relative; aspect-ratio: 4/3; overflow: hidden; border-radius: var(--radius); margin-bottom: var(--space-sm);
  background: var(--color-line);
  transition: box-shadow var(--transition);
}
.work-feature-card .feature-image img{
  width: 100%; height: 100%; object-fit: cover;
  transform: scale(1.08); transition: transform 900ms cubic-bezier(.16,.84,.44,1);
}
.work-feature-card.is-visible .feature-image img{ transform: scale(1); }
.work-feature-card:hover .feature-image img{ transform: scale(1.03); }
@media (hover: hover){
  .work-feature-card:hover{ transform: translateY(-4px); }
  .work-feature-card:hover .feature-image{ box-shadow: var(--shadow-lift); }
}
.feature-tag{ font-size: 0.78rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--color-steel); font-weight: 600; }
.feature-tag.feature-tag-flagship{ color: var(--color-green); white-space: nowrap; }
.feature-tag.feature-tag-flagship::before{ content: "★ "; }
.work-feature-card h3{ margin-top: 0.5rem; margin-bottom: 0.4rem; }
.work-feature-card p{ margin-bottom: 0.7rem; }

.view-all-row{ margin-top: var(--space-lg); text-align: center; }

/* ============ PROOF OF WORK ============
   Document-shaped thumbnails (portrait, cropped to the top so the actual
   report/certificate heading shows) rather than the wide photo treatment
   used elsewhere, so these read as real paperwork, not portfolio graphics. */
.proof-grid{ display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-md); }
.proof-card{
  display: flex; gap: var(--space-sm); align-items: flex-start;
  background: var(--color-surface); border: 1px solid var(--color-line); border-radius: var(--radius);
  padding: var(--space-sm);
  transition: transform var(--transition), box-shadow var(--transition);
}
@media (hover: hover){
  .proof-card:hover{ transform: translateY(-3px); box-shadow: var(--shadow-lift); }
}
.proof-thumb{
  display: block; flex: none; width: 84px; aspect-ratio: 3/4;
  border-radius: var(--radius-sm); overflow: hidden; border: 1px solid var(--color-line); background: var(--color-bg);
}
.proof-thumb img{ width: 100%; height: 100%; object-fit: cover; object-position: top; transition: transform 400ms ease; }
.proof-card:hover .proof-thumb img{ transform: scale(1.06); }
.proof-card-body h3{ font-size: 1rem; margin: 0.3rem 0 0.35rem; }
.proof-card-body p{ font-size: 0.88rem; }

/* ============ FEATURED PROJECTS (Work page) ============
   .featured-project-primary is the single strongest/current project,
   shown full-width with image and content side by side. Secondary
   projects sit two-up in .featured-project-grid, each stacked
   image-on-top so the narrower column still reads cleanly. */
.featured-projects-kicker{ display: block; margin-bottom: var(--space-md); }

.featured-project{
  background: var(--color-surface); border: 1px solid var(--color-line); border-radius: var(--radius);
  overflow: hidden;
}
.featured-project-primary{
  display: grid; grid-template-columns: 1fr 1fr; align-items: stretch;
  box-shadow: var(--shadow-md);
}
.featured-project-grid{
  display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-md);
  margin-top: var(--space-md);
}
.featured-project-grid .featured-project{ display: flex; flex-direction: column; }

.featured-project-media{ position: relative; overflow: hidden; background: var(--color-line); }
.featured-project-primary .featured-project-media{ min-height: 280px; }
.featured-project-grid .featured-project-media{ aspect-ratio: 16/10; }
.featured-project-media img{
  width: 100%; height: 100%; object-fit: cover;
  transform: scale(1.08); transition: transform 900ms cubic-bezier(.16,.84,.44,1);
}
.featured-project.is-visible .featured-project-media img{ transform: scale(1); }

/* Compact card body: image → tag/name → role/meta → a short description →
   a wrapped row of scope tags → (evidence, primary card only) → a details
   link. Replaces the earlier long vertical responsibility list with a
   much shorter, easier-to-scan hierarchy. */
.featured-project-content{ padding: var(--space-md); display: flex; flex-direction: column; }
.featured-project-grid .featured-project-content{ flex: 1; }
.featured-project-content h3{ margin-top: 0.4rem; margin-bottom: 0.5rem; }
.featured-project-role{ font-weight: 600; color: var(--color-ink); margin-bottom: 0.15rem; }
.featured-project-meta{ font-size: 0.88rem; color: var(--color-ink-faint); margin-bottom: var(--space-sm); }
.featured-project-desc{ color: var(--color-ink-soft); font-size: 0.95rem; margin-bottom: 0.75rem; }
.scope-tags{ display: flex; flex-wrap: wrap; gap: 0.4rem; margin-bottom: var(--space-sm); }
.scope-tag{
  font-size: 0.76rem; padding: 0.3rem 0.7rem; line-height: 1.2;
  background: var(--color-bg); border: 1px solid var(--color-line); border-radius: 999px; color: var(--color-ink-soft);
}
.featured-project-content .text-link{ margin-top: auto; align-self: flex-start; }

/* Project Evidence — deliberately more prominent than a decorative
   thumbnail row: a labelled block with its own hairline, legible
   document-shaped previews (reusing the .proof-thumb aspect/crop logic
   from the Home page's Proof of Work section) and a named caption per
   document so each preview reads as a specific, real piece of evidence. */
.project-evidence{
  margin-top: var(--space-sm); padding-top: var(--space-md); border-top: 1px solid var(--color-line);
}
.project-evidence-label{
  display: inline-flex; align-items: center; gap: 0.55rem;
  font-size: 0.78rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--color-steel); font-weight: 600;
  margin-bottom: 0.85rem;
}
.project-evidence-label::before{ content: ""; display: inline-block; width: 14px; height: 1px; background: var(--color-steel); flex: none; }
.project-evidence-grid{ display: flex; gap: var(--space-sm); }
.project-evidence-item{ display: block; width: 84px; }
.project-evidence-item img{
  display: block; width: 84px; aspect-ratio: 3/4; object-fit: cover; object-position: top;
  border-radius: var(--radius-sm); border: 1px solid var(--color-line);
  transition: transform var(--transition), box-shadow var(--transition);
}
.project-evidence-item span{ display: block; margin-top: 0.5rem; font-size: 0.76rem; line-height: 1.3; color: var(--color-ink-faint); }
@media (hover: hover){ .project-evidence-grid:hover img{ transform: translateY(-2px); box-shadow: var(--shadow-lift); } }

/* ============ EARLIER PROJECT EXPERIENCE (Work page) ============
   Clean text-led entries — none of these three roles has a surviving
   project photo, so there is no image slot at all rather than an
   empty placeholder. A project-entry-image variant is kept for any
   future entry that does have a real photo. */
.project-list{ display: flex; flex-direction: column; }
.project-entry{
  display: flex; gap: var(--space-md);
  padding: var(--space-md) 0; border-bottom: 1px solid var(--color-line);
}
.project-entry:first-child{ border-top: 1px solid var(--color-line); }
.project-entry-image{ flex: none; width: 140px; aspect-ratio: 4/3; border-radius: var(--radius); overflow: hidden; }
.project-entry-image img{ width: 100%; height: 100%; object-fit: cover; }
.project-entry-body{ flex: 1; }
.project-entry-head{ display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 0.5rem; }
.project-entry-head h4{ font-size: 1.08rem; font-weight: 600; }
.project-entry-dates{ font-size: 0.82rem; color: var(--color-ink-faint); white-space: nowrap; }
.project-entry-meta{ font-size: 0.85rem; color: var(--color-ink-faint); margin-top: 0.2rem; margin-bottom: 0.5rem; }
.project-entry-focus{ color: var(--color-ink-soft); }

/* ============ 9. CASE STUDY ============ */
.case-hero{ padding-top: calc(var(--header-h) + var(--space-md)); padding-bottom: var(--space-md); }
.case-breadcrumb{ font-size: 0.88rem; color: var(--color-ink-faint); margin-bottom: var(--space-md); }
.case-breadcrumb a{ color: var(--color-ink-soft); border-bottom: 1px solid var(--color-line); }
.case-breadcrumb a:hover{ color: var(--color-ink); }
.case-meta-row{
  display: flex; flex-wrap: wrap; gap: var(--space-md); margin-top: var(--space-md);
  padding-top: var(--space-md); border-top: 1px solid var(--color-line);
}
.case-meta-item .snapshot-label{ display: block; font-size: 0.78rem; color: var(--color-steel); letter-spacing: 0.06em; text-transform: uppercase; margin-bottom: 0.3rem; }
.case-meta-item .snapshot-value{ font-weight: 600; }
.case-hero-image{ position: relative; margin-top: var(--space-lg); border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow-md); }
/* used for the King Salman Park source photo (1280×332) so it displays near its native
   ultra-wide aspect instead of being upscaled to fill a 16:9 box */
.case-hero-image.aspect-native img{ aspect-ratio: 1280/332; }
.case-hero-image img{
  width: 100%; display: block; aspect-ratio: 16/9; object-fit: cover;
  transform: scale(1.08); transition: transform 900ms cubic-bezier(.16,.84,.44,1);
}
.case-hero-image.is-visible img{ transform: scale(1); }

.case-body{ display: grid; grid-template-columns: 200px 1fr; gap: var(--space-lg); }
.case-body-label{
  display: inline-flex; align-items: center; gap: 0.6rem;
  font-size: 0.8rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--color-steel); font-weight: 600;
  padding-top: 0.3rem; align-self: start; position: sticky; top: calc(var(--header-h) + 1.5rem);
  scroll-margin-top: calc(var(--header-h) + var(--space-md));
}
.case-body-label::before{ content: ""; display: inline-block; width: 14px; height: 1px; background: var(--color-steel); flex: none; }
.case-body-content > * + *{ margin-top: var(--space-sm); }
.case-body-content ul{ list-style: none; display: flex; flex-direction: column; gap: 0.6rem; }
.case-body-content ul li{ position: relative; padding-left: 1.2rem; color: var(--color-ink-soft); }
.case-body-content ul li::before{ content: "—"; position: absolute; left: 0; color: var(--color-steel); }

.case-gallery{ display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-sm); margin-top: var(--space-sm); }

/* Round 40: case-study "stat block" (e.g. King Salman Park's "Infrastructure
   at Scale") — reuses the home page's .stat-item/.stat-number/.stat-label
   look (bordered card, steel-blue accent number) for visual consistency,
   but at 2 columns rather than 4: .case-body-content is a much narrower
   column (200px label + 1fr content, not the full-width home page
   container), and these labels run longer ("Overall development area"
   vs. "Years of Experience"), so 2-up stays comfortably readable at every
   width without a separate mobile override. */
.case-stat-grid{ display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-sm); }
.case-stat-grid .stat-item{ padding: var(--space-sm); }
.case-stat-grid .stat-number{ font-size: clamp(1.6rem, 2.2vw + 1rem, 2.2rem); }
.case-gallery img{ width: 100%; border-radius: var(--radius); border: 1px solid var(--color-line); }
.case-gallery figure{ margin: 0; }
.case-gallery figcaption{ font-size: 0.8rem; color: var(--color-ink-faint); margin-top: 0.4rem; }

.evidence-block{
  background: var(--color-surface); border: 1px solid var(--color-line); border-radius: var(--radius);
  padding: var(--space-md); margin-top: var(--space-sm);
}
.evidence-block .redact-note{ font-size: 0.82rem; color: var(--color-ink-faint); margin-top: var(--space-xs); }

.case-nav{
  display: flex; flex-wrap: nowrap; justify-content: space-between; align-items: center;
  gap: var(--space-sm); padding: var(--space-lg) 0;
  border-top: 1px solid var(--color-line);
}

/* Prev/Next case-study buttons — real buttons (border, padding, pill
   radius) rather than bare underlined links, so they read as
   navigation controls. Both sit in a single row at every width: each
   is a shrinkable flex item with its label truncated by ellipsis
   instead of wrapping, so long project names never force a second
   line. The pair is intentionally asymmetric — Previous stays quiet,
   Next carries more visual weight — so the two are easy to tell apart
   at a glance (the "separation" between them). */
.case-nav-btn{
  display: inline-flex; align-items: center; gap: 0.55rem; min-width: 0; max-width: 100%;
  padding: 0.75rem 1.25rem; border-radius: 999px; border: 1px solid var(--color-line);
  font-weight: 600; font-size: 0.88rem; color: var(--color-ink-soft);
  transition: border-color var(--transition), color var(--transition), background var(--transition), transform var(--transition);
}
.case-nav-btn:hover{ border-color: var(--color-steel); color: var(--color-steel); transform: translateY(-1px); }
.case-nav-prev{ flex: 0 1 auto; }
.case-nav-next{
  flex: 0 1 auto; margin-left: auto;
  color: var(--color-ink); border-color: var(--color-ink);
}
.case-nav-next:hover{ background: var(--color-ink); color: #fff; border-color: var(--color-ink); }
.case-nav-label{
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.case-nav-btn .arrow{ flex: none; }
.case-nav-prev:hover .arrow{ transform: translateX(-3px); }
.case-nav-next:hover .arrow{ transform: translateX(3px); }

/* ============ 10. BACKGROUND / DETAILS CARDS ============ */
.bg-summary p + p{ margin-top: var(--space-sm); }

.bg-timeline{ display: flex; flex-direction: column; position: relative; }
.bg-timeline::before{ content: ""; position: absolute; top: 0; bottom: 0; left: 216px; width: 1px; background: var(--color-line); }
.bg-timeline-item{
  position: relative;
  display: grid; grid-template-columns: 200px 1fr; gap: var(--space-md);
  padding: var(--space-sm) 0; border-bottom: 1px solid var(--color-line);
}
.bg-timeline-item::before{
  content: ""; position: absolute; left: 216px; top: 1.35rem; width: 9px; height: 9px; margin-left: -4px;
  border-radius: 50%; background: var(--color-bg); border: 2px solid var(--color-steel); z-index: 1;
}
.bg-timeline-item.is-current::before{ background: var(--color-steel); }
.bg-timeline-item:first-child{ border-top: 1px solid var(--color-line); }
.bg-timeline-item.is-current{ padding-right: 5.5rem; }
.bg-timeline-dates{ font-size: 0.88rem; color: var(--color-ink-faint); padding-top: 0.15rem; }

/* "Current" status badge — pinned to the row's own top-right corner
   (not inline with the date text, which is how it used to overlap
   "Present") with a continuous animated glow so it reads as a live,
   active status rather than a static label. */
.current-tag{
  position: absolute; top: var(--space-sm); right: 0; z-index: 2;
  display: inline-block; font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.06em;
  color: #fff; padding: 0.3rem 0.7rem; border-radius: 999px; line-height: 1.3;
  background: linear-gradient(115deg, var(--color-steel), #7BB3D6, var(--color-navy), var(--color-steel));
  background-size: 300% 300%;
  animation: current-tag-shift 4s ease-in-out infinite, current-tag-glow 2.4s ease-in-out infinite;
}
@keyframes current-tag-shift{
  0%{ background-position: 0% 50%; }
  50%{ background-position: 100% 50%; }
  100%{ background-position: 0% 50%; }
}
@keyframes current-tag-glow{
  0%, 100%{ box-shadow: 0 2px 6px rgba(20,22,26,0.2), 0 0 0px 0 rgba(62,107,138,0.55); }
  50%{ box-shadow: 0 2px 6px rgba(20,22,26,0.2), 0 0 14px 4px rgba(62,107,138,0.65); }
}
.bg-timeline-role{ font-weight: 600; font-size: 1.05rem; margin-bottom: 0.2rem; }
.bg-timeline-project{ font-size: 0.92rem; color: var(--color-ink-faint); margin-bottom: 0.6rem; }
/* Intro sentence added ahead of each timeline entry's bullets (Background
   page copy pass) — additive rule, doesn't touch the rules above it. */
.bg-timeline-summary{ font-size: 0.95rem; color: var(--color-ink-soft); max-width: 60ch; margin-bottom: 0.6rem; }
.bg-timeline-item ul{ list-style: none; display: flex; flex-direction: column; gap: 0.4rem; }
.bg-timeline-item ul li{ position: relative; padding-left: 1.1rem; font-size: 0.95rem; }
.bg-timeline-item ul li::before{ content: "—"; position: absolute; left: 0; color: var(--color-steel); }

.detail-cards{ display: flex; flex-direction: column; gap: 0.75rem; }
.detail-card{
  background: var(--color-surface); border: 1px solid var(--color-line); border-radius: var(--radius);
  overflow: hidden;
}
.detail-card summary{
  list-style: none; cursor: pointer; padding: var(--space-sm) var(--space-md);
  display: flex; justify-content: space-between; align-items: baseline; gap: var(--space-sm);
  transition: background var(--transition);
}
@media (hover: hover){
  .detail-card summary:hover{ background: var(--color-bg); }
}
.detail-card summary::-webkit-details-marker{ display: none; }
.detail-card summary .dc-year{ font-size: 0.85rem; color: var(--color-steel); font-weight: 600; min-width: 5.5em; }
.detail-card summary .dc-title{ font-weight: 600; flex: 1; }
.detail-card summary .dc-toggle{ color: var(--color-ink-faint); font-size: 1.3rem; line-height: 1; transition: transform var(--transition); }
.detail-card[open] summary .dc-toggle{ transform: rotate(45deg); }
.detail-card-body{ padding: 0 var(--space-md) var(--space-md); }
.detail-card-body p{ margin-bottom: var(--space-sm); }
.detail-gallery{ display: grid; grid-template-columns: repeat(auto-fit, minmax(min(160px, 100%), 1fr)); gap: var(--space-sm); }
.detail-gallery figure{ margin: 0; }
.detail-gallery img{
  width: 100%; height: auto; max-height: 420px; object-fit: contain; background: var(--color-bg);
  border: 1px solid var(--color-line); border-radius: var(--radius);
}
.detail-gallery img.cover{ object-fit: cover; aspect-ratio: 4/3; }
.detail-gallery figcaption{ font-size: 0.78rem; color: var(--color-ink-faint); margin-top: 0.35rem; }

.cert-tier-label{ font-size: 0.8rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--color-ink); font-weight: 600; margin-bottom: var(--space-sm); }
.cert-tier-label-secondary{ margin-top: var(--space-lg); color: var(--color-ink-faint); }
.cert-grid{ display: grid; grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr)); gap: var(--space-md); }
.cert-card{ display: flex; flex-direction: column; gap: 0.5rem; transition: transform var(--transition); }
.cert-thumb{
  aspect-ratio: 4/3; border-radius: var(--radius); border: 1px solid var(--color-line); border-top: 3px solid var(--color-green); overflow: hidden;
  transition: box-shadow var(--transition);
}
@media (hover: hover){
  .cert-card:hover{ transform: translateY(-3px); }
  .cert-card:hover .cert-thumb{ box-shadow: var(--shadow-lift); }
}
.cert-thumb img{ width: 100%; height: 100%; object-fit: cover; object-position: top; }
.cert-card h4{ font-size: 0.98rem; font-weight: 600; }
.cert-org{ font-size: 0.85rem; color: var(--color-green); font-weight: 600; }
.cert-meta{ font-size: 0.8rem; color: var(--color-ink-faint); }

/* A fixed 2-column grid — not auto-fit — so English/Urdu and
   Hindi/Kashmiri are ALWAYS paired on the same row at every viewport
   width. auto-fit let the column count change with container width
   (2 columns here, 3 or 5 there), which broke that pairing and made
   rows land at different, unpredictable positions. A fixed track
   count is what actually guarantees precise, identical alignment. */
.lang-row{
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1.6rem var(--space-lg); margin-top: var(--space-sm);
  max-width: 32rem;
  align-items: start;
}
.lang-item strong{ display: block; font-size: 0.98rem; line-height: 1.3; }
.lang-item span{ display: block; font-size: 0.82rem; color: var(--color-ink-faint); margin-top: 0.2rem; line-height: 1.3; }

.tools-row{ display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem; margin-top: var(--space-sm); }
.tool-chip{
  display: inline-flex; align-items: center; line-height: 1;
  font-size: 0.88rem; padding: 0.6rem 1rem; border: 1px solid var(--color-line); border-radius: 999px; color: var(--color-ink-soft);
}

/* Modal (used for cert enlargement on Background page, optional) */
.modal{ position: fixed; inset: 0; z-index: 300; display: flex; align-items: center; justify-content: center; padding: var(--space-md); }
.modal[hidden]{ display: none; }
.modal-overlay{ position: absolute; inset: 0; background: rgba(20,22,25,0.6); }
.modal-panel{
  position: relative; z-index: 1; background: var(--color-surface); border-radius: var(--radius);
  max-width: 720px; width: 100%; max-height: 88vh; overflow-y: auto; padding: var(--space-lg); box-shadow: var(--shadow-md);
}
.modal-close{ position: absolute; top: 14px; right: 14px; background: none; border: none; font-size: 1.6rem; line-height: 1; color: var(--color-ink-soft); }
.modal-body{ margin-top: var(--space-sm); }
.modal-body img{ width: 100%; border-radius: var(--radius); }

/* ============ 10.5 ROUND 30 ADDITIONS ============ */

/* Item 6/8a: work.html's "Selected Project Experience" heading and its
   "Earlier Project Experience" lede both need to stay on one line at
   every width. Each gets its own tuned class rather than reusing the
   shared .nowrap-heading (already tuned for a different, shorter
   heading on background.html — resizing it here would move that one
   too, outside this round's scope). */
.work-page-title{ white-space: nowrap; }
@media (max-width: 640px){
  .work-page-title{ font-size: clamp(1rem, 5.6vw + 0.05rem, 1.3rem); }
}

/* Large but light (regular weight, not the heading's 600) display
   treatment. Was a forced-nowrap single line, sized against the short
   "Earlier stage projects of Haris Dawood's career" text. Round 38 note:
   that text is now ~2x longer — "Earlier roles that built Haris Dawood's
   foundation in construction quality and site engineering." — so on
   mobile (≤860px) nowrap was overflowing this element's own narrow
   column and getting silently clipped by the sitewide overflow-x:hidden
   safety net (invisible truncated text, not a scrollbar). Fixed by
   moving the forced nowrap into the desktop-only override below,
   scoped to respect the round-36 desktop lock: desktop keeps the exact
   same single-line-that-overflows-into-adjacent-whitespace rendering it
   always had (harmless there — the page is wide enough that the
   overflow never reaches the viewport edge), while mobile now wraps
   normally, same as .lede, instead of clipping. */
.earlier-projects-lede{
  font-family: var(--font-display);
  font-weight: 400;
  letter-spacing: -0.01em;
  color: var(--color-ink);
  /* Solved against the real rendered text width (not just guessed), the
     same way the hero's own mobile formulas were — at every width from
     320px up this keeps the line's actual pixel width comfortably inside
     the container's available width, right up to where the 1.3rem cap
     takes over (~486px), which still leaves ample margin against every
     wider container this line ever sits in. */
  font-size: clamp(0.8rem, 4.6vw - 0.1rem, 1.3rem);
}
@media (min-width: 861px){
  .earlier-projects-lede{ white-space: nowrap; }
}

/* Item 7: work.html's desktop-only avatar, top-right, aligned to the exact
   same left/right band the floating nav pill occupies — the same
   width: calc(100% - 2*space-md); max-width: calc(max-width - 40px)
   formula .navbar and .hero-cover already share, so it lines up with the
   nav pixel-for-pixel rather than approximately. Hidden entirely on
   mobile — there's no empty top-right space to fill there, and the
   mobile hero already carries its own avatar. */
.work-page-head{ position: relative; }
.work-avatar-align{ display: none; }
@media (min-width: 861px){
  .work-avatar-align{
    display: block;
    position: absolute; top: calc(var(--header-h) + var(--space-sm));
    left: 0; right: 0;
    width: calc(100% - 2 * var(--space-md));
    max-width: calc(var(--max-width) - 40px);
    margin: 0 auto;
    height: 0;
    pointer-events: none;
  }
  .work-avatar{
    /* 2.5x the size the client actually saw on screen, per explicit
       request (round 31). What he saw was already shrunk to 80% by the
       (now-removed) broken zoom rule — clamp(84px,8vw,128px) * 0.8 — so
       the 2.5x is applied to THAT rendered size: 84*0.8*2.5=168,
       8*0.8*2.5=16, 128*0.8*2.5=256. */
    display: block; position: absolute; top: 0; right: var(--space-md);
    width: clamp(168px, 16vw, 256px); height: clamp(168px, 16vw, 256px);
    border-radius: 50%; overflow: hidden;
    border: 4px solid var(--color-surface);
    box-shadow: 0 0 0 1px var(--color-line), var(--shadow-md);
    pointer-events: auto;
  }
  /* Same crop as the home page's own avatar — object-position: center top
     keeps hair fully in frame instead of cropping into it. */
  .work-avatar img{ width: 100%; height: 100%; object-fit: cover; object-position: center top; }

  /* FIXED (round 31): at 2.5x its original size the avatar can reach up
     to 256px — measured and confirmed this collides with the heading
     text column (.section-head's normal 620px max-width) both at the
     narrowest desktop width where the avatar first appears (861px) and
     at very wide desktop widths, once .container and the avatar's own
     nav-width-matching wrapper both hit their max-width caps and the
     gap between the heading and the right edge narrows. Capping the
     heading column at 560px specifically on this page's header (not
     .section-head sitewide) leaves a verified clear gap at every width
     from 861px up, without shrinking the avatar back down. */
  .work-page-head .section-head{ max-width: 560px; }
}

/* Item 9: background.html's avatar, centred (mobile and desktop alike)
   and sitting above the Professional Summary heading/text rather than
   beside it, same crop treatment as the home page avatar. img is
   block-level by default (global reset), so margin:0 auto centres it
   directly — no extra wrapper needed. */
.bg-avatar{
  display: block; margin: 0 auto var(--space-md);
  width: 132px; height: 132px;
  border-radius: 50%; overflow: hidden;
  object-fit: cover; object-position: center top;
  border: 4px solid var(--color-surface);
  box-shadow: 0 0 0 1px var(--color-line), var(--shadow-md);
}

/* Item 13: centre the Skills & Languages section as a whole — heading,
   the tool-chip row and the language grid. Grid items themselves keep
   left-aligned text (a centred name/level pair reads worse than a
   centred block of left-aligned pairs). */
.skills-container{ text-align: center; }
.skills-container .section-head{ margin-left: auto; margin-right: auto; }
.skills-container .tools-row{ justify-content: center; }
/* FIXED (round 31): the client pointed out the language grid still
   wasn't centred like the tools row above it, and asked for it as a
   clean single "column bar" — a tight horizontal row of items sitting
   next to each other, not a wide 2-column grid with large gaps between
   only two items per row. The base .lang-row is a fixed 2-column GRID
   (deliberately, elsewhere on the site, to keep English/Urdu and
   Hindi/Kashmiri paired) — inside .skills-container specifically, that's
   overridden to a centred flex row instead, matching .tools-row's own
   look exactly: every language sits inline next to the others, wrapping
   only if the row runs out of width, with the whole row centred as a
   group. */
.skills-container .lang-row{
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: 1.6rem var(--space-lg);
  max-width: none;
  margin: var(--space-sm) auto 0;
  text-align: left;
}

/* Item 1: the client asked for the whole desktop view, every page, to
   render about 20% smaller.
   FIXED (round 31): the first version of this used CSS `zoom: 0.8` on
   body. `zoom` is not a real layout property — on Chrome/Edge it behaves
   like an implicit `transform: scale()` for hit-testing purposes on some
   engine versions, which is exactly what broke the nav: a `transform`
   (real or effective) on an ancestor creates a NEW containing block for
   `position:fixed` descendants, so the fixed nav pill (and the JS custom
   cursor, which places its dot/ring with raw un-zoomed
   `clientX`/`clientY`) stopped tracking the real viewport the way
   `position:fixed` normally does — the pill kept rendering roughly where
   expected, but the browser's actual click hit-testing and the cursor's
   own coordinate math both drifted out of sync with it, so clicks landed
   on the wrong element (or nothing) and the custom cursor visibly
   detached from the real pointer. `zoom` also changes what the browser
   uses as the *effective* viewport width for `vw` units and can affect
   media-query evaluation on some engines — exactly the kind of
   viewport-identity confusion that produces this class of bug.
   This replacement never touches the viewport, the coordinate system, or
   any containing-block behaviour — it only changes what 1rem resolves
   to. Nearly everything sitewide (every --fs-, --space- and --hero-
   custom property, and most component rules) is already sized in rem,
   so shrinking the
   root font-size shrinks all of it together automatically and
   proportionally, with zero effect on click hit-testing, fixed
   positioning, or the custom cursor's coordinate math — those all still
   operate in real, unscaled CSS pixels. The handful of structural
   values that were hardcoded in raw px (not rem) are scaled by the same
   0.8 factor explicitly just below, so they stay in proportion with
   everything else. Left at 100% on mobile — not part of this request
   there. */
@media (min-width: 861px){
  html{ font-size: 80%; }
  :root{
    --header-h: 67.2px;             /* 84px   * 0.8 */
    --max-width: 896px;             /* 1120px * 0.8 */
    --max-width-narrow: 576px;      /* 720px  * 0.8 */
    --radius: 11.2px;               /* 14px   * 0.8 */
    --radius-sm: 7.2px;             /* 9px    * 0.8 */
    --hero-avatar-size: clamp(112px, 17.6vw, 256px); /* 140px/22vw/320px * 0.8 */
  }
  .navbar{ height: 49.6px; }        /* 62px * 0.8 */
  .bg-avatar{ width: 105.6px; height: 105.6px; } /* 132px * 0.8 */
}

/* ============ 10.6 ROUND 32 ADDITIONS ============ */

/* Item A: originally a fixed 5-column grid built for 5 languages. Now that
   the list holds 3 (English/Hindi/Arabic — round 51), a fixed 5-column grid
   packs those 3 items into the first 3 columns, left-aligned rather than
   centred as a group. FIXED (round 52): centred flex row instead, matching
   the desktop rule above (.skills-container .lang-row with no media query)
   and wrapping cleanly regardless of how many languages are listed. Font
   sizes stay cut below the desktop defaults so mobile columns/rows still
   read comfortably at small widths; the two longer level labels ("Limited
   Working") are still allowed to wrap to a second line within their own
   item rather than being forced to shrink illegibly small or overflow. */
@media (max-width: 860px){
  .skills-container .lang-row{
    display: flex; flex-wrap: wrap; justify-content: center;
    gap: 0.9rem 1.1rem; max-width: none; text-align: center;
  }
  .skills-container .lang-item strong{
    font-size: clamp(0.56rem, 1.7vw + 0.28rem, 0.82rem);
    line-height: 1.2;
  }
  .skills-container .lang-item span{
    font-size: clamp(0.48rem, 1.25vw + 0.22rem, 0.68rem);
    line-height: 1.25; margin-top: 0.2rem;
  }
}

/* Item B (rounds 32-33, superseded below): work.html's desktop avatar
   (.work-avatar-align/.work-avatar, positioned absolute) stays hidden below
   861px — unchanged, still exactly what round 30 shipped. Rounds 32/33 had
   reused this same absolutely-positioned element on mobile too, sized to
   whatever gap was left beside the nowrap heading text (a hard geometric
   ceiling of ~44-76px, since that heading deliberately never wraps and, at
   ordinary phone widths, runs almost the full row).

   FIXED (round 34): the client asked for the mobile avatar to be roughly
   2.5x bigger still — genuinely not possible beside a full-width one-line
   heading without covering the text. Rather than shrinking the heading's
   own type size to manufacture room (which would have made it read as an
   afterthought), this drops the absolute-overlay approach on mobile
   entirely in favor of a real in-flow float: a SEPARATE mobile-only avatar
   element (.work-avatar-mobile, new markup in work.html, hidden on desktop)
   sits inside .section-head as its first child and floats right, with the
   heading's forced single-line treatment turned off *for mobile only*
   (.work-page-title{ white-space: normal }) so "Selected Project
   Experience" wraps around it exactly the way text wraps around a floated
   image — the browser guarantees zero overlap by construction, so the
   avatar can be sized freely rather than against a fragile, hand-measured
   ceiling. The lede paragraph gets `clear: right` so it always starts
   below the avatar's bottom rather than wrapping alongside it too, keeping
   the earlier "avatar bottom sits above the description" behavior intact.
   .section-head gets `display: flow-root` (mobile only) so its own box
   properly contains the float's full height, rather than letting it spill
   into the Featured Projects content below.
   The desktop-only .work-avatar-align/.work-avatar block above is
   completely untouched by any of this — a separate element, so none of
   this touches its positioning, which the client confirmed is fine as-is. */
.work-avatar-mobile{ display: none; }
@media (max-width: 860px){
  .work-avatar-mobile{
    display: block;
    float: right;
    /* Pulls the float flush with the mobile nav pill's own right edge
       (width: calc(100% - 1.4rem), centred — set in the max-width:860px
       block above) rather than the container's own, wider right padding:
       0.7rem is half of that 1.4rem inset, so subtracting it from the
       container's own right padding (--space-md here) is exactly the gap
       still needed to reach the nav's true right edge. */
    margin: 0 calc(-1 * (var(--space-md) - 0.7rem)) 0.6rem 1rem;
    width: clamp(96px, 24vw, 152px);
    height: clamp(96px, 24vw, 152px);
    border-radius: 50%; overflow: hidden;
    border: 3px solid var(--color-surface);
    box-shadow: 0 0 0 1px var(--color-line), var(--shadow-md);
  }
  .work-avatar-mobile img{ width: 100%; height: 100%; object-fit: cover; object-position: center top; display: block; }
  /* .section-head's own default max-width:620px (line ~624) is what the
     float is actually contained/right-aligned against (a float's
     containing block is its nearest block ancestor's content box, not the
     wider .container around it) — past ~684px viewport width that 620px
     cap kicks in and narrows .section-head below .container's own width,
     which would pull the float's right edge inward with it and off the
     nav's right edge. Neutralised here (mobile, this page only) so
     .section-head always spans the full container width, matching what
     the margin math above assumes. */
  .work-page-head .section-head{ display: flow-root; max-width: none; }
  .work-page-title{ white-space: normal; }
  .work-page-head .lede{ clear: right; }
}
@media (max-width: 640px){
  /* Container padding drops to 1.1rem here (see the max-width:640px block
     below), so the same right-edge-matching math above needs its own,
     smaller subtraction: 1.1rem - 0.7rem instead of --space-md - 0.7rem. */
  .work-avatar-mobile{ margin-right: -0.4rem; }
}

/* ============ 11. RESPONSIVE ============ */
@media (max-width: 1024px){
  .current-project-inner{ grid-template-columns: 1fr; gap: var(--space-md); }
  .current-project-photo img{ aspect-ratio: 16/9; }
  .work-feature-grid{ grid-template-columns: 1fr; }
  .featured-project-primary{ grid-template-columns: 1fr; }
  .featured-project-primary .featured-project-media{ min-height: 0; aspect-ratio: 16/9; }
  .featured-project-grid{ grid-template-columns: 1fr; }
  .case-body{ grid-template-columns: 1fr; }
  .case-body-label{ position: static; }
  .case-gallery{ grid-template-columns: 1fr 1fr; }
  .contact-grid{ grid-template-columns: 1fr; }
}

/* Item 14: contact.html, desktop only (the 2-column contact-grid only
   exists at 1025px+, one pixel above the max-width:1024px collapse just
   above) — stretch the two grid columns to equal height, then let the
   three channel cards distribute across that full height with
   space-between instead of a fixed gap. That automatically makes the
   two card-to-card gaps equal to each other (space-between always splits
   remaining space evenly) AND lands the last (LinkedIn) card's bottom
   exactly on the form column's bottom — both requirements solved by the
   same rule, with no manual pixel math to drift out of sync later. */
@media (min-width: 1025px){
  .contact-grid{ align-items: stretch; }
  .contact-channels{ justify-content: space-between; gap: 0; }
}

@media (max-width: 860px){
  /* Same nav bar as desktop, just tightened up to fit and anchored to the
     bottom of the screen instead of the top — the client's preferred
     mobile position from the earlier bottom-tab-bar layout, kept for the
     floating pill too: narrower side margins/padding, smaller wordmark
     and link type, and the full "Download CV" button swapped for its
     icon-only equivalent (see .navbar-cv-icon above). If a very narrow
     phone still can't fit all four links, .nav-links scrolls horizontally
     rather than wrapping or overflowing the page. */
  .site-header{ top: auto; bottom: 0; }
  .navbar{ --navbar-pad-left: 0.8rem; width: calc(100% - 1.4rem); padding: 0 0.3rem 0 var(--navbar-pad-left); gap: 0.4rem; box-shadow: 0 -10px 28px -16px rgba(20,22,26,0.14); }
  .site-header.scrolled .navbar{ box-shadow: 0 -16px 34px -14px rgba(20,22,26,0.22); }
  .wordmark{ font-size: 0.82rem; padding-right: 0.8rem; }
  .nav-menu{ gap: 0.4rem; }
  .nav-links{ gap: 0.45rem; }
  .nav-links li:not(:last-child)::after{ right: -0.225rem; height: 11px; }
  .nav-link{ font-size: 0.7rem; }
  .btn-nav{ display: none; }
  .navbar-cv-icon{ width: 36px; height: 36px; }
  .navbar-cv-icon svg{ width: 16px; height: 16px; }

  /* The fixed nav now sits at the bottom instead of the top, so pages no
     longer need top clearance for it (see .page-head-pad/.hero/.case-hero
     overrides below) — but they do need bottom clearance so the last
     section and the back-to-top button don't sit underneath the pill. */
  body{ padding-bottom: calc(var(--header-h) + env(safe-area-inset-bottom)); }
  .back-nav{ bottom: calc(var(--header-h) + 16px + env(safe-area-inset-bottom)); }
  .page-head-pad{ padding-top: var(--space-lg); }
  .hero{ padding-top: 0; }
  .case-hero{ padding-top: var(--space-md); }

  /* Same profile banner + avatar as desktop (base rules above), but
     full-bleed edge-to-edge again (not inset to the nav's own width —
     that alignment is a desktop-specific fix), sharp corners to match,
     and a smaller, fixed avatar size so it stays proportional to the
     much shorter mobile banner instead of the desktop clamp's larger
     end. The avatar's own overlap (--hero-avatar-overlap, set to 0.6 —
     40% in / 60% out — a few lines below, up from the desktop 35% out)
     still scales automatically from this smaller size. .hero-cover's own
     margin needs its own copy of the "name-bottom = avatar-bottom" calc
     from the base rule though, since the eyebrow's font-size is smaller
     here (0.72rem, set further down in this same block) than the 0.85rem
     the desktop version assumes. */
  .hero-cover-media{ border-radius: 0; }
  /* At the 0.6 overlap ratio the avatar hangs low enough that its own
     widest point sits in the same vertical band as the tagline/name text
     (needed so the name's bottom can land exactly on the avatar's own
     bottom) — and unconstrained, the tagline's full single-line width
     reaches into the avatar's own horizontal footprint at the widths
     below, visually overlapping the photo (verified: caught this in a
     screenshot before shipping, not left in). Capping their width to stop
     short of the avatar's left edge fixes it. */
  .hero-eyebrow, .hero-title{
    max-width: calc(100% - var(--hero-avatar-size) - var(--space-sm) - var(--space-xs));
  }
  :root{
    /* Responsive, not the old fixed 108px. Tuned (by direct measurement,
       not guesswork) so the avatar is exactly as large as it can be while
       still leaving the tagline enough width to stay on one line, at every
       width where a single line is geometrically possible at all — see
       the note on the three .hero-cover margin rules below for the widths
       where it plainly isn't. */
    --hero-avatar-size: clamp(64px, 17.8vw + 12.9px, 108px);
    /* Shared between .hero-eyebrow's own font-size and the .hero-cover
       margin calc below, same reasoning as --hero-title-fs above. */
    --hero-eyebrow-fs: clamp(9.5px, 3.7vw - 5.3px, 11.52px);
    /* 40% in / 60% out on mobile (was 35% out, matching desktop) — per the
       client's own request, giving the tagline+name block enough room in
       the protrusion band to reach the avatar's bottom edge exactly,
       without dipping into the banner's own artwork. */
    --hero-avatar-overlap: 0.6;
  }
  .hero-avatar{ right: var(--space-sm); }

  /* Small, size-matched letter-spacing (not the old flat 0.07em) — at the
     smaller end of --hero-eyebrow-fs above, 0.07em was costing just enough
     extra width to force a second line one breakpoint earlier than
     necessary. */
  .hero-eyebrow{ font-size: var(--hero-eyebrow-fs); letter-spacing: 0.045em; }

  /* --- .hero-cover's bottom margin: the name-bottom = avatar-bottom exact
     match, in THREE width bands ------------------------------------------
     The margin formula (protrusion minus the tagline+name block's own
     height) only comes out exact if it uses the block's REAL rendered
     height, i.e. it has to know how many lines the tagline and the name
     are each actually wrapping to at a given width — verified pixel-by-
     pixel in Playwright (getBoundingClientRect, binary search per element),
     not assumed or rounded to a nice breakpoint number:
       - 372px and up: both the tagline and the name fit on one line, so
         the single-line formula (same shape as the desktop rule) applies
         and the match is exact.
       - 362-371px: the tagline wraps to a second line, the name still
         fits on one — a distinct formula accounts for the tagline's extra
         line so the match stays exact here too.
       - 361px and below: both wrap to two lines. A brute-force search
         over avatar size, font size and letter-spacing at every width
         down to 320px confirmed there is no combination that keeps the
         tagline on one line here without an illegibly small font or a
         barely-visible avatar — a hard geometric limit of the phone-
         width/text-length numbers involved, not a CSS gap. Worse, once
         the name ALSO wraps to two lines, its own two-line height exceeds
         what any reasonably-sized avatar can protrude to match — so
         below 361px the formula still uses the correct (2-line) height,
         but the max(0px, ...) floor is genuinely reached and a small,
         disclosed residual gap remains between the name's bottom and the
         avatar's bottom, rather than ever overlapping the banner artwork
         to force a false match. */
  .hero-cover{
    width: 100%; max-width: none;
  }
  @media (min-width: 372px){
    .hero-cover{
      margin: 0 auto max(0px, calc(var(--hero-avatar-size) * var(--hero-avatar-overlap) - (var(--hero-eyebrow-fs) * 1.3) - var(--space-2xs) - (var(--hero-title-fs) * 1.04)));
    }
  }
  @media (max-width: 371px) and (min-width: 362px){
    /* Tagline wraps to 2 lines here; the name still fits on one. */
    .hero-cover{
      margin: 0 auto max(0px, calc(var(--hero-avatar-size) * var(--hero-avatar-overlap) - (var(--hero-eyebrow-fs) * 1.3 * 2) - var(--space-2xs) - (var(--hero-title-fs) * 1.04)));
    }
  }
  @media (max-width: 361px){
    /* Narrowest phones: both the tagline and the name wrap to 2 lines. */
    .hero-cover{
      margin: 0 auto max(0px, calc(var(--hero-avatar-size) * var(--hero-avatar-overlap) - (var(--hero-eyebrow-fs) * 1.3 * 2) - var(--space-2xs) - (var(--hero-title-fs) * 1.04 * 2)));
    }
  }

  .expertise-list{ columns: 1; }
  .expertise-group-items{ grid-template-columns: 1fr; }
  .capability-grid{ grid-template-columns: repeat(2, 1fr); }
  .stats-grid{ grid-template-columns: repeat(2, 1fr); row-gap: var(--space-sm); }
}

@media (max-width: 640px){
  :root{ --space-xl: 4rem; --header-h: 68px; }
  .container, .container-narrow{ padding: 0 1.1rem; }
  .hero-actions{ flex-direction: column; align-items: stretch; }
  .hero-actions .btn{ width: 100%; }
  .capability-grid{ grid-template-columns: 1fr; }
  .proof-grid{ grid-template-columns: 1fr; }
  .project-entry{ flex-direction: column; }
  .project-entry-image{ width: 100%; max-width: 220px; }
  .form-row{ grid-template-columns: 1fr; }
  .bg-timeline::before, .bg-timeline-item::before{ display: none; }
  .bg-timeline-item{ grid-template-columns: 1fr; gap: 0.5rem; }
  .case-gallery{ grid-template-columns: 1fr; }
  .detail-card summary{ flex-wrap: wrap; }
  .detail-card summary .dc-year{ min-width: auto; }
  .case-nav-btn{ padding: 0.65rem 0.9rem; font-size: 0.82rem; gap: 0.4rem; }
  .nowrap-heading{ font-size: 1.3rem; }
  .case-nav-label{ max-width: 34vw; }

  /* Item 4b: Yamhama Palace only — dates move below the project name
     instead of sitting top-right, in mobile layout only. Other entries
     (Navi Mumbai, Kishanganga) keep the default top-right placement. */
  .project-entry-head-stack-mobile{
    flex-direction: column; align-items: flex-start; gap: 0.15rem;
  }
}

@media (max-width: 480px){
  .stat-item{ padding: var(--space-sm); }
  .stat-number{ font-size: clamp(1.8rem, 5vw + 1rem, 2.6rem); }
  .proof-card{ flex-direction: column; }
  .proof-thumb{ width: 100%; aspect-ratio: 16/9; }
}

/* ============ 12. ANIMATIONS & ACCESSIBILITY ============ */
.reveal{ opacity: 0; transform: translateY(14px); transition: opacity 700ms ease, transform 700ms ease; }
.reveal.is-visible{ opacity: 1; transform: translateY(0); }

a:focus-visible, button:focus-visible, input:focus-visible{ outline: 2px solid var(--color-steel); outline-offset: 2px; }

/* ============ 13. AWARD-LEVEL POLISH ============
   Curtain-wipe image reveals + corner-bracket framing, split-text
   headline reveal, custom cursor, blueprint-grid texture, scroll
   progress, magnetic buttons, page transitions, count-up stats.
   Every effect here is skipped for prefers-reduced-motion and (where
   relevant) for touch/coarse-pointer devices. */

/* -- curtain-wipe over the four "hero-weight" image slots -- */
.hero-photo::before, .case-hero-image::before,
.feature-image::before, .featured-project-media::before{
  content: ""; position: absolute; inset: 0; z-index: 2;
  background: var(--color-bg);
  transform-origin: right center; transform: scaleX(1);
  transition: transform 700ms cubic-bezier(.65,0,.35,1);
}
.hero-photo.is-visible::before, .case-hero-image.is-visible::before,
.work-feature-card.is-visible .feature-image::before,
.featured-project.is-visible .featured-project-media::before{ transform: scaleX(0); }

/* -- corner-bracket "registration mark" framing, layered on the same slots -- */
.hero-photo::after, .case-hero-image::after,
.feature-image::after, .featured-project-media::after{
  content: ""; position: absolute; inset: 10px; z-index: 3; pointer-events: none;
  background:
    linear-gradient(var(--color-ink), var(--color-ink)) top left,
    linear-gradient(var(--color-ink), var(--color-ink)) top left,
    linear-gradient(var(--color-ink), var(--color-ink)) top right,
    linear-gradient(var(--color-ink), var(--color-ink)) top right,
    linear-gradient(var(--color-ink), var(--color-ink)) bottom left,
    linear-gradient(var(--color-ink), var(--color-ink)) bottom left,
    linear-gradient(var(--color-ink), var(--color-ink)) bottom right,
    linear-gradient(var(--color-ink), var(--color-ink)) bottom right;
  background-size: 2px 16px, 16px 2px, 2px 16px, 16px 2px, 2px 16px, 16px 2px, 2px 16px, 16px 2px;
  background-repeat: no-repeat;
  opacity: 0; transition: opacity 500ms ease 220ms;
}
.hero-photo.is-visible::after, .case-hero-image.is-visible::after,
.work-feature-card.is-visible .feature-image::after,
.featured-project.is-visible .featured-project-media::after{ opacity: 1; }

/* -- split-text word-by-word reveal (applied by script.js to h1 / section headings) -- */
.split-word{
  display: inline-block; opacity: 0; transform: translateY(0.5em);
  transition: opacity 640ms cubic-bezier(.2,.7,.2,1), transform 640ms cubic-bezier(.2,.7,.2,1);
  transition-delay: calc(var(--i, 0) * 45ms);
  will-change: transform, opacity;
}
.is-visible .split-word{ opacity: 1; transform: translateY(0); }

/* -- custom cursor (desktop, fine-pointer only — created by script.js) -- */
.cursor-dot, .cursor-ring{ position: fixed; top: 0; left: 0; border-radius: 50%; pointer-events: none; z-index: 999; opacity: 0; }
.cursor-dot{ width: 6px; height: 6px; background: var(--color-navy); transition: opacity 200ms ease; }
.cursor-ring{
  width: 34px; height: 34px; border: 1px solid var(--color-ink-faint);
  transition: opacity 200ms ease, width 260ms ease, height 260ms ease, border-color 260ms ease, background 260ms ease;
}
body.cursor-ready .cursor-dot, body.cursor-ready .cursor-ring{ opacity: 1; }
body.cursor-ready.cursor-hover .cursor-ring{ width: 56px; height: 56px; border-color: var(--color-steel); background: var(--color-steel-soft); }
@media (hover: hover) and (pointer: fine){ body.cursor-ready, body.cursor-ready a, body.cursor-ready button, body.cursor-ready summary{ cursor: none; } }

/* -- scroll progress bar (created by script.js) -- */
.scroll-progress{ position: fixed; top: 0; left: 0; height: 2.5px; width: 0%; background: var(--color-steel); z-index: 150; }

/* -- magnetic buttons: script.js sets an inline transform on mousemove; this just keeps it smooth on release -- */
.btn{ will-change: transform; }

/* -- full-page transition curtain (markup present on every page; script.js only toggles .leaving) -- */
.page-transition{
  position: fixed; inset: 0; z-index: 500; pointer-events: none;
  background: var(--color-bg);
  animation: page-reveal 800ms 60ms cubic-bezier(.65,0,.35,1) both;
}
@keyframes page-reveal{ from{ transform: translateY(0%); } to{ transform: translateY(-100%); } }
.page-transition.leaving{
  animation: none; transform: translateY(0%);
  transition: transform 450ms cubic-bezier(.65,0,.35,1);
}

@media (prefers-reduced-motion: reduce){
  *{ animation-duration: 0.001ms !important; animation-iteration-count: 1 !important; transition-duration: 0.001ms !important; }
  .reveal{ opacity: 1; transform: none; }
  .hero-photo::before, .case-hero-image::before, .feature-image::before, .featured-project-media::before{ display: none; }
  .hero-photo::after, .case-hero-image::after, .feature-image::after, .featured-project-media::after{ opacity: 1; transition: none; }
  .cursor-dot, .cursor-ring{ display: none; }
}

/* =========================================================
   ROUND 44 — MOBILE SPACING TIGHTENING (confirmed, now locked)
   Trialled, reviewed on-device, and confirmed by the client — this is
   now part of the permanent locked state (see round 44 in the decision
   log). To roll back to the pre-round-44 state, restore style.css from
   .locked-final-baseline/ (the round-43 snapshot, kept as a historical
   reference). No HTML/markup changed for this; no desktop rule touched
   (desktop stays locked, verified pixel-identical at every desktop
   width down to 861px); no card-internal padding touched (card style
   preserved) — only the GAPS between sections/blocks, on mobile only
   (≤860px / ≤640px).

   Root cause being addressed: desktop already gets every --space-*
   token scaled down ~20% via the html{font-size:80%} rule at 861px+
   (10.6 ROUND 32 ADDITIONS above). Mobile never got an equivalent
   reduction, so several section-level gaps render at FULL, un-scaled
   size on phones — often larger in absolute px than on desktop despite
   the much narrower screen. Separately, a few grids that are 2-column
   (or wrap) on desktop collapse to a single column on mobile, and their
   `gap` (meant as a column-gap between side-by-side content) becomes a
   large vertical gap between stacked items that were never meant to sit
   that far apart — most visible as a case-study page's "THE PROJECT"
   label floating 64px above its own paragraph. */
@media (max-width: 860px){
  /* Section-level rhythm — halves the least-necessary gaps, same
     reduction .section-tight.section-sep already applies as its own
     "tight" variant (space-lg → space-md), just extended consistently. */
  .section{ padding: var(--space-lg) 0; }             /* was --space-xl (112px) → 56px */
  .section-tight{ padding: var(--space-md) 0; }        /* was --space-lg (56px) → 32px */
  .section-head{ margin-bottom: var(--space-md); }     /* was --space-lg (56px) → 32px */
  .page-head-pad{ padding-top: var(--space-md); }      /* was --space-lg (56px) → 32px; no fixed top nav to clear on mobile */
  .case-hero-image{ margin-top: var(--space-md); }     /* was --space-lg (56px) → 32px */
  .cert-tier-label-secondary{ margin-top: var(--space-md); } /* was --space-lg (56px) → 32px */

  /* Round 41 additions (Background page) — used --space-lg for their
     own gaps without a mobile override at the time; same reduction. */
  .expertise-groups{ gap: var(--space-md); }           /* was --space-lg (56px) → 32px, between the 4 groups */
  .skills-subsection + .skills-subsection{ margin-top: var(--space-md); } /* was --space-lg (56px) → 32px */
}

@media (max-width: 860px){
  /* .case-body and .case-meta-row already collapse to a single column /
     wrapped stack starting at max-width:1024px (rules above, ~line
     1330) — narrower than that would technically reach into the
     861-1024px range the desktop lock covers, even though those specific
     components already render single-column there. Kept strictly at
     ≤860px instead (the same boundary every other mobile-only rule in
     this file uses) so the desktop lock stays untouched at every tested
     desktop width, 861px included, with zero exceptions. Give the
     now-vertical stacking its own smaller row-gap instead of inheriting
     the column-gap meant for desktop's side-by-side layout — this is the
     fix for the "label floating 64px above its own paragraph" effect.
     Column-gap is left alone (irrelevant once collapsed to 1 column). */
  .case-body{ row-gap: 28px; }                         /* was --space-lg (56px) between every stacked pair */
  .case-meta-row{ gap: var(--space-sm) var(--space-md); } /* row-gap 20px (was 32px), column-gap unchanged */
  .contact-grid{ row-gap: var(--space-md); }           /* was --space-lg (56px) between channels and form */
}

@media (max-width: 640px){
  /* Same row/column-gap split as above, for the home page's 4 stacked
     Expertise cards (already single-column at this breakpoint). */
  .capability-grid{ row-gap: var(--space-sm); }        /* was --space-md (32px) between stacked cards */
}
