/* ============================================================
   Ibrahim Abiade — Portfolio Site
   Design tokens + base + components
   ============================================================ */

@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,500;0,600;0,700;0,800;1,500;1,600;1,700&display=swap');

:root {
  /* ---- Color: "ops console" palette ----
     Deep navy ink for authority/control, warm paper background
     (kraft-label warmth, not sterile SaaS white), copper accent
     drawn from shipping-label orange — distinct from Shopify green
     and from generic terracotta. */
  --bg: #0B0F1C;
  --surface: #1B2740;
  --surface-sunken: #080A12;
  --ink: #F3EFE5;
  --ink-soft: #C7CCDA;
  --ink-muted: #A9B0C2;
  --line: rgba(243, 239, 229, 0.14);
  --line-strong: rgba(243, 239, 229, 0.26);

  --accent: #D97B2E;
  --accent-ink: #FFFFFF;
  --accent-soft: #FBE9D6;

  --navy: #10192E;
  --navy-soft: #1B2740;
  --on-navy: #F3EFE5;
  --on-navy-muted: #A9B0C2;

  /* Merges the site's three brand colors — navy, copper accent, warm
     paper cream — into one gradient for the "dark" sections (stat
     strip, section-dark, CTA panel), instead of a flat navy fill.
     Stays dark enough throughout for --on-navy text to stay legible:
     the copper/cream stops are blended toward navy, not used at full
     strength, and the cream only appears as a small corner glint. */
  --brand-gradient:
    radial-gradient(ellipse 520px 320px at 94% 0%, rgba(250, 247, 241, 0.10), transparent 70%),
    linear-gradient(135deg, #10192E 0%, #10192E 36%, #5A3D28 50%, #10192E 64%, #10192E 100%);

  /* The homepage hero's light gradient — copper (sculpture's own glow
     color) + sky-blue (sculpture ring shadows) over the cream page
     background. Reused as-is on the page-hero banners of Services,
     Results, and About for a consistent opening-section treatment. */
  --hero-gradient:
    radial-gradient(ellipse 800px 550px at 88% 10%, rgba(217, 123, 46, 0.10), transparent 60%),
    radial-gradient(ellipse 950px 750px at 0% 75%, rgba(37, 99, 235, 0.26), transparent 68%),
    var(--bg);

  --status-good: #3F7A5C;
  --status-good-bg: #E4F1EA;
  --status-wait: #B8862F;
  --status-wait-bg: #FBF0DC;

  --radius-sm: 8px;
  --radius-md: 14px;
  --radius-lg: 22px;
  --radius-pill: 999px;

  --shadow-card: 0 1px 2px rgba(16, 25, 46, 0.04), 0 8px 24px -12px rgba(16, 25, 46, 0.12);
  --shadow-panel: 0 20px 60px -20px rgba(16, 25, 46, 0.35);

  --font-head: 'Playfair Display', Georgia, serif;
  --font-body: 'Playfair Display', Georgia, serif;

  --max: 1180px;
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
html {
  scroll-behavior: smooth;
  /* Safety net: real mobile Safari has occasionally shown 1px-ish rounding
     overflow from vw-unit math that never reproduces in desktop/emulated
     testing (see the ops-sculpture fix this same day). Hiding overflow-x at
     the root can't fix a genuine layout bug, but it stops any stray
     sub-layout overflow from creating a page-wide horizontal scrollbar. */
  overflow-x: hidden;
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; }
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 {
  font-family: var(--font-head);
  font-weight: 500;
  color: var(--ink);
  line-height: 1.15;
  margin: 0;
  letter-spacing: -0.01em;
}

.accent-word { color: var(--accent); }

p { margin: 0; }
a { color: inherit; text-decoration: none; touch-action: manipulation; }
img { max-width: 100%; display: block; }
ul { margin: 0; padding: 0; list-style: none; }
button { font: inherit; cursor: pointer; touch-action: manipulation; }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 4px;
}

/* ---------------- Full-screen page loader ----------------
   Shown on every page while it loads, built around the same wireframe
   sculpture as the homepage hero. The fade-out is a plain CSS animation
   with a forwards fill, not a JS-toggled class — that's deliberate: the
   page must become usable even if js/main.js fails to load or throws,
   so removal can never depend on JS running correctly. main.js only
   enhances this with a rotating sculpture and a real progress readout. */
.page-loader {
  position: fixed;
  inset: 0;
  z-index: 500;
  background: var(--navy);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 32px;
  animation: loaderHide 250ms ease-out 1350ms forwards;
}
@keyframes loaderHide {
  to { opacity: 0; visibility: hidden; pointer-events: none; }
}
.loader-word {
  font-family: var(--font-head);
  font-weight: 800;
  font-size: 18px;
  letter-spacing: 0.02em;
  color: var(--on-navy);
}
.loader-sculpture-wrap {
  width: min(56vw, 380px);
  aspect-ratio: 1 / 1;
}
.loader-sculpture-svg { width: 100%; height: 100%; overflow: visible; }
.loader-ring {
  fill: none;
  stroke: rgba(243, 239, 229, 0.22);
  stroke-width: 1.5;
}
.loader-ring-dashed { stroke-dasharray: 1.5 9; opacity: 0.6; }
#loaderBack path {
  fill: none;
  stroke: rgba(243, 239, 229, 0.3);
  stroke-width: 2;
  stroke-linecap: round;
  opacity: 0.5;
}
#loaderFront path {
  fill: none;
  stroke: url(#loaderGradient);
  stroke-width: 4.5;
  stroke-linecap: round;
  filter: drop-shadow(0 0 14px rgba(217, 123, 46, 0.5));
}
.loader-progress { width: min(58vw, 260px); }
.loader-bar {
  height: 2px;
  background: rgba(243, 239, 229, 0.18);
  border-radius: 2px;
  overflow: hidden;
}
.loader-bar-fill {
  height: 100%;
  width: 0%;
  background: var(--accent);
}
.loader-percent {
  margin-top: 10px;
  text-align: center;
  font-size: 12px;
  letter-spacing: 0.08em;
  color: var(--on-navy-muted);
  font-variant-numeric: tabular-nums;
}
@media (prefers-reduced-motion: reduce) {
  .page-loader { animation-delay: 350ms; }
}

.wrap {
  max-width: var(--max);
  margin: 0 auto;
  padding: 0 28px;
}

.section {
  padding: 88px 0;
}
@media (max-width: 720px) {
  .section { padding: 64px 0; }
}

.section-head {
  max-width: 620px;
  margin-bottom: 48px;
}
.section-head h2 {
  font-size: clamp(28px, 3.4vw, 38px);
  font-weight: 500;
}
.section-head p {
  margin-top: 14px;
  font-size: 17px;
  color: var(--ink-soft);
}
.section-dark .section-head,
.services-overview .section-head,
.tools-section .section-head {
  margin-left: auto;
  margin-right: auto;
  text-align: center;
}

/* Navy section background — same family as .stat-strip / .cta-panel.
   Cards inside stay exactly as styled (white surface, peach icon tiles);
   only the section shell and its heading/lede switch to on-navy colors,
   so the cards read as floating on the dark panel rather than being
   redesigned themselves. */
