/* ==========================================================================
   Kofineur — components (V1: masthead, atlas index, specimen page)
   Tokens live in application.css / DESIGN.md. No utility framework.
   ========================================================================== */

/* --- Cover (wood-green hero band; used by index + specimen) ------------- */
.cover { background: var(--cover); color: var(--on); }
.cover a { color: inherit; }
.cover a:hover { text-decoration: none; }

/* --- Masthead ---------------------------------------------------------- */
.masthead { display: flex; justify-content: space-between; align-items: baseline; padding: 30px 0; }
.brand { display: flex; align-items: center; gap: 10px; font-weight: 600; font-size: 1.5rem; letter-spacing: .02em; }
.brand svg { color: var(--on-cherry); }
.nav { display: flex; gap: 28px; }
.nav a { color: var(--on-muted); font-variant: small-caps; letter-spacing: .1em; font-weight: 500; font-size: .96rem; }
.nav a.on { color: var(--on); }
.nav a:hover { color: var(--on); }
.edn { color: var(--on); font-size: .86rem; }

/* --- Index intro ------------------------------------------------------- */
.atlas-intro { padding: 36px 0 40px; }
.atlas-intro h1 { font-weight: 300; font-size: 2.6rem; color: var(--on); letter-spacing: -.01em; }
.atlas-intro p { color: var(--on-muted); font-size: 1.1rem; margin-top: 9px; max-width: none; }
.stats { display: flex; gap: 34px; margin-top: 24px; flex-wrap: wrap; }
.stat { display: flex; align-items: baseline; gap: 8px; }
.stat b { font-size: 1.3rem; font-weight: 500; color: var(--on); }
.stat span { font-size: .82rem; color: var(--on-muted); font-variant: small-caps; letter-spacing: .08em; }

/* --- Atlas grid -------------------------------------------------------- */
.atlas { padding-block: 44px 96px; }
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(360px, 1fr)); gap: 22px; }

/* --- Specimen card ----------------------------------------------------- */
.card { background: var(--surface); border-radius: var(--radius-card); padding: 24px 26px 22px; position: relative; display: flex; flex-direction: column; }
.card .stamp { position: absolute; top: 20px; right: 22px; color: var(--ink); opacity: .32; }
.card .stamp .cherry { fill: var(--cherry); stroke: none; opacity: .85; }
.num { font-size: .8rem; color: var(--faint); font-variant: small-caps; letter-spacing: .1em; }
.card .name { font-size: 1.42rem; font-weight: 500; line-height: 1.12; margin: 6px 0 0; max-width: 82%; }
.card .name a { color: inherit; }
.card .name a:hover { text-decoration: underline; text-underline-offset: 3px; text-decoration-color: var(--cherry); }
.o { font-size: .95rem; color: var(--muted); font-variant: small-caps; letter-spacing: .04em; margin-top: 9px; }
.by { font-size: .86rem; color: var(--faint); font-variant: small-caps; letter-spacing: .06em; margin-top: 2px; }
/* Archive-only freshness caption (out-of-stock / delisted). Live cards never
   render it — only the roaster archive grid shows non-in-stock coffees. */
.archived-note { font-size: .8rem; color: var(--faint); font-variant: small-caps; letter-spacing: .05em; margin-top: 9px; padding-left: 10px; border-left: 2px solid var(--hair); }
.vp { font-size: 1rem; color: var(--faint); margin-top: 9px; }
.notes { color: var(--muted); font-size: .96rem; margin-top: 9px; }

/* flavor families */
.fams { display: flex; flex-wrap: wrap; gap: 13px; margin-top: 13px; }
.fams .fam { font-style: normal; display: flex; align-items: center; gap: 7px; font-size: .92rem; }
.fam-dot { width: 10px; height: 10px; border-radius: 3px; display: inline-block; flex: none; }

/* sensory scales */
.scales { display: flex; gap: 20px; margin-top: 15px; padding-top: 14px; border-top: 1px solid var(--hair); }
.sci { font-size: .74rem; color: var(--muted); }
.sci .lab { font-variant: small-caps; letter-spacing: .05em; display: block; margin-bottom: 5px; }
.dots { display: inline-flex; gap: 3px; }
.dots i { width: 6px; height: 6px; border-radius: 50%; background: var(--hair); display: inline-block; }
.dots i.on { background: var(--ink); }

.cf { display: flex; justify-content: flex-end; margin-top: 16px; }
.ver { font-size: .98rem; color: var(--cherry); font-variant: small-caps; letter-spacing: .06em; border-bottom: 1px solid var(--cherry); padding-bottom: 2px; }
.ver:hover { text-decoration: none; }

