/* NAS Digital — 2026-07-29 rebuild.
 *
 * Every colour here is sampled from Nathan's own portrait rather than picked:
 * the hoodie is the ground (#111318), its background cyan is the accent
 * (#7ceaff), and the beard's burnt amber (#894b26, lifted to #e0915a for
 * contrast) is the warm note. That is what makes it read as personal without
 * having to say so.
 *
 * The old site opened "a little studio making beautiful things" and then sold
 * enterprise AI security four sections later. This one is one business talking
 * to one audience.
 */

:root {
  --ink: #0c0e12;          /* deeper than the hoodie, so the hoodie reads as a shape */
  --surface: #111318;      /* the hoodie */
  --surface-2: #171a21;
  --line: #232733;
  --line-soft: #1a1e26;

  --text: #eef2f7;
  --text-2: #a7b0bf;
  --text-3: #6f7889;

  --cyan: #7ceaff;         /* the portrait background */
  --cyan-deep: #0e2a33;
  --amber: #e0915a;        /* the beard, lifted for contrast */

  --wrap: 1120px;
  --narrow: 68ch;
  --pad: clamp(20px, 5vw, 48px);

  /* No webfont, deliberately. This site's pitch is that it loads fast, so a
     render-blocking request to a font CDN for a display face would undercut
     the thing being sold. ui-serif resolves to New York on Apple and Georgia
     elsewhere - both genuinely characterful, both already on the machine. */
  --f-display: ui-serif, Georgia, "Times New Roman", serif;
  --f-body: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --f-mono: ui-monospace, "JetBrains Mono", "SF Mono", Menlo, Consolas, monospace;
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  margin: 0;
  background: var(--ink);
  color: var(--text);
  font-family: var(--f-body);
  font-size: 17px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}
img { max-width: 100%; height: auto; display: block; }
a { color: var(--cyan); text-decoration-color: color-mix(in srgb, var(--cyan) 40%, transparent); }
a:hover { text-decoration-color: var(--cyan); }
:focus-visible { outline: 2px solid var(--cyan); outline-offset: 3px; border-radius: 3px; }

.skip {
  position: absolute; left: -9999px; top: 0; background: var(--cyan); color: var(--ink);
  padding: 10px 16px; z-index: 100; font-weight: 600;
}
.skip:focus { left: 0; }

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

/* ── header ─────────────────────────────────────────────────────────── */
.site-head {
  position: sticky; top: 0; z-index: 50;
  display: flex; align-items: center; justify-content: space-between; gap: 20px;
  padding: 14px var(--pad);
  background: color-mix(in srgb, var(--ink) 88%, transparent);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--line-soft);
}
.wordmark {
  display: flex; align-items: center; gap: 11px;
  color: var(--text); text-decoration: none; font-weight: 600; letter-spacing: -.01em;
}
.wordmark img { border-radius: 8px; }
.wordmark em { font-style: normal; color: var(--text-3); font-weight: 500; }
.site-head nav { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }
/* On a phone the four nav items plus the wordmark cannot share a row without
   wrapping into the logo. Drop the two secondary links - both are reachable by
   scrolling - and keep the one action that matters. */