.section-dark {
  background: var(--brand-gradient);
  background-size: 220% 220%;
  animation: sectionGradientFlow 14s ease-in-out infinite;
}
/* Slow, endless flow across the gradient itself — the same "always
   moving" quality as the hero sculpture's idle rotation, rather than a
   static fill. Oversized (220%) so panning never shows a hard edge. */
@keyframes sectionGradientFlow {
  0%, 100% { background-position: 0% 30%; }
  50% { background-position: 100% 70%; }
}
@media (prefers-reduced-motion: reduce) {
  .section-dark { animation: none; }
}
.section-dark .section-head h2 {
  color: var(--on-navy);
}
.section-dark .section-head p {
  color: var(--on-navy-muted);
}

/* ---------------- Buttons ---------------- */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 14px 26px;
  border-radius: var(--radius-pill);
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 15px;
  border: 1px solid transparent;
  transition: transform 150ms ease, background 150ms ease, border-color 150ms ease, color 150ms ease;
  cursor: pointer;
}
/* Scoped to real pointer devices — see the same note on .card:hover
   for why an unscoped :hover here would "stick" on touchscreens. */
@media (hover: hover) and (pointer: fine) {
  .btn:hover { transform: scale(0.97); }
}
.btn-primary {
  background: var(--navy);
  color: var(--on-navy);
  border-color: rgba(243, 239, 229, 0.22);
}
.btn-primary:hover { background: var(--navy-soft); border-color: rgba(243, 239, 229, 0.34); }
.btn-secondary {
  background: transparent;
  color: var(--ink);
  border-color: var(--line-strong);
}
.btn-secondary:hover { border-color: var(--ink); }
.btn-accent {
  background: var(--accent);
  color: var(--accent-ink);
}
.btn-accent:hover { background: #C36A22; }
.btn-block { width: 100%; justify-content: center; }

/* ---------------- Nav ---------------- */
.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: rgba(11, 15, 28, 0.88);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--line);
}
.nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 18px 28px;
  max-width: var(--max);
  margin: 0 auto;
}
.wordmark {
  font-family: var(--font-head);
  font-weight: 800;
  font-size: 18px;
  display: flex;
  align-items: center;
  gap: 10px;
}
.wordmark .mark {
  width: 32px;
  height: 32px;
  border-radius: 9px;
  background: var(--navy);
  color: var(--on-navy);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 0.01em;
  object-fit: cover;
  flex-shrink: 0;
}
.nav-links {
  display: flex;
  align-items: center;
  gap: 34px;
}
.nav-links a {
  font-size: 15px;
  font-weight: 600;
  color: var(--ink-soft);
  transition: color 150ms ease;
}
.nav-links a:hover,
.nav-links a[aria-current="page"] { color: var(--ink); }
.nav-links a[aria-current="page"] {
  position: relative;
}
.nav-links a[aria-current="page"]::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: -6px;
  height: 2px;
  background: var(--accent);
  border-radius: 2px;
}
.nav-cta { display: flex; align-items: center; gap: 14px; }

/* ---------------- Language flag switcher ---------------- */
.lang-switcher { position: relative; }
.lang-toggle {
  width: 44px;
  height: 44px;
  flex-shrink: 0;
  border-radius: 50%;
  border: 1px solid var(--line-strong);
  background: var(--surface);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 19px;
  line-height: 1;
  cursor: pointer;
  transition: border-color 150ms ease, transform 150ms ease;
}
.lang-toggle[aria-expanded="true"] { border-color: var(--accent); }
@media (hover: hover) and (pointer: fine) {
  .lang-toggle:hover { transform: scale(0.97); }
}
.lang-menu {
  position: absolute;
  top: calc(100% + 10px);
  right: 0;
  width: 288px;
  max-width: calc(100vw - 40px);
  max-height: 320px;
  /* Visible, not auto, at the 6-column desktop layout — all 34 flags fit
     without scrolling there, and staying visible lets each flag's hover
     tooltip escape the menu's own box instead of being clipped by it.
     The mobile breakpoint below re-adds scrolling, where it's genuinely
     needed and tooltips don't apply anyway (no real hover on touch). */
  overflow: visible;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: 14px;
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 8px;
  box-shadow: 0 16px 40px rgba(16, 25, 46, 0.18);
  z-index: 60;
}
.lang-menu[hidden] { display: none; }
.lang-option {
  position: relative;
  width: 38px;
  height: 38px;
  border-radius: 8px;
  border: 2px solid transparent;
  background: var(--bg);
  font-size: 17px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: border-color 150ms ease, transform 150ms ease;
}
.lang-option.is-selected { border-color: var(--accent); background: var(--accent-soft); }
@media (hover: hover) and (pointer: fine) {
  .lang-option:hover { transform: scale(0.97); }

  /* Hover tooltip: the flag's country/language name, read straight off
     its own title attribute so there's one source of truth (and so it
     still reflects Google Translate's rewritten title if the page itself
     has been translated — see js/main.js initLangSwitcher). */
  .lang-option::after {
    content: attr(title);
    position: absolute;
    bottom: calc(100% + 9px);
    left: 50%;
    transform: translateX(-50%) translateY(4px);
    background: var(--navy);
    color: var(--on-navy);
    font-size: 12px;
    font-weight: 600;
    white-space: nowrap;
    padding: 5px 10px;
    border-radius: 6px;
    opacity: 0;
    pointer-events: none;
    transition: opacity 150ms ease, transform 150ms ease;
    z-index: 70;
  }
  .lang-option::before {
    content: '';
    position: absolute;
    bottom: calc(100% + 4px);
    left: 50%;
    transform: translateX(-50%) translateY(4px);
    border: 5px solid transparent;
    border-top-color: var(--navy);
    opacity: 0;
    pointer-events: none;
    transition: opacity 150ms ease, transform 150ms ease;
    z-index: 70;
  }
  .lang-option:hover::after,
  .lang-option:hover::before,
  .lang-option:focus-visible::after,
  .lang-option:focus-visible::before {
    opacity: 1;
    transform: translateX(-50%) translateY(0);
  }
}
@media (max-width: 480px) {
  .lang-menu {
    grid-template-columns: repeat(5, 1fr);
    right: -14px;
    overflow-y: auto;
    overflow-x: hidden;
  }
}

.nav-toggle {
  display: none;
  background: none;
  color: var(--ink);
  border: 1px solid var(--line-strong);
  border-radius: 10px;
  width: 44px;
  height: 44px;
  align-items: center;
  justify-content: center;
}
.nav-toggle svg { width: 20px; height: 20px; }

