/* ============================================
   CATEGORY CARD STACK

   A 3D coverflow built on top of a native horizontal scroller.

   The trick, and why this is worth the code: the *input* is an ordinary
   `overflow-x: scroll` strip with `scroll-snap`. Touch swipe, trackpad,
   keyboard, momentum and accessibility all come from the browser for
   free. The 3D cards are a separate, purely decorative layer
   (`pointer-events: none`) that reads `scrollLeft` and transforms itself
   to match.

   Layering:
     .card-stack__scroller  — the real links. Content lives here, so the
                              section works with no JS and reads correctly
                              to screen readers.
     .card-stack__visuals   — generated by js/card-stack.js, aria-hidden,
                              never interactive.

   Without JS, or with prefers-reduced-motion, the visuals layer is never
   built (or is hidden) and the scroller styles itself as a plain
   snap-scrolling row of cards. Nothing is lost but the animation.
   ============================================ */

.card-stack {
    position: relative;
    width: 100%;
    margin: 0 auto;
    /* aspect-ratio (not a fixed height) is what keeps the 3D maths
       proportional as the container scales. It gets wider — and the cards
       within it correspondingly bigger — at each breakpoint, so the stack
       fills the section instead of floating in the middle of it. */
    max-width: 30rem;
    aspect-ratio: 4 / 5;
}

@media (min-width: 768px) {
    .card-stack {
        max-width: 52rem;
        aspect-ratio: 3 / 2;
    }
}

@media (min-width: 1024px) {
    .card-stack {
        max-width: 68rem;
        aspect-ratio: 16 / 9;
    }
}

/* ============================================
   INPUT LAYER — the real, scrollable links
   ============================================ */
.card-stack__scroller {
    display: flex;
    height: 100%;
    overflow-x: scroll;
    overflow-y: hidden;
    scroll-snap-type: x mandatory;
    /* Firefox / IE. The Chrome/Safari equivalent is the ::-webkit rule
       below — the original reference had a typo in this selector
       (`scrollabe`), so its scrollbar was never actually hidden. */
    scrollbar-width: none;
    -ms-overflow-style: none;
}

.card-stack__scroller::-webkit-scrollbar {
    display: none;
}

.card-stack__link {
    flex: 1 0 100%;
    width: 100%;
    height: 100%;
    scroll-snap-align: center;
    scroll-snap-stop: always;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    color: var(--page-text);
    text-decoration: none;
}

/* ============================================
   NO-JS / REDUCED-MOTION PRESENTATION
   The links style themselves as ordinary cards. This is the default;
   the enhanced state below only kicks in once JS has built the visuals.
   ============================================ */
.card-stack__card {
    position: relative;
    width: 100%;
    height: 100%;
    border-radius: var(--radius-xl);
    overflow: hidden;
    background-color: var(--page-bg-alt);
    background-size: cover;
    background-position: center;
    box-shadow: var(--shadow-3);
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
}

/* Scrim so the title stays legible over any photograph. */
.card-stack__card::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(
        to top,
        rgba(10, 17, 40, 0.92) 0%,
        rgba(10, 17, 40, 0.55) 42%,
        rgba(10, 17, 40, 0.12) 100%
    );
}

/* z-index, not just position: the scrim is an ::after, so it is the last
   child and would otherwise paint on top of this text and wash it out. */