/* --- Specimen page: hero --------------------------------------------- */
.coffee .hero { display: grid; grid-template-columns: 1fr 240px; gap: 60px; align-items: center; padding: 18px 0 60px; }
.hero .kick { font-size: 1rem; color: var(--on-cherry); margin-bottom: 20px; }
.hero h1 { font-weight: 500; font-size: clamp(2.6rem, 5vw, 4.2rem); line-height: 1.01; color: var(--on); letter-spacing: -.012em; }
.hero .origin { font-weight: 400; font-size: 1.4rem; color: var(--on-muted); margin-top: 18px; font-variant: small-caps; letter-spacing: .05em; }
.hero .plate { color: var(--on); text-align: center; }
.hero .plate svg { opacity: .78; margin-inline: auto; }
.hero .plate .cherry { fill: var(--on-cherry); stroke: none; }
.hero .plate .lc { margin-top: 12px; font-size: .84rem; color: var(--on-muted); font-variant: small-caps; letter-spacing: .1em; }

/* --- Specimen page: dossier ------------------------------------------ */
.dossier { padding-block: 60px 100px; }
.specs { display: flex; flex-wrap: wrap; gap: 26px 56px; }
.spec { display: flex; flex-direction: column; }
.spec .k { font-size: .7rem; color: var(--muted); font-variant: small-caps; letter-spacing: .09em; margin-bottom: 6px; }
.spec .v { font-size: 1.4rem; font-weight: 500; }
.spec .v a, .fam-link { color: inherit; text-decoration: underline; text-decoration-thickness: 1px; text-decoration-color: var(--cherry); text-underline-offset: 4px; }
.spec .v a:hover, .fam-link:hover { color: var(--cherry); }
.spec .sug { font-size: 1.05rem; font-weight: 400; color: var(--muted); }
.spec .sug a { text-decoration-color: var(--hair); }
.spec .sug em { font-style: normal; font-variant: small-caps; letter-spacing: .06em; font-size: .82rem; color: var(--faint); }
.spec .stated { display: block; margin-top: 5px; font-size: .82rem; font-style: italic; color: var(--faint); }
.drow { margin-top: 64px; }
.drow > .h { display: block; font-size: .95rem; color: var(--muted); font-variant: small-caps; letter-spacing: .12em; margin-bottom: 18px; }
.drow > .h--pick { color: var(--cherry); font-weight: 500; }
.lead { font-size: 1.1rem; line-height: 1.6; max-width: var(--measure); }
.notes-lg { font-weight: 300; font-size: 2rem; line-height: 1.25; max-width: none; }
.notes-lg span { color: var(--hair); }
.dossier .fams { gap: 24px; }
.dossier .fams .fam { font-size: 1.1rem; }
.dossier .scales { border-top: 0; padding-top: 0; gap: 36px; }

.foot { margin-top: 64px; }
.foot .cta { display: flex; flex-direction: column; align-items: flex-start; gap: 10px; }
.foot .cta form { margin: 0; }
.src-note { font-size: .8rem; color: var(--faint); font-variant: small-caps; letter-spacing: .06em; }
.buy { font-size: 1rem; font-weight: 600; font-family: inherit; color: var(--paper); background: var(--cherry); border: 0; border-radius: var(--radius-button); padding: 14px 24px; font-variant: small-caps; letter-spacing: .06em; display: inline-block; cursor: pointer; transition: background-color var(--dur) var(--ease-out); }
/* The one solid button on the site had no pointer feedback at all — it only
   suppressed the underline. Ripening toward ink keeps it the same cherry and
   raises the label's contrast rather than lowering it. */
.buy:hover { background: color-mix(in oklch, var(--cherry) 88%, var(--ink)); text-decoration: none; }
.buy:active { background: color-mix(in oklch, var(--cherry) 78%, var(--ink)); }

/* --- Roaster badge kit (F4.1): quiet, secondary, below the fold ------- */
.badge-kit { margin-top: 56px; padding-top: 28px; border-top: 1px solid var(--hair); max-width: 560px; }
.badge-kit .h { display: block; font-size: .85rem; color: var(--muted); font-variant: small-caps; letter-spacing: .08em; }
.badge-kit p { color: var(--faint); font-size: .95rem; margin: 6px 0 20px; max-width: 46ch; }

/* One self-contained embed unit: live badge + copy action in the header, the
   code below. Contained because it's a control, not a content card. */
