/* ===================================================================
   beachzoom.io — base CSS
   Built from the v2 desktop mockup. Covers home + beach screens.
   Mobile (<1400px) is deferred — see bottom of file for a placeholder.
   =================================================================== */

@import url('https://fonts.googleapis.com/css2?family=Bebas+Neue&family=Montserrat:wght@300;400;500;600;700&family=Pacifico&display=swap');

:root {
    --teal-light:   #93D6CD;
    --teal-bright:  #3FC8BD;
    --teal-deep:    #1A7E78;
    --cream:        #FAF2E1;
    --sand:         #F5E8C8;
    --gold:         #E8C766;
    --ink:          #1A1815;
    --ink-soft:     #2A2A2A;

    /* Pin / category palette */
    --rest: #F76659;
    --play: #1A8073;
    --stay: #1F4070;

    /* Type scale */
    --beach-name-size: 132px;
    --val-num-size:     65px;   /* 25% smaller than previous 87px (was 66% of beach-name) */
}

* { box-sizing: border-box; margin: 0; padding: 0; }

html, body {
    width: 100%;
    height: 100%;
    overflow: hidden;
    background: #0A1F2A;
    font-family: 'Montserrat', -apple-system, BlinkMacSystemFont, sans-serif;
    color: var(--cream);
    -webkit-font-smoothing: antialiased;
    user-select: none;
    -webkit-user-select: none;
    -webkit-tap-highlight-color: transparent;
}

button { font-family: inherit; cursor: pointer; border: none; background: none; color: inherit; padding: 0; }
a { color: inherit; text-decoration: none; }

/* ===================================================================
   SCREEN SYSTEM — home and beach swap places via .active
   =================================================================== */

.screen {
    position: fixed;
    inset: 0;
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.55s ease, visibility 0s linear 0.55s;
}
.screen.active {
    opacity: 1;
    visibility: visible;
    transition: opacity 0.55s ease, visibility 0s linear 0s;
}

/* ===================================================================
   SHARED — logo (top-left, both screens)
   =================================================================== */

.logo {
    position: absolute;
    top: 36px;
    left: 48px;
    z-index: 100;
    font-family: 'Montserrat', sans-serif;
    font-weight: 600;          /* SemiBold for "beachzoom" */
    font-size: 48px;
    letter-spacing: -0.01em;
    line-height: 1;
    color: var(--gold);
    text-shadow: 0 2px 12px rgba(0, 0, 0, 0.4);
    cursor: default;
}
.logo .io {
    font-family: 'Montserrat', sans-serif;
    font-weight: 400;          /* Regular for ".io" */
    font-size: 38px;
    margin-left: 2px;
    color: #FFFFFF;
}

/* Landing-page logo lives inside .home-content, above the slogan,
   so it shares the slogan's left edge and sits in the same vertical
   stack instead of being absolute-positioned to the corner. Sized
   at roughly half the slogan title — same clamp ratio applied. */
.home-logo {
    font-family: 'Montserrat', sans-serif;
    font-weight: 600;
    font-size: clamp(28px, 3.4vw, 56px);
    letter-spacing: -0.01em;
    line-height: 1;
    color: var(--gold);
    margin-bottom: 18px;
    text-shadow: 0 2px 12px rgba(0, 0, 0, 0.4);
}
.home-logo .io {
    font-family: 'Montserrat', sans-serif;
    font-weight: 400;
    color: #FFFFFF;
    margin-left: 2px;
}

/* ===================================================================
   SPLASH — initial loader with JS-driven progress bar
   =================================================================== */

#splash {
    position: fixed;
    inset: 0;
    z-index: 9999;
    background: linear-gradient(180deg, #1A7E78 0%, #0F2329 100%);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 24px;
    transition: opacity 0.6s ease;
}
#splash.hide { opacity: 0; pointer-events: none; }

#splash .splash-logo {
    font-family: 'Montserrat', sans-serif;
    font-weight: 600;
    font-size: 56px;
    color: var(--gold);
    line-height: 1;
}
#splash .splash-logo .io {
    font-family: 'Montserrat', sans-serif;
    font-weight: 400;
    color: var(--cream);
    margin-left: 2px;
    font-size: 44px;
}

/* Real progress bar — width driven by JS via #splash-bar-fill.style.width */
#splash .splash-bar {
    width: 320px;
    height: 4px;
    background: rgba(250, 242, 225, 0.18);
    border-radius: 2px;
    overflow: hidden;
    margin-top: 8px;
}
#splash .splash-bar-fill {
    height: 100%;
    width: 0%;
    background: var(--cream);
    border-radius: 2px;
    transition: width 0.45s cubic-bezier(.4, 0, .2, 1);
}

/* Stage message (left) + percentage (right), tabular numerals */
#splash .splash-meta {
    width: 320px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 16px;
    color: rgba(250, 242, 225, 0.75);
    font-family: 'Montserrat', sans-serif;
    font-size: 13px;
    font-weight: 500;
    letter-spacing: 0.04em;
}
#splash .splash-tag {
    flex: 1;
    text-align: left;
}
#splash .splash-pct {
    font-variant-numeric: tabular-nums;
    color: rgba(250, 242, 225, 0.95);
    min-width: 38px;
    text-align: right;
}

