/*
 * Styles for the small set of behaviors rebuilt from scratch rather than reusing the
 * original vendor plugin (RevSlider hero, Getwid image slider, wp-image-zoooom product
 * zoom) — see IMPLEMENTATION_NOTES.md. Everything else on the site is styled by the
 * self-hosted original CSS files loaded in BaseLayout.
 */

/* --- Task 32: site-wide (every page, every language) — page content sat with a
 * ~20px gap from the true left/right viewport edges everywhere except the header
 * and footer, reported as making the site "look less clean." Root cause, found by
 * reading the theme cascade rather than guessing a container/padding value to
 * override: the vendor theme (twentytwenty/parent-style.min.css) gives every
 * *direct child* of `.entry-content` that isn't a WP "wide"/"full"-aligned block
 * `margin-left:auto;margin-right:auto` plus `width:calc(100% - 4rem)` — a
 * deliberate 40px (`4rem`, and `1rem=10px` under this theme's own
 * `html{font-size:62.5%}`) reservation that's a standard WordPress block-editor
 * convention (regular content gets breathing room; only explicitly "wide"/"full"
 * blocks reach the edges). The child theme (geppa/twentytwenty-style.min.css)
 * already overrides this rule's `max-width` to `unset`, but keeps that same
 * `width: calc(100% - 4rem)` — so the 40px reservation (auto-centered, i.e. ~20px
 * each side) survived into this rebuild untouched.
 *
 * Every page in this rebuild renders its content as `.entry-content`'s direct
 * child (`.grids-section` on the 4 main Grids-plugin pages and their EN/DE
 * equivalents; `.wines-row-container` on the wine category listing pages) — none
 * of these carry an `alignwide`/`alignfull`/`is-style-wide` class, so *all* of
 * them were caught by this rule, while `#site-header`/`#site-footer` (siblings of
 * `#site-content`, not descendants of `.entry-content` at all — see
 * `BaseLayout.astro`) were never subject to it, matching exactly the reported
 * "every page except header/footer" pattern. (Wine product detail pages use their
 * own `.wine-container`, not `.entry-content`, so they were never affected
 * either — confirmed by reading that page template, not assumed.)
 *
 * Fixed by overriding just the `width` this rule sets, back to the child's full
 * available `100%` — removing the reserved 40px rather than changing how it's
 * distributed. The selector below is a verbatim copy of the child theme's own
 * (`:root body .entry-content>*:not(...)`), which guarantees this wins: identical
 * specificity, and `site.css` loads last of all stylesheets (`BaseLayout.astro`).
 * Known side effect: `/it/privacy-policy/`'s one placeholder callout box (the
 * "Nota per il titolare del sito" warning, Task 6) is also a direct
 * `.entry-content` child with no alignment class, so its bordered box now spans
 * the full column width too instead of stopping 40px short — a presentational
 * width change only, its own text/border/background and all legal content are
 * untouched, so this doesn't need separate authorization under the frozen-legal-
 * content rule (which covers factual/legal text, not a generic sitewide spacing
 * fix that happens to reach this page too). --- */
:root
  body
  .entry-content
  > *:not(.alignwide):not(.alignfull):not(.alignleft):not(.alignright):not(
    .is-style-wide
  ) {
  width: 100%;
}

/* --- Header: logo permanently compact, pre-header bar only visible at the
 * top (site-wide, every page/language) ---
 * public/scripts/site.js's stickyHeader() toggles `.sticky` on #site-header
 * as soon as `window.scrollY > 0` (immediate, no threshold) — used both for
 * `position:fixed` pinning (unchanged) and, in the vendor theme
 * (twentytwenty-style.min.css), to shrink the logo (130px/80px mobile ->
 * 50px) and hide `.pre-header-inner` (the language-switcher/social-icons
 * bar). Per explicit user decision, the logo stays locked at its compact
 * 50px size unconditionally, at every scroll position — `!important` is
 * deliberate, not defensive: this exact header area already had one real
 * specificity-tie bug in this project (see Task 15's `#site-header.sticky`
 * fix in IMPLEMENTATION_NOTES.md), so an unambiguous win is preferred over
 * relying on selector-specificity/load-order alone.
 *
 * The pre-header bar is deliberately handled differently: an earlier round
 * force-hid it permanently, then force-showed it permanently, then narrowed
 * that to "visible only at the top" (matching the vendor default) — but
 * reported live as looking "buggy": the vendor's own `.sticky .pre-header-
 * inner{display:none}` hides it with `display`, which cannot be transitioned
 * by CSS at all, so it always was a hard, instant cut, no matter what
 * triggered it. Replaced with a `max-height`+`opacity` transition instead
 * (both are animatable), which requires overriding the vendor's `display:
 * none` with `display:flex!important` so the element stays in the layout for
 * the transition to actually animate between two states, rather than
 * vanishing from the layout tree the instant `.sticky` is added. `60px` is a
 * safely-larger-than-content estimate for the collapsed/expanded max-height
 * (its real content — `font-size:1.3rem` text plus small icons — is
 * comfortably under that), not an exact measurement; a max-height transition
 * doesn't require an exact value to look like a real slide, only one that
 * comfortably fits the content without clipping it at rest. --- */
/* --- Task 34 (logo half): reverted. This project tried two variants of a
 * bigger/centered logo tied to the pre-header's visibility — a two-row layout,
 * then (per explicit request to "try the alternative") an overlay where the
 * logo floated centered on top of the nav row — and both were rejected live
 * ("I do not like the big logo" / "this does not look good"). Per explicit
 * request to "go back to the compact logo that we always had on this project,"
 * reverted to exactly Task 25's original rule below: logo locked at its compact
 * 50px size unconditionally, at every scroll position and screen size, no
 * pre-header-tied variation. Per `CLAUDE.md`'s circuit-breaker rule (two live
 * rejections in a row for the same feature), this isn't a third incremental
 * variant to try next — if a bigger/centered logo is wanted again later, that
 * needs a fresh conversation about what specifically didn't work, not another
 * guess at positioning. The menu font/case change from this same task (below)
 * was not reported as disliked and stays as-is. --- */
header#site-header .header-inner .site-logo img {
  max-height: 50px !important;
}

/* --- Task 34 follow-up: desktop nav menu switched to normal sentence case,
 * matching the rest of the site's text, per explicit request ("swap the menu
 * font style to that of the rest of the site").
 *
 * The menu's actual rendered *font-family* was already `var(--body-font-family)`
 * (Proza Libre, the same font the rest of the site uses) before this change —
 * confirmed by tracing the full cascade, not assumed: `ul.primary-menu` itself
 * has a leftover `font-family:Merriweather` (style-custom.min.css), but
 * Merriweather is never actually loaded anywhere in this rebuild (no Google
 * Fonts link, no `@font-face`), and a more specific, later rule
 * (`ul.primary-menu li{font-family:var(--body-font-family)}`,
 * geppa/twentytwenty-style.min.css) sets the font directly on the `<li>` itself
 * — an element's own explicit declaration always wins over what it would
 * otherwise inherit from an ancestor, regardless of that ancestor's own
 * specificity, so the dead `Merriweather` declaration was never actually
 * reaching the rendered text. What *was* visibly different was
 * `text-transform:uppercase` (also from that same style-custom.min.css rule) —
 * confirmed with the user this was the real target before touching it, since
 * the font-family half of the original request turned out to already be true.
 *
 * Targets `ul.primary-menu` directly, not just an ancestor wrapper: the
 * `uppercase` rule is declared directly *on* that element (style-custom.min.css),
 * and an element's own explicit declaration always wins over whatever it would
 * otherwise inherit from an ancestor, regardless of the ancestor rule's own
 * specificity — so overriding only `.primary-menu-wrapper` (a parent of the
 * `<ul>`, not the `<ul>` itself) would have had no effect at all, the same
 * inheritance trap the font-family half of this investigation just uncovered.
 * `header#site-header` in the selector (rather than bare `ul.primary-menu`) is
 * only there to safely out-specificity style-custom.min.css's rule regardless
 * of load order, matching this file's usual approach in this same header area.
 * Once the `<ul>`'s own computed value is `none`, normal inheritance (nothing
 * else in the chain redeclares text-transform) carries it down through the
 * nested `.sub-menu` dropdown items too ("I nostri vini"'s children) without
 * needing a separate rule for them. The mobile hamburger menu is unaffected by
 * construction, not extra scoping — it's a structurally separate
 * `<ul class="modal-menu">` in the slide-out `.menu-modal`, not a descendant of
 * `ul.primary-menu` at all; changing its treatment too would be a separate
 * decision, not asked for here. --- */
header#site-header ul.primary-menu {
  text-transform: none;
}

header#site-header .pre-header-inner {
  overflow: hidden;
  max-height: 60px;
  opacity: 1;
  transition: max-height 0.35s ease, opacity 0.25s ease;
}
header#site-header.sticky .pre-header-inner {
  display: flex !important;
  max-height: 0;
  opacity: 0;
}

/* --- Task 43 round 8 (supersedes round 7's `transition:none` patch below,
 * removed): round 7 correctly found the *symptom's* timing but the wrong
 * mechanism. The user confirmed live that the jump/gap happens site-wide,
 * on every page, whenever the pre-header (language switcher) collapses on
 * first scroll — not just on wine pages (wine pages just also have their
 * own, still-unexplained, separate top-of-page whitespace on top of this).
 * Scoping the fix to wine pages only, as round 7 did, was therefore wrong
 * in scope, and killing the transition entirely (rather than fixing the
 * actual mismatch) would have thrown away Task 25/34's deliberate smooth
 * collapse everywhere it was applied.
 *
 * Real root cause: `#site-header` sits in `body`'s own CSS Grid
 * (`grid-area:header`, `twentytwenty-style.min.css`), and `.sticky` (site.js's
 * `stickyHeader()`, on the very first scroll) switches it to
 * `position:fixed` (vendor rule, `#site-header.sticky{position:fixed}`).
 * `position` cannot be CSS-transitioned — the switch to `fixed` removes the
 * header from grid flow *instantly*, in the same frame `.sticky` is added,
 * collapsing the grid's "header" row to 0 and instantly shifting all
 * following content (`#site-content`, `#site-footer`) up by the header's
 * *entire* previous height, well before `.pre-header-inner`'s own 0.35s
 * collapse (below) has even started. The animated collapse was only ever
 * changing the *fixed* header's own painted height after that instant
 * jump already happened — two effects, two different timings, on what
 * should be one smooth motion.
 *
 * Fixed at the actual source: `#site-header.sticky` now uses
 * `position:sticky` instead of `position:fixed`. A sticky element is never
 * removed from document/grid flow — it keeps contributing its own real,
 * current height to the grid's row sizing at all times, and only visually
 * pins to `top:0` once its natural position would scroll past that point
 * (functionally identical end state to `fixed` once scrolled — still reads
 * as "pinned to the top" — so Task 25/34's original intent, a header that
 * stays visible while scrolling, is unchanged). Because it's still in flow,
 * `.pre-header-inner`'s existing `max-height`/`opacity` transition now
 * *naturally* carries `#site-content` along with it frame-by-frame as it
 * animates — the grid row shrinks in step with the collapse instead of
 * jumping to its end state before the collapse even starts. No more
 * instant jump, on any page, and no per-page scoping needed. `z-index:1000`
 * (vendor, unchanged) still applies to `.sticky` regardless of `position`
 * value, so stacking above page content while pinned is unaffected. --- */
header#site-header.sticky {
  position: sticky;
}

/* --- Hero (replaces RevSlider) --- */
.hero-slider {
  position: relative;
  overflow: hidden;
  width: 100%;
  height: 520px;
}
/* --- Task 35: single looping background video, replacing the 4-photo
 * crossfading carousel that used to fill this same box (`.hero-slider__slide`/
 * `.hero-slider__dots` and their rules removed outright — nothing left to
 * paginate between with only one video, so nothing was left dead here). Sized
 * identically to how the old slides filled their box (`position:absolute;
 * inset:0`, full width/height, `object-fit:cover` doing the same job
 * `background-size:cover` did) — `.hero-slider`'s own height (520px desktop,
 * 420px mobile, or the Task 17 fixed-viewport override below) is unchanged and
 * still fully governs the visible box; the video just fills whatever that box
 * is, exactly like the photos did. No `object-position` override (the old
 * `background-position:center 30%` isn't reproduced here): that value was
 * tuned for the specific 4 product/vineyard photos it framed, not for this
 * new, unrelated video's own composition — worth checking once seen live and
 * adding an equivalent `object-position` then, if the video's subject needs
 * similar vertical framing help. --- */
/* Task 42: plain white fallback instead of the `poster` photo this element
 * originally had — see Hero.astro's own Task 42 comment for why. `background-
 * color` (not, say, a solid-white replacement image) since a real fallback
 * image was never actually needed here: the video fills this box edge-to-edge
 * (`inset:0`, `object-fit:cover`, same as below) the instant it can render a
 * frame, so the background only shows for the brief window before that, and a
 * flat color is both simpler and one less asset than a fake "blank" image
 * file. `#fff` to match `.loading-screen`'s own white (Task 13) — the same
 * white this box sits behind while the loading screen is still up, so there's
 * no visible seam between the two if the video happens to still be loading
 * exactly as the loading screen's reveal transition is mid-flight. */
.hero-slider__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  background-color: #fff;
}
@media (max-width: 768px) {
  /* Task 61: full-viewport hero on mobile, per explicit request — the video
   * now fills the screen exactly the way the desktop `.hero-fixed-wrapper`
   * calculation already does (screen height minus the header's own real
   * rendered height, so header+hero together fill exactly one screen on
   * load, with no extra scroll needed to see the video's bottom edge).
   * `--header-height` is now measured at every width by Hero.astro's
   * `initHeaderHeightVar()` (previously desktop-only) — the `0px` fallback
   * only matters for the brief instant before that script runs. `dvh`, not
   * `vh`, matching this project's established mobile-dynamic-toolbar
   * convention (this same component's own Task 17 sizing, the loading
   * screen's Task 30 reveal). Replaces the previous fixed 420px. */
  .hero-slider {
    height: calc(100dvh - var(--header-height, 0px));
  }
}

