/* ============================================================
   site.css - this website's style: the theme tokens, the page's
   backdrop, the header and nav, the hero and its portrait, the
   project cards, the about and contact blocks of the footer, the
   About page, and their breakpoints.

   The engine's style is in libraries/harrisxrwebengine/engine.css.
   Every page links that file first and this one second, so a rule
   here wins a tie, and the :root below sets the engine's theme. The
   editor injects its own rules into <head> at runtime, after both
   files, so the editor wins a tie with either.

   Rule: do not reorder a block to tidy the file. CSS resolves ties
   by source order, so a moved block can change the page without
   changing a single declaration.

   Sections:
     1. FOUNDATION AND CHROME       4. ABOUT AND CONTACT
     2. HERO                        5. SHARED EFFECTS
     3. FEATURES                    6. RESPONSIVE
   ============================================================ */

/* ============================================================
   1. FOUNDATION AND CHROME
   ------------------------------------------------------------
   The theme tokens, the page's backdrop and layout helpers, and the
   parts of the page that sit outside any one section: the progress
   bar, the header and nav, and the buttons. The reset and the shared
   type are in engine.css.
   ============================================================ */

/* ------------------------------------------------------------
   DESIGN SYSTEM: soft-charcoal / azure
   ------------------------------------------------------------
   The theme tokens. engine.css gives every one of them a default,
   and this block sets this site's own. */
:root {
  /* surfaces */
  --bg:        #16181d;   /* soft charcoal: page floor               */
  --bg-deep:   #101217;   /* deepest pockets                          */
  --panel:     #1e2128;   /* lifted panel                             */
  --panel-2:   #23262f;   /* lifted panel, hover                      */
  --line:      rgba(255,255,255,.08);   /* hairline borders          */
  --line-soft: rgba(255,255,255,.05);

  /* text */
  --text:      #ebeef4;   /* near-white primary                       */
  --text-soft: #c3c9d4;
  --muted:     #969eaa;   /* secondary / meta                         */
  --dim:       #6b7280;   /* faintest                                 */

  /* accent: azure is the only ACTIVE accent */
  --accent:        #4aa5e8;
  --accent-bright: #6fbcf2;
  --accent-deep:   #2e87cf;
  --accent-glow:   rgba(74,165,232,.35);
  --accent-faint:  rgba(74,165,232,.10);

  /* staged accents: dormant, ready when we want them */
  --c-orange: #f0883e;
  --c-yellow: #f2c14e;
  --c-lime:   #b5e853;

  /* type */
  --font: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
          Helvetica, Arial, sans-serif;

  /* spacing rhythm */
  --section-pad: clamp(4rem, 9.5vw, 8rem);
  --gutter:      clamp(1.25rem, 5vw, 3rem);
  --maxw:        1180px;

  /* shape + motion */
  --radius:    16px;
  --radius-sm: 10px;
  --ease:      cubic-bezier(.22, 1, .36, 1);   /* easeOutExpo-ish      */
  --ease-soft: cubic-bezier(.4, 0, .2, 1);
}

/* ------------------------------------------------------------
   BACKDROP AND LAYOUT HELPERS
   ------------------------------------------------------------ */