@media (max-width: 860px) {
  .nav-links {
    position: fixed;
    top: 69px; left: 0; right: 0;
    background: var(--surface);
    border-bottom: 1px solid var(--line);
    flex-direction: column;
    align-items: flex-start;
    gap: 0;
    padding: 8px 28px 20px;
    transform: translateY(-8px);
    opacity: 0;
    pointer-events: none;
    transition: opacity 180ms ease, transform 180ms ease;
  }
  .nav-links a { padding: 12px 0; width: 100%; border-bottom: 1px solid var(--line); }
  .nav-links.is-open { transform: translateY(0); opacity: 1; pointer-events: auto; }
  .nav-toggle { display: flex; }
  .nav-cta .btn-secondary { display: none; }

  /* The desktop active-link underline is an absolutely-positioned ::after at
     bottom:-6px, which works inline but bleeds onto the next stacked item
     once links wrap to a vertical list. Swap it for an inline marker here. */
  .nav-links a[aria-current="page"]::after { display: none; }
  .nav-links a[aria-current="page"] { color: var(--ink); }
  .nav-links a[aria-current="page"]::before {
    content: '';
    display: inline-block;
    width: 6px; height: 6px;
    border-radius: 50%;
    background: var(--accent);
    margin-right: 8px;
  }
}

/* ---------------- Hero ---------------- */
.hero {
  padding: 64px 0 40px;
  background: var(--hero-gradient);
}
.hero .wrap {
  display: grid;
  grid-template-columns: 1.15fr 1fr;
  gap: 56px;
  align-items: center;
}
@media (max-width: 980px) {
  .hero .wrap { grid-template-columns: 1fr; }
  .hero .wrap > div:first-child { text-align: center; }
  .identity-tag,
  .hero h1,
  .hero .lede { margin-left: auto; margin-right: auto; }
  .hero-cta { justify-content: center; }
  .hero-proof { justify-content: flex-start; }
  .identity-tag .who { text-align: left; }
}

.identity-tag {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 6px 14px 6px 6px;
  border-radius: var(--radius-pill);
  background: var(--surface);
  border: 1px solid var(--line);
  margin-bottom: 22px;
}
.identity-tag img,
.identity-tag .avatar-fallback {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  object-fit: cover;
  background: var(--navy);
  color: var(--on-navy);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  font-weight: 700;
}
.identity-tag .who { display: flex; flex-direction: column; line-height: 1.2; }
.identity-tag .who strong { font-size: 13.5px; font-family: var(--font-head); }
.identity-tag .who span { font-size: 12px; color: var(--ink-muted); }

.hero h1 {
  font-size: clamp(36px, 4.6vw, 58px);
  font-weight: 500;
  max-width: 19ch;
}
.hero .lede {
  margin-top: 20px;
  font-size: 18px;
  color: var(--ink-soft);
  max-width: 46ch;
}
@media (max-width: 640px) {
  .hero h1 { font-size: 30px; }
  .hero .lede { font-size: 16px; }
}
.hero-cta {
  display: flex;
  align-items: center;
  gap: 18px;
  margin-top: 32px;
  flex-wrap: wrap;
}
.hero-proof {
  margin-top: 34px;
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
}
.hero-proof .stat-chip {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  color: var(--ink-soft);
  font-weight: 600;
}
.hero-proof .stat-chip strong { color: var(--ink); font-family: var(--font-head); }
.hero-proof .divider { width: 1px; height: 16px; background: var(--line-strong); }

/* ---------------- Ops sculpture (hero visual) ----------------
   A draggable, rotatable wireframe loop — pure SVG + vanilla JS 3D math,
   no canvas or library. The shape is a torus knot: a single continuous
   loop, standing in for the diagnose → fix → systemize cycle named in
   the caption below it and spelled out on the Services page. Front and
   back strands are rendered as separate path groups (split by JS on
   each rotation) so the shape reads as genuinely three-dimensional
   rather than a flat line drawing. */
.ops-sculpture-wrap {
  position: relative;
}
.ops-sculpture {
  position: relative;
  /* Browsers apply a default margin (commonly 1em 40px) to <figure> —
     this is a <figure> element, and that default was never reset, so
     it sat 40px right of true-center within its wrap while width:100%
     still measured against the un-shifted container. */
  margin: 0;
  width: 100%;
  aspect-ratio: 1 / 1;
  cursor: grab;
  touch-action: pan-y;
  -webkit-user-select: none;
  user-select: none;
}
.ops-sculpture::before {
  content: '';
  position: absolute;
  inset: 6%;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(217, 123, 46, 0.1), transparent 70%);
  pointer-events: none;
}
.ops-sculpture:active,
.ops-sculpture.is-dragging {
  cursor: grabbing;
}
.sculpture-svg {
  width: 100%;
  height: 100%;
  overflow: visible;
}
.sculpture-ring {
  fill: none;
  stroke: var(--line-strong);
  stroke-width: 3.5;
  filter:
    drop-shadow(0 0 10px rgba(37, 99, 235, 0.55))
    drop-shadow(0 16px 34px rgba(37, 99, 235, 0.5));
}
.sculpture-ring-dashed {
  stroke-dasharray: 1.5 9;
  opacity: 0.7;
}
.sculpture-tick {
  stroke: var(--line-strong);
  stroke-width: 1.4;
}
#sculptureBack path {
  fill: none;
  stroke: var(--ink-muted);
  stroke-width: 2.8;
  stroke-linecap: round;
  opacity: 0.45;
}
#sculptureFront path {
  fill: none;
  stroke: url(#sculptureGradient);
  stroke-width: 6;
  stroke-linecap: round;
  filter:
    drop-shadow(0 4px 10px rgba(217, 123, 46, 0.45))
    drop-shadow(0 14px 32px rgba(16, 25, 46, 0.28));
}
.grad-stop-a { stop-color: var(--accent); }
.grad-stop-b { stop-color: var(--navy); }
.loader-grad-a { stop-color: var(--accent); }
.loader-grad-b { stop-color: var(--on-navy); }
.sculpture-caption {
  margin-top: 20px;
  text-align: center;
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--ink-muted);
  max-width: 34ch;
  margin-left: auto;
  margin-right: auto;
}
.sculpture-caption .drag-hint {
  color: var(--ink-soft);
  font-weight: 600;
}
@media (max-width: 640px) {
  /* On phones, the sculpture stops being its own block below the hero
     text and instead sits behind it as a faint watermark: both children
     of .hero .wrap share one grid cell (stacked via grid-template-areas)
     so they overlap, with the sculpture dimmed, non-interactive, and
     layered underneath via z-index. */
  .hero .wrap {
    grid-template-areas: "stack";
  }
  .hero .wrap > div:first-child {
    grid-area: stack;
    z-index: 1;
  }
  .ops-sculpture-wrap {
    grid-area: stack;
    z-index: 0;
    align-self: center;
    justify-self: center;
    width: 100%;
    opacity: 0.16;
    pointer-events: none;
  }
}

/* ---------------- Conversation ----------------
   A short exchange instead of another claim-plus-stats block: states the
   founder's real skepticism out loud, then answers it by pointing at the
   portfolio rather than asserting more adjectives. */
.convo {
  background-image:
    linear-gradient(180deg, rgba(16, 25, 46, 0.66), rgba(16, 25, 46, 0.72)),
    url('../assets/convo-bg.jpg');
  background-repeat: no-repeat, no-repeat;
  background-position: center, 15% 15%;
  background-size: cover, 160% auto;
  padding: 88px 28px;
  margin-bottom: 56px;
  animation: convoBgDrift 28s ease-in-out infinite alternate;
}
/* Slow, endless side-to-side pan across the skyline photo — like a camera
   sliding past the scene rather than tilting up/down. The image is
   oversized (160%) so it can pan within the section without ever
   showing an edge; alternate direction avoids any visible loop-seam. */
