/* 
  Global Contract (Krea S.r.l.) - Premium Hero Stylesheet
  Vanilla CSS Custom Tokens, Layouts, and Micro-interactions
*/

/* -----------------------------------------
   0. SELF-HOSTED FONTS
   ----------------------------------------- */
/* WOFF2 first, TTF only as the fallback no current browser will reach.
   These were served as bare variable TTFs — 440KB + 484KB, uncompressed, and
   both fetched on every page because the italic face was listed unconditionally.
   WOFF2 is the same outlines with Brotli inside the font: 212KB and 236KB.
   The italic is used six times in the whole stylesheet, so `unicode-range`
   would not help — what keeps it off the critical path is that a face is only
   fetched once something on the page actually resolves to it. */
@font-face {
  font-family: 'Newsreader';
  src: url('../assets/fonts/newsreader/Newsreader-VariableFont_opsz,wght.woff2') format('woff2-variations'),
       url('../assets/fonts/newsreader/Newsreader-VariableFont_opsz,wght.woff2') format('woff2'),
       url('../assets/fonts/newsreader/Newsreader-VariableFont_opsz,wght.ttf') format('truetype');
  font-weight: 200 800;
  font-style: normal;
  font-display: swap;
}
/* SUBSET. The italic face is used for three accent phrases on the whole site
   ("c'è un metodo.", "risultati.", "al fianco"), and it was shipping the full
   236KB variable font to render them — on every page, to every device. This
   subset keeps both variable axes (wght, opsz) and a generous superset of the
   glyphs those phrases need (all Latin letters, the accented vowels Italian
   uses, digits and common punctuation), so an edit to the copy does not fall
   back to a synthesised oblique. 236KB -> 72KB.
   Regenerate with pyftsubset if the accent copy ever needs a glyph outside
   that set — see the note in project-logs, or just widen --text and re-run. */
@font-face {
  font-family: 'Newsreader';
  src: url('../assets/fonts/newsreader/Newsreader-Italic-subset.woff2') format('woff2-variations'),
       url('../assets/fonts/newsreader/Newsreader-Italic-subset.woff2') format('woff2'),
       url('../assets/fonts/newsreader/Newsreader-Italic-VariableFont_opsz,wght.ttf') format('truetype');
  font-weight: 200 800;
  font-style: italic;
  font-display: swap;
}
@font-face {
  font-family: 'General Sans';
  src: url('../assets/fonts/general-sans/GeneralSans-Variable.woff2') format('woff2-variations'),
       url('../assets/fonts/general-sans/GeneralSans-Variable.woff2') format('woff2'),
       url('../assets/fonts/general-sans/GeneralSans-Variable.ttf') format('truetype');
  font-weight: 200 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'General Sans';
  src: url('../assets/fonts/general-sans/GeneralSans-VariableItalic.woff2') format('woff2-variations'),
       url('../assets/fonts/general-sans/GeneralSans-VariableItalic.woff2') format('woff2'),
       url('../assets/fonts/general-sans/GeneralSans-VariableItalic.ttf') format('truetype');
  font-weight: 200 700;
  font-style: italic;
  font-display: swap;
}

/* -----------------------------------------
   1. DESIGN SYSTEM & TOKENS
   ----------------------------------------- */
:root {
  /* Colors */
  --color-magenta-deep: #B01E56;        /* Primary magenta sampled from logo */
  --color-magenta-glow: #E6236A;        /* Slightly brighter for accents/glow */
  --color-magenta-brand: #C9215A;       /* Literal brand magenta - reserved for primary actions only (hero + final CTA) */
  --color-bg-dark: #060608;             /* Rich luxury black background */
  --color-bg-dark-alt: #0C0C10;         /* Slightly lighter container dark */
  --color-white: #FFFFFF;
  --color-offwhite: #F5F5F7;
  --color-gray-text: #A1A1A6;
  --color-gray-dark: #2C2C2E;
  
  /* Fonts */
  --font-serif: 'Newsreader', Georgia, serif;
  --font-sans: 'General Sans', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;

  /* Legacy fonts, kept for home floating-cards (.card-category / .card-title) — do not repurpose */
  --font-serif-cards: 'Cormorant Garamond', Georgia, serif;
  --font-sans-cards: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  
  /* Transitions */
  --transition-smooth: all 0.6s cubic-bezier(0.16, 1, 0.3, 1);
  --transition-fast: all 0.3s ease;
  
  /* Z-Index */
  --z-bg: 1;
  --z-content: 5;
  --z-header: 100;
  --z-loader: 200;

  /* Client marquee strip height - shared with .hero-section's height calc
     (below) so hero + marquee always sum to exactly 100vh: the marquee is
     guaranteed visible without scrolling in the first viewport. */
  --gc-marquee-h: clamp(48px, 6vw, 64px);
}

/* Reset & Base */
*, *::before, *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html {
  scroll-behavior: auto; /* Required for Lenis smooth scroll */
  background-color: var(--color-bg-dark);
  color: var(--color-white);
  font-family: var(--font-sans);
  overflow-x: hidden;
  /* Belt-and-braces against the same class of bug as the overflow-x above,
     for touch specifically: `overflow-x: hidden` on the root clips PAINT,
     but on iOS/Android a horizontal drag can still pan the VISUAL viewport
     sideways past whatever briefly overflows (a marquee frame, a grid row
     a fraction of a px too wide) before the clip catches up - reported as
     a black/blank strip draggable in from the right edge. This site has no
     intentional horizontal interaction anywhere, so disabling the gesture
     outright is free: pan-y still allows vertical scroll and pinch-zoom,
     it only refuses to let the browser interpret a horizontal drag as a
     pan. overscroll-behavior-x is the same idea for the rubber-band
     bounce at the edges once a drag does register. */
  touch-action: pan-y;
  overscroll-behavior-x: none;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* 2026-08-19 (restyle-agenti, resize jitter fix): the browser's default
     CSS scroll-anchoring heuristic tries to "compensate" scroll position
     when off-screen content resizes. A first isolated test (forcing this
     off via the console, one run) appeared to eliminate a scroll jump
     crossing 768px entirely, but repeated multi-run testing afterward
     showed that result was a fluke - anchoring measurably changes the
     glitch's magnitude/landing value but is not its sole cause (js/main.js's
     resize fix #9 - a synchronous 'scroll' event guard - is what actually
     closed it down to a single frame). Kept anyway: it's a real contributing
     factor, Lenis already drives all deliberate scrolling so the browser's
     own compensation heuristic serves no purpose here, and disabling it
     costs nothing. */
  overflow-anchor: none;
}

body {
  touch-action: pan-y;
  overscroll-behavior-x: none;
  /* NO overflow-x here. On body, `overflow-x: hidden` forces the browser to
     compute `overflow-y: auto` (CSS overflow interop rule: one axis
     non-visible => the other becomes a scroll value), which makes BODY a
     scroll container. A scroll container ancestor silently kills
     `position: sticky` on every descendant — that is what left the whole
     #build-stage method section as ~4 screens of empty black on iOS Safari:
     the section scrolled away instead of pinning, so the reader scrolled
     through an empty runway and nothing responded.
     Horizontal overflow is still clipped: `overflow-x: hidden` on the ROOT
     element (html, above) propagates to the viewport, and viewport-level
     clipping does not create a scroll container on body. */
  position: relative;
  background-color: #050505; /* Deeper black for architectural high contrast */
  background-image:
    radial-gradient(circle at 90% 10%, rgba(230, 218, 202, 0.055) 0%, rgba(230, 35, 106, 0.015) 35%, transparent 70%); /* Ultra-subtle upper-right light cast with gentle magenta hints */
  background-attachment: fixed;
  /* overflow-anchor isn't inherited - see the html{} rule above for why
     this is disabled (resize jitter fix). */
  overflow-anchor: none;
}

/* Background-scroll lock while the fullscreen consult form is open
   (js/main.js's expand-form controller toggles this on <html>). Plain
   overflow:hidden, not lenis.stop() - Lenis keeps ticking underneath with
   nothing to move (window.scrollTo becomes a no-op while overflow is
   hidden), so it never needs re-initializing when the lock lifts.
   --gc-scrollbar-width (set by the same controller, measured as
   window.innerWidth - document.documentElement.clientWidth right before
   locking) compensates the native scrollbar's own width with matching
   padding, so removing it doesn't reflow the page's content by that same
   width - without this, opening/closing visibly shifted everything under
   the form sideways on any desktop browser with a classic (non-overlay)
   scrollbar. */
/* Resize-drag freeze (separate from gc-scroll-locked above - kept as its
   own class so a resize happening while the consult form is open can't
   race/collide with that controller's own lock/unlock calls). Same
   overflow:hidden no-op-scroll mechanism, applied for the duration of an
   active resize burst only (js/main.js) - no scrollbar-width padding
   compensation needed here, since the viewport is already visibly
   resizing the whole time this is active. */
html.gc-resize-locked,
html.gc-resize-locked body {
  overflow: hidden;
}

html.gc-scroll-locked,
html.gc-scroll-locked body {
  overflow: hidden;
}
html.gc-scroll-locked body {
  padding-right: var(--gc-scrollbar-width, 0px);
}

/* iOS/Android: a fixed background attachment on <body> forces the engine to
   re-rasterise the gradient against the viewport on every scroll frame, and
   iOS Safari does not composite it on its own layer the way desktop does —
   it is one of the cheapest wins available on a page that is already asking
   a phone to scrub a canvas. The gradient is a barely-visible light cast, so
   scrolling with the page instead of behind it is not a visible change. */
@media (max-width: 768px), (pointer: coarse) {
  body {
    background-attachment: scroll;
  }
}

/* iOS/Android: a fixed background attachment on <body> forces the engine to
   re-rasterise the gradient against the viewport on every scroll frame, and
   iOS Safari does not composite it on its own layer the way desktop does —
   it is one of the cheapest wins available on a page that is already asking
   a phone to scrub a canvas. The gradient is a barely-visible light cast, so
   scrolling with the page instead of behind it is not a visible change. */
@media (max-width: 768px), (pointer: coarse) {
  body {
    background-attachment: scroll;
  }
}

/* Custom Scrollbar */
::-webkit-scrollbar {
  width: 8px;
}
::-webkit-scrollbar-track {
  background: var(--color-bg-dark);
}
::-webkit-scrollbar-thumb {
  background: var(--color-gray-dark);
  border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
  background: var(--color-magenta-deep);
}

/* Lenis Scroll Tweaks */
html.lenis, html.lenis body {
  height: auto;
}
.lenis-smooth {
  scroll-behavior: auto !important;
}
.lenis-smooth [data-lenis-prevent] {
  overscroll-behavior: contain;
}
.lenis-stopped {
  overflow: hidden;
}

/* -----------------------------------------
   2. AMBIENT GLOW ORBS
   ----------------------------------------- */
/* ==========================================================================
   ANIMAZIONI INFINITE — in pausa quando non si vedono
   ==========================================================================

   Un'animazione CSS `infinite` tiene sporco il proprio layer per sempre: il
   browser continua a produrre frame per essa anche quando l'elemento e'
   ottomila pixel sopra lo schermo. Sono decorazioni — la freccia "SCORRI"
   della hero, il nastro dei marchi — e non c'e' motivo di pagarle mentre il
   lettore sta scorrendo tutt'altra parte della pagina.

   La classe la mette e la toglie un IntersectionObserver in js/main.js. Il
   selettore deve arrivare anche allo pseudo-elemento, perche'
   `animation-play-state` non si eredita dal genitore allo ::after. */
.gc-anim-pausa,
.gc-anim-pausa::after,
.gc-anim-pausa::before,
.gc-anim-pausa .hero-scroll-cue-line::after {
  animation-play-state: paused !important;
}

/* ==========================================================================
   COPERTURA DELLA HERO — retta dal compositor, non da JavaScript
   ==========================================================================

   Su touch la hero non è più "pinnata" da GSAP con il blocco bianco traslato
   da una tween: è `position: sticky` dentro una pista alta due viewport, e il
   blocco bianco le scorre sopra da solo, alla velocità del dito.

   Perché. Un pin GSAP tiene fermo l'elemento SCRIVENDO un transform da
   JavaScript a ogni evento di scroll. Su iOS la pagina scorre sul thread del
   compositor e quegli eventi arrivano tardi e accorpati: quando il main
   thread perde un frame, la sezione resta ferma mentre il dito continua — è
   il disegno a frame alterni che si sente come "grattare". `position: sticky`
   invece è tenuto FERMO DAL COMPOSITOR: nessun JavaScript partecipa, quindi
   nessun frame perso dal main thread può bloccare la sezione.

   È esattamente il meccanismo con cui #build-stage era già stato sistemato
   (vedi la nota in css/build-stage.css). Qui viene applicato anche alla hero.

   GEOMETRIA
     .gc-hero-runway        alta 2 viewport (1 di hero + 1 di copertura)
       .gc-hero-pin-wrapper sticky, alta 1 viewport
     .gc-trust-block        margin-top -100svh, quindi parte già a filo del
                            bordo inferiore della hero e la copre salendo

   Il risultato è una copertura 1:1 col dito: nessuna amplificazione, quindi
   nessun errore da amplificare. La pista viene creata da js/main.js una volta
   sola, come già fa per #build-stage.
   -------------------------------------------------------------------- */
.gc-hero-runway {
  position: relative;
}

html.gc-hero-sticky .gc-hero-runway {
  height: 200vh;
  height: 200svh;
}

html.gc-hero-sticky .gc-hero-runway > .gc-hero-pin-wrapper {
  position: sticky;
  top: 0;
  height: 100vh;
  height: 100svh;
}

html.gc-hero-sticky .gc-trust-block {
  margin-top: -100vh;
  margin-top: -100svh;
}

/* LA TENDA DEL CTA, STESSO SCHEMA (2026-09-08, vedi initCurtainReveal in
   js/main.js). Su touch #contatti non e' piu' pinnata da GSAP con una
   trasformazione riscritta a ogni evento di scroll — la causa degli scatti
   su iPhone — ma e' `position: sticky` dentro una pista che main.js crea
   una volta sola.

   GEOMETRIA
     .gc-curtain-runway    margin-top -100svh (ereditato dalla sezione, che
                           qui lo perde): parte una viewport PRIMA della fine
                           di #settori, quindi sotto di lei
       #contatti           sticky, top 0: si incolla quando la pista tocca il
                           bordo alto = quando il fondo di #settori tocca il
                           bordo basso, come prima
       .gc-curtain-spacer  alto 100svh: la corsa. Finisce quando il fondo di
                           #settori tocca il bordo alto, come prima

   z-index: #settori sta sopra (var(--z-content) + 1, in .sectors-section)
   e #contatti sotto (var(--z-content)) — la pista non ha z-index apposta,
   cosi' non crea un contesto e i due si confrontano come prima. */
html.gc-curtain-sticky .gc-curtain-runway {
  position: relative;
  margin-top: -100vh;
  margin-top: -100svh;
}

html.gc-curtain-sticky .gc-curtain-runway > .consult-section {
  position: sticky;
  top: 0;
  margin-top: 0;
}

html.gc-curtain-sticky .gc-curtain-spacer {
  height: 100vh;
  height: 100svh;
  pointer-events: none;
}

/* GLOW ORBS — the most expensive thing on the site, and invisible.

   As authored these were two `position: fixed` circles, 1200x1200 and
   1400x1400, carrying `filter: blur(200px)` / `blur(220px)`,
   `mix-blend-mode: screen`, and a 40s/45s infinite animation. Fixed
   position means all of that was paid on every scroll frame of every
   section of every page, not just where they sit.

   Each of the three properties is a compositor trap on a phone:
     - a 200px Gaussian blur needs an intermediate surface ~1800px square
       and is re-rasterised whenever the layer is dirtied;
     - `mix-blend-mode` forces the browser to read the backdrop back and
       blend it, which takes the whole page below out of fast compositing;
     - the infinite animation dirties the layer forever, so neither of the
       above is ever cached.

   Measured on a 6x-throttled iPhone 13 profile, scrolling the hero and the
   trust block: median frame 29ms -> 18ms and p95 177ms -> 47ms with them
   removed. That is half the frame rate of the entire site.

   And they were showing nothing. The gradients are rgba(...,0.03) and
   rgba(...,0.015) under `opacity: 0.1` — an effective alpha around 0.003,
   which is below one 8-bit level. Screenshot-diffed against the same page
   with the orbs deleted: max channel difference 0, mean 0.000. Not
   "subtle": mathematically invisible.

   So the blur, the blend and the animation are gone. The gradients stay —
   they are free, they render identically, and if the intent is ever
   revisited the shapes are still here to raise the alpha on. */
.glow-orb {
  position: fixed;
  border-radius: 50%;
  opacity: 0.1;
  z-index: var(--z-bg);
  pointer-events: none;
}

.glow-orb-1 {
  width: 1200px;
  height: 1200px;
  top: -15%;
  right: -20%;
  /* Stops widened to where the 200px blur used to push them, so the falloff
     is the same shape without a filter pass. */
  background: radial-gradient(circle, rgba(230, 218, 202, 0.03) 0%, rgba(230, 35, 106, 0.008) 45%, transparent 72%);
}

.glow-orb-2 {
  width: 1400px;
  height: 1400px;
  bottom: -30%;
  right: -10%;
  background: radial-gradient(circle, rgba(215, 222, 235, 0.015) 0%, transparent 72%);
}

/* floatOrb1 / floatOrb2 removed with the animations that used them: an
   infinite animation on a fixed, blurred, blend-mode layer kept it dirty
   forever, which is what stopped the blur and the blend ever being cached.
   The orb ELEMENTS are deliberately still here (see .glow-orb above) — the
   gradients cost nothing and the shapes are the place to raise the alpha if
   the intent is ever revisited. */

/* -----------------------------------------
   3. LUXURY LOADER
   ----------------------------------------- */
.loader {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  z-index: var(--z-loader);
  overflow: hidden;
  pointer-events: none;
}

.loader-bg {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: var(--color-bg-dark);
  z-index: 99;
}

.loader-logo-container {
  position: fixed;
  top: 50%;
  left: 50%;
  width: 150px;
  height: 150px;
  z-index: 101;
  display: flex;
  justify-content: center;
  align-items: center;
  pointer-events: none;
  transform: translate(-50%, -50%) translateY(-45px); /* Offset upward during loading */
}

.loader-logo-large {
  width: 100%;
  height: auto;
  opacity: 1; /* Fully opaque during the entire transition to prevent flashing */
  animation: logoPulse 2s infinite ease-in-out;
}

.loader-progress-wrapper-large {
  position: fixed;
  top: calc(50% + 100px);
  left: 50%;
  transform: translateX(-50%);
  width: 100%;
  max-width: 320px;
  z-index: 100;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.loader-progress-container-large {
  width: 100%;
  height: 3px;
  background-color: rgba(255, 255, 255, 0.08);
  border-radius: 3px;
  overflow: hidden;
}

.loader-progress-bar-large {
  width: 0%;
  height: 100%;
  background-color: var(--color-magenta-deep);
  box-shadow: 0 0 15px var(--color-magenta-glow);
  transition: width 0.1s ease-out;
}

@keyframes logoPulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.05); }
}

/* -----------------------------------------
   4. NAVIGATION HEADER
   ----------------------------------------- */
.main-header {
  position: fixed;
  top: 1.5rem;
  left: 50%;
  transform: translateX(-50%);
  width: 90%;
  max-width: 800px;
  z-index: var(--z-header);
  background: transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border: 1px solid transparent;
  border-radius: 100px;
  box-shadow: none;
  padding: 0.45rem 1.4rem;
  transition: background-color 300ms ease, border-color 300ms ease, backdrop-filter 300ms ease, -webkit-backdrop-filter 300ms ease, box-shadow 300ms ease, top 0.4s cubic-bezier(0.16, 1, 0.3, 1);
  opacity: 0; /* Initially hidden for the preloader animation */
}

/* Desktop/tablet only: hero photo can be light (bar interiors), leaving the
   navbar text unreadable in the un-scrolled state. Give the pill a permanent
   semi-transparent gradient backdrop so the links/logo/CTA always read.
   Mobile keeps its own bar treatment (see max-width:768px block). */
@media (min-width: 769px) {
  .main-header {
    background: linear-gradient(180deg, rgba(12, 12, 16, 0.72) 0%, rgba(12, 12, 16, 0.45) 100%);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    border-color: rgba(255, 255, 255, 0.06);
  }
}

.main-header.scrolled {
  top: 1rem;
  background-color: rgba(12, 12, 16, 0.85);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-color: rgba(255, 255, 255, 0.08);
  box-shadow: 0 15px 35px rgba(0, 0, 0, 0.3);
}

/* This class is added by a scroll handler, so on a phone the blur above would
   be live for the entire time the reader is moving — a fixed, full-width
   backdrop resample competing with the scroll itself. Coarse pointers get an
   opaque pill instead. Desktop keeps the glass. */
@media (pointer: coarse) {
  .main-header.scrolled {
    background-color: rgb(14, 14, 18);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
}

/* MERGE-BOUNDARY NOTE for Luis: index.html-only navbar override. His sticky
   (position:fixed + .scrolled) navbar is untouched for shop.html/cart.html/
   product.html/projects.html (they use js/nav.js, not js/main.js, and don't
   carry the gc-index-page body class). On index.html the .scrolled class
   toggle in js/main.js's scroll handler is commented out (Lorenzo's
   non-sticky navbar), so this only needs to override the base position —
   also fixes a real gap: mobile (<769px) had no background rule at all
   outside the >=769px query, so the pill sat fully transparent over the
   hero photo there. */
body.gc-index-page .main-header {
  position: absolute;
}

@media (max-width: 768px) {
  body.gc-index-page .main-header {
    /* Denser gradient instead of a backdrop blur. The blur here existed to
       keep the links readable over a bright hero photo; carrying the contrast
       in the gradient's own alpha does the same job without asking the phone
       to resample what is behind the pill. */
    background: linear-gradient(180deg, rgba(12, 12, 16, 0.88) 0%, rgba(12, 12, 16, 0.66) 100%);
    border-color: rgba(255, 255, 255, 0.06);
  }
}

.main-header .header-container {
  width: 100%;
  max-width: 100%;
  margin: 0;
  padding: 0;
  display: flex;
  justify-content: space-between; /* Space out the three column slots */
  align-items: center;
  height: 44px;
}

.header-left {
  flex: 0 0 auto; /* Sized to the logo, not stretched - see .header-center */
  display: flex;
  justify-content: flex-start;
  align-items: center;
}

.header-center {
  /* Fills exactly the space left over between the logo and the CTA, then
     centers the nav within THAT space - so the two gaps either side of the
     nav match regardless of how wide the logo vs. the CTA happen to be.
     Centering against the full header width instead would look centered
     mathematically but lopsided optically, since logo and CTA aren't the
     same width. */
  flex: 1 1 auto;
  display: flex;
  justify-content: center;
  align-items: center;
}

.header-right {
  flex: 0 0 auto; /* Sized to the CTA, not stretched - see .header-center */
  display: flex;
  justify-content: flex-end;
  align-items: center;
}

.logo-link {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 37px; /* +~19% again: strong optical presence in the header */
  text-decoration: none;
}

.nav-logo-img {
  height: 37px;
  width: auto;
  display: block;
  opacity: 1; /* Visible from the first paint: the loader logo no longer flies
                 here, so nothing else would ever hand this its opacity. */
  transition: transform 0.4s ease;
}

.logo-link:hover .nav-logo-img {
  transform: rotate(5deg) scale(1.05);
}

.logo-text {
  font-family: var(--font-sans);
  font-size: 0.85rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  color: var(--color-offwhite);
  text-transform: uppercase;
  margin-left: 0.65rem;
  white-space: nowrap;
  opacity: 1; /* Visible from the first paint: see .nav-logo-img above. */
  transition: opacity 0.4s ease;
}

/* Nav Menu */
.nav-menu {
  display: flex;
  align-items: center;
  gap: 2.2rem; /* Clean, refined spacing */
}

.nav-mobile-wordmark {
  display: none; /* Hidden on desktop */
}

.nav-item {
  font-family: var(--font-sans);
  font-size: 0.95rem; /* Bolder optical presence */
  font-weight: 600;
  letter-spacing: 0.06em;
  color: var(--color-offwhite) !important; /* Premium offwhite text color */
  text-decoration: none;
  position: relative;
  padding: 0.2rem 0; /* Sleeker vertical height */
  transition: opacity 0.3s ease, color 0.3s ease;
  opacity: 0.9;
}

.nav-item:hover, .nav-item.active {
  color: var(--color-white) !important;
  opacity: 1;
}

.nav-item::after {
  content: '';
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 1.5px; /* Slightly more defined hairline indicator */
  background-color: var(--color-magenta-glow); /* Brand magenta accent */
  transform: scaleX(0);
  transform-origin: right;
  transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}

.nav-item:hover::after, .nav-item.active::after {
  transform: scaleX(1);
  transform-origin: left;
}

.header-actions {
  display: flex;
  align-items: center;
}

/* RIPROPORZIONATO. Era alto 34px, 0.8rem, tracking 0.08em: dentro una pillola
   larga 800px pesava piu' del logo e della navigazione messi insieme, e la
   navbar leggeva come un annuncio. Resta pieno di magenta — e' l'azione
   primaria del sito — ma torna alla scala della barra: alto quanto le voci,
   non piu' alto di loro. In home il bottone non c'e' proprio (vedi
   .main-header--nocta): la stessa azione e' gia' nella hero. */
.header-actions .btn-outline {
  color: var(--color-white) !important; /* White text on magenta button */
  background-color: var(--color-magenta-deep) !important; /* Global Contract magenta background */
  border: none !important;
  height: 30px !important;
  display: inline-flex !important;
  align-items: center !important;
  justify-content: center !important;
  padding: 0 1.05rem !important;
  border-radius: 100px !important; /* Match the navbar pill radius (see .main-header) */
  font-size: 0.685rem !important;
  font-weight: 600 !important;
  letter-spacing: 0.085em !important;
  text-transform: uppercase !important;
  transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1) !important;
}