/* a faint, fixed astral grid behind everything for depth */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -2;
  background:
    radial-gradient(1200px 800px at 78% -10%, rgba(74,165,232,.08), transparent 60%),
    radial-gradient(900px 700px at -10% 110%, rgba(74,165,232,.06), transparent 55%),
    var(--bg);
}
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: .35;
  background-image:
    linear-gradient(var(--line-soft) 1px, transparent 1px),
    linear-gradient(90deg, var(--line-soft) 1px, transparent 1px);
  background-size: 64px 64px;
  -webkit-mask-image: radial-gradient(120% 80% at 50% 0%, #000 35%, transparent 78%);
          mask-image: radial-gradient(120% 80% at 50% 0%, #000 35%, transparent 78%);
}

/* layout helpers */
.wrap { width: 100%; max-width: var(--maxw); margin-inline: auto; padding-inline: var(--gutter); }
/* Keeps a name such as a model number on one line.
   A line may otherwise break at the name's hyphen. */
.nowrap { white-space: nowrap; }
section { scroll-margin-top: 84px; }

/* ------------------------------------------------------------
   SCROLL PROGRESS BAR
   ------------------------------------------------------------ */
.progress {
  position: fixed; top: 0; left: 0;
  height: 3px; width: 100%;
  transform: scaleX(0); transform-origin: 0 50%;
  background: linear-gradient(90deg, var(--accent-deep), var(--accent-bright));
  z-index: 1000;
  will-change: transform;
}

/* ------------------------------------------------------------
   HEADER / NAV
   ------------------------------------------------------------ */
.site-header {
  position: fixed; top: 0; left: 0; right: 0;
  z-index: 900;
  display: flex; align-items: center; justify-content: space-between;
  padding: 1.1rem var(--gutter);
  transition: padding .4s var(--ease-soft);
}
/* Blur/background live on a pseudo-element so the header box itself carries
   no filter, because a filtered ancestor would otherwise trap the fixed mobile
   drawer inside the (short) header box instead of the viewport. */
.site-header::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: rgba(18,20,25,.72);
  backdrop-filter: saturate(140%) blur(14px);
  -webkit-backdrop-filter: saturate(140%) blur(14px);
  border-bottom: 1px solid transparent;
  opacity: 0; pointer-events: none;
  transition: opacity .4s var(--ease-soft);
}
.site-header.scrolled::before { opacity: 1; border-bottom-color: var(--line); }
.site-header.scrolled { padding-block: .8rem; }

.brand {
  display: inline-flex; align-items: center; gap: 0;
  font-weight: 800; letter-spacing: .04em; font-size: .95rem;
}
.brand .mark {
  width: 30px; height: 30px; border-radius: 8px; flex: none;
  display: grid; place-items: center;
  background: var(--panel);
  border: 1px solid var(--line);
  color: var(--accent);
  font-size: .72rem; font-weight: 900; letter-spacing: -.04em;
  margin-right: .7rem;
  transition: transform .5s var(--ease), border-color .4s, color .4s;
}
.brand:hover .mark { transform: rotate(-6deg) scale(1.05); border-color: var(--accent); }
/* Desktop: name + summary on ONE line, separated by a pipe.
   Mobile: stacked (two lines, no pipe); see the 880px media query. */
.brand__name { display: flex; align-items: baseline; line-height: 1.1; }
/* The name starts large at the top of the page and shrinks to its compact
   size once the header is `scrolled`. */
.brand__title {
  color: var(--text); font-weight: 800;
  font-size: 1.4rem;
  transition: font-size .45s var(--ease-soft);
}
.site-header.scrolled .brand__title { font-size: .95rem; }
.brand__sub {
  color: var(--muted); font-weight: 600;
  font-size: .98rem; letter-spacing: .02em;
  transition: font-size .45s var(--ease-soft);
}
.site-header.scrolled .brand__sub { font-size: .8rem; }
.brand__sub::before {
  content: "|"; color: var(--line);
  margin: 0 .6rem; font-weight: 400;
}

.nav { display: flex; align-items: center; gap: clamp(2rem, 4vw, 4rem); }
.nav a {
  position: relative;
  font-size: .82rem; font-weight: 600; letter-spacing: .14em; text-transform: uppercase;
  color: var(--muted);
  padding: .35rem .5rem;
  transition: color .3s var(--ease-soft);
}
.nav a::after {
  content: ""; position: absolute; left: .5rem; bottom: -2px;
  width: calc(100% - 1rem); height: 2px; background: var(--accent);
  transform: scaleX(0); transform-origin: 0 50%;
  transition: transform .4s var(--ease);
}
.nav a:hover, .nav a.active { color: var(--text); }
.nav a:hover::after, .nav a.active::after { transform: scaleX(1); }

.nav__toggle { display: none; }

