/* ==========================================================================
   Andrea Raschi — Design System
   ========================================================================== */

/* Self-hosted fonts (replaces Google Fonts CDN) */
@font-face {
  font-family: 'Space Mono';
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/space-mono-400-italic-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Space Mono';
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/space-mono-400-italic-latin.woff2') format('woff2');
  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: 'Space Mono';
  font-style: italic;
  font-weight: 700;
  font-display: swap;
  src: url('../fonts/space-mono-700-italic-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Space Mono';
  font-style: italic;
  font-weight: 700;
  font-display: swap;
  src: url('../fonts/space-mono-700-italic-latin.woff2') format('woff2');
  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: 'Space Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/space-mono-400-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Space Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/space-mono-400-latin.woff2') format('woff2');
  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: 'Space Mono';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('../fonts/space-mono-700-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Space Mono';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('../fonts/space-mono-700-latin.woff2') format('woff2');
  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: 'Cinzel Decorative';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('../fonts/cinzel-decorative-700-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Cinzel Decorative';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('../fonts/cinzel-decorative-700-latin.woff2') format('woff2');
  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: 'Sora';
  font-style: normal;
  font-weight: 300;
  font-display: swap;
  src: url('../fonts/sora-300-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Sora';
  font-style: normal;
  font-weight: 300;
  font-display: swap;
  src: url('../fonts/sora-300-latin.woff2') format('woff2');
  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: 'Sora';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('../fonts/sora-500-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Sora';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('../fonts/sora-500-latin.woff2') format('woff2');
  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: 'Sora';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('../fonts/sora-700-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Sora';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('../fonts/sora-700-latin.woff2') format('woff2');
  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;
}

:root {
  /* Consolidated palette — a small set of semantic base colors that the admin
     "Palette colori" panel edits and /theme.css overrides. Every alpha tint /
     scrim used across the site is derived from one of these bases inline via
     color-mix(in srgb, <base> N%, transparent) — which is exactly equivalent to
     rgba(<base>, N/100) — so editing a base cascades to all its tints and the
     variable list stays short. See git history for the previous 55-var layout. */

  /* Surfaces */
  --color-bg: #ffffff;          /* page background */
  --color-surface: #fbfaf7;     /* elevated panels & cards */

  /* Ink — warm dark text/fills on light surfaces (now the primary text color) */
  --color-ink: #2b2620;
  --color-ink-muted: rgba(43, 38, 32, 0.62);

  /* Cream — light text/fills on dark surfaces */
  --color-cream: #f2ede4;
  --color-cream-soft: rgba(242, 237, 228, 0.7);
  --color-cream-muted: rgba(242, 237, 228, 0.55);
  --color-cream-faint: rgba(242, 237, 228, 0.4);

  /* Gold accent */
  --color-gold: #c9a227;
  --color-gold-soft: rgba(201, 162, 39, 0.35);

  /* Borders (ink at low alpha) */
  --color-border: rgba(43, 38, 32, 0.12);
  --color-border-hover: rgba(43, 38, 32, 0.22);

  /* Neutrals — bases for scrims, shadows and highlights */
  --color-black: #000;
  --color-white: #fff;

  /* Dark veil over media (section cards, banners) — base for veil tints.
     Matches --contacts-bg (Contatti section) for palette consistency. */
  --color-veil: #1a1613;

  /* Books shelf rail */
  --color-shelf-rail: #14110d;

  /* Hero fade veil — RGB channels only, because one gradient stop drives its
     alpha via calc() with --hero-fade (see .hero-section::after). Stored as a
     channel triplet so rgba(var(--color-hero-veil-rgb), <alpha>) can vary it. */
  --color-hero-veil-rgb: 10, 10, 15;

  /* Admin panel status colors */
  --color-danger: #f87171;
  --color-info: #93c5fd;
  --color-success: #4ade80;

  /* Site-wide type system: Cinzel Decorative 700 for titles/primary text,
     Sora 300 for everything else. Every font-family in this file should
     resolve through one of these variables. */
  --font-display: 'Cinzel Decorative', Georgia, serif;
  --font-display-accent: 'Cinzel Decorative', Georgia, serif;
  --font-body: 'Sora', ui-sans-serif, system-ui, sans-serif;
  --font-mono: 'Sora', ui-sans-serif, system-ui, sans-serif;

  --font-hero-title: var(--font-display);
  --font-hero-tagline: var(--font-body);

  --header-height: 4rem;
  --hero-fade: 0;
  --scroll-progress: 0;

  /* Larghezza massima dei contenitori di contenuto in home (about/preview/
     contatti). Ridotta rispetto a 1100px per rendere le sezioni più compatte. */
  --home-content-width: 980px;

  /* Stacco verticale tra una sezione e l'altra della home. Deve restare
     chiaramente percepibile: circa il doppio dei gap ritmici usati nelle
     griglie interne (about-inner desktop clamp(2.5rem,6vw,4.5rem),
     book-shelf clamp(1.5rem,5vw,3.5rem)). */
  --section-gap: clamp(3.5rem, 10vw, 7rem);

  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --transition-base: 0.4s var(--ease-out);

  /* --- Effetto "spotlight"/blob di sfondo -------------------------------------
     Toni delle macchie usate SOLO sulle fasce scure (contatti, footer,
     section-quote). L'oro è fortemente smorzato: tanto ink, poco gold, così
     sul fondo scuro non legge come "oro" ma come un tepore appena percepibile.
     --spot-grey è una macchia neutra fredda che lo affianca senza fare
     "tutto oro". Tutti i toni derivano dalla palette via color-mix (pannello
     admin "Palette colori"), niente valori hardcoded. Il campo chiaro globale
     (bianco editoriale freddo + soffio di gold) sta invece su body::before. */
  --spot-warm: color-mix(in srgb, var(--color-gold) 30%, var(--color-ink) 70%);
  --spot-grey: color-mix(in srgb, var(--color-cream) 38%, var(--color-black) 62%);
}

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

html {
  scroll-behavior: smooth;
}

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

body {
  margin: 0;
  position: relative;
  font-family: var(--font-body);
  font-weight: 300;
  letter-spacing: 0.02em;
  background: var(--color-bg);
  color: var(--color-ink);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* Campo chiaro GLOBALE e continuo — l'effetto spotlight/blob delle zone chiare.
   Un unico layer (non un trattamento per-sezione) grande quanto l'intero
   documento: le coordinate delle macchie sono percentuali sull'ALTEZZA TOTALE
   della pagina, non su ogni singola box, quindi non ci sono cuciture ai bordi
   tra una sezione e l'altra. È position:absolute (scorre col contenuto), NON
   fixed: niente "zone morte" da background ancorato al viewport. Le sezioni
   chiare (about, sections-preview, section-content) restano trasparenti e lo
   lasciano trasparire. Base = bianco editoriale FREDDO (--color-bg), sopra il
   quale un paio di soffi larghissimi e tenui (gold + ink) — nessuna forma che
   si legga come "cerchio". */
body::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(70% 42% at 18% 8%,
      color-mix(in srgb, var(--color-gold) 24%, transparent) 0%,
      transparent 68%),
    radial-gradient(62% 40% at 86% 30%,
      color-mix(in srgb, var(--color-ink) 20%, transparent) 0%,
      transparent 68%),
    radial-gradient(66% 42% at 20% 58%,
      color-mix(in srgb, var(--color-gold) 18%, transparent) 0%,
      transparent 70%),
    radial-gradient(62% 40% at 88% 84%,
      color-mix(in srgb, var(--color-ink) 18%, transparent) 0%,
      transparent 70%),
    radial-gradient(68% 42% at 30% 100%,
      color-mix(in srgb, var(--color-gold) 20%, transparent) 0%,
      transparent 70%),
    var(--color-bg);
}

body.nav-open { overflow: hidden; }

img { max-width: 100%; display: block; }

a { color: inherit; text-decoration: none; }

.hidden { display: none !important; }

/* Grain texture overlay */
.grain-overlay {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 9999;
  opacity: 0.015;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-repeat: repeat;
  background-size: 180px;
}

/* ==========================================================================
   Header & Navigation
   ========================================================================== */

.site-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  height: var(--header-height);
  padding: 0 1.25rem;
  transition: background var(--transition-base), backdrop-filter var(--transition-base);
}

.site-header.is-scrolled {
  background: color-mix(in srgb, var(--color-ink) 82%, transparent);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--color-border);
}

.site-logo {
  font-family: var(--font-display);
  font-style: normal;
  font-size: 1.0625rem;
  font-weight: 700;
  letter-spacing: 0.01em;
  color: var(--color-cream);
  text-shadow: 0 2px 20px color-mix(in srgb, var(--color-black) 60%, transparent);
  z-index: 102;
}

.nav-toggle {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 2.5rem;
  height: 2.5rem;
  padding: 0;
  background: none;
  border: none;
  cursor: pointer;
  z-index: 102;
}

.nav-toggle span {
  display: block;
  width: 1.5rem;
  height: 1.5px;
  background: var(--color-cream);
  transition: transform var(--transition-base), opacity var(--transition-base);
  margin: 0 auto;
}

.nav-toggle[aria-expanded="true"] span:nth-child(1) {
  transform: translateY(6.5px) rotate(45deg);
}
.nav-toggle[aria-expanded="true"] span:nth-child(2) {
  opacity: 0;
}
.nav-toggle[aria-expanded="true"] span:nth-child(3) {
  transform: translateY(-6.5px) rotate(-45deg);
}

.nav-menu {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: color-mix(in srgb, var(--color-ink) 97%, transparent);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--transition-base), visibility var(--transition-base);
  z-index: 101;
}

.nav-menu.is-open {
  opacity: 1;
  visibility: visible;
}

.nav-close {
  position: absolute;
  top: 1.1rem;
  right: 1.25rem;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.5rem;
  padding: 0;
  background: none;
  border: none;
  color: var(--color-cream);
  font-size: 2rem;
  line-height: 1;
  cursor: pointer;
  z-index: 102;
}

.nav-menu ul {
  list-style: none;
  margin: 0;
  padding: 0;
  text-align: center;
}

.nav-menu li + li { margin-top: 1.75rem; }

.nav-menu a {
  font-family: var(--font-body);
  font-style: normal;
  font-size: clamp(1.75rem, 5vw, 2.5rem);
  font-weight: 700;
  color: var(--color-cream-muted);
  transition: color 0.25s;
}

.nav-menu a:hover,
.nav-menu a.is-active {
  color: var(--color-cream);
}

.nav-overlay {
  display: none;
}

/* IT|EN language switch: plain <a> tags inside .nav-menu, so they already
   inherit the .nav-menu a rules above (color, hover, is-active) — only the
   flex layout + separator need their own rule, no new color tokens. */
.nav-lang {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.4em;
}

.nav-lang__sep {
  color: var(--color-cream-muted);
  opacity: 0.5;
}

@media (min-width: 900px) {
  .site-header { padding: 0 2.5rem; }

  .nav-toggle { display: none; }

  .nav-close { display: none; }

  .nav-menu {
    position: fixed;
    inset: auto;
    top: 0;
    right: 2.5rem;
    height: var(--header-height);
    display: flex;
    align-items: center;
    opacity: 1;
    visibility: visible;
    background: none;
    width: auto;
  }

  .nav-menu ul {
    display: flex;
    gap: 2rem;
    align-items: center;
  }

  .nav-menu li + li { margin-top: 0; }

  .nav-menu a {
    font-family: var(--font-body);
    font-style: normal;
    font-size: 0.6875rem;
    font-weight: 500;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    text-shadow: 0 2px 20px color-mix(in srgb, var(--color-black) 60%, transparent);
    opacity: 0.75;
    transition: opacity 0.2s;
  }

  .nav-menu a:hover,
  .nav-menu a.is-active {
    color: var(--color-cream);
    opacity: 1;
  }

  .nav-lang {
    margin-left: 0.25rem;
  }
}

/* ==========================================================================
   Landing Hero
   ========================================================================== */

.hero-section {
  position: relative;
  height: 100dvh;
  min-height: 500px;
  overflow: hidden;
  --hero-fade: 0;
}

.landing-container {
  position: absolute;
  inset: 0;
}

.landing-hero {
  /* Fixed px overscan (not a percentage) so this matches, at first paint,
     the top/height parallax.js's measure() computes from its OVERSCAN
     constant (see .puzzle-item__parallax below for the same reasoning) —
     otherwise the image visibly resizes/re-crops the instant JS takes over. */
  position: absolute;
  top: -24px;
  right: 0;
  left: 0;
  height: calc(100% + 48px);
  will-change: transform;
}

.landing-layer {
  position: absolute;
  inset: 0;
  background-size: cover;
  /* Anchored to the top edge (not centered): on tall/narrow viewports a
     centered cover crop pushes the subject's head off-screen just as often
     as it clips the feet, since figures in these paintings are framed near
     the top of the source image. Top-anchoring keeps that framing intact,
     however the crop has to grow or shrink to cover the screen.
     The offset matches the overscan below, not the `top` keyword: this layer
     IS the .landing-hero box, which parallax.js overscans on every side (see
     .landing-hero's own top/height above) so it has slack to translate
     during scroll. `top` would anchor the image to that oversized box's own
     edge — above the actually-visible top of .hero-section — clipping
     the top of the image by exactly that much.
     Offsetting the anchor by the FULL overscan amount would cancel it back
     out exactly at rest, but parallax.js's translateY is applied on top of
     that afterwards — it doesn't know the anchor already spent the overscan
     canceling itself out, so any nonzero offset then pushes the image's real
     top edge past the clip window with nothing left to cover it, opening a
     gap right at the top of the viewport. Anchoring at 0 instead keeps the
     overscan's slack intact for parallax to actually use. */
  background-position: center top;
  background-repeat: no-repeat;
  opacity: 0;
  transition: opacity 0.8s ease-in-out;
  will-change: opacity;
}

.landing-layer.is-visible { opacity: 1; }

.landing-layer--no-transition { transition: none; }

.landing-grain {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(ellipse at center, transparent 45%, rgba(var(--color-hero-veil-rgb), 0.26) 100%);
}

.hero-section::after {
  content: '';
  position: absolute;
  inset: 0;
  /* Top stop kept as a small legibility floor for the cream nav/logo, which
     sit over hero images of unpredictable brightness (still well below the
     original 0.15 veil — see handoff §2). */
  background: linear-gradient(
    to bottom,
    rgba(var(--color-hero-veil-rgb), 0.11) 0%,
    rgba(var(--color-hero-veil-rgb), 0.03) 40%,
    rgba(var(--color-hero-veil-rgb), calc(0.16 + var(--hero-fade, 0) * 0.45)) 100%
  );
  pointer-events: none;
  z-index: 2;
}

.hero-scrim {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 38vh;
  background: linear-gradient(to top, color-mix(in srgb, var(--color-black) 68%, transparent) 0%, color-mix(in srgb, var(--color-black) 42%, transparent) 30%, color-mix(in srgb, var(--color-black) 20%, transparent) 55%, transparent 100%);
  z-index: 1;
  pointer-events: none;
}

.hero-overlay {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: center;
  padding: 2rem 1.5rem 3rem;
  opacity: calc(1 - var(--hero-fade, 0) * 1.2);
  transform: translateY(calc(var(--hero-fade, 0) * -30px));
  transition: opacity 0.1s linear;
}

.hero-title {
  text-align: center;
  margin: 0 0 2.5rem;
}

.hero-title__name {
  display: block;
  font-family: var(--font-hero-title);
  font-style: normal;
  font-size: clamp(3.75rem, 15vw, 6.5rem);
  font-weight: 700;
  letter-spacing: 0.02em;
  line-height: 1.05;
  color: var(--color-cream);
  text-shadow: 0 2px 40px color-mix(in srgb, var(--color-black) 60%, transparent);
}

.hero-title__name.is-clipped {
  /* Overridden inline by landing.js with the exact crop math that matches
     the visible .landing-layer background; these are just a safe fallback. */
  background-size: cover;
  background-position: center;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  text-shadow: none;
  filter: brightness(1.6) contrast(1.2) saturate(1.1);
  transition: filter 0.5s ease;
}

/* Toggled by landing.js for the specific rotation frames whose painting is
   too light (white text vanishes) or too dark/high-contrast (parts of the
   word drop into the scrim) to read under the normal brighten/contrast boost
   — see LOW_CONTRAST_FRAME_IDS. Strong grayscale + luminance invert push the
   letterforms toward a neutral HIGH-CONTRAST fill (flip dark<->light so the
   word separates from its surroundings) rather than a full color negative,
   which on saturated crops reads as a jarring "wrong picture". */
.hero-title__name.is-clipped.is-low-contrast {
  filter: brightness(1.05) contrast(1.85) grayscale(0.85) invert(0.9);
}

.hero-title__tagline {
  display: block;
  margin-top: 0.5rem;
  font-family: var(--font-hero-tagline);
  font-size: clamp(0.75rem, 2vw, 0.9rem);
  font-weight: 500;
  letter-spacing: 0.25em;
  text-transform: uppercase;
  color: var(--color-gold);
}

.scroll-hint {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.75rem;
  background: none;
  border: none;
  cursor: pointer;
  padding: 0.5rem;
  color: var(--color-cream-muted);
  animation: scrollPulse 2.5s ease-in-out infinite;
}

.scroll-hint__text {
  font-size: 0.7rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
}

.scroll-hint__line {
  display: block;
  width: 1px;
  height: 2.5rem;
  background: linear-gradient(to bottom, var(--color-gold), transparent);
}

@keyframes scrollPulse {
  0%, 100% { opacity: 0.5; transform: translateY(0); }
  50% { opacity: 1; transform: translateY(6px); }
}

@media (prefers-reduced-motion: reduce) {
  .scroll-hint { animation: none; opacity: 0.7; }
}

/* ==========================================================================
   Sfondo chiaro condiviso — Home (about + sections-preview) e pagine sezione
   (section-content: dipinti/digitali/murales/libri/disegni)
   ========================================================================== */

/* Trasparenti: NON hanno un proprio sfondo. Lasciano trasparire il campo
   chiaro globale e continuo di body::before (bianco editoriale freddo + soffio
   di gold), così l'effetto attraversa senza cuciture il confine tra una
   sezione chiara e l'altra. */
.about-section,
.sections-preview,
.section-content {
  background: transparent;
}

.about-section {
  position: relative;
  z-index: 10;
  padding: clamp(4rem, 13vw, 6.5rem) 1.25rem clamp(1.75rem, 5vw, 2.75rem);
  opacity: 0;
  transform: translateY(40px);
  transition: opacity 1s var(--ease-out), transform 1s var(--ease-out);
}

.about-section.is-visible {
  opacity: 1;
  transform: translateY(0);
}

.about-inner {
  max-width: 620px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(2rem, 6vw, 2.75rem);
}

.about-body {
  width: 100%;
}

.about-photo {
  flex: 0 0 auto;
  width: clamp(200px, 62vw, 260px);
  aspect-ratio: 1 / 1;
  margin: 0;
  overflow: hidden;
  border-radius: 42% 58% 46% 54% / 55% 44% 56% 45%;
  border: 1px solid var(--color-gold-soft);
  box-shadow: 0 24px 48px color-mix(in srgb, var(--color-black) 42%, transparent);
  transform: rotate(-3deg);
}

.about-photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

@media (min-width: 880px) {
  .about-inner {
    max-width: 900px;
    flex-direction: row;
    align-items: center;
    gap: clamp(2.5rem, 6vw, 4.5rem);
  }

  .about-body {
    flex: 1 1 auto;
    max-width: 620px;
  }

  .about-photo {
    width: clamp(240px, 30vw, 320px);
    aspect-ratio: 4 / 5;
  }
}

.about-eyebrow,
.section-eyebrow {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--color-ink-muted);
  margin: 0 0 1.25rem;
}

.about-heading {
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 6vw, 1.875rem);
  font-weight: 700;
  line-height: 1.25;
  margin: 0 0 1.75rem;
  color: var(--color-ink);
}