/* Senza il CTA la pillola resterebbe larga 800px con mezzo lato vuoto e la
   navigazione spinta a destra: `.header-center` centra dentro lo spazio che
   avanza, e con la colonna destra vuota quello spazio e' tutto a destra. Qui
   la barra smette di essere tre colonne e diventa una coppia — marchio e
   navigazione — centrata insieme. Sotto i 768px torna a tre colonne perche'
   il tasto del menu deve restare al suo posto, in fondo a destra. */
.main-header--nocta { max-width: 620px; }
@media (min-width: 769px) {
  .main-header--nocta .header-container {
    justify-content: center;
    gap: clamp(2.25rem, 5vw, 4rem);
  }
  .main-header--nocta .header-center { flex: 0 0 auto; }
}
@media (max-width: 768px) {
  .main-header--nocta { max-width: 800px; }
}

.header-actions .btn-outline::before {
  display: none !important;
}

.header-actions .btn-outline:hover {
  background-color: var(--color-white) !important; /* White background on hover */
  color: var(--color-magenta-deep) !important; /* Magenta text on hover */
  box-shadow: 0 4px 12px rgba(176, 30, 86, 0.25) !important; /* Elegant soft shadow */
  transform: translateY(-1px) !important;
}

/* Button UI */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.7rem 1.4rem;
  font-family: var(--font-sans);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-decoration: none;
  border-radius: 4px;
  transition: var(--transition-fast);
  cursor: pointer;
  position: relative;
  overflow: hidden;
}

.btn-outline {
  color: var(--color-white);
  background-color: transparent;
  border: 1.5px solid var(--color-magenta-deep);
}

.btn-outline::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: var(--color-magenta-deep);
  z-index: -1;
  transform: scaleY(0);
  transform-origin: bottom;
  transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}

.btn-outline:hover {
  color: var(--color-white);
  box-shadow: 0 0 20px rgba(176, 30, 86, 0.4);
  border-color: var(--color-magenta-glow);
}

.btn-outline:hover::before {
  transform: scaleY(1);
}

/* -----------------------------------------
   5. SINGLE VIEWPORT HERO CONTAINER
   ----------------------------------------- */
.hero-section {
  /* Was calc(100vh - var(--gc-marquee-h)) - that reserved room for the old
     .gc-marquee strip that used to sit directly under the hero (see its own
     comment at css/style.css:2795). That component has zero references in
     index.html anymore (removed when the brand marquee moved inside
     .gc-trust-block), so the subtraction just left a permanent 48-64px
     shortfall - .hero-section's own flat background-color showed through
     above the trust-block curve as a black strip. Root-caused via pixel
     sampling (2026-08-05): the strip color matched .hero-section's
     background-color exactly, not the image or body background. */
  height: 100vh;
  min-height: 590px;
  width: 100%;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  position: relative;
  /* overflow-x: hidden here would force the browser to auto-compute
     overflow-y: auto (CSS overflow interop rule: one axis non-visible +
     other visible => visible axis becomes auto), turning this pinned,
     height:100vh box into its own internal scroll-clipped container that
     traps the panels mid-ascent. Left fully visible instead - html/body
     already carry overflow-x: hidden, so the bled/rotated cards still get
     clipped at the true viewport edge (with no scrollbar), but nothing in
     the hero's own ancestor chain crops their hit area or their 3D paint. */
  overflow-x: visible;
  overflow-y: visible;
  /* No padding-top: the navbar is position:fixed (floating), so the hero photo
     must fill from the very top on ALL screen sizes — otherwise an 80px black
     band shows under the header. (Mobile already zeroed this; now global.) */
  padding-top: 0;
  background-color: var(--color-bg-dark);
  background-image: radial-gradient(ellipse 60% 45% at 50% 55%, rgba(120, 40, 70, 0.14) 0%, rgba(30, 20, 26, 0.06) 45%, transparent 75%);
}

/* PROOF HERO. heroExitTL (js/main.js) animates .hero-proof-photo directly on
   the pre-descent ascent - keep the class name stable. Fills the full
   hero-section height (the old .hero-proof-block plaque was removed - see
   .gc-marquee near the end of this file, which replaces it below the hero). */
.hero-proof-photo {
  flex: 1 1 auto;
  position: relative;
  overflow: hidden;
  background: var(--color-bg-dark-alt);
  /* MOBILE: copy is CENTERED in the frame (not pinned to the bottom), so it
     reads as part of the photograph — like signage composed into the scene
     rather than a caption overlaid on it. */
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}
/* The hero photo is wrapped in <picture> (phones get a 1024w file via a
   `media` source). `display: contents` keeps the wrapper out of the layout
   entirely, so .hero-proof-photo-img positions against .hero-proof-photo
   exactly as it did when the <img> was a direct child. */
.hero-proof-photo picture {
  display: contents;
}

.hero-proof-photo-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* Soft CENTERED vignette instead of a bottom slab: darkens the middle of the
   frame just enough for the words to sit on the bright marble/wood image
   without a heavy dark bar, so the type feels embedded in the light. */
.hero-proof-photo-gradient {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse 92% 60% at 50% 47%, rgba(8, 8, 12, 0.42) 0%, rgba(8, 8, 12, 0.16) 55%, transparent 82%),
    linear-gradient(to bottom, rgba(8,8,12,0.22) 0%, transparent 18%, transparent 82%, rgba(8,8,12,0.34) 100%);
  pointer-events: none;
}
/* Centered editorial title block — flanked by thin hairlines (::before /
   ::after) so it reads as a composed emblem set into the image. */
.hero-proof-headline {
  position: relative;
  z-index: 2;
  padding: 0 1.6rem;
  max-width: 100%;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
}
.hero-proof-headline::before,
.hero-proof-headline::after {
  content: '';
  width: 54px;
  height: 1px;
  background: rgba(255, 255, 255, 0.85);
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
  flex: none;
}
.hero-proof-headline::before { margin-bottom: 1.5rem; }
.hero-proof-headline::after  { margin-top: 1.5rem; }
.hero-proof-title {
  font-family: var(--font-serif);
  font-weight: 500;
  font-size: clamp(1.85rem, 7vw, 3rem);
  line-height: 1.14;
  letter-spacing: 0.01em;
  color: #fff;
  margin: 0;
  text-shadow: 0 2px 26px rgba(0, 0, 0, 0.45);
}
.hero-proof-title-accent {
  color: var(--color-magenta-deep);
  font-style: italic;
}
.hero-proof-subline {
  margin: 1.1rem 0 0;
  font-family: var(--font-sans);
  font-size: 0.54rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-indent: 0.16em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.9);
  text-shadow: 0 1px 14px rgba(0, 0, 0, 0.55);
}
/* Hero dual-CTA (restyle-agenti, hot-lead shortcut): primary/secondary pair
   for visitors who arrive from social already convinced and want to act
   at the first frame, without scrolling the pinned hero. Sits inside
   .hero-proof-headline so it moves with heroExitTL's existing y/opacity
   tween on .hero-proof-photo (js/main.js) - no separate ScrollTrigger
   needed, the hero's pinned mechanic is untouched. */
.hero-cta-row {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  margin-top: 1.6rem;
}
.hero-cta-primary,
.hero-cta-secondary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  min-height: 52px;
  padding: 0 1.6rem;
  font-family: var(--font-sans);
  font-size: 0.76rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  border-radius: 999px;
  cursor: pointer;
  transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1),
              box-shadow 0.3s cubic-bezier(0.16, 1, 0.3, 1),
              background-color 0.3s ease,
              border-color 0.3s ease,
              color 0.3s ease;
}
/* PRIMARY - the only filled-magenta control in the hero, matches the
   final CTA's own primary treatment (same literal hex, same weight). */
.hero-cta-primary {
  background: var(--color-magenta-brand);
  color: #FFFFFF;
  border: 1px solid var(--color-magenta-brand);
  box-shadow: 0 14px 32px -14px rgba(201, 33, 90, 0.65);
}
.hero-cta-primary svg { width: 14px; height: 14px; flex-shrink: 0; transition: transform 0.3s ease; }
.hero-cta-primary:hover {
  transform: translateY(-1px);
  box-shadow: 0 18px 38px -14px rgba(201, 33, 90, 0.75);
}
.hero-cta-primary:hover svg { transform: translateX(3px); }

/* SECONDARY - deliberately subordinate: outline only, no magenta fill,
   quieter weight/opacity so the pair reads primary/secondary at a glance
   (color AND treatment both differ, per brief). */
.hero-cta-secondary {
  background: transparent;
  color: rgba(255, 255, 255, 0.85);
  border: 1px solid rgba(255, 255, 255, 0.32);
  font-weight: 600;
}
.hero-cta-secondary:hover {
  border-color: rgba(255, 255, 255, 0.6);
  color: #FFFFFF;
  background: rgba(255, 255, 255, 0.06);
}

@media (max-width: 480px) {
  /* Clear primary/secondary hierarchy on the smallest screens: the filled
     CTA stays a full-width, taller pill and does the heavy lifting;
     "Vedi lavori" stays a pill too (brief asked to keep the shape) but
     shrinks — narrower (auto width, not full-width), shorter, smaller
     type, thinner/quieter border — so the pair reads primary/secondary
     by SIZE as well as by fill, not just by color. */
  .hero-cta-row { width: 100%; flex-direction: column; gap: 0.75rem; }
  .hero-cta-primary {
    width: 100%;
    min-height: 56px;
    font-size: 0.8rem;
  }
  .hero-cta-secondary {
    width: auto;
    min-height: 38px;
    padding: 0 1.1rem;
    font-size: 0.66rem;
    font-weight: 500;
    color: rgba(255, 255, 255, 0.68);
    border: 1px solid rgba(255, 255, 255, 0.22);
  }
  .hero-cta-secondary:hover {
    border-color: rgba(255, 255, 255, 0.45);
    color: #FFFFFF;
  }
}

.hero-scroll-cue {
  bottom: 1.4rem;
}
.hero-scroll-cue-line {
  height: 34px;
}

/* Hero scroll cue (ported from lavoro-LR) — replaces the micro-CTA as the
   hero's downward affordance. */
.hero-scroll-cue {
  position: absolute;
  left: 50%;
  bottom: 2rem;
  transform: translateX(-50%);
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.7rem;
  text-decoration: none;
  cursor: pointer;
}
.hero-scroll-cue-label {
  font-family: var(--font-sans);
  font-size: 0.6rem;
  font-weight: 600;
  letter-spacing: 0.28em;
  text-indent: 0.28em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.85);
  text-shadow: 0 1px 10px rgba(0, 0, 0, 0.55);
  transition: color 0.3s ease;
}
.hero-scroll-cue-line {
  position: relative;
  width: 1px;
  height: 46px;
  overflow: hidden;
  background: rgba(255, 255, 255, 0.22);
}
.hero-scroll-cue-line::after {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 45%;
  background: linear-gradient(to bottom, transparent, var(--color-white));
  animation: heroScrollTravel 1.9s cubic-bezier(0.5, 0, 0.5, 1) infinite;
}
@keyframes heroScrollTravel {
  0%   { transform: translateY(-110%); }
  100% { transform: translateY(230%); }
}
.hero-scroll-cue:hover .hero-scroll-cue-label {
  color: #fff;
}

.hero-micro-cta {
  display: inline-flex;
  align-items: center;
  justify-content: flex-start;
  min-height: 44px;
  margin-top: 0.85rem;
  font-family: var(--font-sans);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--color-white);
  text-decoration: underline;
  text-underline-offset: 6px;
  text-decoration-thickness: 1px;
  transition: color 0.25s ease, opacity 0.25s ease;
  cursor: pointer;
}
.hero-micro-cta:hover {
  color: rgba(255, 255, 255, 0.75);
}

/* .hero-proof-block (proof numbers/clients/link) removed - replaced by the
   .gc-marquee section below the hero. See css block near end of file. */

@media (min-width: 769px) {
  .hero-proof-headline {
    max-width: 720px;
    padding: 0 4vw 3.5vw;
  }
  .hero-proof-title {
    font-size: clamp(3rem, 5.2vw, 4.6rem);
  }
  .hero-proof-subline {
    font-size: 0.78rem;
  }
  .hero-micro-cta {
    font-size: 0.8rem;
    margin-top: 1.1rem;
  }
}

.hero-title-top {
  font-family: var(--font-serif);
  font-size: clamp(2.4rem, 6.2vh, 4rem); /* Dynamic elegant serif size */
  font-weight: 400;
  line-height: 1.15;
  letter-spacing: -0.015em;
  color: var(--color-white);
  max-width: 900px;
  margin: 0;
}

.text-magenta {
  color: var(--color-magenta-glow);
}

.text-magenta {
  color: var(--color-magenta-glow);
}

/* -----------------------------------------
   5. RESPONSIVE MOBILE NAVIGATION DRAWER
   ----------------------------------------- */
/* Hamburger Toggle Button */
.mobile-menu-toggle {
  display: none; /* Hidden on desktop */
  background: none;
  border: none;
  cursor: pointer;
  padding: 0.5rem;
  z-index: 2100; /* Higher than header and overlay */
  flex-direction: column;
  gap: 6px;
  width: 28px;
  height: 24px;
  justify-content: center;
  align-items: flex-end;
  margin-left: 0.5rem;
}

.mobile-menu-toggle span {
  display: block;
  width: 20px;
  height: 1.5px;
  background-color: var(--color-white);
  transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1), 
              width 0.4s cubic-bezier(0.16, 1, 0.3, 1),
              background-color 0.4s ease;
}

.mobile-menu-toggle span:last-child {
  width: 14px;
}

/* Active hamburger state (X) */
.mobile-menu-toggle.active span:first-child {
  transform: translateY(4px) rotate(45deg);
  width: 20px;
}

.mobile-menu-toggle.active span:last-child {
  transform: translateY(-4px) rotate(-45deg);
  width: 20px;
}

/* Mobile Menu Overlay */
.mobile-menu-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100vh;
  /* Layered dark ground instead of a flat fill: a faint warm magenta glow
     high in the frame plus a deep vertical gradient - the same "spotlight in
     the dark" language as the sector cards, not a generic solid overlay. */
  background:
    radial-gradient(circle at 50% 15%, rgba(176, 30, 86, 0.16) 0%, transparent 55%),
    linear-gradient(180deg, rgba(9, 9, 11, 0.98) 0%, rgba(5, 5, 6, 0.99) 100%);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  z-index: 2000;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  opacity: 0;
  visibility: hidden;
  clip-path: inset(0 0 100% 0);
  transition: opacity 0.6s cubic-bezier(0.16, 1, 0.3, 1),
              visibility 0.6s cubic-bezier(0.16, 1, 0.3, 1),
              clip-path 0.7s cubic-bezier(0.16, 1, 0.3, 1);
}

.mobile-menu-overlay.active,
.mobile-menu-overlay.open {
  opacity: 1;
  visibility: visible;
  clip-path: inset(0 0 0% 0);
}

.mobile-nav-menu {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0;
  width: 100%;
  padding: 0 2.5rem;
}

.mobile-nav-item {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  font-family: var(--font-serif); /* Same voice as the hero headline */
  font-size: 2.1rem;
  font-weight: 400;
  letter-spacing: -0.01em;
  text-transform: none;
  color: var(--color-offwhite);
  text-decoration: none;
  padding: 1.05rem 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
  opacity: 0;
  transform: translateY(24px);
  transition: opacity 0.6s cubic-bezier(0.16, 1, 0.3, 1),
              transform 0.6s cubic-bezier(0.16, 1, 0.3, 1),
              color 0.3s ease;
}

.mobile-nav-item:last-of-type {
  border-bottom: none;
}

.mobile-menu-overlay.active .mobile-nav-item,
.mobile-menu-overlay.open .mobile-nav-item {
  opacity: 0.92;
  transform: translateY(0);
}

.mobile-menu-overlay.active .mobile-nav-item:active,
.mobile-menu-overlay.active .mobile-nav-item:hover,
.mobile-menu-overlay.open .mobile-nav-item:active,
.mobile-menu-overlay.open .mobile-nav-item:hover {
  opacity: 1;
  color: var(--color-white);
}

/* Magenta sweep on touch/hover - the same underline motif as the desktop
   nav, so the mobile drawer reads as the same brand, not a different menu. */
.mobile-nav-item::after {
  content: '';
  position: absolute;
  bottom: -1px;
  left: 50%;
  width: 0;
  height: 1.5px;
  background-color: var(--color-magenta-glow);
  transition: width 0.4s cubic-bezier(0.16, 1, 0.3, 1), left 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}

.mobile-menu-overlay.active .mobile-nav-item:active::after,
.mobile-menu-overlay.active .mobile-nav-item:hover::after,
.mobile-menu-overlay.open .mobile-nav-item:active::after,
.mobile-menu-overlay.open .mobile-nav-item:hover::after {
  width: 60%;
  left: 20%;
}

.mobile-cta-item {
  margin-top: 2rem;
  width: 100%;
  min-height: 48px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-sans);
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0 1.5rem;
  border: none;
  border-bottom: none;
  border-radius: 3px !important;
  color: var(--color-white);
  background-color: var(--color-magenta-deep);
  box-shadow: 0 8px 24px rgba(176, 30, 86, 0.3);
  transition: background-color 0.3s ease, box-shadow 0.3s ease, transform 0.3s ease;
  text-decoration: none;
}

.mobile-cta-item::after {
  display: none; /* Solid button, no underline sweep */
}

.mobile-menu-overlay.active .mobile-cta-item:active,
.mobile-menu-overlay.active .mobile-cta-item:hover,
.mobile-menu-overlay.open .mobile-cta-item:active,
.mobile-menu-overlay.open .mobile-cta-item:hover {
  color: var(--color-white) !important;
  background-color: var(--color-magenta-glow);
  box-shadow: 0 10px 30px rgba(230, 35, 106, 0.45);
  transform: translateY(-1px);
}

/* Staggered transition delays for menu items - premium cascade */
.mobile-menu-overlay.active .mobile-nav-item:nth-child(1) { transition-delay: 0.12s; }
.mobile-menu-overlay.active .mobile-nav-item:nth-child(2) { transition-delay: 0.2s; }
.mobile-menu-overlay.active .mobile-nav-item:nth-child(3) { transition-delay: 0.28s; }
.mobile-menu-overlay.active .mobile-nav-item:nth-child(4) { transition-delay: 0.36s; }

/* Stacking context and style shift for mobile menu open state */
body.menu-open .main-header {
  z-index: 2200 !important; /* Above the overlay z-index: 2000 */
  background: transparent !important;
  border-color: transparent !important;
  box-shadow: none !important;
}

body.menu-open .mobile-menu-toggle span {
  background-color: var(--color-white) !important;
}


/* -----------------------------------------
   8. HERO BOTTOM BLOCK (~10vh - Scroll Hint)
   ----------------------------------------- */


/* -----------------------------------------
   6b. SECTORS SECTION — relocated 7-card gallery (was the hero above the
   fold; commit 2 moves it here as a normal in-page section).

   2026-08-20 (curtain reveal, desktop only): js/main.js's initCurtainReveal
   scrubs #settori's own transform upward as one block right after the
   grid, lifting off to reveal #contatti beneath it (pulled up to meet it
   via that section's own negative margin-top below - see that rule's own
   comment). z-index bumped one step above --z-content so it stays
   visually on top of #contatti while lifting - harmless outside the
   reveal itself (the two sections never otherwise overlap in normal
   flow).
   ----------------------------------------- */
.sectors-section {
  padding: clamp(1.5rem, 4vh, 2.5rem) 4rem clamp(3rem, 7vh, 4.5rem) 4rem;
  background: var(--color-bg-dark);
  position: relative;
  z-index: calc(var(--z-content) + 1);
  /* Curtain reveal (see js/main.js's initCurtainReveal + the -100vh margin
     on .consult-section above): #contatti's pin-engage alignment only
     lands exactly at the top of the viewport (no gap, no bleed into
     #vincenzo above) when #settori's own real height is at least one full
     viewport - guaranteed here rather than left to chance. Mobile's
     stacked-card layout already runs well past one viewport on its own
     (no visible effect); desktop's real content (~700px at 1440px) is
     shorter, so this is a real floor there. justify-content:center keeps
     the existing content vertically centered in the taller box instead of
     leaving dead space pinned to the top - the cards/entrance animation's
     own logic (untouched) reads live rects either way. */
  /* 2026-09-02 (curtain widen/lag fix): was 100dvh. dvh tracks the
     mobile browser's chrome (address bar) LIVE, including its
     show/hide animation while the page is actively scrolling - which
     is exactly when the user is scrolling through the #settori ->
     #contatti pin. ScrollTrigger's own autoRefreshEvents already
     excludes 'resize' (see js/main.js) so the PIN's cached start/end
     never re-measure mid-scroll, but dvh doesn't need a resize event
     to change the box itself: the browser recomputes it continuously
     as the chrome animates. That mismatch - JS holding one measurement
     while CSS live-resizes the box underneath it - is what read as the
     white area "stretching" exactly at the black/white boundary,
     confirmed against the reported symptom (widens only when a sliver
     of #settori's dark background is still visible next to #contatti's
     white, i.e. mid-transition, mid-chrome-animation). svh is the
     SMALLEST possible viewport (chrome fully shown) and never changes
     during a scroll gesture, so the pin's geometry is now constant
     through the whole reveal - the vh line stays first as a fallback
     for engines without svh support. */
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.sectors-section .section-title-wrapper {
  text-align: center;
  max-width: 700px;
  margin: 0 auto;
}
.sectors-section .section-tag {
  display: inline-block;
  font-family: var(--font-sans);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--color-gray-text);
  margin-bottom: 1rem;
}
/* Cormorant Garamond to match the trust-section (.gc-trust-title) display
   voice, not the site-wide Newsreader default - and the accent word reuses
   the same italic+magenta treatment as .gc-trust-italic-accent ("fiducia."). */
.sectors-section .details-title {
  font-family: 'Cormorant Garamond', 'Newsreader', serif;
  font-weight: 500;
  font-size: clamp(2.4rem, 4.2vw, 3.25rem);
  line-height: 1.15;
}
/* 2026-08-17 (restyle-agenti pass): magenta RESTORED - reverses the
   2026-08-17 mobile-cleanup pass's "magenta=CTA only, strip decorative
   magenta from settori" rule. Explicit client instruction this pass: this
   specific word marks the section division and is an intentional
   exception, not a violation of the CTA-only rule (the intro subline's
   own accent, "esigenze specifiche", stays off-white - only this header
   word is the carve-out). Italic + weight kept alongside the restored
   color for a richer accent than color alone. */
.sectors-section .details-title-accent {
  font-style: italic;
  font-weight: 600;
  color: #C9215A;
}
.sectors-subline {
  font-family: var(--font-sans);
  font-size: 0.88rem;
  font-weight: 400;
  color: var(--color-gray-text);
  line-height: 1.5;
  margin-top: 0.75rem;
  margin-bottom: 0;
  max-width: 620px;
  margin-left: auto;
  margin-right: auto;
}
/* Decorative accent, magenta removed (2026-08-17, mobile-cleanup pass) -
   same brand rule as .details-title-accent above: weight + offwhite
   instead of color, against the subline's own dimmer --color-gray-text. */
.sectors-subline-accent {
  font-weight: 600;
  color: var(--color-offwhite);
}

/* -----------------------------------------
   6. INTERACTIVE GALLERY (~58vh - Edge-to-Edge)
   ----------------------------------------- */
/* -----------------------------------------
   6. INTERACTIVE GALLERY (~58vh - Edge-to-Edge)
   ----------------------------------------- */
.gallery-container {
  display: block;
  width: 100%;
  height: auto;
  position: relative;
  /* Relocated from the old hero (was pushed down to clear the headline/CTA
     that lived above it there) - now a normal in-page section, so this is
     just breathing room above/below the card chain. */
  margin-top: 2rem;
  margin-bottom: 3rem; /* dark floor zone below the cards for the reflection/light pool */
  z-index: 2;
}

/* Desktop Gallery styling */
.desktop-gallery {
  display: block;
  position: relative;
  width: 100%;
  height: 40vh;
  min-height: 340px;
  margin: 0;
  z-index: 150;
  overflow: visible;
}

/* Stage: perspective lives here, separate from .desktop-gallery's own layout
   rule above (same element, two classes) so the 3D-scene concern and the
   block-layout concern don't live in one selector. */
.hero-stage {
  perspective: 1500px;
  perspective-origin: 50% 30%;
  transform-style: preserve-3d;
}

/* Soft floor light pool under the group - the boards read as standing on a
   dark reflective floor rather than floating, without a literal floor asset. */
.desktop-gallery::before {
  content: '';
  position: absolute;
  left: 50%;
  bottom: -30px;
  transform: translateX(-50%);
  width: 68%;
  height: 160px;
  background: radial-gradient(ellipse at center, rgba(230, 35, 106, 0.10) 0%, rgba(255, 255, 255, 0.04) 40%, transparent 72%);
  filter: blur(28px);
  z-index: 1;
  pointer-events: none;
}

/* Panel wrapper: rotation/height driven by --ry/--h set inline per panel in
   index.html. transform-origin/preserve-3d/rotateY are the mandated literal
   rule; cursor/box-shadow/filter/transition are supplementary (existing,
   pre-approved behavior - clickable cursor, grounding shadow, resting
   brightness - carried over, not part of the thickness technique itself). */
