/* ══════════════════════════════════════════════════════════════════════
   nickboylan.com

   WillowWorks — core + Balsam skin.
   Core declares slots and never names a hue or a typeface. The skin
   below fills every slot, for every mode. Components reference slots
   only; a component that names a hex is a bug.

   Reference: Documents/WillowWorks/design-study/
   ══════════════════════════════════════════════════════════════════════ */

/* ──────────────────────────────────────────────────────────────────────
   CORE — structure, rhythm, geometry, motion. No hues. No faces.
   ────────────────────────────────────────────────────────────────────── */
:root {
  /* grid */
  --ww-unit: 4px;
  --ww-baseline: 24px;

  /* spacing — named by value, the way a draftsman reads a dimension */
  --ww-space-2: 2px;    --ww-space-4: 4px;    --ww-space-8: 8px;
  --ww-space-12: 12px;  --ww-space-16: 16px;  --ww-space-24: 24px;
  --ww-space-32: 32px;  --ww-space-48: 48px;  --ww-space-64: 64px;
  --ww-space-96: 96px;

  /* geometry — honoured by components, never enforced by a global override */
  --ww-radius: 0px;
  --ww-radius-micro: 2px;

  /* type scale — every line-height lands on a multiple of 4 */
  --ww-size-11: 0.688rem; --ww-lh-11: 16px;
  --ww-size-12: 0.750rem; --ww-lh-12: 16px;
  --ww-size-13: 0.813rem; --ww-lh-13: 20px;
  --ww-size-14: 0.875rem; --ww-lh-14: 20px;
  --ww-size-16: 1.000rem; --ww-lh-16: 24px;
  --ww-size-18: 1.125rem; --ww-lh-18: 28px;
  --ww-size-20: 1.250rem; --ww-lh-20: 24px;
  --ww-size-24: 1.500rem; --ww-lh-24: 28px;
  --ww-size-30: 1.875rem; --ww-lh-30: 36px;
  --ww-size-38: 2.375rem; --ww-lh-38: 44px;
  --ww-size-48: 3.000rem; --ww-lh-48: 52px;
  --ww-size-60: 3.750rem; --ww-lh-60: 64px;

  /* motion — decisive, no overshoot */
  --ww-motion-fast: 120ms;
  --ww-motion-base: 200ms;
  --ww-motion-ease: cubic-bezier(0.2, 0, 0.2, 1);

  --ww-measure: 62ch;

  /* material lighting for debossed surfaces — shadow above, highlight below */
  --ww-emboss-shadow: rgba(0, 0, 0, 0.10);
  --ww-emboss-light: rgba(255, 255, 255, 0.75);

  --ww-grain: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ──────────────────────────────────────────────────────────────────────
   SKIN: BALSAM — light. Winter lodge, repaired against design-study/03.
   ────────────────────────────────────────────────────────────────────── */
:root {
  --ww-canvas: #F5F3EB;
  --ww-surface: #FCFAF5;
  --ww-raised: #FFFFFF;
  --ww-sunken: #EAE5D7;

  --ww-ink: #141C16;
  --ww-ink-secondary: #48544B;
  --ww-ink-muted: #5C665E;

  --ww-primary: #1C3829;      --ww-on-primary: #FFFFFF;
  --ww-primary-text: #1C3829;
  --ww-accent: #6C1E2D;       --ww-on-accent: #FFFFFF;
  --ww-support: #C6821C;      --ww-on-support: #2E1D00;
  --ww-signal: #B52222;       --ww-on-signal: #FFFFFF;

  --ww-rule-hairline: rgba(20, 28, 22, 0.12);   /* decorative — exempt */
  --ww-rule-boundary: rgba(20, 28, 22, 0.49);   /* functional — meets 3:1 */

  --ww-grain-opacity: 0.035;
  --ww-plate-opacity: 0.38;

  --ww-font-display: 'Cormorant Garamond', Garamond, Georgia, 'Times New Roman', serif;
  --ww-font-ui: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
  --ww-font-mono: 'JetBrains Mono', ui-monospace, Consolas, 'Courier New', monospace;
  --ww-display-tracking: -0.015em;
}

/* ── Balsam, dark. Cordovan and Hearth invert their roles here: Hearth
      becomes text-capable, Cordovan becomes fill-only. ────────────────── */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --ww-canvas: #0A120E;
    --ww-surface: #111E17;
    --ww-raised: #1A2C23;
    --ww-sunken: #060B08;

    --ww-ink: #F0F6F2;
    --ww-ink-secondary: #9EB5A6;
    --ww-ink-muted: #7B9584;

    --ww-primary: #3B7252;      --ww-on-primary: #FFFFFF;
    --ww-primary-text: #52996F;
    --ww-accent: #9E2D43;       --ww-on-accent: #FFFFFF;
    --ww-support: #E5A134;      --ww-on-support: #141C16;
    --ww-signal: #F04545;       --ww-on-signal: #141C16;

    --ww-rule-hairline: rgba(158, 181, 166, 0.12);
    --ww-rule-boundary: rgba(240, 246, 242, 0.36);

    --ww-emboss-shadow: rgba(0, 0, 0, 0.45);
    --ww-emboss-light: rgba(255, 255, 255, 0.055);

    --ww-plate-opacity: 0.42;
  }
}

