/* site.css — THE design layer. Everything visual lives in this one file, so the look can be
 * replaced without touching the content (allowlist/*.json) or the safety gate (lib/, tests/).
 *
 * Theme after Matthäus Jandl's portfolio (awwwards): a modular grid, an inset rounded panel
 * on a plain field, a wall of black symbols, and an oversized wordmark that anchors the page.
 *
 * The one deliberate departure: Jandl's symbols are bold flat silhouettes that survive at
 * 150px. Kyle's drawings are dense pen-and-ink and turn to grey mush at that size — so the
 * wall runs fewer, larger cells, and every drawing is cropped to its ink. Same idea,
 * adapted to the artwork rather than imposed on it.
 *
 * Type is a system grotesk. No webfont: a CDN font leaks every visitor's IP to a third party
 * and adds a network dependency to a page whose whole claim is that it is self-contained.
 * On the Mac and iPhone this renders as Helvetica Neue, which is the reference's face anyway.
 */

:root {
  --field: #e9e9e6;      /* the page behind the panels */
  --panel: #f2f2f0;      /* hero panel */
  --paper: #fbfbfa;      /* content panels */
  --ink: #111110;
  --ink-2: #3a3a37;
  --muted: #86867e;
  --line: #dededa;
  --line-2: #eeeeea;

  --ok: #1f7a53;
  --warn: #9a6520;
  --bad: #a8322b;

  --sans: "Helvetica Neue", Helvetica, Inter, system-ui, -apple-system, Arial, sans-serif;
  --mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, monospace;

  --r: 14px;
  --gap: 10px;
  --pad: clamp(18px, 3.2vw, 40px);
}

@media (prefers-color-scheme: dark) {
  :root {
    --field: #0e0e0f;
    --panel: #171719;
    --paper: #141416;
    --ink: #f2f2ef;
    --ink-2: #cdcdc7;
    --muted: #85857e;
    --line: #2b2b2f;
    --line-2: #212125;
    --ok: #4bbd8a;
    --warn: #d29a53;
    --bad: #e0716c;
  }
}

* { box-sizing: border-box; }

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

body {
  margin: 0;
  background: var(--field);
  color: var(--ink);
  font: 400 16px/1.45 var(--sans);
  -webkit-font-smoothing: antialiased;
  letter-spacing: -0.005em;
}

.skip { position: absolute; left: -9999px; padding: 13px 16px; }
.skip:focus {
  left: 14px; top: 14px; z-index: 20; background: var(--paper);
  padding: 13px 16px; border-radius: 6px; border: 1px solid var(--line);
}

a { color: inherit; }

main { padding: var(--gap); display: flex; flex-direction: column; gap: var(--gap); }

.panel {
  background: var(--paper);
  border-radius: var(--r);
  padding: var(--pad);
}

/* =========================================================================
   HERO — bio + drawing wall + oversized name + rail
   ========================================================================= */

.hero {
  background: var(--panel);
  border-radius: var(--r);
  padding: var(--pad);
  display: flex;
  flex-direction: column;
  gap: clamp(20px, 3vw, 40px);
  min-height: min(88vh, 900px);
}

.hero-top {
  display: grid;
  grid-template-columns: minmax(240px, 25%) 1fr;
  gap: clamp(20px, 4vw, 56px);
  align-items: start;
}

.hero-bio {
  font: 400 clamp(15px, 1.35vw, 19px)/1.35 var(--sans);
  letter-spacing: -0.017em;
  color: var(--ink);
  margin: 0;
  max-width: 34ch;
}

/* The drawing wall. Jandl runs 4x2 tiny symbols; these want room, so it is 4-up on desktop
   with each cell tall enough that the line work still reads. */
.wall {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(8px, 1.4vw, 22px);
  align-items: center;
  justify-items: center;
}

.wall img {
  display: block;
  width: 100%;
  height: clamp(84px, 12.5vw, 178px);
  object-fit: contain;
  /* The drawings are 1-bit black ink on white. Multiply drops the white to nothing on the
     panel; in dark mode we invert the ink instead of shipping a second asset. */
  mix-blend-mode: multiply;
}

@media (prefers-color-scheme: dark) {
  .wall img, .strip img { filter: invert(1); mix-blend-mode: screen; }
}

.hero-name {
  font: 500 clamp(52px, 13.2vw, 208px)/0.86 var(--sans);
  letter-spacing: -0.045em;
  /* Anchors the wordmark and the rail to the floor of the panel, so the bio and the drawing
     wall breathe at the top. In the reference the name is the base the whole panel sits on;
     left to flow it strands a dead band under the rail. */
  margin: auto 0 0;
  white-space: nowrap;
  color: var(--ink);
}

