/* ==========================================================================
   Kofineur — base stylesheet
   Design tokens + reset + base typography. Components get their own files,
   added per slice. DESIGN.md is the system of record; keep these in sync.
   No utility framework: structure comes from semantic classes + tokens.
   ========================================================================== */

/* --- Design tokens (see DESIGN.md) ------------------------------------- */
:root {
  /* surfaces */
  --paper: oklch(0.966 0.014 88);        /* warm old-paper body bg (NOT cream) */
  --surface: oklch(0.948 0.018 92);      /* cards / panels */
  --cover: oklch(0.275 0.055 155);       /* deep wood-green hero/cover */

  /* ink (text on paper) */
  --ink: oklch(0.27 0.035 155);          /* body + headings (deep green) */
  --muted: oklch(0.45 0.03 150);         /* secondary text */
  /* Tertiary / metadata. Tuned to clear 4.5:1 on the darker of the two
     backgrounds it sits on (4.60:1 on --surface, 4.83:1 on --paper): it reads
     as the quietest step, but it carries real data — a card's live count, the
     N.º, "Tostado por …" — so it is body text and takes body text's floor. It
     was 0.60, which measured 3.37:1 and failed that. --hair stays where it is:
     it draws lines, never text. */
  --faint: oklch(0.525 0.022 150);       /* tertiary / metadata */
  --hair: oklch(0.85 0.018 110);         /* hairlines, empty scale dots */
  /* The paper-side counterpart to --on-hair-strong, and it exists for the same
     reason: --hair is tuned to *separate* (a rule between rows), and a rule may
     be faint because nothing depends on seeing it. A chip's boundary is the only
     thing that says "this is a control" — its labels are plain ink, not
     underlined — so it is a UI component boundary and takes the 3:1 that needs
     (3.1:1 on --surface, 3.3:1 on --paper). --hair measured 1.35:1 there, which
     is a rule pretending to be a button. Boundaries only; never text, never a
     separator. */
  --hair-strong: oklch(0.62 0.02 120);

  /* ink on cover (deep green) */
  --on: oklch(0.95 0.022 100);
  --on-muted: oklch(0.80 0.032 115);
  /* the cover's answer to --hair. A hairline on paper can be whisper-faint
     because paper is bright; on the wood green the same gesture disappears, so
     these are tuned to clear the 3:1 a component boundary needs (3.5:1 and
     4.0:1 measured against --cover) rather than to match --hair's lightness. */
  --on-hair: color-mix(in oklch, var(--on-muted) 65%, transparent);
  --on-hair-strong: color-mix(in oklch, var(--on-muted) 70%, transparent);

  /* accent — the one ripe-cherry red, used sparingly (<=10% of a screen) */
  --cherry: oklch(0.52 0.17 28);
  /* Cherry brightened for the cover. It was 0.64, which measures 3.99:1 on
     --cover: enough for the focus ring and a chip border (3:1) but under the
     floor for the text it also draws — the ficha's roaster kicker, the filter's
     counts, the quiz's kicker and entry link. 0.68 measures 4.66:1 and keeps
     the full 0.19 chroma, so it is the same ripe cherry, not a washed one.
     Same correction --faint took on paper. */
  --on-cherry: oklch(0.68 0.19 30);
  /* The cover's link underline at rest, one step below --on-cherry so hovering
     it has somewhere to go. It exists as a token because three rules were
     reaching for "a quieter cherry" independently and one of them
     (.roaster-links) had picked 60%, which composites to 2.4:1 over --cover —
     the exact figure brightening to --on-cherry was meant to escape, undone by
     the knock-back. 80% measures 3.38:1: still visibly the quiet state, and
     over the 3:1 an underline carrying a link's whole affordance has to clear
     (DESIGN §Separator-vs-Boundary). Percentages below 75% do not clear it, so
     this is a floor, not a preference. */
  --on-cherry-quiet: color-mix(in oklch, var(--on-cherry) 80%, transparent);

  /* flavor-family categorical palette — ALWAYS paired with a text label */
  --fam-frutos-rojos: oklch(0.53 0.18 22);
  --fam-citricos: oklch(0.74 0.15 80);
  --fam-florales: oklch(0.55 0.12 320);
  --fam-dulces: oklch(0.56 0.15 40);
  --fam-chocolate: oklch(0.40 0.05 50);
  --fam-te: oklch(0.60 0.07 155);
  --fam-frutos-amarillos: oklch(0.76 0.14 92);
  --fam-frutos-secos: oklch(0.50 0.05 65);
  --fam-tropicales: oklch(0.70 0.15 145);
  --fam-fermentados: oklch(0.45 0.13 350);

  /* type — one serif only */
  --serif: "Newsreader", Georgia, "Times New Roman", serif;

  /* spacing scale */
  --space-xs: 6px;
  --space-sm: 12px;
  --space-md: 24px;
  --space-lg: 48px;
  --space-xl: 72px;

  /* radius */
  --radius-card: 5px;
  --radius-pill: 999px;
  --radius-button: 3px;

  /* layout */
  --measure: 68ch;   /* max prose line length */
  --wrap: 1180px;    /* max content width */

  /* motion — state feedback only, never choreography. Exponential ease-out so
     a change settles rather than bounces (DESIGN.md: nothing elastic). */
  --dur: 170ms;
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
}