.badge-kit .embed { border: 1px solid var(--hair); border-radius: var(--radius-card); overflow: hidden; background: var(--surface); }
.badge-kit .embed-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 14px; padding: 14px 16px; background: var(--paper); border-bottom: 1px solid var(--hair); }
.badge-kit .preview { display: inline-flex; line-height: 0; }
.badge-kit .preview a { text-decoration: none; }
.badge-kit .copy { display: inline-flex; align-items: center; gap: 7px; font-family: inherit; font-size: .85rem; font-variant: small-caps; letter-spacing: .06em; color: var(--ink); background: transparent; border: 1px solid var(--muted); border-radius: var(--radius-button); padding: 8px 16px; cursor: pointer; transition: color .15s ease, background-color .15s ease, border-color .15s ease; }
.badge-kit .copy svg { width: 15px; height: 15px; flex: none; }
.badge-kit .copy:hover { color: var(--paper); background: var(--ink); border-color: var(--ink); }
.badge-kit .code { display: block; width: 100%; box-sizing: border-box; resize: vertical; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: .76rem; line-height: 1.6; color: var(--muted); background: transparent; border: 0; padding: 13px 16px; }
.badge-kit .code:focus-visible { outline: 2px solid var(--cherry); outline-offset: -2px; }

/* --- Atlas body: rail + main ----------------------------------------- */
.atlas-body { display: grid; grid-template-columns: 196px 1fr; gap: 48px; }
.atlas-main { min-width: 0; }
.listhead { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 22px; }
/* DESIGN.md sets the label at 500; these were inheriting the body's 400, so
   the hub's <h2> section labels (which take 500 from the base heading rule)
   would otherwise have read heavier than the identical label one click away. */
.listhead .h { font-size: .85rem; font-weight: 500; color: var(--muted); }

/* --- Browse rail ----------------------------------------------------- */
.rail .fh { font-size: .82rem; color: var(--muted); margin-bottom: 11px; margin-top: 28px; }
.rail .fh:first-child { margin-top: 0; }
.rail .fh a { color: inherit; }
.rail .fh a:hover { color: var(--cherry); text-decoration: none; }
.rail ul { list-style: none; }
.rail-link { display: flex; align-items: center; gap: 9px; padding: 5px 0; font-size: 1rem; color: var(--ink); }
.rail-link:hover { color: var(--cherry); text-decoration: none; }
.rail-link.on .rail-name { font-weight: 600; text-decoration: underline; text-decoration-thickness: 1px; text-decoration-color: var(--cherry); text-underline-offset: 4px; }
.rail-link em { margin-left: auto; font-style: normal; color: var(--faint); font-size: .82rem; font-variant: small-caps; }

/* --- Landing header (taxonomy pages) --------------------------------- */
.landing-head { padding: 32px 0 40px; }
.landing-head .crumb { color: var(--on-muted); font-size: .9rem; margin-bottom: 16px; }
.landing-head .crumb b { color: var(--on); font-weight: 500; }
.landing-head h1 { font-weight: 400; font-size: clamp(2.4rem, 4vw, 3.6rem); color: var(--on); letter-spacing: -.015em; }
.landing-head .lede { color: var(--on-muted); font-size: 1.15rem; margin-top: 14px; max-width: 56ch; font-weight: 300; }
.landing-head .term-kin { color: var(--on-muted); font-size: .95rem; margin-top: 14px; max-width: 64ch; }
.landing-head .term-kin a { color: var(--on); text-decoration: underline; text-decoration-thickness: 1px; text-decoration-color: var(--cherry); text-underline-offset: 3px; }
.landing-head .term-kin .fam { display: inline-flex; align-items: center; gap: 6px; }

/* --- Notas index (grouped by family) ---------------------------------- */
.note-group { margin-bottom: 44px; }
.note-group h2 { font-size: .95rem; color: var(--muted); font-weight: 500; letter-spacing: .06em; margin-bottom: 16px; }
.note-group h2 a { color: inherit; display: inline-flex; align-items: center; gap: 8px; }
.note-group h2 a:hover { color: var(--cherry); text-decoration: none; }

/* --- El paseo (similar coffees, each card says why) ------------------- */
.paseo-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 18px; }
.paseo-card { background: var(--surface); border-radius: var(--radius-card); padding: 20px 22px 18px; display: flex; flex-direction: column; }
.paseo-card .why { font-size: .74rem; letter-spacing: .09em; color: var(--cherry); font-weight: 600; }
.paseo-card .name { font-size: 1.25rem; font-weight: 500; line-height: 1.15; margin-top: 9px; }
.paseo-card .name a { color: inherit; }
.paseo-card .name a:hover { text-decoration: underline; text-underline-offset: 3px; text-decoration-color: var(--cherry); }
.paseo-card .by { margin-top: 4px; }
.paseo-card .cf { margin-top: auto; padding-top: 16px; }