.about-heading em {
  font-style: normal;
  color: var(--color-gold);
}

.about-heading i {
  font-style: italic;
}

.about-text p {
  margin: 0 0 1.25rem;
  color: var(--color-ink-muted);
  font-family: var(--font-body);
  font-size: 0.90625rem;
  line-height: 1.75;
}

.about-text strong {
  color: var(--color-ink);
  font-weight: 500;
}

.about-text em {
  font-style: normal;
  color: var(--color-gold);
}

.about-text i {
  font-style: italic;
}

/* ==========================================================================
   Section Preview Cards (Homepage)
   ========================================================================== */

.sections-preview {
  position: relative;
  /* Il margin non lascia più bande: lo spazio scopre il campo chiaro continuo
     di body::before, non un bianco piatto. */
  margin-top: var(--section-gap);
  padding: 0 1.25rem clamp(3rem, 9vw, 4.5rem);
}

.sections-preview__header {
  max-width: var(--home-content-width);
  margin: 0 auto;
  padding: 0 0 clamp(2rem, 7vw, 3rem);
}

.section-title em {
  font-style: normal;
  color: var(--color-gold);
}

.section-title i {
  font-style: italic;
}

.section-title {
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 5vw, 2rem);
  font-weight: 700;
  margin: 0;
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 0.8s var(--ease-out), transform 0.8s var(--ease-out);
}

.section-title.is-visible {
  opacity: 1;
  transform: translateY(0);
}

.sections-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(1rem, 3vw, 1.75rem);
  /* NIENTE background qui: trasparente, così il campo chiaro continuo di
     body::before riempie anche gli spazi tra le card. */
  max-width: var(--home-content-width);
  margin: 0 auto;
}

.section-card {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  overflow: hidden;
  isolation: isolate;
  aspect-ratio: 4 / 5;
  padding: clamp(1.75rem, 6vw, 2.25rem) clamp(1.25rem, 5vw, 1.75rem);
  background: var(--color-bg);
  opacity: 0;
  transform: translateY(24px);
  transition: opacity 0.7s var(--ease-out), transform 0.7s var(--ease-out);
}

.section-card.is-visible {
  opacity: 1;
  transform: translateY(0);
}

.section-card__media {
  position: absolute;
  inset: 0;
  z-index: -2;
  background: var(--color-surface);
}

.section-card__media picture {
  display: block;
  width: 100%;
  height: 100%;
}

.section-card__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 0.5s var(--ease-out), transform 1.1s var(--ease-out);
}

.section-card__media img[src] {
  opacity: 1;
}

.section-card:hover .section-card__media img {
  transform: scale(1.05);
}

.section-card__scrim {
  position: absolute;
  inset: 0;
  z-index: -1;
  /* Legibility gradient confined to the text zone at the bottom of the card,
     not a full-height dark veil (see handoff §2). */
  background: linear-gradient(
    to top,
    color-mix(in srgb, var(--color-veil) 72%, transparent) 0%,
    color-mix(in srgb, var(--color-veil) 30%, transparent) 38%,
    transparent 62%
  );
  transition: background var(--transition-base);
}

