.material-symbols-outlined { font-variation-settings: 'FILL' 0, 'wght' 300, 'GRAD' 0, 'opsz' 24; }
.text-underline-luxury { position: relative; }
.text-underline-luxury::after { content: ''; position: absolute; width: 100%; height: 1px; bottom: -2px; left: 0; background-color: #d9c1bb; transition: transform 0.3s ease; transform-origin: right; transform: scaleX(1); }
.text-underline-luxury:hover::after { transform: scaleX(0); transform-origin: left; }
.js .reveal { opacity: 0; transform: translateY(24px); transition: opacity 1s cubic-bezier(0.16, 1, 0.3, 1), transform 1s cubic-bezier(0.16, 1, 0.3, 1); }
.reveal.active { opacity: 1; transform: translateY(0); }
/* Full-bleed portfolio filmstrip: swipeable on mobile, hairline-free on desktop. */
.no-scrollbar::-webkit-scrollbar { display: none; }
.no-scrollbar { scrollbar-width: none; -ms-overflow-style: none; }
/* Crossfade between gallery categories (Novias/Producciones/Social/Famosos)
   instead of an instant cut — timing is driven from main.js's GALLERY_FADE_MS. */
.gallery-panel { transition: opacity 250ms ease; }
/* Tactile linen/paper grain over the cream canvas, per DESIGN.md's
   "textura de papelería o lino de gama alta" — fixed, non-interactive,
   low-opacity so it reads as material, not noise.
   No mix-blend-mode here: a blend mode on a viewport-fixed pseudo-element
   can force browsers to composite/blend other fixed layers (like the
   mobile menu) with page content behind them, making them look transparent
   on some mobile browsers. Plain opacity avoids that risk entirely. */
body::before {
    content: '';
    position: fixed;
    inset: 0;
    z-index: 1;
    pointer-events: none;
    opacity: 0.09;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='grain'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23grain)'/%3E%3C/svg%3E");
    background-size: 200px 200px;
}
/* Hard guarantee the fullscreen mobile menu is opaque, regardless of any
   Tailwind utility/compositing quirks elsewhere on the page. */
#mobile-menu {
    background-color: #fcf9f8;
    isolation: isolate;
}
/* The Tailwind CDN's forms plugin injects its own chevron background-image
   on <select> elements. We already render our own Material Symbols chevron
   next to it, which doubled up into a "WW" look — kill the plugin's one.
   ID selector so it wins regardless of the CDN's late style injection order. */
#whatsapp-modal-type {
    background-image: none;
}
/* Center a gallery category whose photos all fit on screen (Social on desktop)
   instead of leaving it flush left. The `safe` keyword is what does the work:
   it drops back to flush-left the moment the filmstrip overflows, so the first
   photos never get scrolled out of reach. */
.gallery-panel .flex.overflow-x-auto {
    justify-content: safe center;
}
/* Fallback for browsers without `safe` alignment (they discard the rule
   above): at minimum, a lone photo still gets centered. */
.gallery-panel .flex.overflow-x-auto:has(> :only-child) {
    justify-content: center;
}
/* Click-and-drag affordance for the gallery filmstrip (see main.js) —
   trackpad/wheel scrolling already works via native overflow-x-auto. */
.gallery-panel .overflow-x-auto {
    cursor: grab;
}
.gallery-panel .overflow-x-auto.is-dragging {
    cursor: grabbing;
    scroll-snap-type: none;
    user-select: none;
}
.gallery-panel .overflow-x-auto.is-dragging img {
    pointer-events: none;
}