/* dim scrim behind the open mobile drawer */
.nav-overlay {
  position: fixed; inset: 0; z-index: 880;
  background: rgba(0,0,0,.5);
  opacity: 0; pointer-events: none;
  transition: opacity .45s var(--ease-soft);
}
.nav-overlay.open { opacity: 1; pointer-events: auto; }
body.nav-open { overflow: hidden; }

/* ------------------------------------------------------------
   BUTTONS
   ------------------------------------------------------------ */
.btn {
  --b: var(--accent);
  display: inline-flex; align-items: center; gap: .7rem;
  font-weight: 700; font-size: .95rem; letter-spacing: .02em;
  padding: .9rem 1.5rem;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--panel);
  color: var(--text);
  cursor: pointer;
  transition: transform .4s var(--ease), border-color .4s, background .4s, box-shadow .4s;
  will-change: transform;
}
.btn:hover {
  transform: translateY(-3px);
  border-color: var(--accent);
  box-shadow: 0 14px 36px -16px var(--accent-glow);
}
.btn--ghost { background: transparent; }
.btn .arrow { transition: transform .5s var(--ease); }
.btn:hover .arrow { transform: translateY(3px); }

/* ============================================================
   2. HERO
   ------------------------------------------------------------
   The opening statement, the portrait, and the halo behind it.
   ============================================================ */

/* ------------------------------------------------------------
   HERO
   ------------------------------------------------------------ */
.hero {
  position: relative;
  min-height: clamp(620px, 92vh, 920px);
  display: flex; align-items: center;
  /* Headroom above the eyebrow/headline: base 6rem + ~50px (3.125rem) so the
     hero content sits lower beneath the fixed header. Mobile gets a smaller
     bump (see the ≤880px block). */
  padding-top: 9.125rem;
  overflow: hidden;
}
/* The hero backdrop: one concentrated blue halo. Position and size come
   from --halo-x, --halo-y and --halo-size, so a breakpoint can move the
   halo without new rules. */
.hero__stage {
  position: absolute; inset: 0;
  z-index: -1; pointer-events: none;
  --halo-x: 74%; --halo-y: 50%;
  --halo-size: 634px;
}
.hero__halo {
  position: absolute; left: var(--halo-x); top: var(--halo-y);
  transform: translate(-50%, -50%);
  width: var(--halo-size); height: var(--halo-size);
  border-radius: 50%;
  background: radial-gradient(circle,
      rgba(74,165,232,.55) 0%,
      rgba(56,150,220,.34) 28%,
      rgba(40,120,200,.13) 52%,
      transparent 70%);
  filter: blur(16px);
  animation: haloPulse 9s var(--ease-soft) infinite alternate;
}
@keyframes haloPulse {
  from { transform: translate(-50%, -50%) scale(1);    opacity: .9; }
  to   { transform: translate(-50%, -50%) scale(1.07); opacity: 1; }
}
.hero__inner { max-width: 980px; }
.hero h1 {
  font-size: clamp(2.5rem, 4.8vw, 3.6rem);
  line-height: 1.07;
  letter-spacing: -.025em;
  font-weight: 800;
  margin-top: 1.4rem;
  text-wrap: balance;
}
.hero h1 .hl { color: var(--accent); }
.hero__sub {
  font-size: clamp(1.12rem, 1.8vw, 1.5rem);
  color: var(--text-soft);
  font-weight: 400;
  max-width: 46ch;
  margin-top: 1.7rem;
  line-height: 1.5;
}
.hero__note {
  font-style: italic;
  color: var(--muted);
  margin-top: 1.1rem;
  max-width: 44ch;
}
.hero__cta { margin-top: 2.6rem; display: flex; gap: 1rem; flex-wrap: wrap; }

/* keep the hero text above the halo and the portrait */
.hero .wrap { position: relative; z-index: 2; }

/* ------------------------------------------------------------
   HERO PORTRAIT
   ------------------------------------------------------------
   The photo stands on the hero's floor at the right edge of the
   content column, and the halo glows behind the head. One token,
   --portrait-w, sizes it. The text column, the hero's height and the
   halo follow that token. The file fades the photo's lower edge to
   transparent, so it meets the next section with no line.
   site.js reads .hero__portrait to decide the header's look. */