/* ===================================================================
   HOME SCREEN — full-bleed satellite map · hero copy over water
   The map fills the entire viewport. We use MapLibre setPadding (applied
   in initHomeMap) to push the camera's effective center into the right
   third — so Portugal renders on the right and open ocean fills the left.
   =================================================================== */

#screen-home {
    background: #0A1F2A;
}

/* Hero copy block — left side, vertically centered, over the water */
.home-content {
    position: absolute;
    left: 96px;
    top: 50%;
    transform: translateY(-50%);
    z-index: 10;
    max-width: 60vw;
    color: var(--cream);
    pointer-events: none;     /* let the map below receive drag/zoom */
}
.home-title {
    /* Montserrat Light overall, with <strong> bumping the emphasised
       phrase ("tua praia.") up to SemiBold. */
    font-family: 'Montserrat', sans-serif;
    font-weight: 300;
    font-size: clamp(56px, 6.8vw, 112px);
    /* Tracking: -20 in InDesign units = -20/1000 em = -0.02em. Pulls
       the letters in just enough to feel more deliberate without
       collapsing readability. */
    letter-spacing: -0.02em;
    /* Tighter line spacing as in the reference. */
    line-height: 0.94;
    color: var(--cream);
    margin-bottom: 24px;
    max-width: 16ch;
    /* The reference is sentence-case lowercase — guard against any
       inherited text-transform. */
    text-transform: none;
    text-shadow: 0 4px 24px rgba(0, 0, 0, 0.55), 0 2px 6px rgba(0, 0, 0, 0.40);
}
.home-title strong { font-weight: 600; }

.home-sub {
    font-family: 'Montserrat', sans-serif;
    font-weight: 300;
    font-size: clamp(18px, 1.4vw, 26px);
    letter-spacing: -0.02em;
    color: rgba(250, 242, 225, 0.92);
    line-height: 1.4;
    max-width: 56ch;
    text-transform: none;
    text-shadow: 0 2px 14px rgba(0, 0, 0, 0.55), 0 1px 4px rgba(0, 0, 0, 0.45);
}

/* Full-bleed Portugal satellite map */
#home-map {
    position: absolute;
    inset: 0;
    z-index: 1;
    background: #0A1F2A;
}

#home-map .maplibregl-canvas-container { background: transparent; }

/* Soft vignette to deepen the edges and lift the hero copy */
#screen-home::before {
    content: '';
    position: absolute;
    inset: 0;
    pointer-events: none;
    z-index: 2;
    background:
        linear-gradient(90deg, rgba(10,31,42,0.35) 0%, rgba(10,31,42,0.10) 35%, transparent 60%),
        radial-gradient(ellipse at 50% 50%, transparent 50%, rgba(0,0,0,0.30) 100%);
}

/* "Reset zoom" pill — top of the right third */
.reset-zoom {
    position: absolute;
    right: 24px;
    top: 36px;
    z-index: 30;
    background: rgba(15, 30, 28, 0.65);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
    color: rgba(250, 242, 225, 0.92);
    border: 1px solid rgba(255, 255, 255, 0.10);
    padding: 8px 16px;
    border-radius: 999px;
    font-family: 'Montserrat', sans-serif;
    font-size: 12px;
    font-weight: 500;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.25s ease;
}
.reset-zoom.shown { opacity: 1; pointer-events: auto; }

/* ===================================================================
   BEACH SCREEN — full-bleed 3D map with overlays
   =================================================================== */

#screen-beach {
    background: #0A2E38;
}

#beach-map {
    position: absolute;
    inset: 0;
    z-index: 1;
}

/* Soft vignette over the 3D scene */
.vignette {
    position: absolute;
    inset: 0;
    pointer-events: none;
    z-index: 2;
    background:
        radial-gradient(ellipse at 50% 100%, transparent 50%, rgba(0,0,0,0.30) 100%),
        radial-gradient(ellipse at 50% 0%,   transparent 60%, rgba(0,0,0,0.22) 100%);
}

/* ── Environment atmosphere overlays ───────────────────────── */
#env-layer {
    position: absolute;
    inset: 0;
    pointer-events: none;
    z-index: 10;   /* above vignette (2), below UI panels (18) */
}
#env-darkness,
#env-tint {
    position: absolute;
    inset: 0;
    transition: background 4s ease;   /* slow fade on minute update */
}
#env-fog {
    position: absolute;
    inset: 0;
    overflow: visible;
}
@keyframes envFogDrift {
    0%   { transform: translate(0, 0)       scale(1);    }
    33%  { transform: translate(4%, -3%)    scale(1.06); }
    66%  { transform: translate(-3%, 5%)    scale(0.97); }
    100% { transform: translate(0, 0)       scale(1);    }
}