/* --- Task 17: full-viewport hero + fully-opaque, reversible white fade. See
 * .claude/TODO.md Task 17 for the full design; IMPLEMENTATION_NOTES.md for why
 * this replaces a fully-discarded prior attempt at the same effect.
 *
 * `--header-height` is set by Hero.astro's script from the header's own real
 * rendered height (not assumed) — `.hero-fixed-wrapper` reserves exactly that
 * much less than 100dvh of normal document-flow space, and `.hero-slider` (the
 * actual fixed, visible image) starts exactly `top: var(--header-height)`, so it
 * geometrically never overlaps the header at any screen size — no z-index
 * reasoning needed for that specific guarantee.
 *
 * `.home-content` (wraps everything after the hero, homepage only) uses its own
 * `background-color`, not a separate overlay element competing via z-index — an
 * element's own background is guaranteed by the CSS box model to always paint
 * behind its own content, which a separate overlay could never guarantee. That
 * pattern failed twice in the discarded prior attempt; this one hasn't.
 *
 * `body.home #site-footer` needs its own explicit position+z-index for a
 * different, easy-to-miss reason: `.hero-slider` is `position: fixed` with an
 * explicit (non-auto) z-index, and by the CSS2.1 painting order, *any* positioned
 * element with a specified z-index paints above *any* non-positioned in-flow
 * content — regardless of DOM order. Without this, the footer (which has an
 * opaque background but no position/z-index of its own) would render behind the
 * still-fixed hero once scrolled into that same screen region, hiding it
 * entirely. Scoped to `body.home` since the fixed hero only exists there. --- */
@media (min-width: 769px) {
  .hero-fixed-wrapper {
    position: relative;
    height: calc(100dvh - var(--header-height, 0px));
  }
  .hero-slider {
    position: fixed;
    top: var(--header-height, 0px);
    right: 0;
    bottom: 0;
    left: 0;
    width: auto;
    height: auto;
  }
  .home-content {
    position: relative;
    z-index: 500;
    background-color: transparent;
    transition: background-color 500ms ease;
  }
  html.is-hero-faded .home-content {
    background-color: #fff;
  }
  body.home #site-footer {
    position: relative;
    z-index: 500;
  }
}
/* Deliberately combined with min-width:769px, not a standalone
 * prefers-reduced-motion query: everything here only needs to undo what the
 * min-width:769px block above did, and nothing else in this file behaves
 * differently under reduced motion at narrow widths. A standalone
 * `@media (prefers-reduced-motion: reduce)` block setting `.hero-slider`'s height
 * back to 520px would incorrectly win a cascade tie against the max-width:768px
 * mobile rule above (420px) for narrow-screen reduced-motion users, since it
 * would come later in source order at equal specificity — this combined query
 * only ever matches at >=769px, where that mobile rule was never in play. */
@media (min-width: 769px) and (prefers-reduced-motion: reduce) {
  .hero-fixed-wrapper {
    height: auto;
  }
  .hero-slider {
    position: relative;
    top: auto;
    right: auto;
    bottom: auto;
    left: auto;
    height: 520px;
  }
  .home-content {
    position: static;
    background-color: transparent;
  }
}

/* --- Task 18 refinement: extra breathing room before "Dalla campagna al
 * bicchiere", so the scroll-jacking brake (see Hero.astro) has more runway
 * before engaging instead of triggering almost immediately after leaving the
 * hero. The section's own scraped inline styles resolve its top margin to 0
 * (--_gs-m-desktop's top value), so this simply gives it one instead.
 * `!important` for the same reason as the header-size fix in Task 16: a
 * deliberate, permanent override of the vendor default, not a defensive
 * measure against a specificity tie that could go either way — site.css loads
 * after the vendor grids CSS regardless.
 *
 * Both the brake's approach-zone check and Task 17's fade-trigger threshold
 * measure this section's live rendered position at runtime
 * (getBoundingClientRect()), not a hardcoded distance — so this one CSS change
 * is sufficient on its own; both effects automatically shift later to match,
 * with no JS changes needed. Desktop/tablet only, matching every other part of
 * this scroll experience — mobile has no fixed hero or brake effect to give
 * more runway to. */
@media (min-width: 769px) {
  .home-philosophy-section {
    margin-top: 280px !important;
  }
}

/* --- Task 20: "La nostra collezione" redesigned — centered text, then 6
 * category images laid out in a horizontal row acting as navigation buttons,
 * replacing the old 2-column layout (a rotating Getwid image-slider carousel
 * of individual wine products on one side, a separate vertical icon+text
 * category list on the other). See .claude/TODO.md Task 20 for the full
 * design and category/image mapping (verified against src/data/wines.json
 * and src/data/categories*.json, not guessed from filenames).
 *
 * Removing the carousel markup entirely orphaned the Getwid-slider CSS that
 * used to live here (`.wp-block-getwid-images-slider__wrapper`, `.getwid-
 * slider-arrow`) and its JS (site.js's enhanceImageSliders()) — confirmed via
 * grep that no page anywhere in src/content/main still uses that component,
 * so both were removed rather than left as dead code.
 *
 * `.collection-showcase` isn't grid-item-positioned the way the rest of this
 * section's scraped Grids markup is (it doesn't use the plugin's
 * --_ga-column/--_ga-row custom properties at all) — grid-column: 1 / -1
 * makes it span the full width of the parent .grids-s-w_i grid container
 * regardless of that container's own 12-column grid-template, sidestepping
 * any auto-placement ambiguity. The heading/paragraphs get a constrained,
 * centered max-width (readable line length); the category row is
 * deliberately NOT constrained the same way, so it can use the section's
 * full width instead of feeling cramped into a narrow column.
 *
 * `position: relative` is required, not decorative: this section's visible
 * background isn't painted by .grids-section/.grids-area at all — it comes
 * from a separate `position: absolute` `::before` pseudo-element on the
 * parent .grids-s-w_i (part of the Grids plugin's own CSS,
 * grids-frontend.min.css), with z-index: auto. Per the CSS painting-order
 * spec, a `position: static` element (the default, and what this had before
 * this fix) always paints *behind* a `position: absolute` element in the
 * same stacking context, regardless of DOM order — so without this,
 * .collection-showcase silently rendered underneath that pseudo-element's
 * opaque grey background, fully invisible. The scraped .grids-area markup
 * this replaced had `position: relative` for exactly this reason; this new
 * element needs the same property to get the same guarantee.
 *
 * The heading's `margin-left/right: auto !important` below is deliberate,
 * not defensive: the theme's own CSS (twentytwenty/parent-style.min.css) has
 * `body .entry-content h1, h2, h3, h4, h5 { margin-left: 0; margin-right: 0;
 * ... }` — that selector has *higher* specificity than a plain
 * `.collection-showcase h2` (an extra `body` element in the selector beats
 * ours on element count, even though we both have one class), so it silently
 * won the cascade and forced the heading back to margin: 0, left-aligned
 * instead of centered, regardless of the `margin: auto` below. No matching
 * override was found for `.entry-content p`, so the paragraphs don't need
 * the same `!important`. `font-size` on the heading needs it for the same
 * reason: the theme sets a bare `h2 { font-size: 36px }` (confirmed via
 * grep, not assumed) which a same-specificity-or-lower override could lose
 * to depending on load order. --- */
.collection-showcase {
  position: relative;
  grid-column: 1 / -1;
  grid-row: 1 / -1;
  text-align: center;
}
.collection-showcase h2,
.collection-showcase > p {
  max-width: 700px;
  margin-left: auto !important;
  margin-right: auto !important;
}
.collection-showcase h2 {
  font-size: 3rem !important;
}
.collection-showcase__categories {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 16px;
  margin-top: 40px;
}
.collection-showcase__category {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  width: 200px;
  text-decoration: none;
  color: inherit;
  transition: opacity 200ms ease;
}
.collection-showcase__category:hover {
  opacity: 0.75;
}
.collection-showcase__category img {
  width: 100%;
  aspect-ratio: 3 / 4;
  /* contain, not cover: the 6 source product photos have noticeably different
   * native framing (some have generous white space above/below the bottle,
   * some crop tight to it) — cover would crop each one to fill the box,
   * which is harmless for the loosely-framed ones but cuts directly into the
   * bottle itself for a tightly-framed one (confirmed live: Cabernet
   * Sauvignon). contain guarantees no image is ever cropped regardless of
   * its own proportions, at the cost of some showing a bit more surrounding
   * space than others — a smaller issue than clipping the subject. */
  object-fit: contain;
  border-radius: 4px;
}
.collection-showcase__category span {
  font-size: 0.8rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
@media (max-width: 768px) {
  .collection-showcase__category {
    width: 140px;
  }
}

/* Task 1 originally replaced wp-image-zoooom (the live site's real behavior is a
 * cursor-following magnifier lens via .zoomContainer/.zoomTint, confirmed by
 * inspecting the live product page) with a simple hover-scale transform. Per
 * Task 3, that's a different, unintended site-level zoom interaction rather than a
 * faithful reproduction, so it's removed outright — the image is static, matching
 * neither a fabricated effect nor a disproportionate jQuery-plugin reimplementation.
 */

/* --- Loading screen (Task 13, new — no original-site equivalent, homepage only) ---
 * A single full-viewport black panel, scaled from the horizontal center via
 * `transform: scaleX()`. Growing 0 -> 1 reads as "white to black, sliding from the
 * center to both sides" (the entry — unchanged since Task 13's original design).
 * The logo/spinner are separate elements with their own opacity transition so the
 * panel's scaleX never stretches/squishes them.
 * `.loading-screen` itself carries the opaque white background (not just the black
 * panel): the panel starts at scaleX(0) — zero width, covering nothing — so without
 * this, the real page underneath would show through until the panel grows, making
 * the entry read as "website to black" instead of "white to black". This background
 * is plain CSS present from first paint (no JS delay), so there's never a flash of
 * real content. It's switched to transparent the instant `.is-hiding` is added (see
 * below) — invisible at that moment since the panel is still at full black coverage
 * — so the reveal exposes the real page, not another white layer, as it recedes.
 *
 * Task 30: the *reveal* (only) redesigned from "shrink back toward center"
 * (scaleX 1->0) to a vertical slide-up, "so as to look like a scroll down" per
 * explicit request — the panel, logo, and spinner all translate upward together
 * and off the top of the viewport, rather than the panel alone shrinking
 * horizontally. The entry (scaleX growth) is untouched — only reveal changed.
 *
 * All three elements share the exact same `transform: translateY(-110dvh)` value
 * (one combined selector below, not three separately-typed numbers) and the same
 * `650ms ease-in-out` transition — a literal shared value is what actually
 * *guarantees* "the same speed" (distance ÷ time, both identical by construction)
 * rather than three independently-chosen numbers that merely look similar.
 * `-110dvh` (not `100dvh`): a small safety margin so no 1px sliver of the panel
 * can remain visible at the very top edge if a browser rounds the transition's
 * endpoint slightly short; `dvh` (not `vh`) for the same mobile-dynamic-toolbar
 * reasoning already established elsewhere in this codebase (Hero.astro's
 * `--header-height`/100dvh sizing) rather than introducing a second convention.
 *
 * The spinner needed restructuring into an outer wrapper (`.loading-screen__spinner`,
 * gets the translateY) plus an inner ring (`.loading-screen__spinner-ring`, keeps
 * the continuous rotation). Putting both `transform`s on one element would have
 * been wrong: a CSS `animation` and a CSS `transition` targeting the *same*
 * property on the *same* element don't combine — the animation wins outright and
 * the transition is simply never visible — so the rotating ring is structurally
 * isolated onto its own element instead of relying on any cascade-priority
 * behavior to "probably" work out. */
.loading-screen {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: #fff;
}
.loading-screen.is-hiding {
  background: transparent;
}
.loading-screen__panel {
  position: absolute;
  inset: 0;
  background: #000;
  transform: scaleX(0);
  transform-origin: center;
  transition: transform 350ms ease-in-out;
}
.loading-screen__logo {
  position: relative;
  z-index: 1;
  width: min(45vw, 220px);
  height: auto;
  opacity: 0;
  transition: opacity 400ms ease, transform 650ms ease-in-out;
}
/* Simple minimal spinner below the logo — plain border-ring technique (no extra
 * asset/SVG needed), white to read against the fully-black panel. Fades in
 * alongside the logo (same opacity transition/delay) rather than popping in
 * abruptly. Not gated separately behind prefers-reduced-motion: the whole
 * `.loading-screen` is already `display:none` for those visitors below, so this
 * spinner never renders/animates for them either. Split into an outer positioning
 * wrapper (`.loading-screen__spinner`, opacity + reveal transform) and an inner
 * ring (`.loading-screen__spinner-ring`, the visible border-ring + its continuous
 * rotation) — see the Task 30 comment above for why this split is structural, not
 * just tidiness. */
.loading-screen__spinner {
  position: relative;
  z-index: 1;
  width: 32px;
  height: 32px;
  margin-top: 24px;
  opacity: 0;
  transition: opacity 400ms ease, transform 650ms ease-in-out;
}
.loading-screen__spinner-ring {
  display: block;
  width: 100%;
  height: 100%;
  border: 3px solid rgba(255, 255, 255, 0.3);
  border-top-color: #fff;
  border-radius: 50%;
  animation: loading-screen-spin 0.8s linear infinite;
}
@keyframes loading-screen-spin {
  to {
    transform: rotate(360deg);
  }
}
.loading-screen.is-active .loading-screen__panel {
  transform: scaleX(1);
}
.loading-screen.is-active .loading-screen__logo {
  opacity: 1;
  transition-delay: 250ms;
}
.loading-screen.is-active .loading-screen__spinner {
  opacity: 1;
  transition-delay: 250ms;
}
/* Reveal: intentionally slower/more deliberate than the entry, per explicit request —
 * `.is-hiding` is added alongside removing `.is-active` (see LoadingScreen.astro).
 * Both durations were halved from their original 600ms/1200ms per an explicit
 * "speed up the sliding animations" request — the entry:exit 1:2 ratio (exit
 * slower/more deliberate) is preserved, just scaled down. Task 30: the exit
 * `transform` value changed from an implicit fallback to `scaleX(0)` to an
 * explicit vertical slide, shared identically across all 3 elements (see the
 * longer comment above `.loading-screen` for why this guarantees "same speed"). */
.loading-screen.is-hiding .loading-screen__panel,
.loading-screen.is-hiding .loading-screen__logo,
.loading-screen.is-hiding .loading-screen__spinner {
  transform: translateY(-110dvh);
}
.loading-screen.is-hiding .loading-screen__panel {
  transition-duration: 650ms;
}
@media (prefers-reduced-motion: reduce) {
  .loading-screen {
    display: none;
  }
}

/* --- Task 21: the homepage's closing section ("Un vino che esalta i sensi...")
 * — solid black background instead of the original rigoni-barique-riflessi-c.jpg
 * photo (swapped directly in the scraped inline custom properties, not here —
 * see home.html/en/home.html/de/home.html's --_gs-bg-* values), and the gap that
 * used to sit between the text/button group and the footer filled in.
 *
 * The text's own color (#f5efe0, from the theme's `.has-background-color` rule)
 * was already a light color chosen for the original dark photo background, so
 * it needed no adjustment for the new solid-black background — confirmed via
 * the theme CSS before assuming, not guessed.
 *
 * Two earlier attempts at filling the gap both grew the section's own box from
 * the inside (a flat `min-height: 70vh` guess, then a JS-measured min-height on
 * `.grids-s-w_i`) — both reported live as still leaving a gap. The likely real
 * cause, found by reading the theme CSS rather than guessing a third number:
 * `#site-content` (the shared wrapper around the entire page body, every page,
 * not homepage-specific) has `overflow: hidden` set on it
 * (twentytwenty-style.min.css / parent-style.min.css). Growing something deep
 * inside an overflow:hidden ancestor via min-height doesn't reliably repaint the
 * way normal block growth does, even when the style value itself is being set
 * correctly — a different failure class than "wrong number", not fixable by
 * guessing a bigger one.
 *
 * Rather than keep fighting that (or risk a site-wide `overflow` change, which
 * needs explicit approval given the blast radius — #site-content is shared by
 * every page), this fills the gap from the *other* side instead: `#site-footer`
 * is a sibling of `#site-content`, not a descendant, so it isn't subject to that
 * clipping at all. The `--closing-gap-fill` custom property that
 * `initClosingSectionFill()` in Hero.astro sets (see that script for the
 * measurement logic) still drives this — only *how* the footer extends into
 * that gap changed since.
 *
 * Found live (reported as a thin strip of the still-playing hero video visible
 * right at this exact seam, confirmed with a headless-browser probe —
 * `elementFromPoint` at that pixel returned the `<video>` itself, not this
 * fill): the original approach used a `#site-footer::before` pseudo-element
 * (`position: absolute`, no z-index of its own) to paint the gap. Its computed
 * height/background were both confirmed correct in isolation, yet it still lost
 * the paint order against the fixed-position hero `<video>` in practice — a
 * stacking-context subtlety (likely the pseudo-element not actually sharing
 * `#site-footer`'s own already-proven z-index:500 context the way a plain
 * child element would) that held even though the reasoning for why it *should*
 * have worked seemed sound. Rather than chase that further, switched to a
 * structural guarantee instead, per this project's own stated preference for
 * correctness that doesn't depend on winning a stacking fight: extend
 * `#site-footer`'s *own real box* into the gap (negative `margin-top` pulls
 * its top edge up by the gap amount; equal `padding-top` keeps its actual
 * content sitting exactly where it already was). `#site-footer`'s own
 * background is already `var(--footer-background-color)` (confirmed in
 * twentytwenty-style.min.css) — the exact same white this is meant to show —
 * so no color needs restating here. This is the *same* element already
 * confirmed (via `body.home #site-footer{position:relative;z-index:500}`,
 * Task 17/18) to correctly paint above the fixed hero everywhere else on this
 * page; it no longer depends on a second, independently-stacked element
 * replicating that guarantee. `!important` on both properties is required to
 * beat `footer#site-footer{margin:0!important;padding:6rem 0!important}`
 * (twentytwenty-style.min.css) — confirmed via computed-style, not assumed;
 * this selector's own specificity (one more class than that rule) already
 * wins unassisted, `!important` is only needed to out-rank that rule's own
 * `!important`, not for specificity. Scoped to the same
 * `@media (min-width: 769px)` block as before — on mobile the hero isn't
 * `position: fixed` either, so there's no bleed-through to cover there in the
 * first place. ---
 */
@media (min-width: 769px) {
  body.home #site-footer {
    margin-top: calc(-1 * var(--closing-gap-fill, 0px)) !important;
    padding-top: calc(6rem + var(--closing-gap-fill, 0px)) !important;
  }
}