.section-card:hover .section-card__scrim {
  background: linear-gradient(
    to top,
    color-mix(in srgb, var(--color-veil) 80%, transparent) 0%,
    color-mix(in srgb, var(--color-veil) 38%, transparent) 40%,
    transparent 66%
  );
}

.section-card h3 {
  font-family: var(--font-display);
  font-size: 1.375rem;
  font-weight: 700;
  margin: 0.6rem 0 0.5rem;
  color: var(--color-cream);
}

.section-card p {
  margin: 0;
  font-family: var(--font-body);
  font-size: 0.8125rem;
  color: var(--color-cream-soft);
  max-width: 32ch;
}

@media (min-width: 640px) {
  .sections-grid {
    grid-template-columns: repeat(2, 1fr);
  }
  .section-card--wide {
    grid-column: 1 / -1;
    aspect-ratio: 21 / 9;
  }
}

/* ==========================================================================
   Contatti (Homepage)
   ========================================================================== */

.contacts-section {
  /* Unica sezione che resta scura: chiusura visiva forte a fine pagina (handoff §8).
     Variabili locali hardcoded — NON esposte nel pannello admin "Palette colori". */
  --contacts-bg: #1a1613;
  --contacts-surface: #221d18;
  --contacts-ink: #f2ede4;
  position: relative;
  /* L'header è fixed: senza scroll-margin-top il salto da "/#contacts"
     (link di navigazione) allinea il bordo della sezione sotto l'header,
     coprendone l'inizio. */
  scroll-margin-top: var(--header-height);
  margin-top: var(--section-gap);
  padding: clamp(3rem, 9vw, 4.5rem) 1.25rem clamp(3.5rem, 10vw, 5rem);
  /* Fascia scura: due sole macchie ampie e morbide — un tepore desaturato
     (--spot-warm) affiancato da una macchia neutra fredda (--spot-grey) — che
     sfumano fino alla base BEN PRIMA dei bordi. Così il bordo inferiore resta
     base pura (#1a1613): identico al bordo superiore del footer, che è la
     stessa base — la giunzione contatti→footer è senza soluzione di
     continuità, un'unica area scura. */
  background:
    radial-gradient(115% 80% at 24% 24%,
      color-mix(in srgb, var(--spot-warm) 55%, transparent) 0%,
      transparent 64%),
    radial-gradient(105% 72% at 82% 64%,
      color-mix(in srgb, var(--spot-grey) 46%, transparent) 0%,
      transparent 64%),
    var(--contacts-bg);
  color: var(--contacts-ink);
}

.contacts-inner {
  max-width: var(--home-content-width);
  margin: 0 auto;
}

.contacts-section .section-eyebrow {
  color: var(--color-cream-muted);
}

.contacts-heading {
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 5vw, 2rem);
  font-weight: 700;
  margin: 0.6rem 0 clamp(1.75rem, 6vw, 2.5rem);
  color: var(--contacts-ink);
}

.contacts-heading em {
  color: var(--color-gold);
  font-style: normal;
}

.contacts-heading i {
  font-style: italic;
}

.contacts-social-intro {
  font-family: var(--font-body);
  font-size: clamp(0.9rem, 2.5vw, 1rem);
  color: var(--contacts-ink);
  text-align: center;
  max-width: 32rem;
  margin: 0 auto clamp(1rem, 4vw, 1.5rem);
}

.contacts-social {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: clamp(1rem, 5vw, 2rem);
  padding: 0 0 clamp(0.5rem, 2vw, 1rem);
}

.contacts-social__link[hidden] {
  display: none;
}

.contacts-social__link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: clamp(2.5rem, 9vw, 3rem);
  height: clamp(2.5rem, 9vw, 3rem);
  border: 1px solid color-mix(in srgb, var(--contacts-ink) 24%, transparent);
  border-radius: 50%;
  color: var(--contacts-ink);
  opacity: 0;
  transform: translateY(24px);
  transition: opacity 0.7s var(--ease-out), transform 0.35s var(--ease-out),
    color var(--transition-base), background var(--transition-base), border-color var(--transition-base);
}

.contacts-social__link.is-visible {
  opacity: 1;
  transform: translateY(0);
}

.contacts-social__link:hover,
.contacts-social__link:focus-visible {
  color: var(--contacts-bg);
  background: var(--color-gold);
  border-color: var(--color-gold);
  transform: translateY(-3px);
}

.contacts-social__link svg {
  width: 48%;
  height: 48%;
  display: block;
}

.contact-form-block {
  margin-top: clamp(2rem, 6vw, 3rem);
  padding-top: clamp(1.75rem, 6vw, 2.5rem);
  border-top: 1px solid color-mix(in srgb, var(--contacts-ink) 15%, transparent);
  opacity: 0;
  transform: translateY(24px);
  transition: opacity 0.7s var(--ease-out), transform 0.7s var(--ease-out);
}

.contact-form-block.is-visible {
  opacity: 1;
  transform: translateY(0);
}

.contact-form-heading {
  font-family: var(--font-display);
  font-size: clamp(1.25rem, 4vw, 1.625rem);
  font-weight: 700;
  margin: 0 0 clamp(1.25rem, 4vw, 1.75rem);
  color: var(--contacts-ink);
}

.contact-form-heading em {
  color: var(--color-gold);
  font-style: normal;
}

.contact-form {
  width: 100%;
}

.contact-form__row {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.25rem;
}

.contact-form label {
  display: block;
  margin-bottom: 1.25rem;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--contacts-ink) 62%, transparent);
}

.contact-form input,
.contact-form textarea {
  display: block;
  width: 100%;
  margin-top: 0.5rem;
  padding: 0.7rem 0.85rem;
  border-radius: 2px;
  border: 1px solid color-mix(in srgb, var(--color-ink) 15%, transparent);
  background: var(--color-white);
  color: var(--color-ink);
  font-family: var(--font-body);
  font-size: 0.875rem;
  text-transform: none;
  letter-spacing: normal;
  transition: border-color var(--transition-base);
}

.contact-form input:focus,
.contact-form textarea:focus {
  outline: none;
  border-color: var(--color-gold);
}

.contact-form textarea {
  resize: vertical;
  min-height: 8rem;
}

.contact-form__submit {
  display: inline-block;
  width: auto;
  cursor: pointer;
  background: var(--color-gold);
  color: var(--contacts-bg);
  border: none;
  border-radius: 2px;
  padding: 0.7rem 1.5rem;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  font-weight: 500;
  transition: opacity var(--transition-base);
}

.contact-form__submit:hover {
  opacity: 0.85;
}

.contact-form__status {
  margin: 0.75rem 0 0;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--color-gold);
  min-height: 1em;
}

.contact-form__note {
  margin: 1rem 0 0;
  font-family: var(--font-body);
  font-size: 0.75rem;
  line-height: 1.6;
  color: color-mix(in srgb, var(--contacts-ink) 55%, transparent);
}

.contact-form__note a {
  color: color-mix(in srgb, var(--contacts-ink) 75%, transparent);
  text-decoration: underline;
}

@media (min-width: 640px) {
  .contact-form__row {
    grid-template-columns: 1fr 1fr;
    gap: 1.25rem 1.5rem;
  }
}

/* ==========================================================================
   Section banner + quote (Section pages — template 3a)
   ========================================================================== */

.section-banner {
  position: relative;
  height: 80dvh;
  min-height: 420px;
  overflow: hidden;
}

.section-banner__media {
  /* Fixed px overscan, matching parallax.js's OVERSCAN constant — see
     .landing-hero above and .puzzle-item__parallax below for why this must
     stay in px, not a percentage. */
  position: absolute;
  top: -24px;
  right: 0;
  left: 0;
  height: calc(100% + 48px);
  margin: 0;
  will-change: transform;
}

.section-banner__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Top-anchored, not centered — see .landing-layer for why on the anchor
     itself. Anchored at 0, not the overscan amount: canceling the overscan
     exactly would leave nothing for parallax.js's translateY to move into,
     opening a gap at the top the instant the transform goes nonzero (see
     .landing-layer's comment for the full reasoning). */
  object-position: center top;
}

.section-banner__gradient {
  position: absolute;
  inset: 0;
  /* Bottom band for title legibility (handoff §2), plus a small top floor —
     matching .hero-section::after — so the nav stays readable over bright
     banner images instead of sitting on a flat, unveiled strip. */
  background: linear-gradient(
    to top,
    color-mix(in srgb, var(--color-veil) 65%, transparent) 0%,
    transparent 45%,
    transparent 88%,
    color-mix(in srgb, var(--color-veil) 30%, transparent) 100%
  );
}

.section-banner__content {
  position: absolute;
  left: 1.25rem;
  right: 1.25rem;
  bottom: 1.75rem;
}

.section-banner__content .section-eyebrow {
  color: var(--color-cream-soft);
  margin: 0 0 0.6rem;
}

.section-banner__title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.85rem, 8vw, 3.5rem);
  line-height: 1.05;
  margin: 0;
  color: var(--color-cream);
}

.section-banner__title em {
  font-style: normal;
  color: var(--color-gold);
}

.section-banner__title i {
  font-style: italic;
}

@media (min-width: 640px) {
  .section-banner__content {
    left: 3.5rem;
    right: 3.5rem;
    bottom: 2.75rem;
  }
}

.section-quote {
  position: relative;
  /* Stesso linguaggio dei contatti ma ancora più sobrio: è una fascia piccola,
     quindi UNA sola macchia molto ampia e tenue (non tre compresse, che
     leggevano come cerchi). Sfuma alla base ben prima dei bordi. */
  background:
    radial-gradient(150% 130% at 26% 36%,
      color-mix(in srgb, var(--spot-warm) 44%, transparent) 0%,
      transparent 66%),
    radial-gradient(130% 120% at 84% 78%,
      color-mix(in srgb, var(--spot-grey) 34%, transparent) 0%,
      transparent 68%),
    var(--color-veil);
  color: var(--color-cream);
  text-align: center;
  padding: clamp(2.5rem, 10vw, 4rem) 1.25rem clamp(3rem, 8vw, 6rem);
}

.section-quote__text {
  position: relative;
  font-family: var(--font-body);
  font-weight: 300;
  font-style: normal;
  font-size: clamp(1.05rem, 4vw, 1.4rem);
  line-height: 1.6;
  max-width: 40ch;
  margin: 0 auto;
}

/* ==========================================================================
   Section Grid (dynamic works — template 3a)
   ========================================================================== */

.section-content {
  padding-bottom: clamp(3rem, 8vw, 5rem);
}

.portfolio-loading,
.portfolio-empty {
  text-align: center;
  color: var(--color-ink-muted);
  padding: 3rem 1.25rem;
}

.section-grid-hero {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.25rem;
  position: relative;
  z-index: 2;
  margin-top: -2rem;
  max-width: calc(1100px + 2.5rem);
  margin-left: auto;
  margin-right: auto;
  padding: 0 1.25rem;
}

.section-grid-hero__media {
  margin: 0;
  overflow: hidden;
  box-shadow: 0 20px 40px color-mix(in srgb, var(--color-black) 35%, transparent);
}

