/* ============================================================
   ANDREA'S LAB — site stylesheet
   Mobile-first. Luxurious, dark, restrained. Elegant serif
   headlines (Cormorant) + Montserrat Alt1 for UI (self-hosted).
   Red reserved for primary action; muted champagne accent
   for labels and hairlines.
   ============================================================ */

/* ---------- Self-hosted fonts (Montserrat Alt1, OFL) ---------- */
@font-face {
  font-family: "MontserratAlt1";
  src: url("../fonts/MontserratAlt1-Regular.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "MontserratAlt1";
  src: url("../fonts/MontserratAlt1-Medium.woff2") format("woff2");
  font-weight: 500; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "MontserratAlt1";
  src: url("../fonts/MontserratAlt1-SemiBold.woff2") format("woff2");
  font-weight: 600; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "MontserratAlt1";
  src: url("../fonts/MontserratAlt1-Bold.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}

/* ---------- Self-hosted headline + body fonts (SIL Open Font License) ----------
   Cormorant Garamond (headlines) and Montserrat (body) used to be pulled from
   fonts.googleapis.com / fonts.gstatic.com. They are now served from this domain
   instead: a third-party font request hands the visitor's IP address to Google
   on every page load, which is exactly the transfer the site's privacy policy
   would otherwise have to declare (and which Italian/EU authorities treat as a
   real issue). Self-hosting removes the transfer entirely, drops two DNS+TLS
   handshakes from every first paint, and lets the Content-Security-Policy go
   back to allowing NO external origin at all.

   Both families are OFL-licensed, so redistributing the files is permitted.
   Only the `latin` subset is shipped — it covers Italian and English in full;
   anything outside it falls back through the same Georgia / system-ui stack as
   before. Weights match exactly what the old Google request asked for, so the
   rendering is unchanged. `font-display: swap` keeps text visible while loading.
   unicode-range lets the browser skip a file entirely if the page needs no
   glyph from it. */
@font-face {
  font-family: "Cormorant Garamond";
  src: url("../fonts/CormorantGaramond-300.woff2") format("woff2");
  font-weight: 300; font-style: normal; font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Cormorant Garamond";
  src: url("../fonts/CormorantGaramond-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Cormorant Garamond";
  src: url("../fonts/CormorantGaramond-500.woff2") format("woff2");
  font-weight: 500; font-style: normal; font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Cormorant Garamond";
  src: url("../fonts/CormorantGaramond-600.woff2") format("woff2");
  font-weight: 600; font-style: normal; font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Cormorant Garamond";
  src: url("../fonts/CormorantGaramond-300-Italic.woff2") format("woff2");
  font-weight: 300; font-style: italic; font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Cormorant Garamond";
  src: url("../fonts/CormorantGaramond-400-Italic.woff2") format("woff2");
  font-weight: 400; font-style: italic; font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Cormorant Garamond";
  src: url("../fonts/CormorantGaramond-500-Italic.woff2") format("woff2");
  font-weight: 500; font-style: italic; font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Montserrat";
  src: url("../fonts/Montserrat-300.woff2") format("woff2");
  font-weight: 300; font-style: normal; font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Montserrat";
  src: url("../fonts/Montserrat-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Montserrat";
  src: url("../fonts/Montserrat-500.woff2") format("woff2");
  font-weight: 500; font-style: normal; font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Montserrat";
  src: url("../fonts/Montserrat-600.woff2") format("woff2");
  font-weight: 600; font-style: normal; font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ---------- Fonts ----------
   --display  : easily swappable. Alternatives that work well here:
                "Marcellus", "Bodoni Moda", "Italiana", "Playfair Display".
                Change just this one line to try another headline font.
   --ui       : self-hosted MontserratAlt1 (the real font from the logo).
                "Montserrat Alternates" kept as fallback (95% identical).
   --wordmark : alias for the brand wordmark + footer. Same font, bold.
*/
:root {
  --display:  "Cormorant Garamond", Georgia, "Times New Roman", serif;
  --ui:       "MontserratAlt1", "Montserrat Alternates", "Montserrat", system-ui, sans-serif;
  --wordmark: "MontserratAlt1", "Montserrat Alternates", "Montserrat", system-ui, sans-serif;
  --body:     "Montserrat", system-ui, -apple-system, "Segoe UI", sans-serif;

  /* Palette — warm near-black (a touch lighter than v1), luxe off-white */
  --bg:        #111114;     /* was #0a0a0c — a little less dark */
  --bg-2:      #16161a;     /* was #0e0e11 */
  --panel:     #1c1c22;     /* was #141417 */
  --panel-2:   #23232a;     /* was #1b1b1f */
  --text:      #f3f1ed;     /* warm off-white */
  --muted:     #b3ada3;     /* warm gray (slightly brighter for contrast on new bg) */
  --soft:      #837d72;
  --faint:     #5a544c;

  /* Accents */
  --gold:      #c2a878;     /* muted champagne — luxe accent, used sparingly */
  --gold-2:    #d8c39a;
  --gold-soft: rgba(194, 168, 120, .14);
  --red:       #d70b18;     /* primary action only */
  --red-bright:#e51a28;
  --red-deep:  #b40e1a;
  --maroon:    #63131b;     /* logo tie-in */

  --line:      rgba(255, 255, 255, .085);
  --line-2:    rgba(255, 255, 255, .05);
  --line-gold: rgba(194, 168, 120, .26);

  --shadow:    0 40px 100px -30px rgba(0, 0, 0, .7);
  --shadow-sm: 0 18px 50px -20px rgba(0, 0, 0, .6);

  /* Fluid type scale */
  --t-label: clamp(.7rem, .68rem + .1vw, .76rem);
  --t-sm:    clamp(.86rem, .84rem + .12vw, .94rem);
  --t-body:  clamp(.98rem, .95rem + .15vw, 1.08rem);
  --t-lead:  clamp(1.1rem, 1.02rem + .35vw, 1.32rem);
  --t-h3:    clamp(1.5rem, 1.3rem + .9vw, 2.1rem);
  --t-h2:    clamp(2.1rem, 1.6rem + 2.4vw, 3.6rem);
  --t-h1:    clamp(2.9rem, 2.1rem + 4vw, 5.6rem);

  --gutter:  clamp(1.15rem, .9rem + 1.5vw, 2.5rem);
  --section: clamp(3.5rem, 2.6rem + 4.5vw, 8rem);
  /* Default gap between the LAST block and the footer border. It is the old
     "--section (contatti bottom) + footer margin-top" combined into one value,
     so the per-page footer knob controls the whole visible gap. */
  --page-end: clamp(5.5rem, 4.1rem + 6vw, 11rem);
  --max:     1240px;
  --radius:  20px;
  --radius-lg: 28px;
}

/* ---------- Reset / base ---------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
body {
  margin: 0;
  font-family: var(--body);
  font-size: var(--t-body);
  font-weight: 400;
  line-height: 1.7;
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}
img, svg, video { display: block; max-width: 100%; height: auto; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; cursor: pointer; background: none; border: 0; }
h1, h2, h3, h4 { margin: 0; font-weight: 400; }

.skip {
  position: absolute; left: -9999px; top: 0;
  background: var(--text); color: var(--bg);
  padding: .6rem 1rem; z-index: 1000; border-radius: 10px;
  font-family: var(--ui); font-size: .85rem;
}
.skip:focus { left: 1rem; top: 1rem; }
:focus-visible { outline: 2px solid var(--gold); outline-offset: 3px; border-radius: 3px; }

.wrap { width: 100%; max-width: var(--max); margin-inline: auto; padding-inline: var(--gutter); }

/* ---------- Shared bits ---------- */
.kicker {
  display: inline-flex; align-items: center; gap: .55rem;
  font-family: var(--ui);
  font-size: var(--t-label);
  font-weight: 600;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--gold);
}
.kicker svg { width: 15px; height: 15px; stroke: var(--gold); flex: 0 0 auto; }
.kicker .bar { width: 26px; height: 1px; background: var(--line-gold); }

.h-serif {
  font-family: var(--display);
  font-weight: 400;
  letter-spacing: -.01em;
  line-height: 1.02;
  color: var(--text);
}
.h-serif em { font-style: italic; color: var(--gold-2); }
.h-serif .red { color: var(--red); font-style: italic; }

.lead { color: var(--muted); font-size: var(--t-lead); line-height: 1.6; }
.muted { color: var(--muted); }

/* Buttons */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .6rem;
  font-family: var(--ui); font-weight: 600;
  font-size: .9rem; letter-spacing: .02em;
  min-height: 52px; padding: 0 1.5rem;
  border-radius: 999px;
  border: 1px solid transparent;
  transition: transform .25s, filter .25s, background .25s, border-color .25s, color .25s;
}
.btn svg { width: 16px; height: 16px; transition: transform .25s; }
.btn:hover svg { transform: translateX(3px); }
.btn-primary {
  background: linear-gradient(180deg, var(--red-bright), var(--red-deep));
  color: #fff;
  box-shadow: 0 16px 40px -14px rgba(183, 14, 26, .6);
}
.btn-primary:hover { transform: translateY(-2px); filter: brightness(1.07); }
.btn-line {
  border-color: var(--line-gold);
  color: var(--text);
  background: transparent;
}
.btn-line:hover { border-color: var(--gold); color: var(--gold-2); background: var(--gold-soft); }
.btn-ghost { border-color: var(--line); color: var(--text); }
.btn-ghost:hover { border-color: rgba(255,255,255,.25); background: rgba(255,255,255,.04); }

/* Text link with arrow */
.tlink {
  display: inline-flex; align-items: center; gap: .5rem;
  font-family: var(--ui); font-weight: 600;
  font-size: .82rem; letter-spacing: .08em; text-transform: uppercase;
  color: var(--gold);
  padding-bottom: 4px;
  border-bottom: 1px solid var(--line-gold);
  transition: gap .25s, color .25s, border-color .25s;
}
.tlink svg { width: 14px; height: 14px; transition: transform .25s; }
.tlink:hover { gap: .9rem; color: var(--gold-2); border-color: var(--gold); }

/* ---------- Header ---------- */
.site-header {
  position: sticky; top: 0; z-index: 60;
  background: color-mix(in srgb, var(--bg) 80%, transparent);
  backdrop-filter: saturate(160%) blur(16px);
  -webkit-backdrop-filter: saturate(160%) blur(16px);
  border-bottom: 1px solid transparent;
  transition: border-color .3s, background .3s;
}
.site-header.is-scrolled {
  border-bottom-color: var(--line);
  background: color-mix(in srgb, var(--bg) 92%, transparent);
}
.nav {
  min-height: 80px;
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
}

/* Brand: emblem + wordmark, perfectly aligned */
.brand {
  display: inline-flex; align-items: center; gap: .65rem;
  flex: 0 0 auto;
  min-width: 0; /* allow text to shrink if needed */
}
.brand-emblem {
  height: 52px; width: auto;        /* was 46px — a touch bigger */
  filter: drop-shadow(0 6px 14px rgba(0,0,0,.5));
  transform: rotate(0.02deg);       /* Firefox fix — see the foot of this file */
  transition: transform .3s;
  flex: 0 0 auto;
}
.brand:hover .brand-emblem { transform: scale(1.04) rotate(0.02deg); }

.brand-word {
  font-family: var(--wordmark);
  font-weight: 700;                 /* MontserratAlt1-Bold */
  font-size: clamp(1.05rem, .82rem + .9vw, 1.4rem);
  letter-spacing: -.005em;
  line-height: 1;
  color: var(--text);
  white-space: nowrap;
  /* optical alignment: emblem has internal padding, nudge text up a hair */
  margin-top: 2px;
}
/* Brand-red wordmark span. Emitted by the [[…]] text marker (helpers.php →
   alb_rich) and by the static wordmark in the header/footer. It is red in EVERY
   context — headings, hero titles, the story band, the contact box, etc. — so
   the [[…]] rule behaves consistently wherever the owner uses it (this mirrors
   the admin panel's own global .lab rule). Do NOT re-scope this per section. */
.lab { color: var(--red); }
.brand:hover .brand-word { color: var(--text); }

/* Phones: the IT/EN pill now always sits in the top bar (left of Instagram),
   so give the brand a little less room. The wordmark is scaled down in two
   tiers (never hidden), and the brand may shrink as a LAST resort so that a
   very wide logo or a tiny screen truncates the wordmark instead of pushing the
   controls off-screen. On roomy phones nothing shrinks. */
@media (max-width: 879px) {
  .brand { flex: 0 1 auto; }                 /* allow shrink only if the row is tight */
  .brand-word { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
}
@media (max-width: 420px) {
  .brand { gap: .45rem; }
  .brand-emblem { height: 46px; }
  .brand-word { font-size: .92rem; }
}
@media (max-width: 360px) {
  .brand { gap: .4rem; }
  .brand-emblem { height: 42px; }
  .brand-word { font-size: .86rem; }
}

.nav-menu { display: none; }
.nav-right { display: flex; align-items: center; gap: .5rem; }

.ig-link {
  display: inline-grid; place-items: center;
  width: 44px; height: 44px; border-radius: 50%;
  border: 1px solid var(--line);
  color: var(--text);
  transition: border-color .25s, color .25s, background .25s;
}
.ig-link:hover { border-color: var(--gold); color: var(--gold); background: var(--gold-soft); }
.ig-link svg { width: 19px; height: 19px; }

.nav-toggle {
  display: inline-grid; place-items: center;
  width: 46px; height: 46px; border-radius: 12px;
  border: 1px solid var(--line);
}
.nav-toggle svg { width: 22px; height: 22px; stroke: var(--text); }

/* Desktop nav */
@media (min-width: 880px) {
  .nav-toggle { display: none; }
  .nav-menu {
    display: flex; align-items: center; gap: 2rem;
    font-family: var(--ui); font-weight: 500;
    font-size: .9rem;
  }
  .nav-menu a { position: relative; color: var(--text); opacity: .82; transition: opacity .2s; padding: .4rem 0; }
  .nav-menu a:hover, .nav-menu a.is-current { opacity: 1; }
  .nav-menu a.is-current::after {
    content: ""; position: absolute; left: 0; right: 0; bottom: -8px;
    height: 1px; background: var(--gold);
  }
  .brand-emblem { height: 58px; }   /* was 50px — slightly bigger on desktop */
  .brand { gap: .85rem; }
}

/* Mobile menu overlay */
.nav-menu.mobile {
  position: fixed; inset: 0; top: 80px;
  display: flex; flex-direction: column; justify-content: center;
  gap: 1.5rem;
  padding: 2rem var(--gutter);
  background: var(--bg);
  transform: translateY(-120%);
  /* Fully hide when closed so the off-screen panel can never "peek" back into
     view on short/landscape phones (the last item used to show at the top). */
  visibility: hidden;
  pointer-events: none;
  overflow-y: auto;                 /* if opened in landscape, it scrolls */
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  transition: transform .4s cubic-bezier(.6,0,.2,1), visibility .4s;
  z-index: 55;
  border-bottom: 1px solid var(--line);
}
.nav-menu.mobile.is-open { transform: translateY(0); visibility: visible; pointer-events: auto; }
.nav-menu.mobile a {
  font-family: var(--display);
  font-size: 2rem; font-weight: 400;
  color: var(--text);
  letter-spacing: -.01em;
}
.nav-menu.mobile a .n { font-family: var(--ui); font-size: .8rem; color: var(--gold); display: block; letter-spacing: .12em; }
@media (min-width: 880px) { .nav-menu.mobile { display: none; } }

/* ---------- Hero ----------
   Spacing is tunable from the panel (Contenuti → Home → Hero → Spaziatura):
   the values below are the responsive DEFAULTS, overridden per-field by CSS
   variables emitted by site-vars.php only when the admin sets them. Mobile
   keeps its original values; the desktop overrides live in the ≥880px block. */
.hero {
  padding-top: var(--hero-pt, clamp(2.25rem, 1.6rem + 1.6vw, 3.1rem));
  padding-bottom: var(--hero-pb, clamp(2.5rem, 2rem + 3vw, 4rem));
}
.hero-inner { display: grid; gap: 2.5rem; }
.hero-copy .kicker { margin-bottom: 1.5rem; }
.hero-title { font-size: var(--t-h1); margin: 0 0 1.5rem; }
.hero-lead { max-width: 42ch; margin: 0 0 2rem; }
.hero-actions { display: flex; flex-wrap: wrap; gap: .85rem; }

.hero-carousel {
  position: relative;
  border-radius: var(--radius-lg);
  overflow: hidden;
  border: 1px solid var(--line);
  box-shadow: var(--shadow);
}
.hero-stage { position: relative; aspect-ratio: 4 / 5; }
.hero-slide {
  position: absolute; inset: 0; margin: 0;
  opacity: 0; visibility: hidden;
  transition: opacity 1s cubic-bezier(.4,0,.2,1), visibility 1s;
}
.hero-slide.is-active { opacity: 1; visibility: visible; }
.hero-slide::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(180deg, transparent 50%, rgba(0,0,0,.62));
  pointer-events: none;
}
/* The rotate(0.02deg) is the Firefox fix — see FIREFOX ZOOM FIX at the foot of
   this file before touching it. This animation (6s across 8% of scale) is the
   one that showed the fault worst. */
.hero-slide img {
  width: 100%; height: 100%; object-fit: cover;
  transform: scale(1.02) rotate(0.02deg);
}
.hero-slide.is-active img { animation: heroKen 6s ease-out forwards; }
@keyframes heroKen {
  from { transform: scale(1.08) rotate(0.02deg); }
  to   { transform: scale(1.0)  rotate(0.02deg); }
}
.hero-slide figcaption {
  position: absolute; left: 1.4rem; bottom: 1.4rem; z-index: 2;
  font-family: var(--ui); font-size: .82rem; letter-spacing: .04em; color: var(--text);
}
.hero-slide figcaption small { display: block; color: var(--gold-2); letter-spacing: .16em; text-transform: uppercase; margin-bottom: 5px; font-weight: 600; font-size: .66rem; }
.hero-dots {
  position: absolute; right: 1.2rem; bottom: 1.3rem; z-index: 3;
  display: flex; gap: .5rem;
}
.hero-dot {
  width: 8px; height: 8px; padding: 0; border-radius: 50%;
  background: rgba(255,255,255,.32);
  transition: background .3s, transform .3s, width .3s;
}
.hero-dot.is-on { background: var(--gold-2); width: 22px; border-radius: 999px; }
.hero-dot:hover { background: rgba(255,255,255,.6); }

@media (min-width: 880px) {
  /* Desktop top gap is reduced (the kicker sits closer under the navbar) and
     the bottom padding is kept; both are overridable from the panel. */
  .hero {
    padding-top: var(--hero-pt-d, clamp(1.25rem, 0.6rem + 1vw, 1.9rem));
    padding-bottom: var(--hero-pb-d, clamp(2.5rem, 2rem + 3vw, 4rem));
  }
  /* The carousel is centered against the text column, then nudged up by a small
     amount so it reads as centered on the TEXT (kicker + title + lead) rather than
     on the whole column — i.e. without the red button below pulling it down. The
     alignment (in alto / centrato / in basso), the column gap, and this vertical
     nudge are all editable in the panel; the defaults here apply when a field is
     left empty. (Default −40px ≈ half the button block; set 0 to centre on the
     full column, button included.) */
  .hero-inner {
    grid-template-columns: 1.05fr .95fr;
    align-items: var(--hero-align-d, center);
    gap: var(--hero-gap-d, clamp(2.5rem, 4vw, 5rem));
  }
  /* .hero-carousel is already position:relative (it anchors its own dots), so this
     `top` just offsets it visually — no reflow, dots/caption move with it. Applied
     only here, so the mobile (single-column) hero is unaffected. */
  .hero-carousel { top: var(--hero-car-shift, -40px); }
  /* When there are no hero images, the copy stands alone full-width (no empty column). */
  .hero-inner--noimg { grid-template-columns: minmax(0, 1fr); }
}

/* ---------- Section scaffold ----------
   Vertical rhythm rule: every major block OWNS THE SPACE ABOVE ITSELF (its
   padding-top), and none carries bottom padding; the space before the footer
   is the page's own bottom padding (see the per-page spacing block further
   down). This makes each gap a single, well-defined value — editable from the
   panel (Contenuti → <pagina> → Spaziatura) via the per-page CSS variables
   emitted by site-vars.php — and it degrades gracefully when a section is
   empty and disappears (the next block still brings its own gap). */
.section { padding-top: var(--section); padding-bottom: 0; position: relative; }
.section-head { margin-bottom: clamp(2rem, 1.5rem + 2vw, 3.5rem); }
.section-head .kicker { margin-bottom: 1rem; }
.section-head h2 { font-size: var(--t-h2); max-width: 16ch; }
.section-head .sub { color: var(--muted); margin-top: 1rem; max-width: 52ch; font-size: var(--t-lead); }
.section-head.between { display: grid; gap: 1.25rem; }
@media (min-width: 820px) {
  .section-head.between { grid-template-columns: 1fr auto; align-items: end; }
}

/* ---------- Editable per-page section spacing ----------
   Each gap between two blocks is the LOWER block's padding-top (see the
   rhythm note above); the space before the footer is <main>'s padding-bottom.
   The values below are the responsive DEFAULTS; each is overridable per page
   from the panel (Contenuti → pagina → "Spaziatura della pagina") through the
   CSS variables emitted by site-vars.php — mobile variable below 880px, the
   "-d" variable from 880px up (the same split the hero uses). An unset field
   simply keeps the default, so the layout is always responsive out of the box.
   Anchored sections get a scroll-margin so in-page jumps (#creazioni,
   #personalizzazioni, #contatti) land clear of the sticky header. */
#creazioni, #personalizzazioni, #chi-sono, .sec-contatti { scroll-margin-top: 86px; }

/* Home (the three ids are home-only, so no page scope is needed) */
#creazioni         { padding-top: var(--sph-creazioni, var(--section)); }
#personalizzazioni { padding-top: var(--sph-pz, var(--section)); }
#chi-sono          { padding-top: var(--sph-story, var(--section)); }
.page-home .sec-contatti { padding-top: var(--sph-contatti, var(--section)); }
.page-home main          { padding-bottom: var(--sph-footer, var(--page-end)); }
@media (min-width: 880px) {
  #creazioni         { padding-top: var(--sph-creazioni-d, var(--section)); }
  #personalizzazioni { padding-top: var(--sph-pz-d, var(--section)); }
  #chi-sono          { padding-top: var(--sph-story-d, var(--section)); }
  .page-home .sec-contatti { padding-top: var(--sph-contatti-d, var(--section)); }
  .page-home main          { padding-bottom: var(--sph-footer-d, var(--page-end)); }
}

/* Galleria (.g-grid keeps its default in its own rule further down) */
.page-galleria .sec-contatti { padding-top: var(--spg-contatti, var(--section)); }
.page-galleria main          { padding-bottom: var(--spg-footer, var(--page-end)); }
@media (min-width: 880px) {
  .g-grid                      { padding-top: var(--spg-grid-d, clamp(1.5rem, 1rem + 2vw, 2.5rem)); }
  .page-galleria .sec-contatti { padding-top: var(--spg-contatti-d, var(--section)); }
  .page-galleria main          { padding-bottom: var(--spg-footer-d, var(--page-end)); }
}

/* Chi sono (.cs-bio-sec / .cs-timeline keep their defaults in their own rules) */
.page-chisono .sec-contatti  { padding-top: var(--spc-contatti, var(--section)); }
.page-chisono main           { padding-bottom: var(--spc-footer, var(--page-end)); }
@media (min-width: 880px) {
  .cs-bio-sec                  { padding-top: var(--spc-bio-d, clamp(2rem, 1.5rem + 2vw, 3.5rem)); }
  .cs-timeline                 { padding-top: var(--spc-timeline-d, clamp(1rem, .5rem + 1.5vw, 2.5rem)); }
  .page-chisono .sec-contatti  { padding-top: var(--spc-contatti-d, var(--section)); }
  .page-chisono main           { padding-bottom: var(--spc-footer-d, var(--page-end)); }
}

/* Product card (gallery grid) */
.card {
  /* Column flex so the photo + body stay top-aligned when the card is stretched
     taller than its content (grid/flex rows stretch every card to the tallest in
     the row). With plain `display:block`, <button> cards centre their content
     vertically — that pushed the photo down (leaving a dark band above it) and
     dropped the footer out of line whenever a title was shorter than its row. */
  display: flex;
  flex-direction: column;
  background: linear-gradient(180deg, var(--panel), var(--bg-2));
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  /* box-shadow is deliberately NOT in this list. Transitioning a shadow repaints
     the whole card on every frame, and the card contains the photo that is
     scaling at the same time — so the photo got dragged into a main-thread
     repaint 60x a second instead of being scaled by the GPU. The shadow still
     appears on hover, it just arrives at once instead of fading in over .3s. */
  transition: transform .4s cubic-bezier(.2,.7,.2,1), border-color .3s;
  height: 100%;
}
.card:hover { transform: translateY(-5px); border-color: var(--line-gold); box-shadow: var(--shadow-sm); }
.card-photo { aspect-ratio: 1 / 1; overflow: hidden; background: #1a1a1f; }
.card-photo img { width: 100%; height: 100%; object-fit: cover; transform: rotate(0.02deg); transition: transform 1.2s cubic-bezier(.2,.7,.2,1); }
.card:hover .card-photo img { transform: scale(1.06) rotate(0.02deg); }
.card-body { padding: 1.1rem 1.2rem 1.3rem; }
.card-cat {
  font-family: var(--ui); font-size: .64rem; font-weight: 600;
  letter-spacing: .14em; text-transform: uppercase; color: var(--gold);
  margin-bottom: .45rem;
}
.card-title { font-family: var(--display); font-size: 1.3rem; line-height: 1.1; letter-spacing: -.005em; }

/* ---------- Alternating category blocks ---------- */
.cat-block {
  display: grid; gap: 1.5rem;
  padding-block: clamp(2.5rem, 1.8rem + 2.4vw, 4.25rem);
  position: relative;
  align-items: center;
}
.cat-block + .cat-block::before {
  content: ""; position: absolute; top: 0; left: 50%; transform: translateX(-50%);
  width: min(100%, 640px); height: 1px;
  background: linear-gradient(90deg, transparent, var(--line-gold), transparent);
}
.cat-figure {
  position: relative;
  border-radius: var(--radius-lg);
  overflow: hidden;
  border: 1px solid var(--line);
  aspect-ratio: 5 / 4;
}
.cat-figure img { width: 100%; height: 100%; object-fit: cover; transform: rotate(0.02deg); transition: transform 1.1s cubic-bezier(.2,.7,.2,1); }
/* Only the slide actually on screen zooms. The old rule matched every <img> in
   the figure, so hovering a block animated all N hidden carousel frames at once
   — N times the work per frame, for one visible result. */
.cat-block:hover .cat-figure > img,
.cat-block:hover .cat-figure .catc-item.is-active img { transform: scale(1.05) rotate(0.02deg); }
.cat-index {
  font-family: var(--display); font-style: italic;
  font-size: 1.4rem; color: var(--gold);
  margin-bottom: .6rem;
}
.cat-block h3 { font-family: var(--display); font-size: var(--t-h3); margin-bottom: .75rem; }
.cat-block p { color: var(--muted); max-width: 44ch; margin: 0 0 1.5rem; font-size: var(--t-body); }

@media (min-width: 820px) {
  .cat-block { grid-template-columns: 1.18fr .82fr; gap: clamp(2.5rem, 4vw, 5rem); }
  /* Keep the image (carousel) in the WIDE column on every block. .flip still swaps
     the visual order (image left vs. right) for rhythm, but mirroring the column
     tracks here keeps the image the same larger size whether it sits left or right
     (without this, .flip put the image in the narrow column on alternate blocks). */
  .cat-block.flip { grid-template-columns: .82fr 1.18fr; }
  .cat-block.flip .cat-figure { order: 2; }
  .cat-block.flip .cat-text { order: 1; }
  .cat-block .cat-text { padding-inline: clamp(0px, 2vw, 2rem); }
}

/* Mobile (single column): the image carousel sits BETWEEN the description and the
   "Vedi…" link — index → title → description → carousel → button. The text block is
   flattened with display:contents so its children and the figure share the block's
   grid and can be ordered. The desktop two-column layout (above) is untouched. */
@media (max-width: 819.98px) {
  .cat-block { gap: 0; }
  .cat-block .cat-text { display: contents; }
  .cat-block .cat-text .cat-index { order: 1; }
  .cat-block .cat-text h3 { order: 2; }
  .cat-block .cat-text p { order: 3; }
  .cat-block .cat-figure { order: 4; margin-bottom: 1.5rem; }
  .cat-block .cat-text .tlink { order: 5; justify-self: start; }
}

/* ---------- Engraving feature list (Personalizzazioni) ---------- */
.eng-list { list-style: none; padding: 0; margin: 1.25rem 0 2rem; display: grid; gap: .75rem; }
.eng-list li { display: flex; align-items: flex-start; gap: .75rem; color: var(--text); font-size: var(--t-sm); }
.eng-list svg { width: 18px; height: 18px; stroke: var(--gold); flex: 0 0 auto; margin-top: 3px; }
.eng-list .eng-ico { width: 18px; height: 18px; object-fit: contain; flex: 0 0 auto; margin-top: 3px; }

/* ---------- Story band (home "La storia") ----------
   Photo badge in the same family as the #contatti .cta-box: gold hairline,
   rounded, maroon radial accent — with the whole-lab photo behind a dark,
   text-legible overlay. The photo is a real <img> (.story-bg) clipped by the
   box, the gradient is .story-overlay, and the gold hairline is drawn ON TOP
   via .story-band::after. Drawing the ring on top (instead of a CSS `border`
   under a clipped background) means the border always hugs the photo exactly —
   no sub-pixel fringe at the rounded corners. Falls back to --bg-2 if the photo
   is missing. */
.story-band {
  position: relative; overflow: hidden; isolation: isolate;
  border-radius: var(--radius-lg);
  background-color: var(--bg-2);
  padding: clamp(2rem, 1.6rem + 4vw, 3.5rem) var(--gutter);
}
.story-bg {
  position: absolute; inset: 0; z-index: 0;
  width: 100%; height: 100%;
  object-fit: cover; object-position: center;
}
.story-overlay {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background:
    linear-gradient(180deg, rgba(17, 17, 20, .82), rgba(17, 17, 20, .94)),
    radial-gradient(circle at 84% 10%, rgba(99, 19, 27, .34), transparent 60%);
}
.story-band::after {
  content: ""; position: absolute; inset: 0; z-index: 2; pointer-events: none;
  border-radius: inherit;
  box-shadow: inset 0 0 0 1px var(--line-gold);
}
.story-band-inner { position: relative; z-index: 3; max-width: 48ch; }
.story-band .kicker { color: var(--gold); margin-bottom: 1rem; }
.story-band h2 { font-family: var(--display); font-size: var(--t-h2); margin: 0 0 1rem; }
.story-band h2 em { font-style: italic; color: var(--gold-2); }
.story-band p { color: var(--text); margin: 0 0 1rem; max-width: 46ch; }
.story-band .dietro-label { color: var(--muted); }
.story-band .dietro-claim { color: var(--text); }
@media (min-width: 880px) {
  .story-band {
    display: flex; align-items: center;
    min-height: 420px;
    padding-block: clamp(3rem, 2rem + 4vw, 4.5rem);
  }
  /* Left-heavy on desktop: text sits over the darkest part of the photo. */
  .story-overlay {
    background:
      linear-gradient(90deg, rgba(17, 17, 20, .94) 0%, rgba(17, 17, 20, .84) 42%, rgba(17, 17, 20, .5) 74%, rgba(17, 17, 20, .42) 100%),
      radial-gradient(circle at 86% 14%, rgba(99, 19, 27, .4), transparent 56%);
  }
  .story-band-inner { max-width: 52ch; }
}

/* Home "Dietro Andrea's Lab" — closing claim (the brand promise). Kept light
   so the home stays balanced; the full story lives on chi-sono.html. */
.dietro-label { font-family: var(--ui); font-size: var(--t-sm); letter-spacing: .03em; color: var(--soft); margin: 1.75rem 0 .85rem; }
.dietro-claim {
  font-family: var(--display); font-weight: 300;
  font-size: clamp(1.7rem, 1.2rem + 2.2vw, 2.6rem);
  line-height: 1.12; letter-spacing: -.01em; color: var(--text);
  margin: 0 0 2rem; max-width: 24ch;
}
.dietro-claim strong { display: block; font-weight: 500; font-style: italic; color: var(--gold-2); }

/* ---------- Personalizzazioni: a single horizontal row of cards ----------
   The cards sit side-by-side on ONE line: they grow to fill the row when there are
   few, and the row scrolls sideways (never wraps) when there are more than fit. Each
   card's photo area is a .cat-carousel / .catc-* fade carousel (auto-advancing, arrows
   on hover; on touch the row owns the horizontal swipe and the photos auto-advance).
   Cards are display-only (not links). */
.pz-strip { position: relative; margin-top: 1.75rem; }
.pz-collections {
  display: flex;
  flex-wrap: nowrap;                 /* one line — never wrap to a new row */
  gap: clamp(1rem, .7rem + 1vw, 1.6rem);
  overflow-x: auto;                  /* scroll sideways when the cards overflow */
  scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
  scrollbar-width: none;             /* arrows + the next-card peek are the affordance */
  padding-bottom: .15rem;
}
.pz-collections::-webkit-scrollbar { display: none; }
.pz-card {
  flex: 1 0 clamp(14rem, 38vw, 16.5rem);   /* fill the line when few; basis caps how many fit before scrolling */
  min-width: 0;
  scroll-snap-align: start;
  display: flex; flex-direction: column;
  background: linear-gradient(180deg, var(--panel), var(--bg-2));
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  transition: transform .4s cubic-bezier(.2,.7,.2,1), border-color .3s;   /* not box-shadow — see .card */
}
.pz-card:hover { transform: translateY(-5px); border-color: var(--line-gold); box-shadow: var(--shadow-sm); }
.pz-card-media { margin: 0; aspect-ratio: 1 / 1; background: #1a1a1f; }   /* square inner carousel */
.pz-card-body { padding: 1.1rem 1.2rem 1.3rem; }
.pz-card-eyebrow {
  font-family: var(--ui); font-size: .64rem; font-weight: 600;
  letter-spacing: .14em; text-transform: uppercase; color: var(--gold);
  margin-bottom: .45rem;
}
.pz-card-title { font-family: var(--display); font-size: 1.3rem; line-height: 1.1; letter-spacing: -.005em; }
.pz-card-desc { color: var(--muted); margin: .55rem 0 0; font-size: var(--t-body); }
/* On these cards the inner arrows reveal on hover/focus only; touch uses the row swipe. */
@media (hover: none) { .pz-card-media.cat-carousel .catc-arrow { opacity: 0; } }

/* The whole card opens a zoomable detail view (the same lightbox as the gallery).
   The open trigger covers the photo but sits BELOW the carousel arrows / counter in
   z-index (3), so those still work; the body is clickable too (wired in JS). The
   magnifier hint mirrors the gallery cards. */
.pz-card-open {
  position: absolute; inset: 0; z-index: 2;
  appearance: none; -webkit-appearance: none;
  margin: 0; padding: 0; border: 0; background: transparent; cursor: pointer;
}
.pz-card-open:focus-visible { outline: 2px solid var(--gold); outline-offset: -2px; }
.pz-card-body { cursor: pointer; }
.pz-card-zoom {
  position: absolute; top: .7rem; right: .7rem; z-index: 2; pointer-events: none;
  width: 34px; height: 34px; border-radius: 50%;
  display: grid; place-items: center;
  background: color-mix(in srgb, var(--bg) 70%, transparent);   /* flat, not blurred — see .catc-arrow */
  border: 1px solid var(--line); color: var(--text);
  opacity: 0; transform: translateY(-4px); transition: opacity .3s, transform .3s;
}
.pz-card:hover .pz-card-zoom,
.pz-card-open:focus-visible ~ .pz-card-zoom { opacity: 1; transform: none; }
.pz-card-zoom svg { width: 16px; height: 16px; }
@media (hover: none) { .pz-card-zoom { opacity: .85; transform: none; } }

/* Row scroll controls — added by JS only when the row overflows; hidden on touch
   (swipe instead). They reuse the round control look of the site's carousels. */
.pz-strip-nav { display: none; gap: .5rem; justify-content: flex-end; margin-top: 1.1rem; }
.pz-strip.is-scrollable .pz-strip-nav { display: flex; }
.pz-strip-btn {
  width: 46px; height: 46px; border-radius: 50%;
  border: 1px solid var(--line-gold); color: var(--text);
  display: grid; place-items: center; background: transparent;
  transition: background .25s, color .25s, border-color .25s, opacity .25s;
}
.pz-strip-btn:hover { background: var(--gold-soft); color: var(--gold-2); border-color: var(--gold); }
.pz-strip-btn svg { width: 18px; height: 18px; }
.pz-strip-btn[disabled] { opacity: .3; cursor: default; }
.pz-strip-btn[disabled]:hover { background: transparent; color: var(--text); border-color: var(--line-gold); }
@media (hover: none) { .pz-strip-nav { display: none !important; } }

/* ---------- Story link (text + arrow, secondary CTA) ---------- */
.story-link {
  display: inline-flex; align-items: center; gap: .5rem;
  margin-top: 1.5rem;
  font-family: var(--ui); font-weight: 700;
  font-size: .82rem; letter-spacing: .1em; text-transform: uppercase;
  color: var(--gold-2);
  padding-bottom: 4px;
  border-bottom: 1px solid var(--line-gold);
  transition: gap .25s, color .25s, border-color .25s;
}
.story-link:hover { gap: .9rem; color: var(--gold); border-color: var(--gold); }
.story-link svg { width: 14px; height: 14px; flex: 0 0 auto; }

/* ---------- CTA ---------- */
.cta-box {
  position: relative; overflow: hidden;
  border-radius: var(--radius-lg);
  border: 1px solid var(--line-gold);
  background:
    radial-gradient(circle at 82% 10%, rgba(99, 19, 27, .4), transparent 60%),
    linear-gradient(180deg, var(--panel), var(--bg-2));
  padding: clamp(2.25rem, 1.8rem + 4vw, 4.5rem) var(--gutter);
  text-align: center;
}
.cta-box .kicker { justify-content: center; margin-bottom: 1.25rem; }
.cta-box h2 { font-family: var(--display); font-size: var(--t-h2); max-width: 18ch; margin: 0 auto 1rem; }
.cta-box p { color: var(--muted); max-width: 44ch; margin: 0 auto 2rem; }
.cta-actions { display: flex; flex-wrap: wrap; gap: .85rem; justify-content: center; }

/* ---------- Footer ---------- */
.site-footer {
  border-top: 1px solid var(--line);
  padding-block: clamp(2rem, 1.5rem + 1.5vw, 3rem);
  /* No margin-top: the gap before the footer is the page's own bottom padding
     (--page-end / the per-page footer knob), so one value owns the whole gap. */
}
/* Three independent areas. On desktop each one is its own grid column
   (1fr / auto / 1fr), so the centre stays centred on the PAGE no matter how
   wide the left and right content is. The previous flex "space-between"
   positioned the middle cluster from the leftover space, which is why editing
   the footer text visibly shifted the icons out of line. */
.foot-inner {
  display: grid;
  grid-template-columns: 1fr;
  justify-items: center; align-items: center;
  gap: 1.1rem; text-align: center;
}
.foot-area { display: flex; align-items: center; gap: .7rem; min-width: 0; }
.foot-area:empty { display: none; }
.foot-brand {
  display: inline-flex; align-items: center; gap: .65rem;
}
.foot-brand img {
  height: 40px; width: auto;
  filter: drop-shadow(0 4px 10px rgba(0,0,0,.4));
}
.foot-name {
  font-family: var(--wordmark); font-weight: 700;
  font-size: clamp(1.1rem, .95rem + .5vw, 1.4rem);
  letter-spacing: -.005em; line-height: 1;
  color: var(--text);
}
/* Footer contact icons (Instagram + Email) — one coherent cluster, identical
   on every page; each icon renders only when its target is set. */
.foot-links { display: flex; align-items: center; gap: .7rem; }
.foot-ico {
  display: inline-grid; place-items: center;
  width: 42px; height: 42px; border-radius: 50%;
  border: 1px solid var(--line); color: var(--muted);
  transition: color .2s, border-color .2s, background .2s;
}
.foot-ico:hover { color: var(--gold); border-color: var(--gold); background: var(--gold-soft); }
.foot-ico svg { width: 18px; height: 18px; }
.foot-meta {
  font-family: var(--ui); font-size: .68rem; font-weight: 500;
  letter-spacing: .14em; text-transform: uppercase; color: var(--soft);
}
.foot-divider {
  width: 56px; height: 1px;
  background: linear-gradient(90deg, transparent, var(--line-gold), transparent);
}

@media (min-width: 720px) {
  .foot-inner {
    grid-template-columns: 1fr auto 1fr;
    justify-items: stretch;
    text-align: left; gap: 1.5rem;
  }
  .foot-divider { display: none; }
  /* An empty column must still take its share of the row, otherwise removing
     the content of one area would let the others slide across. */
  .foot-area:empty { display: block; }
  .foot-area--left   { justify-content: flex-start; }
  .foot-area--center { justify-content: center; }
  .foot-area--right  { justify-content: flex-end; text-align: right; }
}

/* Legal line under the three areas: privacy / cookie notices, always reachable. */
.foot-legal {
  margin-top: 1.4rem; padding-top: 1.1rem;
  border-top: 1px solid var(--line);
}
.foot-legal-nav {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: .5rem 1.4rem;
  font-family: var(--ui); font-size: .68rem; font-weight: 500;
  letter-spacing: .12em; text-transform: uppercase;
}
.foot-legal-nav a { color: var(--soft); transition: color .2s; }
.foot-legal-nav a:hover, .foot-legal-nav a:focus-visible { color: var(--gold); }

/* ---------- Legal pages (privacy notice / cookie policy) ---------- */
.legal-hero { padding-top: clamp(2.5rem, 2rem + 3vw, 4.5rem); padding-bottom: clamp(1rem, .8rem + 1.5vw, 1.8rem); }
.legal-hero .crumb {
  font-family: var(--ui); font-size: .76rem; letter-spacing: .12em;
  text-transform: uppercase; color: var(--soft); margin-bottom: 1rem;
}
.legal-hero h1 { font-size: var(--t-h1); margin: 0 0 .8rem; }
.legal-intro { color: var(--muted); max-width: 62ch; font-size: var(--t-body); }
.legal-updated {
  margin-top: .9rem; font-family: var(--ui); font-size: .7rem;
  letter-spacing: .1em; text-transform: uppercase; color: var(--soft);
}
.legal-body { padding-bottom: clamp(2rem, 1.5rem + 2vw, 3.5rem); max-width: 78ch; }
.legal-block + .legal-block { margin-top: clamp(1.8rem, 1.4rem + 1.2vw, 2.6rem); }
.legal-block h2 { font-size: clamp(1.25rem, 1.1rem + .6vw, 1.6rem); margin: 0 0 .7rem; }
.legal-block p { color: var(--muted); font-size: var(--t-body); margin: 0 0 .9rem; }
.legal-block p:last-child { margin-bottom: 0; }
.legal-block strong { color: var(--text); font-weight: 600; }
.legal-more { display: flex; flex-wrap: wrap; gap: .8rem; margin-top: clamp(2rem, 1.6rem + 1.4vw, 3rem); }

/* ============================================================
   GALLERIA
   ============================================================ */
.g-hero { padding-top: clamp(2.5rem, 2rem + 3vw, 4.5rem); padding-bottom: clamp(1.5rem, 1rem + 2vw, 2.5rem); }
.g-hero .crumb { font-family: var(--ui); font-size: .76rem; letter-spacing: .12em; text-transform: uppercase; color: var(--soft); margin-bottom: 1rem; }
.g-hero h1 { font-family: var(--display); font-size: var(--t-h1); margin: 0 0 1rem; }
.g-hero h1 em { font-style: italic; color: var(--gold-2); }
.g-hero p { color: var(--muted); max-width: 50ch; font-size: var(--t-lead); }

.g-filters {
  position: sticky; top: 80px; z-index: 40;
  background: color-mix(in srgb, var(--bg) 92%, transparent);
  backdrop-filter: saturate(160%) blur(14px);
  -webkit-backdrop-filter: saturate(160%) blur(14px);
  border-bottom: 1px solid var(--line);
  padding-block: .9rem;
}
.g-filter-bar { display: flex; align-items: center; gap: .5rem; }
.g-filter-scroll {
  flex: 1 1 auto; min-width: 0;
  display: flex; gap: .5rem;
  overflow-x: auto; scrollbar-width: none;
  scroll-behavior: smooth;          /* smooth for arrow / chip-into-view scrolling */
}
.g-filter-scroll::-webkit-scrollbar { display: none; }
/* When the chips overflow, the row is draggable on desktop (grab cursor). */
.g-filter-scroll.is-scrollable { cursor: grab; }
.g-filter-scroll.is-dragging {
  cursor: grabbing; scroll-behavior: auto;   /* 1:1 while dragging */
  user-select: none; -webkit-user-select: none;
}
.g-filter-scroll.is-dragging .chip { pointer-events: none; }
/* Prev / next arrows: shown only on fine-pointer (desktop) when overflowing.
   They reuse the round control look of the carousels. */
.g-filter-arrow {
  flex: 0 0 auto;
  display: grid; place-items: center;
  width: 40px; height: 40px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--panel); color: var(--text);
  cursor: pointer; transition: border-color .2s, color .2s, opacity .2s;
}
.g-filter-arrow svg { width: 18px; height: 18px; }
.g-filter-arrow:hover:not(:disabled) { border-color: var(--line-gold); color: var(--gold-2); }
.g-filter-arrow:disabled { opacity: .3; cursor: default; }
.g-filter-arrow[hidden] { display: none; }
@media (hover: none), (pointer: coarse) {
  .g-filter-arrow { display: none !important; }   /* touch: native swipe instead */
}
.chip {
  flex: 0 0 auto;
  font-family: var(--ui); font-size: .8rem; font-weight: 500;
  padding: .55rem 1rem; min-height: 40px;
  border-radius: 999px; border: 1px solid var(--line);
  color: var(--text); background: transparent;
  white-space: nowrap;
  transition: all .2s;
}
.chip:hover { border-color: var(--line-gold); color: var(--gold-2); }
.chip[aria-pressed="true"] { background: var(--text); color: var(--bg); border-color: var(--text); font-weight: 600; }

.g-grid {
  display: grid; gap: 1rem;
  grid-template-columns: repeat(2, 1fr);
  /* Space above the grid (below the filter bar) — editable per page; the
     space AFTER the grid is owned by the Contatti section's padding-top. */
  padding-top: var(--spg-grid, clamp(1.5rem, 1rem + 2vw, 2.5rem));
  padding-bottom: 0;
}
@media (min-width: 720px) { .g-grid { grid-template-columns: repeat(3, 1fr); gap: 1.25rem; } }
@media (min-width: 1080px) { .g-grid { grid-template-columns: repeat(4, 1fr); } }

.g-empty {
  grid-column: 1 / -1;
  text-align: center; padding: 3rem 1rem;
  color: var(--muted);
}
.g-empty .kicker { justify-content: center; margin-bottom: 1rem; }
.g-empty p { font-family: var(--display); font-size: 1.5rem; color: var(--text); }

/* ---------- Reveal animations ---------- */
.reveal { opacity: 0; transform: translateY(26px); transition: opacity .9s cubic-bezier(.2,.7,.2,1), transform .9s cubic-bezier(.2,.7,.2,1); }
.reveal.is-in { opacity: 1; transform: none; }

.hero-copy > * { opacity: 0; animation: rise .9s cubic-bezier(.2,.7,.2,1) forwards; }
.hero-copy > *:nth-child(1) { animation-delay: .1s; }
.hero-copy > *:nth-child(2) { animation-delay: .22s; }
.hero-copy > *:nth-child(3) { animation-delay: .34s; }
.hero-copy > *:nth-child(4) { animation-delay: .46s; }
.hero-carousel { opacity: 0; animation: fade 1.3s .25s ease forwards; }
@keyframes rise { from { opacity: 0; transform: translateY(22px); } to { opacity: 1; transform: none; } }
@keyframes fade { from { opacity: 0; } to { opacity: 1; } }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; }
  .reveal { opacity: 1; transform: none; }
  .hero-copy > *, .hero-carousel { opacity: 1; animation: none; }
  .hero-slide.is-active img { animation: none; transform: none; }
  html { scroll-behavior: auto; }
}

@media print {
  .site-header, .site-footer, .g-filters { display: none; }
  body { background: #fff; color: #000; }
}

/* ============================================================
   ADDED — related block carousels, gallery cards (button),
   lightbox / detail panel
   ============================================================ */

/* ---------- Related-image carousel inside category blocks ---------- */
.cat-carousel { position: relative; }
.catc-stage { position: absolute; inset: 0; }
.catc-item {
  position: absolute; inset: 0;
  opacity: 0; visibility: hidden;
  transition: opacity .7s ease, visibility .7s;
}
.catc-item.is-active { opacity: 1; visibility: visible; }
.catc-item img { width: 100%; height: 100%; object-fit: cover; }
.catc-arrow {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: 44px; height: 44px; border-radius: 50%;
  display: grid; place-items: center; z-index: 3;
  /* No backdrop-filter here on purpose. These arrows become visible exactly when
     the carousel is hovered — which is exactly when the photo underneath is
     scaling — and a backdrop blur must re-sample and re-blur its backdrop on
     every single frame of that scale. With six category blocks on the home page
     that was the single most expensive thing on screen during a zoom. A slightly
     more opaque flat background reads the same over a photo. */
  background: color-mix(in srgb, var(--bg) 72%, transparent);
  border: 1px solid var(--line-gold); color: var(--text);
  opacity: 0; transition: opacity .3s, background .25s, color .25s, border-color .25s;
}
.cat-carousel:hover .catc-arrow,
.cat-carousel:focus-within .catc-arrow { opacity: 1; }
.catc-arrow.prev { left: .8rem; }
.catc-arrow.next { right: .8rem; }
.catc-arrow:hover { background: var(--gold-soft); color: var(--gold-2); border-color: var(--gold); }
.catc-arrow svg { width: 18px; height: 18px; }
.catc-count {
  position: absolute; right: .9rem; bottom: .9rem; z-index: 3;
  font-family: var(--ui); font-size: .7rem; letter-spacing: .08em; color: var(--text);
  background: color-mix(in srgb, var(--bg) 72%, transparent);   /* flat, not blurred — see .catc-arrow */
  padding: .25rem .6rem; border-radius: 999px; border: 1px solid var(--line);
}
.catc-count .cur { color: var(--gold-2); }
@media (hover: none) { .cat-carousel .catc-arrow { opacity: .92; } }

/* ---------- Gallery cards rendered as buttons ---------- */
.card { width: 100%; text-align: left; appearance: none; -webkit-appearance: none; font: inherit; }
a.card, button.card { cursor: pointer; }
button.card { padding: 0; }
.card .card-photo { position: relative; }
.card-zoom {
  position: absolute; top: .7rem; right: .7rem; z-index: 2;
  width: 34px; height: 34px; border-radius: 50%;
  display: grid; place-items: center;
  background: color-mix(in srgb, var(--bg) 70%, transparent);   /* flat, not blurred — see .catc-arrow */
  border: 1px solid var(--line); color: var(--text);
  opacity: 0; transform: translateY(-4px); transition: opacity .3s, transform .3s;
}
.card:hover .card-zoom, .card:focus-visible .card-zoom { opacity: 1; transform: none; }
.card-zoom svg { width: 16px; height: 16px; }
@media (hover: none) { .card-zoom { opacity: .85; transform: none; } }

/* ---------- Lightbox / detail panel ----------
   The figure (photo + caption) is a full-size column flex centred in the padded
   viewport; the photo itself is sized by JAVASCRIPT (script.js → fitPhoto): on
   every load / resize / rotation it measures the room left by the caption rows
   and sets an exact pixel width+height that keeps the photo's own ratio. This
   replaces the old percentage-max-height flex chain, which WebKit (iOS Safari,
   and every iOS browser) resolves inconsistently — the <img> box got stretched
   wider than the contained photo, so object-fit letterboxing exposed the img's
   own dark background as "bars" beside the photo, and rotating the device kept
   the stale size. With an exact-ratio box the 1px border always hugs the
   visible photo, nothing is ever cropped, and rotation re-fits instantly. The
   max-width/height:auto below are only the pre-fit fallback (the image is
   invisible until .is-ready anyway). */
.lightbox {
  position: fixed; inset: 0; z-index: 200;
  display: flex; align-items: center; justify-content: center;
  padding: clamp(1rem, 3vh, 2.5rem) clamp(3.5rem, 9vw, 5.5rem);
  background: rgba(6, 6, 8, .9);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  opacity: 0; transition: opacity .35s ease;
  overflow: hidden;
}
.lightbox.is-open { opacity: 1; }
.lightbox[hidden] { display: none; }

.lb-figure {
  margin: 0; min-height: 0; min-width: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: clamp(.5rem, 1.6vh, 1rem);
  width: 100%; height: 100%;        /* definite box = reliable JS measurements */
  max-width: min(960px, 100%);
  transform: scale(.98) rotate(0.02deg); transition: transform .35s cubic-bezier(.2,.7,.2,1);
}
.lightbox.is-open .lb-figure { transform: scale(1) rotate(0.02deg); }

.lb-imgwrap {
  display: flex; align-items: center; justify-content: center;
  max-width: 100%; min-height: 0;
  flex: 0 1 auto;            /* shrink-wraps the JS-sized photo */
  transform-origin: center center;
  touch-action: none;        /* custom pinch / pan / swipe — disables native gestures here only */
  will-change: transform;
}
.lb-imgwrap.is-zoomed { cursor: grab; }
.lb-img {
  display: block;
  max-width: 100%; width: auto; height: auto;   /* pre-fit fallback only; JS sets exact px */
  object-fit: contain;
  border-radius: var(--radius);
  border: 1px solid var(--line);
  background: #15151a;
  opacity: 0; transition: opacity .4s ease;
}
.lb-img.is-ready { opacity: 1; }

/* Caption rows keep their natural height; only the photo gives up space.
   They fade out while the photo is zoomed (see the .is-zoomed rule below). */
.lb-cap, .lb-count, .lb-thumbs { flex: 0 0 auto; transition: opacity .2s ease, visibility .2s ease; }

/* While the photo is pinch / double-tap zoomed, hide the counter, thumbnail strip
   and caption. They sit AFTER .lb-imgwrap in the DOM, so with no z-index they'd
   paint on top of the enlarged image and show through it (the faint "preview
   cards" over a zoomed photo). visibility:hidden also stops them catching touches
   meant for panning. Everything returns on zoom-out. */
.lb-imgwrap.is-zoomed ~ .lb-count,
.lb-imgwrap.is-zoomed ~ .lb-thumbs,
.lb-imgwrap.is-zoomed ~ .lb-cap { opacity: 0; visibility: hidden; }
.lb-cap { display: flex; flex-direction: column; gap: .3rem; text-align: center; }
.lb-cat { font-family: var(--ui); font-size: .68rem; font-weight: 600; letter-spacing: .16em; text-transform: uppercase; color: var(--gold); }
.lb-title { font-family: var(--display); font-size: 1.6rem; color: var(--text); }
.lb-close, .lb-nav {
  position: fixed; z-index: 210;
  width: 52px; height: 52px; border-radius: 50%;
  display: grid; place-items: center;
  background: color-mix(in srgb, var(--bg) 50%, transparent);
  border: 1px solid var(--line-gold); color: var(--text);
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  transition: background .25s, color .25s, border-color .25s;
}
.lb-close { top: clamp(1rem, 3vw, 1.6rem); right: clamp(1rem, 3vw, 1.6rem); }
.lb-nav { top: 50%; transform: translateY(-50%); }
.lb-nav.prev { left: clamp(.6rem, 2vw, 1.6rem); right: auto; }
.lb-nav.next { right: clamp(.6rem, 2vw, 1.6rem); left: auto; }
.lb-close:hover, .lb-nav:hover { background: var(--gold-soft); color: var(--gold-2); border-color: var(--gold); }
.lb-close svg, .lb-nav svg { width: 20px; height: 20px; }

/* Grab cursor while dragging a zoomed photo with the mouse. */
.lb-imgwrap.is-grabbing { cursor: grabbing; }

/* On-screen zoom controls. Bottom-left keeps them clear of the close button
   (top-right), the side arrows (vertical centre) and the centred caption. They sit
   at the lightbox root, so they stay visible while the photo is zoomed (the
   caption-hiding rule only targets .lb-figure children). Hidden by default and
   revealed only where a real pointer + hover exist, so touch devices keep the
   pinch / double-tap gestures and gain no extra chrome. */
.lb-zoom {
  position: fixed; z-index: 210;
  left: clamp(.6rem, 2.5vw, 1.6rem);
  bottom: clamp(.6rem, 2.5vw, 1.6rem);
  display: none; align-items: center; gap: .1rem;
  padding: .25rem;
  background: color-mix(in srgb, var(--bg) 55%, transparent);
  border: 1px solid var(--line-gold); border-radius: 999px;
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
}
.lb-zoom-btn {
  width: 38px; height: 38px; padding: 0; border: 0; border-radius: 50%;
  display: grid; place-items: center;
  background: transparent; color: var(--text); cursor: pointer;
  transition: background .2s, color .2s;
}
.lb-zoom-btn svg { width: 18px; height: 18px; }
.lb-zoom-btn:hover:not(:disabled) { background: var(--gold-soft); color: var(--gold-2); }
.lb-zoom-btn:disabled { opacity: .35; cursor: default; }
.lb-zoom-btn:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.lb-zoom-level {
  min-width: 3.6ch; padding: 0 .25rem; border: 0; background: transparent;
  font-family: var(--ui); font-size: .72rem; font-weight: 600; letter-spacing: .04em;
  color: var(--muted); cursor: pointer; text-align: center;
  transition: color .2s;
}
.lb-zoom-level:hover { color: var(--gold-2); }
.lb-zoom-level:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; border-radius: 5px; }

@media (hover: hover) and (pointer: fine) {
  .lb-zoom { display: flex; }
}
@media (prefers-reduced-motion: reduce) {
  .lb-zoom-btn, .lb-zoom-level { transition: none; }
}

/* Narrow PORTRAIT phones: arrows as a centred bottom row (gives the photo the
   most vertical room). Reserve bottom padding so they never sit on the photo.
   Gated to portrait, so a rotated phone keeps the side arrows below. */
@media (max-width: 640px) and (orientation: portrait) {
  .lightbox { padding: clamp(1rem, 3vh, 1.5rem) clamp(1rem, 4vw, 1.5rem) 5.25rem; }
  .lb-nav { width: 46px; height: 46px; top: auto; bottom: clamp(1rem, 4vh, 1.8rem); transform: none; }
  .lb-nav.prev { left: calc(50% - 3.7rem); right: auto; }
  .lb-nav.next { right: calc(50% - 3.7rem); left: auto; }
  .lb-title { font-size: 1.3rem; }
}

/* Short viewports (phones in LANDSCAPE, small windows): compact chrome so the
   photo + caption have the most room. The figure's flex-fit keeps it on-screen;
   this only trims the surrounding chrome. */
@media (max-height: 600px) {
  .lightbox { padding-block: clamp(.6rem, 2.5vh, 1rem); padding-inline: clamp(3.25rem, 8vw, 5rem); }
  .lb-figure { gap: clamp(.35rem, 1.2vh, .6rem); }
  .lb-cat { font-size: .6rem; }
  .lb-title { font-size: 1.05rem; }
  .lb-close, .lb-nav { width: 42px; height: 42px; }
  .lb-close { top: clamp(.6rem, 2.5vh, 1rem); right: clamp(.6rem, 2.5vh, 1rem); }
  .lb-nav { top: 50%; bottom: auto; transform: translateY(-50%); }
  .lb-nav.prev { left: clamp(.5rem, 2vw, 1rem); right: auto; }
  .lb-nav.next { right: clamp(.5rem, 2vw, 1rem); left: auto; }
}

@media (prefers-reduced-motion: reduce) {
  .lightbox, .lb-figure, .lb-img, .catc-item, .hero-slide,
  .lb-cap, .lb-count, .lb-thumbs { transition: none; }
}

/* ============================================================
   CHI SONO — separate page (chi-sono.html)
   Alternating chronological timeline + page hero + bio card
   ============================================================ */
.cs-hero {
  padding-top: clamp(3rem, 2.2rem + 3.5vw, 5.5rem);
  /* No bottom padding: the block below (bio) owns the gap via .cs-bio-sec. */
  padding-bottom: 0;
}
.cs-bio-sec { padding-top: var(--spc-bio, clamp(2rem, 1.5rem + 2vw, 3.5rem)); }
.cs-hero .crumb { font-family: var(--ui); font-size: .76rem; letter-spacing: .12em; text-transform: uppercase; color: var(--soft); margin-bottom: 1rem; }
.cs-hero h1 { font-family: var(--display); font-size: var(--t-h1); margin: 0 0 1rem; }
.cs-hero h1 em { font-style: italic; color: var(--gold-2); }
.cs-hero .cs-intro { color: var(--muted); max-width: 56ch; font-size: var(--t-lead); }

/* Two-column hero: copy on the left, portrait of Andrea on the right.
   Stacks on mobile (copy first, portrait directly under the intro). */
.cs-hero-grid { display: grid; gap: clamp(1.75rem, 1rem + 3vw, 2.75rem); align-items: center; }
.cs-hero-fig {
  border-radius: var(--radius-lg); overflow: hidden;
  border: 1px solid var(--line);
  aspect-ratio: 4 / 5;
  background: var(--panel);
  box-shadow: var(--shadow-sm);
}
.cs-hero-fig img { width: 100%; height: 100%; object-fit: cover; }
@media (min-width: 880px) {
  .cs-hero-grid { grid-template-columns: 1.3fr .7fr; gap: clamp(2.5rem, 4vw, 4.5rem); align-items: center; }
  /* width:100% gives the figure a definite width inside its grid column even though
     its <img> is absolutely positioned (see the cover-image pinning rule below);
     without it, margin-left:auto disables grid stretch and aspect-ratio has no width
     to resolve against, so the portrait collapsed to ~0 on desktop. */
  .cs-hero-fig { width: 100%; max-width: 420px; margin-left: auto; }
}

/* (The old .lab-strip filmstrip for the chi-sono lab photos was removed — the
   lab photos now use the .cat-carousel image+text carousel; see ".cs-bio" /
   ".cs-bio-fig" below.) */

.cs-timeline { padding-top: var(--spc-timeline, clamp(1rem, .5rem + 1.5vw, 2.5rem)); padding-bottom: 0; position: relative; }

/* Wrapper around just the step articles — the rail is anchored here so it
   spans only the steps and never crosses the intro header above them. */
.cs-steps { position: relative; }

/* Vertical timeline rail (desktop) */
@media (min-width: 880px) {
  .cs-steps::before {
    content: ""; position: absolute;
    top: 0; bottom: 0; left: 50%;
    width: 1px;
    background: linear-gradient(180deg, transparent, var(--line-gold) 8%, var(--line-gold) 92%, transparent);
    transform: translateX(-.5px);
    pointer-events: none;
  }
}

.cs-step {
  display: grid; gap: 1.25rem;
  padding-block: clamp(1.5rem, 1rem + 2vw, 3rem);
  position: relative;
  align-items: center;
}

/* Mobile: full-width single column */
.cs-step .cs-fig {
  border-radius: var(--radius-lg); overflow: hidden;
  border: 1px solid var(--line);
  aspect-ratio: 5 / 4;
  background: var(--panel);
}
.cs-step .cs-fig img { width: 100%; height: 100%; object-fit: cover; transform: rotate(0.02deg); transition: transform 1s cubic-bezier(.2,.7,.2,1); }
/* Only the visible carousel frame zooms — see the note on .cat-figure above. */
.cs-step:hover .cs-fig > img,
.cs-step:hover .cs-fig .catc-item.is-active img { transform: scale(1.04) rotate(0.02deg); }

/* Card content uses MontserratAlt1 Bold for the year/title */
.cs-card {
  background: linear-gradient(180deg, var(--panel), var(--bg-2));
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.5rem 1.5rem 1.6rem;
  transition: border-color .35s;
}
.cs-step:hover .cs-card { border-color: var(--line-gold); }
.cs-card .cs-year {
  font-family: var(--wordmark); font-weight: 700;
  font-size: clamp(1.4rem, 1.1rem + 1.2vw, 2rem);
  letter-spacing: -.01em;
  color: var(--gold-2);
  margin-bottom: .35rem;
}
.cs-card .cs-step-title {
  font-family: var(--wordmark); font-weight: 700;
  font-size: clamp(1.1rem, .95rem + .55vw, 1.35rem);
  letter-spacing: .005em;
  color: var(--text);
  margin-bottom: .9rem;
  line-height: 1.25;
}
.cs-card p { color: var(--muted); margin: 0 0 .8rem; font-size: var(--t-body); }
.cs-card p:last-child { margin-bottom: 0; }

/* Desktop alternating layout */
@media (min-width: 880px) {
  .cs-step {
    grid-template-columns: 1fr 1fr;
    gap: clamp(2.5rem, 4vw, 5.5rem);
  }
  .cs-step.flip .cs-fig { order: 2; }
  .cs-step.flip .cs-card { order: 1; }
  /* Centered timeline dot at the rail */
  .cs-step::after {
    content: ""; position: absolute;
    top: 50%; left: 50%;
    width: 10px; height: 10px; border-radius: 50%;
    background: var(--gold);
    transform: translate(-50%, -50%);
    box-shadow: 0 0 0 4px var(--bg), 0 0 0 5px var(--line-gold);
    z-index: 2;
  }
}

/* Mobile (single column): show each step's photo BELOW its text card
   (period date + description first, image after). The desktop alternating
   layout above is unaffected — this is a max-width query. */
@media (max-width: 879.98px) {
  .cs-step .cs-card { order: 1; }
  .cs-step .cs-fig  { order: 2; }
}

/* "Il laboratorio" — intro text beside the lab-photo carousel (.cat-carousel).
   On desktop it's a two-column grid: carousel LEFT, text RIGHT. On mobile it
   stacks (text, then carousel) — the carousel is the "lab pic" that sits below
   the intro and directly above the timeline. */
.cs-bio {
  margin-bottom: clamp(1.75rem, 1.2rem + 2vw, 3rem);
  max-width: 72ch;
}
.cs-bio .cs-bio-text h2 {
  font-family: var(--wordmark); font-weight: 700;
  font-size: clamp(1.5rem, 1.2rem + 1vw, 2rem);
  color: var(--text);
  margin: .5rem 0 1rem;
  letter-spacing: -.005em;
}
.cs-bio .cs-bio-text p { color: var(--muted); margin: 0 0 1rem; max-width: 64ch; }
.cs-bio .cs-bio-text p:last-child { margin-bottom: 0; }

/* Framed picture box for the lab carousel (like .cat-figure / the hero
   portrait). The .cat-carousel / .catc-* rules (above) drive the fade, arrows
   and the 1/N counter. On mobile it sits full-width below the text. */
.cs-bio-fig {
  position: relative; margin: 1.75rem 0 0;
  border-radius: var(--radius-lg); overflow: hidden;
  border: 1px solid var(--line);
  aspect-ratio: 5 / 4;
  background: var(--panel);
  box-shadow: var(--shadow-sm);
}
.cs-bio-fig img { width: 100%; height: 100%; object-fit: cover; }

@media (min-width: 880px) {
  .cs-bio {
    display: grid;
    grid-template-columns: .82fr 1.18fr;   /* carousel (left) | text (right) */
    gap: clamp(2.5rem, 4vw, 4.5rem);
    align-items: center;                    /* carousel vertically centred beside the text */
    max-width: none;
  }
  .cs-bio-fig  { order: 1; margin: 0; max-width: 460px; }
  .cs-bio-text { order: 2; }
}


/* ============================================================
   PHP-conversion additions
   Former inline style="" rules are migrated to classes here so a
   strict Content-Security-Policy needs no 'unsafe-inline' for styles.
   Plus: a no-JS reveal fallback, a generic hide helper, and the
   lightbox per-piece description line.
   ============================================================ */

/* No-JS fallback: if assets/js/head.js didn't run, reveal content stays visible.
   (Under JS, html.js is set before first paint, so this selector won't match
   and the normal scroll-reveal animation applies — no flash.) */
html:not(.js) .reveal { opacity: 1 !important; transform: none !important; }

/* Generic hide — gallery filtering, empty-state toggle, admin UI. */
.is-hidden { display: none !important; }

/* contatti kicker accent (was: style="color:var(--gold)") */
.kicker--gold { color: var(--gold); }

/* personalizzazioni engraving list — wide auto-fit columns
   (was: inline style on .eng-list) */
.eng-list--cols {
  max-width: none;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  margin-top: 2.5rem;
}

/* chi-sono timeline header centring (was: inline styles on the header) */
.section-head--center { text-align: center; max-width: 56ch; margin-inline: auto; }
.section-head--center .kicker { justify-content: center; }
.section-head--center h2,
.section-head--center .sub { margin-inline: auto; }

/* Lightbox description line (per-piece notes; collapses when empty).
   pre-line keeps the line breaks typed in the admin panel (the text is inserted
   with textContent, so this is the only thing needed to honour them) while still
   wrapping long lines and collapsing incidental runs of spaces. */
.lb-desc {
  font-family: var(--body);
  font-size: .9rem;
  line-height: 1.55;
  color: var(--muted);
  max-width: 52ch;
  margin-inline: auto;
  max-height: 28vh;
  overflow-y: auto;
  white-space: pre-line;
}
.lb-desc:empty { display: none; }
@media (max-height: 720px) { .lb-desc { font-size: .82rem; max-height: 22vh; } }

/* Desktop / large screens: the description keeps a capped height and scrolls when
   it overflows, so the photo stays the same comfortable size for every product —
   short caption or long — as you arrow between pieces. The matching photo height
   budget (60vh, 52vh on short windows) now lives in script.js → fitPhoto, which
   sizes the photo in exact pixels; keep these numbers in sync if you change them. */
@media (min-width: 880px) {
  .lb-desc { max-height: 18vh; }
}
@media (min-width: 880px) and (max-height: 760px) {
  .lb-desc { max-height: 14vh; }
}

/* Honour line breaks typed in the admin panel for every editable rich-text
   field, the same way .lb-desc does for product captions. These elements print
   their value via PHP e()/alb_rich() or JS textContent, none of which convert
   newlines, so pre-line is all that's needed: blank lines and intentional
   breaks show, while long lines still wrap and incidental runs of spaces
   collapse. Only textarea-backed fields can contain newlines; single-line
   inputs caught by these selectors (e.g. .dietro-label, .crumb) are unaffected
   because they never carry a line break. Update this list if a new multi-line
   field is added to the panel. */
.hero-lead,
.sub,
.cat-text p,
.pz-card-desc,
.story-band-inner > p,
.cs-intro,
.cs-bio-text p,
.cs-card p,
.cta-box > p,
.contact-privacy,
.contact-note,
.g-hero p {
  white-space: pre-line;
}

/* ============================================================
   Lightbox: per-piece photo strip + counter (multi-photo products)
   Arrows/swipe browse BETWEEN pieces; this strip switches the photos
   of the piece currently open. Both are hidden for single-photo pieces.
   ============================================================ */
.lb-count {
  font-family: var(--ui); font-size: .7rem; font-weight: 600;
  letter-spacing: .14em; color: var(--muted);
}
.lb-count .cur { color: var(--gold-2); }

.lb-thumbs {
  display: flex; gap: .45rem;
  max-width: 100%; padding: .15rem .1rem;
  overflow-x: auto; overflow-y: hidden;
  scrollbar-width: thin; scrollbar-color: var(--line-gold) transparent;
  justify-content: center;
}
.lb-thumbs::-webkit-scrollbar { height: 6px; }
.lb-thumbs::-webkit-scrollbar-thumb { background: var(--line-gold); border-radius: 3px; }
.lb-thumb {
  flex: 0 0 auto; width: 54px; height: 54px; padding: 0;
  border: 1px solid var(--line); border-radius: 9px; overflow: hidden;
  background: #15151a; cursor: pointer; opacity: .5;
  transition: opacity .2s, border-color .2s, transform .2s;
}
.lb-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.lb-thumb:hover { opacity: .85; }
.lb-thumb.is-active { opacity: 1; border-color: var(--gold); }
.lb-thumb:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; opacity: 1; }

/* The thumbnail strip is just another caption row in the flex figure, so the
   photo already gives up the space it needs — no separate height budget. */

@media (max-width: 640px) and (orientation: portrait) {
  .lb-thumb { width: 46px; height: 46px; }
}
@media (max-height: 600px) {
  .lb-thumb { width: 40px; height: 40px; }
  .lb-count { font-size: .62rem; }
}
@media (prefers-reduced-motion: reduce) {
  .lb-thumb { transition: none; }
}

/* ============================================================
   Sub-pixel hairline fix (cover images)
   On fractional device-pixel ratios (1.25/1.5/1.75) a cover image clipped by
   overflow:hidden against a darker container can show a faint 1px horizontal
   "bar" at the bottom edge: the clip boundary lands mid-device-pixel and that
   row blends photo + background. Most visible on bright photos. Fix: the image
   overhangs by 1px (so it always reaches the box edges) AND the immediate photo
   box does not clip its own bottom — the rounded frame is clipped by the parent
   (.card / .cat-block frame already round + clip), so corners stay rounded while
   the bottom row is solid photo with nothing to blend against.
   ============================================================ */
.cs-hero-fig, .cs-fig { position: relative; }
/* Descendant selectors + !important so this wins no matter what other rule (or a
   hand-merged older rule, or the global img{height:auto;max-width:100%} reset)
   tries to do: the image is pinned to the box, 1px overscan on every side, and
   cover-cropped. This guarantees the photo reaches the TOP and bottom edges with
   no container background showing (no dark band, no hairline). */
.card-photo img,
.cat-figure img,
.cs-hero-fig img,
.cs-fig img,
.cs-bio-fig img,
.catc-item img,
.hero-slide img {
  position: absolute !important;
  top: -1px !important;
  left: -1px !important;
  right: auto !important;
  bottom: auto !important;
  width: calc(100% + 2px) !important;
  height: calc(100% + 2px) !important;
  max-width: none !important;
  object-fit: cover !important;
}
.card-photo { overflow: visible; }

/* ============================================================
   Graceful single-column collapse when a section's image is absent
   When an editorial image was cleared in the panel or its file was removed,
   the renderer drops the <figure> and adds a --noimg modifier so the remaining
   text spans the full width instead of leaving an empty column / framed box.
   Equal specificity to the two-column breakpoint rules above + later in source,
   so these win at every width.
   ============================================================ */
.cat-block--noimg,
.cs-hero-grid--noimg,
.cs-bio--noimg,
.cs-step--noimg { grid-template-columns: minmax(0, 1fr); }
.cat-block--noimg .cat-text { padding-inline: 0; }

/* ============================================================
   Contact form (Contatti area — all pages)
   Inherits the page's design tokens. Fields are left-aligned inside
   the otherwise-centred .cta-box; the submit reuses .btn .btn-primary.
   ============================================================ */
.contact-form {
  text-align: left;
  max-width: 560px;
  margin: 0 auto 1.75rem;
}
.contact-sub {
  color: var(--muted);
  font-size: var(--t-sm);
  margin: 0 0 1.25rem;
  text-align: center;
}
/* Honeypot — kept in the DOM and the tab order out, hidden from people + AT. */
.contact-hp {
  position: absolute;
  left: -9999px;
  width: 1px; height: 1px;
  overflow: hidden;
}
.contact-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem;
}
@media (max-width: 560px) {
  .contact-grid { grid-template-columns: 1fr; }
}
.field {
  display: flex;
  flex-direction: column;
  gap: .45rem;
  margin-bottom: 1rem;
}
.field-label {
  font-family: var(--ui);
  font-size: var(--t-label);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--muted);
}
.field-input {
  font-family: var(--body);
  font-size: var(--t-body);
  color: var(--text);
  background: linear-gradient(180deg, var(--bg-2), var(--panel));
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: .85rem 1rem;
  width: 100%;
  transition: border-color .2s, box-shadow .2s, background .2s;
}
.field-input::placeholder { color: var(--soft); }
.field-input:hover { border-color: var(--line-gold); }
.field-input:focus {
  outline: none;
  border-color: var(--gold);
  box-shadow: 0 0 0 3px var(--gold-soft);
  background: var(--panel);
}
.field-textarea {
  min-height: 140px;
  resize: vertical;
  line-height: 1.55;
}
.field.has-error .field-input {
  border-color: var(--red-bright);
  box-shadow: 0 0 0 3px rgba(215, 11, 24, .14);
}
.contact-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem;
  margin-top: 1.25rem;
}
.contact-submit.is-busy { opacity: .72; cursor: progress; }
.contact-privacy {
  flex: 1 1 14rem;
  min-width: 12rem;
  color: var(--soft);
  font-size: var(--t-label);
  line-height: 1.5;
}
/* Inline (JS) feedback + server-rendered (no-JS) notice share one look. */
.contact-note,
.contact-alert {
  font-family: var(--ui);
  font-size: var(--t-sm);
  border-radius: 14px;
  padding: .8rem 1.05rem;
  border: 1px solid var(--line);
  margin: 1.1rem auto 0;
  max-width: 560px;
}
.contact-alert { margin-bottom: 1.5rem; text-align: left; }
.contact-note.is-ok,
.contact-alert.is-ok {
  color: var(--gold-2);
  border-color: var(--line-gold);
  background: var(--gold-soft);
}
.contact-note.is-err,
.contact-alert.is-err {
  color: #f1b8bc;
  border-color: rgba(215, 11, 24, .4);
  background: rgba(215, 11, 24, .1);
}
/* "oppure" divider before the alternative-contact buttons. */
.contact-alt { margin-top: 1.5rem; }
.contact-alt-sep {
  display: block;
  text-align: center;
  font-family: var(--ui);
  font-size: var(--t-label);
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--soft);
  margin-bottom: 1.1rem;
  position: relative;
}
.contact-alt-sep::before,
.contact-alt-sep::after {
  content: "";
  position: absolute;
  top: 50%;
  width: clamp(2.5rem, 22%, 7rem);
  height: 1px;
  background: var(--line);
}
.contact-alt-sep::before { left: 0; }
.contact-alt-sep::after  { right: 0; }