@media (max-width: 620px) {
  .site-head { padding: 12px 16px; }
  .site-head nav a[href="#open-source"],
  .site-head nav a[href="/#open-source"] { display: none; }
  .site-head nav a { padding: 7px 9px; font-size: 14px; }
}
@media (max-width: 420px) {
  .site-head nav a[href="#services"],
  .site-head nav a[href="/#services"] { display: none; }
}
.site-head nav a {
  color: var(--text-2); text-decoration: none; font-size: 15px;
  padding: 8px 12px; border-radius: 8px;
}
.site-head nav a:hover { color: var(--text); background: var(--surface-2); }
.site-head nav a[aria-current] { color: var(--cyan); }
.site-head nav a.cta {
  background: var(--cyan); color: var(--ink); font-weight: 600;
}
.site-head nav a.cta:hover { background: #a5f1ff; }

/* ── hero ───────────────────────────────────────────────────────────── */
.hero { padding: clamp(48px, 9vw, 104px) 0 clamp(40px, 7vw, 88px); }
.hero-grid {
  display: grid; gap: clamp(32px, 6vw, 64px); align-items: center;
  grid-template-columns: 1fr;
}
@media (min-width: 900px) { .hero-grid { grid-template-columns: 1.25fr .75fr; } }

.hero h1 {
  font-family: var(--f-display);
  font-size: clamp(2.6rem, 6vw, 4.4rem);
  line-height: 1.02;
  letter-spacing: -.02em;
  margin: 0 0 22px;
  text-wrap: balance;
  font-weight: 400;
}
.hero h1 span { color: var(--cyan); }
.hero .lede { font-size: clamp(1.05rem, 2vw, 1.22rem); color: var(--text-2); max-width: 52ch; margin: 0 0 30px; }
.eyebrow {
  font-family: var(--f-mono); font-size: 12px; letter-spacing: .16em; text-transform: uppercase;
  color: var(--text-3); margin: 0 0 20px; display: flex; align-items: center; gap: 10px;
}
.eyebrow::before { content: ""; width: 26px; height: 1px; background: var(--cyan); }

/* The portrait keeps its own cyan ground - it is already his identity
   elsewhere, and a flat colour block against the near-black is a stronger
   graphic move than cutting him out and floating him. */
/* width:100% matters as much as the max-width. Without it the 380px floor
   exceeded the 351px of content space at a 390px viewport and widened the
   whole page, so every section overflowed horizontally on a phone. */
.portrait {
  border-radius: 18px; overflow: hidden; border: 1px solid var(--line);
  width: 100%; max-width: 380px;
}
.portrait img { width: 100%; }

.btn {
  display: inline-block; background: var(--cyan); color: var(--ink);
  padding: 13px 24px; border-radius: 10px; font-weight: 650; text-decoration: none;
  transition: transform .15s ease, background .15s ease;
}
.btn:hover { background: #a5f1ff; transform: translateY(-1px); }
.btn.ghost { background: none; color: var(--text); border: 1px solid var(--line); }
.btn.ghost:hover { border-color: var(--cyan); color: var(--cyan); background: none; }
.btn-row { display: flex; gap: 12px; flex-wrap: wrap; }

/* ── sections ───────────────────────────────────────────────────────── */
section { padding: clamp(48px, 8vw, 92px) 0; }
section + section { border-top: 1px solid var(--line-soft); }
.sec-head { max-width: 60ch; margin-bottom: clamp(28px, 4vw, 44px); }
.sec-head h2 {
  font-family: var(--f-display); font-weight: 400;
  font-size: clamp(1.9rem, 4vw, 2.7rem); line-height: 1.1; letter-spacing: -.015em;
  margin: 0 0 14px; text-wrap: balance;
}
.sec-head p { color: var(--text-2); margin: 0; }

.grid { display: grid; gap: 18px; grid-template-columns: repeat(auto-fit, minmax(min(100%, 290px), 1fr)); }

.svc {
  background: var(--surface); border: 1px solid var(--line-soft);
  border-radius: 14px; padding: 26px 24px;
}
.svc h3 { margin: 0 0 10px; font-size: 1.12rem; letter-spacing: -.01em; }
.svc p { color: var(--text-2); margin: 0 0 14px; font-size: .97rem; }
.svc ul { margin: 0; padding-left: 18px; color: var(--text-3); font-size: .92rem; }
.svc li { margin-bottom: 5px; }
.svc .num {
  font-family: var(--f-mono); font-size: 11px; color: var(--cyan);
  letter-spacing: .12em; display: block; margin-bottom: 14px;
}

/* ── writing cards ──────────────────────────────────────────────────── */
.cards { display: grid; gap: 16px; grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr)); }
.card {
  display: block; background: var(--surface); border: 1px solid var(--line-soft);
  border-radius: 14px; padding: 24px; text-decoration: none; color: inherit;
  transition: border-color .18s ease, transform .18s ease;
}
.card:hover { border-color: var(--cyan); transform: translateY(-2px); }
.card h2 { font-size: 1.14rem; margin: 0 0 10px; line-height: 1.3; letter-spacing: -.01em; }
.card p { color: var(--text-2); font-size: .95rem; margin: 0 0 16px; }
.card-meta, .card-tags { font-family: var(--f-mono); font-size: 11.5px; color: var(--text-3); }
.card-tags { margin-bottom: 12px; display: flex; gap: 6px; flex-wrap: wrap; }
.tag {
  font-family: var(--f-mono); font-size: 11px; letter-spacing: .04em;
  background: var(--cyan-deep); color: var(--cyan);
  padding: 3px 9px; border-radius: 100px; white-space: nowrap;
}
.tags { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 18px; }

