/* packs/article — shared stylesheet for the estate's article sites.
 *
 * Byte-identical between every site on this pack, so it stays cacheable.
 * Everything brand-specific arrives as CSS custom properties in a small inline
 * block per page (engine/theme.py reads site.yaml `brand:` and `fonts:`), which
 * is why not one colour and not one typeface is written literally below.
 * viiniveli.fi and rosabotti.fi both build on this file and look nothing alike;
 * a hardcoded hex here is how that stops being true.
 *
 * Tokens consumed, all declared per site in site.yaml `brand:`:
 *   --canvas --canvas-alt --surface --ink --on-ink --body --accent
 *   --accent-soft --highlight        --font-display --font-body
 *
 * THE SIZES AND WEIGHTS BELOW ARE MEASURED, NOT CHOSEN. They come from what
 * viiniveli.fi actually renders (Playwright, computed styles, 1280px viewport),
 * not from what its theme declares. Its stylesheet declares a brand red
 * (#DD183B) that is painted on nothing; the colour a reader sees is a magenta
 * the theme panel never mentions. Reading the declared tokens would have
 * shipped the wrong site. A pack-level default that a site overrides is fine;
 * a pack-level default nobody checked against the live page is not.
 */

:root {
  --wrap: 1200px;      /* the live .ast-container max-width */
  --narrow: 780px;
  --gap: 24px;
  --radius: 12px;
  /* Fallbacks, so the sheet is legible even with no site.yaml brand block. A
     pack that renders white-on-white without configuration cannot be reviewed. */
  --canvas: #fff;
  --canvas-alt: #f2f2f2;
  --surface: #fff;
  --ink: #111;
  --on-ink: #fff;
  --body: #444;
  --accent: #7a0047;
  --accent-soft: #e7e1e3;
  --highlight: #4d002c;
  --font-display: system-ui, sans-serif;
  --font-body: system-ui, sans-serif;
}

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

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

body {
  margin: 0;
  background: var(--canvas);
  color: var(--body);
  font-family: var(--font-body);
  /* Live renders body copy at 18px / 29.7px, i.e. line-height 1.65. */
  font-size: clamp(1rem, 0.94rem + 0.2vw, 1.125rem);
  line-height: 1.65;
}

h1, h2, h3, h4 {
  font-family: var(--font-display);
  color: var(--ink);
  font-weight: 600;          /* live sets every heading in Poppins 600 */
  line-height: 1.2;
  margin: 0 0 0.5em;
}
/* Live: 50px. */
h1 { font-size: clamp(2rem, 1.5rem + 2vw, 3.125rem); }
/* Live renders article <h2> at 48px, three pixels off its own <h1>, so a
   section heading reads as a second page title. Capped here at 34px to restore
   the hierarchy. This is the one type size that does NOT reproduce the live
   page; everything else does. */
h2 { font-size: clamp(1.5rem, 1.25rem + 1vw, 2.125rem); }
h3 { font-size: 1.25rem; }

p { margin: 0 0 1.1em; }

a { color: var(--accent); text-decoration-thickness: 1px; text-underline-offset: 2px; }
a:hover { color: var(--highlight); }

img { max-width: 100%; height: auto; }

.wrap { width: 100%; max-width: var(--wrap); margin: 0 auto; padding: 0 20px; }
.wrap.narrow { max-width: var(--narrow); }

.visually-hidden {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--ink); color: var(--on-ink); padding: 10px 16px;
}
.skip-link:focus { left: 0; }

:focus-visible { outline: 3px solid var(--ink); outline-offset: 2px; }

/* ------------------------------------------------------------------ header
 * Live: a solid brand bar 81px tall, white wordmark on the left. */

/* Header bar. --header-bg / --header-ink are optional per-site tokens;
   without them this is exactly what it was, so viiniveli is unchanged.
   rosabotti's live header is white with dark type, viiniveli's is its
   magenta with white type — one pack, two headers, no fork. */
.site-header { background: var(--header-bg, var(--accent));
               color: var(--header-ink, var(--on-ink)); }