:root[data-theme="dark"] {
  --ww-canvas: #0A120E;
  --ww-surface: #111E17;
  --ww-raised: #1A2C23;
  --ww-sunken: #060B08;

  --ww-ink: #F0F6F2;
  --ww-ink-secondary: #9EB5A6;
  --ww-ink-muted: #7B9584;

  --ww-primary: #3B7252;      --ww-on-primary: #FFFFFF;
  --ww-primary-text: #52996F;
  --ww-accent: #9E2D43;       --ww-on-accent: #FFFFFF;
  --ww-support: #E5A134;      --ww-on-support: #141C16;
  --ww-signal: #F04545;       --ww-on-signal: #141C16;

  --ww-rule-hairline: rgba(158, 181, 166, 0.12);
  --ww-rule-boundary: rgba(240, 246, 242, 0.36);

  --ww-emboss-shadow: rgba(0, 0, 0, 0.45);
  --ww-emboss-light: rgba(255, 255, 255, 0.055);

  --ww-plate-opacity: 0.42;
}

/* The safe plate opacity depends on which pixels land under the title,
   which depends on the crop, which changes at the breakpoint. Above 600px
   the title sits over dark forest and the plate carries 0.62 (title at
   6.9:1). The mobile crop shows the whole frame, putting bright sky behind
   the title, where the same value measures about 3.1:1 — so dark stays at
   0.42 there. Two values, both measured. Light needs only one: its crop
   never lifts the composite far enough to matter. */
@media (min-width: 601px) and (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) { --ww-plate-opacity: 0.62; }
}
@media (min-width: 601px) {
  :root[data-theme="dark"] { --ww-plate-opacity: 0.62; }
}

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

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

body {
  margin: 0;
  min-height: 100vh;
  background: var(--ww-canvas);
  color: var(--ww-ink);
  font-family: var(--ww-font-ui);
  font-size: var(--ww-size-16);
  line-height: var(--ww-lh-16);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  display: flex;
  flex-direction: column;
}

/* Grain lives on the canvas, at the skin's intensity, and never prints. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  background-image: var(--ww-grain);
  opacity: var(--ww-grain-opacity);
  pointer-events: none;
  z-index: 9999;
}

@media (prefers-reduced-transparency: reduce) {
  body::before { display: none; }
}

h1, h2 {
  margin: 0;
  font-family: var(--ww-font-display);
  font-weight: 600;
  letter-spacing: var(--ww-display-tracking);
  text-wrap: balance;
}

p { margin: 0; }

a {
  color: var(--ww-primary-text);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--ww-rule-boundary);
  transition: text-decoration-color var(--ww-motion-fast) var(--ww-motion-ease);
}

a:hover { text-decoration-color: currentColor; }

:where(a, button, [tabindex]):focus-visible {
  outline: 2px solid var(--ww-primary-text);
  outline-offset: 2px;
}

/* The one legitimate !important in the system: it has to defeat any
   component-level transition. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition-duration: 0ms !important;
    animation: none !important;
  }
}

/* ──────────────────────────────────────────────────────────────────────
   LABEL — the interface face carries every heading below the 20px floor
   ────────────────────────────────────────────────────────────────────── */
.label {
  font-family: var(--ww-font-ui);
  font-size: var(--ww-size-11);
  line-height: var(--ww-lh-11);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ww-ink-muted);
}

.mono {
  font-family: var(--ww-font-mono);
  font-variant-numeric: tabular-nums;
}