/* ── open source ────────────────────────────────────────────────────── */
.repo {
  display: flex; align-items: baseline; gap: 12px; padding: 15px 0;
  border-bottom: 1px solid var(--line-soft); text-decoration: none; color: inherit;
}
.repo:last-child { border-bottom: none; }
.repo code { font-family: var(--f-mono); color: var(--cyan); font-size: .95rem; }
.repo span { color: var(--text-2); font-size: .93rem; }

/* ── post ───────────────────────────────────────────────────────────── */
.page-head { padding-bottom: 0; }
.page-head h1, .post h1 {
  font-family: var(--f-display); font-weight: 400;
  font-size: clamp(2.1rem, 5vw, 3.2rem); line-height: 1.08; letter-spacing: -.02em;
  margin: 0 0 16px; text-wrap: balance;
}
.lede { color: var(--text-2); font-size: 1.1rem; margin: 0 0 24px; }
.post { padding: clamp(36px, 6vw, 72px) 0; }
.back { font-family: var(--f-mono); font-size: 12.5px; text-decoration: none; display: inline-block; margin-bottom: 22px; }
.byline { display: flex; align-items: center; gap: 12px; font-size: .9rem; color: var(--text-3); margin: 0 0 8px; }
.byline img { border-radius: 50%; }
.byline strong { color: var(--text); font-weight: 600; }