.section-grid-hero__media img {
  width: 100%;
  /* The width/height HTML attributes (persisted image dimensions) map to a
     presentational height hint that this rule must explicitly override —
     otherwise the box renders at the image's raw natural height instead of
     the aspect-ratio height, since aspect-ratio only governs an auto axis. */
  height: auto;
  aspect-ratio: var(--img-ratio, 16 / 11);
  object-fit: cover;
  display: block;
  transition: transform 1.1s cubic-bezier(0.22, 1, 0.36, 1);
}

.section-grid-hero__media:hover img {
  transform: scale(1.035);
}

.section-grid-hero__caption {
  font-family: var(--font-display);
  font-weight: 700;
  font-style: normal;
  font-size: 1.125rem;
  line-height: 1.4;
  color: var(--color-ink);
  margin: 0;
}

.section-grid-spacer {
  padding-bottom: clamp(2.5rem, 8vw, 5.5rem);
}

/* Puzzle grid: a true masonry layout, built from real column <div>s that
   portfolio.js distributes tiles into by estimated height (rather than CSS
   `column-count`, which fragments elements across columns — WebKit has a
   long-standing bug where a position:absolute descendant inside a column
   fragment (.puzzle-item__parallax below) gets its containing block/clip
   resolved wrong once its transform is rewritten by JS on scroll, causing
   flicker and images that fail to paint. Real DOM columns have no
   fragmentation context, so that bug class can't occur). Below 640px
   portfolio.js emits a single column (one full-width image per row). */
.section-grid-puzzle {
  display: flex;
  align-items: flex-start;
  max-width: calc(1100px + 2.5rem);
  margin-left: auto;
  margin-right: auto;
  padding: 0 1.25rem clamp(3rem, 8vw, 6rem);
}

.puzzle-column {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.puzzle-item {
  display: block;
  margin-bottom: 1.75rem;
}

.puzzle-item__media {
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: var(--img-ratio, 4 / 3);
  margin: 0;
  overflow: hidden;
  /* Own compositing layer for the parallax transform below, for smoother
     scroll compositing (not a fragmentation workaround — the puzzle grid no
     longer uses CSS columns, so that bug class doesn't apply here). */
  transform: translateZ(0);
}

/* Parallax moves this inner layer, not .puzzle-item__media itself, so the
   tile's box (and the masonry gap below it) stays put — only the image
   content slides within the clipped window (same reasoning as
   .section-banner__media). Overscan is a fixed px amount rather than a
   percentage: tiles here can be as short as ~150px, and the parallax
   offset scales with the tile's distance down the page and its speed, not
   with its own height, so a percentage would under-cover short tiles.
   Must match parallax.js's OVERSCAN constant, which overwrites this at
   runtime — kept small because cover-fit pays for this box's full height in
   zoom, and that cost is severe on tiles this short. */
.puzzle-item__parallax {
  position: absolute;
  top: -24px;
  right: 0;
  left: 0;
  height: calc(100% + 48px);
  margin: 0;
  will-change: transform;
}

.puzzle-item__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  cursor: pointer;
  transition: transform 1.1s cubic-bezier(0.22, 1, 0.36, 1);
}

.puzzle-item__media:hover img {
  transform: scale(1.045);
}

.puzzle-item__caption {
  position: absolute;
  inset: auto 0 0 0;
  margin: 0;
  padding: 2.5rem 1rem 0.85rem;
  background: linear-gradient(to top, rgba(10, 8, 6, 0.78), rgba(10, 8, 6, 0) 85%);
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  opacity: 0;
  transform: translateY(6px);
  transition: opacity 0.35s ease, transform 0.35s ease;
  pointer-events: none;
}

.puzzle-item__media:hover .puzzle-item__caption,
.puzzle-item__media:focus-within .puzzle-item__caption {
  opacity: 1;
  transform: translateY(0);
}

.puzzle-item__title {
  font-family: var(--font-display);
  font-weight: 700;
  font-style: normal;
  font-size: 1rem;
  color: var(--color-cream);
}

@media (min-width: 720px) {
  .section-grid-hero {
    grid-template-columns: 7fr 5fr;
    align-items: end;
    gap: 0;
    margin-top: -3rem;
    padding: 0 3.5rem;
  }

  .section-grid-hero__text {
    padding: 0 0 1.5rem 2rem;
  }

  .section-grid-hero__caption { font-size: 1.375rem; }
}

@media (min-width: 640px) {
  .section-grid-puzzle {
    gap: 1.5rem;
    padding: 0 3.5rem clamp(4rem, 8vw, 6rem);
  }

  .puzzle-item {
    margin-bottom: 1.5rem;
  }
}

.section-grid-hero__media img {
  cursor: pointer;
}

/* ==========================================================================
   Lightbox (grid image slider)
   ========================================================================== */

.lightbox {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  background: color-mix(in srgb, var(--color-ink) 95%, transparent);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.3s var(--ease-out), visibility 0.3s var(--ease-out);
  padding: 1.25rem;
}

.lightbox.is-open {
  opacity: 1;
  visibility: visible;
}

.lightbox__figure {
  margin: 0;
  max-width: min(92vw, 1100px);
  max-height: 82vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.75rem;
}

.lightbox__image {
  max-width: 100%;
  max-height: 82vh;
  object-fit: contain;
  display: block;
  box-shadow: 0 30px 60px color-mix(in srgb, var(--color-black) 50%, transparent);
}

.lightbox__caption {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.08em;
  color: var(--color-cream-faint);
  text-align: center;
}

.lightbox__close {
  position: absolute;
  top: 1rem;
  right: 1rem;
  width: 2.5rem;
  height: 2.5rem;
  display: flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  color: var(--color-cream);
  font-size: 1.75rem;
  line-height: 1;
  cursor: pointer;
}

.lightbox__prev,
.lightbox__next {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 2.75rem;
  height: 2.75rem;
  display: flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: 1px solid var(--color-border);
  border-radius: 50%;
  color: var(--color-cream);
  font-size: 1.5rem;
  cursor: pointer;
  opacity: 0.75;
  transition: opacity 0.2s, border-color 0.2s;
}

.lightbox__prev:hover,
.lightbox__next:hover {
  opacity: 1;
  border-color: var(--color-border-hover);
}

.lightbox__prev { left: 1rem; }
.lightbox__next { right: 1rem; }

@media (min-width: 640px) {
  .lightbox__close { top: 1.5rem; right: 1.5rem; }
  .lightbox__prev { left: 1.75rem; }
  .lightbox__next { right: 1.75rem; }
}

body.lightbox-open {
  overflow: hidden;
}

/* ==========================================================================
   Project detail overlay (libri & murales — full-screen in-page detail,
   deep-linkable via ?opera=<id>; see public/js/project-detail.js). Sits under
   the inner lightbox (z 200) but above the header. Colors derive from the
   semantic palette so it follows whatever /theme.css sets (light or dark); the
   backdrop uses --color-black so it stays a dark scrim in any palette.
   ========================================================================== */

.project-detail {
  position: fixed;
  inset: 0;
  z-index: 150;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  overflow-y: auto;
  padding: clamp(1rem, 4vw, 3rem);
  background: color-mix(in srgb, var(--color-black) 72%, transparent);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.3s var(--ease-out), visibility 0.3s var(--ease-out);
}

.project-detail.is-open { opacity: 1; visibility: visible; }

.project-detail__panel {
  position: relative;
  width: 100%;
  max-width: 960px;
  margin: auto;
  padding: clamp(1.5rem, 4vw, 3rem);
  background: var(--color-surface);
  color: var(--color-ink);
  border: 1px solid var(--color-border);
  border-radius: 2px;
  box-shadow: 0 34px 70px -34px color-mix(in srgb, var(--color-black) 34%, transparent);
}

.project-detail__close {
  position: absolute;
  top: 0.75rem;
  right: 0.75rem;
  z-index: 2;
  width: 2.5rem;
  height: 2.5rem;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0;
  padding: 0;
  background: none;
  border: none;
  color: var(--color-ink);
  font-size: 1.75rem;
  line-height: 1;
  cursor: pointer;
}
.project-detail__close:hover { color: var(--color-gold); }

.project-detail__title {
  margin: 0 0 0.75rem;
  padding-right: 2rem;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.4rem, 3vw, 2rem);
  color: var(--color-ink);
}

/* Libri: cover + meta side by side on desktop, stacked on mobile. */
.project-detail__book {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(1.5rem, 4vw, 2.5rem);
  align-items: start;
}
@media (min-width: 640px) {
  .project-detail__book { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); }
}

/* The cover keeps its own natural (uncropped) proportions — only the extra
   mockup/gallery images below it join the puzzle-style cropped grid. */
.project-detail__cover,
.project-detail__cover .project-detail__image {
  margin: 0;
  aspect-ratio: auto;
  overflow: visible;
}
.project-detail__cover .project-detail__image img {
  width: 100%;
  height: auto;
  object-fit: initial;
  display: block;
  border-radius: 2px;
  box-shadow: 0 20px 40px color-mix(in srgb, var(--color-black) 30%, transparent);
}

.project-detail__author {
  margin: 0 0 1rem;
  font-family: var(--font-mono);
  font-size: 0.9rem;
  letter-spacing: 0.02em;
  color: var(--color-ink-muted);
}

.project-detail__buy {
  display: inline-block;
  margin: 0 0 1.25rem;
  padding: 0.65rem 1.5rem;
  background: var(--color-gold);
  color: var(--color-bg);
  font-family: var(--font-mono);
  font-size: 0.8rem;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-decoration: none;
  border-radius: 2px;
  transition: opacity 0.2s;
}
.project-detail__buy:hover { opacity: 0.9; }

.project-detail__synopsis {
  margin: 0;
  white-space: pre-line;
  line-height: 1.7;
  color: var(--color-ink);
}

.project-detail__murales-meta {
  margin: 0;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem;
  font-family: var(--font-mono);
  font-size: 0.85rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-ink-muted);
}
.project-detail__dot { opacity: 0.5; }
.project-detail__murales-description {
  margin: 0.85rem 0 0;
  white-space: pre-line;
  line-height: 1.7;
  color: var(--color-ink);
}

/* Internal image grid — clickable, opens the inner lightbox. Same real-column
   masonry as the dipinti/digitali/disegni puzzle grid (see .section-grid-puzzle
   / .puzzle-column above and portfolio.js distributeIntoColumns): project-detail.js
   distributes tiles into real column <div>s, 3 columns on desktop and a single
   stacked column on mobile. margin-top is the one gap between the cover
   (libri) / head (murales) and the grid, so both layouts read the same. */
.project-detail__images {
  display: flex;
  align-items: flex-start;
  gap: clamp(1rem, 2.5vw, 1.5rem);
  margin-top: clamp(1.75rem, 4vw, 2.5rem);
}
.project-detail__images:empty { margin-top: 0; }

.project-detail__column {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: clamp(1rem, 2.5vw, 1.5rem);
}

.project-detail__image {
  display: block;
  margin: 0;
  cursor: pointer;
  aspect-ratio: var(--img-ratio, 4 / 3);
  overflow: hidden;
  border-radius: 2px;
}
.project-detail__image img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

body.project-detail-open { overflow: hidden; }

/* Click affordance for the project sections' entry points. */
.book-slide,
.wall-item,
.mural-slide { cursor: pointer; }
.book-slide:focus-visible,
.wall-item:focus-visible,
.mural-slide:focus-visible {
  outline: 2px solid var(--color-gold);
  outline-offset: 4px;
}

/* ==========================================================================
   Wall Grid (Murales — full-bleed banner stack, replaces .section-grid-puzzle
   only for category === 'murales'; see design-handoff/Murales e layout banner)
   ========================================================================== */