.hero { --portrait-w: clamp(280px, 34vw, 460px); }
.hero__portrait {
  position: absolute; bottom: 0; z-index: 1;
  /* the right edge of the .wrap content, at any window width */
  right: calc(max(0px, (100% - var(--maxw)) / 2) + var(--gutter));
  width: var(--portrait-w); height: auto;
  pointer-events: none; user-select: none;
}

/* Desktop: the hero is tall enough to stand the portrait clear of the
   header, and the text keeps to the room the portrait leaves. */
@media (min-width: 881px) {
  .hero {
    /* 1.221 is the photo's height over its width */
    min-height: calc(var(--portrait-w) * 1.221 + 6.5rem);
    align-items: start;
  }
  .hero__inner { max-width: min(640px, calc(100% - var(--portrait-w) - 2.5rem)); }
  .hero__stage {
    --halo-x: calc(100% - max(0px, (100% - var(--maxw)) / 2) - var(--gutter) - var(--portrait-w) / 2);
    --halo-y: calc(100% - var(--portrait-w) * .74);
    --halo-size: calc(var(--portrait-w) * 1.45);
  }
}

/* hero entrance animation (plays on load) */
.reveal-hero > * { opacity: 0; transform: translateY(26px); }
.loaded .reveal-hero > * {
  animation: heroUp .9s var(--ease) forwards;
}
.loaded .reveal-hero > *:nth-child(1) { animation-delay: .05s; }
.loaded .reveal-hero > *:nth-child(2) { animation-delay: .16s; }
.loaded .reveal-hero > *:nth-child(3) { animation-delay: .28s; }
.loaded .reveal-hero > *:nth-child(4) { animation-delay: .40s; }
.loaded .reveal-hero > *:nth-child(5) { animation-delay: .52s; }
@keyframes heroUp { to { opacity: 1; transform: none; } }

/* the eyebrow sits outside .reveal-hero, so it gets its own matching
   entrance, slightly ahead of the headline. */
.hero .wrap > .eyebrow { opacity: 0; transform: translateY(26px); }
.loaded .hero .wrap > .eyebrow { animation: heroUp .9s var(--ease) .02s forwards; }

/* ============================================================
   3. WORK
   ------------------------------------------------------------
   Section headings, the What I do band, the project cards, and
   the gallery page's intro. A project card is the editor's
   project block, so the editor's project form edits it. The
   carousel in a card is the engine's, in engine.css.
   ============================================================ */

/* ------------------------------------------------------------
   SECTION HEADINGS
   ------------------------------------------------------------ */
.section-head { max-width: 60ch; margin-bottom: clamp(2.5rem, 6vw, 4.5rem); }
.section-head h1,
.section-head h2 {
  font-size: clamp(2rem, 4.4vw, 2.9rem);
  letter-spacing: -.02em; font-weight: 800; line-height: 1.08;
  margin-top: 1rem;
}
.section-head p {
  color: var(--muted); margin-top: 1rem;
  font-size: clamp(1rem, 1.4vw, 1.18rem);
}

/* ------------------------------------------------------------
   WHAT I DO
   ------------------------------------------------------------
   Three columns, one for each kind of work, and the line under
   them that names where the three meet. It sits close under the
   hero, and the work after it starts closer than a full section,
   because the band introduces the cards. */
.focus { padding-top: clamp(2rem, 5vw, 4rem); }
.focus .section-head { margin-bottom: clamp(1.2rem, 2.6vw, 1.8rem); }
.focus__grid {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: clamp(1rem, 2.4vw, 1.6rem);
}
.focus__col {
  display: flex; flex-direction: column; gap: .9rem;
  padding: clamp(1.3rem, 2.4vw, 1.8rem);
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--panel);
}
.focus__title { font-size: 1.12rem; font-weight: 800; letter-spacing: -.01em; line-height: 1.25; }
/* the text grows, so the chips line up along the bottom of the row */
.focus__text { flex: 1 1 auto; color: var(--text-soft); font-size: .95rem; }
.focus__center {
  margin-top: clamp(1.4rem, 3vw, 2rem); max-width: 64ch;
  color: var(--muted); font-size: clamp(1rem, 1.4vw, 1.1rem);
}
.focus__center b { color: var(--text); font-weight: 700; }
.focus + .work { padding-top: clamp(3.5rem, 7vw, 5.5rem); }