.hero-panel {
  position: absolute;
  bottom: 0;
  height: calc(var(--panel-base-h) * var(--h));
  transform-origin: bottom center;
  transform: rotateY(var(--ry));
  transform-style: preserve-3d;
  --panel-base-h: clamp(368px, 42vh, 460px);
  cursor: pointer;
  box-shadow: 0 15px 35px rgba(0, 0, 0, 0.6);
  transition: box-shadow 0.5s cubic-bezier(0.16, 1, 0.3, 1);
  /* No filter here (moved to .hero-panel__face) - discovered via this pass's
     truth test: `filter` on an element forces its whole subtree to be
     pre-composited as a flat 2D bitmap before the filter is applied, which
     silently flattened every nested preserve-3d child (face/back/sides),
     making the new real side faces render nothing regardless of correct
     transform math. Confirmed by directly toggling filter:none and watching
     the side face appear. Rotation/position/scale tweens still target this
     wrapper only (per spec); only the brightness filter itself now targets
     .hero-panel__face, a 3D-content-free leaf where filter is safe. */
}

/* Thickness (glass slab pass): the side faces are the SAME photo, blurred/
   darkened/desaturated - a glass mass showing its own material continuing
   around the corner, not a separate colored strip (a "tail"). --panel-img
   is set per panel by initPanelSlabImage in main.js. */
.hero-panel__slab {
  /* position/width/height are required infrastructure, not part of the
     mandated value list - without them the slab (position:static, no
     explicit size) collapses to near-zero height: transform-style:
     preserve-3d makes it a containing block for its position:absolute
     children regardless of its own static position, so an undersized slab
     drags every inset:0 child (face/back/sides) down to the same collapsed
     size. This was the actual root cause of the panels rendering as a
     sliver of near-black - not a loader or headless-timing issue as it
     first appeared to be. */
  position: relative;
  width: 100%;
  height: 100%;
  --depth: 18px;
  --radius: 12px;
  transform-style: preserve-3d;
}
.hero-panel__face {
  position: absolute;
  inset: 0;
  border-radius: var(--radius);
  overflow: hidden;
  transform: translateZ(calc(var(--depth) / 2));
  backface-visibility: hidden;
  /* filter lives here, not on .hero-panel (see comment on .hero-panel above) -
     CSS filter on an ancestor of preserve-3d content flattens it to 2D,
     which would hide the side faces entirely regardless of correct geometry. */
  filter: brightness(0.82);
}
.hero-panel__back {
  position: absolute;
  inset: 0;
  border-radius: var(--radius);
  background: #101010;
  transform: translateZ(calc(var(--depth) / -2)) rotateY(180deg);
  backface-visibility: hidden;
}
.hero-panel__side {
  position: absolute;
  top: 0;
  bottom: 0;
  width: var(--depth);
  overflow: hidden;
  backface-visibility: hidden;
  background-image: var(--panel-img);
  background-size: auto 100%;
  filter: blur(7px) brightness(.5) saturate(1.15);
}
.hero-panel__side--left {
  left: 0;
  transform-origin: left center;
  transform: rotateY(-90deg) translateZ(calc(var(--depth) / 2 * -1));
  border-radius: var(--radius) 0 0 var(--radius);
  background-position: left center;
}
.hero-panel__side--right {
  right: 0;
  transform-origin: right center;
  transform: rotateY(90deg) translateZ(calc(var(--depth) / 2 * -1));
  border-radius: 0 var(--radius) var(--radius) 0;
  background-position: right center;
}
.hero-panel__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  /* Brightness lives solely on .hero-panel__face (see REST_FILTER in
     main.js) so raising a panel to brightness(1.08) actually brightens the
     visible photo instead of fighting a second, separately-baked dark
     filter here. */
  filter: contrast(1.02);
  transition: transform 1.2s cubic-bezier(0.16, 1, 0.3, 1);
}
.hero-panel.is-active .hero-panel__img {
  transform: scale(1.04);
}

/* Bottom darkening so the image melts into its floor contact point instead
   of cutting off with a hard edge - moved to ::before (::after is now the
   mandated glass corner-glow below, both live on .hero-panel__face). */
.hero-panel__face::before {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(to bottom, transparent 65%, rgba(0, 0, 0, 0.55) 100%);
  pointer-events: none;
  z-index: 2;
}

/* Glass light on the corner - the glow where front meets side, inside the
   face itself, not a separate stripe. Only one side's inset highlight reads
   as prominent depending on the panel's own rotation sign - correct/desired. */
.hero-panel__face::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: var(--radius);
  box-shadow:
    inset 2px 0 3px -1px rgba(255, 255, 255, .35),
    inset -2px 0 3px -1px rgba(255, 255, 255, .35),
    inset 0 0 0 1px rgba(255, 255, 255, .06);
  pointer-events: none;
}

/* Always-visible sector label - replaces the old .card-title-block/
   .card-title/.card-title-line trio with a single element per the mandated
   markup; the ::after recreates the same magenta underline accent. */
.hero-panel__label {
  position: absolute;
  top: 1.5rem;
  left: 1.6rem;
  right: 1rem;
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  font-family: var(--font-sans);
  font-size: clamp(0.6rem, 1.25vh, 0.72rem);
  font-weight: 600;
  letter-spacing: 0.14em;
  color: var(--color-white);
  text-transform: uppercase;
  text-shadow: 0 2px 4px rgba(0, 0, 0, 0.8);
  line-height: 1.3;
  pointer-events: none;
}
.hero-panel__label::after {
  content: '';
  width: 16px;
  height: 1px;
  background-color: var(--color-magenta-deep);
  margin-top: 0.3rem;
}

.hero-panel.is-active {
  box-shadow: 0 45px 90px rgba(0, 0, 0, 0.9), 0 0 50px rgba(176, 30, 86, 0.35);
}

/* Floor reflection: a JS-cloned copy of each panel's image, flipped and
   masked to a soft fade (see initReflections in main.js). Not built with
   -webkit-box-reflect - that property silently no-ops on elements inside
   ScrollTrigger's pinned/GPU-composited hero, so the duplicated-element
   technique is used instead, per the fallback called out in spec. Resting
   rotateY/z are applied by GSAP (see REST table in main.js), matching each
   reflection to its source panel so the floor plane continues its card. */
.desktop-gallery .card-reflection {
  position: absolute;
  top: 100%;
  height: 150px;
  overflow: hidden;
  pointer-events: none;
  opacity: 0.85;
  filter: blur(3px);
  transform-style: preserve-3d;
  -webkit-mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.55) 0%, rgba(0, 0, 0, 0.12) 55%, transparent 100%);
  mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.55) 0%, rgba(0, 0, 0, 0.12) 55%, transparent 100%);
}
/* Contact shadow: a soft dark ellipse at the seam, independent of the
   reflection's own opacity/mask - what actually reads as "standing on the
   floor" rather than floating above it. */
.desktop-gallery .card-reflection::before {
  content: '';
  position: absolute;
  top: -10px;
  left: 8%;
  width: 84%;
  height: 20px;
  background: radial-gradient(ellipse at center, rgba(0, 0, 0, 0.6) 0%, rgba(0, 0, 0, 0.25) 45%, transparent 75%);
  filter: blur(6px);
  pointer-events: none;
}
.desktop-gallery .card-reflection[data-index="0"] { left: -6.81%; width: 22.86%; }
.desktop-gallery .card-reflection[data-index="1"] { left: 13.23%; width: 16.19%; }
.desktop-gallery .card-reflection[data-index="3"] { left: 27.11%; width: 16.56%; }
.desktop-gallery .card-reflection[data-index="2"] { left: 42.41%; width: 15.53%; }
.desktop-gallery .card-reflection[data-index="4"] { left: 56.94%; width: 15.77%; }
.desktop-gallery .card-reflection[data-index="5"] { left: 71.42%; width: 16.41%; }
.desktop-gallery .card-reflection[data-index="6"] { left: 85.87%; width: 13.68%; }

/* Seven physical panels standing on one shared floor line (bottom: 0), read
   as a folding-screen / paravent chain - rotation direction ALTERNATES card
   to card (-,+,-,+,-,+,-, see REST table in main.js, mandatory), translateZ
   stays shallow because the depth read comes from the rotation angles, not
   from pushing cards far back. Resting rotateY/z are applied by GSAP on load
   so the activate/hover choreography can tween the exact same properties
   back to these values. Only layout (position/size/stacking) lives here.
   Left/width form an edge-to-edge hinged chain (0.6% kiss-overlap per seam,
   just enough to avoid a black sliver where two rotated edges meet - not a
   stacked/overlapping deck). Height follows the symmetric tall-short wave:
   tall,short,tall,short(center),tall,short,tall - only tops vary, all
   bottoms share the floor baseline. */
.hero-panel[data-index="0"] {
  left: -6.81%;
  width: 22.86%;
  z-index: 8;
  order: 0; /* visually inert under position:absolute - kept because
    runForcedScrollTween in main.js reads getComputedStyle(panel).order to
    sequence the descent stagger; do not remove. */
}
.hero-panel[data-index="1"] {
  left: 13.23%;
  width: 16.19%;
  z-index: 16;
  order: 1;
}
.hero-panel[data-index="3"] {
  left: 27.11%;
  width: 16.56%;
  z-index: 12;
  order: 2;
}
.hero-panel[data-index="2"] {
  left: 42.41%;
  width: 15.53%;
  z-index: 20;
  order: 3;
}
.hero-panel[data-index="4"] {
  left: 56.94%;
  width: 15.77%;
  z-index: 16;
  order: 4;
}
.hero-panel[data-index="5"] {
  left: 71.42%;
  width: 16.41%;
  z-index: 12;
  order: 5;
}
.hero-panel[data-index="6"] {
  left: 85.97%;
  width: 13.68%;
  z-index: 8;
  order: 6;
}

/* Active-state label strip: confined to the bottom strip only, at well under
   25% opacity, so activating a panel (brightness(1.08)) actually reads the
   photo instead of hiding it under a veil. */
.hero-panel .card-hover-overlay {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 32%;
  background: linear-gradient(to top, rgba(0, 0, 0, 0.24) 0%, transparent 100%);
  z-index: 4;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding-bottom: 1.4rem;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}

.hero-panel.is-active .card-hover-overlay {
  opacity: 1;
}

.hero-panel .discover-text {
  font-family: var(--font-sans);
  font-size: clamp(0.7rem, 1.5vh, 0.8rem);
  font-weight: 500;
  letter-spacing: 0.22em;
  color: var(--color-white);
  text-transform: uppercase;
  border-bottom: 1.5px solid var(--color-magenta-glow);
  padding-bottom: 0.3rem;
}

/* Default Mobile Gallery (hidden on desktop) */
.mobile-gallery {
  display: none;
}

/* Explicit desktop-hidden default for the two mobile-only sector-card
   additions (2026-08-06) - functionally redundant with .mobile-gallery's
   own display:none above (nothing inside it paints regardless), but keeps
   each element's OWN computed display literally "none" on desktop rather
   than relying solely on the hidden ancestor. Both are switched back on
   inside the max-width:768px block, same pattern as .mobile-gallery. */
.sector-card-desc-mobile,
.sector-card-arrow-text {
  display: none;
}

/* -----------------------------------------
   9. .details-title / .details-title-accent - shared with #settori's
   heading (see project-logs/lorenzo-caruso-reorder-2026-08-04.md; the
   #dettagli section these were originally built for, along with
   .details-section/.details-container, was removed).
   ----------------------------------------- */
.details-title {
  font-family: var(--font-serif);
  font-size: 3rem;
  font-weight: 400;
  letter-spacing: -0.01em;
  color: var(--color-white);
  line-height: 1.2;
}

.details-text {
  font-family: var(--font-sans);
  font-size: 1.05rem;
  font-weight: 300;
  line-height: 1.7;
  color: var(--color-gray-text);
  margin-bottom: 1.5rem;
}


/* The old floating-gallery desktop rules that used to live in this
   min-width:1024px block are gone (that gallery, then its fan-carousel
   replacement, were both removed in turn). .gallery-footer/.gallery-monogram
   below predate either of them entirely (unused in current markup) and are
   left as-is - outside this task's scope. */
@media (min-width: 1024px) {

  /* Gallery Footer area styling */
  .gallery-footer {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 2rem;
    margin-top: 5rem;
  }
  
  .gallery-footer-text {
    font-family: var(--font-serif);
    font-size: 2.2rem;
    font-weight: 300;
    line-height: 1.35;
    color: var(--color-white);
    letter-spacing: -0.01em;
  }
  
  .gallery-footer-text .text-magenta {
    color: var(--color-magenta-glow);
  }
  
  /* Monogram Diamond Logo */
  .gallery-monogram {
    margin-top: 1rem;
    display: flex;
    justify-content: center;
  }
  
  .monogram-diamond {
    width: 42px;
    height: 42px;
    background-color: var(--color-bg-dark);
    border: 1px solid var(--color-magenta-glow);
    transform: rotate(45deg);
    display: flex;
    align-items: center;
    justify-content: center;
    box-shadow: 0 0 15px rgba(230, 35, 106, 0.25);
  }
  
  .monogram-diamond span {
    transform: rotate(-45deg);
    display: block;
    font-family: var(--font-sans);
    font-weight: 700;
    font-size: 0.9rem;
    letter-spacing: 0.05em;
    color: var(--color-white);
  }
}

/* -----------------------------------------
   9b. FINAL CTA — "COORDINATION HUB", CENTRAL OBJECT + EXPANDING FORM
   ----------------------------------------- */
/* 2026-08-18/19 (restyle-agenti-6/7/8/9): the one light-surface section on
   the whole page — every other section is dark, so this reads as a
   deliberate "new room" right before the final ask. Local ink tokens
   below (not global :root) because inverting text color is scoped to this
   section only. Scattered service markers/connector-lines were dropped
   (the disciplines live INSIDE the future central image, not as DOM
   dots). Order is now the SAME at every width - copy, action, map, source
   order, no CSS `order` needed. Rounded top corners (radius set per
   breakpoint below) over the plain body background (dark - see body{} at
   the top of this file) replace the old hairline border-top as the
   boundary against the dark section above; overflow:hidden keeps the
   section's own light fill inside that curve. */
.consult-section {
  position: relative;
  z-index: var(--z-content);
  background-color: var(--color-offwhite);
  padding: clamp(4rem, 9vw, 5.5rem) 1.75rem;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  border-radius: 28px 28px 0 0;
  /* Curtain reveal (both breakpoints - see js/main.js's initCurtainReveal):
     #contatti is GSAP-pinned directly behind #settori (which sits above it
     in normal flow, z-index bumped - see .sectors-section) for exactly one
     viewport height of scroll starting the instant #settori's own bottom
     edge leaves the viewport - #settori keeps scrolling normally the whole
     time (it isn't pinned itself), sliding up and off like a curtain to
     uncover the CTA underneath.

     A GSAP pin freezes its target at whatever screen position it would
     NATURALLY render at the instant the pin engages - not at the top of
     the viewport by default. Since #contatti directly follows #settori in
     the document, its natural top at that instant is #settori's bottom
     edge itself (screen y = viewport height, i.e. JUST off the bottom of
     the screen) - pinning it there would hold it fully off-screen for the
     whole reveal. This -100vh margin pulls #contatti's natural position up
     by exactly one viewport height first, so its natural top lands at
     screen y = 0 (filling the viewport) at that exact instant instead -
     the pin then holds THAT position for the reveal's whole duration.
     Purely a static alignment offset (not resize-tracked JS state like the
     old --gc-curtain-lift var was) - matches this section's own
     min-height: 100vh/100svh 1:1 at both breakpoints, so it's exact at any
     viewport size with no measurement needed.

     2026-09-02 (curtain widen/lag fix): was -100dvh. dvh live-tracks the
     mobile chrome (address bar) DURING its own show/hide animation, which
     runs exactly while the user is scrolling through this pin - so the
     margin (and therefore this section's pinned geometry) was being
     resized by the browser in real time, independent of and unsynced
     with ScrollTrigger's own cached pin measurements (autoRefreshEvents
     excludes 'resize', see js/main.js, so the PIN itself never
     re-measures mid-scroll). That live mismatch is what read as the
     white area stretching/widening exactly at the moment a sliver of
     #settori's dark background was still visible next to #contatti's
     white - the reported symptom. svh is the smallest possible viewport
     (chrome fully shown) and stays constant through a scroll gesture,
     so the curtain's geometry no longer moves under the pin. */
  margin-top: -100vh;
  margin-top: -100svh;
  --consult-ink: #17161B;
  --consult-ink-body: #46434C;
  --consult-ink-mute: #5B5864;
  --consult-hairline: rgba(23, 22, 27, 0.12);
  --consult-hairline-soft: rgba(23, 22, 27, 0.07);
}

/* Mobile-first: one centered column, flex so the three children (copy,
   map, action - flat siblings, no wrapper divs) can be reordered per
   breakpoint with plain CSS `order` instead of duplicating markup. Source
   order already matches the mobile brief (subtitle → image → button). */
.consult-inner {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
  max-width: min(560px, 92vw);
  text-align: center;
}

/* 2026-09-08 (CTA desktop): involucro dell'argomento. Sotto i 1100px non
   esiste - `display: contents` lo toglie dal flusso e i suoi figli tornano a
   essere figli diretti di .consult-inner, cioe' esattamente la colonna
   flessibile di prima. Serve solo alla griglia a due colonne del desktop
   (vedi il blocco @media (min-width: 1100px) in fondo alla sezione). */
.consult-pitch { display: contents; }

.consult-copy { text-align: center; }

.consult-letterhead {
  display: block;
  font-family: var(--font-sans);
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--consult-ink-mute);
  margin-bottom: 1.4rem;
}

/* Cormorant Garamond explicitly, not var(--font-serif) alone - that token's
   own fallback chain ('Newsreader', Georgia) points at a face this site
   never actually loads (only Cormorant Garamond + Inter are linked in
   index.html's <head>), so anything using the bare var silently renders in
   the Georgia fallback instead of the site's real serif. Matches the
   pattern already correct on .sector-card-count. */
.consult-statement {
  font-family: 'Cormorant Garamond', var(--font-serif), serif;
  font-size: clamp(2.2rem, 8.6vw, 2.9rem);
  font-weight: 500;
  line-height: 1.15;
  letter-spacing: -0.01em;
  color: var(--consult-ink);
  margin: 0;
}
/* Single-word editorial accent (brief: "a touch of magenta on the
   headline, one word, not the whole title") - italic (inherited from the
   existing .consult-statement em rule) plus the literal brand hex,
   reserved everywhere else for primary actions only. */
.consult-statement em {
  font-style: italic;
  font-weight: 500;
}
.consult-statement .consult-accent {
  color: var(--color-magenta-brand);
  font-weight: 700;
}

.consult-lede {
  font-family: var(--font-sans);
  font-size: 1rem;
  font-weight: 400;
  line-height: 1.5;
  color: var(--consult-ink-body);
  margin: 1.1rem auto 0;
}

/* ENTRANCE for the copy block (unchanged mechanism from the prior session -
   one IntersectionObserver, one class toggle, see initConsultReveal in
   js/main.js). The central object below has its own, separate GSAP
   entrance (initConsultMapReveal) - see the CSS further down. */
.consult-letterhead,
.consult-statement,
.consult-lede {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 0.8s cubic-bezier(0.22, 1, 0.36, 1),
              transform 0.8s cubic-bezier(0.22, 1, 0.36, 1);
  will-change: opacity, transform;
}
.consult-in .consult-letterhead,
.consult-in .consult-statement,
.consult-in .consult-lede {
  opacity: 1;
  transform: translateY(0);
}
.consult-in .consult-letterhead { transition-delay: 0.02s; }
.consult-in .consult-statement  { transition-delay: 0.12s; }
.consult-in .consult-lede       { transition-delay: 0.22s; }