.section-grid-wall {
  position: relative;
  width: 100vw;
  padding-left: 30px;
}

/* Runs the full height of the stack (not just the viewport) — a plain
   absolute child of the full-height wall column, not sticky, matching the
   hifi prototype exactly. */
.wall-rail {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 30px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  padding: 14px 0;
  background: var(--color-shelf-rail);
}

.wall-rail__label {
  writing-mode: vertical-rl;
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.1em;
  color: var(--color-cream-faint);
}

.wall-item {
  display: block;
}

.wall-item__media {
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: var(--img-ratio, 1.4);
  overflow: hidden;
  transform: translateZ(0);
}

/* Same clipped-layer parallax pattern as .puzzle-item__parallax: the image
   is the layer that moves, .wall-item__media is the static clip window, so
   the banner's own real proportions (via --img-ratio above) never jump
   around under the parallax transform. */
.wall-item__parallax {
  position: absolute;
  top: -24px;
  right: 0;
  left: 0;
  height: calc(100% + 48px);
  margin: 0;
  will-change: transform;
}

.wall-item__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  cursor: pointer;
}

.wall-item__caption {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 8px 16px;
  border-bottom: 1px solid var(--color-gold);
}

/* :first-of-type, not :first-child — .wall-rail (a <div>) is the actual
   first child of .section-grid-wall, ahead of every .wall-item (<article>). */
.wall-item:first-of-type .wall-item__caption {
  border-top: 1px solid var(--color-gold);
}

.wall-item__caption-left {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  color: var(--color-ink-muted);
}

.wall-item__caption-right {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  color: var(--color-gold);
  white-space: nowrap;
}

@media (max-width: 480px) {
  .section-grid-wall {
    padding-left: 20px;
  }

  .wall-rail {
    width: 20px;
  }

  .wall-rail__label {
    font-size: 8px;
  }
}

/* ==========================================================================
   Book Shelf (Libri — carousel, replaces .section-grid on that page only)
   ========================================================================== */

.book-shelf {
  padding-bottom: clamp(3rem, 8vw, 5rem);
  /* Pulls the whole carousel up over the cream quote section's bottom edge —
     the centered book is meant to visually rise into it (see
     .book-slide translateY below). The track absorbs the matching top
     padding itself rather than this margin, because overflow-x: auto also
     forces overflow-y: auto (CSS can't have one axis visible and not the
     other), which would otherwise clip that rise. */
  margin-top: -6.5rem;
  position: relative;
  z-index: 2;
}

.book-shelf__viewport {
  position: relative;
  padding: clamp(1.25rem, 3vw, 2rem) 0 clamp(2rem, 6vw, 3rem);
}

.book-track {
  display: flex;
  overflow-x: auto;
  overflow-y: auto;
  scroll-snap-type: x mandatory;
  gap: clamp(1.5rem, 5vw, 3.5rem);
  align-items: center;
  padding: 5.5rem clamp(20vw, 32vw, 38vw) 1.5rem;
  scrollbar-width: none;
}

.book-track::-webkit-scrollbar {
  display: none;
}

.book-slide {
  flex: 0 0 auto;
  scroll-snap-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  transform: translateY(calc(var(--lift, 0) * -1px)) scale(var(--scale, 1));
  /* Always fully opaque — only scale/lift interpolate with scroll distance. */
  opacity: 1;
  transition: transform 0.35s ease;
}

/* Height + aspect-ratio live on the cover itself, not the slide: the slide
   is a flex column that also holds the caption below, and giving the ratio
   to the slide's own box (shared with the caption's variable height) made
   the cover's *rendered* box drift away from the image's real ratio by
   however much vertical space that book's caption happened to take —
   worse the more lines its title wrapped to. object-fit: cover then
   quietly cropped the difference on every single cover. Sizing the cover
   independently of the caption means it always renders at exactly the
   persisted ratio, regardless of caption height. */
.book-cover {
  position: relative;
  height: min(56vh, 540px);
  aspect-ratio: var(--ratio, 0.7);
  border-radius: 0px 6px 6px 0px;
  box-shadow: inset 0 0 0 1px var(--color-border-hover), 0 25px 55px color-mix(in srgb, var(--color-black) 50%, transparent), 0 4px 12px color-mix(in srgb, var(--color-black) 35%, transparent);
  cursor: pointer;
  overflow: hidden;
}

.book-cover img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Spine overlay: a banded gradient standing in for the bound spine's
   rounded cylinder (soft, continuous light falloff — no hard steps, so
   it reads as a curved surface rather than flat stripes). The cover's own
   inset border (see .book-cover box-shadow) already reads as the crease
   where the spine folds into the cover, so no separate joint line is
   layered on top of it. */
.book-cover__band {
  position: absolute;
  inset: 0 auto 0 0;
  pointer-events: none;
  width: 8%;
  max-width:30px;
  background: linear-gradient(
    to right,
    transparent 0%,
    transparent 10%,
    color-mix(in srgb, var(--color-black) 40%, transparent) 45%, transparent 60%
  );
}

/* "Flexible" cover profile (coverType: 'flexible'): the paperback/softcover
   variant — same geometry as the rigid default, only lighter shadowing. The
   drop-shadow layers and the spine band's gradient are dialed down so the book
   reads as a limp, un-embossed cover rather than a hardbound one. Every other
   property (blur radii, offsets, stop positions, the inset crease border) is
   inherited unchanged from .book-cover / .book-cover__band. Placed after the
   base rules so these equal-specificity overrides win. The modifier classes are
   added by book-shelf.js when a book's coverType is 'flexible'; rigid books get
   no extra class. */
.book-cover--flexible {
  box-shadow: inset 0 0 0 1px var(--color-border-hover), 0 25px 55px color-mix(in srgb, var(--color-black) 32%, transparent), 0 4px 12px color-mix(in srgb, var(--color-black) 22%, transparent);
}

