/* ===========================================================================
   present.css — the 2026 deck.

   Theme: deep sky. The 1888 door is brass and gaslight; this one is the same
   person looking the other way, at the sky. Everything is dark, wide-tracked
   and quiet, with one warm amber accent kept from the Victorian side so the
   two eras read as one site.

   Layout rule for the whole file: nothing has a fixed pixel width. Every
   grid is `auto-fit` + `minmax(min(<ideal>, 100%), 1fr)`, which collapses to a
   single column on a phone without a single media query firing. The media
   queries at the bottom only handle type scale and the nav.
   ========================================================================= */

:root {
  color-scheme: dark;

  /* --- ground ------------------------------------------------------- */
  --bg: #05060d;
  --bg-2: #090b16;
  --ink: #e8ecfb;
  --ink-dim: rgba(232, 236, 251, .62);
  --ink-faint: rgba(232, 236, 251, .36);

  /* --- light -------------------------------------------------------- */
  --violet: #8b7cf6;
  --cyan: #58d3f7;
  --mint: #4fd1b3;
  --amber: #f0ad57;          /* the one thing carried over from 1888 */
  --rose: #f472a6;

  /* --- surfaces ----------------------------------------------------- */
  --glass: linear-gradient(158deg, rgba(255,255,255,.075), rgba(255,255,255,.022) 46%, rgba(255,255,255,.010));
  --edge: rgba(255, 255, 255, .085);
  --edge-lit: rgba(255, 255, 255, .26);
  --shadow: 0 24px 60px rgba(0, 0, 0, .6);
  --radius: 22px;

  /* --- type --------------------------------------------------------- */
  --display: "Sora", "Space Grotesk", system-ui, sans-serif;
  --sans: "Inter", -apple-system, "Segoe UI", Roboto, system-ui, sans-serif;
  --mono: "JetBrains Mono", ui-monospace, "SF Mono", Consolas, monospace;

  /* --- rhythm ------------------------------------------------------- */
  --gutter: clamp(1.15rem, 5vw, 4rem);
  --section-y: clamp(4.5rem, 11vh, 9rem);
  --pad: clamp(1.15rem, 2.6vw, 2rem);

  /* stars are painted on a canvas, so their colour has to be readable from
     JS — present.js reads this and re-reads it when the theme changes */
  --star-rgb: 232, 236, 251;
  --live-ink: #b9f5e2;
  --sheen: rgba(255, 255, 255, .16);
  --h2-grad: linear-gradient(120deg, #fff, #c9d4f5 55%, rgba(139, 124, 246, .85));
  --num-grad: linear-gradient(120deg, #ffffff, #58d3f7);
  --h1-grad: linear-gradient(115deg, #ffffff 8%, #b9c6f2 44%, #8b7cf6 78%, #58d3f7);
  --nav-bg: rgba(5, 6, 13, .55);
}

/* ===========================================================================
   DAYLIGHT

   Same page at noon: warm paper instead of deep sky, the nebula turned down to
   a wash, and the star canvas inverted to faint ink specks so it reads as a
   constellation chart rather than a hole in the page.

   Written twice on purpose — once for the system preference, once for an
   explicit choice — so the toggle wins in both directions.
   ========================================================================= */
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    color-scheme: light;
    --bg: #f6f4ef;
    --bg-2: #efece4;
    --ink: #14161f;
    --ink-dim: rgba(20, 22, 31, .74);
    --ink-faint: rgba(20, 22, 31, .58);
    --violet: #5a45d6;
    --cyan: #0d6f8c;
    --mint: #0d7355;
    --amber: #9a5510;
    --rose: #b83c70;
    --glass: linear-gradient(158deg, rgba(255,255,255,.88), rgba(255,255,255,.62) 46%, rgba(255,255,255,.5));
    --edge: rgba(20, 22, 31, .10);
    --edge-lit: rgba(20, 22, 31, .26);
    --shadow: 0 18px 44px rgba(60, 50, 30, .12);
    --star-rgb: 20, 22, 31;
    --live-ink: #0c6a51;
    --sheen: rgba(109, 90, 224, .17);
    --h2-grad: linear-gradient(120deg, #14161f, #3b3f57 55%, #5a45d6);
    --num-grad: linear-gradient(120deg, #14161f, #0d6f8c);
    --h1-grad: linear-gradient(115deg, #14161f 8%, #3a3f5c 44%, #5a45d6 78%, #0d6f8c);
    --nav-bg: rgba(246, 244, 239, .72);
  }
}

:root[data-theme="light"] {
  color-scheme: light;
  --bg: #f6f4ef;
  --bg-2: #efece4;
  --ink: #14161f;
  --ink-dim: rgba(20, 22, 31, .74);
  --ink-faint: rgba(20, 22, 31, .58);
  /* Every accent here is picked for contrast against #f6f4ef, not by taking
     the dark palette and hoping: the dark cyan lands at 3.4:1 on paper, which
     is under AA for the small tracked-out type it is used on. */
  --violet: #5a45d6;
  --cyan: #0d6f8c;
  --mint: #0d7355;
  --amber: #9a5510;
  --rose: #b83c70;
  --glass: linear-gradient(158deg, rgba(255,255,255,.88), rgba(255,255,255,.62) 46%, rgba(255,255,255,.5));
  --edge: rgba(20, 22, 31, .10);
  --edge-lit: rgba(20, 22, 31, .26);
  --shadow: 0 18px 44px rgba(60, 50, 30, .12);
  --star-rgb: 20, 22, 31;
  --live-ink: #0c6a51;
  --sheen: rgba(109, 90, 224, .17);
  --h2-grad: linear-gradient(120deg, #14161f, #3b3f57 55%, #5a45d6);
  --num-grad: linear-gradient(120deg, #14161f, #0d6f8c);
  --h1-grad: linear-gradient(115deg, #14161f 8%, #3a3f5c 44%, #5a45d6 78%, #0d6f8c);
  --nav-bg: rgba(246, 244, 239, .72);
}

/* the sky is quieter in daylight, and the grid a touch stronger so the page
   does not look empty without the stars carrying it */
:root[data-theme="light"] .nebula { opacity: .5; filter: blur(60px) saturate(140%); }
:root[data-theme="light"] .grid-bg { opacity: .8; }
:root[data-theme="light"] .sky { opacity: .55; }
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .nebula { opacity: .5; filter: blur(60px) saturate(140%); }
  :root:not([data-theme="dark"]) .grid-bg { opacity: .8; }
  :root:not([data-theme="dark"]) .sky { opacity: .55; }
}

* { box-sizing: border-box; }

/* No `overflow-x` on <html>. It was added to contain two things that bled
   past the viewport — the hero orbit and the nebula's negative inset — and it
   worked, but clipping the root while fixed, animated layers sit inside it
   left Chrome painting a stale narrow copy of the page behind the live one on
   resize: two pages on top of each other, and no amount of repainting cleared
   it.

   Nothing bleeds any more, so nothing needs clipping at the root. The hero
   clips its own orbit, and the nebula's overhang is a transform rather than a
   negative inset — transforms do not contribute to layout or to scrollable
   overflow, so there is nothing left to contain. */
html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: clamp(15px, 1.02vw, 16.5px);
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

img { max-width: 100%; }
a { color: inherit; }

/* ===========================================================================
   1. THE SKY

   Three stacked layers, all fixed, all pointer-events:none.
   `.sky`     — canvas: parallax stars + the occasional meteor (present.js)
   `.nebula`  — CSS: four slow radial clouds, the only thing that drifts
   `.grid-bg` — a faint coordinate grid so the glass has something to bend
   ========================================================================= */
.sky,
.nebula,
.grid-bg {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}

.sky { width: 100%; height: 100%; }

.nebula {
  /* `inset: 0` plus a scale, not `inset: -25%`: the overhang has to come from
     a transform so it never enters layout. A negative inset made this element
     1228px wide inside an 833px window, which is overflow the root then had to
     clip — see the html rule above. */
  inset: 0;
  transform-origin: 50% 50%;
  background:
    radial-gradient(34% 30% at 18% 22%, rgba(139, 124, 246, .26), transparent 70%),
    radial-gradient(30% 26% at 82% 16%, rgba(88, 211, 247, .20), transparent 70%),
    radial-gradient(42% 36% at 66% 76%, rgba(79, 209, 179, .14), transparent 70%),
    radial-gradient(26% 24% at 12% 82%, rgba(240, 173, 87, .13), transparent 70%);
  filter: blur(50px) saturate(125%);
  transform: scale(1.5);
  animation: drift 34s ease-in-out infinite alternate;
}

/* every frame carries the 1.5 base scale that replaces the old negative inset */
@keyframes drift {
  0%   { transform: translate3d(-2%, -1%, 0) scale(1.50); }
  50%  { transform: translate3d(3%, 2%, 0) scale(1.62) rotate(2deg); }
  100% { transform: translate3d(-1%, 3%, 0) scale(1.56) rotate(-2deg); }
}

.grid-bg {
  opacity: .5;
  background-image:
    linear-gradient(rgba(var(--star-rgb), .035) 1px, transparent 1px),
    linear-gradient(90deg, rgba(var(--star-rgb), .035) 1px, transparent 1px);
  background-size: 72px 72px;
  mask-image: radial-gradient(ellipse 80% 60% at 50% 30%, #000 20%, transparent 78%);
  -webkit-mask-image: radial-gradient(ellipse 80% 60% at 50% 30%, #000 20%, transparent 78%);
}

/* scroll progress, hairline, top of viewport */
.progress {
  position: fixed;
  inset: 0 0 auto;
  height: 2px;
  z-index: 60;
  transform-origin: 0 50%;
  transform: scaleX(var(--p, 0));
  background: linear-gradient(90deg, var(--violet), var(--cyan) 55%, var(--amber));
}

/* ===========================================================================
   2. NAV
   ========================================================================= */
.nav {
  position: sticky;
  top: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: .85rem var(--gutter);
  background: var(--nav-bg);
  backdrop-filter: blur(16px) saturate(150%);
  -webkit-backdrop-filter: blur(16px) saturate(150%);
  border-bottom: 1px solid var(--edge);
}

.nav-mark {
  font-family: var(--display);
  font-weight: 700;
  font-size: 1.02rem;
  letter-spacing: .06em;
  text-decoration: none;
}
.nav-mark span { color: var(--amber); }

.nav-links {
  display: flex;
  gap: clamp(.5rem, 1.6vw, 1.5rem);
  margin-inline: auto;
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.nav-links::-webkit-scrollbar { display: none; }

.nav-links a {
  position: relative;
  padding: .3rem .1rem;
  font-size: .88rem;
  color: var(--ink-dim);
  text-decoration: none;
  white-space: nowrap;
  transition: color .25s ease;
}
.nav-links a::after {
  content: "";
  position: absolute;
  left: 0; right: 100%; bottom: 0;
  height: 1px;
  background: var(--cyan);
  transition: right .3s ease;
}
.nav-links a:hover { color: var(--ink); }
.nav-links a:hover::after { right: 0; }

.nav-back {
  flex: none;
  padding: .42rem .85rem;
  font-family: var(--mono);
  font-size: .74rem;
  letter-spacing: .12em;
  text-decoration: none;
  color: var(--amber);
  border: 1px solid rgba(240, 173, 87, .32);
  border-radius: 999px;
  background: rgba(240, 173, 87, .07);
  transition: background .25s ease, border-color .25s ease;
}
.nav-back:hover {
  background: rgba(240, 173, 87, .16);
  border-color: rgba(240, 173, 87, .6);
}

/* ===========================================================================
   3. SECTION SHELL
   ========================================================================= */
main { position: relative; z-index: 1; }

section {
  max-width: 78rem;
  margin-inline: auto;
  padding: var(--section-y) var(--gutter);
}

.eyebrow {
  display: flex;
  align-items: center;
  gap: .7rem;
  margin: 0 0 .6rem;
  font-family: var(--mono);
  font-size: clamp(10px, .74vw, 11.5px);
  font-weight: 500;
  letter-spacing: .34em;
  text-transform: uppercase;
  color: var(--cyan);
}
.eyebrow::before {
  content: "";
  width: 1.6rem;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--cyan));
}

h1, h2, h3 {
  font-family: var(--display);
  font-weight: 600;
  letter-spacing: -.025em;
  line-height: 1.08;
  margin: 0;
}

h2 {
  font-size: clamp(1.75rem, 4.2vw, 3rem);
  margin-bottom: clamp(1.6rem, 4vw, 2.6rem);
  background: var(--h2-grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* ===========================================================================
   4. GLASS
   ========================================================================= */
.glass {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  padding: var(--pad);
  border-radius: var(--radius);
  background: var(--glass);
  border: 1px solid var(--edge);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .12), var(--shadow);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  transition: transform .45s cubic-bezier(.22, .9, .3, 1), border-color .45s ease;
}

/* the pointer highlight — coordinates come from present.js */
.glass::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: 0;
  background: radial-gradient(20rem 20rem at var(--mx, 50%) var(--my, 0%), rgba(139, 124, 246, .20), transparent 70%);
  transition: opacity .4s ease;
}
@media (hover: hover) {
  .glass:hover { transform: translateY(-3px); border-color: var(--edge-lit); }
  .glass:hover::before { opacity: 1; }
}

/* reveal-on-scroll. `.js` is set by present.js, so with scripting off
   everything is simply visible. */
.js .reveal { opacity: 0; transform: translateY(22px); }
.js .reveal.in {
  opacity: 1;
  transform: none;
  transition: opacity .7s ease, transform .7s cubic-bezier(.22, .9, .3, 1);
}
/* ---------------------------------------------------------------------------
   "Reduce motion" is honoured by slowing things down, not by switching them
   off. The setting is about unprompted, large, repetitive motion — a page that
   stops responding when you touch it is not calmer, it is broken. So under
   `.calm` (set by present.js from the media query) the ambient loops run at a
   third of their speed, and everything the visitor triggers themselves is
   untouched.
   ------------------------------------------------------------------------- */
.calm .nebula { animation-duration: 96s; }

/* ===========================================================================
   5. HERO
   ========================================================================= */
.hero {
  /* the orbit deliberately bleeds past the text column; this stops that bleed
     from widening the document */
  overflow: clip;
  min-height: min(92svh, 900px);
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-top: clamp(2rem, 6vh, 5rem);
}

.hero h1 {
  font-size: clamp(2.9rem, 11vw, 7.4rem);
  font-weight: 700;
  letter-spacing: -.04em;
  margin: .35rem 0 1.1rem;
  background: var(--h1-grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.hero-role {
  max-width: 34ch;
  margin: 0 0 1rem;
  font-family: var(--display);
  font-size: clamp(1.05rem, 2.5vw, 1.55rem);
  font-weight: 500;
  line-height: 1.34;
  color: var(--ink);
}
.hero-role b { color: var(--cyan); font-weight: 600; }

.hero-lede {
  max-width: 62ch;
  margin: 0 0 1.7rem;
  color: var(--ink-dim);
}

.hero-chips {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  margin-bottom: 1.8rem;
}

.chip {
  padding: .34rem .8rem;
  font-family: var(--mono);
  font-size: .7rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-dim);
  border: 1px solid var(--edge);
  border-radius: 999px;
  background: rgba(127, 127, 127, .10);
}
.chip.live {
  /* pale mint reads on deep sky and vanishes on paper, so the ink is a
     variable like everything else here */
  color: var(--live-ink);
  font-weight: 500;
  border-color: rgba(79, 209, 179, .4);
  background: rgba(79, 209, 179, .1);
}
.chip.live::before {
  content: "";
  display: inline-block;
  width: 6px; height: 6px;
  margin-right: .5rem;
  border-radius: 50%;
  background: var(--mint);
  box-shadow: 0 0 10px var(--mint);
  animation: pulse 2.4s ease-in-out infinite;
}
@keyframes pulse { 50% { opacity: .35; } }

/* --- buttons --- */
.cta-row { display: flex; flex-wrap: wrap; gap: .6rem; }

.btn {
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  padding: .68rem 1.25rem;
  font-family: var(--mono);
  font-size: .78rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--ink);
  border: 1px solid var(--edge);
  border-radius: 999px;
  background: rgba(255, 255, 255, .04);
  cursor: pointer;
  transition: transform .3s cubic-bezier(.22,.9,.3,1), background .3s ease, border-color .3s ease;
}
.btn:hover { transform: translateY(-2px); background: rgba(127, 127, 127, .20); border-color: var(--edge-lit); }
.btn.primary {
  color: #08111d;
  font-weight: 600;
  border-color: transparent;
  background: linear-gradient(120deg, var(--cyan), var(--violet));
  box-shadow: 0 10px 30px rgba(88, 211, 247, .22);
}
.btn.primary:hover { box-shadow: 0 14px 38px rgba(139, 124, 246, .34); }

/* --- the orbit: hero's signature object -----------------------------------
   Pure CSS. Three rings on different planes, each with one body riding it.
   Sits behind the hero copy on a phone (as ambient light) and beside it on a
   wide screen. --- */
.orbit {
  position: absolute;
  right: clamp(-8rem, -2vw, 2rem);
  top: 46%;
  translate: 0 -50%;
  width: clamp(20rem, 34vw, 34rem);
  aspect-ratio: 1;
  z-index: -1;
  pointer-events: none;
  opacity: .8;
}
.orbit i {
  position: absolute;
  inset: 0;
  border: 1px solid rgba(255, 255, 255, .1);
  border-radius: 50%;
  animation: spin 26s linear infinite;
}
.orbit i:nth-child(1) { inset: 16%; animation-duration: 16s; }
.orbit i:nth-child(2) { inset: 6%;  animation-duration: 26s; rotate: x 62deg; }
.orbit i:nth-child(3) { inset: 0;   animation-duration: 40s; rotate: y 70deg; }

.orbit i::after {
  content: "";
  position: absolute;
  top: -4px; left: 50%;
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--cyan);
  box-shadow: 0 0 18px 3px rgba(88, 211, 247, .7);
}
.orbit i:nth-child(2)::after { background: var(--violet); box-shadow: 0 0 18px 3px rgba(139,124,246,.7); }
.orbit i:nth-child(3)::after { background: var(--amber);  box-shadow: 0 0 18px 3px rgba(240,173,87,.6); }

.orbit b {
  position: absolute;
  inset: 42%;
  border-radius: 50%;
  background: radial-gradient(circle at 34% 30%, #fff, var(--amber) 42%, #a2521a 100%);
  box-shadow: 0 0 60px 12px rgba(240, 173, 87, .35);
  animation: breathe 7s ease-in-out infinite;
}

@keyframes spin { to { transform: rotate(360deg); } }
@keyframes breathe { 50% { transform: scale(1.07); } }

.calm .orbit i { animation-duration: 70s; }
.calm .orbit i:nth-child(2) { animation-duration: 96s; }
.calm .orbit i:nth-child(3) { animation-duration: 130s; }
.calm .orbit b { animation-duration: 18s; }

.hero { position: relative; }

/* ===========================================================================
   6. METRICS
   ========================================================================= */
.metrics {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr));
  gap: clamp(.7rem, 1.6vw, 1.1rem);
}

.metric { display: flex; flex-direction: column; gap: .35rem; }
.metric .n {
  font-family: var(--display);
  font-size: clamp(1.9rem, 4.6vw, 2.9rem);
  font-weight: 700;
  letter-spacing: -.03em;
  line-height: 1;
  background: var(--num-grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  font-variant-numeric: tabular-nums;
}
.metric .l { font-size: .84rem; color: var(--ink-dim); }

/* ===========================================================================
   7. EXPERIENCE
   ========================================================================= */
.timeline { display: grid; gap: clamp(.9rem, 2vw, 1.4rem); }

.role-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: .5rem 1rem;
  margin-bottom: .3rem;
}
.role-head h3 { font-size: clamp(1.15rem, 2.6vw, 1.5rem); }
.role-when {
  margin-left: auto;
  font-family: var(--mono);
  font-size: .72rem;
  letter-spacing: .1em;
  color: var(--ink-faint);
  white-space: nowrap;
}
.role-sub {
  margin: 0 0 .9rem;
  font-size: .95rem;
  color: var(--cyan);
}
.role-now {
  color: var(--mint);
}

.bullets { margin: 0; padding-left: 1.05rem; display: grid; gap: .5rem; }
.bullets li { color: var(--ink-dim); }
.bullets li::marker { color: var(--violet); }
.bullets b { color: var(--ink); font-weight: 600; }

/* ===========================================================================
   8. CARDS (projects)
   ========================================================================= */
.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(21rem, 100%), 1fr));
  gap: clamp(.8rem, 1.8vw, 1.3rem);
}

.card { display: flex; flex-direction: column; gap: .8rem; }
.card h3 { font-size: 1.16rem; }
.card p { margin: 0; color: var(--ink-dim); font-size: .94rem; }
.card b { color: var(--ink); font-weight: 600; }

.stack { display: flex; flex-wrap: wrap; gap: .35rem; margin-top: auto; }
.stack span {
  padding: .22rem .58rem;
  font-family: var(--mono);
  font-size: .68rem;
  letter-spacing: .04em;
  color: var(--ink-faint);
  border: 1px solid var(--edge);
  border-radius: 6px;
}

.card-link {
  font-family: var(--mono);
  font-size: .76rem;
  letter-spacing: .08em;
  color: var(--cyan);
  text-decoration: none;
}
.card-link:hover { text-decoration: underline; }

/* ===========================================================================
   9. SKILLS
   ========================================================================= */
.skill-groups {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(19rem, 100%), 1fr));
  gap: clamp(.8rem, 1.8vw, 1.2rem);
}
.skill-groups h3 {
  font-size: .82rem;
  font-family: var(--mono);
  font-weight: 500;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--amber);
  margin-bottom: .8rem;
}
.tags { display: flex; flex-wrap: wrap; gap: .35rem; }
.tags span {
  padding: .28rem .62rem;
  font-size: .78rem;
  color: var(--ink-dim);
  border: 1px solid var(--edge);
  border-radius: 8px;
  background: rgba(127, 127, 127, .08);
}