/* --- Explorar -------------------------------------------------------- */
/* The cover and the coffees it describes need air between them; the grid used
   to start hard against the cover's edge. */
.explore { padding-block: 38px 88px; }
.ask { text-align: center; padding: 72px 0 16px; }
.ask h1 { font-weight: 300; font-size: clamp(2.4rem, 5vw, 3.6rem); letter-spacing: -.015em; }
/* `.ask` only ever sits inside the wood-green cover, so its lede takes the
   cover's secondary ink. It was --muted, which is tuned against paper and
   measures about 2:1 on the green — under any text floor. Same correction the
   --faint pass made, one surface over. */
.ask p { color: var(--on-muted); font-size: 1.25rem; margin-top: 14px; font-weight: 300; max-width: none; }

/* The way in for a reader who cannot answer "¿qué buscás?" yet — the mirror of
   the "cruzá características" line, which is for the reader who already can.
   Cherry on the cover (the brightened accent), used once per page. */
.quiz-entry {
  display: inline-block; margin-top: 20px; color: var(--on-cherry);
  font-variant: small-caps; letter-spacing: .06em; font-size: .98rem;
  border-bottom: 1px solid color-mix(in oklch, var(--on-cherry) 55%, transparent);
  padding-bottom: 2px;
  transition: color var(--dur) var(--ease-out), border-color var(--dur) var(--ease-out);
}
.quiz-entry:hover { color: var(--on); border-bottom-color: var(--on); text-decoration: none; }
.explore-section { padding: 40px 0 8px; }
.explore-section .lbl { font-size: .85rem; color: var(--muted); text-align: center; margin-bottom: 26px; }
.intents { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; }
/* The taste-first choice card (DESIGN.md `intent-card`), shared by /explorar's
   doors and the quiz's options: the same affordance doing the same job, so it
   has one set of metrics. Only the shell is shared — a door is a link, an
   option is a radio, and their states differ below. */
.intent, .qopt { background: var(--surface); border-radius: 6px; padding: var(--space-md); color: var(--ink); display: flex; flex-direction: column; gap: 8px; }
.intent .t, .qopt .t { font-size: 1.5rem; font-weight: 400; line-height: 1.15; }
.intent .d, .qopt .d { font-size: .98rem; color: var(--muted); }
.intent:hover { text-decoration: none; }
.intent:hover .t { text-decoration: underline; text-underline-offset: 3px; text-decoration-color: var(--cherry); }
.intent .bar { width: 34px; height: 5px; border-radius: 3px; }
.intent .bar--surprise { background: linear-gradient(90deg, var(--fam-frutos-rojos), var(--fam-citricos), var(--fam-florales)); }
.intent .t { margin-top: 6px; }
.intent .go { margin-top: auto; padding-top: 14px; font-variant: small-caps; letter-spacing: .08em; color: var(--cherry); font-size: .95rem; }
/* --- The filter: its headline, its facets (Slice 2) -------------------------------------------
   The query is the page's headline; a strip of pills below it mirrors that
   headline word for word, and clicking one opens its options underneath. Every
   option is a link, so the control works with no JavaScript — the pills are
   simply hidden then and all six rows stay open. `filter--live` is the switch.
   Chips follow DESIGN.md: pill, 1px hairline, transparent fill, quiet. Borders
   use --on-hair, the cover's answer to --hair, so a boundary on the green
   clears 3:1 instead of dissolving into it. */
.filter { display: block; }
.filter-headline {
  font-weight: 300; font-size: clamp(1.45rem, 4.6vw, 2.8rem); line-height: 1.28;
  letter-spacing: -.015em; color: var(--on); max-width: 24ch; padding-top: 10px;
}
.filter-headline .noun { color: var(--on-muted); }

.pills { display: none; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 20px; }
.filter--live .pills { display: flex; }
.pills .lbl { font-size: .72rem; color: var(--on-muted); margin-right: 6px; }

/* The pill is the frame; the button inside carries the padding, so the whole
   label is clickable rather than sitting in dead space the span owned. */
.pill {
  display: inline-flex; align-items: center;
  border: 1px solid var(--on-hair); border-radius: var(--radius-pill);
  color: var(--on-muted); white-space: nowrap;
  transition: color var(--dur) var(--ease-out), border-color var(--dur) var(--ease-out),
              background-color var(--dur) var(--ease-out);
}
.pill button {
  display: inline-flex; align-items: baseline; gap: 8px;
  font: inherit; color: inherit; background: none; border: 0;
  padding: 7px 15px; cursor: pointer;
}
.pill .noun { font-size: .74rem; font-variant: small-caps; letter-spacing: .09em; }
/* The ring belongs to the pill, not to the button inside it: on the button it
   drew a rectangle around a rounded shape. */
