/* ═══════════════════════════════════════════════════════════════════
   provire.com — home
   A person, not a console. Dark but calm: one column, generous measure,
   editorial type, a single muted accent. Deliberately shares nothing
   with the project themes; the contrast between this page and those is
   the point.
   ═══════════════════════════════════════════════════════════════════ */
:root {
  /* slate, not neutral black: this is a chalkboard, and a well-used one */
  --bg:      #0E1411;
  --bg-2:    #141B17;
  --fg:      #EDEAE0;   /* chalk white, warm */
  --fg-2:    #A8AFA4;
  /* the dimmest text sits directly over the board, so it is set against the
     brightest chalk the mask trough can let through, not against the flat
     background: 4.99:1 over that, 7.23:1 over bare slate */
  --fg-3:    #9BA497;
  --rule:    #222C27;
  --rule-2:  #19211D;
  --accent:  #E5D68C;   /* pale yellow, the second stick in the tray */
  --accent-d:#A89C55;

  --disp: "Bricolage Grotesque", "Helvetica Neue", sans-serif;
  --body: "Newsreader", Georgia, serif;
  --mono: "JetBrains Mono", ui-monospace, Menlo, monospace;

  --measure: 66ch;
  --pad: clamp(1.4rem, 5vw, 3rem);
  --e: cubic-bezier(.2, .7, .2, 1);
}

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font: 300 clamp(1.02rem, .35vw + .95rem, 1.14rem)/1.68 var(--body);
  /* a barely-there warmth so the flat dark does not read as a terminal */
  background-image: radial-gradient(120% 80% at 50% -10%, #16201B 0%, transparent 62%);
  background-repeat: no-repeat;
}