/* ------------------------------------------------------------
   PROJECTS
   ------------------------------------------------------------ */
.work { padding-block: var(--section-pad); counter-reset: project; }

.project {
  display: grid;
  grid-template-columns: 1.35fr 1fr;
  gap: clamp(1.8rem, 4vw, 4rem);
  align-items: start;   /* top-align both columns; image is nudged to title level below */
  padding-block: clamp(3rem, 7vw, 6rem);
  border-top: 1px solid var(--line-soft);
  counter-increment: project;     /* drives the automatic 01, 02, 03… index */
}
.project:first-of-type { border-top: 0; }

/* AUTO LEFT/RIGHT: projects alternate sides by position, so reordering or
   pasting a block needs no class change. Odd projects use the base rule above
   (image left); even projects flip (image right). :nth-of-type counts only
   <article> siblings, so the non-article .section-head doesn't throw it off. */
.project:nth-of-type(even) { grid-template-columns: 1fr 1.35fr; }
.project:nth-of-type(even) .project__media { order: 2; }
.project:nth-of-type(even) .project__body  { order: 1; }

/* media column (the .gallery holder lives here; see PROJECT IMAGE GALLERY
   in engine.css).
   min-width:0 lets this grid track size by its fr share rather than growing to
   fit content, so a long no-wrap caption clips with an ellipsis instead of
   widening the column. */
/* Nudge the image down so its top sits at the project TITLE, leaving the
   "01" index above it (the header stands out). ~= the eyebrow's height;
   zeroed out in the ≤880px stacked layout where the image is on top. */
.project__media { position: relative; min-width: 0; margin-top: 2.2rem; }

/* body column */
.project__index {
  font-size: .8rem; font-weight: 800; letter-spacing: .2em;
  color: var(--accent); display: block; margin-bottom: .9rem;
}
/* AUTO NUMBER: the index span is left empty in the HTML; this prints the
   running count as 01, 02, 03… so a reordered or pasted block self-numbers. */
.project__index::before { content: counter(project, decimal-leading-zero); }
.project__title {
  font-size: clamp(1.6rem, 3vw, 2.15rem);
  font-weight: 800; letter-spacing: -.02em; line-height: 1.1;
}
.project__meta {
  font-size: .85rem; color: var(--muted); margin-top: .5rem;
  letter-spacing: .04em;
}
.project__lead { margin-top: 1.2rem; color: var(--text-soft); }
.project__desc { margin-top: 1rem; color: var(--muted); font-size: .98rem; }

.stats { display: flex; flex-wrap: wrap; gap: .6rem; margin-top: 1.4rem; }
.stat {
  border: 1px solid var(--line);
  background: var(--panel);
  border-radius: 999px;
  padding: .4rem .9rem;
  font-size: .82rem; font-weight: 600; color: var(--text-soft);
}
.stat b { color: var(--accent); font-weight: 800; }

.specs { margin-top: 1.5rem; display: grid; gap: .55rem; }
.spec { display: grid; grid-template-columns: 84px 1fr; gap: 1rem; font-size: .88rem; }
.spec dt {
  color: var(--dim); text-transform: uppercase; letter-spacing: .12em;
  font-size: .72rem; font-weight: 700; padding-top: .15rem;
}
.spec dd { color: var(--text-soft); }

.chips { display: flex; flex-wrap: wrap; gap: .4rem; }
.chip {
  font-size: .76rem; color: var(--muted);
  border: 1px solid var(--line-soft); border-radius: 7px;
  padding: .22rem .6rem; background: rgba(255,255,255,.02);
}