.rail {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  border-top: 1px solid var(--line);
  padding-top: 16px;
  font: 400 clamp(13px, 1.1vw, 16px)/1 var(--sans);
  letter-spacing: -0.01em;
}

/* 44pt tap target (Apple's minimum) via padding, with negative margin so the rail's
   visual position is unchanged. Measured, not guessed — tools/device_check.mjs reported
   these links at 13pt tall on a real 440pt viewport. */
.rail a {
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  margin: -14px 0;
}
.rail a:hover { opacity: 0.55; }
.rail .mid { color: var(--muted); }

/* =========================================================================
   STATEMENT — the big centred line
   ========================================================================= */

.statement {
  background: transparent;
  padding: clamp(48px, 9vw, 128px) var(--pad);
  text-align: center;
}

.statement p {
  margin: 0 auto;
  max-width: 20ch;
  font: 400 clamp(24px, 3.6vw, 46px)/1.16 var(--sans);
  letter-spacing: -0.03em;
}

/* =========================================================================
   SECTIONS
   ========================================================================= */

.eyebrow {
  font: 400 12.5px/1 var(--sans);
  color: var(--muted);
  margin: 0 0 14px;
  letter-spacing: 0;
}

.head {
  font: 500 clamp(26px, 3.4vw, 44px)/1.08 var(--sans);
  letter-spacing: -0.032em;
  margin: 0 0 8px;
  max-width: 18ch;
}

.sub {
  color: var(--muted);
  font: 400 15px/1.45 var(--sans);
  margin: 0 0 30px;
  max-width: 56ch;
}

.about-grid {
  display: grid;
  grid-template-columns: minmax(260px, 30%) 1fr;
  gap: clamp(20px, 4vw, 56px);
}

.about-body p {
  margin: 0 0 16px;
  max-width: 62ch;
  font-size: clamp(16px, 1.25vw, 18.5px);
  line-height: 1.5;
  letter-spacing: -0.012em;
}
.about-body p:last-child { margin-bottom: 0; }

/* --- filters --- */

.filters { display: flex; flex-wrap: wrap; gap: 7px; margin: 0 0 22px; }

.filters button {
  font: 400 13px/1 var(--sans);
  letter-spacing: -0.01em;
  color: var(--muted);
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 100px;
  padding: 9px 15px;
  min-height: 44px;
  cursor: pointer;
}
.filters button:hover { color: var(--ink); }
.filters button[aria-pressed="true"] {
  color: var(--paper); background: var(--ink); border-color: var(--ink);
}

/* --- cards --- */

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(292px, 100%), 1fr));
  gap: var(--gap);
}

.card {
  background: var(--panel);
  border-radius: 10px;
  padding: 22px;
  display: flex;
  flex-direction: column;
}

.card h3 {
  font: 500 18px/1.25 var(--sans);
  letter-spacing: -0.022em;
  margin: 0 0 5px;
}

.card .headline {
  font: 400 14px/1.35 var(--sans);
  letter-spacing: -0.012em;
  color: var(--ink-2);
  margin: 0 0 12px;
}

.card .blurb {
  font: 400 14px/1.5 var(--sans);
  color: var(--muted);
  margin: 0 0 18px;
  letter-spacing: -0.006em;
}

.card .tags { margin-top: auto; }

.tags { display: flex; flex-wrap: wrap; gap: 5px; }

.tag {
  font: 400 11.5px/1 var(--sans);
  color: var(--muted);
  border: 1px solid var(--line);
  border-radius: 100px;
  padding: 6px 9px;
  white-space: nowrap;
}
.tag.ok { color: var(--ok); border-color: color-mix(in srgb, var(--ok) 32%, transparent); }
.tag.warn { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 32%, transparent); }
.tag.bad { color: var(--bad); border-color: color-mix(in srgb, var(--bad) 32%, transparent); }

/* --- loops get a drawing --- */

.loop-card { position: relative; overflow: hidden; }

.loop-card .mark {
  position: absolute;
  right: -26px; top: -18px;
  width: 156px; height: 156px;
  object-fit: contain;
  opacity: 0.085;
  pointer-events: none;
  mix-blend-mode: multiply;
}
@media (prefers-color-scheme: dark) {
  .loop-card .mark { filter: invert(1); mix-blend-mode: screen; opacity: 0.13; }
}
.loop-card > * { position: relative; }

/* --- stats --- */

.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(136px, 1fr));
  gap: var(--gap);
  margin-bottom: 34px;
}

.stat { background: var(--panel); border-radius: 10px; padding: 20px; }

.stat .n {
  font: 500 clamp(30px, 3.6vw, 46px)/1 var(--sans);
  letter-spacing: -0.04em;
  display: block;
  margin-bottom: 7px;
}