/* --- Reset (minimal) --------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
html { -webkit-text-size-adjust: 100%; }
img, svg, picture { max-width: 100%; display: block; }

/* --- Base -------------------------------------------------------------- */
body {
  font-family: var(--serif);
  font-weight: 400;
  line-height: 1.5;
  color: var(--ink);
  background: var(--paper);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  /* Sticky footer: the wood-green band is pushed to the bottom of a short page
     (`margin-top: auto` on .foot) instead of leaving a strip of paper under it,
     which reads as a rendering bug rather than as the end of the page. */
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
}

/* A column flex container would let a tall page's sections shrink to fit the
   viewport; this keeps every one of them at its natural height, so the flex
   above changes nothing but where the footer lands. */
body > * { flex: 0 0 auto; }

h1, h2, h3, h4 {
  font-weight: 500;
  line-height: 1.12;
  letter-spacing: -0.01em;
  text-wrap: balance;
}
p { text-wrap: pretty; max-width: var(--measure); }

a { color: var(--cherry); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 3px; }

::selection { background: color-mix(in oklch, var(--cherry) 22%, transparent); }

/* --- Foundational helpers (NOT a utility framework) -------------------- */
/* `width: 100%` is load-bearing, not belt-and-braces. The sticky footer above
   makes <body> a flex column, and a flex item with `auto` cross-axis margins is
   sized to fit-content rather than stretched — so every `.wrap` that is a direct
   child of body (the <main> on all seven content page types) shrank to whatever
   its widest row happened to be and centred itself inside the gap. The cover and
   the footer never showed it: their `.wrap` is nested one level down, in a normal
   block context, which is why the bands stayed full width while the content under
   them pulled in. An explicit width restores the stretch; `max-width` still caps
   it and the auto margins still centre it, in both layout modes. */
.wrap { width: 100%; max-width: var(--wrap); margin-inline: auto; padding-inline: var(--space-lg); }
.sc   { font-variant: small-caps; letter-spacing: 0.12em; }
/* Present to the page but not to the eye: the field a copy button reads from,
   which has to be a real input for the pre-Clipboard-API fallback to select
   it. Not `display: none`, which browsers refuse to copy from. */
.vh   { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }

/* --- Accessibility ----------------------------------------------------- */
:focus-visible { outline: 2px solid var(--cherry); outline-offset: 2px; }
/* On the wood green the same cherry falls to 2.4:1, under the 3:1 a focus
   indicator has to clear. The brightened accent is the identical signal at
   4.0:1, so the ring stays cherry everywhere and stays legible everywhere. */
.cover :focus-visible { outline-color: var(--on-cherry); }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}