@keyframes convoBgDrift {
  from { background-position: center, 5% 15%; }
  to   { background-position: center, 95% 15%; }
}
@media (prefers-reduced-motion: reduce) {
  .convo { animation: none; background-position: center, 50% 15%; }
}
.convo-wrap {
  max-width: 640px;
  margin: 0 auto;
  padding: 0;
}
.convo-tag {
  display: flex;
  align-items: center;
  gap: 8px;
  width: fit-content;
  margin: 0 auto 40px;
  padding: 8px 16px;
  border-radius: var(--radius-pill);
  background: rgba(243, 239, 229, 0.07);
  border: 1px solid rgba(243, 239, 229, 0.14);
  color: var(--on-navy-muted);
  font-size: 13px;
  font-weight: 600;
}
.convo-tag .dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent);
  flex-shrink: 0;
}
.convo-row {
  display: flex;
  align-items: flex-end;
  gap: 12px;
  margin-bottom: 16px;
}
.convo-row--right { justify-content: flex-end; }
.convo-row--left { justify-content: flex-start; }
.convo-avatar {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  object-fit: cover;
  flex-shrink: 0;
  border: 2px solid rgba(243, 239, 229, 0.18);
}
.convo-avatar--client {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--navy-soft);
  color: var(--on-navy-muted);
  font-weight: 700;
  font-size: 15px;
}
.convo-bubble {
  max-width: 66%;
  border-radius: 20px;
  padding: 16px 20px;
}
.convo-bubble--light {
  background: var(--on-navy);
  color: var(--navy);
  border-bottom-right-radius: 6px;
}
.convo-bubble--dark {
  background: var(--navy-soft);
  color: var(--on-navy);
  border-bottom-left-radius: 6px;
}
.convo-bubble--large {
  max-width: 78%;
  padding: 22px 26px;
}
.convo-label {
  display: block;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  opacity: 0.55;
  margin-bottom: 4px;
}
.convo-bubble p,
.convo-bubble h1 {
  font-size: 16px;
  line-height: 1.5;
  font-family: var(--font-body);
  font-weight: 400;
  text-transform: none;
  color: inherit;
}
.convo-bubble--large p,
.convo-bubble--large h1 {
  font-size: 16px;
  font-weight: 600;
}
.convo-cta {
  display: block;
  width: fit-content;
  margin: 36px auto 0;
}
@media (max-width: 640px) {
  .convo { padding: 64px 20px; }
  .convo-bubble { max-width: 80%; }
  .convo-bubble--large { max-width: 88%; }
  .convo-avatar { width: 34px; height: 34px; }
}

/* Second conversation: slides in from the right as it's scrolled into
   view, triggered early (see the IntersectionObserver threshold in
   main.js) and then stays put — a single deliberate scroll moment, not a
   scrubbed animation tied continuously to scroll position. Content is
   present and readable without JS; this only ever adds motion on top. */