@media (prefers-reduced-motion: reduce) {
  .consult-letterhead,
  .consult-statement,
  .consult-lede {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* -----------------------------------------
   The central object slot. Entrance is a GSAP timeline (initConsultMapReveal
   in js/main.js), not a CSS class toggle - gsap.set establishes the hidden
   state, so with JS disabled it is simply visible in its resting position
   (no CSS opacity:0 default here).
   ----------------------------------------- */
.consult-map {
  position: relative;
  width: 100%;
  max-width: 300px;
  margin: 2.4rem 0 0;
}
/* MOBILE-FIRST = PORTRAIT: the rule stands vertically and the pills sit ON
   it, left and right of the spine. The wide horizontal ribbon is the
   >=769px treatment - see that breakpoint block. The hub, not the object,
   carries the aspect: .consult-orbit is inset:0 of the hub too, so joint
   coordinates computed against the object are already orbit coordinates
   with no offset arithmetic. */
.consult-map-hub {
  position: relative;
  width: 100%;
  aspect-ratio: 300 / 430;
}

/* The object itself: an SVG folding rule injected by initConsultMapReveal.
   No background, no border, no radius - it is a cut-out object floating on
   the section's own light fill, not a framed picture. Transform+opacity are
   the only animated properties (bounce entrance), hence will-change. */
.consult-map-object {
  position: absolute;
  inset: 0;
  z-index: 1;
  will-change: transform, opacity;
  /* NO filter() here. The site photo grade (saturate .90 / contrast 1.05 /
     brightness .97) is baked into the SVG's own colour ramp instead - see
     the M{} table in initConsultMapReveal. A filter on an element that is
     translated and scaled every frame re-filters the whole object on every
     one of those frames, which is a large part of what made rev 1 strobe. */
}
.consult-map-object svg { width: 100%; height: 100%; display: block; overflow: visible; }

/* Hairline from a pill back to the joint it labels. Drawn by JS as a child
   of the OUTER pill span, so it travels with that pill's parallax instead
   of detaching from it. */
.consult-orbit-leader {
  position: absolute;
  height: 1px;
  background: rgba(23, 22, 27, 0.18);
  transform-origin: 0 50%;
  pointer-events: none;
}

/* -----------------------------------------
   SERVICE ORBIT (2026-08-22, restyle-agenti)
   Decorative chips drifting around the central object. This block owns
   APPEARANCE only - every position lives in the two breakpoint blocks
   further down, because desktop orbits OUTSIDE the frame while mobile has
   to overlap it (only ~39px of clearance either side at 390px).

   Deliberately no `transform` here: the float tweens in
   initConsultMapReveal (js/main.js) animate x/y, and a CSS translate on the
   same element would be overwritten by GSAP on the first tick, snapping
   every pill out of place. Placement is top + left/right only.
   ----------------------------------------- */
.consult-orbit {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 2;
}

/* OUTER: position only. JS writes left/top from the joint this pill labels
   (already offset by half the pill's own box, so no CSS translate is
   needed - a CSS transform here would be wiped by GSAP on the first tick).
   GSAP owns this element's transform for the SCROLL PARALLAX. */
.consult-orbit-pill {
  position: absolute;
  will-change: transform;
}

/* INNER: all of the appearance, plus the entrance and the slow idle drift.
   Splitting the two is what lets the parallax (outer) and the drift (inner)
   run at the same time without overwriting each other's transform. */
.consult-orbit-pill-body {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  white-space: nowrap;
  font-family: var(--font-sans);
  font-weight: 500;
  letter-spacing: 0.01em;
  color: var(--consult-ink-body);
  border: 1px solid var(--consult-hairline-soft);
  border-radius: 999px;
  /* position:relative so the leader hairline can be an absolutely positioned
     CHILD OF THIS ELEMENT - it has to inherit this element's opacity, or the
     lines show up on their own before the labels fade in. */
  position: relative;
  /* Solid, NOT backdrop-filter. The chips do sit on the object at phone
     widths and have to stay legible over metal, but a blur behind eight
     chips that are transformed on every scroll tick is one of the most
     expensive things you can ask a compositor for - it was a main cause of
     the white/black strobing in rev 1. At 0.94 on a #F5F5F7 section the
     blur was buying nothing visible. */
  background: rgba(255, 255, 255, 0.94);
  box-shadow: 0 8px 20px -12px rgba(23, 22, 27, 0.32);
  will-change: transform, opacity;
}

/* The dot. An <i> rather than a pseudo-element, so the flex gap owns the
   spacing and the chip stays one inline-flex row at any font size. */
/* Il livello che galleggia. Sta SOPRA la capsula bianca e contiene anche la
   stanghetta, cosi' l'etichetta si muove tutta insieme invece di muovere il
   proprio contenuto restando ferma. */
.consult-orbit-pill-drift {
  position: relative;
  display: inline-flex;
  will-change: transform;
}

.consult-orbit-pill-body i {
  display: block;
  border-radius: 50%;
  background: var(--color-magenta-brand);
  flex-shrink: 0;
}

@media (prefers-reduced-motion: reduce) {
  .consult-orbit-pill,
  .consult-orbit-pill-body,
  .consult-map-object { will-change: auto; }
}

/* -----------------------------------------
   The action: a plain, always-in-flow magenta pill. It never changes
   position, size, or leaves the document flow - opening the request form
   cannot reflow anything in this section, because the form itself is a
   SEPARATE top-level element (#consultFormBackdrop / #consultFormOverlay,
   markup near the end of index.html, right after .project-modal), not
   nested in here. See initConsultExpandForm in the inline script at the
   bottom of index.html for the FLIP animation that morphs the overlay
   from this button's own on-screen rect out to (near-)fullscreen.
   ----------------------------------------- */
.consult-action {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.4rem;
  margin-top: 2rem;
}

.consult-cta-shell {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
  background: var(--color-magenta-brand);
  overflow: hidden;
  box-shadow: 0 18px 40px -18px rgba(201, 33, 90, 0.55);
  -webkit-tap-highlight-color: transparent;
}

.consult-cta-btn {
  position: relative;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: 0.75rem;
  min-height: 52px;
  padding: 1.05rem 1.9rem;
  background: none;
  border: none;
  color: #FFFFFF;
  font-family: var(--font-sans);
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  white-space: nowrap;
  cursor: pointer;
}
.consult-cta-btn svg { width: 16px; height: 16px; flex-shrink: 0; }

/* Il popover a tendina (.consult-choice-*) e' stato rimosso il 2026-09-08:
   in home ogni "Richiedi consulenza" apre la scheda contatti a tre viste
   (vedi .gcf-contact piu' sotto). js/consult-trigger.js resta su disco per
   le pagine secondarie, che pero' non hanno piu' trigger. */

/* -----------------------------------------
   The request-form overlay itself - a top-level fixed layer (see the
   markup note above), always position:fixed from page load so it can
   never be affected by any ancestor and never reserves space in any
   document flow.

   2026-08-20 (real button->form morph, take 2): a plain centered
   scale+fade (tried right after the original FLIP rebuild) read as "the
   form just appears" - no perceived connection to the button. Back to a
   FLIP-style morph (js/main.js's initConsultExpandForm), but keeping the
   fixes the scale+fade version added: .consult-form-panel is
   `position: fixed` with NO CSS top/left/width/height/radius/background
   of its own (all of that is set by JS, animated every open/close from
   the trigger button's own live rect out to a centered card-sized target
   and back) - .consult-form-overlay stays a plain full-viewport pointer-
   events layer (no flex-centering needed anymore, the panel positions
   itself). .consult-form-backdrop still owns the separate dim-fade.
   ----------------------------------------- */
.consult-form-backdrop {
  position: fixed;
  inset: 0;
  z-index: 99998;
  background-color: rgba(23, 22, 27, 0.5);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  opacity: 0;
  pointer-events: none;
}
.consult-form-backdrop.is-open { pointer-events: auto; }

.consult-form-overlay {
  position: fixed;
  inset: 0;
  z-index: 99999;
  pointer-events: none;
  /* Re-declared here (same values as .consult-section) - the form panel
     used to inherit these from that section as its DOM parent; now that
     it's a top-level overlay outside .consult-section entirely (moved so
     it can never be clipped by that section's own overflow:hidden), it
     needs its own copy of the same tokens or .consult-form-title etc.
     fall through to whatever color the page's own dark body inherits
     (confirmed live: rendered pure white-on-white, invisible). */
  --consult-ink: #17161B;
  --consult-ink-body: #46434C;
  --consult-ink-mute: #5B5864;
  --consult-hairline: rgba(23, 22, 27, 0.12);
}
.consult-form-overlay.is-open { pointer-events: auto; }

.consult-form-panel {
  /* top/left/width/height/border-radius/background-color: all JS-driven
     (see initConsultExpandForm) - resting values here only matter before
     JS ever runs / if it fails, so they're a harmless, invisible 0-size
     box, never a flash of wrong geometry. */
  position: fixed;
  top: 0;
  left: 0;
  width: 0;
  height: 0;
  overflow-y: auto;
  box-shadow: 0 40px 90px -30px rgba(0, 0, 0, 0.5);
  padding: 2.6rem 2.6rem 2.3rem;
  opacity: 0;
  pointer-events: none;
  text-align: left;
}
.consult-form-overlay.is-open .consult-form-panel {
  pointer-events: auto;
}

.consult-form-close {
  position: absolute;
  /* Sopra .gcf-contact: la bolla gli lascia un transform inline (crea un
     contesto di stacking) e senza z-index la barra "indietro" del modulo
     copriva la X. */
  z-index: 2;
  top: 0.9rem;
  right: 0.9rem;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: 1px solid var(--consult-hairline);
  background-color: transparent;
  color: var(--consult-ink);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: border-color 0.25s ease, background-color 0.25s ease;
}
.consult-form-close:hover {
  border-color: var(--color-magenta-brand);
  background-color: rgba(201, 33, 90, 0.06);
}
.consult-form-close svg { width: 15px; height: 15px; }

.consult-form-head { margin-bottom: 1.5rem; padding-right: 2.4rem; }
.consult-form-eyebrow {
  font-family: var(--font-sans);
  font-size: 0.64rem;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--color-magenta-brand);
}
.consult-form-title {
  font-family: var(--font-serif);
  font-size: 1.5rem;
  font-weight: 500;
  line-height: 1.15;
  color: var(--consult-ink);
  margin-top: 0.5rem;
}

/* =========================================================
   LA SCHEDA CONTATTI (2026-09-08) — tre viste dentro il pannello.
   Tutto in scala con il resto del CTA: eyebrow magenta in maiuscoletto,
   titolo in serif, corpo in sans, hairline a 12% d'inchiostro. Il colore
   pieno resta sull'unica azione primaria di ogni schermata; le scelte
   (chip, card, canali) sono in hairline e diventano magenta solo quando
   selezionate — cosi' l'occhio trova sempre prima la domanda, poi le
   risposte, poi il tasto.
   ========================================================= */
.gcf-contact {
  display: flex;
  flex-direction: column;
  min-height: 100%;
}
.gcf-contact-view { display: none; flex-direction: column; flex: 1 1 auto; }
.gcf-contact[data-view="scelta"] .gcf-contact-view--scelta,
.gcf-contact[data-view="form"]   .gcf-contact-view--form,
.gcf-contact[data-view="fatto"]  .gcf-contact-view--fatto { display: flex; }

@keyframes gcContactIn {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}
.gcf-contact-view.is-entering,
.gcf-step.is-entering { animation: gcContactIn 0.32s cubic-bezier(0.2, 0.7, 0.2, 1) both; }

/* Barra in alto: indietro + "2 di 5" + filo di avanzamento. Lascia spazio
   a destra alla X di chiusura, che sta nell'angolo del pannello. */
.gcf-contact-top {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  margin: -0.4rem 0 1.4rem;
  padding-right: 3rem;
}
.gcf-contact-top[hidden] { display: none; }
.gcf-contact-back {
  flex: 0 0 auto;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: 1px solid var(--consult-hairline);
  background: transparent;
  color: var(--consult-ink);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: border-color 0.25s ease, background-color 0.25s ease;
}
.gcf-contact-back:hover,
.gcf-contact-back:focus-visible {
  border-color: var(--color-magenta-brand);
  background-color: rgba(201, 33, 90, 0.06);
  outline: none;
}
.gcf-contact-back svg { width: 16px; height: 16px; }
.gcf-contact-progress {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  gap: 0.8rem;
  min-width: 0;
}
.gcf-contact-step {
  font-family: var(--font-serif);
  font-style: italic;
  font-size: 1rem;
  color: var(--consult-ink-mute);
  white-space: nowrap;
}
/* Quattro segmenti = le quattro sezioni del modulo (il tuo locale · cosa
   ti serve · tempi e budget · i tuoi contatti). Le sezioni passate sono
   piene, quella corrente si riempie passo dopo passo. */
.gcf-contact-bar {
  flex: 1 1 auto;
  display: flex;
  gap: 5px;
  height: 3px;
}
.gcf-contact-seg {
  flex: 1 1 0;
  height: 100%;
  background: var(--consult-hairline);
  border-radius: 3px;
  overflow: hidden;
}
.gcf-contact-seg b {
  display: block;
  height: 100%;
  background: var(--color-magenta-brand);
  transform-origin: 0 50%;
  transform: scaleX(0);
  transition: transform 0.45s cubic-bezier(0.2, 0.7, 0.2, 1);
}

.gcf-contact-sub {
  font-family: var(--font-sans);
  font-size: 0.9rem;
  line-height: 1.5;
  color: var(--consult-ink-body);
  margin-top: 0.6rem;
}
/* Nel modulo tutto e' centrato: domanda, riga sotto, risposte. Una
   domanda alla volta si legge come un cartello, non come un modulo. */
.gcf-contact-view--form .consult-form-title,
.gcf-contact-view--form .gcf-contact-sub { text-align: center; }
.gcf-contact-view--form .gcf-contact-sub { margin-left: auto; margin-right: auto; max-width: 36ch; }
.gcf-step-eyebrow {
  display: block;
  text-align: center;
  font-family: var(--font-sans);
  font-size: 0.64rem;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--color-magenta-brand);
  margin-bottom: 0.55rem;
}

/* I tre canali: righe piene, larghe tutto il pannello, con il pollice
   che ha 76px d'altezza da colpire. */
.gcf-contact-options { display: flex; flex-direction: column; gap: 0.7rem; }
.gcf-contact-option {
  display: flex;
  align-items: center;
  gap: 0.95rem;
  width: 100%;
  min-height: 76px;
  padding: 0.95rem 1rem 0.95rem 1.05rem;
  background: #FFFFFF;
  border: 1px solid var(--consult-hairline);
  border-radius: 20px;
  color: var(--consult-ink);
  font-family: var(--font-sans);
  text-align: left;
  text-decoration: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: border-color 0.2s ease, background-color 0.2s ease, transform 0.15s ease;
}
.gcf-contact-option:hover,
.gcf-contact-option:focus-visible {
  border-color: var(--color-magenta-brand);
  background-color: rgba(201, 33, 90, 0.035);
  outline: none;
}
.gcf-contact-option:active { transform: scale(0.985); }
.gcf-contact-option-icon {
  flex: 0 0 auto;
  width: 46px;
  height: 46px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(201, 33, 90, 0.09);
  color: var(--color-magenta-brand);
}
.gcf-contact-option-icon svg { width: 21px; height: 21px; }
.gcf-contact-option-text { display: flex; flex-direction: column; gap: 0.18rem; min-width: 0; flex: 1 1 auto; }
.gcf-contact-option-title { font-size: 0.98rem; font-weight: 600; line-height: 1.2; }
.gcf-contact-option-sub { font-size: 0.78rem; line-height: 1.35; color: var(--consult-ink-mute); }
.gcf-contact-option-go { flex: 0 0 auto; color: var(--consult-ink-mute); display: flex; }
.gcf-contact-option-go svg { width: 18px; height: 18px; }
.gcf-contact-option:hover .gcf-contact-option-go { color: var(--color-magenta-brand); }

.gcf-contact-foot {
  margin-top: auto;
  padding-top: 1.6rem;
  font-family: var(--font-sans);
  font-size: 0.72rem;
  line-height: 1.7;
  letter-spacing: 0.02em;
  color: var(--consult-ink-mute);
  text-align: center;
}
.gcf-contact-foot a { color: inherit; text-decoration: underline; text-underline-offset: 3px; text-decoration-color: var(--consult-hairline); }

/* Il modulo: un passo alla volta. Il <legend> e' il titolo della schermata
   e porta lo stesso stile del titolo del pannello. */
.gc-form { display: flex; flex-direction: column; flex: 1 1 auto; }
.gcf-step { display: flex; flex-direction: column; border: 0; padding: 0; margin: 0; min-width: 0; }
.gcf-step[hidden] { display: none; }
.gcf-step > legend { padding: 0; float: left; width: 100%; text-align: center; font-size: 1.65rem; }
.gcf-step > legend + * { clear: both; }
.gcf-step .gcf-contact-sub { margin-bottom: 1.3rem; }
.gcf-step > legend:focus { outline: none; }
.gcf-step .gcf-chips, .gcf-step .gcf-cards { margin-top: 0.2rem; }
.gcf-step .gc-field { margin-top: 1rem; }
.gcf-step > legend + .gc-field { margin-top: 1.2rem; }

/* Chip: pillole in hairline, magenta pieno quando scelte. L'input vero e'
   nascosto ma presente (tastiera e screen reader lo usano). */
.gcf-chips { display: flex; flex-wrap: wrap; gap: 0.55rem; }
.gcf-chips--center { justify-content: center; }
.gcf-chip { position: relative; display: inline-flex; }
.gcf-chip input,
.gcf-card input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
  margin: 0;
  pointer-events: none;
}
.gcf-chip span {
  display: inline-flex;
  align-items: center;
  min-height: 46px;
  padding: 0.65rem 1.05rem;
  border: 1px solid var(--consult-hairline);
  border-radius: 999px;
  background: #FFFFFF;
  font-family: var(--font-sans);
  font-size: 0.86rem;
  font-weight: 500;
  line-height: 1.2;
  color: var(--consult-ink);
  cursor: pointer;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  transition: border-color 0.18s ease, background-color 0.18s ease, color 0.18s ease, transform 0.12s ease;
}
.gcf-chip:hover span { border-color: rgba(201, 33, 90, 0.55); }
.gcf-chip:active span { transform: scale(0.97); }
.gcf-chip input:checked + span {
  border-color: var(--color-magenta-brand);
  background: var(--color-magenta-brand);
  color: #FFFFFF;
}
.gcf-chip input:focus-visible + span,
.gcf-card input:focus-visible ~ .gcf-card-title { box-shadow: 0 0 0 3px rgba(201, 33, 90, 0.22); }

/* Tessere: icona sopra, etichetta sotto, due per riga sul telefono. E' la
   griglia delle risposte "a colpo d'occhio" — attivita', servizi,
   attrezzature, ambienti. Scelta = bordo e icona magenta, spunta
   nell'angolo. */
.gcf-tiles {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.6rem;
}
.gcf-tile {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.55rem;
  min-height: 108px;
  padding: 0.9rem 0.6rem 0.85rem;
  border: 1px solid var(--consult-hairline);
  border-radius: 18px;
  background: #FFFFFF;
  text-align: center;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: border-color 0.18s ease, background-color 0.18s ease, transform 0.12s ease, box-shadow 0.18s ease;
}
.gcf-tile input { position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0; pointer-events: none; }
.gcf-tile-ic {
  width: 46px;
  height: 46px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(23, 22, 27, 0.045);
  color: var(--consult-ink);
  transition: background-color 0.18s ease, color 0.18s ease, transform 0.18s ease;
}
.gcf-tile-ic svg { width: 24px; height: 24px; }
.gcf-tile-label {
  font-family: var(--font-sans);
  font-size: 0.84rem;
  font-weight: 500;
  line-height: 1.25;
  color: var(--consult-ink);
}
.gcf-tile-check {
  position: absolute;
  top: 0.55rem;
  right: 0.55rem;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--color-magenta-brand);
  color: #FFFFFF;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transform: scale(0.6);
  transition: opacity 0.18s ease, transform 0.18s ease;
}
.gcf-tile-check svg { width: 12px; height: 12px; stroke-width: 2.6; }
.gcf-tile:hover { border-color: rgba(201, 33, 90, 0.55); }
.gcf-tile:active { transform: scale(0.97); }
.gcf-tile.is-on,
.gcf-tile:has(input:checked) {
  border-color: var(--color-magenta-brand);
  background: rgba(201, 33, 90, 0.045);
  box-shadow: 0 0 0 1px var(--color-magenta-brand) inset;
}
.gcf-tile.is-on .gcf-tile-ic,
.gcf-tile:has(input:checked) .gcf-tile-ic { background: rgba(201, 33, 90, 0.12); color: var(--color-magenta-brand); transform: scale(1.06); }
.gcf-tile.is-on .gcf-tile-check,
.gcf-tile:has(input:checked) .gcf-tile-check { opacity: 1; transform: scale(1); }
.gcf-tile input:focus-visible ~ .gcf-tile-label { box-shadow: 0 0 0 3px rgba(201, 33, 90, 0.22); border-radius: 6px; }

/* Card: una risposta per riga con una frase sotto, e un punto a destra
   che si riempie. Per le domande in cui il sottotitolo serve a capire la
   differenza fra le opzioni. */
.gcf-cards { display: flex; flex-direction: column; gap: 0.65rem; }
.gcf-card {
  position: relative;
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 0.9rem;
  padding: 0.9rem 3rem 0.9rem 0.9rem;
  border: 1px solid var(--consult-hairline);
  border-radius: 18px;
  background: #FFFFFF;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: border-color 0.18s ease, background-color 0.18s ease, transform 0.12s ease;
}
.gcf-card::after {
  content: '';
  position: absolute;
  right: 1.1rem;
  top: 50%;
  width: 18px;
  height: 18px;
  margin-top: -9px;
  border-radius: 50%;
  border: 1.5px solid var(--consult-hairline);
  transition: border-color 0.18s ease, background-color 0.18s ease, box-shadow 0.18s ease;
}
.gcf-card:hover { border-color: rgba(201, 33, 90, 0.55); }
.gcf-card:active { transform: scale(0.985); }
.gcf-card-ic {
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(23, 22, 27, 0.045);
  color: var(--consult-ink);
  transition: background-color 0.18s ease, color 0.18s ease;
}
.gcf-card-ic svg { width: 22px; height: 22px; }
.gcf-card-text { display: flex; flex-direction: column; gap: 0.2rem; min-width: 0; }
.gcf-card.is-on,
.gcf-card:has(input:checked) {
  border-color: var(--color-magenta-brand);
  background: rgba(201, 33, 90, 0.04);
}
.gcf-card.is-on .gcf-card-ic,
.gcf-card:has(input:checked) .gcf-card-ic { background: rgba(201, 33, 90, 0.12); color: var(--color-magenta-brand); }
.gcf-card.is-on::after,
.gcf-card:has(input:checked)::after {
  border-color: var(--color-magenta-brand);
  background: var(--color-magenta-brand);
  box-shadow: inset 0 0 0 3.5px #FFFFFF;
}
.gcf-card-title { font-family: var(--font-sans); font-size: 0.95rem; font-weight: 600; color: var(--consult-ink); line-height: 1.25; }
.gcf-card-sub { font-family: var(--font-sans); font-size: 0.78rem; line-height: 1.4; color: var(--consult-ink-mute); }

/* Campi di testo. */
.gc-field { display: flex; flex-direction: column; gap: 0.45rem; }
.gc-field[hidden] { display: none; }
.gc-field label,
.gc-field-label {
  font-family: var(--font-sans);
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--consult-ink-mute);
}
.gc-field label em { font-style: normal; font-weight: 400; letter-spacing: 0.04em; text-transform: none; opacity: 0.8; }
.gc-field input,
.gc-field textarea {
  font-family: var(--font-sans);
  font-size: 1rem; /* 16px: sotto, iOS ingrandisce la pagina al focus */
  color: var(--consult-ink);
  background-color: #FFFFFF;
  border: 1px solid var(--consult-hairline);
  border-radius: 12px;
  padding: 0.8rem 0.95rem;
  min-height: 50px;
  width: 100%;
  transition: border-color 0.25s ease, box-shadow 0.25s ease;
}
.gc-field textarea { resize: vertical; min-height: 92px; line-height: 1.45; }
.gc-field input::placeholder,
.gc-field textarea::placeholder { color: rgba(23, 22, 27, 0.32); }
.gc-field input:focus,
.gc-field textarea:focus {
  outline: none;
  border-color: var(--color-magenta-brand);
  box-shadow: 0 0 0 3px rgba(201, 33, 90, 0.14);
}
.gc-field input.is-invalid,
.gc-field textarea.is-invalid { border-color: var(--color-magenta-brand); }
.gc-field .gcf-chips { margin-top: 0.2rem; }
.gc-field .gc-next { margin-top: 0.8rem; }

.gc-error {
  font-family: var(--font-sans);
  font-size: 0.78rem;
  line-height: 1.4;
  color: var(--color-magenta-brand);
  margin-top: 0.35rem;
}
.gc-error[hidden] { display: none; }

/* Bottoni: primario pieno (uno per schermata), fantasma in hairline. */
.gcf-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  min-height: 52px;
  padding: 0.85rem 1.7rem;
  font-family: var(--font-sans);
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-decoration: none;
  border-radius: 999px;
  border: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: transform 0.25s ease, box-shadow 0.25s ease, background-color 0.2s ease;
}
.gcf-btn svg { width: 16px; height: 16px; flex: 0 0 auto; transition: transform 0.3s ease; }
.gcf-btn--primary { color: #FFFFFF; background: var(--color-magenta-brand); }
.gcf-btn--primary:hover { transform: translateY(-1px); box-shadow: 0 14px 30px -14px rgba(201, 33, 90, 0.6); }
.gcf-btn--primary:hover svg { transform: translateX(4px); }
.gcf-btn--primary:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(201, 33, 90, 0.3); }
.gcf-btn--ghost { color: var(--consult-ink); background: transparent; border: 1px solid var(--consult-hairline); }
.gcf-btn--ghost:hover { border-color: var(--color-magenta-brand); }
.gcf-step > .gcf-btn { margin-top: 1.5rem; align-self: stretch; }
.gcf-step .gcf-tiles, .gcf-step .gcf-cards { margin-top: 0.4rem; }
.gcf-step .gcf-chips--center { margin-top: 0.4rem; }
.gcf-btn--send { margin-top: 1.5rem; }
/* 2026-09-09: l'email e' l'azione principale del modulo, WhatsApp la seconda.
   Non "meno importante": piu' piccola e a filo, cosi' la prima cosa che il
   pollice trova e' quella che il cliente ci ha chiesto di privilegiare. */
.gcf-step > .gcf-btn--wa {
  align-self: center;
  min-width: 0;
  min-height: 40px;
  margin-top: 0.7rem;
  padding: 0.5rem 1.2rem;
  font-size: 0.68rem;
  letter-spacing: 0.08em;
  color: var(--consult-ink-mute);
  background: transparent;
  border: 1px solid var(--consult-hairline);
}
.gcf-step > .gcf-btn--wa:hover { color: var(--consult-ink); border-color: rgba(23, 22, 27, 0.28); background: rgba(23, 22, 27, 0.04); }
.gcf-btn--wa svg { width: 15px; height: 15px; }
.gcf-btn--wa:hover svg { transform: none; }

/* IL TELEFONO FUORI ORARIO: SPARISCE, non si spegne. La prima versione lo
   lasciava a schermo disattivato. Non basta: finche' il comando si vede, il
   numero prima o poi si legge, si copia, e la telefonata arriva fuori orario
   lo stesso. Quindi GC_CONTATTI gli mette `hidden` — e qui serve
   `!important`, perche' .gcf-contact-option ha un `display: flex` esplicito
   che lo batterebbe. Il numero non compare da nessuna parte mentre e' chiuso,
   e ogni pagina porta <meta name="format-detection" content="telephone=no">
   perche' su iPhone un numero a schermo diventa toccabile da solo. */
.gcf-contact-option[hidden] { display: none !important; }

/* Al posto della riga "Chiama", a telefono chiuso: solo l'orario, senza
   nemmeno una cifra del numero. Serve a non far sembrare che un telefono non
   esista. */
.gcf-contact-orari {
  font-family: var(--font-sans);
  font-size: 0.8rem;
  line-height: 1.5;
  text-align: center;
  color: var(--consult-ink-mute);
  margin: 0.9rem 0 0;
}
.gcf-btn--send svg { width: 18px; height: 18px; }
.gcf-btn--send:hover svg { transform: none; }

.gcf-contact-alt {
  font-family: var(--font-sans);
  font-size: 0.82rem;
  color: var(--consult-ink-mute);
  text-align: center;
  margin-top: 0.9rem;
}
.gcf-contact-alt a { color: var(--consult-ink); text-decoration: underline; text-underline-offset: 3px; }
.gcf-contact-privacy {
  font-family: var(--font-sans);
  font-size: 0.72rem;
  line-height: 1.5;
  color: var(--consult-ink-mute);
  text-align: center;
  margin-top: 1.2rem;
}

/* Fatto. */
.gcf-contact-view--fatto { align-items: center; text-align: center; padding-top: 1rem; }
.gcf-contact-view--fatto .consult-form-title { margin-top: 1rem; }
.gcf-contact-view--fatto .gcf-contact-sub { max-width: 34ch; }
.gcf-contact-fatto-actions { display: flex; flex-direction: column; gap: 0.6rem; width: 100%; margin-top: 1.5rem; }
.gcf-contact-riepilogo {
  width: 100%;
  margin-top: 1.4rem;
  padding: 1rem 1.1rem;
  border-radius: 14px;
  background: var(--color-offwhite);
  font-family: var(--font-sans);
  font-size: 0.8rem;
  line-height: 1.55;
  color: var(--consult-ink-body);
  text-align: left;
  white-space: pre-wrap;
  word-break: break-word;
}
.consult-success-icon {
  width: 52px;
  height: 52px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #FFFFFF;
  background: var(--color-magenta-brand);
}
.consult-success-icon svg { width: 24px; height: 24px; }