.pill button:focus-visible { outline: none; }
.pill:has(button:focus-visible) { outline: 2px solid var(--on-cherry); outline-offset: 2px; }
.pill.set { border-color: var(--on-cherry); color: var(--on); }
.pill.set .noun { color: var(--on-muted); }
.pill.add { border-style: dashed; }
/* Hover after the state classes, so a chosen pill answers the pointer too —
   it kept its accent border and looked inert before. */
.pill:hover, .pill:focus-within { color: var(--on); border-color: var(--on); }
.pill.set:hover, .pill.set:focus-within { border-color: var(--on-cherry); background: color-mix(in oklch, var(--on) 10%, transparent); }
.pill:active { background: color-mix(in oklch, var(--on) 16%, transparent); }
.pill.open, .pill.open:hover { background: var(--on); color: var(--cover); border-color: var(--on); }
.pill.open .noun, .pill.open .x { color: var(--cover); }
/* The × is its own control: give it a real hit area (44px on touch) and pull
   the extra back with negative margin so the pill keeps its height. */
.pill .x {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 26px; min-height: 26px; margin: -4px 2px -4px -6px;
  color: var(--on-cherry); font-size: .95rem; line-height: 1;
}
.pill .x:hover { color: var(--on); text-decoration: none; }

/* One dimension's options. Open by default — that is the no-JavaScript page. */
.panel {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  margin-top: 14px; padding-top: 14px;
  border-top: 1px solid color-mix(in oklch, var(--on-muted) 28%, transparent);
}
.filter--live .panel { display: none; }
.filter--live .panel.is-open { display: flex; animation: panel-in var(--dur) var(--ease-out) both; }
@keyframes panel-in { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
.panel .lbl { font-size: .72rem; color: var(--on-muted); margin-right: 6px; }
.opt {
  display: inline-flex; align-items: baseline; gap: 7px;
  border: 1px solid var(--on-hair-strong); border-radius: var(--radius-pill);
  padding: 6px 13px; font-size: .93rem; white-space: nowrap; color: var(--on-muted);
  transition: color var(--dur) var(--ease-out), border-color var(--dur) var(--ease-out),
              background-color var(--dur) var(--ease-out);
}
.opt:hover { border-color: var(--on); color: var(--on); background: color-mix(in oklch, var(--on) 8%, transparent); text-decoration: none; }
.opt:active { background: color-mix(in oklch, var(--on) 16%, transparent); }
/* The chosen option carries a check as well as the accent: colour alone must
   never be the signal (PRODUCT.md), and the check also says "click to undo". */
.opt.now { border-color: var(--on-cherry); color: var(--on); }
.opt.now::before { content: "✓"; font-size: .8em; color: var(--on-cherry); margin-right: -1px; }
.opt .c { font-size: .76rem; color: color-mix(in oklch, var(--on-muted) 85%, var(--cover)); font-variant: small-caps; }
.opt:hover .c, .opt.now .c { color: var(--on-muted); }

.filter-answer { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px 16px; margin-top: 20px; padding-bottom: 32px; font-size: .8rem; max-width: none; }
.filter-answer .n { color: var(--on-cherry); }
.filter-answer .quiet { color: var(--on-muted); border-bottom: 1px solid color-mix(in oklch, var(--on-muted) 40%, transparent); transition: color var(--dur) var(--ease-out), border-color var(--dur) var(--ease-out); }
.filter-answer .quiet:hover { color: var(--on); border-bottom-color: var(--on); text-decoration: none; }

/* Touch: every control clears the 44px target the hand needs. */
@media (pointer: coarse) {
  .pill button { padding-block: 11px; }
  .pill .x { min-width: 44px; min-height: 44px; margin-block: -11px; }
  .opt { padding-block: 10px; }
  /* The inline action link ("Ver ficha →", "Seguir el paseo →", "Responder de
     nuevo →") sits at 19px, which is a thumb's worth of missing. The hit area
     is extended with a pseudo-element rather than padding: padding would push
     the cherry underline down off the text, and this shifts no layout at all. */
  .ver { position: relative; }
  .ver::after { content: ""; position: absolute; inset: -13px -10px; }
}

/* The reader who skips the taste door entirely. */
.explore-cross { margin-top: 44px; padding-top: 24px; border-top: 1px solid var(--hair); text-align: center; color: var(--muted); max-width: none; }

/* Nothing crosses everything (IF3). */
.no-match { padding: 8px 0 8px; }
.no-match .h { display: block; font-size: .85rem; color: var(--muted); margin-bottom: 14px; }
.no-match .lead { margin-top: 10px; }

.chips { display: flex; flex-wrap: wrap; gap: 12px; justify-content: center; }
.chip { border: 1px solid var(--hair); border-radius: var(--radius-pill); padding: 11px 22px; font-size: 1.05rem; color: var(--ink); }
.chip:hover { border-color: var(--cherry); text-decoration: none; }
.explore-paseo { margin-top: 48px; padding-top: 26px; border-top: 1px solid var(--hair); text-align: center; }
.explore-paseo .l { font-size: .85rem; color: var(--muted); margin-bottom: 14px; }
.explore-paseo a { font-size: 1.15rem; color: var(--ink); }
.explore-paseo a:hover { text-decoration: underline; text-decoration-color: var(--cherry); text-underline-offset: 3px; }

/* --- The quiz (QZ4) --------------------------------------------------- */
/* One question at a time when JavaScript is there, all three at once when it
   is not — `quiz--live` is the switch, and like the filter's it only ever
   hides what the markup already showed. The options are DESIGN.md's intent
   card at option scale: a surface tint, a serif label, an optional descriptor,
   no borders. The radio itself is hidden from sight but not from the
   accessibility tree, so the whole card is the target and the keyboard still
   gets a real radio group. */
/* One question per screen, sitting in the middle of it. Without this the
   question hangs off the cover with the rest of the viewport empty below, which
   reads as content that ran out rather than as a question asked on its own.
   `safe center` because the no-JavaScript page stacks all three questions: past
   one screen the block must flow from the top, not centre itself and clip. */
.quiz { display: grid; grid-template-rows: auto 1fr; min-height: 100svh; }
/* `width: 100%` because .wrap centres itself with `margin-inline: auto`, and an
   auto margin on a grid item beats the default stretch — without it the wrap
   collapses to its content width and the option grid drops to one column. */
.quiz-body { width: 100%; display: flex; flex-direction: column; justify-content: safe center; padding-block: var(--space-lg) var(--space-xl); }

/* The scroll margins are what the controller's scrollIntoView lands against:
   a question arrives with air above it rather than flush to the top edge, and
   the button clears the bottom edge by the same amount. */
.q { border: 0; padding: 0; margin-bottom: var(--space-xl); scroll-margin-top: var(--space-lg); }
.q legend { display: block; padding: 0; margin-bottom: 18px; }
.q .step { display: block; font-size: .78rem; color: var(--faint); margin-bottom: var(--space-sm); }
/* A `<legend>` is the group's accessible name, so the prompt is a span inside
   it rather than a heading — which also puts it outside the global h1–h3
   balance rule, hence the declaration here. */
.q .prompt { display: block; font-weight: 400; font-size: clamp(1.7rem, 3.4vw, 2.4rem); line-height: 1.14; letter-spacing: -.015em; text-wrap: balance; }
.quiz--live .q { display: none; }
.quiz--live .q.is-open { display: block; animation: panel-in var(--dur) var(--ease-out) both; }

.opts { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 18px; }
/* Shell and type come from `.intent` above; what is added here is what a
   control needs and a link does not — a pointer, and the states. The border is
   transparent at rest so checking one cannot shift the layout by a pixel. */
.qopt {
  cursor: pointer; border: 1px solid transparent;
  transition: border-color var(--dur) var(--ease-out), background-color var(--dur) var(--ease-out);
}
/* Clipped rather than hidden: `display:none`, `visibility:hidden` and even
   `opacity:0` drop the input out of the accessibility tree or out of the tab
   order, and then the question has no control at all. Clipped, it is a real
   radio group — arrow keys move through it, the label is the target, and the
   ring below draws on the card. */
.qopt input {
  position: absolute; width: 1px; height: 1px;
  clip-path: inset(50%); overflow: hidden; white-space: nowrap;
}
/* The engraved mark sits where the intent card's colour bar sits: first, above
   the title. Quiet by default so the words still lead; the taste question
   overrides the colour inline with its family's own. */
.qopt .mark { display: block; color: var(--muted); margin-bottom: 2px; }
.qopt:hover .mark { color: var(--ink); }
.qopt:has(input:checked) .mark { color: var(--cherry); }
.qopt:hover { border-color: var(--hair); background: color-mix(in oklch, var(--surface) 92%, var(--ink)); }
/* Press feedback, the same gesture `.opt` and `.pill` answer with in the
   filter — a card you can click is a card that should react to being clicked. */
.qopt:active { background: color-mix(in oklch, var(--surface) 86%, var(--ink)); }
.qopt:has(input:checked) { border-color: var(--cherry); }
.qopt:has(input:focus-visible) { outline: 2px solid var(--cherry); outline-offset: 2px; }

/* The commitment, and the page's single cherry gesture. Always there without
   JavaScript; with it, held back until the third answer and then brought in on
   the same rise the questions use, so it reads as the flow arriving somewhere
   rather than a control that was hiding. */
/* Right-aligned, the way every "Ver ficha →" sits at the end of its card (.cf):
   it is what comes *after* the options, and the eye leaves a row of cards on
   the right. */
.qgo { display: flex; justify-content: flex-end; margin-top: var(--space-md); scroll-margin-bottom: var(--space-lg); }
.quiz--live .qgo { display: none; }
.quiz--live .qgo.is-ready { display: flex; animation: panel-in var(--dur) var(--ease-out) both; }

/* --- The quiz result -------------------------------------------------- */
/* Presented as a specimen: it borrows the ficha's cover, hero and dossier
   wholesale, and adds only the two things a recommendation has that a ficha
   does not — the why, and the facets it matched. */
.quiz-result .hero .prov { font-size: 1.02rem; color: var(--on-muted); margin-top: 10px; }
.quiz-result .hero .why { font-size: 1.2rem; line-height: 1.5; color: var(--on-muted); margin-top: 22px; max-width: 46ch; font-weight: 300; }
/* The dossier opens on a row here, where the ficha opens on its spec block — so
   the row's own 64px would land on top of the section's 60px. Whichever row
   comes first: with nothing matched there are no facets and the click-out
   leads, and that gap was the same double one. */
.quiz-result .dossier > :first-child { margin-top: 0; }
.qfacets { display: flex; flex-wrap: wrap; gap: var(--space-sm) var(--space-md); }
.qfacet { display: inline-flex; align-items: baseline; gap: 8px; font-size: 1.25rem; font-weight: 500; }
.qfacet .noun { font-size: .74rem; font-weight: 400; color: var(--muted); font-variant: small-caps; letter-spacing: .09em; }
.qfacet a { color: inherit; text-decoration: underline; text-decoration-thickness: 1px; text-decoration-color: var(--cherry); text-underline-offset: 4px; }
.qfacet a:hover { color: var(--cherry); }
.qspec .o { font-size: 1.05rem; color: var(--muted); font-variant: small-caps; letter-spacing: .04em; }
.qspec .vp { font-size: 1.05rem; color: var(--faint); margin-top: 4px; }
.qficha { margin-top: var(--space-md); }
/* The closing block, on the ficha's badge-kit shape: a hairline to separate it
   from the walk above, a line of copy, one action. It was a bare grey link
   floating in the whitespace under the alternates, which read as something left
   behind rather than the way back into the quiz. */
.qagain { margin-top: var(--space-xl); padding-top: var(--space-md); border-top: 1px solid var(--hair); }
.qagain .l { display: block; font-size: .85rem; color: var(--muted); margin-bottom: var(--space-sm); }

/* --- Taxonomy index (term cards) -------------------------------------- */
.term-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 18px; }
.term-card { background: var(--surface); border-radius: var(--radius-card); padding: 24px 26px; color: var(--ink); display: flex; flex-direction: column; gap: 8px; }
.term-card:hover { text-decoration: none; }
.term-card .name { font-size: 1.5rem; font-weight: 500; display: flex; align-items: center; gap: 10px; }
.term-card:hover .name { text-decoration: underline; text-underline-offset: 3px; text-decoration-color: var(--cherry); }
.term-card .d { font-size: .98rem; color: var(--muted); max-width: none; }
/* Grid rows stretch, so a card whose name or description wraps one line short
   used to float its footer mid-card while its neighbour's sat at the bottom.
   Pinned like the paseo card's: `auto` takes the slack, the padding keeps the
   24px it always had when there is none to take. */