/* ── Val-stack (vertical, bottom-left, transparent, flag last) ──── */
.val-stack {
    position: absolute;
    left: 48px;
    bottom: 400px;
    z-index: 18;
    display: flex;
    /* bzDesign Stage 10: orientation + background controllable via
       --bzd-stack-* vars without forking layout selectors. Defaults
       preserve current behaviour.
       NB: justify-content stayed at space-between and the container
       carried a fixed 304px height, which made gap a no-op (items
       were always pushed to the edges of a fixed-height container).
       Removed in #81 so the Icons-tab Internal margin slider drives
       real visual spacing. */
    /* CSS-variable layering (#100): Properties → Stack writes to
       --bzd-stack-prop-* which takes precedence; Icons-tab writes
       to --bzd-stack-*; static defaults fall through. Removes the
       dependence on DOM-injection order for resolving collisions
       between the two control surfaces. */
    flex-direction: var(--bzd-stack-prop-orient, var(--bzd-stack-orient, column));
    align-items:    var(--bzd-stack-align, stretch);
    gap:            var(--bzd-stack-prop-gap,    var(--bzd-stack-gap, 0));
    padding:        var(--bzd-stack-prop-pad,    var(--bzd-stack-pad, 0));
    background:     var(--bzd-stack-prop-bg,     var(--bzd-stack-bg, transparent));
    border-radius:  var(--bzd-stack-prop-radius, var(--bzd-stack-radius, 0));
    color: var(--cream);
    text-shadow: 0 3px 14px rgba(0, 0, 0, 0.55), 0 1px 4px rgba(0, 0, 0, 0.45);
    transition: opacity 0.32s ease;
}
body.popup-open .val-stack { opacity: 0.40; }

.val {
    display: flex;
    /* Icon-vs-digit order on each row, also bzDesign-controlled. */
    flex-direction: var(--bzd-stackitem-orient, row);
    /* Cross-axis alignment between icon and digits — bzDesign-controlled
       via Properties → Stack → Item cross-align. Default center keeps
       prior behaviour; baseline often reads better with tall-body
       display fonts like Bebas Neue. */
    align-items: var(--bzd-stackitem-align, center);
    gap: var(--bzd-stackitem-gap, 14px);
}
.val-ico {
    width: 41px;
    height: 41px;
    flex-shrink: 0;
    color: var(--cream);
    opacity: 0.92;
    filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.5));
}
.val-num {
    font-family: 'Bebas Neue', sans-serif;
    font-weight: 400;
    font-size: var(--val-num-size);
    /* line-height 1 (was 0.85) so the line box matches the visible
       cap height of Bebas Neue. With 0.85 the line box was shorter
       than the cap and flex align-items:center looked off because
       it centred the (smaller) line box, not the visible glyph. */
    line-height: 1;
    letter-spacing: 0.02em;
    color: var(--cream);
}
.val-num sup {
    font-size: 0.55em;
    margin-left: 2px;
    vertical-align: 0.42em;
    font-weight: 400;
}
.val-unit {
    font-family: 'Montserrat', sans-serif;
    font-size: 14px;
    font-weight: 500;
    color: rgba(250, 242, 225, 0.85);
    /* Net visible distance between .val-num and .val-unit = .val's
       flex gap + this margin. Subtracting --bzd-stackitem-gap here
       cancels out the row's flex gap so the SLIDER value
       (--bzd-stackitem-unit-gap) IS the visible gap independent of
       the icon ↔ digit setting. */
    margin-left: calc(var(--bzd-stackitem-unit-gap, 0px) - var(--bzd-stackitem-gap, 14px));
    /* align-self + margin-bottom retired — they used to bottom-anchor
       the unit ('km/h', 'UV') regardless of the row's cross-axis
       alignment, so picking center in Properties → Stack → Item
       cross-align silently didn't apply to .val-unit. Now the unit
       follows the row's align-items setting like every other child. */
}
.val.flag .flag-text {
    font-family: 'Bebas Neue', sans-serif;
    font-weight: 400;
    font-size: calc(var(--val-num-size) * 0.55);
    line-height: 0.95;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--cream);
}
.val.flag .flag-text small {
    display: block;
    font-family: 'Montserrat', sans-serif;
    font-size: 10px;
    font-weight: 500;
    letter-spacing: 0.18em;
    color: rgba(250, 242, 225, 0.70);
    margin-bottom: 3px;
    text-transform: uppercase;
}

/* ── Dedicated animated flag panel ─────────────────────────────── */
#flag-panel {
    position: absolute;
    left: 48px;
    bottom: 180px;
    z-index: 18;
}
.flag-panel {
    color: var(--cream);
}
.flag-panel-svg {
    width: 41px;
    height: 41px;
    display: block;
    color: var(--cream);
    opacity: 0.92;
    filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.5));
}

/* ── Beach label (region · name, transparent, bottom-left) ──────── */
.beach-label {
    position: absolute;
    left: 48px;
    bottom: 220px;
    z-index: 18;
    color: var(--cream);
    text-shadow: 0 4px 22px rgba(0, 0, 0, 0.65), 0 2px 6px rgba(0, 0, 0, 0.50);
}
.beach-label .sup {
    font-family: 'Montserrat', sans-serif;
    font-size: 34px;
    font-weight: 400;
    letter-spacing: 0.01em;
    color: rgba(250, 242, 225, 0.92);
    margin-top: 4px;
    display: flex;
    align-items: center;
    gap: 0.35em;
}
.beach-label .beach-loc-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1em;
    height: 1em;
    line-height: 1;
    font-size: 1em;
}
.beach-label .beach-loc-icon > svg,
.beach-label .beach-loc-icon > img {
    width: 100%;
    height: 100%;
}
.beach-label .beach-loc-icon .material-symbols-outlined {
    font-size: 1em;
    line-height: 1;
}
.beach-label .name {
    font-family: 'Bebas Neue', sans-serif;
    font-weight: 400;
    font-size: var(--beach-name-size);
    line-height: 0.88;
    letter-spacing: 0.005em;
    text-transform: uppercase;
    color: var(--cream);
}
/* renderBeach() wraps "da/de/do" suffixes in <em> for visual splitting;
   we want the same upright weight, no italic. */