@media (min-width: 769px) {
  .gcf-contact-fatto-actions { flex-direction: row; justify-content: center; }
  .gcf-step > .gcf-btn { align-self: center; min-width: 240px; }
  .gcf-tiles { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .gcf-step > .gcf-btn--send { align-self: center; min-width: 300px; }

  /* 2026-09-08 (rev 3, misurato a 1440x900): la scheda desktop e' alta
     min(86% della finestra, 760px) e non cambia da un passo all'altro -
     apposta, cosi' non salta. L'ultimo passo pero' misurava 714px di
     contenuto contro 681 disponibili: la riga "Le tue risposte arrivano
     solo a Global Contract" restava incollata al bordo arrotondato e i
     37px di padding in fondo finivano nello scroll, con l'aria di un
     testo tagliato. Qui si recuperano ~44px di ritmo verticale SOLO
     sull'ultimo passo e SOLO da 769px in su: il telefono, che e' a tutto
     schermo e scorre di suo, resta com'era. */
  .gcf-step[data-step="contatto"] .gc-field { margin-top: 0.75rem; }
  .gcf-step[data-step="contatto"] > legend + .gc-field { margin-top: 0.9rem; }
  .gcf-step[data-step="contatto"] .gc-field textarea { min-height: 84px; }
  .gcf-step[data-step="contatto"] .gcf-btn--send { margin-top: 1rem; }
  .gcf-step[data-step="contatto"] .gcf-contact-alt { margin-top: 0.6rem; }
  .gcf-step[data-step="contatto"] .gcf-contact-privacy { margin-top: 0.8rem; }

  /* 2026-09-08 (rev 3): la vista "fatto" mostra il riepilogo per intero in
     un riquadro grigio. A 1440x900 quel riquadro arrivava fino al bordo
     inferiore della scheda e l'angolo arrotondato lo tagliava a meta' di
     una riga: si legge come un difetto, non come "c'e' altro sotto".
     Qui la vista prende un'altezza definita (solo quando e' quella
     mostrata, cosi' scelta e modulo continuano a far scorrere il pannello
     come prima) e il riquadro scorre DENTRO se stesso: la scheda finisce
     sempre con il suo margine e l'angolo resta pulito. */
  .gcf-contact[data-view="fatto"] { height: 100%; }
  .gcf-contact[data-view="fatto"] .gcf-contact-view--fatto { min-height: 0; }
  .gcf-contact[data-view="fatto"] .gcf-contact-riepilogo {
    flex: 1 1 auto;
    min-height: 7rem;
    overflow-y: auto;
    overscroll-behavior: contain;
  }
}

.consult-caption {
  font-family: var(--font-sans);
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.03em;
  color: var(--consult-ink-mute);
  margin: 0;
}

/* MOBILE: near full-viewport height so this reads as its own "room" when
   scrolled to - a section shorter than the viewport left slivers of the
   dark sections above/below both visible at once, cramped and unclean.
   min-height (not height) so real content that overflows 100svh still
   gets scroll room rather than clipping; svh first with a vh fallback for
   engines without svh support - was dvh, see the 2026-09-02 note on the
   base .consult-section rule above (curtain widen/lag fix: svh doesn't
   move under the pin while the mobile chrome animates mid-scroll). */
@media (max-width: 768px) {
  .consult-section {
    padding: 3.5rem 1.5rem;
    min-height: 100vh;
    min-height: 100svh;
  }
  /* 2026-08-27: the map is the OBJECT slot now, portrait on mobile - the
     folding rule stands vertically and the eight pills sit ON it, spread
     left and right of the spine rather than clinging to a frame edge.
     Placement is written by JS from the rule's own joints, so there are no
     per-pill percentage rules at this breakpoint any more (the old
     --01..--08 top/left table is gone on purpose: it would have been dead
     CSS shadowed by the inline values JS writes). */
  /* 272, not 300: measured at 390x844 the section came out 885px tall with
     a 300px slot, i.e. 41px past the viewport - and the curtain pin holds
     #contatti at exactly 100dvh with overflow:hidden, so that surplus is
     clipped rather than scrolled. At 272 the whole stack (copy + button +
     caption + object) measures 817px and clears one viewport with room. */
  /* Wider than the CTA button on purpose - the Z is the anchor of the block
     now, not a small diagram under it. */
  .consult-map { max-width: min(360px, 94vw); margin-top: 1rem; }
  /* The phone rule is a WIDE ZIGZAG, not a near-vertical line: swinging the
     nine segments hard off the vertical (SEG_V in js/main.js) covers the same
     eight joints in far less height. The slot follows - 300/430 portrait was
     making the section ~885px tall at 390x844, past the viewport the curtain
     pin holds it at. */
  /* Taller than the Z itself on purpose. The SVG is 'meet'-fitted, so the Z
     lands centred at the box's full width and leaves a band of empty space
     above and below it - which is exactly where the top-run and bottom-run
     labels go. */
  /* La sfera con le otto etichette appoggiate sui vertici. L'altezza resta
     il vincolo stretto: il blocco deve stare dentro una schermata da iPhone
     insieme al bottone. */
  .consult-map-hub { aspect-ratio: 356 / 400; }

  .consult-orbit-pill-body {
    /* Su telefono le etichette vanno a capo. Non e' un vezzo: la larghezza
       dell'etichetta e' cio' che limita il raggio del globo, perche' fra il
       profilo e il bordo dello schermo ci deve stare l'etichetta intera piu'
       la stanghetta. Su una riga sola "Montaggio e installazione" misura
       133px e obbligherebbe a un globo da 120px; a capo dentro 96px il globo
       torna a essere il soggetto. */
    white-space: normal;
    max-width: 86px;
    /* tutte le capsule alte uguali: una su una riga sola e una su due righe
       che si alternano fanno sembrare storta anche una griglia perfetta */
    min-height: 2.5em;
    text-align: left;
    line-height: 1.25;
    font-size: 0.55rem;
    padding: 0.32rem 0.55rem;
  }
  /* Il livello che galleggia. Sta SOPRA la capsula bianca e contiene anche la
   stanghetta, cosi' l'etichetta si muove tutta insieme invece di muovere il
   proprio contenuto restando ferma. */
.consult-orbit-pill-drift {
  position: relative;
  display: inline-flex;
  will-change: transform;
}

.consult-orbit-pill-body i { width: 4px; height: 4px; }

  /* Mobile form padding only - width/height/border-radius are JS-driven
     (see initConsultExpandForm's fullscreenTarget: edge-to-edge on
     mobile, "mobile full-screen, desktop near-full-screen centered
     panel"). */
  .consult-form-panel {
    padding: 1.6rem 1.4rem calc(1.7rem + env(safe-area-inset-bottom, 0px));
  }
}

/* DESKTOP: one centered column (same alignment as mobile, just wider and
   re-tuned type) - the text container is fluid (clamp, not a fixed cap)
   so it keeps growing with the window instead of stalling early and
   leaving empty side voids; the lede's own ch-cap is dropped at this
   width so it grows with the same column instead of sitting narrower
   than the headline above it. Image slot stays a moderate, landscape
   object below the button (order matches mobile now - no CSS `order`
   needed). Padding tightened from the mobile default so the whole stack
   clears one 1440 viewport with room to spare (verified at runtime, see
   the project log). */
@media (min-width: 769px) {
  .consult-section {
    padding: clamp(3rem, 5vw, 4rem) 1.75rem;
    border-radius: 48px 48px 0 0;
    position: relative;
    /* Full-viewport to match mobile - the curtain reveal (initCurtainReveal
       in js/main.js) pins this section edge-to-edge behind #settori, so it
       needs to actually fill the screen at every width or the pin would
       leave gaps above/below its own box. Desktop content already clears
       one 1440 viewport with room to spare (see comment below), so this
       never forces internal scrolling in normal use. */
    /* was dvh - see the 2026-09-02 note on the base .consult-section
       rule above (curtain widen/lag fix). */
    min-height: 100vh;
    min-height: 100svh;
  }

  /* 2026-09-08 — LA SEZIONE ERA UNA COLONNA CENTRATA, E LA GERARCHIA ERA
     ROVESCIATA.
     Su 1440 il titolo si stendeva su 938px al centro, il bottone galleggiava
     da solo in mezzo al bianco e sotto arrivava un globo da 387px di diametro
     dentro una fascia larga 1210. Il risultato: l'oggetto DECORATIVO era la
     cosa piu' grande e piu' dettagliata dello schermo, e l'unica cosa che la
     sezione deve far fare - premere il bottone - era l'elemento piu' piccolo,
     senza niente intorno che lo tenesse. In piu' su uno schermo basso
     (1728x832, un portatile) il globo finiva tagliato dal bordo.
     Da 1100px in su la sezione diventa quindi DUE COLONNE: a sinistra
     l'argomento per intero (occhiello, titolo, riga, bottone,
     rassicurazione), a destra il globo con le sue otto etichette. Il bottone
     torna in fondo a un percorso di lettura invece di stare in mezzo al
     nulla, il globo si rimpicciolisce fino a stare comodo nella propria
     colonna, e la larghezza dello schermo viene finalmente usata.
     Fra 769 e 1099px resta la colonna centrata - due colonne li' darebbero un
     globo troppo piccolo per reggersi - ma con il riquadro ridimensionato.
     Il telefono (max-width: 768px) non e' toccato da nessuna di queste
     regole. */
  .consult-inner { max-width: clamp(720px, 84vw, 1280px); }
  /* Il <br> del titolo resta soppresso qui: nella colonna centrata la riga
     unica e' giusta. Torna a valere da 1100px in su, dove la colonna e'
     stretta e il titolo va impilato. */
  .consult-statement { font-size: clamp(2.6rem, 4.4vw, 4.2rem); }
  .consult-statement-break { display: none; }
  .consult-lede { max-width: none; }

  /* Il riquadro del globo non e' piu' un nastro 1210x440 largo quanto il
     testo: e' un riquadro proporzionato, alto quanto serve e non di piu'.
     L'altezza e' in svh (non dvh) come tutto il resto di questa sezione: la
     barra del browser che si ritira non deve poter ridimensionare un riquadro
     che contiene quattro etichette posizionate in pixel. */
  .consult-map { width: min(100%, 820px); max-width: 820px; margin: 2.2rem auto 0; }
  .consult-map-hub { aspect-ratio: auto; height: clamp(340px, 44svh, 420px); }

  /* LE ETICHETTE VANNO A CAPO ANCHE QUI, e non e' un vezzo: e' la loro
     larghezza a decidere quanto puo' essere grande il globo, perche' fra il
     profilo e il bordo del riquadro ci deve stare l'etichetta intera piu' la
     stanghetta. Su una riga sola "Montaggio e installazione" misura 172px e
     costringeva il globo a restare piccolo dove lo spazio e' stretto, oppure
     - com'era su 1440 - a prendersi tutta la larghezza della pagina.
     A capo dentro 134px le otto capsule occupano meno di metri lineari e il
     globo torna a essere un oggetto, non uno sfondo.
     Altezza minima uguale per tutte: una capsula su una riga accanto a una su
     due righe fa sembrare storta anche una disposizione esatta. */
  .consult-orbit-pill-body {
    font-size: 0.72rem;
    padding: 0.45rem 0.8rem;
    white-space: normal;
    /* 114 e non 134: e' la larghezza a cui ogni etichetta va a capo su uno
       SPAZIO invece che su un trattino. A 134 "Assistenza post-vendita" si
       spezzava in "Assistenza post-" / "vendita", che si legge male ed e'
       l'unica cosa storta in una fila di otto capsule identiche. */
    max-width: 114px;
    /* Altezza fissa, non `min-height` in em: con box-sizing: border-box un
       valore in em misura la scatola INTERA (testo + imbottitura + bordo), e
       2.6em ne coprivano appena il testo - le capsule su una riga restavano
       piu' basse di quelle su due e la fila si vedeva storta. 46px e'
       esattamente l'altezza di una capsula su due righe a questo corpo. */
    min-height: 46px;
    text-align: left;
    line-height: 1.3;
  }
  /* Il livello che galleggia. Sta SOPRA la capsula bianca e contiene anche la
   stanghetta, cosi' l'etichetta si muove tutta insieme invece di muovere il
   proprio contenuto restando ferma. */
.consult-orbit-pill-drift {
  position: relative;
  display: inline-flex;
  will-change: transform;
}

.consult-orbit-pill-body i { width: 5px; height: 5px; }
}

/* -----------------------------------------
   CTA FINALE, DESKTOP LARGO (>= 1100px): DUE COLONNE.
   Sinistra: l'argomento e l'azione, allineati a bandiera, uno sotto
   l'altro, con il bottone come ultima cosa che si legge.
   Destra: il globo con le otto discipline.
   La soglia e' 1100 e non 769 perche' sotto quella larghezza la colonna di
   destra non ha abbastanza spazio per un globo che regga il confronto con il
   titolo: meglio la colonna centrata, con il riquadro piu' basso.
   ----------------------------------------- */
@media (min-width: 1100px) {
  .consult-inner {
    display: grid;
    /* 0.86 / 1.14: la colonna del globo deve essere la piu' larga delle due,
       perche' oltre al globo deve contenere due file di etichette. La colonna
       del testo resta comunque sopra i 480px, cioe' una misura di lettura
       giusta per un titolo di questa dimensione. */
    grid-template-columns: minmax(0, 0.86fr) minmax(0, 1.14fr);
    column-gap: clamp(2.5rem, 4vw, 3.6rem);
    align-items: center;
    /* Il tetto e' 1240 e non 1320: sopra i 1440 il corpo del titolo e il
       raggio del globo sono gia' al loro massimo, quindi allargare ancora il
       contenitore non ingrandisce niente - allontana soltanto le due colonne
       e lascia un vuoto in mezzo. Fermando il contenitore, da 1440 in su la
       composizione resta identica e cresce solo il margine intorno, che e'
       il comportamento giusto per una sezione che occupa tutto lo schermo. */
    max-width: clamp(980px, 86vw, 1240px);
    text-align: left;
  }

  /* Qui l'involucro smette di essere trasparente e diventa la cella di
     sinistra: una colonna sola, cosi' l'altezza in eccesso del globo non puo'
     piu' finire fra il titolo e il bottone. */
  .consult-pitch {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    text-align: left;
  }

  .consult-copy { text-align: left; }
  .consult-letterhead { margin-bottom: 1.05rem; }

  /* Il titolo torna su due righe: nella colonna stretta la riga unica non ci
     sta, e comunque "Non devi gestirlo / tutto da solo." e' una battuta che
     vuole andare a capo li'. */
  /* 4.85vw non e' un numero tondo: e' la dimensione a cui "Non devi
     gestirlo" riempie esattamente la colonna di sinistra a ogni larghezza
     fra 1100 e 1600, senza andare a capo da solo. Sotto quella misura il
     testo lasciava 120px vuoti fra se' e il globo e la colonna sembrava
     leggera; il tetto a 4.4rem impedisce che su un 27 pollici il titolo
     diventi un'insegna. */
  .consult-statement { font-size: clamp(2.7rem, 4.85vw, 4.4rem); }
  .consult-statement-break { display: inline; }

  /* Misura di lettura vera, non tutta la colonna: una riga di 34 caratteri
     sotto un titolo di 60px si legge in un colpo d'occhio. */
  .consult-lede { max-width: 36ch; font-size: 1.05rem; margin: 1.35rem 0 0; }

  /* L'azione non e' piu' centrata in mezzo alla pagina: sta in fondo alla
     colonna, allineata al titolo, che e' dove l'occhio arriva dopo aver
     letto. */
  .consult-action {
    align-items: flex-start;
    gap: 1.1rem;
    margin-top: 2.5rem;
  }
  .consult-caption { text-align: left; }

  /* Il globo occupa la sua colonna per intero e viene centrato verticalmente
     rispetto all'argomento. Nessun margine: e' la griglia a tenere le
     distanze. */
  .consult-map {
    grid-column: 2;
    width: 100%;
    max-width: none;
    margin: 0;
    justify-self: center;
  }
  .consult-map-hub { height: clamp(400px, 52svh, 560px); }
}

/* -----------------------------------------
   10. FOOTER SECTION
   ----------------------------------------- */
.main-footer {
  background-color: var(--color-bg-dark);
  /* 2026-08-17 (restyle-agenti, site-wide rhythm): 0.05 -> 0.06, same
     normalization as .consult-section above - see that rule's comment. */
  border-top: 1px solid rgba(255, 255, 255, 0.06);
  padding: 2.5rem 0;
  position: relative;
  z-index: var(--z-content);
}

.footer-container {
  max-width: 1440px;
  margin: 0 auto;
  padding: 0 4rem;
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 1.5rem;
}

.main-footer p {
  font-family: var(--font-sans);
  font-size: 0.75rem;
  color: rgba(255, 255, 255, 0.4);
  letter-spacing: 0.02em;
}

.footer-links {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.footer-links a {
  font-family: var(--font-sans);
  font-size: 0.75rem;
  color: rgba(255, 255, 255, 0.4);
  text-decoration: none;
  transition: var(--transition-fast);
}

.footer-links a:hover {
  color: var(--color-magenta-glow);
}

.separator {
  color: rgba(255, 255, 255, 0.15);
  font-size: 0.85rem;
}

/* -----------------------------------------
   11. RESPONSIVE MEDIA QUERIES
   ----------------------------------------- */
@media (max-width: 1024px) {
  .header-container {
    padding: 0 2rem;
  }
  
  .hero-subtitle {
    font-size: 0.85rem;
    line-height: 1.6;
  }
  
  .sector-panel {
    flex-grow: 1;
    flex-basis: 180px; /* Stack vertically with height base */
    height: 240px;
    border-right: none;
    border-bottom: 1px solid rgba(255, 255, 255, 0.06);
    padding: 1.5rem;
  }
  

  
  .section-title-wrapper {
    padding: 4rem 2rem 2.5rem 2rem;
  }

  .details-title {
    font-size: 2.25rem;
  }
  
  .footer-container {
    padding: 0 2rem;
    flex-direction: column;
    text-align: center;
  }
}

/* 2026-08-24 - fix "card dei settori sotto la piega" nella fascia 769-1024px.
   La regola .section-title-wrapper del blocco max-width:1024px qui sopra e'
   pensata per il layout mobile/tablet (dove la gallery desktop e' display:none
   e le .mobile-card scorrono in colonna): aggiunge 4rem+2.5rem = 104px di
   padding verticale. Sopra i 769px pero' e' ancora attiva la .desktop-gallery,
   che ha un budget verticale fisso (min-height 340px + 2rem/3rem di margine).
   Sommati al padding fluido di .sectors-section, quei 104px portano la sezione
   oltre il suo min-height:100vh: con justify-content:center il contenuto
   deborda in basso e la fila di card finisce fuori dalla viewport proprio
   mentre il titolo e' inquadrato - da cui "le card non si vedono".
   Misurato a 788x716: sezione 745px contro 716px di viewport, 29px di
   eccedenza; a 1000x757: 758px contro 757px, 1px. Azzerando il solo padding
   verticale (quello laterale resta, serve al testo) la sezione torna a 641px
   a 788x716 e la fascia 769-1024 si comporta come il desktop pieno, dove
   questa regola non e' mai stata applicata. Mobile (<=768px) non e' toccato. */
@media (min-width: 769px) and (max-width: 1024px) {
  .sectors-section .section-title-wrapper {
    padding-top: 0;
    padding-bottom: 0;
  }
}

@media (max-width: 768px) {
  /* Hero photo must fill from the very top (behind the fixed floating navbar),
     with no padding-top gap that leaves a black band under the header. */
  .hero-section {
    padding-top: 0;
    height: 100vh;
    min-height: 100vh;
  }

  .main-header {
    width: 98%; /* Extra room so the bigger wordmark + CTA both fit */
    padding: 0.4rem 0.7rem;
  }

  .nav-menu {
    display: none; /* Hide desktop nav menu */
  }

  .logo-text {
    font-size: 0.74rem;
    letter-spacing: 0.08em;
    margin-left: 0.45rem;
  }
  .nav-logo-img {
    height: 30px;
  }
  .logo-link {
    height: 30px;
  }
  
  .mobile-menu-toggle {
    display: flex; /* Show custom hamburger on mobile */
  }
  
  .header-actions {
    gap: 0.3rem;
  }

  .mobile-menu-toggle {
    margin-left: 0.3rem;
  }
  
  /* Removed on mobile per explicit request: the persistent navbar CTA
     competed with the in-page CTAs for attention right at the top of every
     screen. "Richiedi consulenza" is still one tap away in the hamburger
     menu (.mobile-cta-item), so nothing is lost — just decluttered from
     the bar that's visible 100% of the time. */
  .header-actions .btn-outline {
    display: none !important;
  }
  
  .hero-title-top {
    font-size: clamp(1.7rem, 6.2vw, 2.15rem);
    line-height: 1.12;
    letter-spacing: -0.015em;
  }

  .hero-title-bottom {
    font-size: clamp(0.6rem, 2.3vw, 0.68rem);
    letter-spacing: 0.16em;
  }
  
  .hero-stats-container {
    display: grid;
    grid-template-columns: repeat(2, 1fr); /* 2x2 layout on mobile */
    gap: 1.25rem 1.5rem;
    max-width: 100%;
    margin-top: 0.75rem;
  }
  
  .stat-block {
    flex: none;
  }
  
  .stat-number {
    font-size: 1.45rem;
    margin-bottom: 0.2rem;
  }
  
  .stat-label {
    font-size: 0.52rem;
    line-height: 1.3;
  }

  /* Tight top (header sits high) + a big bottom black gap - the section bg
     itself is --color-bg-dark, the marquee below is the only white block
     inside it, so a generous bottom padding here IS the "black breathing
     space" that closes out the section before the next one.
     2026-08-17 (settori-fix, spacing polish): bottom 5.5rem -> 6rem (88px
     -> 96px), the nearest clean 8px-scale step, onto the same 8px-multiple
     scale as .gc-founder-section (48/48) and .consult-section (64/64).
     .gc-trust-block's own padding-bottom (100px mobile / a clamp() on
     desktop) is NOT touched despite also being off that scale - its
     in-file comments (search "recover the height this pass's
     header/spacing changes need") document it as precision-balanced
     against the trust-marquee's height across several prior passes, not
     an arbitrary value; changing it blind risks breaking that balance. */
  .sectors-section {
    padding: 1.5rem 1.25rem 6rem 1.25rem;
  }

  .desktop-gallery {
    display: none !important;
  }

  .mobile-gallery {
    display: flex;
    flex-direction: column;
    gap: 0.85rem;
    width: 100%;
    max-width: 460px;
    margin: 2rem auto 2.5rem auto;
    padding-bottom: 1rem;
  }

  /* ---- Photo-first sector card: full-bleed photo, single warm scrim ----
     2026-08-06: replaced the per-category tinted-panel look (photo as a
     44%-wide flex sibling, --accent color-mix backgrounds/borders/feather)
     with a photo-first design. --accent custom properties are still set
     inline per card (index.html) but are now UNUSED by this stylesheet -
     left in place rather than stripped, since removing them touches
     markup outside this section's styling and they're inert either way.
     Text-block position/width is UNCHANGED from the old flex split (44%
     photo / 56% panel for full-width, 46%/54% top/bottom for compact) -
     only now achieved via absolute positioning over a full-bleed photo
     instead of flex sizing, so padding/alignment/consistency (#7) needed
     zero changes. */
  .sector-card {
    /* Local tokens (scoped to this section only, not touching :root). */
    --scrim-ink: 15, 14, 12;                                        /* #0F0E0C */
    /* 2026-08-06 (reorder+grade pass): pulled back further from the earlier
       0.94/1.03/0.98 - the warm cards (Bar/Ristoranti/Gelaterie) still read
       candy-amber against the cream numbers. saturate lowered to the 0.90
       floor (spec's guardrail: below 0.85 goes dead/catalog), contrast
       nudged up slightly for depth, brightness barely under 1 to seat
       blacks into the scrim ink. No hue-rotate/sepia - pulling toward
       neutral, not warming. */
    /* NOT APPLIED AS A FILTER ANY MORE — kept as the authoritative record of
       what is baked into the -m.webp set. These three numbers and the ones in
       scripts/gcbake_grade.py must agree; see .sector-card .mobile-card-img. */
    --card-photo-filter: saturate(0.90) contrast(1.05) brightness(0.97);
    position: relative;
    display: block;
    text-decoration: none;
    color: inherit;
    -webkit-tap-highlight-color: transparent;
    border-radius: 14px;
    overflow: hidden;
    cursor: pointer;
    /* 2026-08-17: 148px -> 188px -> 220px (filled-pill CTA clearance) ->
       175px (settori-redesign, CTA back to normal flow) -> 200px
       (restyle-agenti: CTA's tap-height padding grew 4px->16px/side to
       clear this pass's 44px hit-area test, adding ~24px to the stack) ->
       224px (restyle-agenti-2: enlarged full-card type scale + bottom-
       anchored panel needs more room for a 2-line title, e.g. "Gelaterie
       e Pasticcerie" - live-measured 4.5px top clipping at 200px, fixed
       with margin to spare). */
    min-height: 224px;
    background: #0F0E0C;           /* neutral fallback while the photo loads */
    /* 2026-08-17 (settori-fix, border polish): 0.08 -> 0.14 opacity. radius
       (14px) was already identical on every card regardless of shape -
       confirmed live, nothing to unify there - but the hairline itself was
       near-invisible against several of the brighter photos, undermining
       its own job of giving each card a defined edge separate from its
       neighbor. Still a hairline, just one that reads against light and
       dark photos both. */
    border: 1px solid rgba(255,255,255,0.14);
    box-shadow: 0 10px 30px -12px rgba(0,0,0,0.6);
  }

  /* 2026-08-18 (restyle-agenti-3, fix B): the CSS-class/IntersectionObserver
     reveal that used to live here is gone - initMobileSectorReveal in
     js/main.js now drives this entirely through GSAP/ScrollTrigger.batch,
     with gsap.set/gsap.to owning both the hidden and revealed states
     inline (no .reveal-* classes, no separate reduced-motion CSS
     override block needed - un-set is simply visible). */

  /* Full-bleed photo, behind everything. */
  .sector-card-photo {
    position: absolute;
    inset: 0;
    z-index: 0;
    overflow: hidden;
  }

  /* The grade that --card-photo-filter used to apply here is now BAKED INTO
     the -m.webp files (scripts/gcbake_grade.py). It is a static colour grade —
     nothing in it depends on scroll, hover or state — but as a CSS `filter` on
     a full-bleed <img> it was recomputed by the compositor on every frame, for
     seven full-width photos in a list whose whole purpose is to be scrolled.
     The -m set is served only to phones (800w in every srcset, and the
     max-width:768px preload), so baking it there leaves desktop's own
     different filters untouched. Verified to 1/255 against the spec's
     matrices. If the grade ever changes, change SATURATE/CONTRAST/BRIGHTNESS
     in that script and re-run it — do not reintroduce the filter here. */
  .sector-card .mobile-card-img {
    width: 100%;
    height: 100%;
    object-fit: cover;
  }

  /* Legibility scrim (2026-08-18, restyle-agenti-2 pass - re-fit after this
     pass moved the text block to a bottom-anchored, right-half position
     (fix B) and enlarged the full-card type scale (fix A), both of which
     shifted where the text actually sits. Re-measured live: title+count+
     label+CTA union now spans 56.8-94.5% horizontal, 8.7-89.9% vertical
     (worst case, 2-line title, e.g. "Gelaterie e Pasticcerie"). Ellipse
     re-centered on the new box with the same soft-feather approach as the
     prior pass. */
  .sector-card-scrim {
    position: absolute;
    inset: 0;
    z-index: 1;
    pointer-events: none;
    background: radial-gradient(ellipse 34% 48% at 76% 55%,
      rgba(var(--scrim-ink), 0.82) 0%,
      rgba(var(--scrim-ink), 0.6)  40%,
      rgba(var(--scrim-ink), 0.25) 70%,
      rgba(var(--scrim-ink), 0)    100%);
  }

  /* Text block: right-anchored, bottom-weighted box (2026-08-18,
     restyle-agenti-2 pass - was inset:0 0 0 44% (full card height,
     content pinned to the TOP via block flow + padding-top only) which
     read semi-centered and top-heavy. auto/0 on top/bottom instead of
     0/0 makes this a height:auto box that hugs its own content and
     sits at the card's bottom edge; left moved 44%->52% so the block
     sits more clearly in the card's right half rather than just past
     center. */
  .sector-card-panel {
    position: absolute;
    inset: auto 0 0 50%;
    z-index: 2;
    padding: 0 0.9rem 1.35rem 1.05rem;
    /* 2026-08-17 (settori-fix pass): was display:flex; flex-direction:column;
       justify-content:flex-start. Dropped - title/count/label now carry
       their own per-line background chips (box-decoration-break:clone,
       see below), which requires them to be genuine inline boxes in normal
       flow; flexbox blockifies flex-item children regardless of their own
       authored `display`, which would collapse each chip to one rectangle
       sized to its widest wrapped line instead of hugging every line. Plain
       block flow stacks title -> meta -> arrow top-to-bottom exactly the
       same way (arrow is position:absolute, unaffected either way) without
       that side effect. */
    overflow: hidden;
  }
  /* Blueprint grid texture - belonged to the old tinted-panel look. OFF by
     default; uncomment to re-enable (kept as one rule, not scattered). */
  /*
  .sector-card-panel::before {
    content: "";
    position: absolute;
    inset: 0;
    background-image:
      linear-gradient(rgba(255,255,255,0.10) 1px, transparent 1px),
      linear-gradient(90deg, rgba(255,255,255,0.10) 1px, transparent 1px),
      linear-gradient(rgba(255,255,255,0.05) 1px, transparent 1px),
      linear-gradient(90deg, rgba(255,255,255,0.05) 1px, transparent 1px);
    background-size: 44px 44px, 44px 44px, 11px 11px, 11px 11px;
    background-position: -1px -1px;
    opacity: 0.5;
    mask-image: linear-gradient(120deg, rgba(0,0,0,0.15) 0%, rgba(0,0,0,0.9) 100%);
    -webkit-mask-image: linear-gradient(120deg, rgba(0,0,0,0.15) 0%, rgba(0,0,0,0.9) 100%);
    pointer-events: none;
  }
  */

  /* No boxed plate (2026-08-17, settori-redesign pass). Two prior passes
     tried a per-line box-decoration-break chip system, then a single
     wrapping .sector-card-plate with a frosted background+blur - both
     read as a heavy, murky slab sitting on the photo rather than text
     that belongs to the image ("il plate testo e fangoso e il blur e
     brutto" - direct client feedback). Both are gone. Legibility now
     comes entirely from the .sector-card-scrim gradient above plus
     text-shadow below - the classic editorial photo-caption technique
     (magazine credit lines, not UI cards) - so text reads as sitting IN
     the photo, not on a panel bolted over it. */
  /* 2026-08-18 (restyle-agenti-2 pass): 1.05rem -> 1.32rem. This is now the
     FULL-WIDTH card's own scale (Bar/Ristoranti/Gelaterie e Pasticcerie) -
     .sector-card--compact overrides it back down below. Before this pass
     both shapes shared one identical scale regardless of card size. */
  .sector-card-title {
    font-family: var(--font-sans);
    font-size: 1.32rem;
    font-weight: 700;
    line-height: 1.12;
    letter-spacing: 0.015em;
    text-transform: uppercase;
    color: var(--color-white);
    text-shadow: 0 1px 4px rgba(0, 0, 0, 0.7), 0 1px 2px rgba(0, 0, 0, 0.5);
  }

  /* Mobile-only description line (2026-08-06) - .sector-card is already a
     mobile-exclusive component (.mobile-gallery is display:none outside
     this same max-width:768px breakpoint), so this rule needs no separate
     "hidden on desktop" toggle - it simply never renders there, same as
     every other .sector-card-* rule in this block. Size/margin unified to
     the square card's own value (canonical scale, same reasoning as title
     above) - was 0.72rem/-0.35rem-0.7rem on full-width cards, big enough
     to crowd into the number block below on the taller-content full cards.
     2026-08-06 (bento-rebalance): clamped to 1 line - measured real
     overflow (screenshot-verified, not just bounding-box math) on compact
     cards with a 2-line title (e.g. "Spazi Commerciali") + a long
     description: "PROGETTI REALIZZATI" was getting clipped by the card's
     own overflow:hidden.
     2026-08-07 (legibility pass): line-clamp/ellipsis REMOVED - full copy
     now shows on up to 2 lines, no truncation. The vertical room this
     reclaims is paid for by tightening line-height (1.35->1.22) and
     .sector-card--compact .sector-card-panel's top inset below, not by
     cutting text. */
  .sector-card-desc-mobile {
    display: block;
    position: relative;
    font-family: var(--font-sans);
    font-size: 0.66rem;
    font-weight: 400;
    line-height: 1.22;
    color: rgba(239, 233, 223, 0.78);
    text-shadow: 0 1px 3px rgba(0, 0, 0, 0.6), 0 0 2px rgba(0, 0, 0, 0.45);
    margin: -0.1rem 0 0.28rem 0;
  }

  /* 2026-08-17 (settori-redesign pass): title/meta/CTA all stack via plain
     block flow now (no plate to carry the rhythm), so each tier owns its
     own margin instead of a shared padding box. margin-top here is the
     title-to-count gap. */
  .sector-card-meta {
    position: relative;
    margin-top: 0.3rem;
  }
  /* Cormorant lining-nums, the card's one serif accent and its visual
     hero - the number is the credibility signal (280+ progetti), title
     is the identifier. 2026-08-17 (settori-redesign pass): 1.4rem -> 1.6rem
     line-height 1.1 -> 1 - with no plate padding competing for vertical
     room, gave the number more presence; still well inside the card's
     height budget once the plate's own padding was reclaimed (see
     .sector-card min-height below). */
  .sector-card-count {
    display: block;
    font-family: 'Cormorant Garamond', var(--font-serif), serif;
    font-variant-numeric: lining-nums;
    font-feature-settings: "lnum" 1;
    font-size: 2.15rem;
    font-weight: 600;
    color: #F5F1E8;
    line-height: 1;
    text-shadow: 0 1px 4px rgba(0, 0, 0, 0.7), 0 1px 2px rgba(0, 0, 0, 0.5);
    margin-bottom: 0.15rem;
  }
  /* 2026-08-17 (settori-redesign pass): color brightened #9A968E -> #C7C2B6
     - the darker value was calibrated to sit on the old plate's near-
     opaque background; directly on the scrim+photo it read muddy/low-
     contrast, the same client complaint the plate itself drew ("fangoso").
     white-space:nowrap keeps "PROGETTI REALIZZATI" on its one line (the
     markup's own <br> is gone, single text node). */
  .sector-card-label {
    display: block;
    font-family: var(--font-sans);
    font-size: 0.7rem;
    font-weight: 600;
    /* 2026-08-18 (restyle-agenti-2 pass): 0.14em -> 0.06em - the narrower
       right-anchored panel (fix B) doesn't have room for "PROGETTI
       REALIZZATI" at the old tracking; live-measured 22px overflow past
       the panel's available width at 0.14em, none at 0.06em. */
    letter-spacing: 0.045em;
    text-transform: uppercase;
    color: #C7C2B6;
    line-height: 1.3;
    white-space: nowrap;
    text-shadow: 0 1px 3px rgba(0, 0, 0, 0.7), 0 1px 2px rgba(0, 0, 0, 0.5);
  }

  /* 2026-08-17 (settori-redesign pass): brand rule is magenta=CTA, and
     ONLY the CTA - the whole card is already the /projects deep-link, so
     SCOPRI is a light visual signal, not a button. Was position:absolute,
     left-aligned to a plate that no longer exists (see prior passes' notes
     - real bugs in that mechanism, now moot). Normal block flow instead:
     one more line in the same stack as title/count/label, own margin-top
     for separation, no position/left/bottom math needed at all - it's
     naturally left-aligned with everything above it because they're all
     the same panel with the same padding.
     2026-08-17 (restyle-agenti pass): vertical padding 4px -> 16px - the
     visible text/icon stay exactly the same small size (light, per spec),
     but the tappable hit area is invisible padding, not visible size, and
     this pass's acceptance test puts a real 44px floor back on it (the
     redesign pass before this one had no such test and under-shot it at
     ~22px). Live-verified height 46px (16+16 padding + 14px icon, the
     tallest inline child) on every card. */
  .sector-card-arrow {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    margin-top: 0.55rem;
    padding: 16px 0;
    background: transparent;
    border: none;
    color: #C9215A;
  }
  .sector-card-arrow-text {
    display: inline;
    font-family: var(--font-sans);
    font-size: 0.8rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    color: inherit;
    line-height: 1;
  }
  .sector-card-arrow svg { width: 14px; height: 14px; flex-shrink: 0; }
  /* Subtle only, per spec - no fill/glow/scale (those belonged to the
     filled-pill version). An arrow nudge is real motion, so it's gated
     behind prefers-reduced-motion: no-preference entirely rather than
     included-then-cancelled - the reduced-motion default is simply no
     transition and no hover rule at all, not a motion that gets switched
     off. Opacity dim is left ungated (a fade is conventionally exempt,
     and the affordance is decorative - the card itself is still the real
     tap target either way). */
  .sector-card:hover .sector-card-arrow,
  .sector-card:focus-visible .sector-card-arrow {
    opacity: 0.75;
  }
  @media (prefers-reduced-motion: no-preference) {
    .sector-card-arrow svg { transition: transform 0.2s ease; }
    .sector-card:hover .sector-card-arrow svg,
    .sector-card:focus-visible .sector-card-arrow svg {
      transform: translateX(3px);
    }
  }

  /* ---- Compact 2-up row (Salumerie + Tabaccherie): stack photo/panel ---- */
  .sector-card-row {
    display: flex;
    gap: 0.85rem;
    width: 100%;
  }
  /* 2026-08-17 (settori-redesign pass): was 266px, sized for the old
     filled-pill CTA (absolute, 56px tall) plus a padded plate. Both are
     gone - content is now title+count+caption+a light one-line text
     link, all in normal flow.
     2026-08-17 (restyle-agenti pass): 200px -> 225px - the CTA's own
     padding grew 4px->16px/side (~24px) to clear this pass's 44px hit-
     area test; re-verified live, no clipping. */
  .sector-card--compact {
    flex: 1 1 0;
    min-width: 0;
    min-height: 225px;
  }
  /* Two-up cards: bottom-anchored (text sits at the bottom). 2026-08-17
     (settori-redesign pass): was a minimal 0.14 cohesion tint (legibility
     came from the plate instead, back when there was one) - now this IS
     the legibility mechanism, strengthened to carry full contrast on its
     own: rich dark at the very bottom (behind caption/link), fading out
     by 68% up so the photo's upper portion stays clear. */
  .sector-card--compact .sector-card-scrim {
    background: linear-gradient(to top,
      rgba(var(--scrim-ink), 0.92) 0%,
      rgba(var(--scrim-ink), 0.72) 20%,
      rgba(var(--scrim-ink), 0.32) 42%,
      rgba(var(--scrim-ink), 0)    68%);
  }
  /* 2026-08-18 (restyle-agenti-3, fix D): inset 22% 0 0 0 -> auto 0 0 0.
     The old fixed 22%-from-top box meant title/count/label always started
     at the same Y regardless of title line count, so the COUNT (the
     card's visual anchor, per the base rule's own comment) landed at a
     different Y depending on whether the title above it wrapped to 1 or 2
     lines - live-measured count baseline at 45.7% of card height for a
     1-line title (Farmacie) vs 53.6% for every 2-line title (Spazi
     Commerciali, Salumerie e Panifici, Tabaccherie). Bottom-anchoring
     instead (auto top, fixed bottom, height:auto hugging content) is the
     same fix already proven on the full-width cards in the prior pass:
     the count/label/CTA stack sits a fixed distance from the CARD BOTTOM
     regardless of how tall the title above it grows, so a longer title
     eats into empty space above rather than pushing the anchor down.
     Re-measured after: count baseline 64.2% of card height on all 4
     square cards, exactly, regardless of 1-line or 2-line title. */
  .sector-card--compact .sector-card-panel {
    inset: auto 0 0 0;
    padding: 0 0.8rem 0.65rem;
  }
  /* 2026-08-18 (restyle-agenti-2 pass): compact/square cards (Farmacie,
     Spazi Commerciali, Salumerie e Panifici, Tabaccherie) now carry their
     OWN, smaller type scale - the base rules above were bumped up to serve
     the full-width cards, and without this override the square cards would
     inherit type sized for a card roughly twice their width. Proportion,
     not just a smaller copy of the same ratio: title stays close to the
     old 1.05rem baseline, count and label pull back further since the
     serif count in particular reads oversized on a square photo. */
  .sector-card--compact .sector-card-title {
    font-size: 0.92rem;
    line-height: 1.2;
  }
  .sector-card--compact .sector-card-count {
    font-size: 1.4rem;
  }
  .sector-card--compact .sector-card-label {
    font-size: 0.58rem;
  }
}


/* -----------------------------------------
   RIPRISTINATO 2026-08-27 (integrazione).
   Questo intero blocco era stato CANCELLATO dal nostro branch quando
   avevamo rimosso la sezione metodo (log 2026-08-20). Luis nel frattempo
   ci ha ricostruito sopra la sua animazione a quattro fasi, e
   css/build-stage.css + scroll-1..3.css RAFFINANO questa geometria, non la
   definiscono: senza queste regole #build-canvas restava position:static e
   il titolo finiva stampato sopra il render.
   Il merge a tre vie non poteva accorgersene: per git era una nostra
   cancellazione pulita su righe che lui non aveva toccato, quindi zero
   conflitti segnalati. Preso da definitivo/lavoro-LR, invariato.
   ----------------------------------------- */
/* -----------------------------------------
   12. BUILD STAGE (DESKTOP ONLY)
   A real 100vh section in normal document flow, further down the page
   after the hero. The forced descent scrolls the actual page to it; it is
   not overlaid/absolute inside the hero anymore.
   ----------------------------------------- */
.build-stage {
  display: none;
}

@media (min-width: 769px) {
  .build-stage {
    display: flex;
    height: 100vh;
    width: 100%;
    align-items: center;
    justify-content: center;
    position: relative;
    /* Pure black to match the frames' own background (rgb 0,0,0) exactly, so
       there is no visible seam/tint around the canvas. The top fades in from
       transparent so the section above blends into it. */
    background: linear-gradient(to bottom, transparent 0%, #000000 25vh, #000000 100%);
    z-index: var(--z-content);
  }

  .build-stage-content {
    width: 100%;
    max-width: 1440px;
    height: 100%;
    padding: 0 6rem;
    display: flex;
    align-items: center;
    justify-content: space-between;
    position: relative;
  }

  .build-copy {
    flex: 0 0 45%;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    text-align: left;
    color: var(--color-offwhite);
  }

  /* No-op on desktop: the lead/detail wrappers (mobile grouping) dissolve so
     eyebrow, headline and sub stay direct flex children exactly as before. */
  .copy-lead,
  .copy-detail {
    display: contents;
  }

  .build-eyebrow {
    font-family: var(--font-sans);
    font-size: 0.85rem;
    font-weight: 700;
    letter-spacing: 0.2em;
    color: var(--color-magenta-glow);
    margin-bottom: 0.85rem;
    opacity: 0;
    transform: translateX(-40px);
    will-change: transform, opacity;
  }

  .build-headline {
    font-family: var(--font-serif);
    font-size: clamp(2.5rem, 6vh, 4.2rem);
    font-weight: 300;
    line-height: 1.15;
    color: var(--color-offwhite);
    margin-bottom: 1.5rem;
    text-transform: uppercase;
    opacity: 0;
    transform: translateX(-40px);
    will-change: transform, opacity;
  }

  .build-sub {
    font-family: var(--font-sans);
    font-size: clamp(0.95rem, 1.8vh, 1.15rem);
    font-weight: 300;
    color: var(--color-gray-text);
    line-height: 1.5;
    max-width: 420px;
    opacity: 0;
    transform: translateX(-40px);
    will-change: transform, opacity;
  }

  /* Absolute overlay with the SAME geometry as scroll-1's .reveal-object-slot
     (left:50% width:50% of the content box, full height, centred). Taken out
     of the flex flow so the copy on the left is unaffected. This makes
     scroll-0's canvas occupy the exact same on-screen box as scroll-1's first
     frame, so the model does not jump in position or scale at the 0->1
     hand-off. Vertical centring comes from align-items:center over the full
     height (no translateY, so main.js's gsap.set(x:0) can't wipe it). */
  .build-canvas-container {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 50%;
    width: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: visible;
    opacity: 0;
    transform: translateX(80px);
    will-change: transform, opacity;
  }

  #build-canvas {
    /* Match the canvas's own intrinsic aspect ratio exactly so it always
       fills its box with no letterboxing. Scaled up a hair past its
       container (clipped by overflow:hidden above) because the GPU's
       texture-edge sampling at the canvas's exact last row/column can blend
       with the opposite edge and leave a faint 1px seam - pushing that seam
       just outside the visible box removes it. */
    width: 100%;
    max-height: 70vh;
    aspect-ratio: 1440 / 805;
    transform: scale(1.02);
  }

  /* Transition done states (for revisit/manual scroll back and forth) */
  .build-stage.transition-done .build-eyebrow,
  .build-stage.transition-done .build-headline,
  .build-stage.transition-done .build-sub,
  .build-stage.transition-done .build-canvas-container {
    opacity: 1;
    transform: none;
  }
}

/* -----------------------------------------
   13. SCROLL 0 - MOBILE LAYOUT
   Same section as the build stage above: on phones the copy sits on top
   and the canvas render below it. The scroll-scrubbed frame sequence
   behaves exactly like on desktop (GSAP pins the section).
   ----------------------------------------- */
@media (max-width: 768px) {
  /* Shared geometry for the 4-phase build-stage copy. The canvas is a fixed
     92vw box centred in the stage; copy is anchored to that box (never to the
     object, which breathes ~15% inside the frame). --gc-copy-gap is measured
     from the canvas RIQUADRO, not the visible object. */
  :root {
    --gc-canvas-h: calc(92vw * 805 / 1440);
    --gc-copy-gap: 26px;
    --gc-copy-inset: 1.6rem;
  }

  .build-stage {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    width: 100%;
    align-items: center;
    justify-content: center;
    position: relative;
    background: linear-gradient(to bottom, transparent 0%, var(--color-bg-dark) 25vh, var(--color-bg-dark) 100%);
    z-index: var(--z-content);
  }

  /* Positioning context for the copy groups. */
  .build-stage-content {
    width: 100%;
    height: 100%;
    padding: 0 1.5rem;
    position: relative;
  }

  /* Neutral: dissolve so .copy-lead / .copy-detail anchor to the stage box. */
  .build-copy {
    display: contents;
  }

  /* Lead (eyebrow + headline) sits ABOVE the canvas box, growing upward. */
  .copy-lead {
    position: absolute;
    left: var(--gc-copy-inset);
    right: var(--gc-copy-inset);
    /* Lowered from the canvas-box anchor so the magenta eyebrow no longer sits
       against the browser bar. Height-aware: full ~7vh drop on tall phones,
       collapsing to 0 on short viewports where there is no spare room (so the
       eyebrow never clips at the top). Paired with a slightly smaller step-1
       headline (below) so the block still clears the canvas. */
    bottom: calc(50% + var(--gc-canvas-h) / 2 + var(--gc-copy-gap) - min(7vh, max(0px, (100vh - 640px))));
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    z-index: 4;
  }

  /* Detail (sub) sits BELOW the canvas box, growing downward. */
  .copy-detail {
    position: absolute;
    left: var(--gc-copy-inset);
    right: var(--gc-copy-inset);
    top: calc(50% + var(--gc-canvas-h) / 2 + var(--gc-copy-gap));
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    z-index: 4;
  }

  /* Cap the body width and centre it so long copy breaks into a balanced,
     symmetric block rather than a full-bleed left-aligned run. */
  .copy-detail .build-sub {
    margin-left: auto;
    margin-right: auto;
  }

  .build-eyebrow {
    font-family: var(--font-sans);
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.32em;
    text-transform: uppercase;
    color: var(--color-magenta-glow);
    margin-bottom: 0.9rem;
    opacity: 0;
    will-change: transform, opacity;
  }

  .build-headline {
    font-family: var(--font-serif);
    font-size: clamp(1.7rem, 7vw, 2.3rem);
    font-weight: 300;
    line-height: 1.1;
    letter-spacing: 0.01em;
    color: var(--color-offwhite);
    margin-bottom: 0;
    text-transform: uppercase;
    opacity: 0;
    will-change: transform, opacity;
  }

  .build-sub {
    font-family: var(--font-sans);
    font-size: 0.95rem;
    font-weight: 300;
    color: var(--color-gray-text);
    line-height: 1.6;
    max-width: 34ch;
    opacity: 0;
    will-change: transform, opacity;
  }

  .build-canvas-container {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 50%;
    width: 92%;
    margin-left: -46%;
    display: flex;
    align-items: center;
    justify-content: center;
    opacity: 0;
    will-change: transform, opacity;
    z-index: 2;
  }

  #build-canvas {
    width: 100%;
    aspect-ratio: 1440 / 805;
    height: auto;
  }
}

/* Very short phones: the step-1 lead can run to 3 lines and, with the canvas
   plus a long body, the block would clip at the top. Tighten the display type
   just here so everything fits without clipping. */
@media (max-width: 768px) and (max-height: 620px) {
  .build-headline {
    font-size: clamp(1.45rem, 6vw, 1.9rem);
    line-height: 1.06;
  }
  .build-sub {
    font-size: 0.86rem;
    line-height: 1.45;
  }
}

/* -----------------------------------------
   12. PROJECT DETAIL MODAL OVERLAY
   ----------------------------------------- */
.project-modal {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 99999;
  display: flex;
  justify-content: center;
  align-items: center;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.5s cubic-bezier(0.25, 1, 0.5, 1);
  overflow: hidden;
}

.project-modal.active {
  opacity: 1;
  pointer-events: auto;
}

.modal-backdrop {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(5, 5, 8, 0.85);
  backdrop-filter: blur(15px);
  -webkit-backdrop-filter: blur(15px);
  z-index: 1;
}

.modal-window {
  position: relative;
  width: 90%;
  max-width: 1100px;
  max-height: 85vh;
  background-color: #0c0c0e;
  border: 1px solid rgba(230, 35, 106, 0.15);
  box-shadow: 0 30px 70px rgba(0, 0, 0, 0.8), 0 0 50px rgba(230, 35, 106, 0.05);
  border-radius: 20px;
  z-index: 2;
  overflow-y: auto;
  transform: scale(0.9) translateY(30px);
  transition: transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
  display: flex;
  flex-direction: column;
}

.project-modal.active .modal-window {
  transform: scale(1) translateY(0);
}

.modal-close-btn {
  position: absolute;
  top: 1.5rem;
  right: 1.5rem;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.1);
  background-color: rgba(12, 12, 14, 0.8);
  color: var(--color-white);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  z-index: 10;
  transition: all 0.3s ease;
}