/* ===========================================================================
   10. CERTIFICATIONS
   ========================================================================= */
.certs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(13rem, 100%), 1fr));
  gap: clamp(.7rem, 1.6vw, 1.1rem);
}
.cert {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .45rem;
  text-align: center;
}
.cert img { width: clamp(72px, 12vw, 104px); height: auto; }
.cert b { font-size: .96rem; }
.cert small { color: var(--ink-faint); font-size: .76rem; }
.cert a {
  margin-top: .4rem;
  padding: .38rem .9rem;
  font-family: var(--mono);
  font-size: .7rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--cyan);
  border: 1px solid rgba(88, 211, 247, .3);
  border-radius: 999px;
  transition: background .25s ease;
}
.cert a:hover { background: rgba(88, 211, 247, .12); }

.note {
  margin-top: 2rem;
  max-width: 70ch;
  color: var(--ink-faint);
  font-size: .88rem;
}

/* ===========================================================================
   11. LOCKWOOD
   ========================================================================= */
.ask { display: grid; gap: .9rem; }

.ask-log {
  display: grid;
  gap: .6rem;
  max-height: min(48vh, 26rem);
  overflow-y: auto;
  padding-right: .3rem;
  overscroll-behavior: contain;
}

.msg {
  max-width: 52ch;
  padding: .68rem .95rem;
  border-radius: 14px;
  font-size: .92rem;
  white-space: pre-wrap;
  border: 1px solid var(--edge);
}
.msg.bot { background: rgba(139, 124, 246, .1); border-color: rgba(139, 124, 246, .22); }
.msg.you {
  justify-self: end;
  background: rgba(88, 211, 247, .1);
  border-color: rgba(88, 211, 247, .22);
}
.msg.err { background: rgba(244, 114, 166, .1); border-color: rgba(244, 114, 166, .26); }