.beach-label .name em {
    font-style: normal;
    font-weight: inherit;
}

/* ── Browse arrows (Bebas Neue, edge-anchored, never truncate) ──── */
.beach-nav {
    position: absolute;
    bottom: 130px;
    z-index: 18;
    color: var(--cream);
    font-family: 'Bebas Neue', sans-serif;
    font-weight: 400;
    font-size: 36px;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    text-shadow: 0 3px 14px rgba(0, 0, 0, 0.60), 0 1px 4px rgba(0, 0, 0, 0.50);
    display: flex;
    align-items: center;
    gap: 14px;
    line-height: 1;
    white-space: nowrap;
    cursor: pointer;
    transition: opacity 0.15s ease;
}
.beach-nav:hover { opacity: 0.85; }
.beach-nav.prev { left: 48px; }
.beach-nav.next { right: 48px; }
.beach-nav svg {
    width: 32px;
    height: 32px;
    stroke: currentColor;
    fill: none;
    stroke-width: 2.5;
    stroke-linecap: round;
    stroke-linejoin: round;
    filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.5));
}

/* ── Cat-stack (right side, vertically centered) ─────────────────── */
.cat-stack {
    position: absolute;
    right: 36px;
    top: 50%;
    transform: translateY(-50%);
    z-index: 18;
    display: flex;
    /* bzDesign Stage 10 + #100 — see .val-stack above for the var
       contract. Properties-tier vars (--bzd-stack-prop-*) win when set. */
    flex-direction: var(--bzd-stack-prop-orient, var(--bzd-stack-orient, column));
    align-items:    var(--bzd-stack-align, stretch);
    gap:            var(--bzd-stack-prop-gap,    var(--bzd-stack-gap, 14px));
    padding:        var(--bzd-stack-prop-pad,    var(--bzd-stack-pad, 0));
    background:     var(--bzd-stack-prop-bg,     var(--bzd-stack-bg, transparent));
    border-radius:  var(--bzd-stack-prop-radius, var(--bzd-stack-radius, 0));
}
.cat-btn {
    width: 60px;
    height: 60px;
    border-radius: 50%;
    color: #FFFFFF;             /* white icon, always */
    display: flex;
    align-items: var(--bzd-stackitem-align, center);
    justify-content: center;
    border: 1px solid rgba(255, 255, 255, 0.18);
    transition: transform 0.12s ease, box-shadow 0.18s ease, opacity 0.18s ease, filter 0.18s ease;
    position: relative;
}
.cat-btn:active { transform: scale(0.96); }
.cat-btn svg { width: 30px; height: 30px; }

/* Each category gets its color as the button background, regardless of toggle */
.cat-btn[data-cat="rest"] { background: var(--rest); box-shadow: 0 6px 18px rgba(247, 102, 89, 0.35); }
.cat-btn[data-cat="play"] { background: var(--play); box-shadow: 0 6px 18px rgba(26, 128, 115, 0.35); }
.cat-btn[data-cat="stay"] { background: var(--stay); box-shadow: 0 6px 18px rgba(31, 64, 112, 0.35); }

/* Active state: brighter halo + slightly larger */
.cat-btn.active { transform: scale(1.05); border-color: rgba(255, 255, 255, 0.35); }
.cat-btn[data-cat="rest"].active { box-shadow: 0 10px 28px rgba(247, 102, 89, 0.65), 0 0 0 3px rgba(247, 102, 89, 0.20); }
.cat-btn[data-cat="play"].active { box-shadow: 0 10px 28px rgba(26, 128, 115, 0.65), 0 0 0 3px rgba(26, 128, 115, 0.20); }
.cat-btn[data-cat="stay"].active { box-shadow: 0 10px 28px rgba(31, 64, 112, 0.65), 0 0 0 3px rgba(31, 64, 112, 0.20); }

/* Empty / error / pre-fetch (idle): hide the button so a beach with no
   businesses in a category never shows that icon. Buttons appear once
   preloadCategoriesForBeach() resolves them as 'ready' (or while still
   'pending' — the spinner ring is informative).
   Exempted on body.bzd-preview so the bzDesign editor can still select
   + style the cat-stack on pages where no live beach data is fetched. */
body:not(.bzd-preview) .cat-btn[data-state="idle"]:not(.ready):not(.pending):not(.active),
body:not(.bzd-preview) .cat-btn.empty,
body:not(.bzd-preview) .cat-btn.err   { display: none !important; }

/* Hide the whole stack when not a single button is showing, so its
   padding / background doesn't leave an empty rectangle. */
body:not(.bzd-preview) .cat-stack:not(:has(.cat-btn.ready)):not(:has(.cat-btn.pending)):not(:has(.cat-btn.active)) {
    display: none !important;
}