/* ============================================================
   Language switch (IT / EN).
   All widths: a compact pill-pair in the header's right cluster,
   just left of the Instagram icon.
   Phones can ALSO show a fuller stacked row at the foot of the
   slide-in menu — optional, toggled in Impostazioni (default on).
   ============================================================ */
.lang-switch {
  display: inline-flex; align-items: center; gap: 2px;
  padding: 2px;
  border: 1px solid var(--line); border-radius: 999px;
  font-family: var(--ui); font-weight: 600;
  font-size: .78rem; letter-spacing: .04em;
}
.lang-switch .lang-opt {
  display: inline-grid; place-items: center;
  min-width: 30px; height: 28px; padding: 0 .5rem;
  border-radius: 999px; color: var(--muted);
  transition: color .2s, background .2s;
}
.lang-switch .lang-opt:hover { color: var(--gold-2); }
.lang-switch .lang-opt.is-active { background: var(--text); color: var(--bg); }

/* The compact IT/EN pill shows on EVERY width, sitting just left of the
   Instagram icon. Desktop hides only the fuller in-menu row. */
@media (min-width: 880px) { .mobile-lang { display: none; } }

/* Phones: tighten the pill and the right-cluster gap so the brand, the pill,
   the Instagram icon and the ☰ button all fit comfortably on one row. */