.msg.dots { display: flex; gap: .3rem; align-items: center; }
.msg.dots span {
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--violet);
  animation: blink 1.2s ease-in-out infinite;
}
.msg.dots span:nth-child(2) { animation-delay: .15s; }
.msg.dots span:nth-child(3) { animation-delay: .3s; }
@keyframes blink { 0%, 100% { opacity: .25; } 50% { opacity: 1; } }

.suggests { display: flex; flex-wrap: wrap; gap: .4rem; }
.suggests button {
  padding: .38rem .8rem;
  font: inherit;
  font-size: .8rem;
  color: var(--ink-dim);
  border: 1px solid var(--edge);
  border-radius: 999px;
  background: rgba(255, 255, 255, .03);
  cursor: pointer;
  transition: color .25s ease, border-color .25s ease;
}
.suggests button:hover { color: var(--ink); border-color: var(--edge-lit); }

.ask-form { display: flex; gap: .5rem; flex-wrap: wrap; }
.ask-form input {
  flex: 1 1 14rem;
  min-width: 0;
  padding: .7rem 1rem;
  font: inherit;
  font-size: .94rem;
  color: var(--ink);
  border: 1px solid var(--edge);
  border-radius: 999px;
  background: rgba(127, 127, 127, .12);
}
.ask-form input::placeholder { color: var(--ink-faint); }
.ask-form input:focus {
  outline: none;
  border-color: rgba(88, 211, 247, .5);
  box-shadow: 0 0 0 3px rgba(88, 211, 247, .12);
}