/* Cat-button progress ring (when preloading) */
.cat-btn .pending-ring {
    position: absolute;
    inset: -2px;
    border-radius: 50%;
    border: 2px solid transparent;
    border-top-color: var(--cream);
    opacity: 0;
    pointer-events: none;
    animation: spinRing 0.9s linear infinite;
}
.cat-btn[data-state="pending"] .pending-ring { opacity: 0.8; }
@keyframes spinRing { to { transform: rotate(360deg); } }

/* ── Cluster (bottom-right, transparent white icons) ─────────────── */
.cluster {
    position: absolute;
    right: 48px;
    bottom: 48px;
    z-index: 18;
    display: flex;
    /* bzDesign Stage 10 + #100 — same var contract as .val-stack /
       .cat-stack. Default orient is row because the cluster ships
       horizontal. */
    flex-direction: var(--bzd-stack-prop-orient, var(--bzd-stack-orient, row));
    align-items:    var(--bzd-stack-align, center);
    gap:            var(--bzd-stack-prop-gap,    var(--bzd-stack-gap, 18px));
    padding:        var(--bzd-stack-prop-pad,    var(--bzd-stack-pad, 0));
    background:     var(--bzd-stack-prop-bg,     var(--bzd-stack-bg, transparent));
    border-radius:  var(--bzd-stack-prop-radius, var(--bzd-stack-radius, 0));
}
.act {
    /* width hugs the icon (#101) so the Gap slider is the SOLE control
       of the distance between action icons — no fixed box padding
       inflating it. Height keeps the row tall for vertical alignment. */
    width: auto;
    height: 56px;
    background: transparent;
    color: var(--cream);
    display: flex;
    align-items: var(--bzd-stackitem-align, center);
    justify-content: center;
    transition: transform 0.1s ease, opacity 0.15s ease;
    opacity: 0.95;
}
.act:hover { opacity: 1; }
.act:active { transform: scale(0.94); }
.act svg {
    width: 44px;
    height: 44px;
    stroke: currentColor;
    fill: none;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
    filter: drop-shadow(0 3px 10px rgba(0, 0, 0, 0.55))
            drop-shadow(0 1px 3px rgba(0, 0, 0, 0.45));
}

/* ── Attribution (bottom-left, below browse arrow) ──────────────── */
.attribution {
    position: absolute;
    bottom: 22px;
    left: 48px;
    z-index: 18;
    color: rgba(250, 242, 225, 0.55);
    font-family: 'Montserrat', sans-serif;
    font-size: 13px;
    letter-spacing: 0.02em;
    text-shadow: 0 1px 4px rgba(0, 0, 0, 0.5);
}

/* ===================================================================
   PINS — classic teardrop HTML markers (used inside #beach-map)
   =================================================================== */

.bz-pin {
    --pin-color: var(--rest);
    --pin-w: 44px;
    --pin-h: 56px;
    width: var(--pin-w);
    height: var(--pin-h);
    cursor: pointer;
    color: var(--pin-color);
    transform-origin: 50% 100%;
    filter: drop-shadow(0 5px 10px rgba(0, 0, 0, 0.40))
            drop-shadow(0 1px 3px rgba(0, 0, 0, 0.28));
    transition: transform 0.32s cubic-bezier(.4, 0, .2, 1),
                opacity   0.32s ease,
                filter    0.32s ease;
    will-change: transform, opacity, filter;
}
.bz-pin-shape {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    display: block;
}
.bz-pin-shape path { fill: currentColor; }

.bz-pin-icon {
    position: absolute;
    top: 7px;
    left: 50%;
    transform: translateX(-50%);
    width: 28px;
    height: 28px;
    color: #FFFFFF;
    display: flex;
    align-items: center;
    justify-content: center;
}
.bz-pin-icon svg { width: 100%; height: 100%; display: block; }

.bz-pin-selected {
    transform: scale(1.55);
    z-index: 10;
    filter: drop-shadow(0 8px 16px rgba(0, 0, 0, 0.50))
            drop-shadow(0 2px 4px rgba(0, 0, 0, 0.35));
}
.bz-pin-faded {
    opacity: 0.45;
    filter: saturate(0.20) brightness(0.95);
    transform: scale(0.85);
}
.bz-pin-leaving {
    opacity: 0;
    transform: scale(0.4);
    pointer-events: none;
}

/* .bz-pin-inner — visual wrapper for selected/faded/entrance transforms.
   maplibre writes its position transform inline on .bz-pin, so we MUST
   animate this inner element to avoid clobbering the lng/lat position. */
.bz-pin-inner {
    position: absolute;
    inset: 0;
    transform-origin: 50% 100%;
    transition: transform 0.32s cubic-bezier(.4, 0, .2, 1),
                opacity   0.32s ease,
                filter    0.32s ease;
    will-change: transform, opacity, filter;
}
/* When pin enters selected/faded/leaving state, transform the inner
   wrapper (not .bz-pin — that would lose to maplibre's inline). */
.bz-pin-selected .bz-pin-inner { transform: scale(1.55); }
.bz-pin-faded    .bz-pin-inner { transform: scale(0.85); opacity: 0.45;
                                 filter: saturate(0.20) brightness(0.95); }
.bz-pin-leaving  .bz-pin-inner { transform: scale(0.4);  opacity: 0; }