.term-card .cf { justify-content: space-between; align-items: baseline; margin-top: auto; padding-top: 16px; }
.term-card .count { font-size: .82rem; color: var(--faint); }
.empty-note { color: var(--muted); }

/* --- Comparison hub (S3.2) -------------------------------------------- */
/* One term grid per pair type. Air between the sections, not a rule: the
   listhead label is the only separation they need. */
.hub-section + .hub-section { margin-top: var(--space-xl); }
/* A cross card carries a name and a count, where a term card also carries a
   descriptive line — and here the rail takes 196px the taxonomy index does not
   spend. A narrower track buys back the third column, so the hub keeps the same
   grid rhythm as /variedades instead of running 34 crosses down two. */
.hub-section .term-grid { grid-template-columns: repeat(auto-fill, minmax(232px, 1fr)); }

/* --- Roaster profiles ------------------------------------------------ */
.roaster-specs { margin-bottom: 48px; }
.roaster-specs .v { font-size: 1.15rem; }
.listhead--archive { margin-top: 64px; }
.roaster-links { margin-top: 14px; display: flex; gap: 20px; }
.roaster-links a { color: var(--on-muted); font-variant: small-caps; letter-spacing: .06em; font-size: .95rem; }
.roaster-links a:hover { color: var(--on); text-decoration: none; }
.roaster-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 18px; }
.roaster-card { background: var(--surface); border-radius: var(--radius-card); padding: 24px 26px; color: var(--ink); display: flex; flex-direction: column; gap: 5px; }
.roaster-card:hover { text-decoration: none; }
.roaster-card .name { font-size: 1.5rem; font-weight: 500; }
.roaster-card:hover .name { text-decoration: underline; text-underline-offset: 3px; text-decoration-color: var(--cherry); }
.roaster-card .o { color: var(--muted); font-variant: small-caps; letter-spacing: .04em; }
.roaster-card .cf { margin-top: 10px; }

