/* Space Grotesk, SIL Open Font License, self-hosted latin subset.
   One variable file covers both weights, so there is a single font request. */
@font-face{
  font-family:'Space Grotesk';
  src:url('/spacegrotesk.woff2') format('woff2');
  font-weight:300 700;
  font-style:normal;
  font-display:swap;
}

:root{
  color-scheme:light dark;
  --paper:#F5F4EF;
  --ink:#141310;
  --body:#2C2A25;
  --muted:#645F57;
  --faint:#726C62;
  --line:#DAD7CF;
  --surface:#F8F7F3;
  --accent:#1B6144;
  /* the nav's tinted state: --surface at 82% so the page shows through the
     blur. Kept as its own token because --surface is a hex and cannot be
     alpha'd inline. */
  --nav-bg:rgba(248,247,243,.82);
  --nav-h:4rem;
  /* the page's side gutter. The nav bar reads it too, so the floating bar and
     the content below it stay on the same left edge at every width. */
  --gutter:1.35rem;
  --display:'Space Grotesk',system-ui,-apple-system,"Segoe UI",Helvetica,Arial,sans-serif;
  --sans:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
  --mono:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,"Liberation Mono",monospace;
}
/* Dark is a token swap only. Every colour is defined on bare :root above, so
   the light palette is never trapped inside a media query. */
@media (prefers-color-scheme:dark){
  :root{
    --paper:#131210;
    --ink:#F4F1E9;
    --body:#C9C4B8;
    --muted:#ABA598;
    --faint:#948D7F;
    --line:#35322B;
    --surface:#1D1C18;
    --accent:#6CC79C;
    --nav-bg:rgba(29,28,24,.82);
  }
}

*{box-sizing:border-box}
html{background:var(--paper);scroll-behavior:smooth}
/* An anchor must not drop its heading flush against the viewport edge, and it
   must clear the sticky nav -- anything under --nav-h lands behind the bar. */
section[id]{scroll-margin-top:calc(var(--nav-h) + 2rem)}
body{
  margin:0;
  color:var(--ink);
  background:var(--paper);
  background-image:
    radial-gradient(52rem 34rem at 82% -14%,rgba(27,97,68,.09),transparent 64%),
    radial-gradient(38rem 26rem at -8% 6%,rgba(30,28,25,.05),transparent 62%);
  background-attachment:fixed;
  font-family:var(--sans);
  font-size:1.0625rem;
  line-height:1.68;
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
  overflow-wrap:break-word;
}
::selection{background:var(--ink);color:var(--paper)}
img{max-width:100%;display:block}

@media (prefers-color-scheme:dark){
  body{
    background-image:
      radial-gradient(52rem 34rem at 82% -14%,rgba(108,199,156,.10),transparent 64%),
      radial-gradient(38rem 26rem at -8% 6%,rgba(237,234,226,.04),transparent 62%);
  }
  .portrait{box-shadow:0 2px 14px rgba(0,0,0,.45)}
  .go{border-bottom-color:rgba(108,199,156,.35)}
}

.wrap{
  max-width:80rem;
  margin:0 auto;
  padding:1.5rem var(--gutter) 5rem;
}
/* The shell is wide so structure can span it. Reading columns stay narrow,
   which is exactly how the reference sites do it: 1200px page, 672px prose. */
.col{max-width:40rem}
.hero .col{margin-inline:auto}

/* ---------- nav ----------
   Sticky, and it AUTO-HIDES at every width rather than sitting pinned:
   reading down slides it away, any upward scroll brings it back. A permanent
   bar costs viewport height on a page that is almost entirely reading.
   THE BAR IS THE INSET ROUNDED PILL, NOT A FULL-WIDTH STRIP. Every other block
   on this page is an inset card on paper, so a flat band running edge to edge
   contradicts the page and reads as browser chrome sitting above it. The
   colour is --surface, the same the "Fast, and it stays up" panel uses, which
   is what ties the two together; the SHAPE is what keeps it part of the design.
   .nav itself stays transparent and carries only the gutter, so the pill lands
   on exactly the left edge the content below it uses. Thresholds are in
   nav.js, not here. */