.highlights { margin-top: 1.4rem; display: grid; gap: .5rem; list-style: none; }
.highlights li {
  position: relative; padding-left: 1.4rem; color: var(--text-soft); font-size: .92rem;
}
.highlights li::before {
  content: ""; position: absolute; left: 0; top: .62em;
  width: 7px; height: 7px; border-radius: 2px; background: var(--accent);
  transform: rotate(45deg);
}

/* A link in a card's copy wears the accent, because links inherit the
   text's color and this one would read as plain text. */
.project__lead a, .project__desc a, .highlights a {
  color: var(--accent); border-bottom: 1px solid transparent; transition: border-color .3s;
}
.project__lead a:hover, .project__desc a:hover, .highlights a:hover { border-bottom-color: var(--accent); }

/* AUTO-HIDE empty optional sections: every project block keeps its optional
   bits (stats, highlights) as tags for a uniform copy-paste shape; when one is
   left empty it vanishes. The sweep in site.js also
   removes any left with stray whitespace (which would defeat :empty), so an
   empty box never shows on the page. */
.project .stats:empty,
.project .highlights:empty { display: none; }

/* ------------------------------------------------------------
   THE GALLERY PAGE'S INTRO
   ------------------------------------------------------------
   The heading and the note above the train grid. The grid is the
   engine's, in engine.css. */
.gal-intro { padding: var(--section-pad) 0 clamp(1.6rem, 3vw, 2.6rem); }
.gal-note { margin-top: 1rem; color: var(--muted); font-size: .88rem; }

/* ============================================================
   4. ABOUT AND CONTACT
   ============================================================ */

/* ------------------------------------------------------------
   ABOUT
   ------------------------------------------------------------ */
.about { padding-block: var(--section-pad); }
.about__grid {
  display: grid; grid-template-columns: 300px 1fr;
  gap: clamp(2rem, 6vw, 5rem); align-items: start;
}
.portrait {
  position: relative; width: 240px; aspect-ratio: 1; margin-inline: auto;
}
.portrait img {
  position: relative; z-index: 1;
  width: 100%; height: 100%; border-radius: 50%; object-fit: cover;
  border: 1px solid var(--line);
}
.portrait::before {
  content: ""; position: absolute; inset: -10px; border-radius: 50%;
  border: 1px solid var(--accent-faint);
  background: conic-gradient(from 200deg, transparent, var(--accent-glow), transparent 70%);
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 2px), #000 0);
          mask: radial-gradient(farthest-side, transparent calc(100% - 2px), #000 0);
  animation: spin 18s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

.about__body p { color: var(--text-soft); }
.about__body p + p { margin-top: 1.1rem; }
.about__body .lede { font-size: clamp(1.15rem, 1.8vw, 1.4rem); color: var(--text); }
.about__body .place { color: var(--muted); margin-top: 1.3rem; }
/* A link in the place line wears the accent, because links inherit the
   text's color and this one would read as plain text. */
.about__body .place a { color: var(--accent); border-bottom: 1px solid transparent; transition: border-color .3s; }
.about__body .place a:hover { border-bottom-color: var(--accent); }
.about__body .textlink { margin-top: 1.6rem; }

/* ------------------------------------------------------------
   THE ABOUT PAGE
   ------------------------------------------------------------
   about.html is a heading over the site footer, and the footer's
   About is the page's content. The heading names the page, so the
   About shows no eyebrow of its own there. It starts close under
   the heading, because the heading opens the same subject. */
.page-intro { padding-top: var(--section-pad); }
.about-page .site-footer .about { padding-top: 0; }
.about-page .site-footer .about .section-head { display: none; }

/* ------------------------------------------------------------
   CONTACT / FOOTER
   ------------------------------------------------------------ */
.contact {
  padding-block: var(--section-pad);
  text-align: center;
  position: relative;
}
.contact h2 {
  font-size: clamp(2.6rem, 8vw, 5rem);
  font-weight: 900; letter-spacing: -.03em; line-height: 1;
}
.contact .email {
  display: inline-block; margin-top: 1.6rem;
  font-size: clamp(1.3rem, 3.4vw, 2.1rem);
  font-weight: 700; color: var(--accent);
  border-bottom: 2px solid transparent;
  transition: border-color .3s, color .3s, transform .4s var(--ease);
}
.contact .email:hover { border-bottom-color: var(--accent); color: var(--accent-bright); transform: translateY(-2px); }

/* the contact buttons, in one row */
.contact__actions {
  margin-top: 2.4rem;
  display: flex; flex-direction: column; align-items: center; gap: .9rem;
}
.contact__row { display: flex; flex-wrap: wrap; justify-content: center; gap: .9rem; }
.contact-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .6rem;
  min-width: 150px;
  padding: .85rem 1.7rem; border-radius: 999px;
  border: 1px solid var(--line); background: var(--panel);
  color: var(--text); font-weight: 700; font-size: .95rem;
  transition: transform .4s var(--ease), border-color .4s, color .4s, background .4s, box-shadow .4s;
}
.contact-btn:hover {
  transform: translateY(-3px); border-color: var(--accent);
  background: var(--panel-2); box-shadow: 0 14px 36px -20px var(--accent-glow);
}
.contact-btn svg { width: 18px; height: 18px; fill: currentColor; flex: none; }
.contact-btn--call { color: var(--accent); border-color: var(--accent-faint); }

