/*
  spicy-or-allergy
  ----------------
  Direction: a specimen catalogue, not a food blog. Names and numbers are set
  in mono, like tags on a collection; prose is set in a plain humanist sans.

  The accent was originally the violet of a nerve signal, on the argument that
  the subject is a sensation rather than heat. It lost to legibility: violet
  against this pale green ground is too low-contrast at the size the stipple
  dots are drawn, and the dots are the one thing on the page that has to be
  readable at a glance. A strong red carries much further at 2px, and it is
  one accent for the whole site rather than red dots beside violet everything
  else.

  --dot is deliberately darker than a hairline too: the unfilled dots are the
  denominator, and a field you can't see makes the filled ones meaningless.

  No web fonts: the type is doing a structural job rather than a decorative
  one, and a self-hosted face would be weight and a licence question for a
  page that is mostly a list.
*/

:root {
  --paper:      #eef2eb;
  --paper-sunk: #e3e9df;
  --card:       #f7faf5;
  --ink:        #16241c;
  --ink-soft:   #5d6d62;
  --rule:       #ccd7c8;
  --hot:        #d40016;  /* tingling */
  --calm:       #24714b;  /* not tingling */
  --dot:        #aabaa6;

  --mono: ui-monospace, "SF Mono", "JetBrains Mono", "IBM Plex Mono", Menlo,
          Consolas, monospace;
  --sans: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial,
          sans-serif;

  --gap: clamp(1rem, 3vw, 2rem);
  --max: 68rem;
}

@media (prefers-color-scheme: dark) {
  :root {
    --paper:      #0e1410;
    --paper-sunk: #141c16;
    --card:       #17201a;
    --ink:        #e4ece1;
    --ink-soft:   #93a397;
    --rule:       #2a3830;
    /* Lighter and less saturated than the light-mode red: a fully saturated
       red on a near-black ground vibrates and is hard to focus on. */
    --hot:        #ff4d4d;
    --calm:       #58bb8b;
    --dot:        #47594d;
  }
}

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

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

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 1rem;
  line-height: 1.55;
}

a { color: inherit; }

/* Present for screen readers, absent from the layout. */
.vh {
  position: absolute; width: 1px; height: 1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}

.skip {
  position: absolute;
  left: -9999px;
}
.skip:focus {
  left: 0.5rem; top: 0.5rem;
  z-index: 10;
  background: var(--hot);
  color: #fff;
  padding: 0.5rem 0.75rem;
}

/* Keyboard focus is visible everywhere, and never removed. */
:focus-visible {
  outline: 2px solid var(--hot);
  outline-offset: 2px;
}

/* --- masthead ---------------------------------------------------------- */

.masthead {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem 1.25rem;
  padding: 1rem var(--gap);
  max-width: var(--max);
  margin: 0 auto;
  border-bottom: 1px solid var(--rule);
}

.wordmark {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  text-decoration: none;
  margin-right: auto;
}

.mark { width: 1.4rem; height: 1.4rem; color: var(--dot); flex: none; }
.mark__hot { fill: var(--hot); }