@media (max-width: 879px) {
  .nav-right { gap: .4rem; }
  .nav-right .lang-switch { font-size: .74rem; letter-spacing: .02em; }
  .nav-right .lang-switch .lang-opt { min-width: 26px; height: 28px; padding: 0 .4rem; }
}
/* Narrower phones: shave the pill and the gap a little more. */
@media (max-width: 420px) {
  .nav-right { gap: .3rem; }
  .nav-right .lang-switch { padding: 1px; font-size: .72rem; }
  .nav-right .lang-switch .lang-opt { min-width: 24px; height: 26px; padding: 0 .34rem; }
}

/* In-menu language row (mobile overlay). Higher specificity than the big
   .nav-menu.mobile a rule so these links stay compact. */
.mobile-lang {
  display: flex; flex-direction: column; gap: .55rem;
  margin-top: .4rem; padding-top: 1.2rem;
  border-top: 1px solid var(--line);
}
.nav-menu.mobile .mobile-lang .lang-opt {
  font-family: var(--ui); font-weight: 500;
  font-size: 1rem; letter-spacing: .02em;
  color: var(--muted);
}
.nav-menu.mobile .mobile-lang .lang-opt.is-active { color: var(--gold-2); }

/* ============================================================
   FIREFOX ZOOM FIX — the rotate(0.02deg) that appears on every
   animated transform in this file
   ------------------------------------------------------------
   Symptom: any slow scale — the home hero's 6s Ken Burns above all, but also
   the card / category / timeline hover zooms — was drawn in visible discrete
   steps in Firefox, like someone tapping "zoom out" once per frame. Chromium
   browsers (tested in Brave) were perfectly smooth throughout.

   Cause: two long-standing Firefox defects, not anything wrong with this site.
     • Bugzilla 663776 — a scale animation over a LONG TIME and a SHORT
       DISTANCE is rendered jumpily. The hero is 6 seconds across 8% of scale:
       the exact case.
     • Bugzilla 739176 — no sub-pixel rendering during transform animations, so
       Firefox snaps the element to whole pixels. Sub-pixel motion therefore
       cannot be drawn smoothly; it arrives one whole pixel at a time.

   Fix: add a rotation far too small to see. It forces Firefox onto its general
   matrix path instead of the snapping fast path, and the animation interpolates
   smoothly. 0.02deg moves the corner of a 400px photo by about 0.07px — below
   the threshold of a single device pixel, so nothing looks tilted.

   This was not guessed. Six ways of writing the same animation were built and
   run side by side in the affected Firefox: as-is, plain, this rotation, the
   individual `scale` property, animating the wrapper, and a shorter/wider
   version. ONLY the rotation was smooth.

   What was REMOVED, and why
   -------------------------
   `will-change: transform`, `translateZ(0)` and `backface-visibility: hidden`
   used to sit here on every photo. They were added to cure this same symptom
   and they never did — the side-by-side test above had them in variant 1, which
   stepped exactly as badly as the plain version. Firefox largely ignores
   `will-change` for this purpose. Meanwhile they cost real memory: the gallery
   promoted 37 photos to permanent compositor layers, the home page another 23,
   which is precisely the case MDN warns against. They are gone.

   Consequence: there is no longer a source-order trap here, so the zoom rules
   live with the components they belong to (.card-photo, .cat-figure, .cs-fig,
   .hero-slide) instead of being re-stated at the bottom of the file. Nothing
   needs to stay last any more.

   If Firefox ever fixes this: every occurrence is greppable as
   `rotate(0.02deg)`. Removing them all restores plain transforms.
   ============================================================ */