.header-row {
  display: flex; align-items: center; gap: 18px; flex-wrap: wrap;
  padding-top: 20px; padding-bottom: 20px;
}
.brand { display: inline-flex; flex: 0 0 auto; }
/* Height-driven so each site's logo keeps its own aspect. 41px is
   viiniveli's height and its 300x77 file lands back on exactly 160x41,
   so the default renders it unchanged. rosabotti sets 36px and its
   768x137 file lands on 202x36 — live's size. No object-fit: with width
   auto there is no box to fit inside. */
.brand img { height: var(--logo-h, 41px); width: auto; display: block; }

.site-nav { flex: 1 1 auto; }
.site-nav ul, .lang-switch ul, .footer-links ul {
  list-style: none; margin: 0; padding: 0; display: flex; gap: 18px;
  flex-wrap: wrap;
}
.site-nav a, .lang-switch a, .site-footer a {
  color: var(--on-ink); text-decoration: none;
}
.site-nav a { font-weight: 500; font-size: 1rem; padding: 6px 2px; display: inline-block; }
.site-nav a:hover, .lang-switch a:hover, .site-footer a:hover { text-decoration: underline; }
.site-nav a[aria-current="page"] { border-bottom: 2px solid var(--on-ink); }

.lang-switch ul { gap: 12px; }
.lang-switch a { font-size: 0.85rem; letter-spacing: 0.06em; opacity: 0.8; }
.lang-switch a.is-current { opacity: 1; text-decoration: underline; }

.header-search { display: flex; gap: 6px; flex: 0 1 240px; }
.search-input {
  flex: 1 1 auto; min-width: 0; border: 0; border-radius: 999px;
  padding: 9px 14px; font: inherit; font-size: 0.95rem;
  background: var(--surface); color: var(--ink);
}
.search-button, .search-page button, .button {
  border: 0; border-radius: 999px; padding: 10px 20px; font: inherit;
  font-size: 0.95rem; font-weight: 500; cursor: pointer;
  background: var(--ink); color: var(--on-ink); text-decoration: none;
  display: inline-block;
}
.search-button:hover, .search-page button:hover, .button:hover {
  background: var(--highlight); color: var(--on-ink);
}

/* -------------------------------------------------------------------- hero
 * The live hero band, measured at rgb(231,225,227). It is the front page's
 * identity, so it renders THERE and only there. On the live site the same band
 * is injected above every article too, carrying a second <h1> — which is how
 * a post page ends up with three of them and Google is left to guess which
 * one names the page. */

.hero { background: var(--canvas-alt); padding: 64px 0 56px; }
.hero h1 { color: var(--accent); margin-bottom: 0.5em; }
.hero .hero-lead { font-size: 1.15em; }
.hero .hero-note { font-size: 0.9em; opacity: 0.85; margin-bottom: 0; }

/* The bot call to action the live site repeats on every page. Kept as a strip
   rather than a 648px hero with a heading of its own. */
.bot-cta { background: var(--canvas-alt); padding: 32px 0; margin-top: 48px; }
.bot-cta p { margin: 0 0 0.5em; }
.bot-cta p:last-child { margin: 0; font-size: 0.9em; opacity: 0.85; }
.bot-cta .cta-lead { font-weight: 500; color: var(--ink); font-size: 1.1em; }
/* After the :last-child rule on purpose: when the button paragraph is the
   strip's last child it must NOT inherit the small ps-line styling. */
.bot-cta p.cta-action { margin: 18px 0 0; font-size: 1rem; opacity: 1; }

/* ------------------------------------------------- bot-open buttons
 * Everything that carries data-bot-open. The header button inverts the bar
 * (surface on accent); the hero buttons are the page's primary action and
 * take the accent; a tip card stays a card that happens to be a button. */