/* --- Task 21 follow-up: the "cantina production" photo added to the left of
 * the closing section's text (same image already used on /it/cantina/ etc.,
 * right after this same "Un vino che esalta i sensi..." text there — see
 * IMPLEMENTATION_NOTES.md).
 *
 * The uncropped, natural-sizing version (matching "Dalla campagna al
 * bicchiere", see IMPLEMENTATION_NOTES.md) had a real, confirmed bug, not
 * just an appearance: `.grids-area`'s flex `align-items:stretch` stretches
 * the `<figure>` itself to 100% of the column width, but the plain `<img>`
 * inside it (inline, `max-width:100%;height:auto`, no explicit width) only
 * renders at its own intrinsic size when that's narrower than the figure —
 * and with no `text-align`/`margin:auto` centering it, it sits flush at the
 * figure's start edge, leaving asymmetric empty space on one side. Confirmed
 * by reading the actual cascade (figure's own stretch vs. the image's
 * separate, smaller intrinsic width), not assumed from the screenshot alone.
 *
 * Fixed by giving the `<img>` itself an explicit `width:100%` (so it always
 * fills its figure, the same guarantee the figure already had — no more
 * leftover asymmetric space to be off-center within) plus a fixed
 * `aspect-ratio` slightly narrower than the source photo's native ~3:2, so
 * `object-fit:cover` crops a bit off the left/right sides while keeping the
 * full bottle height in frame, per explicit request. `4/3` chosen as a
 * common, easily-reasoned-about ratio close to a "slight" crop of the
 * original ~3:2, not an arbitrary decimal. This also directly delivers the
 * "make it bigger" request: the image now reliably fills its full column
 * width rather than rendering at a smaller intrinsic size. --- */