/* Someone who asks for less motion gets no zoom at all. */
@media (prefers-reduced-motion: reduce) {
  .card-photo img, .cat-figure img, .cs-fig img, .hero-slide img {
    transition: none !important;
    animation: none !important;
    transform: none !important;
  }
}

/* ============================================================
   Card text must stay above the hover zoom
   ------------------------------------------------------------
   .card-photo carries overflow:visible (the sub-pixel hairline fix above),
   so on hover the 1.06-scaled photo paints a few pixels past its own box.
   .card-photo is positioned and .card-body was not, which let that overhang
   fall on top of the category + title. One stacking context fixes it: the
   text now paints above the photo. The card itself still clips everything
   (.card has overflow:hidden), and the hairline fix is untouched.
   ============================================================ */
.card-body { position: relative; z-index: 1; }

/* ============================================================
   Product page  (/galleria/<slug> · /en/gallery/<slug>)
   ------------------------------------------------------------
   Showcase only: no price, no cart. Built from components that already
   exist — .g-hero for the header, the .catc-* carousel for the photos,
   .btn for the actions — so the page reads as part of the site and needs
   no page-specific JavaScript.
   ============================================================ */
.pd-crumb { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; }
.pd-crumb a { color: var(--soft); transition: color .25s; }
.pd-crumb a:hover { color: var(--gold-2); }
.pd-crumb span { color: var(--faint); }