.modal-close-btn:hover {
  border-color: var(--color-magenta-glow);
  background-color: var(--color-magenta-deep);
  transform: rotate(90deg);
}

.modal-close-btn svg {
  width: 18px;
  height: 18px;
}

.modal-layout {
  display: grid;
  grid-template-columns: 1.1fr 0.9fr;
  min-height: 500px;
  overflow: hidden;
}

@media (max-width: 900px) {
  .modal-layout {
    grid-template-columns: 1fr;
  }
  .modal-window {
    max-height: 90vh;
  }
}

.modal-media-side {
  position: relative;
  background-color: #050508;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}

.modal-img-container {
  width: 100%;
  height: 100%;
  min-height: 400px;
  position: relative;
}

.modal-project-img {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transform: scale(1.05);
  transition: opacity 0.8s ease, transform 1.2s cubic-bezier(0.16, 1, 0.3, 1);
}

.project-modal.active .modal-project-img {
  opacity: 1;
  transform: scale(1);
}

.modal-info-side {
  padding: 3rem 2.5rem;
  display: flex;
  flex-direction: column;
  justify-content: center;
  overflow-y: auto;
  background: linear-gradient(135deg, #0c0c0e 0%, #08080a 100%);
}

@media (max-width: 600px) {
  .modal-info-side {
    padding: 2rem 1.5rem;
  }
}

.modal-project-category {
  font-family: var(--font-sans);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  color: var(--color-magenta-glow);
  text-transform: uppercase;
  margin-bottom: 0.5rem;
  display: inline-block;
}

.modal-project-title {
  font-family: var(--font-serif);
  font-size: 2.5rem;
  font-weight: 400;
  color: var(--color-white);
  line-height: 1.2;
  margin-bottom: 1.5rem;
}

.modal-details-divider {
  width: 100%;
  height: 1px;
  background: linear-gradient(to right, rgba(230, 35, 106, 0.2), rgba(255, 255, 255, 0.05));
  margin: 1.2rem 0;
}

.modal-project-details-grid {
  display: flex;
  flex-direction: column;
  gap: 1.2rem;
}

.detail-item {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

.detail-label {
  font-family: var(--font-sans);
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  color: rgba(255, 255, 255, 0.4);
  text-transform: uppercase;
}

.detail-value {
  font-family: var(--font-sans);
  font-size: 0.95rem;
  font-weight: 300;
  color: var(--color-white);
  line-height: 1.5;
}

.modal-project-desc {
  font-family: var(--font-sans);
  font-size: 0.95rem;
  font-weight: 300;
  line-height: 1.65;
  color: var(--color-gray-text);
  margin-top: 1rem;
}

/* -----------------------------------------
   CLIENT MARQUEE (.gc-marquee-*) - self-contained block.
   Thin black strip directly under the hero, listing real client/reference
   names, scrolling continuously left -> right in a seamless loop. Two
   identical .gc-marquee-group tracks sit side by side inside a flex track
   sized to content (width:max-content); animating the track's transform
   from translateX(-50%) to translateX(0) shifts exactly one group's width
   per loop, so the wrap is invisible. Pure CSS animation - GPU-composited
   (transform-only), no GSAP/JS involved.
   ----------------------------------------- */
.gc-marquee {
  position: relative;
  width: 100%;
  height: var(--gc-marquee-h);
  background: #000;
  overflow: hidden;
  display: flex;
  align-items: center;
  z-index: var(--z-content);
}
.gc-marquee-track {
  display: flex;
  width: max-content;
  animation: gc-marquee-scroll 26s linear infinite;
  will-change: transform;
}
.gc-marquee-group {
  display: flex;
  align-items: center;
  flex-shrink: 0;
  padding-right: 2.5rem;
}
.gc-marquee-item {
  font-family: var(--font-sans);
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--color-white);
  white-space: nowrap;
}
.gc-marquee-sep {
  margin: 0 1.6rem;
  color: rgba(255, 255, 255, 0.3);
  font-size: 0.9rem;
}
@keyframes gc-marquee-scroll {
  from { transform: translateX(-50%); }
  to { transform: translateX(0%); }
}
@media (prefers-reduced-motion: reduce) {
  .gc-marquee-track {
    animation: none;
    transform: translateX(0);
  }
}
@media (max-width: 768px) {
  :root {
    --gc-marquee-h: 44px;
  }
  .gc-marquee-item {
    font-size: 0.62rem;
    letter-spacing: 0.12em;
  }
  .gc-marquee-sep {
    margin: 0 1.1rem;
  }
  .gc-marquee-track {
    animation-duration: 20s;
  }
}

/* -----------------------------------------
   S2. TRUST BLOCK (.gc-trust-*) — PEPPERMINT-STYLE CARD CASCADE & B&W PALAZZO BACKDROP
   Base: #FFFFFF (crisp white background)
   Cards: Compact centered cards (max-width min(324px, 86vw), max-height 56vh)
   Colors are tied to STACK POSITION (not to which stat occupies it), so
   reordering cards in the DOM never needs a color/rotation change:
     Position 1 (1.000+ locali): Midnight Navy (#0F172A)
     Position 2 (15 città):      Warm ivory (#EDE4D3) — light neutral, keeps
                                  every touching seam a light/dark pair
     Position 3 (30 anni):       Brand Magenta (#C9215A) — accent card
   ----------------------------------------- */
.gc-trust-block {
  position: relative;
  width: 100%;
  background-color: #FFFFFF;
  color: #0F0E0C;
  padding-top: clamp(64px, 10vh, 100px);
  /* Pass 17/18/20: more room below the cards for the brand-marquee (head +
     rule + track + rule) to breathe before the curve into #settori — top
     padding (spacing above the header) and the curve geometry below are
     both untouched, only this one value grows. Pass 20 (v4): bumped again
     to recover height lost by moving the header up (reduced
     .trust-marquee margin-top) - net effect across this pass's changes is
     still a taller band than the v3 baseline, curve unchanged.
     2026-08-05 (layout-fixes pass): measured whitespace above the "Tra i
     nostri progetti" heading (56px) vs below the marquee (238.5px at
     1440x900) was badly asymmetric - the heading/marquee block read as
     sitting too high. Shifted the whole block down by a measured 91px:
     -91px here, +91px on .trust-marquee's margin-top below, so the
     heading-to-track internal spacing is untouched. */
  padding-bottom: calc(clamp(150px, 20vh, 195px) - 91px);
  border-top-left-radius: 50% 28px;
  border-top-right-radius: 50% 28px;
  border-bottom-left-radius: 50% 28px;
  border-bottom-right-radius: 50% 28px;
  margin-top: -28px;
  margin-bottom: -28px;
  /* Explicit, generously-high z-index: guarantees the solid white
     background wins over EVERY later section while it rises to cover the
     hero, with no dependency on default stacking order. */
  z-index: 20;
  /* NO `will-change` here, deliberately.

     It was added and withdrawn the same day. It did help while the backdrop
     curtain was still animating a `mask-image`: that mask dirtied the whole
     section every frame, and promoting this band hid some of the cost
     (41 -> 55 fps in a throttled profile).

     Once the curtain became a `translateY` (see .gc-trust-palazzo-mask) the
     promotion stopped paying for itself — measured 1fps WORSE — and it was
     never free: this band is the full width of the page and several thousand
     pixels tall, so promoting it asks the engine to keep a texture that big
     alive for as long as the page is open. Chromium tiles that away
     cheerfully; WebKit on a phone is where an oversized permanent layer turns
     into memory pressure, which is the failure mode this whole pass exists to
     avoid. Fix the thing that dirties the layer, do not paper over it by
     promoting the layer. */
  border: none;
  outline: none;
  overflow: hidden;
}

/* GRAIN LAYER (opacity 0.04 for clean white base)

   2026-09-04 — DUE COSE TOLTE, NESSUNA DELLE DUE VISIBILE.

   1) La SVG era `width='100%' height='100%'`: il browser la rasterizzava
      alla dimensione VERA di questo pseudo-elemento, cioe' l'intera fascia
      bianca — su un telefono circa 390 x 5000 px, quasi due milioni di
      pixel di feTurbulence a tre ottave. feTurbulence e' uno dei filtri SVG
      piu' cari che esistano, e quel disegno non si paga una volta sola: la
      fascia e' molto piu' alta di uno schermo, quindi WebKit la tiene a
      tasselli e, sotto pressione di memoria, BUTTA i tasselli che escono di
      vista. Tornando indietro vanno ridisegnati — cioe' va rieseguito il
      filtro. E' esattamente la forma del difetto segnalato: la prima
      discesa liscia (tasselli disegnati man mano, con calma), la risalita
      che scatta (tasselli buttati da ridisegnare) e le discese successive
      che continuano a scattare.

      Ora la SVG e' 160x160 e viene RIPETUTA. Stesso generatore, stessi
      parametri, stesso grano: cambia solo che il rumore viene calcolato una
      volta su 25.600 pixel invece che su due milioni, e da li' in poi e'
      una texture come un'altra — la cosa piu' economica che un compositor
      sappia fare. `stitchTiles='stitch'` era gia' li' e serve proprio a
      questo: i bordi combaciano, quindi la ripetizione non si vede.

   2) `mix-blend-mode: multiply` toglieva l'intera fascia dal percorso
      veloce del compositor (va letto indietro lo sfondo e rifuso), ed e' la
      stessa trappola documentata sopra per le glow orb. Qui non serviva:
      sotto questo pseudo-elemento (z-index 0) c'e' SOLO il bianco pieno di
      .gc-trust-block — le card e il palazzo stanno sopra (z-index 1 e 2) —
      e `multiply` contro il bianco e' l'identita'. Il risultato a schermo e'
      lo stesso pixel per pixel; cambia solo quanto costa ottenerlo. */
.gc-trust-block::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  opacity: 0.04;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23noise)'/%3E%3C/svg%3E");
  background-repeat: repeat;
  background-size: 160px 160px;
}