.convo-slide-in {
  opacity: 0;
  transform: translateX(72px);
  transition: opacity 600ms ease, transform 600ms ease;
}
.convo-slide-in.is-visible {
  opacity: 1;
  transform: translateX(0);
}
@media (prefers-reduced-motion: reduce) {
  .convo-slide-in {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* ---------------- Stat strip ----------------
   Same navy + copper combination as .cta-panel, so the two dark
   moments on the page (proof up top, ask at the bottom) read as one
   deliberate device instead of one-off color choices. */
.stat-strip {
  background: var(--brand-gradient);
  background-size: 220% 220%;
  animation: sectionGradientFlow 14s ease-in-out infinite;
  margin: 56px 0;
}
@media (prefers-reduced-motion: reduce) {
  .stat-strip { animation: none; }
}
.stat-strip .wrap {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 24px;
  padding: 44px 28px;
}
@media (max-width: 720px) {
  .stat-strip .wrap { grid-template-columns: repeat(2, 1fr); }
}
.stat-strip .stat {
  border-left: 2px solid var(--accent);
  padding-left: 16px;
}
.stat-strip .stat .n {
  font-family: var(--font-head);
  font-size: clamp(26px, 3vw, 34px);
  font-weight: 800;
  color: var(--on-navy);
}
.stat-strip .stat .t {
  margin-top: 4px;
  font-size: 13.5px;
  color: var(--on-navy);
  font-weight: 500;
}

/* ---------------- Cards ---------------- */
.grid-3 {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 22px;
}
.grid-2 {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 22px;
}
.grid-2 > .card--wide { grid-column: 1 / -1; }
@media (max-width: 860px) {
  .grid-3, .grid-2 { grid-template-columns: 1fr; }
}

.card {
  background: var(--surface-sunken);
  border: 1px solid var(--accent-soft);
  border-radius: var(--radius-md);
  padding: 28px;
  transition: transform 150ms ease;
  /* iOS Safari only applies :active to elements it considers
     "interactive enough" — a plain div never qualifies unless it has
     a cursor: pointer (or an onclick/touch listener). Without this,
     the press-scale below silently never fires on iPhone. */
  cursor: pointer;
}
.card:active {
  transform: scale(0.97);
}
/* Scoped to real pointer devices (mouse/trackpad) — touchscreens don't
   have a true hover to release, so an unscoped :hover here would "stick"
   the scaled-down look after a tap until the user taps elsewhere. */
@media (hover: hover) and (pointer: fine) {
  .card:hover {
    transform: scale(0.97);
  }
}

.icon-tile {
  width: 44px;
  height: 44px;
  border-radius: 12px;
  background: var(--accent-soft);
  color: var(--accent);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 18px;
}
.icon-tile svg { width: 22px; height: 22px; }

.card h3 {
  font-size: 18px;
  font-weight: 500;
  margin-bottom: 10px;
}
.card p {
  font-size: 15px;
  color: var(--ink-soft);
}
.card ul {
  margin-top: 14px;
  display: flex;
  flex-direction: column;
  gap: 9px;
}
.card ul li {
  font-size: 14.5px;
  color: var(--ink-soft);
  display: flex;
  gap: 9px;
  align-items: flex-start;
}
.card ul li svg {
  width: 16px; height: 16px;
  color: var(--status-good);
  flex-shrink: 0;
  margin-top: 3px;
}

/* Navy-card variant — background swaps to navy, all text turns white,
   the icon tile is left completely untouched (same peach/copper look
   it has on a white card, which still reads cleanly against navy). */
.card-dark {
  background: var(--navy);
  border: none;
}
.card-dark h3,
.card-dark p {
  color: var(--on-navy);
}

/* ---------------- Tools strip ---------------- */
/* A faint, real photo behind the tool badges — an analytics-dashboard
   scene, since this section is specifically about the software/tools
   he works in daily, not fulfillment (that's the contact section's own
   photo below). Same low-opacity treatment as .contact-section. */
.tools-section {
  position: relative;
  overflow: hidden;
}
.tools-section::before {
  content: '';
  position: absolute;
  inset: 0;
  background: url('../assets/tools-bg.jpg') center / cover no-repeat;
  opacity: 0.08;
  pointer-events: none;
}
.tools-section .wrap {
  position: relative;
  z-index: 1;
}
.tools-strip {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  /* Extra breathing room above the first row of badges specifically —
     the faint background photo behind this section reads much more
     visibly once there's a dense grid of pills over it than it does
     behind the heading text above, so the badges need more clearance
     from that transition than .section-head's normal margin gives. */
  margin-top: 24px;
}
.tool-badge {
  display: inline-flex;
  align-items: center;
  padding: 10px 20px;
  border: none;
  border-radius: var(--radius-pill);
  font-size: 14px;
  font-weight: 700;
  color: var(--accent-ink);
  background: var(--accent);
  transition: transform 150ms ease;
  cursor: pointer;
}
@media (hover: hover) and (pointer: fine) {
  .tool-badge:hover { transform: scale(0.97); }
}
@media (max-width: 640px) {
  /* Plain flex-wrap leaves long-text badges (e.g. "Manage Ecommerce
     Site") taking a whole row alone on narrow screens, since wrapping
     is greedy and never shrinks items to fit a neighbor. A fixed
     2-column grid guarantees at least two badges per row regardless of
     label length, at the cost of letting long labels wrap to a second
     line within their own pill. */
  .tools-strip {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
  }
  .tool-badge {
    justify-content: center;
    text-align: center;
    white-space: normal;
  }
}

/* ---------------- Testimonial ---------------- */
/* ---------------- Testimonials marquee ----------------
   Horizontal, auto-scrolling, infinite loop — a CSS `animation` sliding
   the track by exactly -50% of its own width, where the track holds two
   identical copies of the card set back to back. Because the two copies
   are identical, the instant jump from -50% back to 0% at the end of
   each cycle is visually seamless (it looks the same as not having
   jumped at all), which is the standard trick for this kind of marquee
   without needing JS to manage positions every frame.

   Click anywhere in the strip to pause the auto-scroll and switch to
   manual browsing — from there, a normal vertical mouse-wheel motion
   drives the (still horizontal) scroll position too, since most mice
   don't have an easy horizontal-scroll gesture (see main.js). */
.testimonials-section { overflow: hidden; }
.testimonial-marquee {
  position: relative;
  overflow: hidden;
  margin-top: 40px;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
}
.testimonial-marquee.is-scrollable {
  overflow-x: auto;
  cursor: grab;
  -webkit-mask-image: none;
  mask-image: none;
}
.testimonial-track {
  display: flex;
  gap: 20px;
  width: max-content;
  padding: 0 28px;
  animation: testimonial-scroll 130s linear infinite;
}
.testimonial-marquee.is-paused .testimonial-track {
  animation-play-state: paused;
}
@keyframes testimonial-scroll {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}
.testimonial-card {
  flex: 0 0 380px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 28px;
  display: flex;
  gap: 16px;
  align-items: flex-start;
}
.testimonial-avatar {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: var(--navy-soft);
  color: var(--on-navy);
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 14px;
  flex-shrink: 0;
  overflow: hidden;
}
.testimonial-avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Illustrative, not identifying — these represent anonymous Upwork
     reviewers, so a slight blur keeps the photo from reading as a
     literal verified portrait of the specific client. */
  filter: blur(1.4px) saturate(0.95);
  transform: scale(1.08);
}
.testimonial-card blockquote {
  margin: 0;
  font-size: 15.5px;
  line-height: 1.55;
  color: var(--ink);
}
.testimonial-card cite {
  display: block;
  margin-top: 14px;
  font-style: normal;
  font-size: 13px;
  color: var(--ink-muted);
}
.testimonial-card cite strong {
  display: block;
  font-size: 14.5px;
  color: var(--ink-soft);
  margin-bottom: 2px;
}
.testimonial-avatar--quote svg {
  width: 20px;
  height: 20px;
  fill: var(--on-navy);
  opacity: 0.85;
}
.testimonial-stars {
  display: flex;
  gap: 2px;
  margin-bottom: 10px;
}
.testimonial-stars .star-icon {
  width: 15px;
  height: 15px;
  fill: var(--accent);
  flex-shrink: 0;
}
.testimonial-stars .star-icon.is-empty {
  fill: var(--line-strong);
}
.testimonial-stars .star-half {
  position: relative;
  width: 15px;
  height: 15px;
  flex-shrink: 0;
}
.testimonial-stars .star-half .star-icon {
  position: absolute;
  inset: 0;
}
.testimonial-stars .star-half .star-icon.is-fill {
  clip-path: inset(0 50% 0 0);
}
@media (prefers-reduced-motion: reduce) {
  .testimonial-track { animation: none; }
}
@media (max-width: 640px) {
  .testimonial-card { flex-basis: 300px; padding: 22px; }
  /* Same animation duration reads as noticeably faster on a narrow phone
     screen — fewer cards are visible at once, so each one's dwell time in
     view is shorter even though the actual scroll speed hasn't changed.
     Slow the animation down here specifically to compensate. */
  .testimonial-track { animation-duration: 220s; }
}

/* ---------------- CTA panel ---------------- */
.cta-panel {
  background: var(--brand-gradient);
  background-size: 220% 220%;
  animation: sectionGradientFlow 14s ease-in-out infinite;
  color: var(--on-navy);
  border-radius: var(--radius-lg);
  padding: 24px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 40px;
}
.cta-panel-content {
  display: flex;
  flex-direction: column;
  gap: 24px;
  padding: 32px 24px;
  flex: 1 1 320px;
}
.cta-panel h2 {
  color: var(--on-navy);
  font-size: clamp(24px, 3vw, 32px);
  max-width: 20ch;
}
.cta-panel p {
  margin-top: 10px;
  color: var(--on-navy-muted);
  font-size: 15.5px;
}
.cta-panel .btn-secondary {
  color: var(--on-navy);
  border-color: rgba(255,255,255,0.28);
}
.cta-panel .btn-secondary:hover { border-color: var(--on-navy); }
@media (prefers-reduced-motion: reduce) {
  .cta-panel { animation: none; }
}
.cta-panel-image {
  width: 380px;
  max-width: 42%;
  height: 260px;
  object-fit: cover;
  border-radius: var(--radius-lg);
  flex-shrink: 0;
}
@media (max-width: 720px) {
  .cta-panel {
    flex-direction: column;
    padding: 16px;
  }
  .cta-panel-content {
    padding: 20px 16px 8px;
  }
  .cta-panel-image {
    width: 100%;
    max-width: 100%;
    height: 200px;
  }
}

/* ---------------- Footer ----------------
   Same navy family as .stat-strip / .section-dark / .cta-panel, so the
   page ends on the same dark note it uses for proof and the final ask. */
.site-footer {
  background: var(--navy);
  padding: 56px 0 32px;
}
.site-footer .wordmark {
  color: var(--on-navy);
}
.site-footer p {
  color: var(--on-navy) !important;
}
.footer-grid {
  display: flex;
  justify-content: space-between;
  gap: 32px;
  flex-wrap: wrap;
}
.footer-links {
  display: flex;
  gap: 40px;
  flex-wrap: wrap;
}
.footer-col h4 {
  font-size: 13px;
  color: var(--on-navy);
  font-weight: 600;
  margin-bottom: 12px;
}
.footer-col ul { display: flex; flex-direction: column; gap: 9px; }
.footer-col a {
  font-size: 14.5px;
  color: var(--on-navy);
}
.footer-col a:hover { color: var(--accent); }
.footer-bottom {
  margin-top: 40px;
  padding-top: 20px;
  border-top: 1px solid rgba(255,255,255,0.12);
  display: flex;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  font-size: 13px;
  color: var(--on-navy);
}

/* ---------------- Page hero (sub-pages) ---------------- */
.page-hero {
  padding: 56px 0 20px;
}
/* Only applied to Services/Results/About — portfolio.html's page-hero
   is intentionally left plain (not requested, and it sits right above
   the portfolio grid rather than a hero visual). */
.page-hero--gradient {
  background: var(--hero-gradient);
}
.page-hero .kicker-tag {
  display: inline-block;
  padding: 7px 14px;
  border-radius: var(--radius-pill);
  background: var(--accent-soft);
  color: #9C5417;
  font-size: 13px;
  font-weight: 700;
  margin-bottom: 18px;
}
.page-hero h1 {
  font-size: clamp(32px, 4.2vw, 48px);
  font-weight: 500;
  max-width: 18ch;
}
.page-hero .lede {
  margin-top: 16px;
  font-size: 17.5px;
  color: var(--ink-soft);
  max-width: 60ch;
}

/* ---------------- Process steps ---------------- */
.process {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
@media (max-width: 860px) {
  .process { grid-template-columns: 1fr; }
}
.process .step {
  background: var(--surface);
  padding: 32px;
  border-left: 1px solid var(--line);
}
.process .step:first-child { border-left: none; }
@media (max-width: 860px) {
  .process .step { border-left: none; border-top: 1px solid var(--line); }
  .process .step:first-child { border-top: none; }
}
.process .step .num {
  font-family: var(--font-head);
  font-size: 13px;
  font-weight: 800;
  color: var(--accent);
  margin-bottom: 14px;
}
.process .step h3 { font-size: 17px; margin-bottom: 8px; }
.process .step p { font-size: 14.5px; color: var(--ink-soft); }

/* ---------------- Contact ---------------- */
/* A faint, real photo behind the cards/form — grounds the page in the
   actual work (fulfillment) rather than leaving it a flat color, but
   stays low-opacity enough that the white cards and form stay easily
   readable on top of it. */
.contact-section {
  position: relative;
  overflow: hidden;
}
.contact-section::before {
  content: '';
  position: absolute;
  inset: 0;
  background: url('../assets/contact-bg.jpg') center / cover no-repeat;
  opacity: 0.1;
  pointer-events: none;
}
.contact-section .wrap {
  position: relative;
  z-index: 1;
}
.contact-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 40px;
  align-items: start;
}
@media (max-width: 900px) {
  .contact-grid { grid-template-columns: 1fr; }
}
.contact-option {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 18px;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--surface);
  margin-bottom: 14px;
  transition: border-color 150ms ease;
}
.contact-option:hover { border-color: var(--line-strong); }
.contact-option .icon-tile { margin-bottom: 0; flex-shrink: 0; }
.contact-option .info h3 { font-size: 15.5px; margin-bottom: 2px; }
.contact-option .info p { font-size: 13.5px; color: var(--ink-muted); }