.pd-hero h1 { margin-bottom: .7rem; }
.pd-cats {
  font-family: var(--ui); font-size: .68rem; font-weight: 600;
  letter-spacing: .14em; text-transform: uppercase; color: var(--gold);
  margin: 0;
}
.pd-cats a { transition: color .25s; }
.pd-cats a:hover { color: var(--gold-2); text-decoration: underline; text-underline-offset: .25em; }

.pd-body { padding-bottom: clamp(2rem, 1.5rem + 2vw, 3.5rem); }
.pd-grid { display: grid; gap: clamp(1.5rem, 1rem + 2.5vw, 3rem); align-items: start; }
@media (min-width: 880px) {
  .pd-grid { grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); }
}

/* The photo frame matches a gallery card: same border, radius and clipping,
   so a piece looks the same whether it is in the grid or on its own page. */
.pd-media {
  position: relative; margin: 0;
  aspect-ratio: 1 / 1;
  border: 1px solid var(--line); border-radius: var(--radius);
  overflow: hidden; background: #1a1a1f;
}
.pd-media .catc-stage { position: absolute; inset: 0; }

.pd-copy { display: flex; flex-direction: column; gap: 1.1rem; }
.pd-desc { color: var(--muted); font-size: var(--t-lead); line-height: 1.65; margin: 0; white-space: pre-line; }
.pd-quote {
  margin: 0; padding: 1rem 1.15rem;
  border: 1px solid var(--line-gold); border-radius: var(--radius);
  background: var(--gold-soft); color: var(--text);
  font-size: var(--t-sm); line-height: 1.6;
}
.pd-actions { display: flex; flex-wrap: wrap; gap: .7rem; }