/* Content Container */
.gc-trust-container {
  max-width: 1140px;
  margin: 0 auto;
  padding: 0 24px;
  position: relative;
  z-index: 1;
}

/* Header Block */
.gc-trust-header-block {
  text-align: center;
  margin-bottom: clamp(36px, 6vh, 64px);
  display: flex;
  flex-direction: column;
  align-items: center;
}

.gc-trust-eyebrow {
  font-family: 'General Sans', 'Inter', sans-serif;
  font-weight: 600;
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(15, 14, 12, 0.6);
}

.gc-trust-rule {
  width: 32px;
  height: 2px;
  background: rgba(15, 14, 12, 0.2);
  margin: 12px 0 20px;
}

.gc-trust-title {
  font-family: 'Newsreader', 'Cormorant Garamond', serif;
  font-weight: 500;
  font-size: clamp(34px, 6vw, 54px);
  line-height: 1.1;
  letter-spacing: -0.02em;
  color: #0F0E0C;
  margin: 0;
}

.gc-trust-italic-accent {
  font-style: italic;
  color: #C9215A;
}

.gc-trust-subline {
  font-family: 'General Sans', 'Inter', sans-serif;
  font-weight: 450;
  font-size: clamp(15px, 1.8vw, 17px);
  line-height: 1.55;
  letter-spacing: 0.005em;
  color: rgba(15, 14, 12, 0.82);
  max-width: 44ch;
  margin-top: 16px;
  margin-bottom: 0;
}

/* STAGE CONTAINER — static, in normal document flow (no pin). */
.gc-trust-stage {
  position: relative;
  width: 100%;
  padding-bottom: clamp(24px, 5vh, 56px);
}

/* B&W PALAZZO BACKDROP WITH GRADIENT MASK. A light scroll-scrubbed parallax
   (no pin) is applied in JS via translateY — see main.js. */
.gc-trust-palazzo-bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
  /* opacity intentionally NOT reduced here: .gc-trust-palazzo-mask is a
     CHILD of this element, and CSS opacity on a container compounds with
     (flattens) everything inside it — a low opacity here made the mask
     unable to ever render as a genuinely opaque cover, so the photo
     showed through at roughly the same (washed-out) faintness regardless
     of the mask's clip-path, i.e. no visible progressive reveal. The
     grayscale/contrast/brightness filter alone gives the moody b&w
     treatment without needing reduced opacity. */
  /* contrast/blur reduced (pass 11 — "calm the backdrop 10-15%, keep it
     visible, cards stay the focus"): contrast 110%->98%, +blur(1px). Both
     live here on the shared parent (not on .gc-trust-palazzo-img) because
     JS only ever writes an inline `filter` on the IMG (brightness only,
     see main.js) — putting contrast/blur there would get clobbered the
     moment that inline filter renders. The parent's filter is never
     touched by JS, so it composes safely underneath the img's own
     animated brightness. */
  /* grayscale(100%) contrast(98%) blur(1px) are now BAKED INTO
     assets/opt/building_corner_bleed.webp. They are static — nothing about
     them varies with scroll — but as a live filter chain on a full-bleed image
     that ScrollTrigger translates, the blur in particular was re-running for
     the whole length of the trust section. Only brightness stays, because JS
     writes an animated brightness on the child <img> and the two must remain
     separable (see the note above). Baking also dropped the file 88KB -> 43KB,
     since a greyscale image compresses far better than a colour one. */
  filter: brightness(0.95);
  mask-image: linear-gradient(180deg, transparent 0%, #000 15%, #000 85%, transparent 100%);
  -webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 15%, #000 85%, transparent 100%);
  will-change: transform;
}

.gc-trust-palazzo-img {
  /* opacity (not on .gc-trust-palazzo-bg, its parent): reduces the
     photo's own visual weight ~15% as part of the pass 11 "calm the
     backdrop" ask, without touching .gc-trust-palazzo-mask (a SIBLING,
     not a descendant of this element, so this opacity can't compound
     with/wash out the mask the way a reduced opacity on the shared
     parent would — see the comment on .gc-trust-palazzo-bg above). JS
     never touches this element's opacity, only its `y`/`filter`, so no
     conflict. */
  opacity: 0.85;
  width: 100%;
  height: 118%;
  object-fit: cover;
  object-position: center 30%;
  display: block;
}

/* WHITE RETRACT MASK: solid white, starts covering the whole backdrop and
   retracts from the top down as the user scrolls through the cascade,
   uncovering the photo underneath top-to-bottom. Driven by the same
   scrub timeline as the cards in main.js (JS only ever writes the single
   --reveal-x custom property, in lockstep with each card's own measured
   position — see main.js), so it reverses cleanly on scroll-up.
   Uses mask-image (a soft gradient), NOT clip-path (a hard edge): clip-path
   gave a visible straight line between "photo" and "white" at every scroll
   position, which read as an abrupt cut rather than the intended
   feathered/haze-like dissolve. --reveal-feather-eff clamps the feather
   band width against BOTH how close --reveal-x is to 0% and to 100% (via
   the two inner min() terms) — without that, a fixed feather width would
   still produce a soft transparent sliver right at the very top even
   when --reveal-x is exactly 0% (before any real reveal has started),
   since a linear-gradient always interpolates between two
   differently-colored stops regardless of how their positions are
   computed. Clamping the feather to min(fixed, x, 100%-x) collapses that
   gap to true zero-width at both extremes, so the mask starts fully
   opaque and ends fully transparent with no residual band either end. */
/* Constant part of the backdrop's exposure. The scrubbed part is the veil
   below — see the palazzoImg block in js/main.js for why the two are split. */
.gc-trust-palazzo-img {
  filter: brightness(1.15);
}

/* The scrubbed darkening, as a composited layer instead of a paint-time
   filter. Sits directly above the photo and below .gc-trust-palazzo-mask
   (z-index 1), so the white curtain still wipes over both. */
.gc-trust-palazzo-shade {
  position: absolute;
  inset: 0;
  background: #000;
  opacity: 0.174;
  z-index: 0;
  pointer-events: none;
  will-change: opacity;
}

/* THE WHITE CURTAIN — now moved, not re-masked.

   What it does is unchanged: white covers the backdrop from `--reveal-x`
   downwards, with a soft 24%-tall edge centred on that line, and js/main.js
   scrubs --reveal-x from 0 to each card's measured position as the cards
   stack.

   HOW changed, because the old way is a paint operation and this one is not.
   It used to be a full-bleed element with a `mask-image: linear-gradient(...)`
   whose colour stops were `calc()`s of --reveal-x. Scrubbing that means the
   engine re-parses the gradient, regenerates a mask bitmap the size of the
   element and re-applies it — on every scroll frame. Chromium hides most of
   that cost; WebKit does not, and this is a full-width element sitting under
   the exact stretch of scroll the reader complained about.

   Now the element is a plain white box with the feather baked into its own
   background gradient, and only `transform: translateY()` is animated — a
   compositor property, free per frame, and the one thing every mobile engine
   is good at.

   THE GEOMETRY, so the next person can change it safely:
     height is 200% of the container, so a `12%` gradient stop is 24% of the
     container — the same feather as before.
     translateY percentages resolve against the element's OWN height, i.e.
     200% of the container, so moving the top edge by R% of the container
     needs R/2. The extra -6% (= half the 12% stop) re-centres the feather on
     --reveal-x instead of starting it there.
     The remaining 188% of solid white below the feather is what guarantees
     the bottom of the backdrop stays covered at every value of --reveal-x;
     .gc-trust-palazzo-bg has overflow:hidden, so the excess never shows.

   Verified against the rendered page at nine values of --reveal-x: the
   feather's midpoint lands on --reveal-x to within 0.0%, its width is 24% of
   the container throughout, and the white always reaches the bottom. */
.gc-trust-palazzo-mask {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 200%;
  z-index: 1;
  pointer-events: none;
  --reveal-x: 0%;
  background: linear-gradient(
    to bottom,
    rgba(255, 255, 255, 0) 0%,
    #FFFFFF 12%,
    #FFFFFF 100%
  );
  transform: translateY(calc(var(--reveal-x) * 0.5 - 6%));
  will-change: transform;
}

/* CARDS CASCADE — same mechanic as paywithpeppermint.com's
   .more-cards_container: a plain flex column in normal flow. Each card is
   position:relative (NOT absolute/pinned); the overlap comes from a
   negative margin-top pulling each card up onto the previous one, and each
   card carries its own fixed rotate(). Identical mechanic at every
   breakpoint — only sizing changes via media query. */
.gc-trust-cards-stage {
  position: relative;
  z-index: 2;
  width: 100%;
  max-width: min(324px, 86vw);
  margin: 0 auto;
  display: flex;
  flex-direction: column;
}

.gc-trust-stack-card {
  position: relative;
  width: 100%;
  max-height: 56vh;
  border-radius: 20px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  /* Stronger, more elevated shadow (depth separation) */
  box-shadow: 0 28px 64px rgba(0, 0, 0, 0.32), 0 8px 20px rgba(0, 0, 0, 0.2);
  will-change: transform, opacity;
}

/* BOLD, HIGH-CONTRAST TRIO + fixed per-card tilt (peppermint-style).
   Navy (1) and magenta (2) previously sat adjacent and, once dimmed by the
   cascade's own opacity fade, read as two similar dark/muted tones fusing
   at the seam. Fix: put a LIGHT warm neutral in the middle (position 2) so
   every touching seam is a light/dark or light/saturated pair — never two
   dark tones together. Magenta moves to the top card as a pure accent
   (still never on the 1.000+ flagship). Colors are tied to STACK POSITION,
   not to which stat currently occupies it. */
.gc-trust-stack-card-1 {
  background-color: #0A2A5C;
  color: #FFFFFF;
  z-index: 1;
  transform: rotate(-3.5deg);
}

/* Card-1: full-bleed blueprint as the card background — same treatment as
   card-2's map. The blueprint's own blue ground matches the card, so the
   image fills the whole surface; a bottom scrim keeps the stat text legible. */
.gc-trust-stack-card-1 {
  background-image:
    linear-gradient(to top, rgba(6, 22, 48, 0.94) 0%, rgba(6, 22, 48, 0.55) 34%, rgba(6, 22, 48, 0) 62%);
  background-repeat: no-repeat, no-repeat;
  background-position: center, center;
  background-size: auto, cover;
}

.gc-trust-stack-card-1 .gc-trust-card-top-media {
  display: none;
}

.gc-trust-stack-card-1 .gc-trust-card-body {
  justify-content: flex-end;
  min-height: clamp(300px, 44vh, 400px);
}

.gc-trust-stack-card-2 {
  background-color: #9C5A4A;
  color: #FFFFFF;
  z-index: 2;
  margin-top: -5%;
  transform: rotate(3deg);
}

/* Card-2: full-bleed Italy map as the card background. The map art already
   sits on the same terracotta ground (#9C5A4A), so the image edges dissolve
   into the card and Italy reads as filling the whole surface. The cramped
   top-strip media slot is removed; a bottom scrim keeps the stat text legible
   over the map. */
.gc-trust-stack-card-2 {
  background-image:
    linear-gradient(to top, rgba(58, 30, 24, 0.92) 0%, rgba(58, 30, 24, 0.55) 34%, rgba(58, 30, 24, 0) 62%);
  background-repeat: no-repeat, no-repeat;
  background-position: center 6%, center 14%;
  background-size: auto, 78%;
}

.gc-trust-stack-card-2 .gc-trust-card-top-media {
  display: none;
}

.gc-trust-stack-card-2 .gc-trust-card-body {
  justify-content: flex-end;
  min-height: clamp(300px, 44vh, 400px);
}

.gc-trust-stack-card-3 {
  background-color: #7A4A32;
  color: #FFFFFF;
  z-index: 3;
  margin-top: -5%;
  transform: rotate(-2.5deg);
}

/* Card-3: full-bleed venue photo as the card background — same treatment as
   cards 1 and 2. A warm-toned scrim keeps the stat text legible and ties the
   image into the clay-brown card. */
.gc-trust-stack-card-3 {
  background-image:
    linear-gradient(to top, rgba(40, 24, 16, 0.94) 0%, rgba(40, 24, 16, 0.5) 36%, rgba(40, 24, 16, 0.12) 68%);
  background-repeat: no-repeat, no-repeat;
  background-position: center, center;
  background-size: auto, cover;
}

.gc-trust-stack-card-3 .gc-trust-card-top-media {
  display: none;
}

.gc-trust-stack-card-3 .gc-trust-card-body {
  justify-content: flex-end;
  min-height: clamp(300px, 44vh, 400px);
}

/* =====================================================================
   DESKTOP + iPAD (>= 768px): three cards side-by-side in one editorial
   row instead of the mobile-first stacked/rotated cascade. Overlap,
   rotation and the negative-margin pull are all neutralised; each card
   becomes an equal column of the same height. The vertical palazzo
   backdrop + its JS-driven top-down mask are meaningless in a horizontal
   layout, so the backdrop is hidden here and the premium entrance is
   handled by a dedicated JS branch (see main.js, min-width:768px).
   ===================================================================== */
@media (min-width: 768px) {
  .gc-trust-palazzo-bg {
    display: none;
  }

  .gc-trust-cards-stage {
    max-width: min(1140px, 92vw);
    flex-direction: row;
    align-items: stretch;
    justify-content: center;
    gap: clamp(18px, 2.4vw, 48px);
    perspective: 1600px;
  }

  .gc-trust-stack-card {
    flex: 1 1 0;
    min-width: 0;
    max-height: none;
    margin-top: 0;
    /* Base resting transform: flat. The premium entrance in JS animates
       from a lifted/tilted state back to this. rotate(0) authored here so
       the per-card CSS rotations below get overridden cleanly. */
    transform: none;
    border-radius: 22px;
    box-shadow: 0 22px 50px rgba(0, 0, 0, 0.26), 0 6px 16px rgba(0, 0, 0, 0.16);
    transition: transform 0.55s cubic-bezier(0.22, 1, 0.36, 1),
                box-shadow 0.55s cubic-bezier(0.22, 1, 0.36, 1);
  }

  /* Kill the mobile cascade's per-card tilt + negative pull in the row. */
  .gc-trust-stack-card-1,
  .gc-trust-stack-card-2,
  .gc-trust-stack-card-3 {
    transform: none;
    margin-top: 0;
  }

  /* Uniform, generous card height so all three read as one aligned trio. */
  .gc-trust-stack-card-1 .gc-trust-card-body,
  .gc-trust-stack-card-2 .gc-trust-card-body,
  .gc-trust-stack-card-3 .gc-trust-card-body {
    min-height: clamp(380px, 46vh, 500px);
  }

  /* Premium hover: a restrained lift + deepened shadow — luxury micro-
     interaction, no scale jitter. Disabled while the entrance is still
     playing (JS holds .gc-trust--entering on the stage until done). */
  .gc-trust-cards-stage:not(.gc-trust--entering) .gc-trust-stack-card:hover {
    transform: translateY(-10px);
    box-shadow: 0 34px 70px rgba(0, 0, 0, 0.32), 0 10px 22px rgba(0, 0, 0, 0.2);
  }
}

/* TOP IMAGE — hard-capped independent of the card's own (auto) height, so it
   can never balloon past ~50% of a compact card. A percentage height here
   would resolve against an indefinite (auto) flex-column parent and fall
   back to the image's intrinsic size — exactly the "image too tall" bug —
   so this uses a fixed clamp() instead, with flex-shrink:0 to protect it
   from being squashed by the body's content. */
.gc-trust-card-top-media {
  width: 100%;
  height: clamp(97px, 17vh, 151px);
  flex-shrink: 0;
  position: relative;
  overflow: hidden;
}

.gc-trust-card-top-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  display: block;
}

/* Card-1 blueprint was outpainted to a landscape ratio (blueprint blue
   extended on both sides), so cover now fills the media box cleanly with
   only a gentle zoom and no letterbox gaps. */
.gc-trust-stack-card-1 .gc-trust-card-top-img {
  object-fit: cover;
  object-position: center;
  background-color: #0A2A5C;
}

/* Card-2 Italy map is a full-height boot — cover would clip the toe/top.
   contain shows all of Italy; the image's own dark ground (#161510) matches
   the card body, so the letterbox reads as one continuous surface. */
.gc-trust-stack-card-2 .gc-trust-card-top-media {
  background-color: #9C5A4A;
}

.gc-trust-stack-card-2 .gc-trust-card-top-img {
  object-fit: contain;
  object-position: center;
}

/* CARD BODY — CENTER ALIGNED TEXT & NUMBER */
.gc-trust-card-body {
  padding: 18px 22px 24px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  flex: 1;
  text-align: center;
}

.gc-trust-card-num {
  font-family: 'Newsreader', 'Cormorant Garamond', serif;
  font-weight: 500;
  font-size: clamp(2.5rem, 5.4vw, 3.6rem);
  line-height: 0.95;
  letter-spacing: -0.02em;
  color: #FFFFFF;
  display: block;
  margin-bottom: 12px;
}

.gc-trust-card-label {
  font-family: 'General Sans', 'Inter', sans-serif;
  font-weight: 700;
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.95);
  margin: 0 0 10px 0;
  line-height: 1.3;
}

.gc-trust-card-desc {
  font-family: 'General Sans', 'Inter', sans-serif;
  font-weight: 400;
  font-size: 13px;
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.75);
  margin: 0;
  max-width: 28ch;
}

/* MOBILE (max-width: 900px) — same compact cascade mechanic + same size
   caps, just a bigger number/label for legibility and a slightly gentler
   overlap so nothing feels cramped. */
@media (max-width: 767px) {
  .gc-trust-block {
    padding-top: 56px;
    /* Pass 20 (v4): this fixed value was silently overriding the desktop
       clamp() on .gc-trust-block below max-width:767px this whole time -
       every prior marquee pass's padding-bottom bump only ever took
       effect on desktop; mobile's white-band height never actually
       changed. Bumped here (56px -> 90px) to recover the height this
       pass's header/spacing changes need on mobile specifically. */
    padding-bottom: 100px;
  }

  .gc-trust-container {
    padding: 0 16px;
  }

  .gc-trust-header-block {
    text-align: center;
    align-items: center;
    margin-bottom: 36px;
  }

  .gc-trust-cards-stage {
    max-width: min(306px, 86vw);
  }

  .gc-trust-stack-card-2,
  .gc-trust-stack-card-3 {
    margin-top: -4%;
  }

  .gc-trust-card-num {
    font-size: clamp(2.7rem, 10.8vw, 4rem);
  }

  .gc-trust-card-label {
    font-size: 13px;
  }

  .gc-trust-card-desc {
    font-size: 13px;
  }

}

@media (prefers-reduced-motion: reduce) and (max-width: 767px) {
  .gc-trust-stack-card-1,
  .gc-trust-stack-card-2,
  .gc-trust-stack-card-3 {
    transform: none;
    margin-top: 24px;
  }
}

