/* R. Andy Marra, personal site
   Bone paper and ink, system type stacks, nothing downloaded.
   See DESIGN.md before changing any of it. */

/* Light only, and deliberately so. Andy's call 2026-08-30: the page needs to be white
   rather than black, and it needs to be accessible.
   Every text token below was solved against the ground for WCAG AA (4.5:1), not eyeballed.
   Measured ratios are in the comments. Do not lower an alpha without re-measuring. */
:root {
  --ground:  #ece9df;                     /* warm white. --ink on it is 14.05:1 */
  --panel:   #e4e0d4;
  --ink:     #1c1c1a;
  --mut:     rgba(28, 28, 26, 0.69);      /* 5.53:1  body copy, AA */
  --faint:   rgba(28, 28, 26, 0.635);     /* 4.67:1  small labels and metadata, AA */
  --edge:    rgba(28, 28, 26, 0.485);     /* 3.01:1  UI component borders, AA non-text */
  --line:    rgba(28, 28, 26, 0.20);      /* 1.50:1  decorative rules only, never a boundary */
  --hover:   rgba(28, 28, 26, 0.055);
  --chip:    rgba(28, 28, 26, 0.075);   /* inline link ground, konzok-style */
  --chip-on: rgba(28, 28, 26, 0.155);
  --grain-o: 0.030;

  /* System stacks only. Nothing is downloaded, so there is no third-party request and
     no render-blocking font fetch. The cost is that the page looks slightly different
     per platform: Palatino Linotype and Segoe UI on Windows, Iowan Old Style and SF Pro
     on a Mac. See DESIGN.md section 3. */
  --sans:  system-ui, -apple-system, "Segoe UI Variable Text", "Segoe UI", "Helvetica Neue", sans-serif;
  --serif: ui-serif, "Iowan Old Style", "Palatino Linotype", Palatino, "Book Antiqua", Georgia, serif;
  --mono:  ui-monospace, SFMono-Regular, "Cascadia Mono", "Segoe UI Mono", Consolas, monospace;

  --measure: 44rem;
  /* One column width for every block in the hero, so the paragraphs, the portrait and
     the question all share a right edge. Before this they ended at four different
     places and the page read as ragged. */
  --col: 36rem;
}

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.65;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* Paper tooth. Fixed so it does not scroll with content. */
.grain {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 1;
  opacity: var(--grain-o);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--ground);
  color: var(--ink);
  padding: 0.6rem 1rem;
  z-index: 10;
}
.skip:focus { left: 0.75rem; top: 0.75rem; }

.shell {
  position: relative;
  z-index: 2;
  max-width: var(--measure);
  margin: 0 auto;
  padding: 2rem 1.25rem 3.5rem;
}

@media (min-width: 40rem) {
  .shell { padding: 3.5rem 2.5rem 5rem; }
}

/* ---------- masthead ---------- */

.masthead {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  padding-bottom: 3.5rem;
}

.wordmark {
  font-family: var(--sans);
  font-weight: 600;
  font-size: 0.8rem;
  letter-spacing: 0.17em;
  text-transform: uppercase;
  color: var(--ink);
}

.slash {
  color: var(--faint);
  padding: 0 0.12em;
  font-weight: 400;
}

.mast-link {
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  color: var(--faint);
  text-decoration: none;
  transition: color 140ms ease;
}
.mast-link:hover,
.mast-link:focus-visible { color: var(--ink); }
.mast-link:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }

/* ---------- hero ---------- */

.hero { padding-bottom: 0; }

.eyebrow {
  font-family: var(--mono);
  font-weight: 500;
  font-size: 0.67rem;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--faint);
  margin: 0 0 1.5rem;
}

.thesis {
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(2.6rem, 12vw, 4.75rem);
  line-height: 1.06;
  letter-spacing: -0.025em;
  margin: 0 0 1.75rem;
  max-width: 14ch;
  text-wrap: balance;
}

.bio {
  margin: 0;
  max-width: var(--col);
  color: var(--mut);
  font-size: clamp(1.15rem, 3.4vw, 1.45rem);
  line-height: 1.55;
}

/* With the headline gone the first paragraph is the first thing read, so it steps up a
   size and sits at full ink. The h1 survives as screen-reader-only text, because a page
   with no heading at all is a document-outline failure even when it looks fine. */
.lead {
  color: var(--ink);
}

/* Inline links inside the paragraph, borrowed from konzok.com: the entity names sit on
   a faint ground rather than being underlined, so the paragraph stays a paragraph and
   still routes people to the practice and the consulting firm. */
.inline {
  color: var(--ink);
  text-decoration: none;
  background: var(--chip);
  border-radius: 3px;
  padding: 0.1em 0.32em;
  /* Keep a two-word name whole. Wrapped, the chip breaks into two rounded boxes on
     separate lines, which reads as a rendering fault rather than a link. */
  white-space: nowrap;
  transition: background-color 140ms ease;
}
.inline:hover,
.inline:focus-visible { background: var(--chip-on); }