/* ============================================================
   404 page
   ------------------------------------------------------------
   A real page in the site's design with two ways out, instead of the
   server's bare error text. Rendered for an unknown URL, an unknown
   product slug, and a legal notice unpublished in the panel.
   ============================================================ */
.nf-sec {
  padding-top: clamp(3rem, 2.2rem + 4vw, 6rem);
  padding-bottom: clamp(3rem, 2.2rem + 4vw, 6rem);
}
.nf-inner { max-width: 46rem; }
.nf-inner h1 { font-family: var(--display); font-size: var(--t-h2); margin: .4rem 0 1rem; }
.nf-text { color: var(--muted); font-size: var(--t-lead); line-height: 1.65; margin: 0 0 1.8rem; max-width: 44ch; }
.nf-actions { display: flex; flex-wrap: wrap; gap: .7rem; }

/* ---------- Lightbox: the title links to the piece's own page ----------
   Deliberately INLINE, inside the existing .lb-title row — not a row of its own.
   .lb-figure is a fixed-height column and script.js → fitPhoto() gives the photo
   whatever height the caption rows leave over; an extra row cost ~40px plus a
   flex gap, which on a short viewport (phone in landscape) drove the photo onto
   fitPhoto's 40px floor and made the centred column overflow upward, under the
   close button. As an inline element the link adds NO height: vertical padding
   on an inline box does not grow its line box, so the caption keeps exactly the
   number of rows it had before, in every orientation.
   Affordance is carried by the underline + the arrow, and the full label
   ("Vedi la scheda completa" / "View full details") is the link's accessible
   name and its tooltip. */