.book-cover__band--flexible {
  background: linear-gradient(
    to right,
    transparent 0%,
    transparent 10%,
    color-mix(in srgb, #999 22%, transparent)  45%, transparent 50%);
}

.book-slide__caption {
  text-align: center;
  margin: 1.1rem 0 0;
  max-width: 22ch;
}

.book-slide__title {
  display: block;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 1.0625rem;
  line-height: 1.3;
  color: var(--color-ink);
}

.book-shelf__arrow {
  position: absolute;
  top: 38%;
  transform: translateY(-50%);
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 50%;
  border: 1px solid var(--color-border-hover);
  background: var(--color-ink-muted);
  backdrop-filter: blur(6px);
  color: var(--color-cream);
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 5;
}

.book-shelf__arrow--prev { left: 1rem; }
.book-shelf__arrow--next { right: 1rem; }

.book-shelf__dots {
  display: flex;
  justify-content: center;
  gap: 0.5rem;
  margin-top: 1.75rem;
}

.book-shelf__dots button {
  position: relative;
  width: 0.4rem;
  height: 0.4rem;
  border-radius: 1rem;
  border: none;
  background: var(--color-border-hover);
  cursor: pointer;
  padding: 0;
  transition: width 0.3s ease, background 0.3s ease;
}

/* The visible dot stays tiny by design, but a 6.4px tap target is nearly
   unhittable on a touchscreen — this invisible pseudo-element enlarges the
   actual hit area without affecting layout or appearance. Kept small enough
   (0.25rem each side) that neighboring dots' hit areas never overlap given
   the 0.5rem gap between them. */
.book-shelf__dots button::before {
  content: '';
  position: absolute;
  inset: -0.25rem;
}

.book-shelf__dots button.is-active {
  width: 1.5rem;
  background: var(--color-gold);
}

@media (max-width: 719px) {
  /* Mobile drops the carousel entirely: instead of a horizontal scroll-snap
     row the covers stack in normal vertical document flow, one card after
     another, and the page scrolls through them like any list. No arrows, no
     dots, no scroll-driven scale/lift (the JS listeners are gated off below
     via matchMedia). Top padding is trimmed since there's no lift headroom to
     reserve anymore. */
  .book-track {
    display: flex;
    flex-direction: column;
    align-items: center;
    overflow: visible;
    scroll-snap-type: none;
    padding: 1.5rem 0;
    gap: clamp(2rem, 8vw, 3rem);
  }

  .book-slide {
    flex: 0 0 auto;
    width: 100%;
    scroll-snap-align: none;
    /* No centered-slide concept in a vertical list: every cover renders at
       full size, un-lifted, regardless of any stale inline --scale/--lift the
       JS may have left before the media query gated it off. */
    transform: none;
  }

  /* Sized by width, not height: the base rule (see .book-cover above) drives
     size from height + aspect-ratio, so simply adding max-width would leave
     height fixed and force the box out of ratio with the image, and
     object-fit: cover would crop to fill the mismatched box. Driving from
     width with height: auto keeps the box's ratio equal to the image's own
     --ratio at any size, so cover never has anything to crop. calc() caps
     the width at what the old height limit would have produced, so books
     with a narrower --ratio are unaffected. */
  .book-cover {
    height: auto;
    width: min(90vw, calc(min(46vh, 420px) * var(--ratio, 0.7)));
  }

  .book-shelf__arrow,
  .book-shelf__dots {
    display: none;
  }

  /* The desktop carousel's large negative margin-top exists so the centered,
     lifted/scaled slide can rise up into the section-quote banner above it
     (see .book-shelf's comment). There's no lift effect in the mobile
     vertical stack, so that same pull just drags the first cover up under
     the quote text instead — cut it down to a small overlap that matches
     the trimmed top padding above. */
  .book-shelf {
    margin-top: -1.5rem;
  }
}

/* ==========================================================================
   Mural Shelf (Murales — carousel of asymmetric restoration-stain shapes,
   replaces .section-grid-wall on this page only)
   ========================================================================== */

.mural-shelf {
  padding-bottom: clamp(3rem, 8vw, 5rem);
  margin-top: -9.5rem;
  position: relative;
  z-index: 2;
}

.mural-shelf__viewport {
  position: relative;
  padding: clamp(1.25rem, 3vw, 2rem) 0 clamp(2rem, 6vw, 3rem);
}

.mural-track {
  display: flex;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  gap: clamp(2rem, 6vw, 4rem);
  align-items: center;
  /* Generous top padding: the centered slide scales up and lifts (see
     mural-shelf.js's updateEffects) enough to rise into the section-quote
     banner above — this is the room it rises through before crossing it. */
  padding: 11rem clamp(14vw, 26vw, 32vw) 1.5rem;
  scrollbar-width: none;
}

.mural-track::-webkit-scrollbar { display: none; }

.mural-slide {
  flex: 0 0 auto;
  scroll-snap-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  transform: translateY(calc(var(--lift, 0) * -1px)) scale(var(--scale, 1));
  transition: transform 0.35s ease;
  cursor: pointer;
}

.mural-blob {
  width: min(70vw, 560px);
  height: min(60vh, 560px);
  /* The shadow lives here, on a plain box with no clip-path of its own:
     clip-path clips filter output too, so putting both on the same element
     (as .mural-blob__clip needs the clip-path) would clip the drop-shadow's
     ink away right along with the excess box — split across two elements is
     the only way to get a shadow that follows the irregular silhouette. */
  filter: drop-shadow(0 10px 20px color-mix(in srgb, var(--color-black) 22%, transparent))
          drop-shadow(0 3px 8px color-mix(in srgb, var(--color-black) 15%, transparent));
  transition: filter 0.35s ease;
}

/* Centered slide: raised above its neighbors (and, via the shelf's negative
   margin-top, above the section-quote banner it rises into) with a stronger
   shadow to sell the depth. */
.mural-slide.is-active {
  z-index: 4;
}

.mural-slide.is-active .mural-blob {
  filter: drop-shadow(0 14px 28px color-mix(in srgb, var(--color-black) 28%, transparent))
          drop-shadow(0 4px 10px color-mix(in srgb, var(--color-black) 18%, transparent));
}

.mural-blob__clip {
  position: relative;
  width: 100%;
  height: 100%;
  /* No whole-shape rotation: only the clip-path silhouette varies per slide,
     so the artwork inside always renders upright, never tilted. */
  /* clip-path set inline per slide — see mural-shelf.js's makeBlobClipPath */
}

.mural-blob img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.mural-blob__vignette {
  position: absolute;
  inset: 0;
  pointer-events: none;
  box-shadow: inset 0 0 50px color-mix(in srgb, var(--color-veil) 35%, transparent);
}

.mural-slide__caption {
  text-align: center;
  margin: 1.25rem 0 0;
  max-width: 24ch;
}

.mural-slide__title {
  display: block;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 1.0625rem;
  line-height: 1.3;
  color: var(--color-ink);
}

.mural-slide__location {
  display: block;
  font-family: var(--font-body);
  font-size: 0.75rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-ink-muted);
  margin-top: 0.3rem;
}

/* Arrows + dots: same rule bodies as .book-shelf__arrow / .book-shelf__dots. */
.mural-shelf__arrow {
  position: absolute;
  top: 38%;
  transform: translateY(-50%);
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 50%;
  border: 1px solid var(--color-border-hover);
  background: var(--color-ink-muted);
  backdrop-filter: blur(6px);
  color: var(--color-cream);
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 5;
}

.mural-shelf__arrow--prev { left: 1rem; }
.mural-shelf__arrow--next { right: 1rem; }

.mural-shelf__dots {
  display: flex;
  justify-content: center;
  gap: 0.5rem;
  margin-top: 1.75rem;
}

.mural-shelf__dots button {
  position: relative;
  width: 0.4rem;
  height: 0.4rem;
  border-radius: 1rem;
  border: none;
  background: var(--color-border-hover);
  cursor: pointer;
  padding: 0;
  transition: width 0.3s ease, background 0.3s ease;
}

.mural-shelf__dots button::before {
  content: '';
  position: absolute;
  inset: -0.25rem;
}

.mural-shelf__dots button.is-active {
  width: 1.5rem;
  background: var(--color-gold);
}

@media (max-width: 719px) {
  /* Mobile drops the carousel entirely: the stain shapes stack in normal
     vertical document flow, one after another, and the page scrolls through
     them like any list. No arrows, no dots, no scroll-driven scale/lift (the
     JS listeners are gated off below via matchMedia). */
  .mural-track {
    display: flex;
    flex-direction: column;
    align-items: center;
    overflow: visible;
    scroll-snap-type: none;
    padding: 1.5rem 0;
    gap: clamp(1.5rem, 6vw, 2.5rem);
  }

  .mural-slide {
    flex: 0 0 auto;
    width: 100%;
    scroll-snap-align: none;
    /* No centered-slide concept in a vertical list: every blob renders at full
       size, un-lifted, regardless of any stale inline --scale/--lift left by
       the JS before the media query gated it off. */
    transform: none;
  }

  .mural-blob {
    width: min(84vw, 400px);
    height: min(48vh, 440px);
  }

  .mural-shelf__arrow,
  .mural-shelf__dots {
    display: none;
  }

  /* Same reasoning as .book-shelf's mobile override: the desktop negative
     margin-top exists to let the centered, lifted/scaled slide rise into the
     section-quote banner — with no lift effect in the mobile vertical stack
     it just overlaps the quote text instead. */
  .mural-shelf {
    margin-top: -1.5rem;
  }
}

/* ==========================================================================
   Footer
   ========================================================================== */

.site-footer {
  /* DEFAULT = CHIARO. Nella maggior parte delle pagine (dipinti/digitali/
     disegni/libri/murales/privacy) il footer arriva subito dopo una zona
     CHIARA (section-content, book-shelf, mural-shelf, legal-page — tutte
     trasparenti sul campo chiaro globale). Lì il footer deve restare chiaro e
     continuare quel campo, senza una banda scura che spezzi lo spotlight: è
     quindi trasparente (lascia trasparire body::before) e usa testo scuro.
     La variante scura (.site-footer--dark, solo home) è definita più sotto. */
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: 0.75rem 1.5rem;
  padding: 1.75rem 1.25rem;
  position: relative;
  background: transparent;
  border-top: 1px solid var(--color-border);
  font-size: 0.75rem;
  color: var(--color-ink-muted);
}

/* Variante SCURA — solo la home, dove il footer segue la .contacts-section
   scura: ne è la continuazione. Stessa base #1a1613 e stesso tepore
   desaturato; il bordo superiore resta base pura (macchia centrata in basso),
   così combacia col bordo inferiore dei contatti — un'unica area scura. */
.site-footer--dark {
  background:
    radial-gradient(150% 150% at 20% 85%,
      color-mix(in srgb, var(--spot-warm) 38%, transparent) 0%,
      transparent 62%),
    #1a1613;
  border-top: 1px solid color-mix(in srgb, var(--color-cream) 10%, transparent);
  color: var(--color-cream-faint);
}

@media (min-width: 640px) {
  .site-footer { padding: 2rem 3.5rem; }
}

.footer-admin-link {
  color: var(--color-ink-muted);
  transition: color 0.25s;
}

.footer-admin-link:hover { color: var(--color-ink); }

.site-footer--dark .footer-admin-link { color: var(--color-cream-faint); }
.site-footer--dark .footer-admin-link:hover { color: var(--color-cream); }

.footer-contacts {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem;
  order: 3;
  width: 100%;
}

@media (min-width: 640px) {
  .footer-contacts { order: 2; width: auto; }
}

.footer-contacts__link[hidden] {
  display: none;
}

.footer-contacts__link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--color-ink-muted);
  transition: color 0.25s;
}

.footer-contacts__link:hover,
.footer-contacts__link:focus-visible { color: var(--color-gold); }

.site-footer--dark .footer-contacts__link { color: var(--color-cream-faint); }

.footer-contacts__link svg {
  width: 1.2rem;
  height: 1.2rem;
  display: block;
}

/* ==========================================================================
   Admin (preserved & enhanced)
   ========================================================================== */

.admin-page .site-header,
.admin-page .grain-overlay { display: none; }

.admin-page {
  /* Il pannello admin è disegnato per testo cream su superfici scure. Il tema
     pubblico ora è chiaro (:root e /theme.css servono valori chiari), quindi qui
     ripristiniamo localmente la palette scura originale — l'admin resta identico
     a prima. Il tab "Palette colori" mostra comunque i valori (chiari) del tema
     salvato: sono dati letti dal server, non influenzati da questo scope. */
  --color-bg: #1a1613;
  --color-surface: #221d18;
  --color-ink: #f2ede4;
  --color-ink-muted: rgba(242, 237, 228, 0.6);
  --color-border: rgba(242, 237, 228, 0.08);
  --color-border-hover: rgba(242, 237, 228, 0.18);
  padding-top: 0;

  /* Glass / liquid-glass design tokens — scoped to the admin panel only. */
  --glass-bg: color-mix(in srgb, var(--color-surface) 62%, transparent);
  --glass-bg-strong: color-mix(in srgb, var(--color-surface) 82%, transparent);
  --glass-border: rgba(242, 237, 228, 0.14);
  --glass-highlight: rgba(242, 237, 228, 0.06); /* inner top light edge */
  --glass-blur: 18px;
  --glass-radius: 16px;
  --glass-radius-sm: 10px;
  --glass-shadow: 0 8px 32px rgba(0, 0, 0, 0.36);

  /* Soft warm mesh behind the glass surfaces. background-attachment: fixed so
     the blur has something to pick up while scrolling. */
  background:
    radial-gradient(60rem 60rem at 12% -10%, color-mix(in srgb, var(--color-gold) 10%, transparent), transparent 60%),
    radial-gradient(50rem 50rem at 110% 20%, color-mix(in srgb, var(--color-gold) 7%, transparent), transparent 55%),
    radial-gradient(70rem 70rem at 50% 120%, rgba(60, 45, 30, 0.5), transparent 60%),
    var(--color-bg);
  background-attachment: fixed;
}

.admin-page .header {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: 0.5rem 1rem;
  padding: 1.25rem 2rem;
  position: sticky;
  top: 0;
  z-index: 100;
  border-bottom: 1px solid var(--glass-border);
  background: var(--glass-bg-strong);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
  box-shadow: inset 0 1px 0 var(--glass-highlight);
}

.admin-page .header h1 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.5rem;
}

.header-actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  justify-content: flex-end;
  flex: 1 1 auto;
  min-width: 0;
  gap: 0.5rem 1.25rem;
}
.header-actions .secondary {
  width: auto;
  margin-top: 0;
}

.admin-link {
  color: var(--color-cream-muted);
  text-decoration: none;
  font-size: 0.9rem;
}
.admin-link:hover { color: var(--color-gold); }

.admin-main {
  max-width: 1200px;
  margin: 0 auto;
  padding: 2rem 1.5rem;
}

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 1.5rem;
}

.card {
  background: var(--glass-bg);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
  border: 1px solid var(--glass-border);
  border-radius: var(--glass-radius);
  box-shadow: var(--glass-shadow), inset 0 1px 0 var(--glass-highlight);
  overflow: hidden;
}

.project-card img,
.admin-project-card img {
  width: 100%;
  aspect-ratio: 4/3;
  object-fit: cover;
  display: block;
}

.project-card .info,
.admin-project-card .info { padding: 1rem; }

.project-card h3,
.admin-project-card h3 {
  margin: 0 0 0.5rem;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.1rem;
}

.project-card p,
.admin-project-card p {
  margin: 0;
  color: var(--color-cream-muted);
  font-size: 0.9rem;
}

.project-card .category-badge,
.admin-project-card .category-badge {
  display: inline-block;
  font-size: 0.65rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--color-gold);
  margin-bottom: 0.35rem;
}

.admin-project-card .featured-badge {
  margin-left: 0.5rem;
  color: var(--color-cream);
  background: var(--color-gold);
  padding: 0.1rem 0.5rem;
  border-radius: 999px;
}

.admin-main .card { padding: 1.5rem; margin-bottom: 1rem; }

.admin-toolbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
}
.admin-toolbar h2 { margin: 0; font-family: var(--font-display); font-weight: 700; font-size: 1.25rem; }
.admin-toolbar-actions { display: flex; gap: 0.75rem; flex-wrap: wrap; }
.admin-toolbar-actions button { width: auto; margin-top: 0; }

.admin-projects { margin-bottom: 1.5rem; }

.admin-project-card .info {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.card-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 0.25rem;
}

.card-actions .order-btn,
.delete-btn, .edit-btn, .feature-btn {
  /* min-width 6rem so labels never overflow the ~280px card: when they don't
     fit side by side they wrap onto the next line instead of spilling out. */
  flex: 1 1 6rem;
  min-width: 0;
  width: auto;
  align-self: stretch;
  background: var(--glass-bg);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  font-weight: 500;
  padding: 0.5rem 0.5rem;
  border-radius: var(--glass-radius-sm);
  cursor: pointer;
  font-family: var(--font-body);
  font-size: 0.8rem;
  text-align: center;
  line-height: 1.2;
  white-space: nowrap;
  transition: background var(--transition-base), border-color var(--transition-base), color var(--transition-base);
}