/* ──────────────────────────────────────────────────────────────────────
   RULES — hierarchy bought with a second line, not with darker ink
   ────────────────────────────────────────────────────────────────────── */
.rule-double {
  height: 3px;
  border-top: 1px solid var(--ww-rule-hairline);
  border-bottom: 1px solid var(--ww-rule-hairline);
}

.rule-single {
  height: 1px;
  background: var(--ww-rule-hairline);
}

/* ──────────────────────────────────────────────────────────────────────
   LAYOUT
   ────────────────────────────────────────────────────────────────────── */
/* The column. Everything that is not the plate sits inside this measure. */
.measure {
  width: 100%;
  max-width: 720px;
  margin: 0 auto;
  padding-inline: var(--ww-space-24);
}

.page {
  flex: 1 0 auto;
  width: 100%;
  max-width: 720px;
  margin: 0 auto;
  padding: var(--ww-space-48) var(--ww-space-24);
  display: flex;
  flex-direction: column;
  gap: var(--ww-space-48);
}

/* ── Hero — the plate, with the title set over it ────────────────────
      The image is held at --ww-plate-opacity against the canvas rather
      than covered by a scrim, so the paper shows through the photograph
      the way a ghosted plate does. The opacity is not a taste value: it
      is the largest one at which ink still clears 4.5:1 over the darkest
      part of the frame, measured, not guessed. Raising it breaks the
      type. See the Design check in PR #5. ──────────────────────────── */
.hero {
  border-bottom: 1px solid var(--ww-rule-hairline);
}

.hero-frame {
  display: grid;
  background: var(--ww-canvas);
  border-bottom: 1px solid var(--ww-rule-hairline);
}

.hero-frame > picture,
.hero-text {
  grid-area: 1 / 1;
  min-width: 0;
}

.hero-frame img {
  display: block;
  width: 100%;
  height: clamp(420px, 64vh, 660px);
  object-fit: cover;
  /* 70% shows the field and the full treeline rather than sky alone. */
  object-position: 50% 70%;
  opacity: var(--ww-plate-opacity);
}

.hero-text {
  align-self: end;
  padding-block: var(--ww-space-48);
}

.hero-text .measure {
  display: flex;
  flex-direction: column;
  gap: var(--ww-space-16);
}

.name {
  font-size: var(--ww-size-60);
  line-height: var(--ww-lh-60);
}

/* Full ink, not secondary. Over the plate, --ww-ink-secondary measures
   3.5:1 against the darkest composite and fails. Size carries the
   hierarchy here instead of value. */
.subtitle {
  font-size: var(--ww-size-18);
  line-height: var(--ww-lh-18);
  color: var(--ww-ink);
  max-width: var(--ww-measure);
  display: flex;
  flex-wrap: wrap;
  gap: var(--ww-space-8);
}

.subtitle-links {
  display: inline-flex;
  flex-wrap: wrap;
  gap: var(--ww-space-8);
  font-family: var(--ww-font-mono);
  font-size: var(--ww-size-14);
  line-height: var(--ww-lh-18);
}

/* The ampersand inherits --ww-ink. Muted ink measured 1.97:1 over the
   plate's darkest composite — it is small, decorative text, but it is
   still text a sighted reader has to see. */

/* Over the plate, the accent cannot carry a link. Measured against the
   composite under the type, --ww-primary-text fails at every opacity in
   dark mode — it only holds 5.57:1 on bare canvas, so any lightening from
   the photograph breaks it. The underline and the mono face carry the
   affordance instead, which is the system's rule anyway: colour is never
   the sole carrier. */
.hero a {
  color: var(--ww-ink);
  text-decoration-color: currentColor;
}

.hero :where(a):focus-visible { outline-color: var(--ww-ink); }

.status-line .sep { color: var(--ww-rule-boundary); }

/* ── Plate credit — the photograph's caption, in the column beneath the
      full-bleed frame. The hero is a sibling of .page rather than a child,
      so it spans the viewport without a 100vw trick and never provokes a
      horizontal scrollbar. ─────────────────────────────────────────────── */
.plate-caption {
  margin: 0;
  padding-block: var(--ww-space-8);
}

.plate-caption .measure {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--ww-space-8);
  font-size: var(--ww-size-11);
  line-height: var(--ww-lh-11);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ww-ink-muted);
}