/* quiet finishing line at the very bottom */
.endbar {
  margin-top: clamp(3.5rem, 8vw, 6rem);
  padding-block: 1.5rem 1.8rem;
  text-align: center;
  font-size: .78rem; letter-spacing: .05em; color: var(--dim);
}

/* ============================================================
   5. SHARED EFFECTS
   ------------------------------------------------------------
   The reduced-motion rules for the hero and the footer's portrait.
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  .hero__halo { animation: none !important; transform: translate(-50%, -50%) !important; }
  .portrait::before { animation: none !important; }
}

/* ============================================================
   6. RESPONSIVE
   ------------------------------------------------------------
   Every breakpoint, gathered. Kept together so the full set of
   overrides for a width can be read in one place.
   ============================================================ */

/* ------------------------------------------------------------
   RESPONSIVE
   ------------------------------------------------------------ */
/* The nav becomes a drawer here, and not at the phone breakpoint below.
   The line is where the header runs out of room, which is a measured number:
   the brand lockup and five nav links stop fitting side by side at 1040. Move
   it when the nav gains or loses an item, and measure again rather than
   guessing - the two numbers are unrelated, which is why they are two rules. */
@media (max-width: 1039px) {
  /* Header bar stays pinned ON TOP; the drawer slides in BELOW it so the
     brand lockup is always visible. Header sits above the panel. */
  .site-header { z-index: 960; }

  .nav {
    position: fixed; top: var(--header-h, 64px); right: 0; bottom: 0;
    width: min(82vw, 340px);
    flex-direction: column; align-items: stretch; justify-content: flex-start;
    gap: .15rem; padding: 1.4rem 1.5rem calc(1.5rem + env(safe-area-inset-bottom));
    background: rgba(18,20,25,.98);
    backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px);
    border-left: 1px solid var(--line);
    border-top: 1px solid var(--line);
    box-shadow: -24px 0 60px -30px rgba(0,0,0,.8);
    overflow-y: auto;
    transform: translateX(100%);
    transition: transform .5s var(--ease);
    z-index: 940;
  }
  .nav.open { transform: translateX(0); }
  .nav a {
    font-size: 1.18rem; letter-spacing: .01em; text-transform: none;
    color: var(--text-soft); padding: .7rem .2rem;
  }
  .nav a::after { display: none; }
  .nav a:hover, .nav a.active { color: var(--accent); }

  /* Work/About/Contact: take the open space and vertically center */
  .nav__links {
    flex: 1 1 auto;
    display: flex; flex-direction: column; justify-content: center; gap: .3rem;
  }

  .nav__toggle {
    display: inline-flex; flex-direction: column; gap: 5px;
    width: 42px; height: 42px; align-items: center; justify-content: center;
    margin-left: auto;   /* always pin to the right edge of the header */
    background: rgba(35,38,47,.92); border: 1px solid var(--line); border-radius: 10px;
    box-shadow: 0 6px 18px -8px rgba(0,0,0,.7);
    cursor: pointer; z-index: 970; position: relative;
  }
  .nav__toggle span { width: 18px; height: 2px; background: var(--text); transition: transform .35s var(--ease), opacity .25s; }
  .nav__toggle.open span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
  .nav__toggle.open span:nth-child(2) { opacity: 0; }
}