.field {
  margin-bottom: 18px;
}
.field label {
  display: block;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--ink-soft);
  margin-bottom: 7px;
}
.field input,
.field textarea,
.field select {
  width: 100%;
  padding: 13px 14px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
  font: inherit;
  color: var(--ink);
  /* 16px minimum — iOS Safari auto-zooms the whole page on focus for any
     input/textarea with a smaller computed font-size, which feels broken
     on a real iPhone even though it's invisible in desktop/emulated testing. */
  font-size: 16px;
}
.field input:focus,
.field textarea:focus,
.field select:focus {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
  border-color: var(--accent);
}
.field textarea { resize: vertical; min-height: 120px; }
.form-note {
  margin-top: 12px;
  font-size: 13px;
  color: var(--ink-muted);
}
.form-success {
  display: none;
  background: var(--status-good-bg);
  color: #235B3E;
  border-radius: var(--radius-sm);
  padding: 14px 16px;
  font-size: 14px;
  font-weight: 600;
  margin-bottom: 18px;
}
.form-success.is-visible { display: block; }
.form-error {
  display: none;
  background: var(--status-wait-bg);
  color: #7A4A0F;
  border-radius: var(--radius-sm);
  padding: 14px 16px;
  font-size: 14px;
  font-weight: 600;
  margin-bottom: 18px;
}
.form-error a { text-decoration: underline; }
.form-error.is-visible { display: block; }

/* ---------------- Utility reveal (single orchestrated hero entrance) ----------------
   Content is visible by default (no JS/animation dependency). JS progressively
   enhances with a one-time fade+rise via the Web Animations API — see main.js.
   This avoids the classic "opacity:0 until JS adds a class" trap, where a slow
   script, a JS error, or JS being disabled leaves the hero permanently blank. */

/* ---------------- Bio / about page ---------------- */
.about-grid {
  display: grid;
  grid-template-columns: 1fr 1.4fr;
  gap: 48px;
  align-items: start;
}
@media (max-width: 900px) {
  .about-grid { grid-template-columns: 1fr; }
}
.about-photo {
  aspect-ratio: 4/5;
  border-radius: var(--radius-lg);
  background: var(--accent-soft);
  color: var(--on-navy-muted);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  font-family: var(--font-head);
  font-size: 15px;
  text-align: center;
}
.about-photo img { width: 100%; height: 100%; object-fit: cover; }
.about-body p { font-size: 16.5px; color: var(--ink-soft); margin-bottom: 18px; }
.about-body h3 { font-size: 19px; margin: 30px 0 14px; }

.lang-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 14px;
}
@media (max-width: 560px) {
  .lang-grid { grid-template-columns: repeat(2, 1fr); }
}
.lang-pill {
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font-size: 14px;
  font-weight: 600;
  color: var(--ink-soft);
  background: var(--surface);
}
.lang-pill strong { display: block; color: var(--ink); font-size: 13px; margin-bottom: 2px; }

/* ---------------- Results page: placeholder case study slot ---------------- */
.case-slot {
  border: 1px dashed var(--line-strong);
  border-radius: var(--radius-md);
  padding: 26px;
  color: var(--ink-muted);
  font-size: 14.5px;
  text-align: center;
}

/* ---------------- Portfolio page ----------------
   One template, reused per project: a meta row (role + date), title,
   a few short paragraphs, skill tags, then the real deliverable/screenshot
   as proof. Stacked full-width rather than side-by-side, since the
   screenshots vary a lot in aspect ratio (dashboards vs. spreadsheets vs.
   store admin) and forcing them into a fixed-height column would crop
   real data. New projects just repeat this same block. */