/* ===========================================================================
   12. CONTACT + FOOTER
   ========================================================================= */
.contact p { max-width: 60ch; color: var(--ink-dim); }

footer {
  position: relative;
  z-index: 1;
  padding: 2.5rem var(--gutter) 3.5rem;
  text-align: center;
  font-family: var(--mono);
  font-size: .74rem;
  letter-spacing: .1em;
  color: var(--ink-faint);
  border-top: 1px solid var(--edge);
}
footer a { color: var(--amber); text-decoration: none; }
footer a:hover { text-decoration: underline; }

/* ===========================================================================
   13. FOCUS + NARROW SCREENS
   ========================================================================= */
:focus-visible {
  outline: 2px solid var(--cyan);
  outline-offset: 3px;
  border-radius: 6px;
}

@media (max-width: 760px) {
  /* the orbit stops competing with the headline and becomes wallpaper */
  .orbit {
    right: -18%;
    top: 9%;
    width: 20rem;
    opacity: .26;
  }
  /* the sun sat squarely on the headline at this width — the rings alone
     carry the idea, and the text stays readable */
  .orbit b { display: none; }
  .nav-mark { display: none; }
  .nav-links { margin-inline: 0 auto; }
  .msg { max-width: 100%; }
  .role-when { margin-left: 0; width: 100%; }
}