.nav{
  position:sticky;
  top:0;
  z-index:100;
  padding:.7rem var(--gutter) 0;
  background:transparent;
  transition:transform .28s ease;
}
.nav.nav-hidden{transform:translateY(-100%)}
/* an open drawer pins the nav: it must not slide away and strand the drawer */
body.nav-drawer-open .nav{transform:none}
.nav-inner{
  /* the page measure MINUS the gutter .nav already applies, so the pill's own
     edges line up with the cards instead of sitting one gutter wider */
  max-width:calc(80rem - var(--gutter) * 2);
  margin:0 auto;
  height:var(--nav-h);
  padding:0 1.15rem 0 1.35rem;
  display:flex;
  align-items:center;
  gap:1rem;
  background:var(--surface);
  border:1px solid var(--line);
  border-radius:999px;
  transition:background .25s ease,border-color .25s ease,box-shadow .25s ease;
}
.nav.scrolled .nav-inner{
  background:var(--nav-bg);
  backdrop-filter:blur(14px) saturate(160%);
  -webkit-backdrop-filter:blur(14px) saturate(160%);
  box-shadow:0 6px 22px rgba(30,28,25,.07);
}
.brand{
  font-family:var(--display);
  font-weight:700;
  font-size:1rem;
  letter-spacing:-.02em;
  color:var(--ink);
  border-bottom:0;
  margin-right:auto;
}
.brand:hover{color:var(--accent)}
.navlinks{display:none;gap:1.75rem}
.navlinks a{
  font-family:var(--mono);
  font-size:.78rem;
  letter-spacing:.04em;
  color:var(--muted);
  border-bottom:0;
}
.navlinks a:hover{color:var(--accent)}
.nav-cta{
  display:inline-flex;
  align-items:center;
  min-height:40px;
  padding:0 1.1rem;
  border:1px solid var(--line-2,var(--line));
  border-radius:999px;
  font-family:var(--display);
  font-weight:700;
  font-size:.85rem;
  color:var(--ink);
  background:transparent;
}
.nav-cta:hover{border-color:var(--accent);color:var(--accent)}

/* --- mobile nav (hamburger + slide-down drawer) ---
   Both the .nav-burger button and the .nav-drawer panel are BUILT AT RUNTIME
   by nav.js from the existing .navlinks, so the links have exactly one home in
   the markup. Below 46rem the desktop links hide and the burger takes over;
   before this existed the links simply vanished on a phone with nothing
   replacing them. */
.nav-burger{
  display:none;
  width:40px;height:40px;min-width:40px;flex-shrink:0;
  align-items:center;justify-content:center;
  background:transparent;border:1px solid var(--line);border-radius:10px;
  color:var(--ink);cursor:pointer;padding:0;
  -webkit-appearance:none;appearance:none;-webkit-tap-highlight-color:transparent;
}
.nav-burger:hover{border-color:var(--accent);color:var(--accent)}
/* Safari sizes SVGs from their attributes, not reliably from CSS on a flex
   child, so the element carries explicit width/height too. */
.nav-burger svg{width:20px;height:20px;display:block;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round}
.nav-drawer{
  position:fixed;top:calc(var(--nav-h) + 1.2rem);left:var(--gutter);right:var(--gutter);z-index:99;
  background:var(--nav-bg);
  backdrop-filter:blur(18px) saturate(160%);
  -webkit-backdrop-filter:blur(18px) saturate(160%);
  border:1px solid var(--line);border-radius:20px;
  padding:.5rem 1.35rem 1.25rem;
  max-height:calc(100vh - var(--nav-h) - 2rem);overflow-y:auto;
  overscroll-behavior:contain;
  transform:translateY(-8px);opacity:0;pointer-events:none;
  transition:opacity .2s ease,transform .2s ease;
}
.nav-drawer.open{transform:translateY(0);opacity:1;pointer-events:auto}
.nav-drawer a{
  display:block;padding:.85rem .5rem;
  font-family:var(--mono);font-size:.85rem;letter-spacing:.04em;
  color:var(--body);border-bottom:1px solid var(--line);
}
.nav-drawer a:hover{color:var(--accent)}
.nav-drawer .nd-cta{margin-top:1.1rem}
.nav-drawer .nd-cta a{
  border-bottom:0;text-align:center;
  font-family:var(--display);font-weight:700;font-size:.9rem;letter-spacing:0;
  color:var(--paper);background:var(--accent);
  border-radius:999px;padding:.8rem;
}
/* iOS Safari ignores overflow:hidden on <body> for touch scrolling, so that
   alone lets the page scroll behind an open drawer on iPhone. position:fixed
   is the only lock that holds everywhere; nav.js parks the scroll offset in
   `top` and restores it on close. Never reduce this to overflow:hidden. */