/* Pop entrance — spring overshoot scale only (no rotation, per operator
   request: previous wiggle tilted left and read off-balance). Runs on
   .bz-pin-inner; .bz-pin-enter class is on the outer .bz-pin and
   self-removes on animationend (see addCategoryLayer). */
@keyframes bz-pin-pop {
    0%   { transform: translateY(-8px) scale(0);    opacity: 0; }
    60%  { transform: translateY(0)    scale(1.08); opacity: 1; }
    80%  { transform: translateY(0)    scale(0.97); }
    100% { transform: translateY(0)    scale(1); }
}
.bz-pin-enter .bz-pin-inner {
    animation: bz-pin-pop 0.25s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}

/* ===================================================================
   BUSINESS POPUP CARD — centered when active
   =================================================================== */

.bz-card-popup {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%) scale(0.96);
    width: min(86vw, 460px);
    z-index: 60;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.28s ease, transform 0.32s cubic-bezier(.4, 0, .2, 1);
}
.bz-card-popup.shown {
    opacity: 1;
    transform: translate(-50%, -50%) scale(1);
    pointer-events: auto;
}

.bz-card {
    background: #FFFFFF;
    border-radius: 0;
    overflow: hidden;
    box-shadow: 0 30px 80px rgba(0, 0, 0, 0.55), 0 6px 16px rgba(0, 0, 0, 0.30);
    color: var(--ink-soft);
    font-family: 'Montserrat', sans-serif;
}