@media (max-width: 880px) {
  /* mobile: stack the name over the summary (no pipe).
     Large at the top, compact once scrolled (see desktop rules). */
  .brand__name { flex-direction: column; align-items: flex-start; }
  .brand__title { font-size: 1.18rem; }
  .site-header.scrolled .brand__title { font-size: .92rem; }
  .brand__sub { font-size: .78rem; margin-top: .12rem; }
  .site-header.scrolled .brand__sub { font-size: .68rem; }
  .brand__sub::before { content: none; }

  /* Mobile hero: the portrait stands over the text, and the halo moves
     up behind the portrait. The text rises a little into the photo's
     faded edge. */
  .hero {
    display: block;
    min-height: auto;
    padding-top: 5.5rem;
    padding-bottom: 3rem;
  }
  .hero__portrait {
    position: relative; right: auto; bottom: auto;
    width: min(72vw, 340px); margin: 0 auto -1.5rem;
  }
  .hero__sub, .hero__note { max-width: 100%; }
  /* keep both hero calls to action on ONE row on phones: nowrap + equal-width
     flex items that can shrink (min-width:0), with trimmed padding/text. */
  .hero__cta { flex-wrap: nowrap; gap: .55rem; width: 100%; }
  .hero__cta .btn {
    flex: 1 1 0; min-width: 0; justify-content: center;
    padding: .8rem .6rem; gap: .4rem;
    font-size: .9rem; white-space: nowrap;
  }
  .hero .eyebrow {
    display: block; line-height: 1.6;
    font-size: .72rem; letter-spacing: .16em;
  }
  .hero .eyebrow::before {
    display: inline-block; vertical-align: middle; margin-right: .5rem;
  }
  .hero__stage {
    --halo-x: 50%; --halo-y: calc(5.5rem + min(72vw, 340px) * .5);
    --halo-size: clamp(340px, 88vw, 540px);
  }

  .focus__grid { grid-template-columns: 1fr; }

  .project, .project:nth-of-type(even) { grid-template-columns: 1fr; gap: 1.8rem; }
  .project:nth-of-type(even) .project__media { order: 0; }
  .project:nth-of-type(even) .project__body  { order: 0; }
  .project__media { margin-top: 0; }   /* stacked: image on top, no title-level nudge */

  .about__grid { grid-template-columns: 1fr; }
  .portrait { margin-bottom: .5rem; }

  .spec { grid-template-columns: 72px 1fr; gap: .7rem; }

  /* keep the three contact buttons on a single row on phones */
  .contact__row { flex-wrap: nowrap; gap: .55rem; width: 100%; }
  .contact__row .contact-btn {
    flex: 1 1 0; min-width: 0; padding: .8rem .6rem; gap: .4rem;
    font-size: .9rem;
  }
}

@media (max-width: 400px) {
  /* tighten the lockup on very narrow phones (large at top, compact scrolled) */
  .brand__title { font-size: 1.02rem; }
  .site-header.scrolled .brand__title { font-size: .82rem; }
  .brand__sub { font-size: .66rem; }
  .site-header.scrolled .brand__sub { font-size: .58rem; }
  .brand .mark { width: 28px; height: 28px; }

  /* shrink the contact buttons a touch more so 3 still fit */
  .contact__row { gap: .4rem; }
  .contact__row .contact-btn { padding: .75rem .35rem; font-size: .82rem; gap: .32rem; }
  .contact__row .contact-btn svg { width: 16px; height: 16px; }

  /* same tightening for the hero CTAs on very narrow phones */
  .hero__cta { gap: .4rem; }
  .hero__cta .btn { padding: .72rem .4rem; font-size: .8rem; gap: .3rem; }
  .hero__cta .btn .arrow { display: none; }
}