.home-closing-section__image img {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

/* --- Task 31: on desktop/tablet, make the photo fill the closing section's full
 * black area top-to-bottom instead of sitting at its own fixed 4:3 crop with gaps
 * above/below. The area's own top/bottom margin (the gap) was removed directly in
 * home.html/en/home.html/de/home.html's `--_ga-m-desktop`/`--_ga-m-tablet`
 * inline values (was `50px 0 50px 0` / `50px 0 25px 0`, now `0 0 0 0`) — the
 * plugin's `.grids-is-advanced>.grids-s-w_i>.grids-area{height:100%}` rule
 * (grids-frontend.min.css) already stretches this area to the full row height
 * (which the text column's own 120px top margin + content drives), so once its
 * own margin is zeroed it naturally spans from the section's top padding to its
 * bottom padding — i.e. the visible black borders — with nothing extra to fix
 * here. `aspect-ratio` is dropped in favor of `height:100%` so the image tracks
 * that row height instead of a fixed ratio derived only from its own width.
 *
 * Filling that height at the column's original width alone made the photo look
 * like a narrow vertical strip, so the figure is widened by a fixed 80px past its
 * grid column and left to overflow (`overflow: visible`, the default — nothing
 * here clips it, and #site-content's own `overflow: hidden` only clips at the
 * page's outer edge, far past this) into the *text* column's own left portion.
 * That's a deliberate, low-risk target: the text column is `text-align: right`,
 * so its left portion is normally empty space anyway. `figure::after` fades the
 * rightmost 20% of the photo to black (the section's own background) so the seam
 * blends rather than hard-cutting under the text, and `.bottone-centro` gets an
 * explicit `z-index` so the text stays legible on the rare line that does cross
 * into the overlap (mainly the heading). Mobile is unchanged (stacks above the
 * text at its own aspect-ratio; no side-by-side overlap to manage there). --- */
@media (min-width: 769px) {
  .home-closing-section__image {
    overflow: visible;
  }
  .home-closing-section__image figure {
    position: relative;
    margin: 0;
    width: calc(100% + 80px);
    height: 100%;
  }
  .home-closing-section__image img {
    width: 100%;
    height: 100%;
    max-width: none;
    aspect-ratio: auto;
  }
  .home-closing-section__image figure::after {
    content: "";
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    width: 20%;
    background: linear-gradient(to right, transparent, #000);
    pointer-events: none;
  }
  .bottone-centro {
    position: relative;
    z-index: 1;
  }
}

/* --- Task 22 follow-up: gives the footer's "go to contacts" link the same
 * hover treatment as the top nav bar (`ul.primary-menu>li:hover` in
 * geppa/twentytwenty-style.min.css) — a light brown stripe across the bottom
 * of the item on hover. Reuses that exact gradient value (`#e2d1b4`, the
 * nav's own hover color — not `#DCC59F`, which that same stylesheet uses for
 * the nav's *active/current-page* state, a different color for a different
 * state) and the nav item's own `padding:5px`, rather than inventing new
 * values, so this reads as the same interaction pattern already established
 * elsewhere on the site. `display:inline-block` so the padding/background box
 * renders cleanly around the short text, the same way it does around a
 * nav `<li>`. --- */
.footer-contact-link {
  display: inline-block;
  padding: 5px;
}
.footer-contact-link:hover {
  background: linear-gradient(0deg, #e2d1b4 45%, rgba(255, 0, 255, 0) 30%);
}

/* --- Task 22 follow-up: the footer logo (swapped to the header/nav logo
 * earlier this task) made a bit smaller — display size reduced from 226x94 to
 * 170x71 in the markup, same aspect ratio — and pushed further from the top
 * of the footer's content column via margin-top. The theme's own
 * `.footer-row-logo{margin:auto}` (geppa/twentytwenty-style.min.css) resolves
 * top/bottom to 0 in normal block flow, so there was no spacing above it
 * beyond the footer's own sitewide top padding; this adds more on top of
 * that, specifically for the logo. Reduced from 40px to 20px per Task 25
 * follow-up feedback — no specific amount was given, so this is a reasonable
 * estimate (half), not a measured value. Reduced again to 8px (Task 28
 * follow-up, "reduce it more", another estimate) — kept a small positive
 * value rather than 0 so the logo doesn't sit flush against the footer's own
 * top padding.
 *
 * Task 28: user asked to swap the static footer logo for "an animation ...
 * most of the width of the viewport, but relatively thin vertically".
 * Presented 3 concrete options (scrolling wordmark marquee, shimmer sweep
 * over the existing logo, hand-drawn signature reveal — the last one flagged
 * as needing a vector logo that doesn't exist in this repo, only PNG/JPG) via
 * AskUserQuestion; user picked the shimmer sweep, which by its own preview
 * description keeps the exact current logo image unanimated and just adds a
 * moving light-glint over it — this doesn't by itself make anything "most of
 * the viewport width", so the container itself was widened to satisfy that
 * original stated requirement: `.footer-row-logo` is no longer sized to the
 * logo's own natural width, it's a wide (`min(90vw,1100px)`), thin band with
 * the unchanged logo centered inside it, and the shimmer gradient
 * (`::before`, `overflow:hidden` on the parent to clip it) sweeps across the
 * *entire band*, not just the logo's own pixels — this is what actually reads
 * as "most of the width", not the (still small, untouched) logo image itself.
 * `100deg` diagonal soft gold-toned gradient (`rgba(220,197,159,...)`, reusing
 * the site's own existing `--link-color-hover` tone rather than inventing a
 * new brand color) rather than plain white, since the footer background is
 * white (`--footer-background-color:#fff` in wp-global.css) and a white-on-
 * white glint would be close to invisible. `background-size:250% 100%`
 * animated via `background-position` (GPU-friendly, no layout thrash) loops
 * continuously; `prefers-reduced-motion` disables it entirely (the shimmer
 * removed, static logo remains) rather than just slowing it down, consistent
 * with this project's existing reduced-motion handling elsewhere (loading
 * screen, Task 17/18's hero effects).
 *
 * Follow-up: widened from `min(90vw,1100px)` to the full `100vw` per explicit
 * "make it take up all of the width of the viewport" request. `.footer-row-
 * logo` sits inside the footer's normally-centered, `max-width`-constrained
 * content column (`.section-inner{margin:0 auto;max-width:120rem}` in the
 * theme CSS) with no `overflow:hidden` on any ancestor between it and
 * `#site-footer` (checked directly, not assumed) — so the standard
 * `width:100vw` + `margin-left/right:calc(50% - 50vw)` full-bleed break-out
 * trick works cleanly here: it escapes the column's own max-width/padding to
 * touch both true viewport edges, without needing to restructure the
 * surrounding footer markup. --- */
.footer-row-logo {
  position: relative;
  overflow: hidden;
  width: 100vw;
  margin: 8px calc(50% - 50vw) 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
.footer-row-logo::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    100deg,
    transparent 35%,
    rgba(220, 197, 159, 0.55) 50%,
    transparent 65%
  );
  background-size: 250% 100%;
  background-repeat: no-repeat;
  animation: footer-logo-shimmer 5s ease-in-out infinite;
  pointer-events: none;
}
.footer-row-logo img {
  position: relative;
  z-index: 1;
}
/* --- Task 54: sideways-scroll fix, live device report on phone.
 *
 * Could NOT just add `overflow-x:hidden` to `html`/`body` as a blanket fix,
 * despite that being the usual reflex for this exact complaint — per the CSS
 * Overflow spec, setting `overflow-x` to anything but `visible` forces
 * `overflow-y` to also become non-`visible` on that *same* element (the
 * "paired axis" rule: an element can't have one axis `visible` and the
 * other not), and any ancestor with non-`visible` overflow breaks
 * `position:sticky` for anything inside it — which is the *exact* bug Task
 * 43 round 4 already found and fixed once this session
 * (`#site-content{overflow:hidden}` was silently breaking the wine bottle's
 * sticky behavior). `html`/`body` are ancestors of both sticky elements on
 * this site (the header, the wine bottle), so touching their overflow here
 * would have quietly undone two already-hard-won fixes.
 *
 * Root-caused instead: the vendor theme's own `#site-content{overflow:
 * hidden}` (twentytwenty-style.min.css) already clips horizontal overflow
 * inside the main content area on every page except wine pages (deliberately
 * — that's what the sticky bottle needs). Header and footer, both siblings
 * of `#site-content` at the body-grid level, have no such protection at
 * all. Grepping this file for `100vw` (the classic source of exactly this
 * bug — on platforms with a non-overlay scrollbar, `100vw` is wider than
 * the real visible viewport by the scrollbar's own width) found exactly one
 * hit: `.footer-row-logo`'s own full-bleed breakout, in the footer — present
 * on every page, matching a general/persistent complaint rather than a
 * single-page one.
 *
 * `footer#site-footer` is confirmed NOT an ancestor of either sticky element
 * (header and the wine bottle are both elsewhere in the grid), so it's safe
 * to clip here with no risk to either — unlike the header, which also
 * couldn't safely get a blanket `overflow-x:hidden` even on just itself,
 * since the desktop nav's hover dropdown (`ul.sub-menu`) needs to visually
 * extend past the header's own box, and the same paired-axis rule would clip
 * that too. --- */
footer#site-footer {
  overflow-x: hidden;
}
/* --- Task 54 round 2: the footer fix above was real but incomplete — a live
 * phone report ("sides are not locked") plus a temporary on-page overflow
 * diagnostic (site.js, since removed) found two different, unrelated culprits
 * neither previous fix touched:
 *
 * 1. The hamburger toggle (`.nav-toggle`, absolutely positioned `right:0`
 *    inside `.header-inner`) was overflowing because its own containing
 *    block was too wide, not because of anything in its own rules. Cause:
 *    the vendor theme only ever hides the desktop inline menu
 *    (`.header-navigation-wrapper` / `.primary-menu-wrapper`) behind
 *    `@media(max-width:782px){.admin-bar.overlay-header ...}` — a body-class
 *    combination from logged-in WordPress admin views that doesn't exist on
 *    this static rebuild — so the full desktop menu ("Azienda, Cantina, I
 *    nostri vini, Contatti, News") was never actually removed from layout on
 *    mobile at all, just visually redundant next to the modal menu. As a
 *    flex child of `.header-inner` with its own `width:100%` demand
 *    (`.header-navigation-wrapper{width:100%}`, twentytwenty-style.min.css),
 *    it forced `.header-inner` wider than the viewport, dragging the
 *    absolutely-positioned toggle's right edge past the true edge with it.
 *    Confirmed via the header partial (`src/content/chrome/header.html`)
 *    that `.header-navigation-wrapper` only ever holds the desktop
 *    `<nav class="primary-menu-wrapper">` — the mobile menu is a fully
 *    separate `.menu-modal` overlay elsewhere in the DOM, and the hamburger
 *    button itself lives in the sibling `.header-titles-wrapper` — so hiding
 *    this one wrapper below the theme's own already-established desktop
 *    breakpoint (`min-width:1000px`, the same value the theme itself uses to
 *    reveal it) cannot affect either the toggle button or the modal.
 *
 * 2. The homepage's philosophy section ("Dalla campagna al bicchiere") was
 *    overflowing by exactly 10px on the right on every one of its text
 *    elements. Cause: it's the *only* `.grids-section.alignfull` anywhere on
 *    the site with a non-zero margin (`0 10px 60px 10px`, confirmed by
 *    grepping every `alignfull` section in every content file) — but the
 *    Grids plugin's own CSS
 *    (`grids-frontend.min.css`) only ever wires its width-adjustment custom
 *    property to `min-width` (`min-width:var(--_gs-mw,...)`), a non-binding
 *    floor, never to an actual `width`/`max-width` cap. The theme's own
 *    `.alignfull{width:100%}` therefore always wins, so the section renders
 *    at full container width *and* keeps its own 10px+10px margin on top —
 *    the classic explicit-width-plus-explicit-margin overflow. Scoped to
 *    just this section (by its own existing `.home-philosophy-section`
 *    class) rather than touching the vendor plugin file, since no other
 *    section sitewide shares this margin+alignfull combination. --- */
@media (max-width: 999px) {
  .header-navigation-wrapper {
    display: none;
  }
}
.home-philosophy-section.alignfull {
  width: calc(100% - 20px);
}
/* --- Task 54 round 3: round 2's `.header-navigation-wrapper` fix above was
 * based on a misreading of the vendor CSS's brace nesting (a text-search
 * heuristic that found the nearest preceding `@media` without checking
 * whether that block had already closed) — the vendor already hides that
 * wrapper unconditionally, with no admin-bar/overlay-header gate at all, so
 * that fix was harmless but never the actual cause. Verified this round with
 * a real headless-browser measurement (Playwright now runs in this sandbox —
 * see IMPLEMENTATION_NOTES.md) instead of more text-parsing.
 *
 * Real cause, confirmed via getComputedStyle: the geppa child theme sets
 * `.nav-toggle .toggle-icon,.nav-toggle svg{width:70px;height:70px}` at
 * `@media(max-width:767px)` (twentytwenty-style.min.css) — but never widens
 * `.nav-toggle`'s own box to fit it. The parent theme's `.header-inner
 * .toggle{padding:0 2rem}` (parent-style.min.css, unconditional) still
 * applies underneath, giving `.nav-toggle` (width:6.6rem ≈ 79px, border-box)
 * 24px of padding on each side — leaving ~31px of actual content room for an
 * icon geppa sizes at 70px. The parent theme's own sizing for this exact
 * selector (`.8rem`/`2.6rem` ≈ 9.6px/31px) fits that 31px box exactly, which
 * is why this was never a problem until the child theme's later, bigger
 * override started winning the cascade without a matching box-size change.
 * Fixed by removing `.nav-toggle`'s own left/right padding at the same
 * breakpoint geppa uses, so the existing 70px icon it already sizes now
 * fits inside the existing 79px button with ~9px to spare — the icon's own
 * appearance is untouched, only the button's padding changes. Selector is
 * `.header-inner .nav-toggle`, not just `.nav-toggle`, to match the vendor
 * rule's own two-class specificity (`.header-inner .toggle`) — a plain
 * `.nav-toggle{padding:0}` compiled correctly but silently lost the cascade
 * to the more specific vendor selector regardless of source order, verified
 * directly via a real headless-browser computed-style check (see
 * IMPLEMENTATION_NOTES.md) rather than assumed from source order alone. --- */
@media (max-width: 767px) {
  .header-inner .nav-toggle {
    padding: 0;
  }
}

/* --- Task 63: hamburger icon (mobile only), per explicit request for
 * "black and more minimal, sleeker". The vendor's Font Awesome `fa-bars`
 * glyph (gold #dcc59f, 48px, geppa/style-custom.min.css) is fully replaced,
 * not just recolored — its `<i class="fas fa-bars">` markup was swapped for
 * 3 plain `<span class="toggle-icon__bar">` elements in header.html (all 3
 * languages, identical markup), so that vendor color/size rule now simply
 * matches nothing rather than needing to be beaten in the cascade.
 *
 * `!important` on width/height: the vendor's `.nav-toggle .toggle-icon,
 * .nav-toggle svg{width:70px;height:70px}` (same file, same breakpoint —
 * see the padding fix's own comment just above) still matches this element
 * too, since it keeps the base `.toggle-icon` class alongside the new
 * `--minimal` modifier. Equal-specificity selector + later source order
 * would likely already win, but this exact vendor rule has previously lost
 * to source-order assumptions once already (see that same comment) — a
 * deliberate, permanent override earns `!important` rather than trusting
 * cascade order again. Matches the vendor rule's own `max-width:767px`
 * breakpoint exactly, so there's no 1px gap where old/new sizing could both
 * apply or neither could. --- */
@media (max-width: 767px) {
  .nav-toggle .toggle-icon--minimal {
    width: 26px !important;
    height: 26px !important;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    gap: 6px;
  }
  .toggle-icon--minimal .toggle-icon__bar {
    display: block;
    width: 100%;
    height: 2px;
    background-color: #000;
    border-radius: 1px;
  }
}
@keyframes footer-logo-shimmer {
  0% {
    background-position: 150% 0;
  }
  100% {
    background-position: -50% 0;
  }
}
@media (prefers-reduced-motion: reduce) {
  .footer-row-logo::before {
    display: none;
  }
}

/* --- Task 22 follow-up: the "go to contacts" column looked vertically
 * off-center relative to the phone/email columns beside it — a real layout
 * bug, not just appearance. `.footer-icon-row{display:flex}` has no
 * `align-items` set, so its default (`stretch`) makes every `.footer-icon-
 * column` the same height as the tallest one (phone/email, which each have
 * an icon + text line). `.footer-icon-column` itself only centers its
 * content horizontally (`align-items:center` on its own cross axis); with no
 * `justify-content` set, its single line of content sits at the top of that
 * stretched box by default, rather than centered within the extra height —
 * unlike the phone/email columns, which are tall enough on their own that
 * this never showed. `.footer-icon-column` is shared/vendor CSS used
 * elsewhere too, so this is scoped to a second, added class on just this one
 * column instead of changing the shared rule. --- */
.footer-contact-column {
  justify-content: center;
}

/* --- Task 22 second follow-up: the `justify-content:center` fix above wasn't
 * enough on its own — found the real remaining cause by reading the cascade
 * further, not by guessing a second time. `twentytwenty/parent-style.min.css`
 * gives every h1/h2/h3 an asymmetric default margin,
 * `margin:6rem auto 3rem` (96px top, 48px bottom); the later, more specific
 * `body h2{...margin-left:0;margin-right:0}` rule (geppa/twentytwenty-
 * style.min.css) only ever zeroes the *left/right* margin, never top/bottom.
 * The contact link is the only one of the three footer columns built from an
 * `<h2>` (phone/email are plain `<div>`s, unaffected) — that leftover 96px
 * top margin pushed it down inside its own flex box, fighting the
 * `justify-content:center` fix above rather than being neutralized by it.
 * Zeroing it here, scoped to just this column's heading, lets the
 * `justify-content:center` centering actually work as intended. */
.footer-contact-column .wp-block-heading {
  margin: 0;
}

/* --- Task 22 second follow-up: the icon row didn't line up under the footer
 * logo either — a second, different bug, also confirmed from the CSS rather
 * than assumed. `.footer-icon-row{display:flex;justify-content:center}`
 * centers the *combined width* of its 3 children as a single block; that
 * only lines up with the row's true center — and thus the logo's own
 * centered position above it — when the flanking columns (phone, email) are
 * equal width. "Email: info@rigonivittorino.com" is visibly longer text than
 * "Tel. +39 0422.746459", so the block is lopsided and the middle column's
 * own center drifts off from the true center by roughly half that
 * difference. Switching to equal-width grid columns removes the dependency
 * on content length entirely — the middle column's center is then
 * guaranteed to match the row's center regardless of how long the phone or
 * email text is, a correct-by-construction fix rather than one that happens
 * to work for today's specific text. Scoped to `min-width:769px` to match
 * the vendor's own existing mobile breakpoint
 * (`@media(max-width:768px){.footer-icon-row{...display:inline-grid...}}`)
 * — without this scoping, this unconditional override would load after, and
 * therefore win over, that mobile-only rule and break the intentional
 * single-column mobile stacking. --- */
@media (min-width: 769px) {
  .footer-icon-row {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
  }
}

/* --- Task 62: WhatsApp/email icons (footer, every page, all 3 languages)
 * removed on mobile only, per explicit request — text (phone number, email
 * address) stays, only the icon graphic above it is hidden. `.footer-icon-
 * image` is the wrapper div around each `<img>` (not the `<img>` itself),
 * scoped to `.footer-icon-row` since that's the only place this class is
 * used sitewide (confirmed via grep). Desktop/tablet (≥769px) untouched. --- */
@media (max-width: 768px) {
  .footer-icon-row .footer-icon-image {
    display: none;
  }
  /* The vendor's own mobile rule (twentytwenty-style.min.css) gives
   * .footer-icon-text a 10px left indent specifically to clear the icon that
   * used to sit beside it — with the icon gone, that indent just reads as an
   * unexplained offset, so it's zeroed to match. */
  .footer-icon-text {
    padding-left: 0;
  }
}

/* --- Site-wide: disables the "rubber-band"/bounce overscroll effect (visible
 * on trackpads, e.g. Safari on macOS, and on mobile) where scrolling past the
 * very top or bottom of the page briefly reveals blank space beyond the
 * page's own content before springing back — a deliberate polish request, not
 * a bug fix. `overscroll-behavior` on `html` targets the real page-level
 * scroll root: this project's own scroll-jacking code (Hero.astro,
 * Task 17/18) already relies on `window.scrollY`/`window.scrollTo`, and that
 * code is confirmed working live, which only happens if `html`/`window` is
 * genuinely the scrolling context — not a nested wrapper div.
 *
 * `scroll-behavior: smooth` added as the deliberately low-complexity choice
 * over building any custom JS scroll-jacking/inertia effect — this only
 * affects *programmatic* scrolls (anchor-link jumps, any JS `scrollTo` on
 * the page) with an eased animation instead of an instant jump; ordinary
 * wheel/trackpad/touch scrolling is already natively smooth at the OS/browser
 * level and isn't something CSS or a reasonable amount of JS can meaningfully
 * improve on without re-implementing what the platform already does.
 * `prefers-reduced-motion: reduce` overrides it back to an instant jump,
 * matching every other motion effect on this page. --- */
html {
  overscroll-behavior-y: none;
  overscroll-behavior-x: none;
  scroll-behavior: smooth;
}
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
}

/* --- Task 25: "Dalla campagna al bicchiere" ("La nostra collezione" and "Un
 * vino che esalta i sensi..." were also reported as having a gap from the
 * viewport edge, but neither has any margin/width reduction of its own in
 * the scraped markup — `--_gs-m-desktop`/`-tablet`/`-mobile` are all `0 0 0
 * 0` for both — so this fix is scoped to the one section confirmed to
 * actually have a cause). This section's own `.grids-s-w_i` gets
 * `--_gs-m-desktop:0 10px 60px 10px` / same 10px left+right at tablet/mobile
 * (only the vertical 0px-top/60px-bottom values differ from that, and stay
 * untouched here) — a real 10px margin on each side, not just an appearance,
 * that leaves the page's own pale background (#f8f8f8) visible in a strip at
 * both edges instead of the section reaching them. Overriding only the
 * longhand `margin-left`/`margin-right` (not the shorthand `margin`, which
 * would also need to redeclare the intentional 60px bottom spacing) removes
 * exactly the side margins without touching that spacing. Applied at every
 * breakpoint since the same 10px side margin is present in all three
 * (desktop/tablet/mobile) values in the markup. --- */
.home-philosophy-section .grids-s-w_i {
  margin-left: 0;
  margin-right: 0;
}

/* --- Task 21 follow-up: positions the "Scopri di più"/"Find out more"
 * button in the "Un vino che esalta i sensi..." group at the exact same
 * distance below the paragraph as the title is above it — measured, not
 * estimated: the h2's own `margin-bottom` is `3rem` (theme rule
 * `h1,.heading-size-1,h2,...{margin:6rem auto 3rem}`, parent-style.min.css),
 * and this theme sets `html{font-size:62.5%}` — 62.5% of the 16px browser
 * default is 10px, so `1rem = 10px` here, making that margin exactly `30px`.
 * The paragraph itself contributes nothing on either side: the sitewide
 * base reset (`h1,h2,...,p,...{margin:0}`) is never re-added for `<p>`
 * anywhere in this theme (confirmed by grep, not assumed) — so the entire
 * visible title-to-text gap is that one 30px h2 margin, and the same 30px
 * applied here as the button's own `margin-top` reproduces it exactly, since
 * flex items (`.bottone-centro` is `display:flex`) don't margin-collapse
 * with each other the way block elements can.
 *
 * `.bottone-home` is a shared theme class (geppa/twentytwenty-style.min.css)
 * used by 2 buttons on this page — this one and the one in "Dalla campagna
 * al bicchiere" — so this stays scoped to `.bottone-centro .bottone-home`
 * specifically, not the bare `.bottone-home` class, which would move the
 * other button too. Superseded two earlier rounds here (a `-10px` "nudge up"
 * estimate, then `-20px`) — this is the first one grounded in an actual
 * measured value rather than a guess. --- */
.bottone-centro .bottone-home {
  margin-top: 30px;
}

