/* ============================================================
   Noorun Nobi — Portfolio
   Theme: deep navy + single teal accent · content-first · fast
   ============================================================ */

:root {
  --bg: #0a1018;
  --bg-raise: #0e1622;
  --bg-card: #111b29;
  --line: rgba(148, 180, 220, 0.12);
  --text: #dbe7f3;
  --text-dim: #8fa3b8;
  --accent: #4fe3c1;
  --accent-soft: rgba(79, 227, 193, 0.12);
  --accent-line: rgba(79, 227, 193, 0.35);
  --font-body: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-head: "Space Grotesk", var(--font-body);
  --font-mono: "JetBrains Mono", ui-monospace, "Cascadia Code", Consolas, monospace;
  --radius: 14px;
  --container: 1080px;
}

* { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  scroll-padding-top: 84px;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 16.5px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: clip; /* safety net (clip, not hidden — hidden is disabled by the
                       default visible overflow-y and wouldn't actually clip) */
}

h1, h2, h3, h4 {
  font-family: var(--font-head);
  line-height: 1.15;
  margin: 0 0 0.5em;
  letter-spacing: -0.01em;
}

p { margin: 0 0 1em; }
a { color: var(--accent); }
code {
  font-family: var(--font-mono);
  font-size: 0.88em;
  background: var(--accent-soft);
  padding: 0.1em 0.4em;
  border-radius: 5px;
}

.container {
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: clamp(20px, 5vw, 40px);
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--accent);
  color: #06281f;
  padding: 10px 18px;
  border-radius: 0 0 10px 0;
  font-weight: 600;
  z-index: 100;
}
.skip-link:focus { left: 0; }

/* Keyboard focus — visible, on-brand */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 4px;
}

/* Scroll progress bar */
.progress {
  position: fixed;
  top: 0;
  left: 0;
  height: 2px;
  width: 0;
  background: var(--accent);
  z-index: 60;
  box-shadow: 0 0 8px rgba(79, 227, 193, 0.6);
}

/* ============ Buttons ============ */
.btn {
  display: inline-block;
  font-family: var(--font-head);
  font-weight: 600;
  font-size: 15px;
  text-decoration: none;
  padding: 12px 26px;
  border-radius: 999px;
  border: 1px solid transparent;
  transition: transform 0.18s ease, box-shadow 0.18s ease, background 0.18s ease, border-color 0.18s ease;
}
.btn--lg { padding: 14px 32px; font-size: 16px; }
.btn--primary {
  background: var(--accent);
  color: #062a21;
}
.btn--primary:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 30px rgba(79, 227, 193, 0.25);
}
.btn--ghost {
  color: var(--text);
  border-color: var(--line);
  background: rgba(255, 255, 255, 0.02);
}
.btn--ghost:hover {
  border-color: var(--accent-line);
  color: var(--accent);
  transform: translateY(-2px);
}

/* ============ Nav ============ */
.nav {
  position: fixed;
  inset-inline: 0;
  top: 0;
  z-index: 50;
  background: rgba(10, 16, 24, 0.78);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-bottom: 1px solid transparent;
  transition: border-color 0.25s ease;
}
.nav.is-scrolled { border-bottom-color: var(--line); }

.nav__inner {
  max-width: var(--container);
  margin-inline: auto;
  padding: 14px clamp(20px, 5vw, 40px);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
}

.nav__brand {
  font-family: var(--font-head);
  font-weight: 700;
  font-size: 17px;
  color: var(--text);
  text-decoration: none;
  letter-spacing: 0.01em;
  white-space: nowrap;
}
.nav__brand-mark {
  color: var(--accent);
  font-family: var(--font-mono);
  font-weight: 500;
}

.nav__links {
  display: flex;
  align-items: center;
  gap: clamp(14px, 2.5vw, 28px);
  list-style: none;
  margin: 0;
  padding: 0;
}
.nav__links a {
  color: var(--text-dim);
  text-decoration: none;
  font-size: 14.5px;
  font-weight: 500;
  transition: color 0.15s ease;
}
.nav__links a:hover,
.nav__links a.is-active { color: var(--accent); }

.nav__cta {
  color: var(--accent) !important;
  border: 1px solid var(--accent-line);
  padding: 7px 16px;
  border-radius: 999px;
  transition: background 0.15s ease;
}
.nav__cta:hover { background: var(--accent-soft); }