.lb-title-link {
  color: inherit;
  text-decoration: underline;
  text-decoration-color: var(--line-gold);
  text-decoration-thickness: 1px;
  text-underline-offset: .18em;
  padding: .35em .1em;                 /* hit area only — inline, so no layout change */
  transition: text-decoration-color .25s, color .25s;
}
.lb-title-link::after {
  content: "\2009\2192";              /* thin space + arrow */
  color: var(--gold-2);
  font-size: .85em;
  text-decoration: none;
}
.lb-title-link:hover { color: var(--gold-2); text-decoration-color: var(--gold); }
.lb-title-link:focus-visible { outline: 2px solid var(--gold); outline-offset: 3px; border-radius: 3px; }
@media (hover: none) {
  /* Touch: no hover to reveal it, so make the underline read as tappable at rest. */
  .lb-title-link { text-decoration-color: var(--gold); }
}
@media (prefers-reduced-motion: reduce) { .lb-title-link { transition: none; } }

/* ---------- Phone in LANDSCAPE: give the photo back some height ----------
   A landscape phone is ~390px tall. After the lightbox padding, the counter,
   the thumbnail strip and the caption, the photo was left roughly 175px — the
   "barely visible" case. This trims only the caption, and only in landscape on
   a short viewport; portrait, tablets and desktop are untouched.

   The description is capped by LINE COUNT here rather than by `vh`: 22vh of a
   390px viewport is an arbitrary 86px, whereas ~3 lines is predictable and still
   scrolls for the rest. The full text is one tap away on the linked title, which
   is why trimming it here is reasonable rather than lossy.
   VISIBLE CHANGE — landscape phones only: shorter description box, no category
   line, slightly smaller thumbnails. Delete this block to revert. */
@media (max-height: 520px) and (orientation: landscape) {
  .lb-cat { display: none; }                 /* the category is on the card and on the piece's page */
  .lb-desc { max-height: 4.7em; font-size: .76rem; }
  .lb-cap { gap: .2rem; }
  .lb-thumb { width: 34px; height: 34px; }
  .lb-count { font-size: .6rem; }
}