body.nav-drawer-open{position:fixed;left:0;right:0;width:100%;overflow:hidden}
@media (max-width:45.999rem){
  .nav-burger{display:flex}
}

/* ---------- hero: centred ---------- */
.hero{
  text-align:center;
  padding:1.5rem 0 0;
}
.portrait{
  width:92px;
  height:92px;
  border-radius:50%;
  object-fit:cover;
  margin:0 auto 1.6rem;
  filter:saturate(.92);
  box-shadow:0 3px 14px rgba(30,28,25,.14);
}
.eyebrow{
  font-family:var(--mono);
  font-size:.72rem;
  font-weight:500;
  letter-spacing:.3em;
  text-transform:uppercase;
  color:var(--faint);
  margin:0 0 1.1rem;
  padding-left:.3em;
}
h1{
  font-family:var(--display);
  font-size:clamp(2.15rem,5.6vw,3.75rem);
  font-weight:700;
  letter-spacing:-.03em;
  line-height:1.08;
  margin:0 auto 1.4rem;
  max-width:17ch;
  text-wrap:balance;
}
h1 .hl{color:var(--accent)}
.sub{
  font-size:1.09rem;
  color:var(--muted);
  margin:0 auto;
  max-width:50ch;
  text-wrap:pretty;
}
.sub strong{color:var(--ink);font-weight:600}
.cta{
  display:inline-flex;
  align-items:center;
  justify-content:center;
  min-height:52px;
  margin-top:2rem;
  padding:0 1.75rem;
  font-family:var(--display);
  font-size:1rem;
  font-weight:700;
  letter-spacing:-.01em;
  color:var(--paper);
  background:var(--ink);
  border:0;
  border-bottom:0;
  border-radius:999px;
  box-shadow:0 6px 20px -8px rgba(30,28,25,.55);
  transition:background-color .18s ease,transform .18s ease;
}
.cta:hover{background:var(--accent);color:var(--paper);transform:translateY(-2px)}

/* ---------- sections ---------- */
section{margin-top:4rem}

/* A tinted, bordered band. Used on the two sections that should read as set
   apart rather than as more page. */
.panel{
  background:var(--surface);
  border:1px solid var(--line);
  border-radius:20px;
  padding:2rem 1.5rem 2.15rem;
}
.panel h2{margin-bottom:1.5rem}

/* Two columns inside the panel so a wide card is not a narrow column of text
   with dead space beside it. */
.split{display:grid;gap:1.25rem}
.split-lead{
  font-family:var(--display);
  font-size:clamp(1.25rem,3vw,1.6rem);
  font-weight:700;
  letter-spacing:-.022em;
  line-height:1.22;
  color:var(--ink);
  margin:0;
}
.split-body p{margin:0 0 1rem;color:var(--body);max-width:40rem}
.split-body p:last-child{margin-bottom:0}