.portfolio-item {
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  padding: 40px;
  margin-bottom: 28px;
}
.portfolio-item:last-child { margin-bottom: 0; }
.portfolio-meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: 16px;
}
.portfolio-role {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  font-weight: 700;
  color: var(--accent);
}
.portfolio-role svg { width: 15px; height: 15px; }
.portfolio-date {
  font-size: 13px;
  color: var(--ink-muted);
}
.portfolio-item h3 {
  font-size: clamp(20px, 2.4vw, 25px);
  font-weight: 500;
  max-width: 34ch;
  margin-bottom: 16px;
}
.portfolio-body p {
  font-size: 15.5px;
  color: var(--ink-soft);
  margin-bottom: 12px;
  max-width: 68ch;
}
.portfolio-body p:last-child { margin-bottom: 0; }
.portfolio-more-btn {
  display: inline-flex;
  align-items: center;
  vertical-align: middle;
  margin-left: 8px;
  padding: 6px 16px;
  background: var(--accent);
  color: var(--accent-ink);
  border: none;
  border-radius: var(--radius-pill);
  font-family: var(--font-head);
  font-weight: 700;
  font-size: 13px;
  cursor: pointer;
  white-space: nowrap;
  transition: transform 150ms ease, background 150ms ease;
}
.portfolio-more-btn:hover { background: #C36A22; }
@media (hover: hover) and (pointer: fine) {
  .portfolio-more-btn:hover { transform: scale(0.97); }
}
.portfolio-skills {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 20px;
}
.portfolio-skills span {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--ink-soft);
  background: var(--surface-sunken);
  border: 1px solid var(--line);
  padding: 6px 12px;
  border-radius: var(--radius-pill);
}
.portfolio-shot {
  margin-top: 26px;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--surface-sunken);
}
.portfolio-shot img {
  width: 100%;
  height: auto;
  display: block;
  cursor: zoom-in;
}
.portfolio-shot figcaption {
  padding: 12px 16px;
  font-size: 13px;
  color: var(--ink-muted);
  border-top: 1px solid var(--line);
  background: var(--surface);
}

/* Multiple screenshots for one project — a horizontally scrolling row
   instead of stacking, so each stays full quality and legible instead
   of being squeezed into a shared column. Images are sized so the next
   one peeks in at the edge, signaling there's more to scroll without
   needing a text hint. */
.portfolio-shot .shot-row {
  display: flex;
  gap: 12px;
  overflow-x: auto;
  scroll-snap-type: x proximity;
  padding: 12px;
  -webkit-overflow-scrolling: touch;
}
.portfolio-shot .shot-row img {
  flex: 0 0 auto;
  width: 78%;
  max-width: 560px;
  border-radius: var(--radius-sm);
  scroll-snap-align: start;
}

/* Mobile: let screenshots fill the card's full width instead of sitting
   inside its 40px padding — on a 375px phone that padding alone was
   eating ~80px, leaving images noticeably cramped. Text stays padded and
   readable; the image frame stretches to the card's own edge, which
   stays aligned with the page's own left/right boundary (.wrap's
   padding) — same as every other section on the page — rather than
   bleeding past it to the literal screen edge, which read as "wider
   than the page" against everything else that respects that margin. */
@media (max-width: 640px) {
  .portfolio-item { padding: 28px 20px; }
  .portfolio-shot {
    margin-left: -20px;
    margin-right: -20px;
    border-left: none;
    border-right: none;
    border-radius: 0;
  }
  .portfolio-shot .shot-row img { width: 92%; }
}
.portfolio-more {
  text-align: center;
  padding: 32px;
  border: 1px dashed var(--line-strong);
  border-radius: var(--radius-lg);
  color: var(--ink-muted);
  font-size: 14.5px;
  margin-top: 28px;
}

/* ---------------- Floating WhatsApp button ---------------- */
.whatsapp-float {
  position: fixed;
  /* env() needs viewport-fit=cover to be non-zero; falls back to the plain
     24px on devices/browsers without a safe-area inset (the "+ 0px" default). */
  right: calc(24px + env(safe-area-inset-right, 0px));
  bottom: calc(24px + env(safe-area-inset-bottom, 0px));
  width: 58px;
  height: 58px;
  border-radius: 50%;
  background: #25D366;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 10px 28px rgba(16, 25, 46, 0.28);
  z-index: 90;
  transition: transform 150ms ease;
  cursor: pointer;
  animation: wa-pulse 5s ease-in-out infinite;
}
.whatsapp-float svg { width: 29px; height: 29px; fill: #fff; }
@keyframes wa-pulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.12); }
}
@media (prefers-reduced-motion: reduce) {
  .whatsapp-float { animation: none; }
}
@media (hover: hover) and (pointer: fine) {
  .whatsapp-float:hover {
    animation-play-state: paused;
    /* !important: a running CSS animation otherwise outranks this same
       `transform` property even while paused, freezing the button at
       whatever point in the pulse cycle the cursor happened to land on
       instead of the intended press-in scale. */
    transform: scale(0.94) !important;
  }
}
@media (max-width: 640px) {
  .whatsapp-float {
    right: calc(16px + env(safe-area-inset-right, 0px));
    bottom: calc(16px + env(safe-area-inset-bottom, 0px));
    width: 52px;
    height: 52px;
  }
  .whatsapp-float svg { width: 26px; height: 26px; }
}

/* ---------------- Lightbox (portfolio image popout) ---------------- */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 300;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 64px 24px;
  background: rgba(16, 25, 46, 0.9);
}
.lightbox[hidden] { display: none; }
.lightbox-img {
  max-width: 100%;
  max-height: 100%;
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-panel);
  animation: lightbox-in 200ms ease;
}
@keyframes lightbox-in {
  from { opacity: 0; transform: scale(0.97); }
  to { opacity: 1; transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .lightbox-img { animation: none; }
}
.lightbox-close {
  position: absolute;
  top: 20px;
  right: 20px;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: none;
  background: var(--surface);
  color: var(--ink);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: transform 150ms ease;
}
.lightbox-close:hover { transform: scale(1.06); }
.lightbox-close svg { width: 20px; height: 20px; }

/* ---------------- Google Translate: hide its own UI chrome ----------------
   The flag switcher drives translation via a cookie + reload (see main.js),
   so only the actual translated text should show — never Google's default
   top banner bar, word-hover tooltips, or highlight boxes. Google's banner
   iframe uses an internal, unstable class name rather than a documented
   one, so it's targeted structurally instead: every iframe Google injects
   carries `.skiptranslate`, and the site has no iframes of its own. */
iframe.skiptranslate {
  display: none !important;
}
#goog-gt-tt,
.goog-te-balloon-frame {
  display: none !important;
}
body {
  top: 0 !important;
  position: static !important;
}
.goog-text-highlight {
  background: none !important;
  box-shadow: none !important;
}
#google_translate_element {
  display: none !important;
}