/* The header's bot button must read as a BUTTON on EITHER header, so it is
   the header's own colours INVERTED — the one pairing guaranteed to contrast,
   because --header-ink is by definition legible on --header-bg.

   The first version hardcoded surface-on-accent. Correct against viiniveli's
   magenta bar; against rosabotti's WHITE bar it rendered white on white and
   the client saw plain text where a button should be.

     viiniveli  no header tokens -> white pill, magenta label
     rosabotti  header_bg #fff   -> near-black pill, white label */
.header-bot {
  border: 0; border-radius: 999px; padding: 10px 18px; font: inherit;
  font-size: 0.95rem; font-weight: 600; cursor: pointer; flex: 0 0 auto;
  margin-left: auto;
  background: var(--header-ink, var(--on-ink));
  color: var(--header-bg, var(--accent));
}
.header-bot:hover { background: var(--accent); color: var(--on-ink); }

/* Mobile: keep logo and button on ONE row. The logo is up to 202px wide and
   the button ~130px, which wraps at 375px and costs a whole extra header row
   on the screen that matters most. Shrinking the logo a little buys the fit. */
@media (max-width: 480px) {
  .header-row { gap: 10px; padding-top: 14px; padding-bottom: 14px; }
  .brand img { height: calc(var(--logo-h, 41px) * 0.8); }
  .header-bot { padding: 9px 14px; font-size: 0.9rem; }
  .lang-switch a { font-size: 0.8rem; }
}

/* The whole hero as a tap target (hero_opens_bot). Pointer on the band so
   desktop shows the affordance too; the buttons inside stay buttons. */
.hero-tap { cursor: pointer; }

/* The landing hero above every article: same band, tighter padding, and its
   heading is a <p> so the article title stays the page's only <h1>. Sized a
   step under the h1 so the article title still reads as the page's name. */
.hero-landing { padding: 36px 0 32px; }
.hero .hero-heading {
  font-family: var(--font-display); color: var(--accent); font-weight: 600;
  line-height: 1.2; margin: 0 0 0.5em;
  font-size: clamp(1.6rem, 1.3rem + 1.2vw, 2.25rem);
}

.hero-ctas { display: grid; gap: 12px; margin-top: 28px; }
@media (min-width: 600px) { .hero-ctas { grid-template-columns: 1fr 1fr; } }
.hero-cta {
  border: 0; border-radius: var(--radius); padding: 18px 20px; font: inherit;
  cursor: pointer; text-align: left; display: block; width: 100%;
  background: var(--accent); color: var(--on-ink);
}
.hero-cta:hover { background: var(--highlight); }
.hero-cta-title {
  display: block; font-family: var(--font-display); font-weight: 600;
  font-size: 1.1em;
}
.hero-cta-sub { display: block; font-size: 0.9em; opacity: 0.9; margin-top: 4px; }

.tip-open {
  display: block; width: 100%; border: 0; padding: 0; margin: 0;
  background: none; font: inherit; text-align: left; color: inherit;
  cursor: pointer;
}
/* Spans inside a button (its content model is phrasing content), styled to
   land exactly where the <p> versions of the same classes land. */
.tip-open .tip-eyebrow, .tip-open .tip-title { display: block; }

/* -------------------------------------------------------------- intro block */

.intro { padding: 56px 0 8px; }
.intro .lead { font-size: 1.1em; }

/* ------------------------------------------------------------- age gate
 * In the flow, first thing in <main> — the flagship's pattern: on a first
 * visit it is part of the first paint (no CLS), a pre-paint head script
 * classes <html> so a returning visitor never sees it, and answering it
 * shifts the page inside the 500 ms click window CLS excludes. A banner
 * above the content, not a wall — until the answer is "no", which hides
 * the content, the bot and its CTA strip. */

/* DARK, not the brand colour. The first version painted the band --accent —
   directly under a header bar that is also --accent — and the client could
   not see it: "it disappears to red color". The gate has to read as a
   different surface from the page, so it is the ink tone with white type. */