.prose { padding-top: 8px; }
.prose > * + * { margin-top: 1.15em; }
.prose h2 {
  font-family: var(--f-display); font-weight: 400; font-size: 1.85rem;
  line-height: 1.2; letter-spacing: -.015em; margin-top: 2em; scroll-margin-top: 80px;
}
.prose h3 { font-size: 1.18rem; margin-top: 1.7em; letter-spacing: -.005em; }
.prose p, .prose li { color: #dfe5ee; }
.prose ul, .prose ol { padding-left: 22px; }
.prose li { margin-bottom: .45em; }
.prose li::marker { color: var(--cyan); }
.prose code {
  font-family: var(--f-mono); font-size: .88em;
  background: var(--surface-2); border: 1px solid var(--line-soft);
  padding: .12em .4em; border-radius: 5px; color: var(--amber);
  overflow-wrap: anywhere;
}
.prose pre {
  background: var(--surface); border: 1px solid var(--line);
  border-left: 3px solid var(--cyan);
  border-radius: 10px; padding: 18px 20px; overflow-x: auto; font-size: .88rem; line-height: 1.6;
}
.prose pre code { background: none; border: none; padding: 0; color: #cfe8f2; font-size: 1em; }
.prose blockquote {
  border-left: 3px solid var(--amber); padding: 4px 0 4px 20px; margin-left: 0;
  color: var(--text-2); font-style: italic;
}
.prose figure { margin: 2em 0; }
.prose img { border-radius: 12px; border: 1px solid var(--line-soft); }
.prose a { overflow-wrap: anywhere; }

.post-end {
  margin-top: 56px; padding-top: 32px; border-top: 1px solid var(--line);
}
.post-end h2 { font-family: var(--f-display); font-weight: 400; font-size: 1.6rem; margin: 0 0 10px; }
.post-end p { color: var(--text-2); margin: 0 0 20px; }

/* ── contact ────────────────────────────────────────────────────────── */
.contact-card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: 18px; padding: clamp(28px, 5vw, 48px);
  display: grid; gap: 28px; align-items: center;
}
@media (min-width: 800px) { .contact-card { grid-template-columns: 1.4fr .6fr; } }
.contact-card h2 { font-family: var(--f-display); font-weight: 400; font-size: clamp(1.8rem, 3.5vw, 2.4rem); margin: 0 0 12px; }
.contact-card p { color: var(--text-2); margin: 0 0 22px; }
.contact-card .portrait { max-width: 220px; margin-left: auto; }

/* ── footer ─────────────────────────────────────────────────────────── */
.site-foot {
  border-top: 1px solid var(--line); margin-top: 40px;
  padding: 44px var(--pad) 56px;
  max-width: var(--wrap); margin-left: auto; margin-right: auto;
  display: grid; gap: 22px;
}
@media (min-width: 760px) { .site-foot { grid-template-columns: 1fr auto; align-items: start; } }
.site-foot p { color: var(--text-3); font-size: .9rem; margin: 6px 0 0; }
.foot-links { display: flex; gap: 18px; flex-wrap: wrap; font-size: .92rem; }
.foot-note { grid-column: 1 / -1; color: var(--text-3); font-size: .86rem; margin-top: 4px; }

/* ══════════════════════════════════════════════════════════════════════
   MOTION LAYER

   This site sells web development, so the site is the portfolio piece and
   the effects are part of the argument. Which is also why they have to be
   restrained: a janky parallax on a developer's own site makes the case
   against him more effectively than the copy makes it for him.

   Everything here is decorative. Nothing the page says or does depends on
   any of it running - see the reduced-motion block at the end, which turns
   the whole layer off and leaves a site that still reads correctly.
   ═══════════════════════════════════════════════════════════════════════ */

.fx { position: fixed; inset: 0; z-index: -1; pointer-events: none; overflow: hidden; }
.fx-layer { position: absolute; inset: -20% -10%; will-change: transform; }

/* Three depths. The grid is the far plane, the two glows sit in front of it
   and drift at different rates, which is what actually reads as depth - a
   single moving layer just reads as a bug. */
.fx-grid {
  background-image:
    linear-gradient(rgba(124, 234, 255, .05) 1px, transparent 1px),
    linear-gradient(90deg, rgba(124, 234, 255, .05) 1px, transparent 1px);
  background-size: 68px 68px;
  mask-image: radial-gradient(ellipse 80% 60% at 50% 0%, #000 10%, transparent 75%);
  -webkit-mask-image: radial-gradient(ellipse 80% 60% at 50% 0%, #000 10%, transparent 75%);
}
.fx-glow-a {
  background: radial-gradient(ellipse 60% 50% at 65% 12%, rgba(124, 234, 255, .13), transparent 70%);
  filter: blur(30px);
}
.fx-glow-b {
  background: radial-gradient(ellipse 50% 45% at 20% 78%, rgba(224, 145, 90, .09), transparent 70%);
  filter: blur(46px);
}
#fx-canvas { position: absolute; inset: 0; width: 100%; height: 100%; }

/* Scroll reveals. Opacity + a small rise; anything bigger turns reading a
   page into waiting for a page.

   Note the `.js` gate. Content must NEVER need JavaScript to become visible:
   if fx.js fails, is blocked, or simply has not run yet, an unqualified
   `[data-reveal] { opacity: 0 }` leaves the entire hero blank with no error
   anywhere. That happened during the build, and it is invisible in testing
   precisely because it produces no console output. The class is set by a tiny
   inline script in <head>, so hiding only ever happens once something is
   definitely running to unhide it again. */
.js [data-reveal] {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity .7s cubic-bezier(.22, .8, .3, 1), transform .7s cubic-bezier(.22, .8, .3, 1);
}
.js [data-reveal].is-in { opacity: 1; transform: none; }
.js [data-reveal][data-delay="1"] { transition-delay: .08s; }
.js [data-reveal][data-delay="2"] { transition-delay: .16s; }
.js [data-reveal][data-delay="3"] { transition-delay: .24s; }

/* Cards lift and light their edge on hover - the one place the accent is
   allowed to move. */
.svc { transition: transform .28s cubic-bezier(.22,.8,.3,1), border-color .28s ease, background .28s ease; }
.svc:hover { transform: translateY(-4px); border-color: color-mix(in srgb, var(--cyan) 45%, var(--line)); }

/* ── promo video ────────────────────────────────────────────────────── */
.video-wrap {
  position: relative; border-radius: 16px; overflow: hidden;
  border: 1px solid var(--line); background: var(--surface);
  aspect-ratio: 16 / 9;
}
.video-wrap video { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Gradient + copy sit over the loop so it reads as a designed panel rather
   than an embedded clip someone forgot to style. */
.video-over {
  position: absolute; inset: 0; display: flex; flex-direction: column;
  justify-content: flex-end; gap: 8px; padding: clamp(20px, 4vw, 36px);
  background: linear-gradient(to top, rgba(12,14,18,.92) 8%, rgba(12,14,18,.45) 45%, transparent 75%);
}
.video-over h3 {
  font-family: var(--f-display); font-weight: 400;
  font-size: clamp(1.4rem, 3vw, 2.1rem); margin: 0; letter-spacing: -.015em;
}
.video-over p { margin: 0; color: var(--text-2); max-width: 46ch; font-size: .96rem; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  * { transition: none !important; animation: none !important; }
  /* Content must never be stuck invisible waiting for an observer that the
     JS deliberately never starts. */
  .js [data-reveal], [data-reveal] { opacity: 1; transform: none; }
  .fx-layer, #fx-canvas { display: none; }
  .video-wrap video { display: none; }
  .video-over { position: static; background: none; padding: clamp(20px, 4vw, 36px); }
}