@media (prefers-reduced-motion: reduce) {

  /* JS never animates the mask when reduced-motion is on (the cascade
     setup bails out early), so without this it would sit at its default
     fully-covering state forever, permanently hiding the backdrop. */
  .gc-trust-palazzo-mask {
    --reveal-x: 100%;
  }
}

/* -----------------------------------------
   BRAND TRUST MARQUEE (pass 18 — rAF-driven, replaces the pass-17
   CSS-keyframe duration-swap: that mechanic rescaled the whole 0-100%
   keyframe on hover, which drifted the chip under the cursor instead of
   holding position. See initTrustMarquee() in js/main.js for the loop
   itself — this file only ever receives a transform via inline style, no
   CSS animation/keyframes on the track anymore. Pure CSS otherwise: no
   second smooth-scroll, Lenis/ScrollTrigger untouched. ----------------------------------------- */
.trust-marquee {
  position: relative;
  z-index: 1;
  width: 100%;
  /* Replaces the old .gc-trust-horizontal-hairline's own margin-top - this
     is the "room above, before the curve below" half of the white-band
     height increase (see .gc-trust-block's padding-bottom for the other
     half). Pass 21 (v5): reduced further from v4's clamp(90px,12vh,120px)
     - header moves up again; height recovered via .gc-trust-block's
     padding-bottom below if the net change (bigger header + bigger rule
     margin - smaller top margin) still comes up short of baseline.
     2026-08-05 (layout-fixes pass): +91px flat, paired with the equal -91px
     on .gc-trust-block's padding-bottom - see that rule's comment for the
     measured before/after. */
  margin-top: calc(clamp(65px, 9vh, 90px) + 91px);
  --line: rgba(26, 26, 26, 0.14);
  --marquee-magenta: #C9215A;
  --muted: rgba(15, 14, 12, 0.5);
}

.trust-marquee__head {
  text-align: center;
  max-width: 640px;
  margin: 0 auto;
  padding: 0 24px;
}

/* Pass 19 (v3): single line now ("Tra i nostri progetti"), so no more
   .is-muted / strong split - the whole line is one ink color except the
   accent word. Size bumped ~1.12x the v2 two-line baseline (44px -> 49px
   at this clamp's max) since a one-line header sitting this close to the
   brand row needs more presence/hierarchy over the names below it.
   Pass 20 (v4): weight 500->600 - read as too thin/light at both sizes,
   one shared rule so the heavier weight applies at the mobile clamp
   value too, not just desktop. */
/* Pass 21 (v5): size bumped ~1.20x again (clamp(31,4.7vw,49) ->
   clamp(37,5.6vw,59)) - the v4 weight-only change (500->600) read as too
   subtle a jump at this size to register as visibly different. Weight
   stays 600 (unchanged, already correct); this pass's fix is size, not
   weight. Whole clamp scaled proportionally so BOTH the desktop max
   (59px) and the mobile min (37px) grow together - v4's root-cause
   finding (Step 0) confirmed no override/media-query was shadowing this
   rule, so there's only the one place to edit. */
.trust-marquee__title {
  font-family: var(--font-serif-cards);
  font-variant-numeric: lining-nums;
  font-weight: 600;
  font-size: clamp(37px, 5.6vw, 59px);
  line-height: 1.2;
  margin: 0;
  color: #0F0E0C;
}

/* The one magenta accent in the heading - only "progetti", rest of the
   line stays ink. Mirrors the "costruito." treatment in the black
   #settori section below. */
.trust-marquee__title-accent {
  color: var(--marquee-magenta);
}

/* Both rule lines share this: solid var(--line) color, faded to transparent
   at each end via mask-image (same idiom as the viewport's own edge-fade
   below) rather than a gradient background - keeps the line itself a flat
   color, only its visible extent tapers. Pass 20 (v4): margin bumped
   ~12.5% (clamp(28,4vh,40) -> clamp(31,4.5vh,45)) - more air between the
   header/rule and the marquee track below (FIX 2); applies symmetrically
   to both rule instances (before and after the viewport) since they share
   this one class. */
/* Pass 22: v5's +15% still read as not enough drop between the header and
   the moving brand row - bumped further (clamp(34,5.2vh,52) ->
   clamp(40,6.5vh,62)). Applies symmetrically to both rule instances
   (before and after the viewport) since they share this class. */
.trust-marquee__rule {
  width: 100%;
  max-width: 480px;
  height: 1px;
  margin: clamp(40px, 6.5vh, 62px) auto;
  background-color: var(--line);
  mask-image: linear-gradient(to right, transparent, #000, transparent);
  -webkit-mask-image: linear-gradient(to right, transparent, #000, transparent);
}

/* Pass 19 (v3): fade zones widened 10%->14% each side (was reading too
   thin/abrupt at the edges). Mobile gets its own, slightly subtler value
   below - the same 14% read as almost nonexistent on a narrow viewport
   (14% of 375px is much less physical width than 14% of 1440px), so
   mobile needs relatively more than desktop's absolute value to be
   perceptible at all, without going dramatic. */
.trust-marquee__viewport {
  position: relative;
  width: 100%;
  overflow: hidden;
  mask-image: linear-gradient(to right, transparent, #000 14%, #000 86%, transparent);
  -webkit-mask-image: linear-gradient(to right, transparent, #000 14%, #000 86%, transparent);
}

/* No CSS animation here anymore - initTrustMarquee() in js/main.js writes
   transform: translate3d(-offset, 0, 0) every frame via a plain
   requestAnimationFrame loop, entirely independent of Lenis/ScrollTrigger
   (it never reads scroll position, only its own internal offset/velocity
   state). Single flex `gap` (not per-chip margin) - the rAF loop measures
   oneSetWidth = track.scrollWidth / 2 directly at runtime and re-measures
   on image load/error, so the two duplicated sets don't need to be forced
   into a fixed relationship via CSS the way the old -50% keyframe did. */
/* Pass 21 (v5): gap tightened 54px -> 40px (real ticker density, was
   reading as spread-out labels). scrollWidth/2 (the rAF wrap point) still
   comfortably exceeds 2x viewport width at this gap - the 11-brand list
   is already repeated x3 per block (33 chips) in the markup, more than
   enough margin even with less gap between chips. */
.trust-marquee__track {
  display: flex;
  align-items: center;
  width: max-content;
  gap: 40px;
  will-change: transform;
}

/* No min-width here on purpose (pass 17 had one - it padded every chip
   out to the widest name's width, leaving big empty gaps around shorter
   ones). Content-driven width + a denser 40-brand list is what makes the
   row read as a continuous ticker instead of spaced-out labels. */
.brand-chip {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  flex-shrink: 0;
  padding: 4px 0;
}

.brand-chip__logo {
  height: 28px;
  width: auto;
  object-fit: contain;
  /* Normalized to a single ink tone if a real logo file is ever dropped in -
     keeps the row's rhythm even instead of some chips suddenly going full
     color while their neighbors are still text. */
  filter: grayscale(1) brightness(0.25);
}

.brand-chip__logo.is-missing {
  display: none;
}

/* Hidden by default (logo present is the norm); the adjacent-sibling
   selector below shows it only once the logo has actually failed to load -
   driven entirely by the .is-missing class the markup's own onerror sets,
   no JS beyond that one inline handler. Bigger/heavier than pass 17 (was
   15px/700) so it reads like an actual wordmark, still visually secondary
   to the Cormorant heading above it. */
.brand-chip__name {
  display: none;
  font-family: var(--font-sans-cards);
  font-weight: 700;
  font-size: 17px;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  color: #1A1A1A;
  white-space: nowrap;
  transition: color 0.15s ease;
}

.brand-chip__logo.is-missing + .brand-chip__name {
  display: block;
}

/* Per-chip hover: name transitions ink -> magenta. This is the ONLY other
   place magenta appears besides "noi." - the category label below stays a
   quiet neutral grey always (never magenta), so the accent reads as a
   highlight instead of repeated noise across 40 identical labels. */
.brand-chip:hover .brand-chip__name {
  color: var(--marquee-magenta);
}

.brand-chip__sector {
  font-family: var(--font-sans-cards);
  font-weight: 400;
  font-size: 12px;
  letter-spacing: 0.02em;
  text-transform: lowercase;
  color: var(--muted);
  white-space: nowrap;
}

@media (max-width: 900px) {
  /* margin-top NOT reduced here on purpose - the clamp() already scales
     down naturally on a shorter mobile viewport via its vh term, and
     mobile needs the same "real breathing room" the spec asks for, not
     less than desktop. */
  .trust-marquee__track {
    gap: 28px;
  }
  .brand-chip__logo {
    height: 20px;
  }
  .brand-chip__name {
    font-size: 14px;
  }
  .brand-chip__sector {
    font-size: 11px;
  }
  /* Slightly stronger than desktop's 14% in relative terms (not absolute)
     since the same percentage reads as much thinner on a narrow viewport -
     kept subtle, not dramatic, per spec. */
  .trust-marquee__viewport {
    mask-image: linear-gradient(to right, transparent, #000 12%, #000 88%, transparent);
    -webkit-mask-image: linear-gradient(to right, transparent, #000 12%, #000 88%, transparent);
  }
}

/* Wide Viewports (1600px and above) */
@media (min-width: 1600px) {
  .gc-trust-container {
    max-width: 1360px;
  }
}

/* The actual motion stop (velocity = 0) is handled in JS (initTrustMarquee
   checks prefers-reduced-motion once at init) since the track no longer
   has a CSS animation to disable here - this just keeps the same
   accessible fallback as before: let a reduced-motion user manually
   horizontal-scroll the row instead of clipping it to a static, immobile
   width. */
@media (prefers-reduced-motion: reduce) {
  .trust-marquee__viewport {
    overflow-x: auto;
    mask-image: none;
    -webkit-mask-image: none;
  }
}

/* -----------------------------------------
   S6. VINCENZO FOUNDER SECTION (.gc-founder-*)
   Dark, sober, editorial portrait section.
   ----------------------------------------- */
.gc-founder-section {
  position: relative;
  width: 100%;
  /* Pure black, close enough to #settori's own near-black
     (--color-bg-dark, #060608) that the two sections - directly adjacent
     since the "metodo" section between them was removed - read as one
     continuous dark field with no perceptible seam. */
  background: #000000;
  /* 2026-08-05: restored to the original 5.5rem (was briefly dropped to
     2.5rem in the same day's earlier layout-fixes pass, paired with a
     grid restructure to close a dead-space bug below the paragraph - that
     restructure is what actually fixed the dead space, per the same-day
     regression-fix pass's diff gate, so the padding itself didn't need to
     shrink; shrinking it too just left the section sitting flush against
     the section above). Desktop only - mobile has its own full padding
     override below at max-width:768px, unaffected either way. */
  padding: 5.5rem 1.5rem;
  z-index: var(--z-content);
  border-top: 1px solid rgba(255, 255, 255, 0.06);
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
.gc-founder-container {
  max-width: 1080px;
  margin: 0 auto;
}
.gc-founder-grid {
  display: grid;
  grid-template-columns: 360px 1fr;
  gap: 4rem;
  align-items: center;
}
/* DESKTOP: the DOM is now ordered by reading priority (title → figure →
   body). Re-map onto a two-column layout with named areas so the portrait
   sits beside the copy without changing the mobile source order.
   2026-08-05 (layout-fixes pass): this used to be a 3-row (then 2-row)
   grid with .gc-founder-photo-figure spanning multiple row-tracks to sit
   beside both title and body. Measured live: whichever row(s) had no
   content of their own (originally the leftover eyebrow row; still true
   with 2 rows) got stretched by the browser's own track-sizing algorithm
   to help satisfy the spanning figure's total height - auto AND
   min-content tracks both did this - which is what produced the ~330px
   (then ~260px) of dead space below the paragraph; align-content:center
   never got a chance to run since the tracks absorbed all the space
   themselves before any leftover existed to center. Fix: figure no longer
   spans multiple rows at all - .gc-founder-copy (title+body, wrapped in
   index.html so figure/copy are simple single-cell siblings in ONE row)
   is its own flex column, vertically centered with justify-content,
   sidestepping the spanning-track growth behavior entirely. */
@media (min-width: 769px) {
  .gc-founder-grid {
    grid-template-columns: 360px 1fr;
    grid-template-areas: "figure copy";
    /* Explicit stretch (overrides the base rule's align-items:center) so
       .gc-founder-copy's own box fills the full row height - its internal
       justify-content:center is what actually centers title+body, not an
       outer align-self shrinking the wrapper to its own content size. */
    align-items: stretch;
    column-gap: 4rem;
  }
  .gc-founder-photo-figure { grid-area: figure; }
  .gc-founder-copy {
    grid-area: copy;
    display: flex;
    flex-direction: column;
    justify-content: center;
  }
}
.gc-founder-photo-slot {
  position: relative;
  width: 100%;
  aspect-ratio: 4 / 5;
  border-radius: 6px;
  overflow: hidden;
  background: #1A1A1A;
  border: 1px solid rgba(255, 255, 255, 0.08);
  box-shadow: 0 20px 40px rgba(0, 0, 0, 0.4);
}
.gc-founder-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  background: radial-gradient(circle at center, #222222 0%, #121212 100%);
}
.gc-founder-monogram {
  font-family: var(--font-serif-cards);
  font-size: 3.5rem;
  font-weight: 500;
  letter-spacing: 0.15em;
  color: rgba(255, 255, 255, 0.18);
  user-select: none;
}
.gc-founder-eyebrow {
  font-family: var(--font-sans);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--color-gray-text);
  display: block;
  margin-bottom: 0.85rem;
}
.gc-founder-title {
  font-family: var(--font-serif-cards);
  font-size: clamp(2.2rem, 3.8vw, 3.2rem);
  font-weight: 500;
  color: var(--color-white);
  line-height: 1.15;
  margin: 0 0 1.5rem;
}
/* Magenta accent on the emotional core of the promise ("al fianco" — the
   partnership, the reason to trust): one focused highlight the eye locks onto
   first, tying the founder message to the brand colour. */
.gc-founder-title-accent {
  color: var(--color-magenta-glow);
  font-style: italic;
}
.gc-founder-body {
  font-family: var(--font-sans);
  font-size: clamp(0.92rem, 1.15vw, 1.05rem);
  font-weight: 400;
  color: rgba(255, 255, 255, 0.78);
  line-height: 1.75;
  margin: 0;
}
.gc-founder-photo-figure {
  margin: 0;
}
.gc-founder-caption {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  margin-top: 1rem;
}
.gc-founder-caption-name {
  font-family: var(--font-serif);
  font-size: 1.05rem;
  font-weight: 500;
  color: var(--color-white);
  letter-spacing: 0.01em;
}
.gc-founder-caption-role {
  font-family: var(--font-sans);
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-gray-text);
}

@media (max-width: 768px) {
  /* MOBILE — single column, DOM order IS the hierarchy:
       1. eyebrow  (label: what this is)      — smallest, muted
       2. title    (the promise)              — dominant
       3. figure   (who backs it: portrait+name) — evidence
       4. body     (the story)                — detail, read last
     Spacing is set per-tier with margins (not a uniform grid gap) so the
     rhythm reflects the hierarchy: a big beat before the portrait, a
     smaller one before the body. */
  .gc-founder-section {
    margin-top: 2.25rem;
    padding: 3rem 1.5rem 3rem;
    /* No border seams on mobile — they'd mark the boundary the user wants to
       disappear against the matching black. */
    border-top: none;
    border-bottom: none;
  }
  /* Everything on one CENTERED axis — copy and portrait share the same
     centre line, like a composed editorial page. */
  .gc-founder-grid {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 0;   /* override the desktop 4rem grid gap — spacing is set per-tier
                 via each element's own margin, so the gap must be zero here */
  }
  /* .gc-founder-copy (added 2026-08-05 for the desktop grid fix, wraps
     title+body in index.html) must not exist as a box on mobile - display:
     contents unwraps it so title/body rejoin .gc-founder-grid's flex
     items directly, then `order` restores the original title -> figure ->
     body reading order (DOM order is now title+body wrapped together,
     then figure, since the wrapper can't sit between them in markup). */
  .gc-founder-copy { display: contents; }
  .gc-founder-title        { order: 1; }
  .gc-founder-photo-figure { order: 2; }
  .gc-founder-body         { order: 3; }

  /* 1. Eyebrow — quiet centered label + centered rule beneath it. */
  .gc-founder-eyebrow {
    font-size: 0.68rem;
    letter-spacing: 0.22em;
    text-indent: 0.22em;
    color: rgba(255, 255, 255, 0.5);
    margin: 0 0 0.85rem;
  }
  .gc-founder-eyebrow::after {
    content: '';
    display: block;
    width: 30px;
    height: 1px;
    background: rgba(255, 255, 255, 0.28);
    margin: 0.85rem auto 0;
  }

  /* Title — dominant, centered, balanced measure. Now the first element of
     the section (eyebrow removed), so it's a touch larger and leads. */
  .gc-founder-title {
    font-size: 2.05rem;
    line-height: 1.18;
    letter-spacing: 0.005em;
    color: #fff;
    max-width: 20ch;
    margin: 0 0 1.7rem;
  }

  /* 3. Evidence — centered 3:4 portrait (kept at its proper size); caption
     tight beneath it. */
  .gc-founder-photo-figure {
    width: 100%;
    max-width: 220px;
    margin: 0 0 1.4rem;
  }
  .gc-founder-photo-slot {
    width: 100%;
    max-width: none;
    margin: 0;
    aspect-ratio: 3 / 4;
    border-radius: 4px;
  }
  .gc-founder-caption {
    align-items: center;
    text-align: center;
    margin-top: 0.85rem;
    gap: 0.12rem;
  }
  .gc-founder-caption-name { font-size: 1rem; }
  .gc-founder-caption-role { font-size: 0.62rem; }

  /* 4. Body — supporting detail, kept CLOSE to the portrait/caption above. */
  .gc-founder-body {
    font-size: 0.9rem;
    line-height: 1.65;
    color: rgba(255, 255, 255, 0.66);
    max-width: 42ch;
    margin: 0.2rem auto 0;
  }

  /* ENTRANCE — each tier starts slightly low + transparent (portrait also a
     touch scaled-down) and settles as the section enters view, staggered in
     reading order. One class toggle (.gc-founder-in) + per-child delays =
     a smooth luxury reveal with no per-frame work. */
  .gc-founder-eyebrow,
  .gc-founder-title,
  .gc-founder-photo-figure,
  .gc-founder-body {
    opacity: 0;
    transform: translateY(22px);
    transition: opacity 0.8s cubic-bezier(0.22, 1, 0.36, 1),
                transform 0.8s cubic-bezier(0.22, 1, 0.36, 1);
    will-change: opacity, transform;
  }
  .gc-founder-photo-figure {
    transform: translateY(22px) scale(0.985);
  }
  .gc-founder-in .gc-founder-eyebrow,
  .gc-founder-in .gc-founder-title,
  .gc-founder-in .gc-founder-photo-figure,
  .gc-founder-in .gc-founder-body {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
  .gc-founder-in .gc-founder-eyebrow      { transition-delay: 0.02s; }
  .gc-founder-in .gc-founder-title        { transition-delay: 0.12s; }
  .gc-founder-in .gc-founder-photo-figure { transition-delay: 0.24s; }
  .gc-founder-in .gc-founder-body         { transition-delay: 0.36s; }
}

/* -----------------------------------------
   S7. STICKY CTA (.gc-sticky-cta-*)
   ----------------------------------------- */
/* Mobile Sticky Micro-CTA */
.gc-sticky-cta {
  display: none;
}

@media (max-width: 768px) {
  .gc-sticky-cta {
    display: block;
    position: fixed;
    bottom: 0;
    left: 0;
    width: 100%;
    z-index: 90;
    padding: 0.6rem 1rem;
    /* Opaque, with no backdrop-filter. This bar is position:fixed and visible
       for most of the page, so its blur was a full-width backdrop resample on
       every frame of every scroll — on the one device class that can least
       afford it. At 0.92 alpha there was almost nothing showing through to
       blur; at 1.0 there is nothing at all, and the bar looks the same. */
    background: rgb(13, 13, 13);
    border-top: 1px solid rgba(255, 255, 255, 0.1);
    transform: translateY(100%);
    transition: transform 0.35s cubic-bezier(0.16, 1, 0.3, 1);
    box-shadow: 0 -10px 25px rgba(0, 0, 0, 0.5);
  }
  .gc-sticky-cta.is-visible {
    transform: translateY(0);
  }
  .gc-sticky-cta-btn {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    min-height: 44px;
    padding: 0 1.25rem;
    background: var(--color-magenta-deep, #E00070);
    color: #FFFFFF;
    font-family: var(--font-sans);
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    border-radius: 4px;
    text-decoration: none;
  }
  .gc-sticky-cta-arrow {
    font-size: 1rem;
    margin-left: 0.5rem;
  }
}



/* .details-title-accent is shared with #settori's heading (this file's
   own #dettagli section, the projects "coverflow", was removed - see
   project-logs/lorenzo-caruso-reorder-2026-08-04.md). */
.details-title-accent {
  color: var(--color-magenta-glow, #E6236A);
}


/* -----------------------------------------
   N. OFF-SCREEN WORK — pause what cannot be seen
   -----------------------------------------
   Five animations on this page are `infinite`: two hero glow orbs, the scroll
   cue, the CTA orbit and the projects marquee. A CSS animation does not stop
   when it scrolls out of view — the compositor keeps ticking every one of them
   for the whole session, and each `will-change: transform` that serves them
   keeps a layer alive to be ticked. On a phone that is a permanent background
   tax paid while the reader is somewhere else entirely.

   js/main.js adds .gc-offscreen to a handful of section wrappers as they leave
   the viewport (see pauseOffscreenAnimations). Pausing rather than removing:
   animation-play-state keeps the element's current position, so anything
   mid-cycle resumes exactly where it was instead of jumping to frame 0. */
.gc-offscreen .gc-marquee-track,
.gc-offscreen .trust-marquee__track,
.gc-offscreen .consult-cta-orbit,
.gc-offscreen .hero-scroll-cue-line::after {
  animation-play-state: paused;
}

/* The glow orbs sit at body level, not inside .hero-section, so the class
   lands on the orb itself rather than on an ancestor. */
.glow-orb.gc-offscreen {
  animation-play-state: paused;
}

/* A paused animation still holds its compositor layer. Drop the promotion too
   while the section is away — it is re-created in the frame the class comes
   off, which is imperceptible for something that is only then entering view. */
.gc-offscreen .gc-marquee-track,
.gc-offscreen .trust-marquee__track,
.gc-offscreen .consult-cta-orbit,
.gc-offscreen .consult-glow {
  will-change: auto;
}


/* -----------------------------------------
   N+1. TRUST CARD PHOTOS — deferred
   -----------------------------------------
   The three stacked trust cards carry their photo as a background-image, and
   the <img> in the markup beside them is display:none (the background is what
   is actually seen). A CSS background is not subject to loading="lazy", so
   those three files — 353KB of card-blueprint / italy_map_card / card-venue —
   were fetched during the initial page load for a section well below the fold,
   competing with the seven sector photos the loader is actually waiting on.

   js/main.js adds .gc-trust-media-in one viewport before the section arrives
   (see deferTrustCardArt). The gradient scrims stay in the base rule so the
   cards keep their exact colour and legibility while the photo is still out;
   only the picture waits. */
.gc-trust-stack-card-1.gc-trust-media-in {
  background-image:
    linear-gradient(to top, rgba(6, 22, 48, 0.94) 0%, rgba(6, 22, 48, 0.55) 34%, rgba(6, 22, 48, 0) 62%),
    url('../assets/opt/card-blueprint.webp');
}
.gc-trust-stack-card-2.gc-trust-media-in {
  background-image:
    linear-gradient(to top, rgba(58, 30, 24, 0.92) 0%, rgba(58, 30, 24, 0.55) 34%, rgba(58, 30, 24, 0) 62%),
    url('../assets/opt/italy_map_card.webp');
}
.gc-trust-stack-card-3.gc-trust-media-in {
  background-image:
    linear-gradient(to top, rgba(40, 24, 16, 0.94) 0%, rgba(40, 24, 16, 0.5) 36%, rgba(40, 24, 16, 0.12) 68%),
    url('../assets/opt/card-venue.webp');
}

/* PHONES — the same three cards at a resolution a phone can actually use.

   A CSS background-image gets no srcset, so every phone was decoding the
   desktop masters: card-blueprint is 2380x1400 and card-venue 1197x1600 for
   a card that is ~360px wide on a 390px screen. In RAM that is 12.7MB and
   7.3MB per card — 22.9MB for the three, held for as long as the section is
   alive, on top of the animation frames below. Decoding them is also a long
   main-thread task landing exactly where the reader starts scrolling, which
   is the stutter reported right after the hero.

   The -m derivatives are the same pictures at 900w / 600w / 800w: 22.9MB of
   RAM becomes 7.0MB and 262KB over the wire becomes 104KB, at a size where
   the difference is not visible. */
@media (max-width: 768px) {
  .gc-trust-stack-card-1.gc-trust-media-in {
    background-image:
      linear-gradient(to top, rgba(6, 22, 48, 0.94) 0%, rgba(6, 22, 48, 0.55) 34%, rgba(6, 22, 48, 0) 62%),
      url('../assets/opt/card-blueprint-m.webp');
  }
  .gc-trust-stack-card-2.gc-trust-media-in {
    background-image:
      linear-gradient(to top, rgba(58, 30, 24, 0.92) 0%, rgba(58, 30, 24, 0.55) 34%, rgba(58, 30, 24, 0) 62%),
      url('../assets/opt/italy_map_card-m.webp');
  }
  .gc-trust-stack-card-3.gc-trust-media-in {
    background-image:
      linear-gradient(to top, rgba(40, 24, 16, 0.94) 0%, rgba(40, 24, 16, 0.5) 36%, rgba(40, 24, 16, 0.12) 68%),
      url('../assets/opt/card-venue-m.webp');
  }
}