.card-actions .order-btn {
  flex: 0 0 2.75rem;
}

.delete-btn {
  color: var(--color-danger);
  border: 1px solid #7f1d1d;
}
.delete-btn:hover { background: #450a0a; }

.edit-btn {
  color: var(--color-info);
  border: 1px solid #1e3a8a;
}
.edit-btn:hover { background: #1e3a5f; }

.feature-btn {
  color: var(--color-gold);
  border: 1px solid var(--color-gold);
}
.feature-btn:hover { background: rgba(212, 175, 55, 0.15); }

.modal-overlay {
  position: fixed;
  inset: 0;
  z-index: 500;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1.5rem;
  background: color-mix(in srgb, var(--color-black) 60%, transparent);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  overflow-y: auto;
}

.modal {
  /* Rich edit modals go nearly full-screen; small modals opt back down with
     .modal--sm. Strong glass surface so the blurred overlay reads behind it. */
  width: min(1100px, 94vw);
  max-width: none;
  max-height: 92vh;
  overflow-y: auto;
  margin: auto;
  background: var(--glass-bg-strong);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
  border: 1px solid var(--glass-border);
  border-radius: var(--glass-radius);
  box-shadow: var(--glass-shadow), inset 0 1px 0 var(--glass-highlight);
}

.modal--sm { width: min(480px, 94vw); }

.admin-upload-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
  margin-bottom: 1rem;
}
.admin-upload-header h2 { margin: 0; font-family: var(--font-display); font-weight: 700; font-size: 1.25rem; }
.admin-upload-header button { width: auto; margin-top: 0; }

.edit-image-wrap {
  position: relative;
  margin-bottom: 1.25rem;
  cursor: pointer;
}
.edit-image-preview {
  width: 100%;
  aspect-ratio: 4/3;
  object-fit: cover;
  display: block;
  border-radius: 2px;
  border: 1px solid var(--color-border);
  transition: opacity 0.15s ease;
}
.edit-image-wrap:hover .edit-image-preview { opacity: 0.75; }
.edit-image-hint {
  display: block;
  margin-top: 0.4rem;
  font-size: 0.75rem;
  color: var(--color-cream-muted);
  text-align: center;
}

.crop-modal {
  width: min(1200px, 96vw);
  height: min(900px, 94vh);
  max-height: 94vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* Edit modals (Modifica progetto / Modifica sezione): preview left, form right
   on wide viewports; single stacked column below ~860px available width. */
.edit-modal-body {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
  gap: 1.75rem;
  align-items: start;
}

.edit-modal-media { min-width: 0; }
.edit-modal-media .edit-image-wrap { margin-bottom: 0.75rem; }
.edit-modal-media .edit-image-preview { aspect-ratio: 3/4; }

@media (max-width: 860px) {
  .edit-modal-body { grid-template-columns: 1fr; gap: 1.25rem; }
  .edit-modal-media .edit-image-preview { aspect-ratio: 4/3; }
}

/* "Chi sono" tab: profile photo left (capped width — it's a portrait
   headshot, not a banner, so it shouldn't stretch to the card's full
   width), text fields right. Single column below ~640px. */
.about-editor {
  display: grid;
  grid-template-columns: minmax(0, 260px) minmax(0, 1fr);
  gap: 1.75rem;
  align-items: start;
}

.about-editor-media { min-width: 0; }
.about-editor-media .edit-image-wrap { max-width: 260px; margin: 0 auto 0.75rem; }
.about-editor-media .edit-image-preview { aspect-ratio: 3/4; }

@media (max-width: 640px) {
  .about-editor { grid-template-columns: 1fr; gap: 1.25rem; }
  .about-editor-media .edit-image-wrap { max-width: 320px; }
}

.crop-variant-toggle {
  display: flex;
  gap: 0.5rem;
  margin-bottom: 0.75rem;
  flex: 0 0 auto;
}
.crop-variant-toggle.hidden { display: none; }

.crop-variant-btn {
  width: auto;
  margin-top: 0;
  background: var(--glass-bg);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  border: 1px solid var(--glass-border);
  border-radius: var(--glass-radius-sm);
  color: var(--color-cream-muted);
  cursor: pointer;
  padding: 0.4rem 0.9rem;
  font-family: var(--font-body);
  font-size: 0.85rem;
  font-weight: 500;
  transition: background var(--transition-base), border-color var(--transition-base), color var(--transition-base);
}

.crop-variant-btn:hover { color: var(--color-cream); border-color: var(--color-border-hover); }

.crop-variant-btn.is-active {
  color: var(--color-bg);
  background: var(--color-gold);
  border-color: var(--color-gold);
}

.crop-stage {
  display: flex;
  justify-content: center;
  align-items: center;
  background: var(--color-black);
  border-radius: 2px;
  padding: 0.5rem;
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
}

.crop-image-wrap {
  position: relative;
  display: inline-block;
  max-width: 100%;
  max-height: 100%;
  line-height: 0;
}

.crop-image-wrap img {
  display: block;
  max-width: 100%;
  max-height: calc(min(900px, 94vh) - 12rem);
  user-select: none;
  -webkit-user-drag: none;
}

.crop-box {
  position: absolute;
  border: 2px solid var(--color-gold);
  box-shadow: 0 0 0 9999px color-mix(in srgb, var(--color-black) 55%, transparent);
  cursor: move;
  touch-action: none;
}

.crop-handle {
  position: absolute;
  width: 14px;
  height: 14px;
  background: var(--color-gold);
  border-radius: 50%;
  touch-action: none;
}
.crop-handle--nw { top: -7px; left: -7px; cursor: nwse-resize; }
.crop-handle--ne { top: -7px; right: -7px; cursor: nesw-resize; }
.crop-handle--sw { bottom: -7px; left: -7px; cursor: nesw-resize; }
.crop-handle--se { bottom: -7px; right: -7px; cursor: nwse-resize; }

.crop-actions {
  display: flex;
  gap: 0.75rem;
  margin-top: 1rem;
  flex: 0 0 auto;
}
.crop-actions button { width: auto; margin-top: 0; }

form label {
  display: block;
  margin-bottom: 1rem;
  font-size: 0.9rem;
  color: var(--color-cream-muted);
}

form label.field-checkbox {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

form label.field-checkbox input[type="checkbox"] {
  display: inline-block;
  width: auto;
  margin-top: 0;
}

input, textarea, select, button {
  display: block;
  width: 100%;
  margin-top: 0.35rem;
  padding: 0.6rem 0.75rem;
  border-radius: 2px;
  border: 1px solid var(--color-border);
  background: var(--color-bg);
  color: var(--color-cream);
  font: inherit;
  /* Every admin form field is wrapped in a <label> (font-size: 0.9rem, see
     "form label" below), and font:inherit was pulling that in — anything
     under 16px makes iOS Safari zoom the whole page in on focus. Pin text
     inputs back to 16px regardless of the wrapping label's size. */
  font-size: 1rem;
}

select { cursor: pointer; }

button[type="submit"],
.admin-toolbar-actions button:not(.secondary) {
  cursor: pointer;
  background: var(--color-gold);
  color: var(--color-bg);
  border: none;
  font-weight: 500;
  padding: 0.65rem 1.25rem;
}
button[type="submit"]:hover,
.admin-toolbar-actions button:not(.secondary):hover { opacity: 0.9; }

button.secondary {
  background: var(--glass-bg);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  color: var(--color-cream-muted);
  border: 1px solid var(--glass-border);
  border-radius: var(--glass-radius-sm);
  transition: background var(--transition-base), border-color var(--transition-base), color var(--transition-base);
}
button.secondary:hover { border-color: var(--color-border-hover); color: var(--color-cream); }

/* Admin form fields — translucent glass surface, gold focus. Scoped to
   .admin-page so the light public forms (.contact-form) are untouched.
   font-size stays 1rem (set by the global rule) to avoid iOS focus zoom. */
.admin-page input,
.admin-page textarea,
.admin-page select {
  background: color-mix(in srgb, var(--color-bg) 55%, transparent);
  border: 1px solid var(--glass-border);
  border-radius: var(--glass-radius-sm);
  transition: border-color var(--transition-base), background var(--transition-base), box-shadow var(--transition-base);
}

.admin-page input:focus,
.admin-page textarea:focus,
.admin-page select:focus {
  outline: none;
  border-color: color-mix(in srgb, var(--color-gold) 60%, transparent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-gold) 14%, transparent);
}

/* Primary CTAs stay solid gold; just harmonise the radius with the new look. */
.admin-page button[type="submit"],
.admin-page .admin-toolbar-actions button:not(.secondary),
.admin-page .seq-timing-save-btn,
.admin-page .crop-actions button:not(.secondary) {
  border-radius: var(--glass-radius-sm);
}

button.link-button {
  display: inline;
  width: auto;
  margin-top: 0.75rem;
  padding: 0;
  background: none;
  border: none;
  color: var(--color-gold);
  text-decoration: underline;
  font-size: 0.85rem;
  cursor: pointer;
}

.empty { text-align: center; color: var(--color-cream-muted); }
.error { color: var(--color-danger); font-size: 0.9rem; }
.success { color: var(--color-success); font-size: 0.9rem; }

/* Busy feedback — driven by setBusy()/setStatusBusy() in admin.js during slow
   async ops (Supabase uploads, server-side sharp crops). Indeterminate on
   purpose: those calls expose no real progress, so we only signal "working". */
@keyframes spin { to { transform: rotate(360deg); } }

.btn-spinner {
  display: inline-block;
  width: 0.85em;
  height: 0.85em;
  margin-right: 0.5em;
  vertical-align: -0.12em;
  border: 2px solid color-mix(in srgb, currentColor 28%, transparent);
  border-top-color: currentColor;
  border-radius: 50%;
  animation: spin 0.7s linear infinite;
}

/* A button mid-operation reads as inert without swapping its colours. */
button[aria-busy="true"] { cursor: default; opacity: 0.75; }

/* Gallery "+ Aggiungi immagine" is a <label> wrapping a file input, so it can't
   host a swapped-in .btn-spinner without losing the input's event listener —
   drive its spinner from a pseudo-element instead. */
.edit-gallery__add.is-busy {
  cursor: default;
  opacity: 0.75;
  pointer-events: none;
}
.edit-gallery__add.is-busy::before {
  content: "";
  display: inline-block;
  width: 0.85em;
  height: 0.85em;
  margin-right: 0.5em;
  vertical-align: -0.12em;
  border: 2px solid color-mix(in srgb, currentColor 28%, transparent);
  border-top-color: currentColor;
  border-radius: 50%;
  animation: spin 0.7s linear infinite;
}

@media (prefers-reduced-motion: reduce) {
  .btn-spinner,
  .edit-gallery__add.is-busy::before { animation-duration: 1.8s; }
}

.field-hint {
  display: block;
  margin-top: 0.35rem;
  font-size: 0.75rem;
  color: var(--color-cream-muted);
}

.hidden-input { display: none; }

/* ==========================================================================
   Admin — tabs, sections editor, hero editor
   ========================================================================== */

.admin-tabs {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  flex-wrap: wrap;
  margin-bottom: 1.5rem;
  padding: 0.4rem;
  border: 1px solid var(--glass-border);
  border-radius: var(--glass-radius);
  background: var(--glass-bg);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
  box-shadow: inset 0 1px 0 var(--glass-highlight);
}

.admin-tab {
  width: auto;
  margin-top: 0;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 999px;
  color: var(--color-cream-muted);
  cursor: pointer;
  padding: 0.5rem 1.1rem;
  font-family: var(--font-body);
  font-weight: 500;
  transition: background var(--transition-base), border-color var(--transition-base), color var(--transition-base);
}

.admin-tab:hover { color: var(--color-cream); border-color: var(--color-border-hover); }

.admin-tab.is-active {
  color: var(--color-bg);
  background: var(--color-gold);
  border-color: var(--color-gold);
}

.admin-tab-panel.hidden { display: none; }

/* ==========================================================================
   Admin — ITA/ENG content language toggle
   ========================================================================== */

.admin-lang-toggle {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.3rem;
  border: 1px solid var(--glass-border);
  border-radius: 999px;
  background: var(--glass-bg);
}

.admin-lang-toggle__btn {
  width: auto;
  margin-top: 0;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 999px;
  color: var(--color-cream-muted);
  cursor: pointer;
  padding: 0.35rem 0.9rem;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 0.8rem;
  letter-spacing: 0.04em;
  transition: background var(--transition-base), border-color var(--transition-base), color var(--transition-base);
}

.admin-lang-toggle__btn:hover { color: var(--color-cream); border-color: var(--color-border-hover); }

.admin-lang-toggle__btn.is-active {
  color: var(--color-bg);
  background: var(--color-gold);
  border-color: var(--color-gold);
}

.admin-lang-missing {
  font-size: 0.75rem;
  color: var(--color-gold);
  white-space: nowrap;
}

/* Sotto i 640px la riga "Admin + toggle lingua + Portfolio + Logout" non ci sta
   più in larghezza: si compattano i controlli e si lascia che le azioni vadano
   a capo, così la barra sticky non manda la pagina in overflow orizzontale. */
@media (max-width: 640px) {
  .admin-page .header { padding: 0.75rem 0.9rem; }
  .admin-page .header h1 { font-size: 1.15rem; }

  .header-actions { gap: 0.5rem 0.6rem; }
  .header-actions .secondary {
    padding: 0.45rem 0.7rem;
    font-size: 0.8rem;
  }

  .admin-link { font-size: 0.8rem; }

  .admin-lang-toggle { gap: 0.2rem; padding: 0.2rem; }
  .admin-lang-toggle__btn { padding: 0.3rem 0.6rem; font-size: 0.75rem; }
  .admin-lang-missing { font-size: 0.7rem; padding-right: 0.25rem; }
}

/* Marks which language an admin form field belongs to; see admin.js
   setAdminLang() for the body.admin-lang-en toggle these respond to. */
.lang-pill {
  display: inline-block;
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--color-bg);
  background: var(--color-gold);
  border-radius: 999px;
  padding: 0.05rem 0.4rem;
  margin-left: 0.35rem;
  vertical-align: middle;
}

.lang-en input,
.lang-en textarea {
  border-left: 3px solid var(--color-gold);
}

.lang-empty-hint { color: var(--color-gold); }

/* Both languages are always loaded and always submitted (see admin.js) —
   this only controls which one is visible on screen. */
body:not(.admin-lang-en) .lang-en { display: none; }
body.admin-lang-en .lang-it { display: none; }

.admin-section-summary {
  width: 100%;
  margin-bottom: 1.5rem;
}

.admin-section-summary img { aspect-ratio: 16/9; max-height: 200px; }

.admin-section-summary:empty { display: none; }

.admin-sequence { padding: 1.25rem; margin-bottom: 1.5rem; }
.admin-sequence--hidden { opacity: 0.6; }

.seq-hidden-badge {
  display: inline-block;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: 0.15rem 0.5rem;
  border-radius: 999px;
  background: var(--color-veil, rgba(0, 0, 0, 0.1));
  vertical-align: middle;
}

.admin-sequence__header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 1rem;
}