/* --- Redesigns the shared "Scopri di più"/"Find out more" button (used
 * twice on the homepage — see IMPLEMENTATION_NOTES.md for the earlier
 * confirmation of exactly where) into an icon-only square: the arrow's own
 * grey background (`.bottone-home i{background-color:#403d3d}`, unchanged)
 * stays as the only visible background; the anchor's own two-tone gradient
 * background (which existed specifically to color the text portion
 * differently from the icon portion) is removed since there's no text
 * portion left to color differently.
 *
 * The label itself is *not* deleted from the markup — its `<span>` got
 * `class="screen-reader-text"` (this codebase's existing accessible-hiding
 * pattern, already used for the header logo's label) instead of `display:
 * none`, specifically because the icon (`<i>`) already has `aria-hidden=
 * "true"` — deleting the text outright would have left the button with no
 * accessible name at all for screen reader users, a real regression, not
 * hidden visually but silently broken for that guest.
 *
 * `width:auto!important` is required, not decorative: EN/DE give this
 * anchor its own inline `style="width:225px"`/`"268px"` (sized for their
 * longer translated button text) — only `!important` can override an inline
 * style from an external stylesheet.
 *
 * `.bottone-home i` gets an explicit equal width/height instead of relying
 * on its existing `padding:15px` + the icon glyph's own natural size to
 * happen to be square — Font Awesome's arrow-right glyph isn't actually
 * square, so that combination wouldn't reliably produce one. `48px` estimates
 * the icon's *previous* effective size (15px padding + ~18px glyph + 15px
 * padding), so the visible square stays close to its original scale rather
 * than changing size as a side effect of this fix.
 *
 * No positioning changes were made beyond this — `.bottone-centro a.bottone-
 * home{margin:auto}` (existing rule) already centers this specific button
 * within its own group, and the *other* instance (in "Dalla campagna al
 * bicchiere") has no such rule and stays exactly where it already sits;
 * shrinking the button doesn't change either mechanism, so each stays where
 * it currently sits, just visually simplified. --- */
.bottone-home,
.bottone-home:hover {
  background: none;
  width: auto !important;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.bottone-home i {
  width: 48px;
  height: 48px;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
}

/* --- Task 36: "label-only" wine cards on /spumanti/ and /bianchi/ (all 3
 * languages) — replaces the bottle photo + name-text display with just the
 * label image (the label already shows the name, so the text underneath was
 * dropped too — see WineCard.astro's own comment for how that's scoped).
 *
 * The label photos have wildly different native aspect ratios (measured, not
 * guessed: from ~0.66, tall, to ~2.09, wide, across the 12 supplied images) —
 * unlike the bottle photos this replaces, which were all near-identical tall
 * portraits and so never needed a shape-normalizing box. Reused the same
 * fixed-box + `object-fit:contain` technique already established (and
 * user-approved) for the homepage's own varying-aspect-ratio product photos
 * (Task 20's `.collection-showcase__category img`) rather than inventing a new
 * pattern: every card gets the same visible box size regardless of category or
 * language, and `contain` guarantees no label is ever cropped, at the cost of
 * some showing a bit more surrounding white space than others — the same
 * accepted tradeoff that pattern already established. `4/3` chosen as a
 * reasonable middle ground for this specific image set (most labels are
 * ~0.66-1.17, i.e. square-ish-to-tall; `4/3` leans slightly the other way so
 * the one very wide outlier, Pinot Nero Spumante at ~2.09, doesn't force an
 * unusually short box for every other card too).
 *
 * `max-width:320px` keeps the box from growing to an awkward size on wide
 * single-column mobile layouts (`.wine-list-container` drops to 1 column
 * there) — the grid's own column width has no other cap on this element.
 * Scoped to the `.wine-image--label` modifier class (added only when
 * `WineCard`'s `imageVariant="label"`, only passed by the spumanti/bianchi
 * category pages), so `.wine-image` on every other category's bottle-photo
 * cards is completely unaffected. --- */
.wine-image--label img {
  width: 100%;
  max-width: 320px;
  aspect-ratio: 4 / 3;
  object-fit: contain;
  margin: 0 auto;
}

/* --- Task 38: "/rossi/" (all 3 languages) wine cards — bigger, higher-
 * resolution bottle photos, no name-text underneath, 2-per-row instead of 3 on
 * tablet/laptop widths, per explicit request.
 *
 * Unlike the label photos (Task 38's `.wine-image--label`, above), these
 * are real bottle photos and all share one consistent native aspect ratio
 * (measured, not guessed: all 5 red wines' higher-resolution `productImage`
 * files are 637x1000, exactly the same ~0.637 ratio the old 191x300 thumbnail
 * already had — same photos, same crop, WordPress had just also generated a
 * much smaller version for card use). No `aspect-ratio`/`object-fit:contain`
 * box is needed for that reason: plain `width` + `height:auto` already
 * preserves proportions correctly with zero risk of cropping or letterboxing,
 * since every card in this category shares the same shape.
 *
 * `max-width:380px` roughly doubles the old default (unset width, rendered at
 * the `<img>`'s own 191px width attribute) — the actual increase in on-screen
 * size that makes the label printed on the bottle itself more readable, which
 * is what was actually asked for; the grid becoming 2-column below gives each
 * card the room to actually use that extra width rather than just growing the
 * gutters. --- */
.wine-image--bottle-large img {
  width: 100%;
  max-width: 380px;
  height: auto;
  margin: 0 auto;
}
/* `.wine-list-container` already becomes 2 columns at 768-878px in the vendor
 * CSS (twentytwenty-style.min.css) — only the >=879px rule (3 columns) needed
 * overriding to also show 2, so a single `min-width:768px` query covers both
 * ranges in one rule (redundant, harmless, at the already-2-column range).
 * Equal specificity to the vendor rules it overrides (one class each); wins on
 * source order alone since `site.css` loads last of every stylesheet
 * (`BaseLayout.astro`) — no `!important` needed. Scoped to the
 * `.wine-list-container--large` modifier class (added only for the "rossi"
 * category's own page template), so every other category's 3-column desktop
 * grid is unaffected. */
@media (min-width: 768px) {
  .wine-list-container--large {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* --- Task 40: native cross-document View Transition between the spumanti
 * category page's wine cards and each wine's own product page — the bottle
 * image morphs smoothly into its new position/size across the navigation
 * instead of an instant cut, per explicit request. Scoped to spumanti only for
 * now (other categories can follow later).
 *
 * No JS, no Astro ClientRouter/`astro:transitions` — this stays a fully
 * static, prerendered multi-page site. The browser itself handles everything:
 * `BaseLayout.astro`'s `enableViewTransitions` prop emits the one required
 * `<meta name="view-transition" content="same-origin">` tag on both the
 * spumanti category page and each of its 8 wines' own product pages (all 3
 * languages); `WineCard.astro`'s `viewTransitionName` prop and this same
 * template literal on the product page's own bottle `<img>`
 * (`view-transition-name: wine-bottle-${slug}`) give the *same* name to what
 * is, since Task 38/39, already the exact same underlying image file at two
 * different displayed sizes — so the browser's automatic old-snapshot/new-
 * snapshot interpolation has no source-image mismatch to paper over, just a
 * clean size/position morph. On browsers without cross-document View
 * Transition support (Safari, Firefox, as of this writing), the `<meta>` tag
 * and `view-transition-name` are simply inert — navigation still works
 * exactly as before, just without the animation; nothing to feature-detect or
 * guard here.
 *
 * The only CSS actually needed on this project's side is this reduced-motion
 * override: browsers don't automatically skip View Transition animations for
 * that preference, so this disables them explicitly, matching this project's
 * existing convention of fully turning motion off (not just slowing it down)
 * for `prefers-reduced-motion: reduce` elsewhere (loading screen, hero video,
 * Task 17/18's scroll effects) — reduced-motion visitors get an instant page
 * swap, same as browsers with no View Transition support at all. --- */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation: none !important;
  }
}

/* --- Task 40 follow-up (found while investigating Task 43's "empty space
 * below the header that disappears on scroll" report): the *root* view
 * transition — every pixel of the page EXCEPT the one named `wine-bottle-*`
 * element — was never explicitly styled above; it was left to the browser's
 * default, which doesn't just cross-fade opacity, it *animates the root
 * snapshot's own width/height* from the outgoing page's captured size to the
 * incoming page's size over the transition's duration. The spumanti category
 * page and a wine's own product page are very different heights, so that
 * default resize animation was mid-flight, for a few hundred ms, growing the
 * captured "new page" snapshot from a shorter starting height up to the
 * product page's real (taller) height — during which the real, already-fully-
 * laid-out document underneath doesn't yet show through the still-growing
 * snapshot, reading as a blank gap between the header and the content until
 * the snapshot finishes growing (or a scroll event forces the browser to
 * settle/repaint, which is exactly the "disappears when I scroll" behavior
 * reported). This was always inherent to Task 40's root cross-fade, not
 * something Task 43's later sticky-bottle CSS introduced — it just wasn't
 * noticed until this task's rounds of testing looked at product pages this
 * closely.
 *
 * Fixed the standard, documented way for this exact "differing page heights
 * make the default root transition look janky" case: disable the root
 * snapshots' own animation entirely, so the header/footer/page background
 * swap instantly (they're visually identical between the two pages anyway —
 * same shared chrome — so an instant swap there isn't a visible loss) while
 * leaving the *named* `wine-bottle-*` transition (the one actually asked
 * for) completely untouched — this selector only targets `(root)`, not the
 * wildcard `(*)` the reduced-motion rule above uses, so the bottle's own
 * `::view-transition-group(wine-bottle-*)` still animates exactly as
 * before. */
::view-transition-old(root),
::view-transition-new(root) {
  animation: none;
}

/* --- Task 43/44: wine product-page reorder. Built and approved for spumanti
 * only across rounds 1-8 (kept below, unedited, as the design history), then
 * extended to every wine category by explicit request ("implement the wine
 * page layout on all the other categories... that which we worked on in the
 * prosecchi e spumanti categories") once approved. All 28 wines across all 6
 * categories carry the same `tastingNoteParagraphs`/`vinificazione` fields
 * this layout needs (confirmed in wines.json, not assumed), so there was no
 * remaining reason to keep this spumanti-only.
 *
 * Every selector below that used to read `.wine-spumanti` (the class only
 * spumanti's `<article>` used to get) now reads `.single-wines` instead — the
 * class *every* wine product `<article>` carries unconditionally, regardless
 * of category (confirmed by grep across all 3 templates) — and the one rule
 * that needed to reach an ancestor of `<article>` (`#site-content`'s
 * `overflow` fix, since a body-level rule can't be scoped by a plain
 * descendant class on a page it doesn't yet know contains a wine article)
 * switched from a purpose-built `bodyClass="wine-spumanti"` prop to
 * `body:has(article.single-wines)` — the same `:has()` technique already
 * proven to work in this codebase (Task 43 round 7). The 3 templates no
 * longer add any spumanti-specific class or `bodyClass` at all. Nothing in
 * this block's actual values (spacing, sticky offsets, colors, italics)
 * changed — only what it's scoped to.
 *
 * This round's changes, on top of the previous ones:
 *
 * (1) "Keep all info on the right side (the column of the title)" —
 * `.wine-other-info` (the short remaining fields + PDF button) had been left
 * as a genuinely full-width block *below* `.wine-container` after the
 * previous round. It now renders inside `.description` too, right after
 * `WineTastingSection` (see the 3 product-page templates), so the entire
 * right-hand column — title, specs, flavor, process, remaining fields, PDF
 * button — is one column, with nothing full-width left on the page.
 *
 * (2) "Make the bottle sticky" — `.wine-image` (the grid cell, not the `img`
 * itself — sticky needs to apply to the actual grid item so it keeps its
 * normal-flow placement/size and only its *painted position* pins) gets
 * `position:sticky`, scoped under `.wine-spumanti` (new class on `<article>`,
 * set by `isSpumanti` in the templates) since `.wine-image` is otherwise an
 * unscoped selector shared by every category. This relies on a plain CSS
 * Grid default: `.wine-container` has one row spanning both `.wine-image` and
 * `.description` (`grid-template-rows:1fr`, both items `grid-row:1`), and
 * `align-items:stretch` (Grid's own default, not overridden anywhere in this
 * codebase) stretches every item's *box* to the row's height — which is now
 * tall, since `.description` carries far more content post-(1). That gives
 * `.wine-image` a tall cell to scroll through while its own sticky `img`
 * (still capped by the existing `.wine-image img{max-height:780px}`) stays
 * pinned inside it — the standard "sticky sidebar" pattern, no JS needed.
 * `top:100px` estimates the fixed sticky header's real height (confirmed from
 * twentytwenty-style.min.css: `.header-inner{padding:2rem 2rem 1rem}` +
 * `.sticky .site-logo img{max-height:50px}` ≈ 20+50+10=80px) plus ~20px
 * breathing room — flagged as an estimate since it can't be measured in a
 * live browser here. Desktop/tablet only (`min-width:769px`, matching this
 * page's own existing `max-width:768px` mobile-stack breakpoint) — below
 * that, `.wine-container` already stacks bottle-then-description in one
 * column, where a "sticky" bottle pinned above a long scrolling column would
 * just eat mobile screen space for no benefit, consistent with how this
 * project already turns off other desktop-only scroll/hover effects on
 * mobile. Per live follow-up ("make sure the bottle sits fully vertically in
 * the viewport... cannot be seen in full" otherwise), the image's own
 * max-height is additionally capped to `min(780px, calc(100vh - 120px))` in
 * this same scope/breakpoint — `100vh` is the real viewport height, so on any
 * screen short enough that 780px wouldn't fully fit below the ~100px sticky
 * offset, the image now scales down instead, guaranteeing the whole sticky
 * bottle is always visible at once rather than being cropped by the top or
 * bottom of the viewport.
 *
 * (3) "Make the flavor description sit on top of the V logo, not on its
 * right" — reverses the previous round's `float:left` text-wrap approach.
 * `.wine-tasting-section` is now `position:relative`; the logo is
 * `position:absolute;top:0;left:0` (a decorative layer, `pointer-events:none`
 * so it never intercepts clicks/selection on the text above it) and the text
 * block is `position:relative;z-index:1` so normal in-flow text paints over
 * the logo rather than wrapping beside it — the flavor paragraphs now overlap
 * the (already low-opacity, per the previous round's `filter:invert(1)`
 * fix) logo directly, matching "sit on top of" literally. No more float
 * means no more clearfix — `overflow:hidden` on `.wine-tasting-section` is
 * removed since it's no longer needed.
 *
 * (4) "Remove the grey background... leave the white background" —
 * `.description{background:#f8f8f8}` is a pre-existing, unscoped vendor rule
 * (twentytwenty-style.min.css) applied to every wine category's top section,
 * not something this task added — but now that the *entire* right column
 * lives inside `.description` (per (1)), that light-grey tint reads over much
 * more content than before. Overridden to `#fff`, scoped to `.wine-spumanti
 * .description` only — every other category keeps its `#f8f8f8` background
 * exactly as before.
 *
 * (5) "Give the flavor descriptions italic font style" —
 * `.wine-tasting-section__text p{font-style:italic}` added. Scoped to the
 * flavor paragraphs specifically, per the literal wording ("descriptions of
 * flavor") — `.wine-process-section p` (the winemaking-process paragraph)
 * keeps its existing upright style, unchanged, matching the earlier-confirmed
 * decision that the process text gets plain paragraph styling.
 *
 * `.wine-tasting-section__text p`/`.wine-process-section p` still reuse this
 * page's own "conventional" body-text values (`.description p{font-size:18px;
 * font-weight:400;color:#000;line-height:22px}`) as their base, per the
 * original "conventional text used by the site" request — italics is the one
 * deliberate departure from that base, per this round's explicit instruction.
 *
 * `.wine-other-info`'s padding is reduced from its previous full-width sizing
 * (`40px 60px 60px`) to a much smaller top-margin-only spacer now that it's
 * nested inside `.description` (which already supplies its own `padding:20px
 * 20% 30px 60px`) — doubling both would over-indent it the same way
 * `WineTastingSection`'s own padding was removed in the previous round for
 * the same reason.
 *
 * Round 6 (live: "add some space between the title of the wine, the
 * stickers and the text... just to give more of a premium feel"): modest
 * (+10px each) increases to two existing gaps — `.description h1`'s own
 * bottom margin (title → spec icons) and `.specs-row`'s own bottom margin
 * (spec icons → flavor text) — both are pre-existing, unscoped vendor rules
 * (twentytwenty-style.min.css) shared by every category, so both overrides
 * are scoped to `.wine-spumanti` like every other change in this task; every
 * other category's spacing is untouched. Deliberately small bumps, not a
 * new spacing system — "not a lot" was explicit. --- */
.single-wines .description h1 {
  margin-bottom: 25px;
}
.single-wines .specs-row {
  margin-bottom: 60px;
}
.wine-tasting-section {
  position: relative;
  padding-top: 20px;
}
.wine-tasting-section__logo {
  position: absolute;
  top: -20px;
  left: 0;
  width: 140px;
  z-index: 0;
  pointer-events: none;
}
.wine-tasting-section__logo img {
  width: 100%;
  height: auto;
  display: block;
  filter: invert(1);
  opacity: 0.2;
}
.wine-tasting-section__text {
  position: relative;
  z-index: 1;
}
.wine-tasting-section__text p {
  font-size: 18px;
  font-weight: 400;
  font-style: italic;
  color: #000;
  line-height: 22px;
  margin: 0 0 1em;
}
.wine-process-section {
  padding: 20px 0 0;
}
.wine-process-section p {
  font-size: 18px;
  font-weight: 400;
  color: #000;
  line-height: 22px;
  margin: 0;
}
.wine-other-info {
  padding: 30px 0 0;
}
.single-wines .description {
  background: #fff;
}

/* --- Task 45: serving-temp badge font, and hiding the remaining short fields +
 * PDF button behind an "other info" toggle.
 *
 * (1) The serving-temp badge's text (`p.contenuto-specs`, `.wine-grado`'s only
 * content) had its own leftover vendor font (`lustria`, weight 600,
 * twentytwenty-style.min.css) that didn't match anything else on the page.
 * Switched to `var(--body-font-family)` — the same variable the header menu
 * itself resolves to (confirmed: `ul.primary-menu li{font-family:var(--body-
 * font-family)}`, this file's own Task 34 comment above) — plus a normal
 * `font-weight:400` to match the menu's own (unbolded) weight rather than the
 * old 600. `.contenuto-specs` is confirmed unique to this one badge (grep
 * across `src/`), so no other element is affected.
 *
 * Also asked whether badge content (this text, and the glass/food SVG icons
 * in the other two badges) should switch to white when a category's own
 * badge-background color is dark enough to need it. Measured all 6 category
 * colors (twentytwenty-style.min.css: spumanti `#d0bca5`, bianchi `#dcc69f`,
 * rossi `#dc8384`, affinati `#bb6f70`, frizzanti-e-rosati `#b07b8d`, passiti
 * `#c89d84`) against the W3C contrast-ratio formula for both black and white
 * text/icons: black wins on every single one, including the more saturated
 * tones — none of them are actually "dark enough" in a strict accessibility
 * sense. Presented this to the user directly; they chose to keep black
 * everywhere over their own visual instinct. So no conditional-color rule
 * exists here — there's nothing in today's palette for it to ever switch on,
 * and a genuinely dark category color, if one's ever added, is its own future
 * styling decision rather than speculative machinery to build now.
 *
 * (2) `.wine-other-info` is now a native `<details>` element (see the 3
 * product-page templates) wrapping the short field list + "SCHEDA TECNICA"/
 * "PRODUCT SHEET"/"WEINBESCHREIBUNG" button, collapsed by default — no JS
 * needed, `<details>`/`<summary>` is a built-in, keyboard- and
 * screen-reader-accessible disclosure widget, consistent with this project's
 * general preference for native behavior over hand-rolled JS (see the sticky
 * bottle, also CSS-only). Per explicit live follow-up on how the toggle
 * should look ("minimal and elegant... just text, with an arrow that changes
 * direction... like a closed and open folder"): plain text, no background
 * fill, and a `>` character (round 2: swapped in for the original small
 * filled-triangle glyph, live feedback "I do not like the triangle... a
 * bigger icon that looks like this >" — literal `>`, sized up to 20px/700
 * weight so it reads as a clear icon rather than punctuation) that rotates
 * from pointing right (collapsed) to pointing down (expanded, a 90° turn) —
 * the browser's own default disclosure marker is removed (`::marker` for
 * Firefox, `::-webkit-details-marker` for Chrome/Safari) so only this one,
 * custom arrow shows. Round 2 also dropped `text-transform:uppercase` (and
 * the letter-spacing that went with it) per live feedback ("I do not want
 * the text to be in caps lock") — plain sentence-case label now. --- */
.contenuto-specs {
  font-family: var(--body-font-family);
  font-weight: 400;
}
.wine-other-info__toggle {
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--body-font-family);
  font-size: 14px;
  color: #000;
}
.wine-other-info__toggle::marker {
  content: "";
}
.wine-other-info__toggle::-webkit-details-marker {
  display: none;
}
.wine-other-info__toggle::after {
  content: ">";
  display: inline-block;
  font-size: 20px;
  font-weight: 700;
  line-height: 1;
  transition: transform 0.2s ease;
}
.wine-other-info[open] > .wine-other-info__toggle::after {
  transform: rotate(90deg);
}
.wine-other-info > .tipologia-row {
  margin-top: 20px;
}
/* --- Task 45 round 2 (live report: "the background of the button icon
 * overflows" on the SCHEDA TECNICA/PRODUCT SHEET/WEINBESCHREIBUNG button's
 * dark arrow square). Root cause: `.arrowBtn` (twentytwenty-style.min.css)
 * only sets `padding-top:22px` and `height:100%` — the *other* 3 padding
 * sides (right/bottom/left) fall through, unreset, to the theme's own
 * generic `button{padding:1.1em 1.44em}` reset (parent-style.min.css, shared
 * by every `<button>` on the site), sized for a *text* button, not a small
 * square icon button. Inside `.scheda-wine` (`display:grid`, this button's
 * only parent that actually gives `height:100%` a definite row height to
 * resolve against — the same class is reused unscoped in the wine-card list,
 * `WineCard.astro`, but there it sits in a plain, non-grid block, where
 * `height:100%` is a no-op and this exact conflict can't occur, which is why
 * this fix is scoped to `.scheda-wine .arrowBtn` only, not `.arrowBtn`
 * itself), that explicit `height:100%` combined with the icon's own
 * inherited padding/line-height needs more vertical room than the row
 * provides, so the button's box — and with it, its `#403d3d` background —
 * grows past the row it's meant to fit inside.
 *
 * Fixed by replacing the padding-top hack with `padding:0` (all 4 sides now
 * explicit, nothing left to inherit) plus `display:flex;align-items:center;
 * justify-content:center` to center the icon glyph regardless of the box's
 * exact height — a structural fix, not a re-tuned number, so it can't
 * reintroduce the same class of mismatch if the row's height ever changes
 * again for an unrelated reason. --- */