.nav__toggle {
  display: none;
  flex-direction: column;
  gap: 6px;
  background: none;
  border: 0;
  padding: 8px;
  cursor: pointer;
}
.nav__toggle span {
  display: block;
  width: 22px;
  height: 2px;
  background: var(--text);
  border-radius: 2px;
  transition: transform 0.25s ease, opacity 0.25s ease;
}
.nav__toggle[aria-expanded="true"] span:first-child { transform: translateY(4px) rotate(45deg); }
.nav__toggle[aria-expanded="true"] span:last-child { transform: translateY(-4px) rotate(-45deg); }

@media (max-width: 760px) {
  .nav__toggle { display: flex; }
  .nav__links {
    position: absolute;
    top: 100%;
    inset-inline: 0;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    background: rgba(10, 16, 24, 0.97);
    backdrop-filter: blur(12px);
    border-bottom: 1px solid var(--line);
    padding: 8px 20px 20px;
    display: none;
  }
  .nav__links.is-open { display: flex; }
  .nav__links a {
    display: block;
    padding: 12px 4px;
    font-size: 16px;
  }
  .nav__cta { text-align: center; margin-top: 10px; }
}

/* ============ Hero ============ */
.hero {
  position: relative;
  min-height: 100svh;
  display: flex;
  align-items: center;
  padding: 120px 0 80px;
  overflow: hidden;
}

.hero__bg {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse 60% 50% at 70% 20%, rgba(79, 227, 193, 0.07), transparent 70%),
    radial-gradient(ellipse 50% 45% at 15% 85%, rgba(64, 120, 230, 0.07), transparent 70%),
    linear-gradient(rgba(148, 180, 220, 0.045) 1px, transparent 1px),
    linear-gradient(90deg, rgba(148, 180, 220, 0.045) 1px, transparent 1px);
  background-size: auto, auto, 48px 48px, 48px 48px;
  mask-image: radial-gradient(ellipse 90% 90% at 50% 40%, black 40%, transparent 100%);
  -webkit-mask-image: radial-gradient(ellipse 90% 90% at 50% 40%, black 40%, transparent 100%);
}

.hero__net {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

.hero__inner {
  position: relative;
  display: grid;
  grid-template-columns: 1.2fr 1fr;
  gap: clamp(32px, 6vw, 72px);
  align-items: center;
  width: 100%;
}
@media (max-width: 880px) {
  .hero__inner { grid-template-columns: 1fr; }
  .hero { padding-top: 104px; }
}

.hero__eyebrow {
  font-family: var(--font-mono);
  font-size: 13.5px;
  color: var(--text-dim);
  display: flex;
  align-items: center;
  gap: 9px;
  margin-bottom: 18px;
}
.status-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 0 rgba(79, 227, 193, 0.5);
  animation: pulse 2.4s ease-out infinite;
}
@keyframes pulse {
  0% { box-shadow: 0 0 0 0 rgba(79, 227, 193, 0.45); }
  70% { box-shadow: 0 0 0 9px rgba(79, 227, 193, 0); }
  100% { box-shadow: 0 0 0 0 rgba(79, 227, 193, 0); }
}

.hero__title {
  font-size: clamp(42px, 7vw, 72px);
  font-weight: 700;
  letter-spacing: -0.025em;
  background: linear-gradient(120deg, #f2f8fd 30%, #9fe8d8 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.hero__subtitle {
  font-size: clamp(17px, 2.2vw, 20px);
  color: var(--text-dim);
  margin-bottom: 30px;
}
.hero__subtitle strong { color: var(--text); font-weight: 600; }

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  margin-bottom: 26px;
}

.hero__hosted {
  font-family: var(--font-mono);
  font-size: 12.5px;
  color: var(--text-dim);
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
}
.hero__hosted svg { color: var(--accent); flex-shrink: 0; }