.wordmark__text {
  font-family: var(--mono);
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.wordmark__or { color: var(--ink-soft); padding: 0 0.35em; }

.find { display: flex; align-items: center; gap: 0.4rem; }
.find__label {
  /* Present for screen readers; the placeholder carries it visually. */
  position: absolute; width: 1px; height: 1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}
.find__input {
  font: inherit;
  font-size: 0.9rem;
  padding: 0.4rem 0.6rem;
  min-width: 12rem;
  color: var(--ink);
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: 2px;
}
.find__go {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 0.45rem 0.7rem;
  color: var(--paper);
  background: var(--ink);
  border: 0;
  border-radius: 2px;
  cursor: pointer;
}

.masthead__about {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

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

/* --- the thesis -------------------------------------------------------- */

/* Below the card now, so it no longer needs to open the page. */
.thesis { padding: 1rem 0 1.5rem; max-width: 34ch; }

.thesis__eyebrow {
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin: 0 0 1rem;
}

.thesis__line {
  font-size: clamp(2.4rem, 8vw, 4.2rem);
  line-height: 0.98;
  letter-spacing: -0.03em;
  font-weight: 700;
  margin: 0;
  text-wrap: balance;
}

.thesis__body {
  max-width: 46ch;
  margin: 1.25rem 0 0;
  color: var(--ink-soft);
}

.thesis__stats {
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  color: var(--ink-soft);
  margin: 1rem 0 0;
}
.thesis__stats strong { color: var(--ink); }

/* --- the quick-vote pitch ---------------------------------------------- */

/*
  One centred column. The picture, the pair of buttons and the two links are
  all `--pitch-col` wide and centred on it, so the buttons line up with the
  edges of the photograph rather than merely sitting near it — the alignment
  is what makes it read as one object to act on instead of three stacked
  controls.
*/
.pitch {
  --pitch-col: 26rem;
  /* Tight to the masthead: this is the first thing on the page and every
     pixel above it is a pixel of scrolling after a vote. */
  margin: 1.25rem 0 2.5rem;
  padding: 1.5rem 1.5rem 1.25rem;
  text-align: center;
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: 3px;
}

.pitch__ack,
.pitch__img,
.pitch__form,
.pitch__skip {
  max-width: var(--pitch-col);
  margin-left: auto;
  margin-right: auto;
}

.pitch__ack {
  margin-bottom: 1.5rem;
  padding-bottom: 1.1rem;
  border-bottom: 1px solid var(--rule);
  font-size: 0.85rem;
  color: var(--ink-soft);
}

.pitch__ask {
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin: 0 0 0.4rem;
}

.pitch__name {
  font-size: clamp(1.9rem, 5vw, 2.8rem);
  line-height: 1.05;
  letter-spacing: -0.03em;
  margin: 0 0 1.25rem;
}
.pitch__name a { text-decoration: none; }
.pitch__name a:hover { text-decoration: underline; }

.pitch__img {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: 2px;
  margin-bottom: 1.25rem;
  background: var(--paper-sunk);
}
/* Much shorter than a real picture. It exists to hold the column together,
   not to stand in for a photograph — at 4:3 an empty box is the largest thing
   on the page, and most of the catalogue has no image yet, so that emptiness
   would be the usual state of the front page rather than the exception. */
.pitch__img--none {
  display: grid;
  place-items: center;
  aspect-ratio: auto;
  min-height: 5.5rem;
  border: 1px dashed var(--rule);
  background: transparent;
}
.pitch__initial {
  font-family: var(--mono);
  font-size: 2rem;
  color: var(--rule);
}

.pitch__form {
  display: grid;
  /* 11rem, not 12: the two buttons must stack on a phone, where the column is
     about 19rem inside the card's padding. */
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: 0.75rem;
}

/* The shared .choice is left-aligned for the fruit page's form. In here it is
   part of a centred column. */
.pitch .choice {
  align-items: center;
  text-align: center;
}

/* Not an answer, so it doesn't look like one: full width beneath the two
   choices, and quiet enough that it can't be mistaken for a third option. */
.nope {
  grid-column: 1 / -1;
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-soft);
  background: none;
  border: 0;
  padding: 0.5rem 0.25rem 0.15rem;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.nope:hover { color: var(--ink); }

.pitch--done { text-align: center; }
.pitch__done-body {
  max-width: 42ch;
  margin: 0.75rem auto 0;
  color: var(--ink-soft);
}

.pitch__skip {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.4rem 1.5rem;
  margin-top: 1.25rem;
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.pitch__another { color: var(--ink); }
.pitch__see { color: var(--ink-soft); }

/* --- legend ------------------------------------------------------------ */

.legend {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1.5rem;
  padding: 1rem 0;
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
.legend__sample { display: flex; align-items: center; gap: 0.5rem; }
.legend__sample .stipple { width: 4.5rem; }

/* --- sorter ------------------------------------------------------------ */

.sorter {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.4rem 1rem;
  padding: 1.25rem 0;
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.sorter__label { color: var(--ink-soft); }
.sorter__opt { color: var(--ink-soft); text-decoration: none; }
.sorter__opt:hover { color: var(--ink); }
.sorter__opt.is-on {
  color: var(--ink);
  box-shadow: inset 0 -2px 0 var(--hot);
}
.sorter__note {
  max-width: 54ch;
  margin: -0.4rem 0 1.25rem;
  font-size: 0.78rem;
  color: var(--ink-soft);
}

/* --- the catalogue ----------------------------------------------------- */

.catalogue { list-style: none; margin: 0; padding: 0; }

.specimen { border-top: 1px solid var(--rule); }
.specimen:last-child { border-bottom: 1px solid var(--rule); }

.specimen__link {
  display: grid;
  grid-template-columns: 3rem minmax(7rem, 1fr) minmax(6rem, 12rem) auto;
  align-items: center;
  gap: 0.75rem 1.25rem;
  padding: 0.7rem 0.5rem;
  text-decoration: none;
  transition: background 120ms ease;
}
.specimen__link:hover { background: var(--paper-sunk); }

.thumb {
  width: 3rem; height: 3rem;
  border-radius: 2px;
  object-fit: cover;
  background: var(--paper-sunk);
}
.thumb--none {
  display: grid;
  place-items: center;
  font-family: var(--mono);
  font-size: 1.1rem;
  color: var(--ink-soft);
  border: 1px dashed var(--rule);
}

.specimen__name {
  font-family: var(--mono);
  font-size: 0.95rem;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.specimen--mine .specimen__name { box-shadow: inset 0 -2px 0 var(--hot); }

.specimen__mine {
  grid-column: 2 / -1;
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--hot);
}
/* An opt-out is not an opinion, so it doesn't get the accent colour. */
.specimen__mine--untried { color: var(--ink-soft); }

/* --- the stipple field: the signature ---------------------------------- */

/* Height follows width so the dots stay circular; the width is what's
   constrained. A dot squashed into a dash reads as a tally mark. */
/* A hundred dots need more room than forty did before they read as a field
   rather than a smear. */
.stipple { display: block; width: 100%; height: auto; }
.stipple--row { max-width: 14rem; }
.stipple--big { max-width: 30rem; margin: 0.75rem 0 1.5rem; }

/* The shared field is <use>d, so its colour has to arrive by inheritance —
   a selector cannot reach inside a <use> shadow tree. The filled dots are
   real children of each svg and are styled directly. */
.stipple use { fill: var(--dot); }
.stipple > circle { fill: var(--hot); }

.stipple-defs { position: absolute; width: 0; height: 0; overflow: hidden; }

/* Nothing to draw yet: a hatch, which reads as "unrecorded" rather than as
   an empty measurement. */
.stipple--unknown {
  display: block;
  width: 100%;
  aspect-ratio: 100 / 25;
  border-top: 1px dashed var(--rule);
  border-bottom: 1px dashed var(--rule);
  background: repeating-linear-gradient(
    -45deg, transparent 0 5px, var(--rule) 5px 6px
  );
  opacity: 0.5;
}

/* --- the result line --------------------------------------------------- */

.result {
  display: flex;
  flex-direction: column;
  font-family: var(--mono);
  line-height: 1.25;
  text-align: right;
  min-width: 8rem;
}
.result__pct { font-size: 1.35rem; font-weight: 600; }
.result__sign { font-size: 0.8em; color: var(--ink-soft); }
.result__band {
  font-size: 0.66rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
.result__n { font-size: 0.66rem; color: var(--ink-soft); opacity: 0.8; }
.result--unknown .result__band { color: var(--ink-soft); }

/* --- fruit page -------------------------------------------------------- */

.specimen-page {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 0 clamp(1.5rem, 5vw, 3.5rem);
  align-items: start;
  padding-top: 2rem;
}
.specimen-page__head { grid-column: 1 / -1; }

.crumb {
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin: 0 0 0.75rem;
}

.specimen-page__name {
  font-size: clamp(2.2rem, 7vw, 3.4rem);
  line-height: 1;
  letter-spacing: -0.03em;
  margin: 0;
}
.specimen-page__desc {
  max-width: 44ch;
  color: var(--ink-soft);
  margin: 0.75rem 0 2rem;
}

.plate { margin: 0; }
.plate__img {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: 2px;
  background: var(--paper-sunk);
}
.plate__empty {
  display: grid;
  place-items: center;
  aspect-ratio: 4 / 3;
  border: 1px dashed var(--rule);
  border-radius: 2px;
  background: var(--paper-sunk);
}
.plate__initial { font-family: var(--mono); font-size: 3rem; color: var(--rule); }
.plate__pending {
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin: 0.5rem 0 0;
}
.plate__credit {
  font-family: var(--mono);
  font-size: 0.66rem;
  color: var(--ink-soft);
  margin-top: 0.5rem;
}

.reading__h, .ballot__h {
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-soft);
  font-weight: 600;
  margin: 0 0 0.5rem;
}

.reading__headline {
  font-size: 1.3rem;
  line-height: 1.3;
  margin: 0;
  text-wrap: pretty;
}
.reading__pct {
  font-family: var(--mono);
  font-size: 2.2rem;
  letter-spacing: -0.03em;
  color: var(--hot);
  /* The mono face runs right up against the following word; the collapsed
     newline between them isn't enough of a gap at this size. */
  margin-right: 0.25rem;
}
.reading__headline--quiet { color: var(--ink-soft); font-size: 1.05rem; }
.reading__sub { color: var(--ink-soft); margin: 0.4rem 0 0; }

.tally {
  display: flex;
  gap: 2rem;
  margin: 1.5rem 0 0;
  padding-top: 1rem;
  border-top: 1px solid var(--rule);
}
.tally__cell { min-width: 6rem; }
.tally__cell dt {
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
.tally__cell dd {
  font-family: var(--mono);
  font-size: 1.4rem;
  margin: 0.15rem 0 0;
}

/* --- divergence: the one moment this speaks to an individual ----------- */

.divergence {
  grid-column: 1 / -1;
  margin: 2rem 0 0;
  padding: 1.25rem 1.25rem 1.25rem 1.5rem;
  background: var(--card);
  border-left: 3px solid var(--hot);
  border-radius: 0 2px 2px 0;
}
.divergence__lede {
  font-family: var(--mono);
  font-size: 0.95rem;
  font-weight: 600;
  margin: 0 0 0.5rem;
}
.divergence__body { margin: 0; max-width: 58ch; color: var(--ink-soft); }

/* --- ballot ------------------------------------------------------------ */

.ballot { grid-column: 1 / -1; margin-top: 2.5rem; }

.ballot__form {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: 0.75rem;
  max-width: 34rem;
}

.choice {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.15rem;
  padding: 0.9rem 1rem;
  text-align: left;
  cursor: pointer;
  color: var(--ink);
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: 2px;
  transition: transform 90ms ease, border-color 120ms ease;
}
.choice:hover { border-color: currentColor; }
.choice:active { transform: translateY(1px); }

.choice__word {
  font-family: var(--mono);
  font-size: 1rem;
  font-weight: 600;
}
.choice__gloss { font-size: 0.8rem; color: var(--ink-soft); }

.choice--spicy.is-chosen {
  border-color: var(--hot);
  box-shadow: inset 0 0 0 1px var(--hot);
}
.choice--spicy.is-chosen .choice__word { color: var(--hot); }
.choice--calm.is-chosen {
  border-color: var(--calm);
  box-shadow: inset 0 0 0 1px var(--calm);
}
.choice--calm.is-chosen .choice__word { color: var(--calm); }

.ballot__after {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.3rem;
  font-size: 0.85rem;
  color: var(--ink-soft);
  margin: 0.9rem 0 0;
}
.ballot__next {
  margin: 1.5rem 0 0;
  padding-top: 1.25rem;
  border-top: 1px solid var(--rule);
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.inline-form { display: inline; }
.linkbtn {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  text-decoration: underline;
}

/* --- prose, results, empties ------------------------------------------ */

.prose { max-width: 62ch; padding-top: 2.5rem; }
.prose__h1 {
  font-size: clamp(2rem, 6vw, 3rem);
  line-height: 1.05;
  letter-spacing: -0.03em;
  margin: 0 0 1rem;
}
.prose__lede { font-size: 1.15rem; }
.prose h2 {
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin: 2.25rem 0 0.5rem;
}

.results-head { padding: 2.5rem 0 1rem; }
.results-head__h {
  font-size: clamp(1.8rem, 5vw, 2.6rem);
  letter-spacing: -0.02em;
  margin: 0;
}
.results-head__count {
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin: 0.4rem 0 0;
}

.nothing { padding: 3rem 0; max-width: 50ch; }
.nothing__lede { font-size: 1.4rem; margin: 0 0 0.5rem; letter-spacing: -0.01em; }
.nothing__body { color: var(--ink-soft); margin: 0 0 1.5rem; }
.nothing__all {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

/* --- footer ------------------------------------------------------------ */

.footer {
  max-width: var(--max);
  margin: 0 auto;
  padding: 1.5rem var(--gap) 3rem;
  border-top: 1px solid var(--rule);
}
.footer__note {
  max-width: 60ch;
  margin: 0;
  font-size: 0.8rem;
  color: var(--ink-soft);
}

/* --- narrow screens ---------------------------------------------------- */

@media (max-width: 46rem) {
  /* The catalogue row becomes a card. Sideways scrolling on a list of fruit
     would be indefensible (PRD S4). */
  .specimen__link {
    grid-template-columns: 3rem 1fr;
    grid-template-areas:
      "thumb name"
      "stip  stip"
      "res   res"
      "mine  mine";
    padding: 0.9rem 0.25rem;
  }
  .thumb { grid-area: thumb; }
  .specimen__name { grid-area: name; }
  .stipple--row { grid-area: stip; }
  .stipple--unknown.stipple--row { grid-area: stip; }
  .result { grid-area: res; text-align: left; align-items: flex-start; }
  .specimen__mine { grid-area: mine; }
  /* Keep the card short enough that scanning sixty of them is still scanning.
     The drawing is comparative, so it only has to be big enough to compare. */
  .stipple--row { max-width: 9rem; }

  /* The whole vote card has to clear the fold on a small phone, or every vote
     costs a scroll — the thing this layout exists to prevent. The picture is
     the biggest thing in it, so it yields first, and it yields against the
     viewport rather than a fixed size so it adapts to whatever the phone is. */
  .pitch { padding: 1.25rem 1rem 1rem; }
  .pitch__img { max-height: 30vh; }
  .pitch__img--none { min-height: 4rem; }
  .pitch__name { margin-bottom: 1rem; }

  .specimen-page { grid-template-columns: 1fr; }
  .plate { margin-bottom: 2rem; }

  .masthead { gap: 0.6rem; }
  /* `flex: 1 1 100%`, not `width: 100%`: as a width, the row's gap is added
     on top of a full-width item and the masthead becomes wider than the
     viewport — which scrolls the whole page sideways, not just the header. */
  .find { order: 3; flex: 1 1 100%; min-width: 0; }
  .find__input { flex: 1 1 auto; min-width: 0; }
}

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}