.scheda-wine .arrowBtn {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
}
@media (min-width: 769px) {
  /* Task 43 round 4: the sticky bottle wasn't sticking at all, live. Fixed
   * one real bug (`#site-content{overflow:hidden}` clipping the sticky
   * scrollport — see the removed comment below, still true and still
   * needed) but that alone wasn't enough, confirmed by round 5's repeat
   * report. The second, actual root cause: `.wine-container`'s default
   * `align-items: stretch` (CSS Grid's own default, never overridden
   * anywhere in this codebase) stretches `.wine-image`'s *own box* to match
   * `.description`'s full height, since both share the grid's one row. A
   * `position:sticky` element can only move within the *slack* between its
   * own box height and its container's — and a stretched box, by
   * definition, already equals its container's height, leaving zero slack.
   * With zero slack, sticky has nothing to do: the (now very tall) box just
   * scrolls normally with the page, and the image — rendered at the *top*
   * of that tall box by ordinary block flow — visually behaves exactly
   * like `position:static`. That's a precise match for "not sticky at
   * all", and it's independent of the earlier `overflow` bug — fixing only
   * the clip, as round 4 did, was necessary but not sufficient.
   *
   * Fixed with `align-self: start`, overriding the default stretch so
   * `.wine-image`'s box keeps its own natural (content-driven) height
   * instead of matching `.description`'s. Within the row's full height
   * (still `.description`'s height — the row itself isn't shrunk, only
   * this one item's box), that leaves real slack for sticky to move
   * through: the short image box now has room to hover near the top of the
   * viewport while the much taller `.description` column scrolls past it.
   * This is the standard, widely-documented "sticky sidebar next to long
   * content" pattern (Grid or Flexbox) — `align-items: flex-start` /
   * `align-self: start` overriding stretch is *always* required for it,
   * which earlier rounds missed. */
  body:has(article.single-wines) #site-content {
    overflow: visible;
  }
  .single-wines .wine-image {
    position: sticky;
    top: 100px;
    align-self: start;
  }
  .single-wines .wine-image img {
    max-height: min(780px, calc(100vh - 120px));
  }
}
@media (max-width: 768px) {
  .wine-tasting-section {
    padding-top: 15px;
  }
  /* --- Follow-up: previously reset to `position:static` here, which made
   * the logo render as its own separate block stacked *above* the tasting-
   * note text instead of overlapping *behind* it — reported live as wrong;
   * the intended look (documented in `WineTastingSection.astro`'s own
   * comment) is the desktop treatment: absolutely positioned, low z-index,
   * text layered in front. Removing the `position`/`margin`/`z-index`
   * overrides lets the base (desktop) rules apply unchanged at every
   * width — only `width` stays mobile-scoped, down from the desktop 140px
   * to something proportionate on a narrow screen. --- */
  .wine-tasting-section__logo {
    width: 80px;
  }
  .wine-other-info {
    padding: 20px 0 0;
  }
}

/* --- Task 46: /it/contatti/ (+ /en/contatti/, /de/contacts/) team section
 * redesign — explicit request: white page background instead of the current
 * grey, the 3 team members turned into rounded "cards" carrying that same
 * grey as their own background, all 3 visible side by side on desktop, one
 * at a time with a horizontal swipe-to-reveal on mobile, and more breathing
 * room between each card's photo/name/role/description.
 *
 * This section is one of the WordPress Grids-plugin's scraped layouts (same
 * mechanism as the homepage sections — see Task 17/18/20's own comments):
 * the visible background isn't a plain CSS property but a CSS custom
 * property (`--_gs-bg-desktop`, read by a `::before` pseudo-element in
 * grids-frontend.min.css) set *inline* on the section's own `style`
 * attribute in the scraped HTML — inline styles beat any external
 * stylesheet rule at equal-or-lower specificity, so overriding it needs
 * `!important`, not just a plain declaration.
 *
 * `.titolo-pagine` (the class carrying that grey) is *not* unique to this
 * page — chi-siamo.html and cantina.html both reuse it for their own title
 * sections — so a bare `.titolo-pagine` override would also whiten those
 * pages' backgrounds, well outside this task's scope. Each language's own
 * content file *does* have a unique WordPress post ID on its outermost
 * `<article>` (confirmed via grep: `post-953` IT, `post-1566` EN, `post-1770`
 * DE), which is used here to scope the override to exactly these 3 pages.
 *
 * `.wp-block-uagb-team.uagb-team__outer-wrap` (the 3 team-member blocks) and
 * everything nested inside it (`.uagb-team__image-wrap`, `.uagb-team__title`,
 * `.uagb-team__prefix`, `.uagb-team__desc`) *are* unique to this section
 * across the whole site (confirmed via grep — no other page's scraped
 * content uses this WordPress block), and every generic class name inside it
 * is identical across all 3 languages (only each instance's own
 * `uagb-block-<hash>` class differs, which nothing here needs to target) —
 * so the actual card styling below needs no per-language or per-post
 * scoping at all, just one set of rules shared by every card in every
 * language.
 *
 * Card sizing relies on a structural guarantee already built into the Grids
 * plugin rather than a re-tuned min-height: all 3 team `.grids-area` cells
 * share the exact same `--_ga-row:1/7` (spans the section's full 6-row grid
 * track) and the plugin's own `.grids-area{align-self:stretch;height:100%}`
 * rule, so the 3 cells are already forced to equal height by the grid
 * itself, regardless of how long each person's bio text is — `height:100%`
 * on `.uagb-team__outer-wrap` (its sole child) just carries that same full
 * height down onto the card's own visible box, so the rounded/colored
 * background actually fills the cell instead of hugging its content. --- */
#post-953 .titolo-pagine,
#post-1566 .titolo-pagine,
#post-1770 .titolo-pagine {
  --_gs-bg-desktop: #fff !important;
  --_gs-bg-tablet: #fff !important;
  --_gs-bg-mobile: #fff !important;
  /* Task 47: this section's own scraped -100px bottom margin (part of the
   * same overlap trick neutralized on the photo section further below) is
   * zeroed here too — negative margins between adjacent siblings collapse
   * by taking the most-negative value of the two, so zeroing only the
   * photo's own top margin wouldn't have been enough on its own; this side
   * needs the same treatment for the overlap to be fully gone. */
  --_gs-m-desktop: 0px 0 0 0 !important;
  --_gs-m-tablet: 0px 0 0 0 !important;
  --_gs-m-mobile: 0px 0 0 0 !important;
}
.wp-block-uagb-team.uagb-team__outer-wrap {
  background: #ede9e4;
  border-radius: 20px;
  padding: 50px 30px;
  height: 100%;
  box-sizing: border-box;
}
.wp-block-uagb-team.uagb-team__outer-wrap .uagb-team__image-wrap {
  margin-bottom: 20px;
}
.wp-block-uagb-team.uagb-team__outer-wrap h3.uagb-team__title {
  margin-bottom: 16px;
}
.wp-block-uagb-team.uagb-team__outer-wrap p.uagb-team__desc {
  margin-top: 20px;
}
/* --- Task 47: pin each card's email to the same height across all 3 cards,
 * "to give more of an ordered feeling" — explicit live follow-up. The email
 * isn't its own element; it's the last line of the bio `<p class="uagb-team__
 * desc">`, after a `<br>`. Rather than trying to equalize bio-text length
 * (which would mean rewriting around a layout constraint), the mailto link
 * is pulled out of flow entirely and anchored to a fixed `bottom` offset —
 * this only reliably lines up across all 3 cards *because* Task 46 already
 * forces the 3 cards to equal height (see that task's own comment on
 * `--_ga-row:1/7` + the Grids plugin's `align-self:stretch`), so `bottom:
 * 30px` resolves to the exact same real pixel position on every card
 * regardless of how long each person's own bio happens to run.
 * `padding-bottom` on the card reserves room so bio text of any length never
 * runs into the anchored email; `border-top` gives the email a quiet visual
 * separation from the bio above it, reading as the card's own "footer". --- */