.card-stack__body {
    position: relative;
    z-index: 1;
    padding: var(--space-5);
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

/* Rating + neighbourhood, above the venue name. */
.card-stack__meta {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin-bottom: var(--space-1);
}

.card-stack__rating {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    padding: var(--space-1) var(--space-2);
    border-radius: var(--radius-full);
    background: linear-gradient(135deg, #b8860b 0%, #ffd700 100%);
    color: #14204a;
    font-size: var(--text-xs);
    font-weight: 700;
}

.card-stack__area {
    font-size: var(--text-xs);
    color: rgba(244, 239, 226, 0.75);
    text-transform: uppercase;
    letter-spacing: 0.06em;
}

.card-stack__title {
    font-family: var(--font-heading);
    font-size: var(--text-xl);
    font-weight: 700;
    color: #ffd700;
    line-height: 1.2;
}

.card-stack__desc {
    font-size: var(--text-sm);
    color: rgba(244, 239, 226, 0.85);
    line-height: 1.5;
}

/* ============================================
   ENHANCED STATE — set by JS once the visuals exist
   ============================================ */

/* The links stay in the layout (they are what is being scrolled) but
   become invisible; the visuals layer draws in their place. They remain
   focusable, and focusing one scrolls it into view, which drives the
   animation exactly as a swipe would. */
.card-stack--enhanced .card-stack__card {
    opacity: 0;
}

.card-stack__visuals {
    position: absolute;
    inset: 0;
    pointer-events: none;
    /* Set by JS to twice the container width, so the perspective stays
       proportional at every breakpoint instead of only looking right at
       one size. */
    perspective: var(--card-stack-perspective, 60rem);
}

/* Width is a percentage of the stack, and every horizontal offset in the
   JS is a percentage of the card's own width — so widening the card
   widens the whole fan proportionally and the maths still holds. */
.card-stack__visual {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 62%;
    height: calc(100% - var(--space-5));
    display: flex;
    align-items: center;
    justify-content: center;
    pointer-events: none;
    transform-style: preserve-3d;
    /* transform / z-index / opacity are all written by JS per frame. */
}

@media (min-width: 768px) {
    .card-stack__visual {
        width: 38%;
    }
}

@media (min-width: 1024px) {
    .card-stack__visual {
        width: 30%;
    }
}

.card-stack__visual-inner {
    /* Must be positioned: the ::after scrim below is inset:0 against this
       box. Without it the scrim resolves against .card-stack__visual and
       spills over the whole wrapper. */
    position: relative;
    width: 100%;
    /* Deliberately shorter than its wrapper. The wrapper is the rotation
       box; this is the visible card. The headroom absorbs the ~1.26x
       perspective magnification from translateZ, so the front card never
       spills past the stack and into the pager dots below. */
    height: 74%;
    border-radius: var(--radius-xl);
    overflow: hidden;
    background-color: var(--page-bg-alt);
    background-size: cover;
    background-position: center;
    box-shadow: var(--shadow-4);
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
}

.card-stack__visual-inner::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(
        to top,
        rgba(10, 17, 40, 0.92) 0%,
        rgba(10, 17, 40, 0.55) 42%,
        rgba(10, 17, 40, 0.12) 100%
    );
}

.card-stack__visual-inner .card-stack__body {
    padding: var(--space-4);
}

/* Keyboard focus has to be visible even though the link itself is
   transparent, so the ring is drawn on the front card instead. */
.card-stack--focus .card-stack__visual[data-front="true"] .card-stack__visual-inner {
    outline: 3px solid #ffd700;
    outline-offset: 3px;
}

/* ============================================
   PAGER DOTS
   ============================================ */
/* Sibling of .card-stack, not a child: the stack itself is sized by
   aspect-ratio, so anything nested inside would overflow that box. */
.card-stack__dots {
    display: flex;
    justify-content: center;
    /* The buttons carry their own spacing now that each is a 28px target. */
    gap: 0;
    margin-top: var(--space-4);
}

/* The dot a thumb hits is 28px square; the dot an eye sees is the 8px mark
   drawn inside it. Sizing the button itself to the mark made a target a
   third of the 24px minimum, which on a phone means missing it. */
.card-stack__dot {
    width: 32px;
    height: 32px;
    display: grid;
    place-items: center;
    padding: 0;
    border: none;
    background: none;
    cursor: pointer;
}

.card-stack__dot::before {
    content: '';
    width: var(--space-2);
    height: var(--space-2);
    border-radius: var(--radius-full);
    background: rgba(244, 239, 226, 0.3);
    transition: background var(--transition-normal), transform var(--transition-normal);
}

.card-stack__dot[aria-current="true"]::before {
    background: #ffd700;
    transform: scale(1.4);
}

/* ============================================
   REDUCED MOTION
   Keep the scroller, drop the 3D layer entirely.
   ============================================ */
@media (prefers-reduced-motion: reduce) {
    .card-stack__visuals {
        display: none;
    }

    .card-stack--enhanced .card-stack__card {
        opacity: 1;
    }

    .card-stack__scroller {
        scroll-behavior: auto;
    }
}

/* ============================================
   VENUES WITHOUT A PHOTOGRAPH

   Some venues have no image in the repository yet. Rather than a blank
   rectangle, the card becomes a tinted panel carrying the venue's initial
   as a watermark — deliberately obviously a placeholder, not a fake photo.
   Drop an image into the venue's `image` field and this stops applying.
   ============================================ */
.card-stack__card--no-photo {
    background-image: linear-gradient(150deg, #1b2a5e 0%, #14204a 55%, #0d1733 100%);
}

/* One tint per category, so a stack of placeholders still reads as a set
   rather than as seven identical grey cards. */
.card-stack__card--no-photo[data-cuisine="shopping"] {
    background-image: linear-gradient(150deg, #1f3a63 0%, #16294a 55%, #0d1733 100%);
}

.card-stack__card--no-photo[data-cuisine="religious"] {
    background-image: linear-gradient(150deg, #4a3410 0%, #2a2038 55%, #0d1733 100%);
}

.card-stack__card--no-photo[data-cuisine="nature"] {
    background-image: linear-gradient(150deg, #17402f 0%, #13293f 55%, #0d1733 100%);
}

.card-stack__card--no-photo[data-cuisine="museum"] {
    background-image: linear-gradient(150deg, #2e2551 0%, #1a2148 55%, #0d1733 100%);
}

.card-stack__card--no-photo[data-cuisine="electronic"],
.card-stack__card--no-photo[data-cuisine="mainstream"] {
    background-image: linear-gradient(150deg, #46184d 0%, #241c4e 55%, #0d1733 100%);
}

.card-stack__card--no-photo[data-cuisine="coffee"],
.card-stack__card--no-photo[data-cuisine="cocktail"],
.card-stack__card--no-photo[data-cuisine="wine"] {
    background-image: linear-gradient(150deg, #4a3316 0%, #2b2438 55%, #0d1733 100%);
}

/* ::after is already the scrim on both card layers, so the watermark uses
   ::before. It sits under the scrim and under the text, which is why it can
   be this large without competing with the venue name. */
.card-stack__card--no-photo::before {
    content: attr(data-initial);
    position: absolute;
    top: 42%;
    left: 50%;
    transform: translate(-50%, -50%);
    font-family: 'Cormorant Garamond', var(--font-heading), serif;
    font-size: 9rem;
    font-weight: 600;
    line-height: 1;
    color: rgba(255, 215, 0, 0.13);
    pointer-events: none;
}

.card-stack__visual-inner.card-stack__card--no-photo::before {
    font-size: 6rem;
}

/* ============================================
   STACK BLOCK — heading + "see all" above each stack
   ============================================ */
.stack-block {
    margin-bottom: var(--space-9);
}

.stack-block:last-child {
    margin-bottom: 0;
}

/* Baseline-aligned rather than centred: the "see all" link is a secondary
   action and reads better sitting on the same line as the heading than
   stacked under it. It wraps to its own line on narrow phones. */
.stack-block__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-4);
    max-width: 68rem;
    margin: 0 auto var(--space-5);
}

.stack-block__title {
    font-family: var(--font-heading);
    font-size: var(--text-2xl);
    font-weight: 700;
    color: #ffd700;
    line-height: 1.2;
    margin: 0;
}

/* Underlined with `text-decoration` rather than a bottom border, so the
   link can carry vertical padding — and so reach a thumb-sized target —
   without the rule drifting away from the words. */
.stack-block__all {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    min-height: 32px;
    padding: 4px 0;
    color: rgba(244, 239, 226, 0.85);
    font-size: var(--text-sm);
    font-weight: 600;
    text-decoration: underline;
    text-decoration-color: rgba(255, 215, 0, 0.45);
    text-decoration-thickness: 1px;
    text-underline-offset: 4px;
    transition: color var(--transition-normal), text-decoration-color var(--transition-normal);
}

.stack-block__all:hover,
.stack-block__all:focus-visible {
    color: #ffd700;
    text-decoration-color: #ffd700;
}

@media (max-width: 767px) {
    .stack-block {
        margin-bottom: var(--space-8);
    }

    .stack-block__title {
        font-size: var(--text-xl);
    }
}