/* ── Status — a sunken well: darker surface, inset hairline, mono content ─ */
.status {
  background: var(--ww-sunken);
  border: 1px solid var(--ww-rule-hairline);
  border-left: 3px solid var(--ww-accent);
  box-shadow: inset 0 1px 0 var(--ww-emboss-shadow),
              inset 0 -1px 0 var(--ww-emboss-light);
  padding: var(--ww-space-16) var(--ww-space-24);
  display: flex;
  flex-direction: column;
  gap: var(--ww-space-8);
}

.status-line {
  font-family: var(--ww-font-mono);
  font-size: var(--ww-size-12);
  line-height: var(--ww-lh-12);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ww-ink-muted);
  display: flex;
  flex-wrap: wrap;
  gap: var(--ww-space-8);
}

.status-line b {
  color: var(--ww-ink);
  font-weight: 600;
}

.status-copy {
  font-size: var(--ww-size-14);
  line-height: var(--ww-lh-14);
  color: var(--ww-ink-secondary);
  max-width: var(--ww-measure);
}

/* ── Contact ────────────────────────────────────────────────────────── */
.contact {
  display: flex;
  flex-direction: column;
  gap: var(--ww-space-12);
}

.contact-list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
}

.contact-list li {
  display: grid;
  grid-template-columns: 88px minmax(0, 1fr);
  gap: var(--ww-space-16);
  align-items: baseline;
  padding: var(--ww-space-12) 0;
  border-bottom: 1px solid var(--ww-rule-hairline);
}

.contact-list li:first-child { border-top: 1px solid var(--ww-rule-hairline); }

.contact-key {
  font-family: var(--ww-font-mono);
  font-size: var(--ww-size-11);
  line-height: var(--ww-lh-11);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ww-ink-muted);
}

.contact-value {
  font-size: var(--ww-size-14);
  line-height: var(--ww-lh-14);
  min-width: 0;
  overflow-wrap: break-word;
}

/* ── Footer ─────────────────────────────────────────────────────────── */
.site-footer {
  flex: none;
  border-top: 1px solid var(--ww-rule-hairline);
  margin-top: auto;
}

.footer-inner {
  max-width: 720px;
  margin: 0 auto;
  padding: var(--ww-space-24);
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--ww-space-8);
  font-family: var(--ww-font-mono);
  font-size: var(--ww-size-11);
  line-height: var(--ww-lh-11);
  letter-spacing: 0.06em;
  color: var(--ww-ink-muted);
}

/* ──────────────────────────────────────────────────────────────────────
   NARROW — the display face still clears its 20px floor by a wide margin
   ────────────────────────────────────────────────────────────────────── */
@media (max-width: 600px) {
  .measure { padding-inline: var(--ww-space-16); }

  .page {
    padding: var(--ww-space-32) var(--ww-space-16);
    gap: var(--ww-space-32);
  }

  .hero-text { padding-block: var(--ww-space-32); }

  .hero-frame img { height: clamp(400px, 62vh, 520px); }

  .name {
    font-size: var(--ww-size-38);
    line-height: var(--ww-lh-38);
  }

  .status { padding: var(--ww-space-16); }

  .contact-list li {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--ww-space-4);
  }

  .footer-inner { padding: var(--ww-space-16); }
}

/* ──────────────────────────────────────────────────────────────────────
   PRINT — a first-class mode. Grain off, true black, solid rules.
   ────────────────────────────────────────────────────────────────────── */
@media print {
  :root {
    --ww-canvas: #FFFFFF;
    --ww-surface: #FFFFFF;
    --ww-sunken: #FFFFFF;
    --ww-ink: #000000;
    --ww-ink-secondary: #333333;
    --ww-ink-muted: #555555;
    --ww-primary-text: #000000;
    --ww-accent: #000000;
    --ww-rule-hairline: #999999;
    --ww-rule-boundary: #444444;
    --ww-grain-opacity: 0;
  }

  body::before { display: none; }

  /* A dark full-bleed photograph is a lot of toner for no information.
     The caption stays; it says what the plate showed. */
  .hero-frame img { display: none; }
  .hero-frame, .hero { border: 0; }
  .hero-text { padding-block: 0 var(--ww-space-24); }

  .status {
    box-shadow: none;
    border: 1px solid #999999;
    border-left: 3px solid #000000;
  }

  .page { padding: 0; max-width: none; }

  a { text-decoration: none; }
}