@media (max-width: 400px) {
  .btn { padding: .6rem 1rem; font-size: .72rem; }
}

/* ===========================================================================
   14. SHINE

   One sweep, reused. `.gleam` puts a soft diagonal band of light behind the
   element's content and walks it across on hover; `.gleam-auto` walks it on a
   loop instead, for the pieces that should catch the light without being
   pointed at (the primary buttons, the live chip, the metric numbers).

   It is a pseudo-element on an `overflow: hidden` box, so it costs one
   composited layer and nothing else. Both palettes get it: --sheen is white in
   the dark theme and a brighter white in daylight, where the surfaces are
   already pale and the band has to be stronger to read.
   ========================================================================= */
.glass::after,
.btn::after,
.cert a::after,
.nav-back::after {
  content: "";
  position: absolute;
  top: -60%;
  left: -70%;
  width: 45%;
  height: 220%;
  z-index: 1;
  pointer-events: none;
  transform: rotate(18deg) translateX(0);
  background: linear-gradient(90deg, transparent, var(--sheen), transparent);
  opacity: 0;
  transition: transform .85s cubic-bezier(.2, .7, .3, 1), opacity .3s ease;
}

.btn, .cert a, .nav-back { position: relative; overflow: hidden; }

@media (hover: hover) {
  .glass:hover::after,
  .btn:hover::after,
  .cert a:hover::after,
  .nav-back:hover::after {
    opacity: 1;
    transform: rotate(18deg) translateX(420%);
  }
}