/* --- Featured editorial card (index) -------------------------------- */
.feat { background: var(--surface); border-radius: var(--radius-card); padding: 28px 30px; display: grid; grid-template-columns: 1fr 130px; gap: 24px; margin-bottom: 22px; align-items: center; }
.feat .lbl { color: var(--cherry); font-weight: 500; font-size: .82rem; }
.feat .name { font-size: 1.9rem; font-weight: 500; line-height: 1.1; margin-top: 6px; }
.feat .name a { color: inherit; }
.feat .name a:hover { text-decoration: underline; text-underline-offset: 3px; text-decoration-color: var(--cherry); }
.feat .blurb { font-size: 1.08rem; line-height: 1.55; margin-top: 12px; max-width: 60ch; }
.feat .meta { font-size: .9rem; color: var(--muted); font-variant: small-caps; letter-spacing: .04em; margin-top: 12px; }
.feat .fams { margin-top: 12px; }
.feat .read { display: inline-block; margin-top: 16px; color: var(--cherry); font-variant: small-caps; letter-spacing: .06em; border-bottom: 1px solid var(--cherry); padding-bottom: 2px; }
.feat .plate { color: var(--ink); opacity: .5; text-align: center; }
.feat .plate svg { width: 110px; height: auto; }
.feat .plate .cherry { fill: var(--cherry); stroke: none; opacity: .85; }