.stat .l { font: 400 12.5px/1.3 var(--sans); color: var(--muted); }

.bars { display: flex; flex-direction: column; gap: 9px; max-width: 620px; }
.bar { display: grid; grid-template-columns: 104px 1fr 40px; align-items: center; gap: 12px; }
.bar .k { font: 400 12.5px/1 var(--sans); color: var(--muted); }
.bar .t { height: 6px; background: var(--line-2); border-radius: 100px; overflow: hidden; }
.bar .f { height: 100%; background: var(--ink); border-radius: 100px; }
.bar .f.ok { background: var(--ok); } .bar .f.warn { background: var(--warn); } .bar .f.bad { background: var(--bad); }
.bar .v { font: 400 12.5px/1 var(--mono); color: var(--muted); text-align: right; }

.cloud { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 34px; }
.cloud .tag b { font-weight: 500; color: var(--ink-2); }

/* --- closing strip of drawings, echoing the hero wall --- */

.strip {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(8px, 1.4vw, 22px);
  align-items: center;
  padding: clamp(26px, 4vw, 52px) var(--pad);
}

.strip img {
  display: block; width: 100%;
  height: clamp(70px, 9vw, 128px);
  object-fit: contain;
  mix-blend-mode: multiply;
}

/* --- footer --- */

.foot { padding: 0 var(--gap) var(--gap); }
.foot-in {
  background: var(--panel);
  border-radius: var(--r);
  padding: 26px var(--pad) 30px;
  color: var(--muted);
  font: 400 13px/1.6 var(--sans);
}
.foot p { margin: 0 0 4px; }
.foot .asof { font-family: var(--mono); font-size: 11px; margin-top: 10px; }

.foot-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 24px;
  flex-wrap: wrap;
}

/* Circular icon links, per the Morningstar footer Kyle referenced. */
.socials { display: flex; gap: 10px; flex-shrink: 0; }

.social {
  width: 44px; height: 44px;
  border: 1px solid var(--line);
  border-radius: 100px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--ink-2);
}
.social:hover { color: var(--paper); background: var(--ink); border-color: var(--ink); }
.social svg { display: block; }

.ventures { margin: 10px 0 0; }
.ventures .vlabel { color: var(--muted); }
/* 44pt tap target on an inline link: pad the block axis, pull it back with a negative
   margin so the footer line does not grow. */
.ventures a {
  color: var(--ink-2);
  text-decoration: none;
  border-bottom: 1px solid var(--line);
  display: inline-block;
  padding: 14px 3px;
  margin: -14px 0;
}
.ventures a:hover { color: var(--ink); border-color: var(--ink); }
.ventures .dot { color: var(--muted); margin: 0 4px; }

@media (max-width: 640px) {
  .foot-row { flex-direction: column; }
}

/* =========================================================================
   NARROW
   ========================================================================= */

@media (max-width: 860px) {
  .hero-top, .about-grid { grid-template-columns: 1fr; }
  .hero-name { font-size: clamp(40px, 15vw, 90px); }
  .rail .mid { display: none; }

  /* The hero is a full-height panel on desktop, where `margin-top: auto` floats the
     wordmark down to the floor. On a phone the panel is taller than its content, so that
     same rule stranded ~700pt of empty space between the drawings and the name — measured
     on a real 440pt viewport. Let the panel size to its content instead. */
  .hero { min-height: 0; gap: 30px; }
  .hero-name { margin-top: 0; }

  /* Reclaim the space for the artwork: bigger cells now that nothing is competing. */
  .wall img { height: clamp(96px, 21vw, 150px); }
}

@media (prefers-reduced-motion: no-preference) {
  .filters button, .rail a { transition: opacity .15s, color .15s, background .15s; }

  /* The drawing wall breathes under the cursor. Hovering the wall floats every drawing
     on a slow swell, staggered by --wi (set per-image by the generator) so the motion
     rolls across as a wave instead of pumping in lockstep; the drawing under the cursor
     pulses a little larger. Inside reduced-motion so it never plays for anyone who has
     asked interfaces to hold still. */
  .wall img { transition: transform .5s ease; }
  .wall:hover img {
    animation: wall-wave 3.6s ease-in-out infinite;
    animation-delay: calc(var(--wi, 0) * 0.22s);
  }
  .wall img:hover,
  .wall .wall-link:hover img {
    animation: wall-pulse 2.2s ease-in-out infinite;
  }
  @keyframes wall-wave {
    0%, 100% { transform: translateY(0) scale(1); }
    50%      { transform: translateY(-7px) scale(1.015); }
  }
  @keyframes wall-pulse {
    0%, 100% { transform: translateY(-2px) scale(1.03); }
    50%      { transform: translateY(-8px) scale(1.07); }
  }
}