/* the unprompted version — slow, wide gaps, so it reads as light moving over
   the page rather than as something demanding attention */
.gleam-auto { position: relative; overflow: hidden; }
.gleam-auto::after {
  content: "";
  position: absolute;
  inset: -60% -70%;
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(90deg, transparent 42%, var(--sheen) 50%, transparent 58%);
  transform: translateX(-60%) rotate(18deg);
  animation: sweep 6.5s ease-in-out infinite;
}
@keyframes sweep {
  0%, 62%  { transform: translateX(-70%) rotate(18deg); }
  100%     { transform: translateX(70%) rotate(18deg); }
}

/* headings get a travelling highlight inside the gradient text itself */
.shimmer {
  background-size: 220% 100%;
  animation: shimmer 9s linear infinite;
}
@keyframes shimmer {
  0%   { background-position: 120% 0; }
  100% { background-position: -120% 0; }
}

/* ===========================================================================
   15. TYPING

   Used in two places: the rotating line under the hero headline, and the
   section eyebrows, which type themselves once as they scroll into view. The
   caret is a border, not a character, so it never affects text metrics and
   never gets selected or read aloud.
   ========================================================================= */
.typed {
  border-right: 2px solid var(--cyan);
  padding-right: 2px;
  animation: caret 1s steps(1) infinite;
}
.typed.done { border-right-color: transparent; animation: none; }
@keyframes caret { 50% { border-right-color: transparent; } }