.contact{text-align:center}
.contact h2{justify-content:center}
.contact h2::after{display:none}
.contact-lead{
  font-family:var(--display);
  font-size:clamp(1.3rem,3.4vw,1.75rem);
  font-weight:700;
  letter-spacing:-.022em;
  line-height:1.25;
  color:var(--ink);
  max-width:26ch;
  margin:0 auto 1rem;
}
.contact-note{
  color:var(--muted);
  font-size:1rem;
  max-width:46ch;
  margin:0 auto;
}
.contact .cta{margin-top:1.75rem}
h2{
  display:flex;
  align-items:center;
  gap:.65rem;
  font-family:var(--mono);
  font-size:.7rem;
  font-weight:500;
  letter-spacing:.18em;
  text-transform:uppercase;
  color:var(--faint);
  margin:0 0 1.35rem;
}
h2::before{
  content:"";
  width:5px;
  height:5px;
  border-radius:50%;
  background:var(--accent);
  flex:0 0 auto;
  transition:width .25s ease,border-radius .25s ease;
}
/* A short section landing at the top of the viewport leaves the NEXT section
   occupying the centre, so the target marks itself rather than relying on
   scroll position to communicate which one was asked for. */
section:target h2{color:var(--accent)}
section:target h2::before{width:22px;border-radius:2px}
section > p{margin:0 0 1.15rem;color:var(--body);max-width:40rem}
section > p:last-child{margin-bottom:0}

/* ---------- now ---------- */
.now{
  list-style:none;
  padding:0;
  margin:0;
  max-width:40rem;
}
.now li{
  color:var(--body);
  position:relative;
  padding-left:1.35rem;
  margin-bottom:.7rem;
}
.now li:last-child{margin-bottom:0}
.now li::before{
  content:"";
  position:absolute;
  left:0;
  top:.82em;
  width:.5rem;
  height:1px;
  background:var(--accent);
}

/* ---------- built ---------- */
.entry{
  padding:1.6rem 0;
  border-top:1px solid var(--line);
}
.entry:first-of-type{padding-top:0;border-top:0}
.entry p{max-width:40rem}

.work-grid{
  display:grid;
  grid-template-columns:1fr;
  gap:1rem;
  margin-top:2rem;
}
.work-grid .entry{
  padding:1.4rem 1.5rem 1.5rem;
  border-top:0;
  border:1px solid var(--line);
  border-radius:14px;
  background:var(--surface,transparent);
}
.work-grid .entry p{max-width:none;font-size:.97rem}

.featured .shot{margin-bottom:1.35rem}

/* Only risksignal.io is public, so only that one is an <a>; the rest use a
   plain <div class="shot">. The hover lift below is scoped to a.shot, so a
   frame that goes nowhere never reads as clickable. */
.shot{
  display:block;
  margin:0 0 1.25rem;
  border:1px solid var(--line);
  border-bottom:1px solid var(--line);
  border-radius:12px;
  overflow:hidden;
  background:var(--paper);
  box-shadow:0 2px 6px rgba(30,28,25,.07),0 18px 40px -22px rgba(30,28,25,.45);
  transition:transform .2s ease,box-shadow .2s ease;
  padding-bottom:0;
  line-height:0;
}
.shot img{width:100%;height:auto}
a.shot:hover{
  transform:translateY(-3px);
  box-shadow:0 3px 8px rgba(30,28,25,.09),0 26px 52px -24px rgba(30,28,25,.55);
}
a.shot:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
/* The caption under an unlinked frame, stating why it has no link. */
.entry .note{
  margin-top:.7rem;
  font-family:var(--mono);
  font-size:.75rem;
  letter-spacing:.01em;
  color:var(--muted,var(--body));
  opacity:.75;
}
.entry h3{
  font-family:var(--display);
  font-size:1.15rem;
  font-weight:700;
  letter-spacing:-.017em;
  margin:0 0 .5rem;
}
.entry p{margin:0;color:var(--body)}
.entry .go{
  display:inline-block;
  margin-top:.7rem;
  font-family:var(--mono);
  font-size:.78rem;
  letter-spacing:.02em;
}

/* ---------- links ---------- */
a{
  color:var(--ink);
  text-decoration:none;
  border-bottom:1px solid var(--line);
  padding-bottom:1px;
  transition:border-color .16s ease,color .16s ease;
}
a:hover{color:var(--accent);border-bottom-color:var(--accent)}
a:focus-visible{
  outline:2px solid var(--accent);
  outline-offset:3px;
  border-bottom-color:transparent;
}
.go{color:var(--accent);border-bottom-color:rgba(27,97,68,.3)}