/* Terminal */
.terminal {
  background: #0b121d;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 24px 70px rgba(0, 0, 0, 0.45), 0 0 0 1px rgba(79, 227, 193, 0.05);
  overflow: hidden;
  font-family: var(--font-mono);
  font-size: 13.5px;
}
.terminal__bar {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 11px 14px;
  border-bottom: 1px solid var(--line);
  background: rgba(255, 255, 255, 0.02);
}
.terminal__dot {
  width: 11px;
  height: 11px;
  border-radius: 50%;
  background: rgba(148, 180, 220, 0.18);
}
.terminal__dot:first-child { background: rgba(255, 110, 110, 0.55); }
.terminal__dot:nth-child(2) { background: rgba(255, 200, 90, 0.55); }
.terminal__dot:nth-child(3) { background: rgba(110, 230, 140, 0.55); }
.terminal__title {
  margin-left: 8px;
  color: var(--text-dim);
  font-size: 12px;
}
.terminal__body {
  padding: 18px 18px 22px;
  min-height: 262px;
  line-height: 1.9;
  color: var(--text-dim);
  white-space: pre-wrap;
  word-break: break-word;
}
.terminal__body .t-prompt { color: var(--accent); }
.terminal__body .t-out { color: var(--text); }
.terminal__cursor {
  display: inline-block;
  width: 8px;
  height: 1.1em;
  background: var(--accent);
  vertical-align: text-bottom;
  animation: blink 1.05s steps(1) infinite;
}
@keyframes blink { 50% { opacity: 0; } }

@media (max-width: 880px) {
  .hero__terminal { max-width: 520px; }
}

/* ============ Sections ============ */
.section { padding: clamp(72px, 10vw, 120px) 0; }
.section--contact { padding-bottom: clamp(90px, 12vw, 150px); }

.section__title {
  font-size: clamp(28px, 4vw, 40px);
  margin-bottom: clamp(28px, 4vw, 44px);
  display: flex;
  align-items: baseline;
  gap: 14px;
}
.section__num {
  font-family: var(--font-mono);
  font-size: 0.5em;
  font-weight: 500;
  color: var(--accent);
  letter-spacing: 0.05em;
}
.section__lede {
  color: var(--text-dim);
  max-width: 640px;
  margin-top: -20px;
  margin-bottom: 40px;
}

/* ============ About ============ */
.about { max-width: 760px; }
.about p { color: var(--text-dim); font-size: 17.5px; }
.about p strong { color: var(--text); }
.about p.about__lead {
  color: var(--text);
  font-size: 22px;
  line-height: 1.4;
  font-weight: 500;
}
@media (max-width: 640px) { .about p.about__lead { font-size: 19px; } }
/* (These sentence-like language/eligibility pills used to need an explicit
   white-space:normal override; .pill now wraps by default, so it's redundant.) */
.about__langs {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 26px;
}

/* ============ Cards / pills ============ */
.cards-3 {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px;
  perspective: 900px;
}
/* Grid items default to min-width:auto, so a long unbreakable pill can force its track
   wider than the 1fr share and push the whole grid past the container. That made the
   page scroll sideways in the 981-1100px band, where the skills grid is 4 columns.
   min-width:0 lets the tracks shrink and the pills wrap instead. */
.cards-3 > * { min-width: 0; }
/* Credentials is 2-up by DEFAULT and only goes 4-up at 1100px, for the same reason
   as skills below. It used to be 4-up from 981px, where each card holds 160px of
   content and the prose runs at ~22 characters per line against a comfortable
   45-75, and the "CERTIFIED · OCT 2025" chip wrapped to two lines.
   ⚠️ ORDER MATTERS, same as skills: this base rule must stay ABOVE the
   max-width:620px block below, or phones inherit two columns instead of one. */
.cards-3--creds { grid-template-columns: repeat(2, 1fr); }
/* Skills is 2-up by DEFAULT and only goes 4-up at 1100px (see the min-width:1100
   block further down). A 4-column skills card holds 185px of content, and the
   longest label needs 271px — so below 1100px the columns are simply too narrow:
   at 981px they are 160px and 17 of 29 pills wrap, two of them to three lines,
   breaking after a "/" or a "·" and leaving dangling separators. 1100 rather than
   1080 because three pills measure 184.4px against 185px available — a 0.6px
   margin that a 1080px breakpoint would tip straight back into wrapping.
   ⚠️ ORDER MATTERS: this base rule must stay ABOVE the max-width:620px block
   below, or phones inherit two 224px columns instead of one. Same specificity,
   later rule wins. */