.agegate {
  background: var(--ink); color: #fff; padding: 28px 0;
  border-bottom: 4px solid var(--accent);
}
.agegate .agegate-title {
  font-family: var(--font-display); font-weight: 600;
  font-size: clamp(1.15rem, 1rem + 0.6vw, 1.5rem);
  margin: 0 0 6px; color: #fff;
}
.agegate .agegate-lead { margin: 0 0 16px; font-size: 0.95rem; color: #fff; }
.agegate-row { display: flex; flex-wrap: wrap; gap: 10px; }
/* White on ink is the highest contrast pair available here, so the primary
   answer is the brightest thing in the band. */
.agegate .agegate-yes { background: #fff; color: var(--ink); font-weight: 600; }
.agegate .agegate-yes:hover { background: var(--canvas-alt); color: var(--ink); }
.agegate .agegate-some { background: transparent; color: #fff; box-shadow: inset 0 0 0 2px #fff; }
.agegate .agegate-some:hover { background: rgba(255, 255, 255, 0.14); color: #fff; }
.agegate .agegate-no { background: transparent; color: #fff; box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.55); }
.agegate .agegate-no:hover { background: rgba(255, 255, 255, 0.14); color: #fff; }
.agegate .agegate-note { margin: 14px 0 0; font-size: 0.8rem; opacity: 0.85; }

/* Tapping a CTA before answering sends the reader here and flashes the band,
   so the redirect reads as an answer to their tap rather than a dead button. */
.agegate-flash { animation: agegate-pulse 0.6s ease-in-out 2; }
@keyframes agegate-pulse {
  50% { background: var(--accent); }
}

.agegate-cats {
  border: 0; margin: 0 0 16px; padding: 0;
  display: flex; flex-wrap: wrap; gap: 8px 20px;
}
.agegate-cats legend { padding: 0; font-size: 0.8rem; text-transform: uppercase;
  letter-spacing: 0.08em; opacity: 0.85; margin-bottom: 8px; }
.agegate-cats label { display: inline-flex; align-items: center; gap: 7px; font-size: 0.9rem; }
.agegate-cats input { width: 17px; height: 17px; accent-color: var(--accent); }

/* Answered "yes": the whole gate goes. */
html.age-ok .agegate { display: none; }
/* Answered "no": content, CTA strip and the bot all go; only the denied
   panel remains. */
html.age-denied #agegate,
html.age-denied .site-content,
html.age-denied .bot-cta,
html.age-denied #chatbot-iframe { display: none; }
/* NOT YET ANSWERED — the case the first version missed. The widget carries
   its own corner launcher inside the iframe, so leaving the iframe on screen
   let a reader start chatting without ever answering the gate. Hide it until
   the answer exists; the pack reveals it by swapping the class. */
html.age-unknown #chatbot-iframe { display: none; }

/* --------------------------------------------------------------- tip cards */

.tips, .latest, .listing, .read-next { padding: 48px 0; }

.tip-grid {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: var(--gap);
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
}
.tip {
  background: var(--surface); border-radius: var(--radius); overflow: hidden;
  border: 1px solid var(--accent-soft);
}
.tip img {
  display: block; width: 100%; aspect-ratio: 3 / 2; object-fit: cover;
  background: var(--accent-soft);
}
.tip-eyebrow {
  margin: 14px 16px 2px; font-size: 0.75rem; font-weight: 500;
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--accent);
}
.tip-title {
  margin: 0 16px 16px; font-family: var(--font-display); color: var(--ink);
  font-weight: 500;
}

/* ------------------------------------------------------------ card grid
 * Live archive cards: Poppins 500 / 20px / --ink, and NO capitalisation —
 * only the single-article title is transformed. Reproduced exactly. */

.card-grid {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: 14px;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
}
.card { background: var(--surface); border: 1px solid var(--accent-soft); border-radius: var(--radius); }
.card > a { display: block; padding: 16px 18px; text-decoration: none; color: inherit; height: 100%; }
.card > a:hover { background: var(--canvas-alt); }
.card h2, .card h3 {
  margin: 0 0 6px; font-size: 1.25rem; font-weight: 500; color: var(--ink);
  line-height: 1.3;
}
.card p { margin: 0; font-size: 0.9rem; opacity: 0.85; }

.more { margin-top: 28px; }

/* ----------------------------------------------------------------- article */

.article { padding: 40px 0 8px; }

/* No text-transform on the article title. The live theme applies
   `capitalize` to .entry-title because its stored titles are lower case; ours
   were corrected at the source (2026-08-08, client approved), so the <title>,
   og:title, JSON-LD headline and visible h1 all read the same correct text.
   Re-adding `capitalize` would break it: it capitalises EVERY word, turning
   "Amarone della Valpolicella" into "Amarone Della Valpolicella". */

.breadcrumbs { font-size: 0.85rem; margin-bottom: 18px; color: var(--body); opacity: 0.85; }
.breadcrumbs a { color: inherit; }

.article-meta { font-size: 0.85rem; opacity: 0.7; margin: -0.4em 0 1.8em; }
.article-meta .dot { margin: 0 6px; }

.lead { font-size: 1.08em; }

/* The imported body. Scoped to .prose so WordPress markup can never restyle
   the page furniture around it. */
.prose > :first-child { margin-top: 0; }
.prose h2 { margin-top: 1.8em; }
.prose h3, .prose h4 { margin-top: 1.5em; color: var(--ink); }
.prose ul, .prose ol { margin: 0 0 1.2em; padding-left: 1.4em; }
.prose li { margin-bottom: 0.4em; }
.prose blockquote {
  margin: 1.5em 0; padding: 0.2em 0 0.2em 1.2em;
  border-left: 3px solid var(--accent); color: var(--ink);
}
.prose table { width: 100%; border-collapse: collapse; margin: 1.5em 0; }
.prose th, .prose td {
  border-bottom: 1px solid var(--accent-soft); padding: 8px 10px; text-align: left;
}
.prose hr { border: 0; border-top: 1px solid var(--accent-soft); margin: 2em 0; }
/* Long external links (Alko product pages) must not push the page sideways on
   a phone. */
.prose a { overflow-wrap: anywhere; }

/* ------------------------------------------------------------------ search */

.search-page { display: flex; gap: 8px; margin: 0 0 20px; }
.search-page input {
  flex: 1 1 auto; min-width: 0; font: inherit; padding: 10px 14px;
  border: 1px solid var(--accent-soft); border-radius: 999px;
  background: var(--surface); color: var(--ink);
}

/* ------------------------------------------------------------------ footer
 * Live: the same brand bar as the header, white text, LANGUAGES and LEGAL
 * columns, copyright line. */

.site-footer {
  background: var(--accent); color: var(--on-ink); margin-top: 56px;
  padding: 40px 0 96px;   /* the lower padding clears the fixed bot launcher */
  font-size: 0.95rem;
}
.footer-cols {
  display: grid; gap: 28px;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  margin-bottom: 28px;
}
.footer-cols h2 {
  color: var(--on-ink); font-size: 0.78rem; font-weight: 500;
  letter-spacing: 0.12em; text-transform: uppercase; margin-bottom: 10px;
  opacity: 0.85;
}
.footer-cols ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.footer-copy { margin: 0; opacity: 0.75; }

/* --------------------------------------------------------------- voice bot
 * Geometry copied from the live site, which is the shape the widget itself
 * expects: a 400x100 launcher that asks to be grown when opened. The pointer
 * queries are the widget's own contract — a fine pointer gets a panel, a
 * coarse one on a small screen gets the full viewport. */

#chatbot-iframe {
  position: fixed;
  right: 0;
  bottom: 0;
  display: flex;
  justify-content: end;
  border: 0;
  width: 400px;
  height: 100px;
  z-index: 60;
  /* Below 400px a fixed 400px box would give the document a horizontal
     scrollbar of its own. */
  max-width: 100vw;
}
@media screen and (pointer: fine) {
  #chatbot-iframe.open { width: 400px; height: calc(60vh + 251px); }
}
@media (pointer: coarse) and (max-width: 768px) {
  #chatbot-iframe.open {
    width: 100%; height: 100dvh; bottom: 0; right: 0; border-radius: 0;
  }
}

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