.wp-block-uagb-team.uagb-team__outer-wrap {
  position: relative;
  padding-bottom: 76px;
}
.wp-block-uagb-team.uagb-team__outer-wrap .uagb-team__desc a[href^="mailto:"] {
  display: block;
  position: absolute;
  left: 30px;
  right: 30px;
  bottom: 30px;
  padding-top: 12px;
  border-top: 1px solid rgba(0, 0, 0, 0.12);
}
/* Mobile: swipe-to-reveal one card at a time, matching the visual idea of
 * the landing page's own scroll interaction ("the viewport sticks to that
 * one") via native CSS scroll-snap rather than reproducing Hero.astro's
 * JS-driven vertical scroll-jacking — that mechanism exists for a fixed,
 * full-viewport vertical hero, a genuinely different problem; a horizontal
 * swipe-through-cards is the textbook use case `scroll-snap-type` was
 * designed for, needs no JS, and behaves natively/correctly with touch
 * gestures, trackpads, and keyboard scrolling alike.
 *
 * `.grids-s-w_i:has(> .contatti-team)` targets specifically the one grid
 * wrapper that directly contains the 3 team cards (`.contatti-team` is a
 * second class already present, scraped as-is, only on the first card's own
 * `.grids-area`, but `:has()` needs just one matching child to select the
 * shared parent) — not `.grids-s-w_i` generally, which the Grids plugin
 * reuses as its generic wrapper class dozens of times across every page.
 * `!important` on `flex-direction` for the same inline-vs-stylesheet
 * reasoning as the background override above: the vendor's own mobile rule
 * (`.grids-s-w_i{display:flex;flex-direction:column;height:100%}`,
 * grids-frontend.min.css) is what stacks every other Grids section
 * vertically on mobile, and this is a deliberate, targeted exception to
 * just that one rule for just this one section. --- */
@media (max-width: 768px) {
  .grids-s-w_i:has(> .contatti-team) {
    flex-direction: row !important;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
  }
  .grids-s-w_i:has(> .contatti-team) > .grids-area {
    flex: 0 0 100%;
    scroll-snap-align: center;
  }
}
/* --- Task 49: the photo between the team cards and the contact-info/form
 * section is removed outright (explicit request) — the whole `grids-
 * section`/`grids-s-w_i`/`grids-area`/`figure` chain that held it is deleted
 * from all 3 content files, not just hidden, so Task 47's visibility fix and
 * Task 48's fade/resize rules for it are dead code and removed here rather
 * than left behind matching nothing.
 *
 * With the photo gone, "no blank space except for the necessary" between the
 * cards and the contact-info/form section means not stacking two paddings
 * for one gap: the form section's own `grids-section` already carries its
 * own `40px` top padding (untouched, original site content, its own
 * purpose independent of this task), so `.titolo-pagine`'s bottom padding
 * — previously trimmed to 20px in Task 48 to make room for the
 * now-removed photo — goes to 0. The one remaining gap is exactly the form
 * section's own pre-existing 40px, not two paddings added together.
 * `.titolo-pagine`'s direct-child `.grids-area` margin (top-of-page-to-
 * cards, Task 48's other fix) is untouched — that gap is unrelated to the
 * photo's removal. --- */
#post-953 .titolo-pagine > .grids-s-w_i > .grids-area,
#post-1566 .titolo-pagine > .grids-s-w_i > .grids-area,
#post-1770 .titolo-pagine > .grids-s-w_i > .grids-area {
  --_ga-m-desktop: 20px 0 20px 0 !important;
  --_ga-m-tablet: 20px 0 20px 0 !important;
  --_ga-m-mobile: 20px 0 20px 0 !important;
}
#post-953 .titolo-pagine,
#post-1566 .titolo-pagine,
#post-1770 .titolo-pagine {
  --_gs-p-desktop: 0 10px 0 10px !important;
  --_gs-p-tablet: 0 10px 0 10px !important;
  --_gs-p-mobile: 0 10px 0 10px !important;
}

/* --- Task 50: contact-section reflow, live follow-up.
 *
 * The intro paragraph ("Per informazioni..."/"For information..."/"Besuchen
 * Sie uns...") — originally moved out alongside the "Email:" line right
 * after it, since removed outright by Task 51 (below) — is pulled out of
 * the old left column and given its own full-width `.grids-area` (new
 * `.contatti-intro` marker class, hand-added to the scraped HTML the same
 * way `.contatti-team` already was) placed *before* the two-column row —
 * `--_ga-column:2/12` matches the combined span of both columns below it
 * (`2/7` + `8/12`), `--_ga-row:1/2` puts it in the grid's first row track,
 * with the hours/form columns shifted from `row:1/4` to `row:2/5` so they
 * sit in the row(s) after it instead of overlapping. The address block is
 * deleted outright (not hidden), and the now-orphaned spacer that used to
 * separate email/social from hours is dropped with it — its old job doesn't
 * exist anymore now that hours is the column's only content. --- */

/* --- Task 51: 3 follow-ups on Task 50's reflow.
 *
 * (1) The "Email:" paragraph under the intro is removed outright (deleted
 * from all 3 content files, not hidden) — the intro `.contatti-intro` area
 * now holds only the one paragraph.
 *
 * (2) Hours and form swap sides: only each element's own `--_ga-column`
 * value is swapped in the scraped HTML (`.contatti-hours` from `2/7` to
 * `8/12`, the form's own area from `8/12` to `2/7`) — DOM order is
 * untouched (hours still comes first in source), which is fine here since
 * `.contatti-hours` has no focusable content of its own to create a
 * confusing tab-order jump ahead of the form. Each element keeps its own
 * padding/spacing values as-is; those were tuned for the *content*, not
 * the column position, so nothing else needed to move with them.
 *
 * (3) "Move opening hours back to the top, instead of center" reverses
 * Task 50's `justify-content:center` (the vertical axis, the one "top" vs
 * "center" actually describes) back to `flex-start` — matching the vendor
 * base rule's own default (`grids-frontend.min.css`'s `justify-content:
 * var(--_ga-va,flex-start)`) in effect, just declared directly rather than
 * removing the override entirely, since this class still needs to exist for
 * `align-items:center` (horizontal centering, kept — "top instead of
 * center" only ever described the vertical position, not the horizontal
 * one, so there's no reason to also undo that). --- */
.contatti-hours {
  justify-content: flex-start;
  align-items: center;
}

/* --- Task 50: Google Map — bigger, and separated from the section above it.
 * `.uagb-google-map__iframe` is already `width:100%` (vendor base rule,
 * uagb-block-css.min.css) — its real displayed width already matches its
 * column, so "bigger" here means height, the axis that's actually fixed
 * (300px, a per-instance rule in each language's own extra-style CSS file);
 * overridden up to 480px. `.wp-block-uagb-google-map` is confirmed unique to
 * this one embed sitewide (no other page uses this WordPress block), so it
 * needs no post-ID scoping to stay safely out of chi-siamo/cantina's own
 * unrelated content — only the *section* it sits in needs that (it's an
 * unclassed, generic `grids-section`, so scoped the same structural way as
 * Task 47's own photo-section fix: `:has(> .wp-block-uagb-google-map)`
 * matches the section that directly contains this specific embed). Its
 * scraped `--_gs-p-desktop:0 0 0 0` (zero padding, top and bottom) is why
 * it had no separation from the section above at all — given real top
 * padding now. --- */
.uagb-google-map__iframe {
  height: 480px !important;
}
/* --- Task 53: the 480px map height above was never scoped to a breakpoint —
 * on a narrow phone the map (already `width:100%`) became a nearly-square,
 * screen-dominating block. Capped back down on mobile; 220px keeps a usable
 * map (still bigger than the original 300px felt on a full-width column
 * that's much narrower on a phone) without eating the whole viewport. --- */
@media (max-width: 768px) {
  .uagb-google-map__iframe {
    height: 220px !important;
  }
}
#post-953 .grids-section:has(> .wp-block-uagb-google-map),
#post-1566 .grids-section:has(> .wp-block-uagb-google-map),
#post-1770 .grids-section:has(> .wp-block-uagb-google-map) {
  --_gs-p-desktop: 40px 0 0 0 !important;
  --_gs-p-tablet: 40px 0 0 0 !important;
  --_gs-p-mobile: 40px 0 0 0 !important;
}

/* --- Task 52: breathing room below the intro paragraph, "to give more of a
 * premium feel" — live follow-up, same modest-bump convention already used
 * for this reason elsewhere on the site (e.g. Task 43 round 6's title/specs
 * spacing). `.contatti-intro`'s own padding was `0 10px 0 10px` (zero
 * bottom) since Task 50 first created it — this just adds the bottom value,
 * everything else unchanged. --- */
.contatti-intro {
  --_ga-p-desktop: 0 10px 30px 10px !important;
  --_ga-p-tablet: 0 10px 30px 10px !important;
  --_ga-p-mobile: 0 10px 30px 10px !important;
}

/* --- Task 55: mobile-only breathing room from the phone's own edges.
 *
 * Confirmed via a real headless-browser measurement (not assumed): every
 * page's main content container currently spans left=0 to right=0 of the
 * viewport at mobile widths — content touches the true screen edges on
 * every page type checked (contatti, chi-siamo, cantina, a wine category
 * page, a wine product page). This is Task 32's own doing: that task
 * removed a *desktop* 40px reservation sitewide (it looked bad on desktop
 * with no context for why it was there) via `.entry-content>*:not(...)
 * {width:100%}` — correct for desktop, but it also zeroed out mobile, where
 * the same "no breathing room" reads as content crammed against the phone's
 * bezel rather than "clean."
 *
 * Reuses Task 32's own selector verbatim (so this narrows to exactly the
 * same, already-audited set of elements — `.entry-content`'s direct
 * children, still excluding `.alignfull`/`.alignwide`/etc so hero
 * images/full-bleed sections stay edge-to-edge as designed) for the 4 main
 * Grids-plugin pages, plus the two other top-level content containers Task
 * 32's own comment already identified as using a different wrapper entirely
 * (`.wines-row-container` for wine category pages, `.wine-container` for
 * wine product pages — neither is a `.entry-content` child, so neither was
 * ever in scope for the selector above).
 *
 * `box-sizing:border-box` set explicitly on the two non-`.entry-content`
 * targets since their vendor-default box-sizing isn't relied upon — adding
 * padding to a `content-box` element would have widened it past the
 * viewport by the padding amount, the exact class of bug fixed sitewide in
 * Task 54. `.entry-content`'s own children already use `box-sizing:
 * border-box` (twentytwenty theme default), confirmed unaffected either
 * way since that branch uses `width`, not `padding`.
 *
 * Scoped to `max-width:768px` (this project's established mobile/tablet
 * collapse breakpoint, e.g. the Grids plugin's own single-column switch) so
 * desktop and wider-tablet layouts are completely untouched, matching what
 * was asked. --- */
@media (max-width: 768px) {
  :root
    body
    .entry-content
    > *:not(.alignwide):not(.alignfull):not(.alignleft):not(.alignright):not(
      .is-style-wide
    ) {
    width: calc(100% - 32px);
    margin-left: auto;
    margin-right: auto;
  }
  .wines-row-container,
  .wine-container {
    box-sizing: border-box;
    padding-left: 16px;
    padding-right: 16px;
  }
}
/* The homepage specifically never got the margin above: the geppa child
 * theme (`twentytwenty-style.min.css`) carries its own pre-existing rule —
 * `@media(max-width:767px){:root body.home .entry-content>*:not(...)
 * {width:auto}}` — one class more specific than the rule above (`body.home`
 * vs. plain `body`), so it always wins there regardless of load order.
 * Matching its exact selector (and its `767px` breakpoint, one pixel
 * narrower than the `768px` used above) is what actually beats it, found by
 * inspecting the real matched-rules list for `.home-closing-section`
 * (Chrome DevTools Protocol `CSS.getMatchedStylesForNode` via the headless
 * browser — see `CLAUDE.md`) rather than assuming source order would
 * decide it, the same specificity trap already hit once this session on
 * the nav-toggle fix (Task 54 round 3). */
@media (max-width: 767px) {
  :root
    body.home
    .entry-content
    > *:not(.alignwide):not(.alignfull):not(.alignleft):not(.alignright):not(
      .is-style-wide
    ) {
    width: calc(100% - 32px);
    margin-left: auto;
    margin-right: auto;
  }
}

/* --- Task 55: homepage "Un vino che esalta i sensi..." section (the
 * closing black band's right-hand text column, `.bottone-centro`) was using
 * its full desktop right-padding (150px) at every viewport width — reported
 * live as only occupying the left half of its section on mobile, with the
 * rest sitting empty. Confirmed via the actual inline style
 * (`src/content/main/home.html`) that `--_ga-p-desktop`, `--_ga-p-tablet`,
 * and `--_ga-p-mobile` are all identically `0 150px 0 0` — unlike its
 * sibling `.home-closing-section__image`, which *does* carry a genuinely
 * different, deliberate mobile value (8px vs. 48px desktop), this column's
 * "mobile" padding was never actually authored differently from desktop at
 * all, it was just copied through.
 *
 * At `max-width:768px` the Grids plugin already collapses this section's
 * grid to a stacked single column (`.grids-s-w_i{display:flex;flex-
 * direction:column}`, `grids-frontend.min.css`), so `.bottone-centro`
 * itself already renders full-width there — the 150px right padding was
 * the only thing still visually confining the heading/paragraph/button to
 * the left portion of that full-width box. Reduced to match the sibling
 * image's own already-established mobile inset (8px) for visual
 * consistency within the same section, at the same breakpoint the layout
 * itself already collapses at, so desktop/tablet (where the 2-column
 * layout — and the 150px gap that makes sense next to it — is unchanged)
 * are completely untouched, as asked. --- */