.elsewhere{
  display:flex;
  flex-wrap:wrap;
  gap:.5rem 1.6rem;
  font-family:var(--mono);
  font-size:.83rem;
  margin:0;
}

/* ---------- footer ---------- */
/* The footer owns every bit of space around itself, top and bottom. The shell's
   own bottom padding would otherwise land UNDER the last line as a second gap
   larger than the footer itself, so it is dropped wherever a footer closes the
   shell. */
.wrap:has(> .foot){padding-bottom:0}
.foot{
  margin-top:4rem;
  padding:1.6rem 0 1.8rem;
  border-top:1px solid var(--line);
}
.foot-top{
  display:flex;
  flex-direction:column;
  gap:1.75rem;
  justify-content:space-between;
}
.foot-brand{
  font-family:var(--display);
  font-size:1.05rem;
  font-weight:700;
  letter-spacing:-.02em;
  margin:0;
}
.foot-tag{
  color:var(--muted);
  font-size:.95rem;
  margin:.3rem 0 0;
  max-width:34ch;
}
.foot-links{
  display:flex;
  flex-wrap:wrap;
  gap:.65rem 1.5rem;
  align-content:start;
}
.foot-links a{
  font-family:var(--mono);
  font-size:.8rem;
  color:var(--muted);
  border-bottom:0;
}
.foot-links a:hover{color:var(--accent)}
.foot-meta{
  margin:2rem 0 0;
  font-family:var(--mono);
  font-size:.7rem;
  letter-spacing:.14em;
  text-transform:uppercase;
  color:var(--faint);
}

/* ---------- freshness ---------- */
.when{
  margin:1.15rem 0 0;
  font-family:var(--mono);
  font-size:.7rem;
  letter-spacing:.14em;
  text-transform:uppercase;
  color:var(--faint);
}

/* ---------- larger screens ---------- */
@media (min-width:46rem){
  /* one gutter token: .wrap and the nav pill both read it, so they cannot
     drift apart at any width */
  :root{--gutter:2rem}
  .wrap{padding:2rem var(--gutter) 6rem}
  .navlinks{display:flex}
  .hero{padding-top:2.5rem}
  .portrait{width:112px;height:112px;margin-bottom:2rem}
  section{margin-top:7rem}
  .work-grid{grid-template-columns:repeat(3,1fr);gap:1.25rem;margin-top:2.5rem}
  section[id]{scroll-margin-top:20vh}
  .panel{padding:2.75rem 3rem 3rem}
  .split{grid-template-columns:0.9fr 1.25fr;gap:3.5rem;align-items:start}
  .foot-top{flex-direction:row;align-items:flex-start;gap:3rem}
  .foot-links{justify-content:flex-end;max-width:32rem}
  .featured{
    display:grid;
    grid-template-columns:1.15fr 1fr;
    gap:2.75rem;
    align-items:center;
    padding-bottom:0;
  }
  .featured .shot{margin-bottom:0}
  .featured .featured-text{min-width:0}
}

@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *{transition:none!important}
}

/* Printing or saving to PDF should keep the URLs, since every link target is
   otherwise lost on paper. */
@media print{
  body{
    background:#fff;
    background-image:none;
    color:#000;
    font-size:11pt;
  }
  .wrap{max-width:none;padding:0}
  .portrait{width:76px;height:76px;box-shadow:none}
  a{color:#000;border-bottom:0}
  .sub a[href^="http"]::after,
  .now a[href^="http"]::after,
  .entry a[href^="http"]::after,
  .elsewhere a[href^="http"]::after{
    content:" (" attr(href) ")";
    font-family:var(--mono);
    font-size:.8em;
    color:#444;
  }
  .panel{background:none;border:0;padding:0}
  .entry{break-inside:avoid}
  h1,h2,h3{break-after:avoid}
}