.cards-3--skills { grid-template-columns: repeat(2, 1fr); }
@media (max-width: 980px) {
  .cards-3, .cards-3--creds { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 620px) {
  .cards-3, .cards-3--creds, .cards-3--skills { grid-template-columns: 1fr; }
  /* Premium phone layout: use more of the width, tighter vertical rhythm. */
  .container { padding-inline: 16px; }
  .section { padding: 54px 0; }
  .section--contact { padding-bottom: 68px; }
  .project { padding: 24px 18px; }
  .card { padding: 22px 18px; }
  .mini { padding: 18px 16px; }
  /* NOTE: this used to re-assert `white-space: nowrap` on skill pills to keep
     them single-line. In one column they fit on most phones, but on a 320px
     screen the longest pill (271px) exceeded the 252px available and overflowed
     again — the same bug, just hidden on larger handsets. Wrapping is correct
     here too; the pills that fit still render on one line. */
}

.card {
  position: relative;
  background: var(--bg-card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 26px 24px;
  transform: translateY(var(--lift, 0)) rotateX(var(--rx, 0)) rotateY(var(--ry, 0));
  transition: transform 0.2s ease, border-color 0.2s ease;
  will-change: transform;
}
.card:hover {
  --lift: -4px;
  border-color: var(--accent-line);
}

/* Cursor-following glow (JS sets --mx/--my) */
.glow::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: radial-gradient(220px circle at var(--mx, 50%) var(--my, 50%),
              rgba(79, 227, 193, 0.08), transparent 65%);
  opacity: 0;
  transition: opacity 0.3s ease;
  pointer-events: none;
}
.glow:hover::before { opacity: 1; }
.card h3 { font-size: 18px; margin-bottom: 14px; }

.card__icon {
  width: 44px;
  height: 44px;
  border-radius: 11px;
  background: var(--accent-soft);
  color: var(--accent);
  display: grid;
  place-items: center;
  margin-bottom: 16px;
}

.pills {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.pill {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--text-dim);
  background: rgba(148, 180, 220, 0.07);
  /* Raised from 0.12. A pill can now span two or three lines, and this border is
     the ONLY cue that "Service-desk /" and "ticketing workflows" are one skill
     rather than two. At 0.12 it was 1.13:1 against the card — grey on grey doing
     semantic work. 0.22 is 1.57:1: still quiet, but actually visible. */
  border: 1px solid rgba(148, 180, 220, 0.22);
  /* Was 999px. A stadium radius resolves to half the height, so a two-line pill
     got a 25.8px corner INSIDE a 14px card — an inner radius rounder than its
     container, which is what made wrapped pills read as "a chip that broke"
     rather than as intent. At one line 999px resolved to 15.9px, so 14px is
     visually the same chip. */
  border-radius: var(--radius);
  padding: 5px 12px;
  /* A pill must never be wider than the card holding it. `nowrap` forced long
     labels to spill past the card edge, where the NEXT card's opaque background
     painted over them — so they read as text clipped mid-word rather than as
     overflow. The skills grid is the worst case: a 4-column card leaves 185px of
     content, while "Service-desk / ticketing workflows" needs 271px. Wrapping
     only engages when a label genuinely doesn't fit, so short pills stay
     single-line chips. `break-word` is the last resort for an unbreakable token
     (nothing here needs it today — the longest is "vulnerabilities" at ~108px).
     NOTE: `text-wrap: balance` was tried here and MEASURED INERT — byte-identical
     line breaks to `normal` on all 29 skills pills at 360/981/1440px, because with
     2-4 short tokens the greedy break is already the balanced one. Removed rather
     than left in looking load-bearing. */
  white-space: normal;
  overflow-wrap: break-word;
}
.pill--quiet { background: transparent; }
.pills--tags { margin-top: 22px; }
.pills--tags .pill {
  color: var(--accent);
  background: var(--accent-soft);
  border-color: transparent;
}

/* ============ Projects ============ */
.project {
  background: var(--bg-card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: clamp(24px, 4vw, 40px);
  margin-bottom: 24px;
  transition: border-color 0.2s ease;
}
.project:hover { border-color: rgba(148, 180, 220, 0.22); }

.project--flagship {
  border-color: var(--accent-line);
  background:
    radial-gradient(ellipse 70% 60% at 100% 0%, rgba(79, 227, 193, 0.05), transparent 60%),
    var(--bg-card);
}

.project__badge {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: 11.5px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #062a21;
  background: var(--accent);
  border-radius: 999px;
  padding: 4px 13px;
  margin-bottom: 16px;
}

.project h3 { font-size: clamp(20px, 2.6vw, 26px); }

.project__links {
  margin: 6px 0 0;
  font-family: var(--font-mono);
  font-size: 14px;
}
.project__links a { text-decoration: none; }
.project__links a:hover { text-decoration: underline; }

.project__oneliner {
  color: var(--text-dim);
  font-size: 15.5px;
  font-style: italic;
  max-width: 720px;
}

.project__summary p {
  color: var(--text-dim);
  max-width: 760px;
}
.project__summary strong { color: var(--text); }

.project__details { margin-top: 6px; }
.project__details summary {
  cursor: pointer;
  list-style: none;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-head);
  font-weight: 600;
  font-size: 14.5px;
  color: var(--accent);
  padding: 8px 0;
  user-select: none;
}
.project__details summary::-webkit-details-marker { display: none; }
.project__details summary::after {
  content: "";
  width: 8px;
  height: 8px;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg) translateY(-2px);
  transition: transform 0.2s ease;
}
.project__details[open] summary::after { transform: rotate(225deg) translateY(-1px); }