.inline:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }

/* ---------- portrait ---------- */

/* The engraved drawing, flattened onto the page ground by make_hero_image.py so there is
   no white card edge. It replaced the assembling digit grid on 2026-08-30: a hedcut and a
   swarm of ones and zeros were two different ideas competing. See DESIGN.md section 5. */
.portrait {
  width: 100%;
  max-width: var(--col);
  margin: 3rem 0 0;
  line-height: 0;
}

.portrait img {
  display: block;
  width: 100%;
  height: auto;
}

.portrait-cap {
  font-family: var(--mono);
  font-size: 0.63rem;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--faint);
  line-height: 1.6;
  margin: 0.9rem 0 0;
}

/* ---------- blocks ---------- */

.block { padding-bottom: 0; }

/* Projects as prose rather than a ruled list. Andy, 2026-08-31: he keeps reaching for the
   minimalist look, and the rules were the loudest thing left on the page. */
.projects { margin-top: 3rem; }

/* A product name that is not a link. Same weight as a linked chip so the sentence reads
   evenly, but no chip ground and no hover, because nothing here is clickable yet. */
.named { font-weight: 600; color: var(--ink); }

/* The contribution strip, sitting inside the portrait figure so the drawing and its data
   read as one object. Andy's idea, 2026-09-01: "merge this into sort of the bottom of my
   picture so that the images are together somehow." It replaced a full width daily grid
   that sat lower down the page and matched the portrait's width exactly, which made the two
   read as a diptych and cost the portrait its place as the page's single figure.

   Six blocks, one per month, ink weighted by that month's volume. Monthly rather than
   daily: see the note in index.html. */
.contrib {
  margin: 0.5rem 0 0;      /* close enough to the drawing above to belong to it */
}

.contrib img {
  display: block;
  width: 100%;
  height: auto;
}

/* The month labels are gone. They were HTML so the image's scale could not touch their
   size, which was right, but the endpoint now serves a rolling window and any label written
   into the page drifts out of agreement with the picture. A six block sparkline reads well
   enough without them. */

/* The one sentence that carries a number, and it is a floor rather than a total, so it
   cannot go false as the real figure grows. Same register as .portrait-cap. */
.portrait figcaption {
  font-family: var(--mono);
  font-size: 0.63rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--faint);
  line-height: 1.6;
  margin: 0.9rem 0 0;
}

.block-h {
  font-family: var(--mono);
  font-weight: 500;
  font-size: 0.67rem;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--faint);
  margin: 0 0 0.35rem;
}

.rows {
  list-style: none;
  margin: 0;
  padding: 0;
}

.row { border-top: 1px solid var(--line); }
.row:last-child { border-bottom: 1px solid var(--line); }

.row-link {
  display: block;
  padding: 1.05rem 0.75rem 1.05rem 0;
  margin: 0 -0.75rem 0 0;
  text-decoration: none;
  color: inherit;
  transition: background-color 120ms ease;
}

a.row-link:hover,
a.row-link:focus-visible {
  background: var(--hover);
}

a.row-link:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
}

.row-head {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.15rem;
}

.row-name {
  font-weight: 600;
  font-size: 1rem;
  letter-spacing: -0.005em;
}

.row-meta {
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.04em;
  color: var(--faint);
  white-space: normal;
  flex-shrink: 0;
}

.arrow {
  display: inline-block;
  margin-left: 0.4em;
  transition: transform 140ms ease;
}

a.row-link:hover .arrow,
a.row-link:focus-visible .arrow {
  transform: translate(1px, -1px);
}

.row-desc {
  display: block;
  margin-top: 0.2rem;
  color: var(--mut);
  font-size: 0.94rem;
  line-height: 1.55;
  max-width: 52ch;
}

/* Mobile first: the metadata sits under the name, and only moves up onto the same
   baseline once there is width for it. */
@media (min-width: 34rem) {
  .row-head {
    flex-direction: row;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
  }
  .row-meta { white-space: nowrap; }
}

/* ---------- ask ---------- */

/* Borrowed from konzok.com: the contact field is styled as running prose rather than as
   a form, with the question sitting in it as placeholder text and a send affordance at
   the end of the line. This one is backed by a real model too, as of 2026-08-31, and the
   constraints on what it may say are in functions/api/ask.js. See DESIGN.md section 7. */

.ask { padding: 3rem 0 3rem; }

/* No rule under the field. Andy's call, 2026-08-31. Nothing marks it as a field until
   you click into it, so the focus ring below is not optional: it is the only affordance
   a keyboard user gets. */
.ask-form {
  display: flex;
  align-items: center;
  gap: 0.8rem;
  max-width: var(--col);
}