::selection { background: var(--accent); color: #0E1411; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 2px; }

.skip {
  position: absolute; left: -9999px;
  background: var(--accent); color: #0E1411; padding: .6rem 1rem;
  font: 500 .8rem/1 var(--mono);
}
.skip:focus { left: 1rem; top: 1rem; z-index: 10; }

main { max-width: 62rem; margin: 0 auto; padding: 0 var(--pad); }

/* ── intro ─────────────────────────────────────────────────────────── */
.intro { padding: clamp(4rem, 12vw, 8.5rem) 0 clamp(3rem, 7vw, 5rem); }
.intro__name {
  margin: 0 0 1.4rem;
  font: 700 clamp(2.6rem, 7vw, 4.1rem)/1.02 var(--disp);
  letter-spacing: -.025em;
  font-variation-settings: "opsz" 72;
}
.intro__bio {
  margin: 0 0 2rem; max-width: var(--measure);
  font-size: 1.2em; line-height: 1.6; color: var(--fg);
}
.intro__meta {
  display: flex; flex-wrap: wrap; align-items: center; gap: .55rem 1.4rem;
  margin: 0; padding-top: 1.4rem;
  border-top: 1px solid var(--rule);
  font: 400 .84rem/1 var(--mono); letter-spacing: .01em;
}
.intro__where { color: var(--fg-3); }
.intro__meta a { color: var(--fg-2); text-decoration: none; border-bottom: 1px solid var(--rule); padding-bottom: 2px; }
.intro__meta a:hover { color: var(--accent); border-bottom-color: var(--accent-d); }

/* ── section furniture ─────────────────────────────────────────────── */
section { padding: clamp(2.4rem, 6vw, 4rem) 0; border-top: 1px solid var(--rule-2); }
.h2 {
  margin: 0 0 .5rem;
  font: 600 .82rem/1 var(--mono);
  letter-spacing: .18em; text-transform: uppercase; color: var(--fg-3);
}
.lede { margin: 0 0 2rem; max-width: 56ch; color: var(--fg-2); font-size: .98em; }

/* ── status pill ───────────────────────────────────────────────────── */
.st {
  display: inline-flex; align-items: center; gap: .42rem;
  font: 400 .74rem/1 var(--mono); letter-spacing: .06em; color: var(--fg-3);
  white-space: nowrap;
}
.st::before {
  content: ""; width: 6px; height: 6px; border-radius: 50%;
  background: var(--fg-3); flex: none;
}
.st--run::before { background: var(--accent); box-shadow: 0 0 0 3px rgba(229,214,140,.14); }
.st--bench::before { background: #C9A227; }
.st--dead::before { background: none; border: 1px solid var(--fg-3); }

/* ── selected work ─────────────────────────────────────────────────── */
.cards { display: grid; gap: 1px; background: var(--rule-2); border: 1px solid var(--rule-2); }
@media (min-width: 720px) { .cards { grid-template-columns: 1fr 1fr; } }
.card {
  display: flex; flex-direction: column; gap: .55rem;
  padding: clamp(1.4rem, 2.6vw, 1.9rem);
  background: var(--bg); text-decoration: none; color: inherit;
  transition: background .25s var(--e);
}
.card:hover { background: var(--bg-2); }
.card__top { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.card__notes { font: 400 .74rem/1 var(--mono); color: var(--fg-3); }
.card__name {
  margin: .2rem 0 0;
  font: 600 clamp(1.28rem, 2vw, 1.5rem)/1.2 var(--disp);
  letter-spacing: -.012em; color: var(--fg);
}
.card:hover .card__name { color: var(--accent); }
.card__sum { margin: 0; color: var(--fg-2); font-size: .97em; }
.card__fields {
  margin-top: auto; padding-top: .9rem;
  font: 400 .74rem/1.5 var(--mono); color: var(--fg-3);
}

/* ── recent notes ──────────────────────────────────────────────────── */
.notes__list { list-style: none; margin: 0; padding: 0; }
.notes__list li {
  display: grid; gap: .15rem .3rem; grid-template-columns: 1fr;
  padding: .95rem 0; border-bottom: 1px solid var(--rule-2);
}
@media (min-width: 760px) {
  .notes__list li { grid-template-columns: 8.5rem 1fr auto; align-items: baseline; gap: 0 1.6rem; }
}
.notes__list time { font: 400 .8rem/1.5 var(--mono); color: var(--fg-3); white-space: nowrap; }
.notes__list a {
  color: var(--fg); text-decoration: none;
  border-bottom: 1px solid transparent; font-size: 1.02em;
}
.notes__list a:hover { color: var(--accent); border-bottom-color: var(--accent-d); }
.note__in { font: 400 .78rem/1.5 var(--mono); color: var(--fg-3); }
@media (min-width: 760px) { .note__in { text-align: right; } }

/* ── everything else ───────────────────────────────────────────────── */
.all { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--rule-2); }
.all li { border-bottom: 1px solid var(--rule-2); }
.all a {
  display: grid; gap: .15rem 1.2rem; grid-template-columns: 1fr auto;
  align-items: baseline; padding: .85rem .2rem;
  text-decoration: none; color: inherit;
  transition: background .2s, padding-left .2s var(--e);
}
@media (min-width: 820px) {
  .all a { grid-template-columns: 16rem 1fr 2.5rem 8rem; }
}
.all a:hover { background: var(--bg-2); padding-left: .6rem; }
.all__name { color: var(--fg); font-size: 1em; }
.all a:hover .all__name { color: var(--accent); }
.all__fields { font: 400 .78rem/1.5 var(--mono); color: var(--fg-3); }
.all__notes { font: 400 .78rem/1.5 var(--mono); color: var(--fg-3); text-align: right; }

/* ── foot ──────────────────────────────────────────────────────────── */
.foot {
  max-width: 62rem; margin: 0 auto;
  padding: 2.5rem var(--pad) 3.5rem;
  border-top: 1px solid var(--rule);
  display: flex; flex-wrap: wrap; justify-content: space-between; gap: .8rem 2rem;
  font: 400 .8rem/1.5 var(--mono); color: var(--fg-3);
}
.foot p { margin: 0; }
.foot__links { display: flex; flex-wrap: wrap; gap: 1.2rem; }
.foot a { color: var(--fg-2); text-decoration: none; border-bottom: 1px solid var(--rule); }
.foot a:hover { color: var(--accent); border-bottom-color: var(--accent-d); }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { transition-duration: .01ms !important; animation-duration: .01ms !important; }
}

/* ═══════════════════════════════════════════════════════════════════
   style pass
   The first cut was too bland: flat ground, uniform grey cards, no
   motion. Adding presence without going back to product-page furniture,
   so: paper grain, a tinted edge per card that previews where it leads,
   a heavier name, and one restrained reveal.
   ═══════════════════════════════════════════════════════════════════ */

/* grain, so the dark reads as a surface rather than an absence */
body::before {
  content: ""; position: fixed; inset: 0; z-index: 0; pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  opacity: .045; mix-blend-mode: overlay;
}
main, .foot { position: relative; z-index: 1; }

/* the name carries the page, so let it */
.intro { position: relative; }
.intro__name {
  font-weight: 700; font-size: clamp(3rem, 9vw, 5.4rem);
  letter-spacing: -.035em; line-height: .95;
  background: linear-gradient(180deg, var(--fg) 30%, #9AA396 100%);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent;
}
/* a hairline that tracks the accent, tying the head to the cards below */
.intro__name::after {
  content: ""; display: block;
  width: 3.5rem; height: 3px; margin: 1.3rem 0 0;
  background: var(--accent);
  box-shadow: 0 0 18px rgba(229,214,140,.35);
}
.intro__bio { font-size: 1.26em; line-height: 1.58; }
.intro__bio::first-line { color: #F5F3EE; }

/* cards borrow their destination's accent, so colour on this page is a
   preview of where the link goes rather than decoration */
.card { position: relative; overflow: hidden; }
.card::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 3px;
  background: var(--tint, var(--accent));
  opacity: .5; transition: opacity .25s var(--e);
}
.card::after {
  content: ""; position: absolute; inset: auto -30% -60% auto;
  width: 16rem; height: 16rem; border-radius: 50%;
  background: radial-gradient(circle, var(--tint, var(--accent)), transparent 70%);
  opacity: 0; transition: opacity .35s var(--e); pointer-events: none;
}
.card:hover::before { opacity: 1; }
.card:hover::after { opacity: .07; }
.card:hover .card__name { color: var(--tint, var(--accent)); }
.card__name { transition: color .2s var(--e); }
.card .st--run::before { background: var(--tint, var(--accent)); box-shadow: 0 0 0 3px color-mix(in srgb, var(--tint) 16%, transparent); }

/* notes get the same tint as a small locator dot */
.notes__list li { position: relative; }
.note__dot {
  position: absolute; left: -.85rem; top: 1.35rem;
  width: 5px; height: 5px; border-radius: 50%;
  background: var(--tint, var(--fg-3)); opacity: .75;
}
@media (max-width: 759px) { .note__dot { display: none; } }
.notes__list li:hover .note__dot { opacity: 1; box-shadow: 0 0 8px var(--tint); }

/* one reveal on load, staggered. Nothing scroll-triggered: the page is
   short enough that it would just feel laggy. */
.js .intro, .js .work, .js .notes, .js .all-wrap {
  opacity: 0; transform: translateY(10px);
  animation: rise .7s var(--e) forwards;
}
.js .work  { animation-delay: .10s }
.js .notes { animation-delay: .18s }
.js .all-wrap { animation-delay: .26s }
@keyframes rise { to { opacity: 1; transform: none } }

@media (prefers-reduced-motion: reduce) {
  .js .intro, .js .work, .js .notes, .js .all-wrap {
    opacity: 1; transform: none; animation: none;
  }
}

/* ═══════════════════════════════════════════════════════════════════
   polish pass
   Still reading flat, because every section had identical weight and
   there was nothing on the page but text. Adding: a colour wash drawn
   from the project accents, a "right now" line, numbered section marks,
   an asymmetric card rhythm, and per-row tints in the long list.
   ═══════════════════════════════════════════════════════════════════ */

/* The board itself: chalk diagrams on slate, generated by assets/_chalkgen.py
   and regenerated from there, not edited by hand. Replaces an earlier set of
   coloured radial washes, which read as lens flare rather than as anything.

   Sixteen working sketches, faint enough to sit behind text: a LoRa up-chirp
   in both frequency and time, a Bode roll-off with its asymptote, a Bayesian
   update, gradient descent over contours, a 16-QAM constellation with noise
   clouds, a clk/sda timing diagram, a Merkle tree, projectile with and
   without drag, a strike zone, a state machine, an s-plane, an FFT, a queue,
   a four-element antenna pattern, a second-order step response, and a
   Fourier series ringing its way towards a square wave. Eleven equations are
   lettered by the same hand as the strokes, through a stroke font, because an
   SVG used as a CSS background cannot load a webfont. */
body::after {
  content: ""; position: fixed; inset: 0; z-index: 0; pointer-events: none;
  background-image: url("/assets/chalk.svg");
  background-position: center top;
  background-repeat: no-repeat;
  background-size: min(1600px, 130%) auto;
  opacity: .8;
  /* Hold the board back behind the reading column, otherwise the diagrams
     cross straight through the bio and the section labels.

     The stops are in rem, not percentages, because the column is a fixed
     62rem centred in the viewport. Percentage stops only line up at one
     window size: at 1024px the column spans the whole screen and the text
     ended up over the full-brightness edges, which took the small mono
     labels down to 2.3:1. In rem the trough always covers the column and
     full brightness is only reachable where no text can sit. */
  mask-image: var(--board-mask);
  -webkit-mask-image: var(--board-mask);
}
:root {
  --board-mask: linear-gradient(90deg,
    #000 0,
    rgba(0,0,0,.26) calc(50% - 33rem),
    rgba(0,0,0,.26) calc(50% + 33rem),
    #000 100%);
}
/* chalk dust: what is left where the board has been wiped */
body { background-blend-mode: normal; }
main::before {
  content: ""; position: fixed; inset: 0; z-index: 0; pointer-events: none;
  background:
    radial-gradient(60% 40% at 18% 8%,  rgba(237,234,224,.045), transparent 70%),
    radial-gradient(50% 34% at 82% 44%, rgba(237,234,224,.035), transparent 72%),
    radial-gradient(70% 30% at 40% 92%, rgba(237,234,224,.03),  transparent 74%);
}

/* right now: the most human line on the page, so give it presence */
.intro__now {
  margin: 0 0 2rem; max-width: 54ch;
  padding-left: 1rem; border-left: 2px solid var(--accent);
  font: 400 1.02em/1.55 var(--body); font-style: italic; color: var(--fg-2);
}

/* numbered marks, so the three sections read as a sequence */
.h2 { display: flex; align-items: baseline; gap: .7rem; }
.h2 i {
  font-style: normal; font-weight: 400;
  color: var(--accent); opacity: .75; letter-spacing: .1em;
}

/* asymmetric rhythm: wide, pair, wide. Guarded so it degrades if the
   featured count ever changes. */
@media (min-width: 720px) {
  .cards { grid-template-columns: 1fr 1fr; }
  .cards > .card:nth-child(1) { grid-column: span 2; }
  .cards > .card:nth-child(4):last-child { grid-column: span 2; }
  /* the lead card gets room to breathe */
  .cards > .card:nth-child(1) { padding: clamp(1.8rem, 3.2vw, 2.6rem); }
  .cards > .card:nth-child(1) .card__name { font-size: clamp(1.5rem, 2.6vw, 2rem); }
  .cards > .card:nth-child(1) .card__sum { font-size: 1.06em; max-width: 52ch; }
}

/* long list: a tint edge on hover, so it is obviously the same set of
   projects as the cards above */
.all a { position: relative; }
.all a::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 2px;
  background: var(--tint, var(--accent)); opacity: 0; transition: opacity .2s var(--e);
}
.all a:hover::before { opacity: .9; }

/* notes: a quiet affordance on hover rather than nothing */
.notes__list a { position: relative; }
.notes__list a::after {
  content: " \2192"; opacity: 0; transition: opacity .2s var(--e);
}
.notes__list a:hover::after { opacity: .7; }

.foot { align-items: baseline; }