.project__case {
  border-top: 1px solid var(--line);
  margin-top: 12px;
  padding-top: 24px;
}
.project__details[open] .project__case {
  animation: caseIn 0.4s ease both;
}
@keyframes caseIn {
  from { opacity: 0; transform: translateY(-8px); }
  to { opacity: 1; transform: none; }
}
.case-block { margin-bottom: 22px; }
.case-block h4 {
  font-size: 13px;
  font-family: var(--font-mono);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--accent);
  margin-bottom: 10px;
}
.case-block p, .case-block li { color: var(--text-dim); font-size: 15.5px; }
.case-block strong { color: var(--text); font-weight: 600; }
.case-block p.quiet {
  border-left: 2px solid var(--line, rgba(255,255,255,.14));
  padding-left: 14px;
  font-style: italic;
  opacity: .85;
}
.case-block ul { margin: 0; padding-left: 20px; }
.case-block li { margin-bottom: 7px; }

.project--small .pills--tags { margin-top: 16px; }

/* Mini project cards */
.minis__title {
  font-size: 17px;
  color: var(--text-dim);
  margin: 40px 0 18px;
  font-family: var(--font-mono);
  font-weight: 500;
  letter-spacing: 0.04em;
}
.minis__title::before { content: "// "; color: var(--accent); }
.minis {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 16px;
}
.mini {
  position: relative;
  background: var(--bg-card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 20px 19px;
  transform: translateY(var(--lift, 0)) rotateX(var(--rx, 0)) rotateY(var(--ry, 0));
  transition: transform 0.2s ease, border-color 0.2s ease;
  will-change: transform;
}
.mini:hover { --lift: -4px; border-color: var(--accent-line); }
.mini h4 {
  font-size: 15.5px;
  margin: 0 0 8px;
}
.mini__link {
  color: var(--text);
  text-decoration: none;
}
.mini__link:hover { color: var(--accent); }
.mini__link span { color: var(--accent); font-size: 0.85em; }
.mini p {
  font-size: 13.8px;
  color: var(--text-dim);
  margin-bottom: 14px;
}

/* Architecture diagram */
.diagram {
  margin: 26px 0 10px;
  padding: clamp(14px, 2.5vw, 24px);
  background: rgba(8, 13, 20, 0.55);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow-x: auto;
}
.diagram svg {
  display: block;
  width: 100%;
  min-width: 560px;
  height: auto;
  font-family: var(--font-mono);
}
.dia-node rect, .dia-chip rect {
  fill: var(--bg-raise);
  stroke: var(--line);
  stroke-width: 1.2;
}
.dia-node text, .dia-chip text {
  fill: var(--text);
  font-size: 13px;
}
.dia-sub {
  fill: var(--text-dim) !important;
  font-size: 10px !important;
}
.dia-node--accent rect, .dia-chip--accent rect {
  stroke: var(--accent-line);
  fill: rgba(79, 227, 193, 0.06);
}
.dia-chip--accent text { fill: var(--accent); }
.dia-host rect {
  fill: rgba(148, 180, 220, 0.025);
  stroke: var(--accent-line);
  stroke-width: 1.2;
  stroke-dasharray: 5 4;
}
.dia-host-label {
  fill: var(--accent);
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.dia-flow {
  fill: none;
  stroke: var(--accent);
  stroke-width: 1.6;
  stroke-dasharray: 6 7;
  opacity: 0.65;
  animation: flow 1.1s linear infinite;
}
@keyframes flow { to { stroke-dashoffset: -13; } }
.dia-caption {
  font-family: var(--font-mono);
  font-size: 11.5px;
  color: var(--text-dim);
  margin-top: 10px;
}

/* Skills goes 4-up ONLY here, where the container has reached its full 1080px and
   each column finally gets its whole 185px of content. 621-1099px keeps the 2-column
   default set near the top of the file; below 621px the phone rule (1 column) wins.
   This block must stay AFTER the ≤620px block — being later at the same specificity
   is what lets it take effect at all. */
@media (min-width: 1100px) {
  /* The "Operations & Facilities" title wraps to two lines at this width while the
     other three don't, which pushed its pill block 20.7px below the rest of the row.
     Reserving two lines aligns all four. Scoped to ≥1100px on purpose: at 2 columns
     the cards are wide enough that no title wraps, and reserving space there would
     just open a gap under every heading. (px floor first for older engines.) */
  .cards-3--skills .card h3 { min-height: 42px; min-height: 2lh; }
  .cards-3--skills { grid-template-columns: repeat(4, 1fr); }
  /* Credentials deliberately does NOT go 4-up here, unlike skills. Measured
     2026-08-14: at 4 columns a credentials card holds 187px of content and its
     prose runs at ~21 characters per line even at 1440px, against a comfortable
     45-75. Skills gets away with 4-up because its cards hold short pills; these
     hold sentences. There is no realistic viewport where 4 columns work for
     them, so 2-up (a 2x2 block of the four cards) is the answer at every width
     above the phone breakpoint. At 1440px that gives ~62 characters per line. */
}

/* Two diagrams, one shown at a time. Scaling the wide one down to phone width put its
   labels at roughly 5.6px, so phones get a purpose-built narrow version instead of a
   shrunken copy. Both are aria-hidden; the <figure>'s aria-label is the accessible name,
   so swapping them changes nothing for a screen reader. */
.diagram .dia--narrow { display: none; }

/* Breakpoint measured in a real viewport, not estimated. The wide diagram is 760 units
   across, so its 13px labels only reach a readable ~12px once the card itself is about
   700px wide — and the card is far narrower than the viewport (615px at an 821px
   viewport, 868px at 1440px). That puts the crossover near 1000px, not the 790px a
   detached-clone probe first suggested; the clone did not inherit the real section
   padding. Below 1000px — phones, tablets and small laptop windows — the stacked
   diagram is used instead. The original 620px breakpoint left everything from 620px to
   1000px rendering the wide diagram at 9.6-11.8px. */
@media (max-width: 1000px) {
  .diagram { overflow-x: visible; }
  .diagram svg { min-width: 0; }
  .diagram .dia--wide { display: none; }
  .diagram .dia--narrow {
    display: block;
    /* Without a cap this would stretch to the full card on a tablet or small laptop
       (over 2x scale) and render enormous labels. Capped and centred, it stays
       proportionate at every width below the breakpoint. */
    /* 340px keeps the scale near 1:1 against the 320-unit viewBox, so the labels land at
       roughly 14.9px — the same size the wide diagram renders at on desktop. A larger cap
       made the tablet diagram bigger than the desktop one. */
    max-width: 340px;
    margin-inline: auto;
  }

  /* The narrow diagram still renders at about 0.89x (the card and section padding take
     roughly 127px of a 412px screen), so the type is sized up here to land back at a
     readable on-screen size rather than at the shrunken one. Measured, not guessed:
     label 14 -> ~12.5px on screen, sub 12 -> ~10.7px. */
  .diagram .dia--narrow .dia-node text,
  .diagram .dia--narrow .dia-chip text { font-size: 14px; }
  .diagram .dia--narrow .dia-sub { font-size: 12px !important; }
  .diagram .dia--narrow .dia-host-label { font-size: 13px; }
}

/* Small phones. The container and card padding take a fixed ~127px whatever the screen
   width, so on a 360px phone the diagram only got 233px and the labels fell back to
   10.2px. Letting the card bleed into the container's own padding (which is empty space)
   and trimming its inner padding buys back about 32px, and the type is sized up to suit.
   Measured at 360px: label 10.2px -> 12.4px, sub 8.7px -> 10.3px. */
@media (max-width: 480px) {
  .diagram {
    margin-inline: -12px;
    padding-inline: 10px;
  }
  .diagram .dia--narrow .dia-node text,
  .diagram .dia--narrow .dia-chip text { font-size: 15px; }
  .diagram .dia--narrow .dia-sub { font-size: 12.5px !important; }
}

/* ============ Experience ============ */
.timeline {
  position: relative;
  max-width: 760px;
  padding-left: 28px;
}
.timeline::before {
  content: "";
  position: absolute;
  left: 5px;
  top: 8px;
  bottom: 8px;
  width: 2px;
  background: linear-gradient(var(--accent-line), var(--line));
}
.timeline__item {
  position: relative;
  margin-bottom: 40px;
}
.timeline__item:last-child { margin-bottom: 0; }
.timeline__item::before {
  content: "";
  position: absolute;
  left: -28px;
  top: 8px;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--bg);
  border: 2.5px solid var(--accent);
}
.timeline__date {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--accent);
  letter-spacing: 0.06em;
  margin: 0 0 4px;
}
.timeline__item h3 { font-size: 19px; }
.timeline__item ul {
  margin: 0;
  padding-left: 20px;
  color: var(--text-dim);
}
.timeline__item li { margin-bottom: 6px; font-size: 15.5px; }