.bz-card-photos {
    height: 280px;
    background: #1A8073;
    display: grid;
    grid-template-columns: 2fr 1fr;
    grid-template-rows: 1fr 1fr;
    gap: 0;
}
.bz-card-photos[data-count="1"] {
    grid-template-columns: 1fr;
    grid-template-rows: 1fr;
}
.bz-card-photos[data-count="2"] {
    grid-template-columns: 1fr 1fr;
    grid-template-rows: 1fr;
}
.bz-card-photo {
    background-size: cover;
    background-position: center;
    background-color: #DDD;
    min-width: 0;
    min-height: 0;
}
.bz-card-photo + .bz-card-photo { border-left: 3px solid #FFFFFF; }
/* When first photo is the "large" hero, it occupies the left column across both rows */
.bz-card-photo.large {
    grid-column: 1;
    grid-row: 1 / 3;
}
/* The two non-large photos in a 3-photo card stack into the right column;
   the second one needs a top border so it doesn't touch the first */
.bz-card-photos[data-count="3"] .bz-card-photo:nth-child(3) {
    border-top: 3px solid #FFFFFF;
    border-left: 3px solid #FFFFFF;
}

.bz-card-head {
    display: grid;
    grid-template-columns: 1fr auto auto;
    gap: 14px;
    align-items: center;
    padding: 22px 22px 16px;
}
.bz-card-name {
    font-family: 'Bebas Neue', sans-serif;
    font-weight: 400;
    font-size: 38px;
    line-height: 0.94;
    letter-spacing: 0.01em;
    color: var(--rest);
    text-transform: uppercase;
    word-break: break-word;
}
.bz-card-rating {
    background: var(--rest);
    border-radius: 8px;
    padding: 8px 14px 7px;
    color: #FFFFFF;
    text-align: center;
    line-height: 1;
}
.bz-rating-num {
    font-family: 'Bebas Neue', sans-serif;
    font-weight: 400;
    font-size: 30px;
    letter-spacing: 0.02em;
}
.bz-rating-stars {
    font-size: 11px;
    margin-top: 3px;
    color: #FFD75E;
    letter-spacing: 1.5px;
}
.bz-card-fav {
    width: 38px;
    height: 38px;
    color: #F5C518;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: transform 0.15s ease;
}
.bz-card-fav:active { transform: scale(1.12); }
.bz-card-fav svg { width: 32px; height: 32px; fill: currentColor; }

.bz-card-quote {
    padding: 0 24px 18px;
    font-style: italic;
    font-size: 16px;
    line-height: 1.45;
    color: #B5B0A8;
    text-align: center;
    font-weight: 400;
}

.bz-card-tabs {
    background: var(--rest);
    color: #FFFFFF;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 18px;
    padding: 16px 22px;
    font-weight: 700;
    font-size: 14px;
    letter-spacing: 0.16em;
    text-transform: uppercase;
}
.bz-card-tab {
    color: inherit;
    background: none;
    border: none;
    padding: 4px 8px;
    font: inherit;
    text-transform: inherit;
    letter-spacing: inherit;
    position: relative;
    cursor: pointer;
    opacity: 0.65;
    transition: opacity 0.15s ease;
}
.bz-card-tab:hover { opacity: 0.9; }
.bz-card-tab.active {
    opacity: 1;
}
.bz-card-tab.active::after {
    content: '';
    position: absolute;
    left: 8px;
    right: 8px;
    bottom: -3px;
    height: 2px;
    background: #FFFFFF;
    border-radius: 1px;
}
.bz-card-tab-divider { opacity: 0.55; font-weight: 400; }

/* Tab panel — scrollable container below the tabs */
.bz-card-tab-panel {
    max-height: 320px;
    overflow-y: auto;
    overscroll-behavior: contain;
    padding: 16px 24px 8px;
    background: #FFFFFF;
    color: #2A2A2A;
    font-family: 'Montserrat', sans-serif;
}
.bz-card-tab-panel[hidden] { display: none; }
/* Subtle scrollbar */
.bz-card-tab-panel::-webkit-scrollbar { width: 6px; }
.bz-card-tab-panel::-webkit-scrollbar-thumb { background: #DDD; border-radius: 3px; }
.bz-card-tab-panel::-webkit-scrollbar-track { background: transparent; }

/* "PREÇOS" small label header above the menu sections */
.bz-prices-header {
    font-size: 11px;
    color: #999;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    font-weight: 500;
    margin-bottom: 4px;
}

/* Section block (e.g. "MENU", "BEBIDAS") */
.bz-price-section { margin-bottom: 14px; }
.bz-price-section:last-child { margin-bottom: 4px; }
.bz-price-section-title {
    font-size: 13px;
    color: #1A1815;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    margin-bottom: 8px;
}

/* Each line: name … €15.00 with a dotted leader between */
.bz-price-row {
    display: flex;
    align-items: baseline;
    padding: 7px 0;
    font-size: 14.5px;
    line-height: 1.2;
}
.bz-price-name {
    color: #2A2A2A;
    flex-shrink: 1;
    min-width: 0;
}
.bz-price-leader {
    flex: 1;
    border-bottom: 1.5px dotted #C8C5C0;
    margin: 0 10px;
    align-self: flex-end;
    margin-bottom: 6px;
    min-width: 24px;
}
.bz-price-amount {
    color: #1A1815;
    font-weight: 700;
    white-space: nowrap;
    flex-shrink: 0;
}

/* Empty state in either tab */
.bz-empty-tab {
    text-align: center;
    color: #999;
    font-style: italic;
    padding: 24px 8px;
    font-size: 14px;
}

/* Raw-text fallback when prices/services come back as a plain string */
.bz-prices-raw {
    font-size: 14px;
    color: #2A2A2A;
    line-height: 1.5;
    white-space: pre-wrap;
}

/* Services tab: simple list of service names */
.bz-services-list { padding: 4px 0; }
.bz-service-row {
    padding: 8px 0;
    font-size: 14.5px;
    color: #2A2A2A;
    border-bottom: 1px solid #F0EDE6;
}
.bz-service-row:last-child { border-bottom: none; }

/* Image-based price sheet (when admin uploads a menu photo) */
.bz-prices-image { padding: 4px 0; }
.bz-prices-image img {
    width: 100%;
    height: auto;
    display: block;
    border-radius: 6px;
}

/* Inline debug panel — shown when no price column is detected. Lists every
   non-trivial field on the business row so we can spot which one holds the
   prices without opening DevTools. */
.bz-debug-header {
    font-size: 12px;
    color: #999;
    font-weight: 500;
    letter-spacing: 0.04em;
    margin-bottom: 10px;
    text-align: center;
    font-style: italic;
}
.bz-debug-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.bz-debug-row {
    background: #F7F2E5;
    border-radius: 6px;
    padding: 8px 10px;
    font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
    font-size: 11.5px;
    color: #2A2A2A;
    border: 1px solid #E8DFC8;
}
.bz-debug-row.is-null {
    background: #F7F7F4;
    border-color: #EAE6DC;
    opacity: 0.55;
}
.bz-debug-row.is-null .bz-debug-val {
    color: #999;
    font-style: italic;
}
.bz-debug-key {
    display: inline-block;
    color: #C03020;
    font-weight: 700;
    margin-bottom: 4px;
}
.bz-debug-val {
    margin: 0;
    white-space: pre-wrap;
    word-break: break-word;
    color: #555;
    font-family: inherit;
    font-size: inherit;
}

.bz-card-cta-row {
    background: #FFFFFF;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 14px;
    padding: 16px 22px 20px;
    font-size: 14.5px;
    color: #6B6B6B;
}
.bz-card-cta {
    color: inherit;
    padding: 4px 6px;
    transition: color 0.12s ease;
    cursor: pointer;
}
.bz-card-cta:hover { color: var(--rest); }
.bz-card-cta-sep { color: #C8C5C0; }
.bz-card-cta.is-disabled,
.bz-card-cta-row .is-disabled {
    color: #C8C5C0;
    cursor: default;
    text-decoration: line-through;
    text-decoration-thickness: 1px;
    text-decoration-color: rgba(200, 197, 192, 0.5);
}

/* Generic cat-popup (fallback for OSM fallback path; rarely used) */
.cat-popup {
    position: absolute;
    z-index: 50;
    background: rgba(15, 30, 28, 0.92);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    border: 1px solid rgba(255, 255, 255, 0.10);
    border-radius: 10px;
    padding: 12px 14px;
    color: var(--cream);
    font-size: 12px;
    transform: translate(-50%, calc(-100% - 10px));
    pointer-events: auto;
    box-shadow: 0 10px 28px rgba(0, 0, 0, 0.40);
    max-width: 240px;
}
.cat-popup a { color: var(--gold); text-decoration: underline; }

/* ===================================================================
   MOBILE — viewports below 1400px (phones, narrow tablets)
   Layout per approved iPhone preview / mockups_V1.pdf page 1:
     - Logo top-left, scaled down
     - No weather overlay (val-stack hidden)
     - Beach name bottom-left
     - Cat-stack bottom-aligned with beach name (right edge)
     - Prev/Next arrows above the action cluster (real tap clearance)
     - Action cluster planted at bottom-right
   Tablet inherits these rules and overrides specific sizes below to
   scale the same layout up proportionally — keeping a single mental
   model from phone to iPad without a separate breakpoint design.
   =================================================================== */
.mobile-placeholder { display: none !important; }

@media (max-width: 1399px) {
    /* Weather panel doesn't appear on mobile/tablet — too much info
       for a smaller canvas. Hidden, not removed, so the JS that
       writes #val-air etc. doesn't error if the user resizes back
       to desktop. */
    .val-stack { display: none; }

    /* Logo */
    .logo {
        top: 18px;
        left: 18px;
        font-size: 32px;
    }
    .logo .io { font-size: 24px; }

    /* Beach label — bottom 110 leaves room for nav arrows above the
       cluster. The cat-stack lines up with this same `bottom`. */
    :root {
        --beach-name-size: 64px;
    }
    .beach-label {
        left: 18px;
        right: 18px;
        bottom: 110px;
        max-width: none;
    }
    .beach-label .sup {
        font-size: 18px;
        letter-spacing: 0.01em;
    }
    .beach-label .name {
        line-height: 0.92;
    }

    /* Prev/next nav — clear airspace above the cluster */
    .beach-nav {
        bottom: 70px;
        font-size: 15px;
        letter-spacing: 0.06em;
    }
    .beach-nav.prev { left: 18px; }
    .beach-nav.next { right: 18px; }
    .beach-nav svg { width: 14px; height: 14px; }

    /* Cat-stack — bottom-aligned with beach name */
    .cat-stack {
        right: 14px;
        bottom: 110px;
        top: auto;
        transform: none;
        /* #101 — respect the bzDesign Gap slider; 12px is the device
           default that applies only when the operator hasn't set one. */
        gap: var(--bzd-stack-prop-gap, var(--bzd-stack-gap, 12px));
    }
    .cat-btn { width: 48px; height: 48px; }
    .cat-btn svg { width: 24px; height: 24px; }

    /* Cluster — corner anchor; compact 32px buttons */
    .cluster {
        right: 18px;
        bottom: 18px;
        gap: var(--bzd-stack-prop-gap, var(--bzd-stack-gap, 14px));
    }
    .cluster .act { width: auto; height: 32px; }
    .cluster .act svg { width: 20px; height: 20px; }
}

/* Very small phones (<= 380px): shave the beach name so two-word
   names ("Costa da Caparica") don't wrap into three lines. */
@media (max-width: 380px) {
    :root { --beach-name-size: 54px; }
    .beach-label .sup { font-size: 16px; }
}

/* Mobile landscape — short viewport, lift everything except cluster
   so prev/next arrows have tap clearance. */
@media (max-width: 1399px) and (orientation: landscape) and (max-height: 500px) {
    :root { --beach-name-size: 44px; }
    .beach-label { bottom: 100px; }
    .beach-label .sup { font-size: 14px; }
    .beach-nav { bottom: 60px; font-size: 13px; }
    .cat-stack { bottom: 100px; gap: var(--bzd-stack-prop-gap, var(--bzd-stack-gap, 10px)); }
    .cat-btn { width: 42px; height: 42px; }
    .cat-btn svg { width: 22px; height: 22px; }
    .cluster .act { width: auto; height: 28px; }
}

/* TABLET — 768–1399px wide. Same anatomy as the mobile layout, every
   size bumped proportionally so the UI matches a tablet's bigger
   canvas. No separate design — just scaled-up phone. */
@media (min-width: 768px) and (max-width: 1399px) {
    .logo { top: 32px; left: 32px; font-size: 48px; }
    .logo .io { font-size: 36px; }

    :root { --beach-name-size: 100px; }
    .beach-label {
        left: 32px;
        right: 32px;
        bottom: 160px;
    }
    .beach-label .sup { font-size: 26px; }

    .beach-nav {
        bottom: 100px;
        font-size: 20px;
    }
    .beach-nav.prev { left: 32px; }
    .beach-nav.next { right: 32px; }
    .beach-nav svg { width: 18px; height: 18px; }

    .cat-stack {
        right: 24px;
        bottom: 160px;
        gap: var(--bzd-stack-prop-gap, var(--bzd-stack-gap, 16px));
    }
    .cat-btn { width: 64px; height: 64px; }
    .cat-btn svg { width: 32px; height: 32px; }

    .cluster {
        right: 32px;
        bottom: 32px;
        gap: var(--bzd-stack-prop-gap, var(--bzd-stack-gap, 18px));
    }
    .cluster .act { width: auto; height: 44px; }
    .cluster .act svg { width: 24px; height: 24px; }
}

/* Tablet landscape — shorter viewport, reduce vertical sizes so the
   beach name doesn't dominate. */
@media (min-width: 768px) and (max-width: 1399px) and (orientation: landscape) {
    :root { --beach-name-size: 80px; }
    .beach-label { bottom: 130px; }
    .beach-nav   { bottom: 84px; }
    .cat-stack   { bottom: 130px; }
}