/* --- Responsive ------------------------------------------------------- */
/* The masthead is one line only while one line fits. With "Comparaciones" (S3.1)
   the wordmark, six nav items and "Explorar" want 1075px, and the nav is not
   hidden until 760px — so between those widths the nav used to run past the
   wrap. It takes a row of its own instead, wrapping again if even that is not
   enough. (Five items already overflowed below ~900px; this covers that too.)

   NOTE: measured at 1084px of usable width (the 1180px wrap less its padding),
   this row has ~9px of slack left. The masthead is full: the next item added to
   it puts the nav on its own row at every width, which is a decision about the
   masthead, not about whichever feature wants the link. The quiz's entry points
   live on /explorar and the atlas cover for exactly that reason. */
@media (max-width: 1120px) {
  .masthead { flex-wrap: wrap; row-gap: 18px; }
  .nav { order: 3; flex-basis: 100%; flex-wrap: wrap; row-gap: 10px; }
}

@media (max-width: 760px) {
  .grid { grid-template-columns: 1fr; }
  .atlas-body { grid-template-columns: 1fr; }
  .rail { display: none; }
  /* The filter is the page's only control and lives in the cover, so it is
     never hidden — it just wraps onto more lines. The cover is trimmed instead:
     a three-facet query used to fill a phone screen on its own, which put the
     coffees the query is about entirely below the fold. */
  .filter-headline { max-width: none; }
  .pills { margin-top: 16px; }
  .filter-answer { margin-top: 16px; padding-bottom: 24px; }
  .coffee .hero { grid-template-columns: 1fr; gap: 30px; }
  .hero .plate { display: none; }
  .intents { grid-template-columns: 1fr; }
  .feat { grid-template-columns: 1fr; }
  .feat .plate { display: none; }
  .wrap { padding-inline: var(--space-md); }
  .nav { display: none; }
}