.admin-sequence__header h3 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.05rem;
  text-transform: capitalize;
}

.admin-sequence__actions {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
}
.admin-sequence__actions button { width: auto; margin-top: 0; }

.admin-sequence__timing {
  display: flex;
  align-items: flex-end;
  gap: 0.75rem;
  flex-wrap: wrap;
  margin-bottom: 1rem;
  padding: 0.75rem;
  border: 1px solid var(--color-border);
  border-radius: 2px;
  background: var(--color-bg);
}

.admin-sequence__timing .timing-field {
  margin: 0;
  font-size: 0.75rem;
  color: var(--color-cream-muted);
}

.admin-sequence__timing input {
  width: 7rem;
  margin-top: 0.35rem;
}

.admin-sequence__timing .seq-timing-save-btn {
  width: auto;
  margin-top: 0;
  cursor: pointer;
  background: var(--color-gold);
  color: var(--color-bg);
  border: none;
  font-weight: 500;
  padding: 0.6rem 1.1rem;
}
.admin-sequence__timing .seq-timing-save-btn:hover { opacity: 0.9; }

.seq-timing-msg {
  font-size: 0.85rem;
  align-self: center;
}
.seq-timing-msg.success { color: var(--color-success); }
.seq-timing-msg.error { color: var(--color-danger); }

.admin-sequence__frames {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: 1rem;
}

.admin-frame {
  border: 1px solid var(--color-border);
  border-radius: 2px;
  overflow: hidden;
  background: var(--color-bg);
}

.admin-frame img {
  width: 100%;
  aspect-ratio: 4/3;
  object-fit: cover;
  display: block;
}

.admin-frame__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  padding: 0.5rem;
}

.admin-frame__actions button,
.frame-file-label {
  width: auto;
  margin-top: 0;
  font-size: 0.75rem;
  padding: 0.4rem 0.6rem;
  /* .edit-btn/.delete-btn (reused here for Ritaglia/Elimina) set
     flex: 1 1 0; min-width: 0; white-space: normal for their original
     3-button card-actions row, which let them shrink below their text's
     width in this denser 5-item row — the overflowing text then painted
     over the next control, and clicks meant for it landed there instead. */
  flex: none;
  min-width: 0;
  white-space: nowrap;
}

.frame-file-label {
  display: inline-block;
  cursor: pointer;
  border-radius: 2px;
  border: 1px solid var(--color-border);
  color: var(--color-cream-muted);
  text-align: center;
}
.frame-file-label:hover { color: var(--color-cream); }

.admin-frame--add {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 120px;
  aspect-ratio: 4/3;
  cursor: pointer;
  color: var(--color-cream-muted);
  border: 1px dashed var(--color-border);
  text-align: center;
  font-size: 0.85rem;
  padding: 1rem;
}

/* Linked-image gallery manager (libri/murales edit form) */
.edit-gallery { margin-bottom: 1rem; }
.edit-gallery__title {
  margin: 0 0 0.75rem;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1rem;
}
.edit-gallery__list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 0.75rem;
  margin-bottom: 0.75rem;
}
.edit-gallery__empty {
  grid-column: 1 / -1;
  margin: 0;
  font-size: 0.85rem;
  color: var(--color-cream-muted);
}
.edit-gallery__add {
  width: 100%;
  padding: 0.6rem;
  font-size: 0.85rem;
}
.gallery-order-btn:disabled { opacity: 0.4; cursor: default; }

/* Glass fallback — browsers without backdrop-filter get opaque surfaces so
   text stays readable (scoped to admin so the public theme is untouched). */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .admin-page .header,
  .admin-page .modal { background: var(--color-surface); }
  .admin-page .card { background: var(--color-surface); }
  .admin-page .admin-tabs,
  .admin-page button.secondary,
  .admin-page .crop-variant-btn,
  .admin-page .card-actions .order-btn,
  .admin-page .card-actions .delete-btn,
  .admin-page .card-actions .edit-btn,
  .admin-page .card-actions .feature-btn { background: var(--color-surface); }
  .admin-page input,
  .admin-page textarea,
  .admin-page select { background: var(--color-bg); }
  .admin-page .modal-overlay { background: color-mix(in srgb, var(--color-black) 78%, transparent); }
}

/* Respect reduced-motion: drop the non-essential glass transitions. */
@media (prefers-reduced-motion: reduce) {
  .admin-page .admin-tab,
  .admin-page button.secondary,
  .admin-page .crop-variant-btn,
  .admin-page .card-actions .order-btn,
  .admin-page .card-actions .delete-btn,
  .admin-page .card-actions .edit-btn,
  .admin-page .card-actions .feature-btn,
  .admin-page input,
  .admin-page textarea,
  .admin-page select { transition: none; }
}

/* Palette editor (superadmin) */
.palette-group-title {
  margin: 1.75rem 0 0.5rem;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.95rem;
  color: var(--color-cream);
}
.palette-group-title:first-child { margin-top: 0; }

.palette-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.4rem 0;
  border-bottom: 1px solid var(--color-border);
}

.palette-label {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  font-size: 0.8rem;
  color: var(--color-cream-soft);
  text-transform: capitalize;
  overflow-wrap: break-word;
}

.palette-swatch {
  flex: none;
  width: 2.5rem;
  height: 2.25rem;
  margin-top: 0;
  padding: 0.15rem;
  cursor: pointer;
}

.palette-text {
  flex: 0 0 12rem;
  max-width: 12rem;
  margin-top: 0;
  font-family: var(--font-mono);
  font-size: 0.85rem;
}

@media (max-width: 480px) {
  .palette-row { flex-wrap: wrap; }
  .palette-text { flex-basis: 100%; max-width: none; }
}

/* User management (superadmin) */
.user-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
  padding: 0.75rem 0;
  border-bottom: 1px solid var(--color-border);
}
.user-row:last-child { border-bottom: none; }

.user-row__name {
  flex: 1 1 8rem;
  min-width: 0;
  font-family: var(--font-mono);
  font-size: 0.9rem;
  color: var(--color-cream);
  overflow-wrap: break-word;
}

.user-row__you {
  font-size: 0.7rem;
  color: var(--color-gold);
  margin-left: 0.4rem;
}

.user-row__role {
  flex: 0 0 auto;
  width: auto;
  margin-top: 0;
}

.user-row__actions {
  display: flex;
  gap: 0.4rem;
  flex-wrap: wrap;
}
.user-row__actions button { width: auto; margin-top: 0; }

/* ==========================================================================
   Legal pages (privacy, cookie policy)
   ========================================================================== */

.legal-page {
  max-width: 46rem;
  margin: 0 auto;
  padding: calc(var(--header-height) + 3rem) 1.25rem 4rem;
}

.legal-page__title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(2rem, 5vw, 2.75rem);
  margin: 0 0 0.5rem;
}

.legal-page__updated {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  color: var(--color-ink-muted);
  margin: 0 0 3rem;
}

.legal-page h2 {
  font-family: var(--font-display-accent);
  font-weight: 700;
  font-size: 1.25rem;
  color: var(--color-gold);
  margin: 2.5rem 0 1rem;
}

.legal-page h2:first-of-type { margin-top: 0; }

.legal-page p {
  color: var(--color-ink);
  margin: 0 0 1rem;
}

.legal-page ul {
  color: var(--color-ink);
  margin: 0 0 1rem;
  padding-left: 1.25rem;
}

.legal-page li { margin-bottom: 0.5rem; }

.legal-page a {
  color: var(--color-gold);
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

.legal-page a:hover { color: var(--color-ink); }

.legal-page strong { color: var(--color-ink); }

.legal-page__placeholder {
  background: var(--color-gold-soft);
  color: var(--color-ink);
  padding: 0.05em 0.35em;
  border-radius: 2px;
}
.admin-frame--add:hover { color: var(--color-gold); border-color: var(--color-gold); }