.type-line {
  display: block;
  min-height: 1.5em;
  font-family: var(--mono);
  font-size: clamp(.78rem, 1.7vw, .95rem);
  letter-spacing: .02em;
  color: var(--mint);
  margin: 0 0 1.4rem;
}

/* ===========================================================================
   16. THEME TOGGLE
   ========================================================================= */
.theme-toggle {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.1rem;
  height: 2.1rem;
  margin-left: .4rem;
  padding: 0;
  font-size: .95rem;
  line-height: 1;
  color: var(--ink);
  border: 1px solid var(--edge);
  border-radius: 50%;
  background: rgba(127, 127, 127, .08);
  cursor: pointer;
  transition: transform .4s cubic-bezier(.22,.9,.3,1), border-color .3s ease, background .3s ease;
}
.theme-toggle:hover { transform: rotate(-18deg) scale(1.08); border-color: var(--edge-lit); }

.calm .gleam-auto::after { animation-duration: 15s; }
.calm .shimmer { animation-duration: 22s; }
.calm .typed { animation-duration: 1.6s; }

/* ===========================================================================
   17. PROJECT SHOTS

   The thumbnail is a link in its own right, so the whole image is clickable
   and not just the caption underneath it. `aspect-ratio` plus `object-fit`
   means the row stays level whatever shape the source image happens to be —
   these came from several different places and none of them agree.
   ========================================================================= */