.ask-input {
  flex: 1 1 auto;
  min-width: 0;
  font-family: var(--serif);
  font-size: clamp(1.15rem, 2.6vw, 1.45rem);
  line-height: 1.4;
  color: var(--ink);
  background: transparent;
  border: 0;
  padding: 0.15rem 0;
  appearance: none;
}
.ask-input::placeholder { color: var(--mut); opacity: 1; }
.ask-input:focus { outline: none; }
.ask-input:focus-visible { outline: 2px solid var(--ink); outline-offset: 6px; border-radius: 2px; }

.ask-send {
  flex: 0 0 auto;
  width: 2.1rem;
  height: 2.1rem;
  border-radius: 50%;
  border: 1px solid var(--edge);
  background: transparent;
  color: var(--mut);
  display: grid;
  place-items: center;
  cursor: pointer;
  transition: background-color 140ms ease, color 140ms ease, border-color 140ms ease;
}
.ask-send:hover,
.ask-send:focus-visible {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--ground);
}
.ask-send:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }
.ask-send svg { width: 0.95rem; height: 0.95rem; display: block; }

/* ---------- the answer ---------- */

/* On submit the page gives way and the answer takes its place, the way konzok.com does
   it. The content is hidden rather than removed, so "ask something else" restores it. */
.is-asking .lead,
.is-asking .portrait,
.is-asking .block { display: none; }

.answer:empty { display: none; }
.answer {
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
  padding: 1.5rem 0 0;
  max-width: 56ch;
}

.answer-q {
  margin: 0;
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  color: var(--faint);
  text-transform: uppercase;
}

.answer-a {
  margin: 0;
  font-family: var(--serif);
  font-size: clamp(1.1rem, 3vw, 1.35rem);
  line-height: 1.55;
  color: var(--ink);
  white-space: pre-wrap;
}

/* Gone with the model, 2026-08-31: .answer-dots, .answer-caret and its keyframes, and
   .answer-note. The dots and the caret existed to make a wait look like thinking, and
   there is no wait any more. The note carried "that reply is automated and it is not legal
   advice", which stopped being true when the reply stopped existing. The footer still
   carries the disclaimer that does apply. */

/* The reply path. The big ask input above it carries no border at all, so a filled box
   here would be the loudest thing on the page for the smallest ask on it. A hairline
   under each field is the least that still reads as somewhere to type. */
.answer-contact {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.1rem;
  align-items: baseline;
}

.answer-field {
  flex: 1 1 9rem;
  min-width: 0;
  font-family: var(--sans);
  font-size: 0.92rem;
  color: var(--ink);
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--edge);
  border-radius: 0;
  padding: 0.35rem 0.1rem;
}
.answer-field::placeholder { color: var(--faint); opacity: 1; }
.answer-field:focus { outline: none; border-bottom-color: var(--ink); }
/* Offset 1px rather than 3px. The name field is focused programmatically the moment the
   form appears, and at 3px the ring floated clear of the field and read as an error state
   rather than as "type here". */
.answer-field:focus-visible { outline: 2px solid var(--ink); outline-offset: 1px; }

/* Same shape as "Ask something else" but a full-strength border rather than the hairline.
   The two stack on a narrow screen, and without that difference they read as a pair of
   equal choices when only one of them is the point of the page. */
.answer-send {
  flex: 0 0 auto;
  align-self: center;
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink);
  background: transparent;
  border: 1px solid var(--ink);
  border-radius: 2px;
  padding: 0.45rem 0.7rem;
  cursor: pointer;
}
.answer-send:hover { background: var(--hover); }
.answer-send:disabled { color: var(--faint); cursor: default; }
.answer-send:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }

/* Sentences, so sans and sentence case. The uppercase mono elsewhere on the page is for
   labels and buttons; a whole sentence set in it reads as shouting. */
.answer-sent,
.answer-err {
  margin: 0;
  font-family: var(--sans);
  font-size: 0.82rem;
  line-height: 1.6;
  color: var(--mut);
}
.answer-err { color: var(--ink); }

.answer-again {
  align-self: flex-start;
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink);
  background: transparent;
  border: 1px solid var(--edge);
  border-radius: 2px;
  padding: 0.45rem 0.7rem;
  cursor: pointer;
}
.answer-again:hover { background: var(--hover); }
.answer-again:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }

/* Both buttons measure 29px tall, which clears the 24px WCAG 2.2 AA target minimum but is
   mean on a thumb. Grow them only where the pointer is coarse, so the desktop proportions
   are exactly as drawn. */
@media (pointer: coarse) {
  .answer-send,
  .answer-again { padding-top: 0.68rem; padding-bottom: 0.68rem; }
  .answer-field { padding-top: 0.55rem; padding-bottom: 0.55rem; }
}

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* ---------- footer ---------- */

.foot {
  padding-top: 1.25rem;
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
}

.fine {
  margin: 0;
  max-width: 46ch;
  font-size: 0.83rem;
  line-height: 1.55;
  color: var(--faint);
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
  }
}