/* ============ Credentials ============ */
.card--cred p { color: var(--text-dim); font-size: 14.5px; margin: 0; }
.cred__status {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-dim);
  border: 1px solid var(--line);
  /* Same family as .pill, same reason: this chip wraps to two lines in the
     981-1080px band (where the Credentials grid is still 4-up), and a stadium
     radius turns it into a capsule rounder than the card holding it. */
  border-radius: var(--radius);
  padding: 3px 11px;
  margin-bottom: 14px;
}
.cred__status--done {
  color: #062a21;
  background: var(--accent);
  border-color: var(--accent);
  font-weight: 500;
}
.cred__status--edu { color: var(--accent); border-color: var(--accent-line); }

/* ============ Contact ============ */
.contact {
  max-width: 640px;
  text-align: center;
  margin-inline: auto;
}
.contact__lede { color: var(--text-dim); font-size: 18px; }
.contact__lede strong { color: var(--text); }
.contact__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 14px;
  margin: 32px 0 22px;
}
.contact__note {
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--text-dim);
  margin: 0;
}

/* ============ Footer ============ */
.footer {
  border-top: 1px solid var(--line);
  padding: 28px 0;
}
.footer__inner {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 10px;
}
.footer p {
  margin: 0;
  font-size: 13.5px;
  color: var(--text-dim);
}
.footer__hosted { font-family: var(--font-mono); font-size: 12.5px; }
.footer__links { display: flex; gap: 18px; }
.footer__links a {
  color: var(--text-dim);
  text-decoration: none;
  font-size: 13.5px;
}
.footer__links a:hover { color: var(--accent); }

/* ============ Reveal animations ============ */
/* Hidden states only apply under html.js, so the page is fully
   readable with JavaScript disabled (progressive enhancement). */
html.js .reveal, html.js .reveal-up {
  opacity: 0;
  transform: translateY(22px);
  transition: opacity 0.7s ease, transform 0.7s ease;
}
html.js .reveal-up { transition-delay: calc(var(--d, 0) * 110ms); }
html.js .reveal.is-visible, html.js .reveal-up.is-visible {
  opacity: 1;
  transform: none;
}

/* No-JS and reduced-motion fallbacks: everything visible, nothing moves */
@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;
  }
  html.js .reveal, html.js .reveal-up { opacity: 1; transform: none; }
}