/* ============================================================
   BLOG — a deliberately distinct editorial identity for the
   writing section: dark background + a monospace display face
   (an "operator's notes" feel, fitting someone who works in
   data/systems all day), but still built from the site's own
   navy + copper tokens rather than a new, unrelated palette.
   The header/footer stay the site's normal light navy/cream —
   only the blog's own content sections go dark, the same way
   .stat-strip/.section-dark already sit inside an otherwise
   light page.
   ============================================================ */
.blog-hero,
.blog-list-section,
.blog-post-section {
  background: #0B0F1C;
  color: var(--on-navy);
}
.blog-hero {
  position: relative;
  overflow: hidden;
  padding: 72px 0 56px;
}
.blog-hero::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(circle 420px at 6% 0%, rgba(217, 123, 46, 0.35), transparent 70%),
    radial-gradient(circle 380px at 100% 20%, rgba(217, 123, 46, 0.2), transparent 70%);
}
.blog-hero .wrap { position: relative; z-index: 1; }
.blog-list-section { padding: 0 0 96px; }
.blog-post-section { padding: 0 0 96px; }

.blog-back {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-head);
  font-size: 12.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 28px;
}
.blog-back:hover { text-decoration: underline; }

.blog-meta,
.blog-kicker {
  font-family: var(--font-head);
  font-size: 12.5px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent);
}
.blog-meta { color: var(--on-navy-muted); margin-bottom: 18px; }
.blog-kicker { display: inline-flex; margin-bottom: 18px; }

.blog-title {
  font-family: var(--font-head);
  font-weight: 500;
  letter-spacing: -0.01em;
  line-height: 1.15;
  color: var(--ink);
  font-size: clamp(36px, 4.6vw, 58px);
  max-width: 24ch;
}
@media (max-width: 640px) {
  .blog-title { font-size: 30px; }
}
.blog-lede {
  margin-top: 18px;
  font-size: 17px;
  color: var(--on-navy-muted);
  max-width: 60ch;
}

/* Listing grid */
.blog-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 28px;
}
@media (max-width: 720px) {
  .blog-grid { grid-template-columns: 1fr; }
}
.blog-card {
  display: block;
  background: rgba(243, 239, 229, 0.04);
  border: 1px solid rgba(243, 239, 229, 0.1);
  border-radius: var(--radius-lg);
  overflow: hidden;
  transition: transform 150ms ease, border-color 150ms ease;
}
@media (hover: hover) and (pointer: fine) {
  .blog-card:hover { transform: scale(0.98); border-color: rgba(217, 123, 46, 0.5); }
}
.blog-card img {
  width: 100%;
  aspect-ratio: 16/10;
  object-fit: cover;
}
.blog-card-body { padding: 22px; }
.blog-card-date {
  font-family: var(--font-head);
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 10px;
}
.blog-card h3 {
  font-family: var(--font-head);
  font-weight: 700;
  font-size: 17px;
  line-height: 1.4;
  color: var(--on-navy);
}
.blog-empty {
  grid-column: 1 / -1;
  border: 1px dashed rgba(243, 239, 229, 0.2);
  border-radius: var(--radius-lg);
  padding: 48px;
  text-align: center;
  color: var(--on-navy-muted);
}

/* Post page: content + "more articles" sidebar */
.blog-post-layout {
  display: grid;
  grid-template-columns: 1fr 320px;
  gap: 56px;
  align-items: start;
}
@media (max-width: 900px) {
  .blog-post-layout { grid-template-columns: 1fr; }
}
.blog-post-hero-img {
  width: 100%;
  border-radius: var(--radius-lg);
  margin-bottom: 40px;
}
.blog-post-body {
  font-size: 17px;
  line-height: 1.75;
  color: var(--on-navy-muted);
}
.blog-post-body h2 {
  font-family: var(--font-head);
  color: var(--on-navy);
  font-size: 21px;
  text-transform: uppercase;
  letter-spacing: -0.01em;
  margin: 40px 0 16px;
}
.blog-post-body p { margin-bottom: 20px; }
.blog-post-body ul { margin: 0 0 20px; padding-left: 20px; list-style: disc; }
.blog-post-body li { margin-bottom: 8px; }
.blog-post-body strong { color: var(--on-navy); }
.blog-post-body hr {
  border: none;
  border-top: 1px solid rgba(243, 239, 229, 0.14);
  margin: 40px 0;
}

/* ---------------- Blog: table of contents ---------------- */
.blog-toc {
  background: var(--navy-soft);
  border: 1px solid rgba(243, 239, 229, 0.14);
  border-radius: var(--radius-md);
  padding: 22px 26px;
  margin-bottom: 36px;
}
.blog-toc summary {
  font-family: var(--font-head);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-size: 15px;
  color: var(--on-navy);
  cursor: pointer;
  list-style: none;
}
.blog-toc summary::-webkit-details-marker { display: none; }
.blog-toc summary::after {
  content: '+';
  float: right;
  font-size: 20px;
  color: var(--accent);
}
.blog-toc[open] summary::after { content: '\2212'; }
.blog-toc ol {
  margin: 18px 0 0;
  padding-left: 20px;
  color: var(--on-navy-muted);
}
.blog-toc li { margin-bottom: 10px; font-size: 15px; }
.blog-toc a { color: inherit; text-decoration: none; border-bottom: 1px solid transparent; transition: border-color 150ms ease, color 150ms ease; }
.blog-toc a:hover { color: var(--accent); border-color: var(--accent); }

/* ---------------- Blog: FAQ accordion ---------------- */
.blog-faq-item {
  border-bottom: 1px solid rgba(243, 239, 229, 0.14);
  padding: 18px 0;
}
.blog-faq-item summary {
  font-family: var(--font-head);
  font-weight: 700;
  font-size: 17px;
  color: var(--on-navy);
  cursor: pointer;
  list-style: none;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
}
.blog-faq-item summary::-webkit-details-marker { display: none; }
.blog-faq-item summary::after {
  content: '+';
  flex-shrink: 0;
  font-size: 22px;
  color: var(--accent);
}
.blog-faq-item[open] summary::after { content: '\2212'; }
.blog-faq-item p { margin-top: 14px; margin-bottom: 0; }

.blog-sidebar {
  position: sticky;
  top: 100px;
}
.blog-sidebar-label {
  font-family: var(--font-head);
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 8px;
}
.blog-sidebar-heading {
  font-family: var(--font-head);
  font-weight: 800;
  text-transform: uppercase;
  font-size: 21px;
  color: var(--on-navy);
  margin-bottom: 24px;
}
.blog-sidebar-list { display: flex; flex-direction: column; gap: 18px; }
.blog-sidebar-card {
  display: flex;
  gap: 14px;
  align-items: flex-start;
}
.blog-sidebar-card img {
  width: 72px;
  height: 72px;
  border-radius: 10px;
  object-fit: cover;
  flex-shrink: 0;
}
.blog-sidebar-date {
  font-family: var(--font-head);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--on-navy-muted);
  margin-bottom: 4px;
}
.blog-sidebar-card h4 {
  font-family: var(--font-head);
  font-size: 13.5px;
  font-weight: 700;
  line-height: 1.4;
  color: var(--on-navy);
}
@media (hover: hover) and (pointer: fine) {
  .blog-sidebar-card:hover h4 { color: var(--accent); }
}