@media (max-width: 768px) {
  .bottone-centro {
    padding-right: 8px;
  }
}

/* --- Task 56 (mobile only, desktop/tablet untouched — every rule below is
 * gated behind `max-width:768px` and none of the selectors are shared with
 * any desktop-only rule elsewhere in this file):
 *
 * 1. Hero video → "Dalla campagna al bicchiere" gap. Measured (headless
 *    browser, not assumed) ~283px of real blank space between the hero and
 *    the section's title. Two real, separate contributors, found via
 *    `CSS.getMatchedStylesForNode`: (a) the empty first `.grids-section`
 *    (a spacer with no visible content at all — `--_ga-p-mobile:0px 0 40px
 *    0`) contributing 40px on its own; (b) the *vendor theme's* generic
 *    `.alignfull{margin-top:5rem}` rule (parent-style.min.css, applies to
 *    *any* full-width block, unconditional) — not the Grids plugin's own
 *    per-instance `--_gs-m-mobile` system at all, which is why overriding
 *    that custom property alone (as done for this same section in Task 54)
 *    never touched this margin. `5rem` = 60px under this theme's own
 *    `html{font-size:62.5%}`. Both reduced, not zeroed — the section still
 *    opens with its own decorative graphic before the title, so some
 *    breathing room from the video above it still reads as intentional
 *    spacing rather than a hard cut.
 * 2. Philosophy title/paragraphs/button "stuck to the right." Confirmed via
 *    computed style: the text column had `padding-left:80px;padding-right:
 *    0` (an unmodified desktop value, meant to sit the text beside a
 *    decorative graphic in the 2-column desktop layout) plus `text-align:
 *    left` — on mobile, where the layout has already collapsed to a single
 *    stacked column, that asymmetric padding pushed the text hard against
 *    the section's right edge with 80px of dead space on the left and
 *    nothing on the right. Replaced with a small symmetric inset and
 *    `text-align:center` (which also centers the button, an inline-block
 *    descendant, not just the text itself, matching what a "centered
 *    section" reads as visually).
 * 3. Grape-photo → "La nostra collezione" gap. Measured ~220px. Tracing it
 *    (`.grids-section`/`.section-news`'s own padding wasn't the whole
 *    story) found a second, systemic mechanism: the Grids plugin's CSS
 *    applies `padding:var(--_gs-p,var(--_gs-p-desktop,0))` and
 *    `margin:var(--_gs-m,var(--_gs-m-desktop,0))` on *both* `.grids-section`
 *    (the outer box) *and* its own inner `.grids-s-w_i` wrapper — the same
 *    custom property gets consumed twice, so a section's declared padding
 *    silently applies twice over. Overriding the outer box's `padding-top`
 *    directly (as first tried) only ever fixed half of that. The correct
 *    fix is overriding the custom property itself, `--_gs-p-desktop`
 *    (`!important`, matching this project's own established convention for
 *    the same reason as Task 16's header-size fix — a deliberate, permanent
 *    override, not a specificity hedge), since both consuming rules read
 *    from the same variable and both then shrink together. Reduced the
 *    philosophy section's `0 0 60px 0` (its own bottom padding — also
 *    double-applied) and the collection section's `100px 10px 100px 10px`
 *    top value, in each case leaving the other three sides untouched.
 * 4. Bottle photo → "Un vino che esalta i sensi" gap, plus a bottom fade.
 *    `.bottone-centro`'s own `margin-top:120px` (mobile value, unreduced
 *    from desktop where it makes sense pairing with a much taller 2-column
 *    section) was the entire gap — reduced substantially. The fade mirrors
 *    the *technique* of the existing desktop-only `figure::after` (Task 31,
 *    a horizontal fade for the overflow seam) but applied vertically to the
 *    bottom 20% instead, fading to the section's own black background so
 *    the now-tighter transition into the title reads as designed rather
 *    than an abrupt crop. --- */
@media (max-width: 768px) {
  body.home .entry-content > .grids-section:first-child {
    padding-bottom: 0;
  }
  .home-philosophy-section.alignfull {
    margin-top: 8px;
    --_gs-p-desktop: 0 0 8px 0 !important;
  }
  .home-philosophy-section .grids-s-w_i {
    padding-bottom: 8px !important;
    /* Task 61: this wrapper also double-consumes --_gs-m-mobile's bottom
     * value (60px, the same double-consumption mechanism Task 56 already
     * found for padding — see that task's own comment above) as a real
     * margin-bottom, on top of its padding-bottom. Zeroed as part of closing
     * the grape-photo -> "La nostra collezione" gap down to ~90% smaller. */
    margin-bottom: 0 !important;
  }
  .home-philosophy-section .grids-area:has(.home-philosophy-title) {
    padding-left: 16px;
    padding-right: 16px;
    text-align: center;
  }
  /* Task 61: the grape/vineyard photo's own bottom margin — 3rem (36px) from
   * the theme's generic un-aligned-block spacing rule
   * (.wp-block-image:not(.alignwide):not(.alignfull)...{margin-bottom:3rem}
   * in parent-style.min.css, shared by many block types sitewide) — is the
   * remaining contributor to the same gap the two rules above already
   * shrink. Scoped to this specific figure (not all wp-block-image figures)
   * so no other photo sitewide is affected. */
  .home-philosophy-section figure.wp-block-image {
    margin-bottom: 2px !important;
  }
  .section-news {
    --_gs-p-desktop: 20px 10px 100px 10px !important;
  }
  /* The custom-property override above is correctly inherited (confirmed via
   * getComputedStyle on the child) but the browser still renders the child's
   * own `padding-top` at the pre-override 100px — some interaction with how
   * this custom property's inheritance/initial-value resolves for this
   * unminified-in-source-but-inherited case that wasn't worth chasing
   * further. A direct, unambiguous override on the actual padding-applying
   * element sidesteps it entirely. */
  .section-news .grids-s-w_i {
    padding-top: 20px !important;
  }
  .bottone-centro {
    margin-top: 24px;
  }
  .home-closing-section__image figure {
    position: relative;
  }
  .home-closing-section__image figure::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 20%;
    background: linear-gradient(to bottom, transparent, #000);
    pointer-events: none;
  }
}

/* --- Task 57 (mobile only, `max-width:768px`, desktop/tablet untouched):
 *
 * 1. Hero video bottom-20% fade. Same technique as Task 56's bottle-photo
 *    fade (`.hero-slider` already has `position:relative;overflow:hidden`,
 *    so a `::after` with a bottom gradient just needs adding).
 * 2. Hero → philosophy gap, reduced further past Task 56's pass. That round
 *    found and fixed two contributors (an empty spacer's own padding, the
 *    vendor `.alignfull` rule's margin-top) but missed two more, only found
 *    this round by measuring every element between the hero and the section
 *    rather than stopping once the visible gap had shrunk some: (a)
 *    `.post-inner{padding-top:5rem}` (`parent-style.min.css`, unconditional
 *    at every viewport width — the theme *does* have its own zero-override
 *    for this, `.reduced-spacing.missing-post-thumbnail .post-inner
 *    {padding-top:0}`, but the homepage doesn't carry `missing-post-
 *    thumbnail` in its `bodyClass` — correctly so, since it has its own
 *    hero video — so the base 60px rule was still winning); (b) the empty
 *    spacer's own *inner* `.grids-area.content-grid` still had its own
 *    unreduced 40px bottom padding — Task 56 only touched the *outer*
 *    `.grids-section`'s padding, not this nested area, the same "two
 *    separate consuming elements" shape as the `.grids-s-w_i` discovery in
 *    that same task. Both scoped to `body.home` specifically — `.post-inner
 *    {padding-top:5rem}` is a *sitewide* base rule relied on by other pages
 *    (any page without `missing-post-thumbnail` needs its normal spacing
 *    below a hero image), so this reduction must not leak to them. --- */
@media (max-width: 768px) {
  .hero-slider {
    position: relative;
  }
  .hero-slider::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 20%;
    /* White, not black: unlike the closing section's bottle photo (fades
       into that section's own #000 background), the hero sits directly
       above the white page background, so it fades into that instead. */
    background: linear-gradient(to bottom, transparent, #fff);
    pointer-events: none;
  }
  body.home .post-inner {
    padding-top: 8px;
  }
  body.home .content-grid {
    padding-bottom: 0;
  }
}

/* --- Follow-up (mobile only, `max-width:768px`): white background for the
 * section holding the sticky V, tightened spacing around it, and a grey
 * (not white) V so it's still visible against that new white background.
 *
 * The section's cream background isn't a plain `background-color` on the
 * section itself — the Grids plugin paints it via a `.grids-s-w_i::before`
 * pseudo-element (confirmed via computed style, not assumed:
 * `getComputedStyle(el).backgroundColor` on the section itself reported
 * transparent; the real color only showed up checking the `::before`
 * pseudo). Overridden there directly, scoped via
 * `.titolo-pagine:has(.v-rigoni-bg)` — unique to chi-siamo's own title
 * section (this exact class/content combination doesn't occur on the
 * homepage's differently-structured philosophy section).
 *
 * The ~50px gap between that background's own edge and the V's visible top
 * (measured directly, not assumed) comes from the wrapping `.grids-area`'s
 * own margin (`--_ga-m-mobile:50px 0 50px 0` in the source content).
 * Reduced to 16px, matching this project's established small-mobile-inset
 * convention (Task 55/56), symmetrically top and bottom.
 *
 * For the grey V: rather than recolor the existing white asset with a CSS
 * filter, reused the homepage's own pre-made grey version of the same
 * graphic (`V-rigoni-grigio.png`, confirmed near-identical aspect ratio to
 * the white one already in use here) via `content:url(...)` on the `<img>`
 * — swaps the rendered image without touching the HTML content files in
 * any of the 3 languages, keeping this purely a mobile-scoped CSS change
 * rather than needing a `<picture>`/`srcset` rework to vary it by
 * viewport. Scoped with the same `img[src*="V-rigoni-bianco"]` check as
 * the sticky rule above, so only chi-siamo's own V (not the homepage's,
 * already grey) is affected. --- */
@media (max-width: 768px) {
  .grids-section.titolo-pagine:has(.v-rigoni-bg) > .grids-s-w_i::before {
    background: #fff !important;
  }
  .grids-section.titolo-pagine:has(.v-rigoni-bg) > .grids-s-w_i > .grids-area {
    margin-top: 16px;
    margin-bottom: 16px;
  }
  .v-rigoni-bg img[src*="V-rigoni-bianco"] {
    content: url("/wp-content/uploads/2020/12/V-rigoni-grigio.png");
  }
}

/* --- Follow-up (mobile only, `max-width:768px`): sticky removed entirely,
 * per explicit request — the V should sit in the text's own top-left
 * corner, not follow the scroll. Both live complaints (a large empty gap
 * between the V and the text before scrolling them "nearer" each other,
 * and the stickiness itself) shared one root cause: the V was a normal
 * in-flow sibling of the paragraph inside the same flex-column
 * (`.grids-s-w_i`), so it always occupied its own real vertical space
 * above the text — sticky only ever changed *when* that space stopped
 * scrolling, never the fact that it existed.
 *
 * Replaced with the same overlap technique already built and approved for
 * the wine product pages' own V logo (`.wine-tasting-section__logo`/
 * `__text` in this file): `position:absolute` takes the V out of flow
 * entirely, so the paragraph becomes the sole in-flow child and starts
 * right at the container's own top — no gap left to create the "buggy"
 * look, nothing to stick or release. `top:-20px;left:0` matches that same
 * wine-page rule's own offset for visual consistency between the two
 * "logo behind text" treatments on this site. `z-index:0` (behind) on the
 * V, `position:relative;z-index:1` (in front) on the paragraph sibling —
 * without the paragraph's own stacking context, its plain in-flow content
 * wouldn't reliably paint above an absolutely-positioned, z-indexed sibling
 * regardless of DOM order.
 *
 * `.grids-s-w_i` (the shared parent) already has `position:relative` from
 * the Grids plugin's own base rule, confirmed earlier this session — so it
 * already serves as this absolute V's containing block with no extra CSS
 * needed here. Width stays 90px, unchanged from the last round (not part
 * of this complaint). --- */
@media (max-width: 768px) {
  .v-rigoni-bg:has(img[src*="V-rigoni-bianco"]) {
    position: absolute;
    top: -20px;
    left: 0;
    width: 90px;
    z-index: 0;
  }
  .v-rigoni-bg:has(img[src*="V-rigoni-bianco"]) ~ .grids-area {
    position: relative;
    z-index: 1;
    /* Found via CDP's matched-styles list, not the Grids custom-property
     * system this time: the geppa theme's own vendor CSS carries
     * `@media(max-width:768px){.v-rigoni-bg+div{margin-top:-140px}}` —
     * a genuinely mobile-only rule (not a desktop value bleeding through),
     * presumably written for the original, simpler in-flow V design this
     * page had before any of this session's changes, pulling the text up
     * to overlap it. Left in place, it pulled the text 140px above where
     * the now-absolute V renders, putting the V *below* the text's start
     * instead of overlapping its top-left corner as intended. Neutralized
     * for this new layout specifically — desktop was never affected by the
     * vendor rule in the first place, confirmed via computed style. */
    margin-top: 0;
  }
}

/* --- cantina (+ EN `/en/winery/`, DE `/de/weinkeller/`) — remove the
 * "overflowing bottle" photo, mobile only. Its wrapping section carries
 * `--_gs-m-desktop:-280px 0 200px 0` — a large negative top margin,
 * identical at every breakpoint in the source markup, that deliberately
 * pulls it up to overlap the section above. Confirmed via a live
 * screenshot this reads as an intentional, clean full-bleed design on
 * desktop (a wide, short-relative-to-viewport overlap that looks
 * considered) but crops the same bottle awkwardly close and isolated on
 * mobile's much narrower, tighter layout — exactly "sits strangely."
 * Desktop is untouched (not reported as a problem there, and the
 * screenshot confirms it isn't one) — this is a `display:none` scoped to
 * `max-width:768px` only, not a content deletion, so nothing here affects
 * the approved desktop design or needs undoing if that reading changes
 * later. Scoped via the image's own *unsuffixed* src
 * (`cantina-rigoni-vittorino-produzione.jpg`, the full-resolution original)
 * rather than the bare filename, since the *homepage's* closing section
 * reuses the very same photo at a *sized* variant
 * (`cantina-rigoni-vittorino-produzione-1024x682.jpg`) — a same-filename,
 * different-src distinction confirmed directly in both pages' own markup,
 * not assumed, so this can't accidentally hide the homepage's own bottle
 * section (fixed and kept intentionally in Tasks 56-57). --- */
@media (max-width: 768px) {
  .grids-section:has(
      img[src="/wp-content/uploads/2021/01/cantina-rigoni-vittorino-produzione.jpg"]
    ) {
    display: none;
  }
}