.card .shot {
  display: block;
  position: relative;
  overflow: hidden;
  border-radius: 14px;
  border: 1px solid var(--edge);
  /* a soft ground behind the contained thumbnail, so a transparent logo has
     something to sit on in both themes */
  background:
    radial-gradient(120% 100% at 50% 0%, rgba(139, 124, 246, .13), transparent 70%),
    rgba(127, 127, 127, .07);
  aspect-ratio: 16 / 9;
  margin-bottom: .2rem;
}
/* `contain`, not `cover`. These come from several places and most are square
   logos or avatars; cover cropped the top and bottom off every one of them and
   no two cards lined up. Contained and padded, each thumbnail sits in the same
   box at the same size whatever shape it arrived in. */
.card .shot img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  padding: 8%;
  display: block;
  transition: transform .7s cubic-bezier(.22, .9, .3, 1), filter .5s ease;
}
@media (hover: hover) {
  .card:hover .shot img { transform: scale(1.06); }
}

/* a light band that crosses the image when the card is pointed at */
.card .shot::after {
  content: "";
  position: absolute;
  inset: -60% -70%;
  pointer-events: none;
  background: linear-gradient(90deg, transparent 42%, var(--sheen) 50%, transparent 58%);
  transform: translateX(-70%) rotate(18deg);
  opacity: 0;
  transition: transform .9s cubic-bezier(.2,.7,.3,1), opacity .3s ease;
}
@media (hover: hover) {
  .card:hover .shot::after { opacity: 1; transform: translateX(70%) rotate(18deg); }
}

/* ===========================================================================
   18. ELSEWHERE
   ========================================================================= */
.orbits {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(11.5rem, 100%), 1fr));
  gap: clamp(.6rem, 1.4vw, .95rem);
}

.orbit-card {
  display: grid;
  grid-template-columns: 2.4rem 1fr;
  grid-template-rows: auto auto;
  column-gap: .7rem;
  align-items: center;
  padding: .85rem .95rem;
  text-decoration: none;
}
.orbit-card img {
  grid-row: 1 / 3;
  width: 2.4rem;
  height: 2.4rem;
  border-radius: 9px;
  object-fit: cover;
  background: rgba(127, 127, 127, .12);
}
.orbit-card b { font-size: .92rem; align-self: end; }
.orbit-card small { font-size: .74rem; color: var(--ink-faint); align-self: start; }

/* ===========================================================================
   19. THE SKY HINT

   The starfield reacts to the pointer and to touch, which nobody discovers
   unless told. One line, said once, in the same mono voice as the eyebrows.
   ========================================================================= */
.sky-hint {
  margin: 1.4rem 0 0;
  font-family: var(--mono);
  font-size: .72rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.sky-hint::before {
  content: "✦";
  margin-right: .5rem;
  color: var(--violet);
  animation: pulse 3.2s ease-in-out infinite;
}
.calm .card .shot::after { transition-duration: 1.4s; }

/* where no real mark is available, a monogram rather than someone else's
   logo standing in for it */
.orbit-card .tile {
  grid-row: 1 / 3;
  display: grid;
  place-items: center;
  width: 2.4rem;
  height: 2.4rem;
  border-radius: 9px;
  font-family: var(--display);
  font-weight: 600;
  font-size: .82rem;
  letter-spacing: .02em;
  color: var(--ink);
  background: linear-gradient(150deg, rgba(139,124,246,.28), rgba(88,211,247,.16));
  border: 1px solid var(--edge);
}
