/* ============================================================
 * AUTO-GENERATED - DO NOT EDIT BY HAND
 * Bundled by Build/BundleCss.ps1 from wwwroot/css/design-system.css
 * Edit the partials under wwwroot/css/ and rebuild.
 * ============================================================ */

@import url("https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,400;0,500;0,600;1,400;1,500&family=Jost:wght@300;400;500;600&display=swap");

/* ===== SpicyVisionLanding/wwwroot/css/design-system.css ===== */
/* =========================================================================
   Emran Visuals - Design System
   Single entry point. _Layout.cshtml loads this one stylesheet and nothing
   else. The order of @imports below is the cascade order: tokens first,
   reset second, primitives third, components fourth, page glue last.

   Architecture, file by file:

     _tokens.css       - design tokens. CSS variables only.
     _reset.css        - minimal reset, body base, skip-link.
     _typography.css   - heading + body + editorial role classes.
     _layout.css       - .sv-container, .sv-section.

     components/*      - one file per component. Pure styling, no page glue.
     pages/*           - page-level composition glue (~30 lines each).

   Anything that isn't in this file's @imports doesn't ship.
   ========================================================================= */

/* Foundation */
/* ===== SpicyVisionLanding/wwwroot/css/_tokens.css ===== */
/* =========================================================================
   Emran Visuals - Design Tokens
   Sepia-chocolate + antique gold. Editorial. Cinematic. Magazine-grade.

   All design tokens live here. CSS variables only - no selectors that emit
   layout or component rules. Any literal (colour, size, radius, duration)
   used anywhere else in this stylesheet system MUST be declared here first.
   ========================================================================= */

:root {
    /* =====================================================
       COLOR - Warm sepia / chocolate base with aged-gold accents.
       Foregrounds are warm cream, never pure white.
       ===================================================== */

    /* Base surface ramp (deepest -> lifted) */
    --ev-bg:           #1a120e;   /* deep espresso - page background           */
    --ev-bg-deep:      #0e0806;   /* footer floor / behind hero gradient stop  */
    --ev-surface:      #241811;   /* warm dark panel - cards, form fields      */
    --ev-surface-2:    #2d1f16;   /* lifted surface - focused inputs, dropdowns*/
    --ev-surface-3:    #382519;   /* tier-3 lift - hover plates, modals        */

    /* Borders & hairlines (gold-tinted, never grey) */
    --ev-border:           #3a2a20;
    --ev-border-strong:    rgba(201, 163, 116, 0.35);
    --ev-hairline-gold:    rgba(201, 163, 116, 0.18);
    --ev-hairline-gold-faint: rgba(201, 163, 116, 0.12);  /* delivery brand-bar underline */

    /* Foreground - warm cream system */
    --ev-text:        #f3e8d8;
    --ev-text-strong: #fff6e6;
    --ev-muted:       #b09c87;
    --ev-muted-faint: #8a7864;
    --ev-text-on-accent: #1a120e;  /* warm near-black on gold, never pure black */

    /* Accent - antique gold (the foil). Ramp for hover/pressed. */
    --ev-accent:        #c9a374;
    --ev-accent-hover:  #d9b689;
    --ev-accent-pressed:#b58e60;
    --ev-accent-dim:    rgba(201, 163, 116, 0.55);
    --ev-accent-tint:   rgba(201, 163, 116, 0.10);
    --ev-accent-tint-2: rgba(201, 163, 116, 0.18);

    /* Editorial flourishes (second accents - use sparingly) */
    --ev-ivory:    #e8dcc4;
    --ev-wine:     #5a1f25;
    --ev-foil:     #e6c590;

    /* Semantic */
    --ev-star:    #e8b84a;
    --ev-danger:  #d08585;
    --ev-success: #c9a374;

    /* Informational - a muted, smoky steel blue. The one cool tone in an
       otherwise warm palette, reserved for calm technical feedback (the
       download-started notice) so it reads as a neutral system message and
       never as a marketing colour. Desaturated on purpose: no bright
       bootstrap blue, no neon. */
    --ev-info:         #93a6bf;                    /* steel-blue text / icon          */
    --ev-info-surface: #20272f;                    /* dark smoky-slate notice fill    */
    --ev-info-accent:  #7d93b0;                    /* slightly deeper left-edge accent*/
    --ev-info-border:  rgba(147, 166, 191, 0.26);  /* soft steel hairline             */

    /* Overlays / scrim - used for hero gradients, modal backdrops, image-card hover veils */
    --ev-overlay-deep:    rgba(0, 0, 0, 0.88);   /* video modal backdrop                  */
    --ev-overlay-strong:  rgba(0, 0, 0, 0.60);   /* hero bottom-band scrim               */
    --ev-overlay-mid:     rgba(0, 0, 0, 0.45);   /* hero mid-band                        */
    --ev-overlay-soft:    rgba(0, 0, 0, 0.35);   /* hero top scrim, image hover overlay  */
    --ev-overlay-faint:   rgba(0, 0, 0, 0.15);   /* hero clearest band                   */
    --ev-overlay-near-bg: rgba(10, 10, 10, 0.80);/* hero bottom fade to bg               */
    --ev-overlay-bg:      #000;                  /* hero pre-load solid (rare)           */
    /* Warm espresso scrim (bg-deep #0e0806 with alpha) for the featured review photo - the
       caption ribbon, the bottom gradient, and the film play disc. Warm, never cold grey. */
    --ev-photo-scrim:      rgba(14, 8, 6, 0.66);
    --ev-photo-scrim-deep: rgba(14, 8, 6, 0.85);

    /* Desktop editorial-hero photograph finish (see _hero.css). Two warm,
       same-hue layers kept inside the picture box so nothing bleeds onto the
       page. The seam-fade is --ev-bg (#1a120e) at zero alpha so the left-edge
       feather ramps with no cold "transparent-black" muddiness; the vignette is
       warm espresso-black (matches --ev-bg-deep) so corner depth never reads as
       cold grey. */
    --ev-hero-seam-fade:      rgba(26, 18, 14, 0);   /* --ev-bg at 0 alpha - clean feather tail        */
    --ev-hero-vignette-clear: rgba(14, 8, 6, 0);     /* vignette centre - fully open over the couple   */
    --ev-hero-vignette-edge:  rgba(14, 8, 6, 0.30);  /* vignette corner darkening - cinematic depth     */

    /* Glass surfaces (topbar, hero ghost button, mobile nav row) */
    --ev-glass-topbar:        rgba(26, 18, 14, 0.85);
    --ev-glass-mobilenav:     rgba(17, 13, 10, 0.96);
    --ev-glass-hero-ghost:    rgba(26, 18, 14, 0.55);
    --ev-glass-hero-ghost-2:  rgba(26, 18, 14, 0.75);
    /* Lightest ghost fill - the mobile hero secondary CTA. Barely-there
       espresso tint so the button reads as a faint frosted outline rather
       than a solid plate competing with the gold primary. */
    --ev-glass-hero-ghost-soft:   rgba(26, 18, 14, 0.10);
    /* Whisper-thin gold hairline for that same secondary CTA - dimmer than
       --ev-border-strong (0.35) so the outline reads as a hint, not a frame. */
    --ev-glass-hero-ghost-border: rgba(201, 163, 116, 0.12);
    /* Delivery brand-bar fill - bg-deep at half alpha, a faint dark wash under
       the clickable "Delivered by Emran Visuals" door. */
    --ev-glass-deliv-brandbar:    rgba(14, 8, 6, 0.5);
    --ev-glass-blur:          14px;
    --ev-glass-blur-soft:     6px;

    /* Brand-foreign palette - the Google G badge keeps Google's own brand colours.
       Do not reuse these anywhere else in the system. */
    --ev-google-blue:   #4285F4;
    --ev-google-red:    #EA4335;
    --ev-google-yellow: #FBBC05;
    --ev-google-green:  #34A853;
    --ev-google-g-fg:   #fff;       /* white "G" mask on coloured wheel */

    /* Tap highlight (mobile) */
    --ev-tap-highlight: rgba(201, 163, 116, 0.20);


    /* =====================================================
       TYPOGRAPHY
       Cormorant Garamond - display + headings. Italic 400/500 reserved for
         editorial pull-quotes, kickers, and numbered legal "Article" labels.
       Jost - body, UI labels, buttons. 300-600. Track up for kickers.
       ===================================================== */

    --ev-font-display: "Cormorant Garamond", Georgia, "Times New Roman", serif;
    --ev-font-body:    "Jost", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
    --ev-font-google-badge: "Arial", "Helvetica", sans-serif; /* Google G mask, brand fidelity */

    /* Type scale - mobile-first, scales up via clamp() at display/h1/h2 */
    --ev-fs-display:    clamp(2.5rem, 5vw + 0.5rem, 4.5rem);
    --ev-fs-h1:         clamp(1.75rem, 3vw + 0.5rem, 3.25rem);
    --ev-fs-h2:         clamp(1.5rem, 1.5vw + 1rem, 2.5rem);
    --ev-fs-h3:         1.5rem;
    --ev-fs-h4:         1.25rem;
    --ev-fs-lead:       1.25rem;
    --ev-fs-body:       1.125rem;   /* 18px default body                                       */
    --ev-fs-small:      1rem;       /* 16px - minimum on form inputs (iOS suppress zoom)       */
    --ev-fs-caption:    0.875rem;
    --ev-fs-eyebrow:    0.8125rem;

    /* Line-height + tracking */
    --ev-lh-tight:   1.12;
    --ev-lh-snug:    1.2;
    --ev-lh-body:    1.65;
    --ev-lh-prose:   1.75;

    --ev-track-display: -0.01em;
    --ev-track-eyebrow: 0.25em;
    --ev-track-button:  0.18em;


    /* =====================================================
       SPACING - 4px scale to 160px. Top end (96/128/160) drives section
       padding and editorial whitespace.
       ===================================================== */
    --ev-space-1:  4px;
    --ev-space-2:  8px;
    --ev-space-3:  12px;
    --ev-space-4:  16px;
    --ev-space-5:  24px;
    --ev-space-6:  32px;
    --ev-space-7:  48px;
    --ev-space-8:  64px;
    --ev-space-9:  96px;
    --ev-space-10: 128px;
    --ev-space-11: 160px;


    /* =====================================================
       RADII - Calm. Editorial. Never bubbly.
       ===================================================== */
    --ev-radius-xs:   2px;     /* pricing card CTA - printed-menu rectangle    */
    --ev-radius-sm:   4px;     /* pricing card body                            */
    --ev-radius:      10px;    /* default - buttons, inputs, film cards        */
    --ev-radius-lg:   16px;    /* large panels (reviews, pillars, gallery)     */
    --ev-radius-pill: 999px;   /* niche pills, filters                         */
    --ev-radius-day:  6px;     /* flatpickr day cell                           */


    /* =====================================================
       ELEVATION - Used sparingly. Three levels plus a focus glow and a
       featured-card shadow.
       ===================================================== */
    --ev-elev-1:        0 2px 18px rgba(0, 0, 0, 0.25);
    --ev-elev-2:        0 10px 40px rgba(0, 0, 0, 0.40);
    --ev-elev-3:        0 24px 80px rgba(0, 0, 0, 0.60);
    --ev-elev-featured: 0 20px 60px rgba(0, 0, 0, 0.45);
    --ev-elev-flatpickr:0 16px 40px rgba(0, 0, 0, 0.50);
    --ev-elev-toast:    0 10px 40px rgba(0, 0, 0, 0.50);
    --ev-elev-sticky:   0 -8px 24px rgba(0, 0, 0, 0.40);
    --ev-elev-modal:    0 24px 80px rgba(0, 0, 0, 0.60);
    --ev-elev-featured-vid: 0 20px 60px rgba(0, 0, 0, 0.50);
    --ev-elev-cta-button: 0 10px 30px rgba(201, 163, 116, 0.20);
    --ev-elev-pricing-ribbon: 0 6px 16px rgba(0, 0, 0, 0.35);
    --ev-glow-accent:   0 0 0 3px rgba(201, 163, 116, 0.18);
    /* Review cards: depth shadow plus a soft warm halo so a lifted card
       separates from the page without any harsh edge or cold grey. */
    --ev-elev-review:        0 4px 22px rgba(0, 0, 0, 0.32), 0 0 26px rgba(201, 163, 116, 0.06);
    --ev-elev-review-strong: 0 12px 40px rgba(0, 0, 0, 0.42), 0 0 34px rgba(201, 163, 116, 0.13);
    --ev-text-shadow-hero:    0 2px 24px rgba(0, 0, 0, 0.60);
    --ev-text-shadow-hero-sub: 0 1px 12px rgba(0, 0, 0, 0.60);


    /* =====================================================
       MOTION
       One ease for everything. Three durations. Always honour reduced-motion.
       ===================================================== */
    --ev-ease:      cubic-bezier(0.22, 0.61, 0.36, 1);
    --ev-dur-fast:  0.2s;
    --ev-dur-base:  0.25s;
    --ev-dur-slow:  0.6s;
    --ev-dur-fade:  0.8s;     /* scroll fade-in                                 */
    --ev-dur-bounce: 2.2s;    /* hero scroll cue                                */


    /* =====================================================
       LAYOUT
       ===================================================== */
    --ev-max-w:           1280px;
    --ev-max-w-prose:     780px;
    --ev-max-w-blog:      820px;
    --ev-max-w-form:      640px;
    --ev-max-w-contact:   760px;
    --ev-max-w-album:     900px;     /* hero inner cap on niche landing            */
    --ev-max-w-pricing:   960px;
    --ev-touch:           48px;
    --ev-sticky-cta-h:    64px;
    --ev-topbar-h:        72px;
    --ev-topbar-h-mobile: 60px;
    --ev-btn-lg-h:        56px;      /* CTA pill */


    /* =====================================================
       BREAKPOINTS - reference values; CSS uses them in @media literally.
       ===================================================== */
    --ev-bp-sm:  640px;
    --ev-bp-md:  768px;
    --ev-bp-lg:  1024px;
    --ev-bp-xl:  1200px;
    --ev-bp-2xl: 1440px;


    /* =====================================================
       LEGACY ALIAS LAYER
       The codebase historically used the --sv-* prefix. Markup classes stay
       .sv-* (per the design system contract), but every CSS *value* now
       comes from a single --ev-* token. The aliases below let any third
       party CSS or admin-rendered HTML that still consults --sv-* keep
       working. Internal stylesheets must consume --ev-* directly.
       ===================================================== */
    --sv-bg: var(--ev-bg);
    --sv-surface: var(--ev-surface);
    --sv-surface-2: var(--ev-surface-2);
    --sv-border: var(--ev-border);
    --sv-accent: var(--ev-accent);
    --sv-accent-hover: var(--ev-accent-hover);
    --sv-text: var(--ev-text);
    --sv-muted: var(--ev-muted);
    --sv-text-muted: var(--ev-muted);
    --sv-danger: var(--ev-danger);
    --sv-star: var(--ev-star);
    --sv-font-heading: var(--ev-font-display);
    --sv-font-body: var(--ev-font-body);
    --sv-fs-body: var(--ev-fs-body);
    --sv-fs-small: var(--ev-fs-small);
    --sv-fs-hero: var(--ev-fs-display);
    --sv-fs-h1: var(--ev-fs-h1);
    --sv-fs-h2: var(--ev-fs-h2);
    --sv-fs-h3: var(--ev-fs-h3);
    --sv-space-1: var(--ev-space-1);
    --sv-space-2: var(--ev-space-2);
    --sv-space-3: var(--ev-space-3);
    --sv-space-4: var(--ev-space-4);
    --sv-space-5: var(--ev-space-5);
    --sv-space-6: var(--ev-space-6);
    --sv-space-7: var(--ev-space-7);
    --sv-space-8: var(--ev-space-8);
    --sv-space-9: var(--ev-space-9);
    --sv-touch: var(--ev-touch);
    --sv-radius: var(--ev-radius);
    --sv-radius-lg: var(--ev-radius-lg);
    --sv-max-w: var(--ev-max-w);
    --sv-sticky-cta-h: var(--ev-sticky-cta-h);
    --sv-ease: var(--ev-ease);
}

/* ===== SpicyVisionLanding/wwwroot/css/_reset.css ===== */
/* =========================================================================
   Reset - minimal. Sets the page background, body type, and a11y defaults.
   No CSS variables declared here; no component selectors. Just the floor.
   ========================================================================= */

/* Webfonts. Loaded once for the whole system. */

html {
    /* The whole site is dark-themed. Declaring it lets the browser draw its
       own native UI - the scroll indicator, form controls, text caret - in a
       matching dark style. Without this the mobile overlay scrollbar renders
       as a bright (light-mode) pill that floats against the dark page; with
       it, the thumb is dark and recedes into the design. */
    color-scheme: dark;
    scroll-behavior: smooth;
    -webkit-text-size-adjust: 100%;
    text-size-adjust: 100%;
}
html, body { overflow-x: hidden; }

body.sv-body {
    background:
        radial-gradient(ellipse 120% 80% at 15% 20%, rgba(201, 163, 116, 0.07), transparent 50%),
        radial-gradient(ellipse 100% 70% at 85% 80%, rgba(201, 163, 116, 0.05), transparent 55%),
        var(--ev-bg);
    background-attachment: fixed;
    color: var(--ev-text);
    font-family: var(--ev-font-body);
    font-size: var(--ev-fs-body);
    line-height: var(--ev-lh-body);
    font-weight: 400;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    padding-bottom: calc(var(--ev-sticky-cta-h) + env(safe-area-inset-bottom, 0px));
    -webkit-tap-highlight-color: var(--ev-tap-highlight);
}

@media (min-width: 768px) {
    body.sv-body { padding-bottom: 0; }
}

/* Inputs must be at least 16px so iOS Safari skips its zoom-on-focus dance. */
.sv-body input,
.sv-body textarea,
.sv-body select { font-size: var(--ev-fs-small); }

/* Keyboard-only focus ring. Mouse focus stays clean. */
.sv-body :focus-visible {
    outline: 2px solid var(--ev-accent);
    outline-offset: 3px;
    border-radius: 3px;
}

/* Skip-to-content - hidden until keyboard focus pulls it down. */
.sv-skip-link {
    position: absolute;
    top: -60px;
    left: var(--ev-space-3);
    background: var(--ev-accent);
    color: var(--ev-text-on-accent);
    padding: 0.65rem 1rem;
    border-radius: var(--ev-radius);
    font-weight: 600;
    z-index: 2000;
    transition: top var(--ev-dur-fast) var(--ev-ease);
}
.sv-skip-link:focus { top: var(--ev-space-3); color: var(--ev-text-on-accent); }

/* ===== SpicyVisionLanding/wwwroot/css/_typography.css ===== */
/* =========================================================================
   Typography
   Heading element defaults (h1-h4 inside .sv-body) plus the editorial role
   classes (.sv-display, .sv-eyebrow, .sv-lead, .sv-quote, .sv-muted, etc.).
   ========================================================================= */

.sv-body h1, .sv-body h2, .sv-body h3, .sv-body h4 {
    font-family: var(--ev-font-display);
    font-weight: 500;
    letter-spacing: var(--ev-track-display);
    color: var(--ev-text);
    line-height: var(--ev-lh-snug);
}

.sv-body a { color: var(--ev-accent); text-decoration: none; }
.sv-body a:hover { color: var(--ev-accent-hover); }

/* Editorial role classes - applied directly to <p>, <span>, etc. */
.sv-display {
    font-family: var(--ev-font-display);
    font-weight: 500;
    font-size: var(--ev-fs-display);
    line-height: var(--ev-lh-tight);
    letter-spacing: var(--ev-track-display);
    color: var(--ev-text);
}
.sv-display-italic {
    font-family: var(--ev-font-display);
    font-style: italic;
    font-weight: 400;
}

.sv-lead {
    font-size: var(--ev-fs-lead);
    line-height: var(--ev-lh-body);
    color: var(--ev-text);
    text-wrap: pretty;
}

.sv-eyebrow,
.sv-section-kicker {
    font-family: var(--ev-font-body);
    font-size: var(--ev-fs-eyebrow);
    font-weight: 500;
    color: var(--ev-accent);
    text-transform: uppercase;
    letter-spacing: var(--ev-track-eyebrow);
}

/* The section kicker also sets its own block rhythm. The inline modifier
   keeps text-align inheriting from the surrounding container (used by the
   bio block where the kicker sits left-aligned with the H2). */
.sv-section-kicker {
    text-align: center;
    margin: 0 0 var(--ev-space-3) 0;
}
.sv-section-kicker--inline { text-align: inherit; }

.sv-quote {
    font-family: var(--ev-font-display);
    font-style: italic;
    font-weight: 400;
    font-size: clamp(1.5rem, 2vw + 1rem, 2rem);
    line-height: 1.35;
    color: var(--ev-ivory);
    text-wrap: pretty;
}

.sv-muted { color: var(--ev-muted); }
.sv-text-center { text-align: center; }

/* Section title + sub - centred headline pair used between sections. */
.sv-section-title {
    font-size: var(--ev-fs-h1);
    text-align: center;
    margin: 0 0 var(--ev-space-6) 0;
}
.sv-section-sub {
    text-align: center;
    color: var(--ev-muted);
    max-width: 680px;
    margin: 0 auto var(--ev-space-6);
}

/* Scroll fade-in primitive - used everywhere via .sv-fade. */
.sv-fade {
    opacity: 0;
    transform: translateY(20px);
    transition: opacity var(--ev-dur-fade) var(--ev-ease), transform var(--ev-dur-fade) var(--ev-ease);
}
.sv-fade.is-visible { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
    /* Reduced-motion users get the final state immediately, no transition.
       !important is required to override the inline-priority transition above
       even though we set transform: none. */
    .sv-fade { transition-duration: 0.01ms !important; transform: none !important; opacity: 1 !important; }
}

/* ===== SpicyVisionLanding/wwwroot/css/_layout.css ===== */
/* =========================================================================
   Layout primitives
   #main-content - page-level cap so full-bleed sections (hero, topbar,
                   trust strip, call CTA, footer) never run away beyond
                   the same width budget the inner content sections use.
                   Caps at 120rem (~1920px), which is the standard desktop
                   monitor width. At normal zoom on a 1920px monitor there
                   are no visible gutters; on huge ultrawide monitors or
                   zoomed-out viewports, gutters appear and the layout
                   stays proportional.
   .sv-container - centred max-width content rail (inside .sv-section).
   .sv-section   - vertical section padding rhythm.
   ========================================================================= */

#main-content {
    max-width: 120rem;
    margin: 0 auto;
}

.sv-container {
    width: 100%;
    max-width: var(--ev-max-w);
    margin: 0 auto;
    padding: 0 var(--ev-space-4);
}
@media (min-width: 768px) {
    .sv-container { padding: 0 var(--ev-space-6); }
}

.sv-section {
    padding: var(--ev-space-8) 0;
}
@media (min-width: 1024px) {
    .sv-section { padding: var(--ev-space-9) 0; }
}


/* Components - alphabetical within tiers */
/* ===== SpicyVisionLanding/wwwroot/css/components/_buttons.css ===== */
/* =========================================================================
   Buttons - the one button system.
   .sv-btn base + .sv-btn-primary / .sv-btn-ghost modifiers + .sv-btn-lg.
   Every CTA on the site composes from these.
   ========================================================================= */

.sv-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--ev-space-2);
    min-height: var(--ev-touch);
    padding: 0.75rem 1.75rem;
    border-radius: var(--ev-radius);
    font-family: var(--ev-font-body);
    font-weight: 500;
    font-size: 1rem;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    cursor: pointer;
    border: 1px solid transparent;
    transition: transform var(--ev-dur-fast) var(--ev-ease),
                background var(--ev-dur-fast) var(--ev-ease),
                color var(--ev-dur-fast) var(--ev-ease),
                border-color var(--ev-dur-fast) var(--ev-ease);
}

.sv-btn-primary {
    background: var(--ev-accent);
    color: var(--ev-text-on-accent);
    border-color: var(--ev-accent);
}
.sv-btn-primary:hover {
    background: var(--ev-accent-hover);
    color: var(--ev-text-on-accent);
    transform: translateY(-1px);
}

.sv-btn-ghost {
    background: transparent;
    color: var(--ev-text);
    border-color: var(--ev-border);
}
.sv-btn-ghost:hover {
    border-color: var(--ev-accent);
    color: var(--ev-accent);
}

.sv-btn-lg {
    padding: 1rem 2.5rem;
    font-size: 1.125rem;
    min-height: var(--ev-btn-lg-h);
}

/* The .sv-body base sets `a { color: var(--ev-accent) }` for all anchors, so
   anchor-shaped buttons (the most common case) need explicit overrides so
   their text colour follows the button modifier instead of the global link
   colour. Listed for both rest and hover to beat the .sv-body a:hover rule. */
.sv-body a.sv-btn-primary,
.sv-body a.sv-btn-primary:hover { color: var(--ev-text-on-accent); }
.sv-body a.sv-btn-ghost { color: var(--ev-text); }
.sv-body a.sv-btn-ghost:hover { color: var(--ev-accent); }

/* ===== SpicyVisionLanding/wwwroot/css/components/_forms.css ===== */
/* =========================================================================
   Forms - the one form system.
   .sv-form, .sv-field, .sv-label, .sv-input, .sv-textarea, .sv-select.
   No bespoke field styling anywhere else - all form controls compose these.
   ========================================================================= */

/* Honeypot - DOM-present so bots fill it, visually removed for humans.
   !important on positioning/dimensions because honeypot defense must survive
   any Bootstrap utility class accidentally applied to it. */
.sv-hp {
    position: absolute !important;
    left: -10000px !important;
    width: 1px !important;
    height: 1px !important;
    overflow: hidden !important;
    opacity: 0;
    pointer-events: none;
}

/* Hide Google's floating reCAPTCHA v3 badge. We satisfy Google's attribution
   requirement with the inline "Protected by reCAPTCHA" notice in the form
   (links to the Google Privacy Policy and Terms), which Google permits as an
   alternative to showing the badge. Left visible, the badge is fixed to the
   viewport's bottom-right and lands on top of the mobile sticky Call bar,
   reading as a stray white box glued to that section. !important is needed to
   beat the inline styles reCAPTCHA injects on the badge - same third-party
   override rationale as the honeypot above. */
.grecaptcha-badge { visibility: hidden !important; }

.sv-form {
    display: flex;
    flex-direction: column;
    gap: var(--ev-space-5);
    max-width: var(--ev-max-w-form);
    margin: 0 auto;
}

.sv-form-row {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--ev-space-5);
}
@media (min-width: 640px) {
    .sv-form-row { grid-template-columns: 1fr 1fr; }
}

.sv-field {
    display: flex;
    flex-direction: column;
    gap: var(--ev-space-2);
}

.sv-label {
    font-size: 0.875rem;
    font-weight: 500;
    color: var(--ev-muted);
    letter-spacing: 0.05em;
    text-transform: uppercase;
}
.sv-label span { color: var(--ev-accent); }

.sv-input {
    width: 100%;
    background: var(--ev-surface);
    color: var(--ev-text);
    border: 1px solid var(--ev-border);
    border-radius: var(--ev-radius);
    padding: 0.85rem 1rem;
    font-family: var(--ev-font-body);
    line-height: 1.5;
    transition: border-color var(--ev-dur-fast) var(--ev-ease),
                background var(--ev-dur-fast) var(--ev-ease),
                box-shadow var(--ev-dur-fast) var(--ev-ease);
    min-height: var(--ev-touch);
}
.sv-input:hover { border-color: rgba(201, 163, 116, 0.45); }
.sv-input:focus {
    outline: none;
    border-color: var(--ev-accent);
    background: var(--ev-surface-2);
    box-shadow: var(--ev-glow-accent);
}
.sv-textarea {
    min-height: 140px;
    resize: vertical;
    line-height: 1.6;
}

/* Native date picker icon fallback - Flatpickr replaces this, but if the
   CDN fails the native picker icon would otherwise paint black on our
   dark surface. */
.sv-input[type="date"]::-webkit-calendar-picker-indicator {
    filter: invert(0.85);
    opacity: 0.6;
    cursor: pointer;
}

.sv-date-alt { cursor: pointer; }

.sv-select {
    appearance: none;
    -webkit-appearance: none;
    background-image: linear-gradient(45deg, transparent 50%, var(--ev-accent) 50%),
                      linear-gradient(135deg, var(--ev-accent) 50%, transparent 50%);
    background-position: calc(100% - 18px) center, calc(100% - 12px) center;
    background-size: 6px 6px, 6px 6px;
    background-repeat: no-repeat;
    padding-right: 2.5rem;
}

.sv-form-error {
    font-size: 0.875rem;
    color: var(--ev-danger);
    min-height: 0.875rem;
}

/* ASP.NET Core MVC unobtrusive validation classes. !important needed because
   the validation runtime sets these classes via JS after the input has
   already mounted with our standard border, and the override sticks even
   when our hover/focus rules try to take it back. */
.input-validation-error { border-color: var(--ev-danger) !important; }
.field-validation-error { font-size: 0.875rem; color: var(--ev-danger); }

.sv-form-summary { color: var(--ev-danger); font-size: 0.95rem; }
.sv-form-summary ul { margin: 0; padding-left: 1.25rem; }

/* AJAX submit error banner (contact-form.js). Validation and send failures surface
   here in place, since the form posts over fetch() and the page never re-renders. */
.sv-form-alert {
    color: var(--ev-danger);
    border: 1px solid var(--ev-danger);
    border-radius: var(--ev-radius);
    padding: var(--ev-space-4) var(--ev-space-5);
    font-size: 0.95rem;
}

.sv-form .sv-flash {
    background: rgba(201, 163, 116, 0.10);
    border: 1px solid var(--ev-accent);
    color: var(--ev-accent);
    padding: 0.75rem 1rem;
    border-radius: var(--ev-radius);
}

@media (max-width: 639.98px) {
    .sv-form-submit { width: 100%; }
}

/* ===== SpicyVisionLanding/wwwroot/css/components/_topbar.css ===== */
/* =========================================================================
   Topbar - sticky brand + nav + phone CTA.
   Glass-blur over hero photography on desktop; a second always-visible row
   of four conversion links sits below the brand strip on mobile so every
   primary action stays one tap away (no hamburger).
   ========================================================================= */

.sv-topbar {
    position: sticky;
    top: 0;
    z-index: 900;
    background: var(--ev-glass-topbar);
    backdrop-filter: blur(var(--ev-glass-blur));
    -webkit-backdrop-filter: blur(var(--ev-glass-blur));
    border-bottom: 1px solid rgba(201, 163, 116, 0.12);
}

.sv-topbar-inner {
    position: relative;
    max-width: var(--ev-max-w);
    margin: 0 auto;
    padding: 0 var(--ev-space-6);
    height: var(--ev-topbar-h);
    display: flex;
    align-items: center;
    gap: var(--ev-space-5);
}

/* Mobile: brand-left + phone-right; nav row sits in .sv-topbar-mobilenav below. */
@media (max-width: 1199.98px) {
    .sv-topbar-inner {
        height: var(--ev-topbar-h-mobile);
        padding: 0 var(--ev-space-4);
        gap: var(--ev-space-3);
        justify-content: space-between;
    }
    .sv-topbar-brand { font-size: 1.375rem; }
    /* The desktop link row is decorative on mobile; the mobilenav row carries
       the same links. Hidden with !important to defeat any Bootstrap
       utility class (.d-flex etc.) that might be applied alongside it. */
    .sv-topbar-nav { display: none !important; }
    .sv-topbar-phone { white-space: nowrap; }
}

/* On phones the call-to-action lives in the sticky bottom pill, so the
   topbar phone chip would be a redundant CTA competing with the wordmark
   for attention. Hide it below 768px. Desktop behaviour unchanged. */
@media (max-width: 767.98px) {
    .sv-topbar-phone { display: none; }
}

/* Extra-narrow phones (<380px): shrink the pill so brand + full phone still
   fit on one line. */
@media (max-width: 379.98px) {
    .sv-topbar-brand { font-size: 1.125rem; }
    .sv-topbar-phone { font-size: 0.875rem; padding: 0.4rem 0.7rem; gap: 0.3rem; }
}

/* The wordmark is an <a> element, so .sv-body a (specificity 0,1,1) was
   beating a plain .sv-topbar-brand (0,1,0) and painting the brand gold.
   Scope under .sv-body so the rule wins on specificity, same trick used by
   the .sv-btn-ghost / .sv-pricing-card-cta overrides further down. */
.sv-body a.sv-topbar-brand {
    font-family: var(--ev-font-display);
    font-size: 1.625rem;
    font-weight: 500;
    /* --ev-text is the warm body cream (#f3e8d8). On the dark topbar it
       reads as gold-tinted, which collides with the actual gold accent
       used elsewhere. The wordmark uses --ev-text-strong (#fff6e6) so it
       lands as unambiguous cream-white, visibly distinct from the gold. */
    color: var(--ev-text-strong);
    letter-spacing: 0.01em;
    flex-shrink: 0;
    text-decoration: none;
}
.sv-body a.sv-topbar-brand:hover { color: var(--ev-accent); }

.sv-topbar-nav {
    display: flex;
    gap: var(--ev-space-5);
    margin-left: auto;
    align-items: center;
}
.sv-topbar-nav a {
    color: var(--ev-text);
    font-size: 0.95rem;
    letter-spacing: 0.02em;
    text-decoration: none;
    padding: 0.5rem 0;
    border-bottom: 1px solid transparent;
    transition: border-color var(--ev-dur-fast) var(--ev-ease), color var(--ev-dur-fast) var(--ev-ease);
}
.sv-topbar-nav a:hover { color: var(--ev-accent); border-bottom-color: var(--ev-accent); }
.sv-topbar-nav a.active { color: var(--ev-accent); }

.sv-topbar-phone {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    color: var(--ev-accent);
    font-weight: 500;
    font-size: 1rem;
    border: 1px solid var(--ev-accent);
    padding: 0.5rem 1rem;
    border-radius: var(--ev-radius);
    text-decoration: none;
    transition: background var(--ev-dur-fast) var(--ev-ease), color var(--ev-dur-fast) var(--ev-ease);
}
.sv-body a.sv-topbar-phone:hover { background: var(--ev-accent); color: var(--ev-text-on-accent); }

/* Mobile-only secondary row. Hidden on desktop. */
.sv-topbar-mobilenav {
    display: flex;
    justify-content: stretch;
    background: var(--ev-glass-mobilenav);
    backdrop-filter: blur(var(--ev-glass-blur));
    -webkit-backdrop-filter: blur(var(--ev-glass-blur));
    border-top: 1px solid rgba(201, 163, 116, 0.14);
    border-bottom: 1px solid rgba(201, 163, 116, 0.18);
}
.sv-topbar-mobilenav a {
    /* flex-basis: auto so each item starts at its own content width and the
       free row-space is divided equally on top. Prevents the longer
       "Real Weddings" label from sharing a 1/3 slot with short labels like
       "Films" and truncating to "REAL WEDDIN...". */
    flex: 1 1 auto;
    min-width: 0;
    padding: 0.7rem 0.35rem;
    text-align: center;
    color: var(--ev-text);
    text-decoration: none;
    font-size: 0.78rem;
    font-weight: 500;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    border-right: 1px solid rgba(201, 163, 116, 0.10);
    transition: color var(--ev-dur-fast) var(--ev-ease), background var(--ev-dur-fast) var(--ev-ease);
}
.sv-topbar-mobilenav a:last-child { border-right: none; }

/* Compact home-icon link at the start of the deep-page mobile nav. The
   text links share remaining width via flex:1; this icon-only link claims
   only the space it needs so the labels next to it don't shrink/truncate. */
.sv-topbar-mobilenav .sv-topbar-mobilenav-icon {
    flex: 0 0 auto;
    padding-left: var(--ev-space-4);
    padding-right: var(--ev-space-4);
    font-size: 1rem;
}
.sv-topbar-mobilenav .sv-topbar-mobilenav-icon i { line-height: 1; }
.sv-topbar-mobilenav a:hover,
.sv-topbar-mobilenav a:focus-visible,
.sv-topbar-mobilenav a.active {
    color: var(--ev-accent);
    background: rgba(201, 163, 116, 0.06);
    outline: none;
}
@media (min-width: 1200px) {
    .sv-topbar-mobilenav { display: none; }
}

/* ===== SpicyVisionLanding/wwwroot/css/components/_hero.css ===== */
/* =========================================================================
   Hero - editorial split, modelled on the cloud design mockup.
   Desktop: text rail left, full-bleed photograph right (no scrim).
   Mobile:  text overlay above a full-bleed, darkened, blurred photograph.

   CMS supplies the photograph URL via a real <img> inside .sv-hero-right
   (the LCP candidate). The same <img> is the source of the mobile
   atmospheric layer - no separate background-image, so no second download
   on small screens. HeroTitle supports one italic-foil phrase wrapped in
   <em class="sv-hero-foil">.
   ========================================================================= */

.sv-hero {
    position: relative;
    background: var(--ev-bg);
}

/* Inner wrapper used by the compact hero variant on Contact, Blog/Storybook,
   Terms, and Privacy. Gives those pages a centred content rail with a
   comfortable mobile gutter so headings and lead copy never run flush to
   the viewport edge. The editorial hero on the homepage does not use this
   wrapper - it has its own padded text rail (.sv-hero-left). */
.sv-hero-inner {
    width: 100%;
    max-width: var(--ev-max-w-contact);
    margin: 0 auto;
    padding: 0 var(--ev-space-5);
}
@media (min-width: 768px) {
    .sv-hero-inner { padding: 0 var(--ev-space-6); }
}

.sv-hero--editorial {
    display: grid;
    grid-template-columns: 1fr;
    grid-template-areas: "text";
    /* Mobile turns this section into a single positioned stage: the
       photograph fills the section as a darkened, blurred atmospheric
       layer and the text rail overlays it (see the mobile block below).
       The grid layout only kicks in from 768px up. */
    min-height: auto;
}

@media (min-width: 768px) {
    .sv-hero--editorial {
        /* ~42/58 split, image side noticeably wider so the photograph reads as
           the hero and the text rail has obvious whitespace against it,
           rather than text running edge-to-edge into the picture.

           Height is rem-based per breakpoint AND capped at viewport-minus-
           topbar via min(). Two reasons:
             1. The rem side is what makes browser zoom (Ctrl+Wheel) scale
                the hero in lockstep with the text inside. A pure-vh hero
                stays put while the text grows, which looked broken at zoom.
             2. The svh side guarantees that on short viewports (small
                laptops, split windows, landscape phones) the section never
                exceeds the visible area, so the CTAs and the bottom of the
                photograph always sit above the fold instead of being cut
                off behind the next section. The topbar is sticky and in-
                flow so its height is subtracted; otherwise the hero would
                push 72px past the bottom edge.

           min-height (not height) so the section can still grow if its
           content somehow exceeds the cap. */
        grid-template-columns: minmax(0, 42fr) minmax(0, 58fr);
        grid-template-areas: "text image";
        min-height: min(42rem, calc(100svh - var(--ev-topbar-h) - 1px));
    }
}

@media (min-width: 1024px) {
    .sv-hero--editorial { min-height: min(46rem, calc(100svh - var(--ev-topbar-h) - 1px)); }
}

@media (min-width: 1280px) {
    .sv-hero--editorial { min-height: min(50rem, calc(100svh - var(--ev-topbar-h) - 1px)); }
}

@media (min-width: 1440px) {
    .sv-hero--editorial { min-height: min(56rem, calc(100svh - var(--ev-topbar-h) - 1px)); }
}

/* ---------- Left: text rail ---------- */

.sv-hero-left {
    grid-area: text;
    display: flex;
    flex-direction: column;
    justify-content: center;
    /* Inter-sibling gap (eyebrow -> H1 -> subhead -> CTAs). Scales
       progressively per breakpoint: tight on phones / small laptops where
       the section is short, generous on large desktops where the cluster
       has room to breathe. This keeps the CTAs visible inside the hero
       section on every viewport rather than slipping below the fold on
       narrow laptops. */
    gap: var(--ev-space-5);
    padding: calc(var(--ev-space-8) + var(--ev-topbar-h-mobile)) var(--ev-space-5) var(--ev-space-7);
}

@media (min-width: 768px) {
    .sv-hero-left {
        /* Padding-driven positioning (no max-width or margin auto) so the
           text rail's left edge keeps a consistent percentage from the
           viewport edge as the screen widens, instead of drifting right
           the way a right-anchored max-width rail does.

           Padding-bottom is intentionally larger than padding-top so the
           justify-content: center cluster biases upward, sitting roughly
           where an editorial layout's optical center is rather than the
           geometric center of the section. */
        padding: var(--ev-space-7) var(--ev-space-8) var(--ev-space-8) var(--ev-space-7);
        /* 48px gap on small laptops (~768-1023). Cluster fits inside the
           42rem section with room to spare. */
        gap: var(--ev-space-7);
    }
}

@media (min-width: 1024px) {
    .sv-hero-left {
        padding: var(--ev-space-7) var(--ev-space-9) var(--ev-space-9) var(--ev-space-8);
        /* 64px on mid desktops (1024-1279, section is 46rem). */
        gap: var(--ev-space-8);
    }
}

@media (min-width: 1280px) {
    .sv-hero-left {
        padding: var(--ev-space-8) var(--ev-space-10) var(--ev-space-10) var(--ev-space-9);
        /* 96px editorial gap kicks in once the section reaches 50rem and
           there is genuine vertical room for it. */
        gap: var(--ev-space-9);
    }
}

@media (min-width: 1440px) {
    .sv-hero-left {
        /* On widescreens both sides scale up together so the cluster sits
           well clear of the viewport edge with a generous gutter before the
           photograph. 7rem (was 10rem): the tighter gutter widens the text
           column by ~90px, which matters vertically - a narrow column wraps
           the H1 and subhead onto extra lines, and that taller cluster is
           what forced the copy to overflow the fold on short widescreens
           (e.g. a 1440x810 MacBook). A wider column wraps fewer lines, so the
           cluster is shorter and the editorial line-spacing below fits inside
           the svh cap. The eyebrow also lands on one line again. */
        padding-left: 7rem;
        padding-right: 7rem;
    }
}

@media (min-width: 1920px) {
    .sv-hero-left {
        padding-left: 9rem;
        padding-right: 9rem;
    }
}

/* ---------- Short-viewport compression ----------

   When the viewport is short (small laptops, split windows, landscape
   phones) the section's min-height is already capped at 100svh minus the
   topbar (see .sv-hero--editorial above), but the text rail's padding +
   3x cluster gap can still exceed that cap and push the CTAs below the
   fold. These rules compress vertical spacing on short viewports so the
   eyebrow, H1, subhead, and CTAs all stay inside the visible area.

   Horizontal padding is left untouched; only vertical rhythm shrinks. */

@media (max-height: 950px) and (min-width: 768px) {
    /* Compression for ~1440x900 and ~1536x864 laptops. Gap drops from the
       default 1280+ rule's 96px to 64px - still generous editorial spacing
       between the eyebrow, H1, subhead, and CTAs (the copy reads as spread
       out, not bunched) but tight enough that the cluster fits the svh cap.
       Padding is top-light / bottom-heavy (32 vs 64): the justify-content:
       center cluster centres inside the padding box, so the larger bottom
       padding lifts the whole block toward the upper third - a clean margin
       under the topbar instead of dead-centre, which read as the copy
       "pushed down" with a wide empty band above it. The 64 (not a bigger
       lift) is deliberate: paired with the wider 1440+ column above, it keeps
       the cluster clear of the fold even on a short 1440x810 widescreen. */
    .sv-hero-left {
        padding-top: var(--ev-space-6);
        padding-bottom: var(--ev-space-8);
        gap: var(--ev-space-8);
    }
}

@media (max-height: 800px) and (min-width: 768px) {
    /* One more notch tighter for ~1366x768 and ~1280x720 laptops. Gap drops
       to 48px - still clearly spread (text never touches text, the CTAs stand
       off the subhead, the hierarchy stays clear), with the same top-light /
       bottom-heavy padding (32 vs 48, a smaller lift than the 950 rule because
       there is less free height to give) so the copy still sits high rather
       than centred. The H1 is left untouched - its clamp already scales the
       font with viewport width. */
    .sv-hero-left {
        padding-top: var(--ev-space-6);
        padding-bottom: var(--ev-space-7);
        gap: var(--ev-space-7);
    }
}

@media (max-height: 680px) and (min-width: 768px) {
    /* Shortest landscape viewports (~1024x600, split-screen windows). The
       larger gaps and lift above would push the CTAs past the fold here, so
       fall all the way back to the tightest even rhythm (32 / 32 / 32). This
       is the original pre-spread spacing, kept only for these rare short
       heights; it is not a lift, just a safe floor. */
    .sv-hero-left {
        padding-top: var(--ev-space-6);
        padding-bottom: var(--ev-space-6);
        gap: var(--ev-space-6);
    }
}

/* Scoped under .sv-body so this rule beats _typography.css's
   ".sv-body h1, .sv-body h2..." default-cream colour when the eyebrow
   element is an <h1> (which is the case now that the SEO H1 lives in
   this slot). Specificity 0,2,0 vs the typography rule's 0,1,1. */
.sv-body .sv-hero-eyebrow {
    font-family: var(--ev-font-body);
    font-size: var(--ev-fs-eyebrow);
    font-weight: 500;
    color: var(--ev-accent);
    text-transform: uppercase;
    letter-spacing: 0.3em;
    line-height: var(--ev-lh-snug);
    margin: 0;
}

.sv-body .sv-hero-eyebrow span {
    /* The mid-dot separator. Slightly dimmed so the labels read as a single
       line of metadata rather than competing items. */
    margin: 0 0.5em;
    color: var(--ev-accent-dim);
}

.sv-hero-title {
    font-family: var(--ev-font-display);
    font-weight: 400;
    /* Editorial sizing that scales smoothly mobile-to-desktop AND continues
       to grow on large viewports so the title doesn't feel small relative
       to the image when the user zooms out. 5rem cap keeps the H1 from
       eating the vertical rhythm of the cluster on widescreens. */
    font-size: clamp(2.5rem, 2.2vw + 1.25rem, 5rem);
    line-height: 1.1;
    letter-spacing: -0.005em;
    color: var(--ev-text);
    margin: 0;
    text-wrap: balance;
    /* 14ch gives each line ~3 words on Cormorant - the H1 wraps into a
       calm 3-line block instead of the 4-line column of fragments that
       a tighter wrap produced. */
    max-width: 14ch;
}

@media (max-width: 767.98px) {
    .sv-hero-title { max-width: none; }
}

.sv-hero-foil {
    font-style: italic;
    font-weight: 400;
    color: var(--ev-foil);
}

.sv-hero-sub {
    font-family: var(--ev-font-body);
    font-size: var(--ev-fs-lead);
    line-height: var(--ev-lh-body);
    color: var(--ev-muted);
    margin: 0;
    max-width: 38ch;
    text-wrap: pretty;
}

/* ---------- CTAs ---------- */

.sv-hero-ctas {
    display: flex;
    flex-wrap: wrap;
    gap: var(--ev-space-3);
    /* The cluster's flex gap (--ev-space-9 on desktop) already gives the
       CTA row a generous 96px stand-off from the subhead, so no margin
       override is needed here. */
}

/* Hero CTAs are the only pill-shaped buttons in the system. The button
   primitive ships with the standard 10px radius; this modifier overrides
   both the radius and the padding to match the editorial pill shape from
   the design system mockup. */
.sv-hero-cta {
    border-radius: var(--ev-radius-pill);
    padding: 1.05rem 2.25rem;
    font-size: 0.875rem;
    letter-spacing: 0.18em;
}

@media (max-width: 639.98px) {
    .sv-hero-cta { width: 100%; justify-content: center; }
}

/* ---------- Right: photograph ---------- */

.sv-hero-right {
    grid-area: image;
    position: relative;
    overflow: hidden;
    aspect-ratio: 4 / 5;
}

/* The <picture> wrapper must fill the column so the <img>'s height:100% has a
   definite parent height (an inline picture would collapse to auto and break the
   full-bleed cover). It carries no styling of its own. */
.sv-hero-right picture {
    display: block;
    width: 100%;
    height: 100%;
}

.sv-hero-img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center;
    display: block;
}

@media (min-width: 768px) {
    .sv-hero-right {
        /* Desktop: image fills the full height of the section, no scrim. */
        aspect-ratio: auto;
        height: 100%;
    }

    /* Pin the photograph as an absolutely-positioned fill layer so its intrinsic
       size can never drive the grid row height. An <img> is a replaced element:
       left in normal flow with height:100% but no DEFINITE parent height, its
       natural size wins - a 1:1 hero at the column's width becomes a square as
       tall as the column is wide (e.g. 882px on a 1536 viewport), and that
       min-content height stretches the row PAST the section's
       min(rem, 100svh - topbar) cap. The overflow pushed the hero's bottom below
       the fold (the image was cut off and needed a scroll) and inflated the empty
       band above the centred text rail. inset:0 ties the image to the column box
       instead, so the section height is set only by the cap and the text rail;
       object-fit: cover (above) still does the cropping. The mobile block below
       pins its own full-bleed layer, so this rule is desktop-only. */
    .sv-hero-right > picture,
    .sv-hero-right > .sv-hero-img {
        position: absolute;
        inset: 0;
    }

    /* Desktop hero finish - two non-destructive light layers painted over the
       photograph, both clipped inside the picture box (overflow:hidden on
       .sv-hero-right) so neither bleeds onto the page:

         1. Left-seam feather. The photo's inner edge dissolves into the EXACT
            espresso of the text rail (--ev-bg), so the two halves of the hero
            read as one composition instead of two abutting boxes. The mask is
            shaped like a gradient-brush stroke, NOT a straight ramp: a tiny
            solid plateau at the seam, a steep middle, then a long gentle tail
            that eases all the way to zero. The eased ENDS are the point - a
            linear fade stops abruptly and the eye finds that stop as a line;
            easing the tail to a near-zero slope hides where the feather ends.
            Each step is --ev-bg mixed toward transparent via color-mix (alpha
            only, premultiplied - no cold "transparent-black" grey), so the
            colour stays centralised in the one token and only the mix ratio
            varies. It is fully clear by 34%, well left of the groom (~34% in),
            so the couple and the lehenga keep full brightness.

         2. Soft vignette. A warm radial darkening centred a little up and right
            of middle (on the couple) that stays clear across the subjects and
            eases down to the corners for cinematic depth. Deliberately gentle
            (0.30 at the corners) and NOT a fade-to-page: the top, right, and
            bottom stay crisp photographic edges, never feathered borders, so
            the jewel-tone dress and the faces keep their brightness.

       One ::after with two stacked backgrounds (feather painted over vignette);
       z-index lifts it above the absolutely-positioned <img>. The mobile scrim
       uses .sv-hero-right::after inside its own @media block, so the two never
       collide. */
    .sv-hero-right::after {
        content: "";
        position: absolute;
        inset: 0;
        z-index: 2;
        pointer-events: none;
        background:
            linear-gradient(90deg,
                var(--ev-bg)                                       0%,
                var(--ev-bg)                                       3%,
                color-mix(in srgb, var(--ev-bg) 92%, transparent)  8%,
                color-mix(in srgb, var(--ev-bg) 78%, transparent) 12%,
                color-mix(in srgb, var(--ev-bg) 55%, transparent) 16%,
                color-mix(in srgb, var(--ev-bg) 32%, transparent) 20%,
                color-mix(in srgb, var(--ev-bg) 15%, transparent) 24%,
                color-mix(in srgb, var(--ev-bg)  5%, transparent) 28%,
                var(--ev-hero-seam-fade)                          34%),
            radial-gradient(85% 90% at 56% 44%,
                var(--ev-hero-vignette-clear) 50%,
                var(--ev-hero-vignette-edge) 100%);
    }
}

/* When no hero image is set the right column renders as a flat surface tile
   instead of collapsing the layout. Admins should always upload an image;
   this is the safety net. */
.sv-hero-right--empty {
    background: var(--ev-surface);
}

/* ---------- Mobile: full-bleed atmospheric photograph ----------

   This block is intentionally placed AFTER the base .sv-hero-right /
   .sv-hero-right--empty rules above so its position/aspect-ratio/
   background overrides actually take effect. CSS cascade at equal
   specificity prefers the later rule, and an unmediated rule competing
   with an @media rule both apply when the media matches, so the later
   one wins. Moving this block above the base rules silently broke the
   mobile layout (image dropped into normal flow below the text). */

@media (max-width: 767.98px) {
    /* Phone hero: the photograph fills the section as a darkened, blurred
       atmospheric layer and the text rail overlays it. The image is felt
       as warm texture, never read as foreground content - the editorial
       copy and CTAs always win on contrast.

       Layout flips from the desktop two-column grid to a single absolute-
       positioned stage: .sv-hero-right is the image layer (z:1), its
       ::after is the multi-stop scrim (z:2), and .sv-hero-left rides on
       top (z:3). */
    .sv-hero--editorial {
        position: relative;
        display: block;
        /* Cap the section to the visible viewport minus the sticky topbar
           and the bottom call-bar so the entire hero (text + CTAs) sits
           inside the fold on a 360-390px phone. svh, not vh, so the iOS
           URL-bar collapse doesn't redraw the layout. */
        min-height: calc(100svh - var(--ev-topbar-h-mobile) - var(--ev-sticky-cta-h));
        overflow: hidden;
    }

    .sv-hero-right {
        /* Unhide and promote to the full-bleed image layer. The desktop
           aspect-ratio (4/5) is overridden because the image now follows
           the section's height, not a card-shaped box. */
        display: block;
        position: absolute;
        inset: 0;
        z-index: 1;
        aspect-ratio: auto;
        height: 100%;
        pointer-events: none;
    }

    .sv-hero-img {
        /* Atmosphere, but the couple should still read. Blur is removed
           entirely - the frame renders sharp. It is kept dark and cinematic
           by the brightness floor (0.62), the contrast(1.08) lift, and the
           differential scrim below (::after), NOT by softening. The heavy
           lifting for text legibility is the scrim: thick under the text
           bands, open over the faces window, so where text sits the image
           barely shows through and where it doesn't the couple comes forward.
           The text is a separate layer above, so its contrast is untouched.
           The 1.06 scale is retained to preserve the established crop and to
           guard against a sub-pixel edge gap. object-position biases higher
           on the frame so subjects' faces aren't sliced by the text overlay.
           (If the sharp frame ever feels too crisp, add a hair of blur(0.5px)
           here - but no blur is the intended default.) */
        filter: saturate(0.62) brightness(0.62) contrast(1.08);
        transform: scale(1.06);
        object-position: center 30%;
    }

    .sv-hero-right::after {
        /* Differential scrim, mapped to where the TEXT sits, not where this
           particular couple stands - the hero image is niche-specific and
           CMS-driven, so the only thing we can rely on across every niche is
           the fixed vertical position of the copy. The scrim is therefore
           thick under the topbar, the title, the subtitle, and the call-bar,
           and OPENS into a near-clear window over the upper third, where a
           portrait crop puts faces and shoulders and where the only copy is
           the short, shadowed gold eyebrow.

           The window opens (7% -> 18%) and closes (18% -> 34%) over ~130px
           each side rather than snapping, so it reads as a soft vignette
           with no visible band edge. faint = the existing "hero clearest
           band" token (0.15), finally put to use. Every stop is an existing
           overlay token; no literal rgbas.

           Stop map over the section height:
             0%   deep    under the nav - dark
             7%   strong  easing down off the top
            18%   faint   clearest point - the couple's faces / shoulders
            26%   soft    window closing
            34%   strong  fully closed exactly as the title begins
            60%   mid     subtitle band, slightly lighter
            82%   strong  CTAs
           100%   deep    under the call-bar - dark */
        content: "";
        position: absolute;
        inset: 0;
        z-index: 2;
        background: linear-gradient(180deg,
            var(--ev-overlay-deep)    0%,
            var(--ev-overlay-strong)  7%,
            var(--ev-overlay-faint)  18%,
            var(--ev-overlay-soft)   26%,
            var(--ev-overlay-strong) 34%,
            var(--ev-overlay-mid)    60%,
            var(--ev-overlay-strong) 82%,
            var(--ev-overlay-deep)  100%);
        pointer-events: none;
    }

    /* Safety net: if no hero image is set, the empty right column should
       not draw the surface-tile background (it'd cover the section). Fall
       back to the section's own --ev-bg so the text rail still reads on
       the standard espresso ground. */
    .sv-hero-right--empty { background: var(--ev-bg); }

    /* Deterministic, no fallback: when a niche has a desktop hero but NO mobile
       crop, phones show an empty hero (the espresso ground + text), never the
       desktop crop. The missing mobile image is a visible gap, not masked. The
       desktop <img> is hidden here and its scrim suppressed; on desktop this
       modifier carries no rules, so the hero renders normally there. */
    .sv-hero-right--no-mobile { background: var(--ev-bg); }
    .sv-hero-right--no-mobile .sv-hero-img { display: none; }
    .sv-hero-right--no-mobile::after { display: none; }

    .sv-hero-left {
        position: relative;
        z-index: 3;
        padding-left: var(--ev-space-5);
        padding-right: var(--ev-space-5);
        min-height: inherit;
    }

    .sv-body .sv-hero-eyebrow { font-size: 0.6875rem; }
    .sv-hero-title { font-size: clamp(2rem, 7vw, 2.5rem); }
    .sv-hero-sub { font-size: 1rem; }

    /* Eyebrow as a masthead-style category label pinned near the top of the
       stage. Lifted out of the centred cluster (which keeps the headline,
       subhead, and CTAs low over the calmer lower half of the frame) so this
       short gold line reads as a clean category label ABOVE the photograph's
       subject - in the quiet band over the couple's heads - rather than
       floating across their chests. The cluster keeps its large top padding,
       so the headline never rises far enough to collide with this label even
       on short phones. */
    .sv-hero-left .sv-hero-eyebrow {
        position: absolute;
        top: var(--ev-space-5);
        left: var(--ev-space-5);
        right: var(--ev-space-5);
        margin: 0;
    }

    /* Lift contrast against the photo. Uses the hero text-shadow tokens
       so the values stay in the design system, not as one-off rgbas. */
    .sv-hero-left .sv-hero-eyebrow,
    .sv-hero-left .sv-hero-title { text-shadow: var(--ev-text-shadow-hero); }
    .sv-hero-left .sv-hero-sub   { text-shadow: var(--ev-text-shadow-hero-sub); }

    /* Secondary CTA hierarchy: the primary "View highlight films" is a solid
       gold pill and must clearly dominate. The "Inquire" ghost is deliberately
       as quiet as it can be while staying tappable - the lightest treatment in
       the system. Light on every axis: an almost-invisible espresso fill, a
       whisper-thin gold hairline, a lighter label weight, and a shorter pill
       (the 48px comfortable tap minimum vs the primary's 56px). The glass
       (backdrop blur) is dropped entirely - it read as extra weight, and the
       dark, scrimmed bottom of the photo carries the label on its own. A soft
       text-shadow stands in for the old glass plate so legibility never
       depends on the fill. */
    .sv-hero-left .sv-btn-ghost {
        background: var(--ev-glass-hero-ghost-soft);
        border-color: var(--ev-glass-hero-ghost-border);
        font-weight: 400;
        min-height: var(--ev-touch);
        text-shadow: var(--ev-text-shadow-hero-sub);
    }

    /* Stack CTAs full-width with a comfortable 56px tap target. Gap between
       the two CTAs uses --ev-space-3 (12px) - tight enough that they read as
       a paired control, loose enough that the second never gets misclicked. */
    .sv-hero-ctas {
        flex-direction: column;
        gap: var(--ev-space-3);
    }
    .sv-hero-cta {
        width: 100%;
        min-height: var(--ev-btn-lg-h);
        justify-content: center;
    }
}

/* ===== SpicyVisionLanding/wwwroot/css/components/_niche-strip.css ===== */
/* =========================================================================
   Niche strip - horizontal scroll-snap row of pills below the hero.
   Mobile gets edge gradients to hint at off-screen items; desktop centres
   the strip and drops the gradients.
   ========================================================================= */

.sv-niche-strip-wrap {
    position: relative;
    background: var(--ev-surface);
    border-top: 1px solid var(--ev-border);
    border-bottom: 1px solid var(--ev-border);
}
.sv-niche-strip-wrap::before,
.sv-niche-strip-wrap::after {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    width: 48px;
    pointer-events: none;
    z-index: 1;
}
.sv-niche-strip-wrap::before { left: 0; background: linear-gradient(90deg, var(--ev-surface), transparent); }
.sv-niche-strip-wrap::after  { right: 0; background: linear-gradient(270deg, var(--ev-surface), transparent); }

.sv-niche-strip {
    display: flex;
    gap: var(--ev-space-3);
    overflow-x: auto;
    padding: var(--ev-space-5) var(--ev-space-4);
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    justify-content: flex-start;
    max-width: var(--ev-max-w);
    margin: 0 auto;
}
.sv-niche-strip::-webkit-scrollbar { display: none; }

@media (min-width: 1024px) {
    .sv-niche-strip { justify-content: center; }
    .sv-niche-strip-wrap::before,
    .sv-niche-strip-wrap::after { display: none; }
}

.sv-niche-pill {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    min-height: var(--ev-touch);
    padding: 0 1.25rem;
    border: 1px solid var(--ev-border);
    border-radius: var(--ev-radius-pill);
    color: var(--ev-text);
    font-weight: 400;
    font-size: 0.95rem;
    letter-spacing: 0.02em;
    white-space: nowrap;
    transition: border-color var(--ev-dur-fast) var(--ev-ease),
                color var(--ev-dur-fast) var(--ev-ease),
                background var(--ev-dur-fast) var(--ev-ease);
}
.sv-niche-pill:hover { border-color: var(--ev-accent); color: var(--ev-accent); }

/* ===== SpicyVisionLanding/wwwroot/css/components/_trust-bar.css ===== */
/* =========================================================================
   Trust bar - per-niche credibility signals rendered as a quiet horizontal
   band between the gallery section and the Bio. Items come from
   Niche.TrustSignals (JSON array). Separated by 4px gold mid-dots; the
   band has gold hairline borders top + bottom matching the brand register.
   ========================================================================= */

.sv-trust-bar {
    background: var(--ev-surface);
    border-top: 1px solid var(--ev-hairline-gold);
    border-bottom: 1px solid var(--ev-hairline-gold);
    padding: var(--ev-space-5) var(--ev-space-4);
}

@media (min-width: 768px) {
    .sv-trust-bar { padding-left: var(--ev-space-6); padding-right: var(--ev-space-6); }
}

.sv-trust-bar-inner {
    max-width: var(--ev-max-w);
    margin: 0 auto;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: var(--ev-space-3) var(--ev-space-5);
    color: var(--ev-muted);
    font-family: var(--ev-font-body);
    font-size: var(--ev-fs-caption);
    text-transform: uppercase;
    letter-spacing: 0.18em;
    line-height: 1.4;
}

.sv-trust-bar-item { color: var(--ev-muted); }

.sv-trust-bar-dot {
    display: inline-block;
    width: 4px;
    height: 4px;
    border-radius: 50%;
    background: var(--ev-accent);
    opacity: 0.6;
    flex-shrink: 0;
}

@media (max-width: 767.98px) {
    /* Stacked column on phones so each signal sits on its own line and the
       dots don't dangle. */
    .sv-trust-bar-inner {
        flex-direction: column;
        gap: var(--ev-space-3);
        text-align: center;
    }
    .sv-trust-bar-dot { display: none; }
}

/* ===== SpicyVisionLanding/wwwroot/css/components/_bio.css ===== */
/* =========================================================================
   Bio - heading, intro-video tile, and bio body assemble into a two-column
   grid on desktop (video left, heading+body stacked on the right) and a
   single column on mobile reordered so the eyebrow/H2 label the section
   FIRST, then the video, then the body paragraphs. The reorder is done via
   named grid areas so the markup keeps a natural desktop reading order.
   ========================================================================= */

.sv-bio {
    display: grid;
    grid-template-columns: 1fr;
    grid-template-areas:
        "heading"
        "video"
        "body";
    gap: var(--ev-space-5);
    align-items: start;
}

.sv-bio-heading { grid-area: heading; text-align: center; }
.sv-bio-video   { grid-area: video; }
.sv-bio-body    {
    grid-area: body;
    text-align: center;
    /* Mobile: present the bio as a soft panel rather than flat text on the
       page. Same card FAMILY as the Google-review card (.sv-review-card) -
       identical gold hairline, --ev-radius-lg corners, and warm
       --ev-elev-review halo - but a deliberately different role. The bio
       sits directly above the reviews in the page order, so to keep it
       reading as the primary "About Emran" statement and not as review #1,
       it uses the darker --ev-surface, the SAME shade as the intro-video
       tile right above it. Bio + video then bond into one unit, while the
       reviews stay on the lighter --ev-surface-2 as a distinct set of proof
       cards. The panel padding doubles as the inset that keeps the prose
       clear of the screen edge. All reset on desktop, where the two-column
       layout and the video tile supply the balance and the body is open
       text beside the video. */
    background: var(--ev-surface);
    border: 1px solid var(--ev-hairline-gold);
    border-radius: var(--ev-radius-lg);
    box-shadow: var(--ev-elev-review);
    padding: var(--ev-space-6) var(--ev-space-5);
}

.sv-bio-heading h2 {
    margin: 0;
    font-size: var(--ev-fs-h2);
}
.sv-bio-heading .sv-section-kicker {
    margin: 0 0 var(--ev-space-2);
}

.sv-bio-body p {
    color: var(--ev-muted);
    margin: 0 0 var(--ev-space-4);
}
.sv-bio-body p:last-child { margin-bottom: 0; }

/* No bio video and no bio image: drop the media area entirely so the section
   is just the heading and the bio text, with no empty tile or column where a
   film would have been. */
.sv-bio--no-media {
    grid-template-areas:
        "heading"
        "body";
}

@media (min-width: 768px) {
    /* Desktop: heading sits centered across the top, then the video and
       body run side by side beneath it. The video is vertically centered
       against the taller paragraph so the height mismatch reads as a
       deliberate visual rest, not lopsided. */
    .sv-bio {
        grid-template-columns: 1.1fr 1fr;
        grid-template-areas:
            "heading heading"
            "video   body";
        gap: var(--ev-space-6) var(--ev-space-8);
        align-items: center;
    }
    .sv-bio-heading {
        text-align: center;
        margin-bottom: var(--ev-space-3);
    }
    .sv-bio-body {
        text-align: left;
        max-width: 52ch;
        padding: 0;
        background: none;
        border: 0;
        box-shadow: none;
    }

    /* No media: single centered column of heading + text. */
    .sv-bio--no-media {
        grid-template-columns: 1fr;
        grid-template-areas:
            "heading"
            "body";
    }
    .sv-bio--no-media .sv-bio-body {
        max-width: 60ch;
        margin-inline: auto;
        text-align: center;
    }
}

/* ---------- Intro video tile ---------- */

/* When BioVideoVimeoId is set, the partial renders this tile instead of
   the static portrait. Reuses _VimeoEmbed for the facade; this wrapper
   supplies the frame, shadow, and the two corner labels.

   Desktop: 16:9 landscape (matches native Vimeo, so the embed slots in
   without any black bars). Mobile: 4:5 portrait so the tile reads as the
   dominant element of the section on a tall phone screen - the actual
   Vimeo iframe still plays 16:9 inside this frame once the user taps the
   facade. */
.sv-bio-video {
    position: relative;
    width: 100%;
    max-width: 720px;
    aspect-ratio: 4 / 5;
    border-radius: var(--ev-radius-lg);
    overflow: hidden;
    margin: 0 auto;
    box-shadow: var(--ev-elev-2);
    background: var(--ev-surface);
}

@media (min-width: 768px) {
    .sv-bio-video { aspect-ratio: 16 / 9; }
}

/* Reuse the album-cover/film-card collapse rule: when a Vimeo facade is
   nested inside a parent that supplies its own aspect ratio, the inner
   embed wrapper drops its padding-bottom trick and fills the parent. */
.sv-bio-video .sv-vimeo-embed {
    position: static;
    padding: 0;
    height: 100%;
}

.sv-bio-video .sv-vimeo-facade {
    border-radius: var(--ev-radius-lg);
}

.sv-bio-video-label {
    position: absolute;
    top: var(--ev-space-3);
    left: var(--ev-space-3);
    z-index: 2;
    display: inline-flex;
    align-items: center;
    gap: 0.3em;
    padding: 6px 10px;
    background: var(--ev-overlay-strong);
    color: var(--ev-text);
    font-family: var(--ev-font-body);
    font-size: 0.7rem;
    font-weight: 500;
    letter-spacing: var(--ev-track-eyebrow);
    text-transform: uppercase;
    border-radius: var(--ev-radius-sm);
    pointer-events: none;
}

.sv-bio-video-label > span { color: var(--ev-accent); opacity: 0.7; }

.sv-bio-video-duration {
    position: absolute;
    bottom: var(--ev-space-3);
    right: var(--ev-space-3);
    z-index: 2;
    padding: 4px 8px;
    background: var(--ev-overlay-strong);
    color: var(--ev-text);
    font-family: var(--ev-font-body);
    font-size: 0.75rem;
    font-weight: 500;
    letter-spacing: 0.02em;
    border-radius: var(--ev-radius-sm);
    pointer-events: none;
}

/* ---------- Still-image fallback ---------- */

/* When no Vimeo ID is wired up but a bio image exists, the image fills the
   tile (object-fit:cover at the same frame the Vimeo facade would use) and
   reads as a plain portrait - no play affordance, since there is no video. */
.sv-bio-video-poster {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center;
    display: block;
}

/* ===== SpicyVisionLanding/wwwroot/css/components/_films.css ===== */
/* =========================================================================
   Films gallery - small set of high-weight tiles.
   The first film spans the full grid width as a featured hero; remaining
   films default to 2-per-row. Admin can promote a niche to 3-per-row via
   Niche.NumberOfVideoInPortfolioRows.
   ========================================================================= */

.sv-films-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--ev-space-4);
}
@media (min-width: 768px) {
    .sv-films-grid {
        grid-template-columns: repeat(2, 1fr);
        gap: var(--ev-space-5);
    }
    .sv-films-grid > :first-child { grid-column: 1 / -1; }
}
@media (min-width: 1024px) {
    .sv-films-grid--three { grid-template-columns: repeat(3, 1fr); }
}

/* Equal-size variant for 3+ video albums - skips the hero-first promotion. */
.sv-films-grid--equal > :first-child { grid-column: auto; }

/* Section kicker margin override - the films section uses a wider gap below
   the kicker than the default. Captured as a class so the partial does not
   need an inline style. */
.sv-films-kicker { margin-bottom: var(--ev-space-6); }

/* 16:9 via padding-bottom - more reliable than aspect-ratio on iframes,
   which can paint at their intrinsic size first and leave empty space. */
.sv-film-card {
    position: relative;
    padding-bottom: 56.25%;
    height: 0;
    background: var(--ev-surface);
    border: 1px solid var(--ev-border);
    border-radius: var(--ev-radius-lg);
    overflow: hidden;
    transition: border-color var(--ev-dur-base) var(--ev-ease),
                transform var(--ev-dur-base) var(--ev-ease);
}
.sv-film-card:hover { border-color: var(--ev-accent); transform: translateY(-2px); }
.sv-film-card iframe {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    display: block;
    border: 0;
}

/* Featured film block - the giant lead video on the home page. */
.sv-featured-video {
    aspect-ratio: 16 / 9;
    border-radius: var(--ev-radius-lg);
    overflow: hidden;
    background: var(--ev-overlay-bg);
    margin-bottom: var(--ev-space-6);
    box-shadow: var(--ev-elev-featured-vid);
}
.sv-featured-video video,
.sv-featured-video iframe {
    width: 100%;
    height: 100%;
    display: block;
    border: 0;
}

/* ===== SpicyVisionLanding/wwwroot/css/components/_photos.css ===== */
/* =========================================================================
   Photos gallery (brick masonry).
   CSS columns preserve each photo's native aspect ratio without forced crop.
   break-inside keeps an item from splitting across columns; margin-bottom
   substitutes for `gap`, which column layouts ignore.
   ========================================================================= */

.sv-gallery {
    column-count: 1;
    column-gap: var(--ev-space-3);
}
@media (min-width: 640px)  { .sv-gallery { column-count: 2; } }
@media (min-width: 1024px) {
    .sv-gallery { column-count: 3; column-gap: var(--ev-space-4); }
}

.sv-gallery-item {
    display: block;
    break-inside: avoid;
    -webkit-column-break-inside: avoid;
    position: relative;
    overflow: hidden;
    border-radius: var(--ev-radius);
    background: var(--ev-surface);
    margin-bottom: var(--ev-space-3);
}
@media (min-width: 1024px) {
    .sv-gallery-item { margin-bottom: var(--ev-space-4); }
}

.sv-gallery-item img {
    width: 100%;
    height: auto;
    display: block;
    transition: transform var(--ev-dur-slow) var(--ev-ease);
}
.sv-gallery-item iframe {
    width: 100%;
    display: block;
    border: 0;
    aspect-ratio: 16 / 9;
}
.sv-gallery-item:hover img { transform: scale(1.04); }

/* Mixed-aspect modifiers - used inside the album editorial layout. */
.sv-gallery-item--wide { grid-column: span 2; aspect-ratio: 16 / 9; }
@media (max-width: 639.98px) {
    .sv-gallery-item--wide { grid-column: span 1; aspect-ratio: 4 / 5; }
}
.sv-gallery-item--tall { grid-row: span 2; aspect-ratio: 3 / 5; }
@media (max-width: 639.98px) {
    .sv-gallery-item--tall { grid-row: span 1; aspect-ratio: 4 / 5; }
}

/* Video tile overlay (the lightGallery video items). */
.sv-gallery-item--video .sv-play-overlay {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: linear-gradient(180deg, transparent 50%, var(--ev-overlay-soft));
    pointer-events: none;
    transition: opacity var(--ev-dur-base);
}
.sv-play-overlay i {
    width: 64px;
    height: 64px;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.9);
    color: var(--ev-text-on-accent);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 1.25rem;
}
.sv-gallery-item--video.is-playing .sv-play-overlay { opacity: 0; }

/* Section-kicker margin override for the standalone Photo Gallery section. */
.sv-photos-kicker { margin-bottom: var(--ev-space-6); }

/* ===== SpicyVisionLanding/wwwroot/css/components/_album-cover.css ===== */
/* =========================================================================
   Vimeo embed - facade then upgrade.
   Default 16:9 responsive container used standalone (e.g. inside a pricing
   card). Inside parents that already supply their own aspect ratio
   (.sv-film-card, .sv-album-cover), the wrapper collapses so the iframe
   positions against the parent box instead of nesting padding-bottom hacks.
   ========================================================================= */

.sv-vimeo-embed {
    position: relative;
    padding-bottom: 56.25%;
    height: 0;
    overflow: hidden;
}
.sv-vimeo-embed iframe {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    display: block;
    border: 0;
}
.sv-film-card .sv-vimeo-embed,
.sv-album-cover .sv-vimeo-embed {
    position: static;
    padding: 0;
    height: 100%;
}

/* Poster facade - the standin button rendered until the user clicks.
   site.js swaps in the real Vimeo iframe on activation. Sized to drop into
   the same box as the eventual iframe in every context. */
.sv-vimeo-facade {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    display: block;
    margin: 0;
    padding: 0;
    border: 0;
    cursor: pointer;
    background: var(--ev-surface);
    overflow: hidden;
    -webkit-appearance: none;
    appearance: none;
}
.sv-vimeo-facade-poster {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    transition: transform 0.4s var(--ev-ease);
}
.sv-vimeo-facade-play {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 64px;
    height: 64px;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.9);
    color: var(--ev-text-on-accent);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: background var(--ev-dur-base) var(--ev-ease),
                color var(--ev-dur-base) var(--ev-ease);
}
.sv-vimeo-facade-play svg {
    width: 26px;
    height: 26px;
    fill: currentColor;
    margin-left: 3px; /* optical centre of the play triangle */
}

.sv-vimeo-facade:hover .sv-vimeo-facade-poster,
.sv-vimeo-facade:focus-visible .sv-vimeo-facade-poster {
    transform: scale(1.04);
}
.sv-vimeo-facade:hover .sv-vimeo-facade-play,
.sv-vimeo-facade:focus-visible .sv-vimeo-facade-play {
    background: var(--ev-accent);
    color: var(--ev-text-on-accent);
}
.sv-vimeo-facade:focus-visible {
    outline: 2px solid var(--ev-accent);
    outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
    .sv-vimeo-facade-poster { transition: none; }
    .sv-vimeo-facade:hover .sv-vimeo-facade-poster,
    .sv-vimeo-facade:focus-visible .sv-vimeo-facade-poster { transform: none; }
}

/* ===== SpicyVisionLanding/wwwroot/css/components/_video-modal.css ===== */
/* =========================================================================
   Video modal - shared overlay that receives the upgraded Vimeo player
   when a facade is clicked. Centered, large, full controls. z-index 3000
   clears every site chrome layer (sticky CTA / topbar / toast top out at 2100).
   ========================================================================= */

.sv-video-modal {
    position: fixed;
    inset: 0;
    z-index: 3000;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--ev-space-4);
    background: var(--ev-overlay-deep);
    opacity: 0;
    transition: opacity var(--ev-dur-fast) var(--ev-ease);
}
/* Author display:flex would otherwise beat the [hidden] UA rule. */
.sv-video-modal[hidden] { display: none; }
.sv-video-modal.is-open { opacity: 1; }
body.sv-modal-open { overflow: hidden; }

.sv-video-modal-backdrop {
    position: absolute;
    inset: 0;
    cursor: pointer;
}
.sv-video-modal-dialog {
    position: relative;
    z-index: 1;
    /* Fit the 16:9 player to whichever of viewport width / height binds
       first, leaving 7rem of headroom for the external close button on
       desktop. */
    width: min(92vw, calc((100vh - 7rem) * 16 / 9));
    width: min(92vw, calc((100dvh - 7rem) * 16 / 9));
    max-width: 1200px;
}
.sv-video-modal-frame {
    position: relative;
    width: 100%;
    aspect-ratio: 16 / 9;
    background: var(--ev-overlay-bg);
    border-radius: var(--ev-radius-lg);
    overflow: hidden;
    box-shadow: var(--ev-elev-modal);
}
.sv-video-modal-frame iframe {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    display: block;
    border: 0;
}
.sv-video-modal-close {
    position: absolute;
    top: -3.25rem;
    right: 0;
    width: 44px;
    height: 44px;
    border: 0;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.14);
    color: var(--ev-google-g-fg);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: background var(--ev-dur-fast) var(--ev-ease),
                color var(--ev-dur-fast) var(--ev-ease);
    /* Stay above the player iframe - on phones the close button overlaps
       the frame, and without z-index the iframe (later in DOM) paints over
       it, leaving the user no visible way to dismiss. */
    z-index: 2;
}
.sv-video-modal-close svg {
    width: 22px;
    height: 22px;
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
}
.sv-video-modal-close:hover,
.sv-video-modal-close:focus-visible {
    background: var(--ev-accent);
    color: var(--ev-text-on-accent);
}
.sv-video-modal-close:focus-visible {
    outline: 2px solid var(--ev-google-g-fg);
    outline-offset: 2px;
}

/* Phones: no headroom above the video, so the close button sits inside
   the top-right corner over the player and the dialog gets more screen. */
@media (max-width: 640px) {
    .sv-video-modal { padding: 0; }
    .sv-video-modal-dialog {
        width: min(96vw, calc((100vh - 2rem) * 16 / 9));
        width: min(96vw, calc((100dvh - 2rem) * 16 / 9));
    }
    .sv-video-modal-frame { border-radius: var(--ev-radius); }
    .sv-video-modal-close {
        top: 8px;
        right: 8px;
        background: rgba(0, 0, 0, 0.55);
    }
}

@media (prefers-reduced-motion: reduce) {
    .sv-video-modal { transition: none; }
}

/* ===== SpicyVisionLanding/wwwroot/css/components/_reviews.css ===== */
/* =========================================================================
   Featured review - one hero proof point: the couple's photo (which doubles
   as the click-to-play film poster) beside their review with a serif
   pull-quote. Only the review at the top of the list renders this, and only
   when it has a featured photo. Photo left / review right on desktop, stacked
   on mobile. The film is lazy: the photo is a .sv-vimeo-facade, so the actual
   Vimeo iframe is only injected by site.js on click (shared modal / in-place
   player) and removed on close. Nothing heavy loads on page load.
   ========================================================================= */
.sv-featured {
    /* Desktop only: on mobile the first review is just the first carousel card (every mobile card is
       featured-style), so the big two-column hero is hidden there. */
    display: none;
    grid-template-columns: 1fr;
    /* A soft warm-dark panel with a faint inner gold glow and only a whisper of a border, so the
       card recedes and the feathered photo + serif quote carry the emotion. */
    background:
        radial-gradient(135% 100% at 16% 22%, var(--ev-accent-tint), transparent 58%),
        var(--ev-surface);
    border: 1px solid var(--ev-hairline-gold-faint);
    border-radius: var(--ev-radius-lg);
    overflow: hidden;
    box-shadow: var(--ev-elev-review-strong);
    margin-bottom: var(--ev-space-5);
}
@media (min-width: 1024px) {
    /* Photo a touch wider than the text, vertically centred, columns butting together so the
       feathered photo edge dissolves straight into the review. */
    .sv-featured { display: grid; grid-template-columns: 1.15fr 1fr; align-items: center; column-gap: 0; }
}

/* First review with no featured photo yet: a single-column featured card (the prominent pull-quote
   still leads) instead of an empty photo panel. The body is width-capped and centred so the full
   width card reads as intentional, not a stretched banner. */
.sv-featured--no-photo { grid-template-columns: 1fr; }
@media (min-width: 1024px) {
    .sv-featured--no-photo { grid-template-columns: 1fr; }
    .sv-featured--no-photo .sv-featured__body { max-width: var(--ev-max-w-prose); margin-inline: auto; width: 100%; }
}

/* --- Photo side (the relative box the absolute poster/facade/iframe fill) --- */
.sv-featured__photo {
    position: relative;
    /* A 3:2 landscape, identical on mobile and desktop, locked to the 1500x1000 upload so the photo
       is shown in FULL with no crop (it is never stretched to the review's height). Landscape keeps
       the supporting photo from dominating the phone, with the review right beside/below it. */
    aspect-ratio: 3 / 2;
    /* Transparent so the feathered photo edges reveal the dark card behind, not a lighter panel. */
    background: transparent;
}
@media (min-width: 1024px) {
    /* Same 3:2 box on desktop; centre it beside the review when the columns differ in height. */
    .sv-featured__photo { min-height: 0; align-self: center; }
}
/* Collapse the Vimeo facade wrapper so its absolute facade/iframe size against
   .sv-featured__photo instead of the default 16:9 padding box (mirrors the
   .sv-film-card rule in _album-cover.css). */
.sv-featured__photo .sv-vimeo-embed {
    position: static;
    padding: 0;
    height: 100%;
}
.sv-featured__img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    /* Feather every edge so the couple emerges softly from the dark card instead of a hard rectangle.
       The fades are WIDE and eased (intermediate half-alpha stops) so there is no visible line - the
       photo dissolves into the card like a cloud. Top/bottom feather ~18%, sides ~14%, all centred so
       the couple stays crisp. mask-composite intersects the two gradients so all four edges feather;
       unsupported browsers just show the full rectangle - graceful. */
    -webkit-mask-image:
        linear-gradient(to bottom, transparent 0%, rgba(0,0,0,0.18) 6%, rgba(0,0,0,0.6) 12%, #000 20%, #000 80%, rgba(0,0,0,0.6) 88%, rgba(0,0,0,0.18) 94%, transparent 100%),
        linear-gradient(to right, transparent 0%, rgba(0,0,0,0.2) 5%, rgba(0,0,0,0.65) 10%, #000 16%, #000 84%, rgba(0,0,0,0.65) 90%, rgba(0,0,0,0.2) 95%, transparent 100%);
    -webkit-mask-composite: source-in;
    mask-image:
        linear-gradient(to bottom, transparent 0%, rgba(0,0,0,0.18) 6%, rgba(0,0,0,0.6) 12%, #000 20%, #000 80%, rgba(0,0,0,0.6) 88%, rgba(0,0,0,0.18) 94%, transparent 100%),
        linear-gradient(to right, transparent 0%, rgba(0,0,0,0.2) 5%, rgba(0,0,0,0.65) 10%, #000 16%, #000 84%, rgba(0,0,0,0.65) 90%, rgba(0,0,0,0.2) 95%, transparent 100%);
    mask-composite: intersect;
}
/* Caption ribbon, top, over a soft warm scrim. */
.sv-featured__cap {
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    z-index: 2;
    padding: var(--ev-space-5) var(--ev-space-5) var(--ev-space-6);
    background: linear-gradient(180deg, var(--ev-photo-scrim), transparent);
    color: var(--ev-text);
    font-size: 0.8125rem;
    letter-spacing: 0.04em;
    text-shadow: var(--ev-text-shadow-hero-sub);
}
/* Bottom scrim so the play affordance reads against any photo. */
.sv-featured__photo .sv-vimeo-facade::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 45%;
    background: linear-gradient(0deg, var(--ev-photo-scrim), transparent);
    z-index: 1;
    pointer-events: none;
}
/* "Watch their film" affordance, bottom-left (only present when a film is set). */
.sv-featured__play {
    position: absolute;
    left: var(--ev-space-5);
    bottom: var(--ev-space-5);
    z-index: 3;
    display: inline-flex;
    align-items: center;
    gap: var(--ev-space-3);
    pointer-events: none;
}
.sv-featured__disc {
    width: 54px;
    height: 54px;
    border-radius: 50%;
    background: var(--ev-photo-scrim);
    -webkit-backdrop-filter: blur(var(--ev-glass-blur-soft));
    backdrop-filter: blur(var(--ev-glass-blur-soft));
    border: 1px solid var(--ev-accent-dim);
    color: var(--ev-accent);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 1rem;
    transition: transform var(--ev-dur-base) var(--ev-ease),
                background var(--ev-dur-base) var(--ev-ease);
}
.sv-featured__play-label {
    font-size: 0.6875rem;
    font-weight: 500;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--ev-text);
    text-shadow: var(--ev-text-shadow-hero-sub);
}
.sv-featured__facade:hover .sv-featured__disc,
.sv-featured__facade:focus-visible .sv-featured__disc {
    transform: scale(1.06);
    background: var(--ev-photo-scrim-deep);
}
.sv-featured__facade:focus-visible {
    outline: 2px solid var(--ev-accent);
    outline-offset: -2px;
}

/* --- Review side --- */
.sv-featured__body {
    padding: var(--ev-space-6);
    display: flex;
    flex-direction: column;
    gap: var(--ev-space-4);
    position: relative;
}
@media (min-width: 1024px) {
    .sv-featured__body { padding: var(--ev-space-7); }
    /* Watermark quote glyph, echoing the mobile quiet-card treatment. */
    .sv-featured__body::before {
        content: "\201C";
        position: absolute;
        top: 10px;
        right: 24px;
        font: italic 500 90px/1 var(--ev-font-display);
        color: var(--ev-accent);
        opacity: 0.13;
        pointer-events: none;
    }
}
.sv-featured__reviewer { display: flex; align-items: center; gap: var(--ev-space-3); }
.sv-featured__avatar {
    width: 50px;
    height: 50px;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-weight: 600;
    font-size: 1.2rem;
    color: var(--ev-google-g-fg);
    overflow: hidden;
    flex-shrink: 0;
    border: 1px solid var(--ev-hairline-gold);
    background: var(--sv-avatar-bg, var(--ev-accent));
}
.sv-featured__avatar img { width: 100%; height: 100%; object-fit: cover; }
.sv-featured__meta { display: flex; flex-direction: column; line-height: 1.3; }
.sv-featured__name { font-weight: 600; color: var(--ev-text); font-size: 1.125rem; }
.sv-featured__sub { color: var(--ev-muted); font-size: 0.8125rem; margin-top: 2px; }
.sv-featured__row { display: flex; align-items: center; gap: var(--ev-space-3); flex-wrap: wrap; }
.sv-featured__quote {
    font-family: var(--ev-font-display);
    font-style: italic;
    font-weight: 400;
    font-size: 1.4rem;
    line-height: 1.32;
    color: var(--ev-ivory);
    text-wrap: pretty;
    margin: 0;
}
@media (min-width: 1024px) {
    .sv-featured__quote { font-size: 1.9rem; line-height: 1.28; }
}
.sv-featured__text {
    color: var(--ev-text);
    font-size: 1rem;
    line-height: 1.7;
    margin: 0;
    white-space: pre-line;
    text-wrap: pretty;
}
@media (min-width: 1024px) { .sv-featured__text { font-size: 1.0625rem; } }
.sv-featured__body .sv-review-google {
    padding-top: var(--ev-space-4);
    border-top: 1px solid var(--ev-hairline-gold);
}

@media (prefers-reduced-motion: reduce) {
    .sv-featured__disc { transition: none; }
    .sv-featured__facade:hover .sv-featured__disc,
    .sv-featured__facade:focus-visible .sv-featured__disc { transform: none; }
}

/* =========================================================================
   Photo lightbox gallery - clicking any couple's photo opens a fullscreen
   still with a film strip of all the couples along the bottom. Built lazily
   by site.js (nothing here loads until the first open).
   ========================================================================= */
/* Click affordance on every gallery photo (hero + cards). */
.sv-photo-zoom { cursor: pointer; }
.sv-photo-zoom:focus-visible { outline: 2px solid var(--ev-accent); outline-offset: 3px; }
.sv-photo-zoom__hint {
    position: absolute;
    top: var(--ev-space-3);
    right: var(--ev-space-3);
    z-index: 3;
    width: 34px;
    height: 34px;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 0.8rem;
    color: var(--ev-text);
    background: var(--ev-photo-scrim);
    -webkit-backdrop-filter: blur(var(--ev-glass-blur-soft));
    backdrop-filter: blur(var(--ev-glass-blur-soft));
    border: 1px solid var(--ev-accent-dim);
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--ev-dur-base) var(--ev-ease);
}
.sv-photo-zoom:hover .sv-photo-zoom__hint,
.sv-photo-zoom:focus-visible .sv-photo-zoom__hint { opacity: 1; }

.sv-review-lightbox {
    position: fixed;
    inset: 0;
    z-index: 4000;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--ev-space-4);
    padding: var(--ev-space-7) var(--ev-space-4) var(--ev-space-4);
    background: var(--ev-overlay-deep);
    -webkit-backdrop-filter: blur(10px);
    backdrop-filter: blur(10px);
    opacity: 0;
    transition: opacity var(--ev-dur-base) var(--ev-ease);
}
.sv-review-lightbox.is-open { opacity: 1; }
.sv-review-lightbox[hidden] { display: none; }

.sv-review-lightbox__close {
    position: absolute;
    top: var(--ev-space-4);
    right: var(--ev-space-4);
    z-index: 2;
    width: var(--ev-touch);
    height: var(--ev-touch);
    border-radius: 50%;
    background: var(--ev-surface-3);
    color: var(--ev-text);
    border: 1px solid var(--ev-border-strong);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 1.1rem;
    cursor: pointer;
}
.sv-review-lightbox__close:hover { background: var(--ev-surface-2); border-color: var(--ev-accent); }

.sv-review-lightbox__stage {
    flex: 1 1 auto;
    min-height: 0;
    width: 100%;
    max-width: var(--ev-max-w);
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--ev-space-3);
}
.sv-review-lightbox__figure {
    margin: 0;
    flex: 1 1 auto;
    min-width: 0;
    height: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--ev-space-4);
}
.sv-review-lightbox__img {
    max-width: 100%;
    max-height: 68vh;
    object-fit: contain;
    border-radius: var(--ev-radius);
    box-shadow: var(--ev-elev-3);
}
.sv-review-lightbox__cap {
    text-align: center;
    max-width: 640px;
}
.sv-review-lightbox__quote {
    display: block;
    font-family: var(--ev-font-display);
    font-style: italic;
    font-size: 1.45rem;
    line-height: 1.3;
    color: var(--ev-ivory);
    text-wrap: pretty;
}
.sv-review-lightbox__name {
    display: block;
    margin-top: var(--ev-space-2);
    font-size: 0.8125rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--ev-muted);
}
.sv-review-lightbox__name .sv-review-google__badge { vertical-align: -3px; margin-right: 6px; }
/* Arrows are absolute overlays (not inline), so they show the same on mobile and desktop and never
   steal width from the image. */
.sv-review-lightbox__nav {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    z-index: 2;
    width: var(--ev-touch);
    height: var(--ev-touch);
    border-radius: 50%;
    background: var(--ev-photo-scrim-deep);
    -webkit-backdrop-filter: blur(var(--ev-glass-blur-soft));
    backdrop-filter: blur(var(--ev-glass-blur-soft));
    color: var(--ev-accent);
    border: 1px solid var(--ev-border-strong);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 1rem;
    cursor: pointer;
}
.sv-review-lightbox__nav--prev { left: var(--ev-space-3); }
.sv-review-lightbox__nav--next { right: var(--ev-space-3); }
.sv-review-lightbox__nav:hover { background: var(--ev-surface-2); border-color: var(--ev-accent); }
.sv-review-lightbox__nav:disabled { opacity: 0.3; cursor: default; }

.sv-review-lightbox__strip {
    flex: 0 0 auto;
    display: flex;
    gap: var(--ev-space-2);
    overflow-x: auto;
    max-width: 100%;
    padding: var(--ev-space-2);
    scrollbar-width: thin;
}
.sv-review-lightbox__thumb {
    flex: 0 0 auto;
    width: 84px;
    aspect-ratio: 3 / 2;
    border-radius: var(--ev-radius-sm);
    overflow: hidden;
    cursor: pointer;
    opacity: 0.5;
    padding: 0;
    background: var(--ev-surface-3);
    border: 2px solid transparent;
    transition: opacity var(--ev-dur-base) var(--ev-ease),
                border-color var(--ev-dur-base) var(--ev-ease);
}
.sv-review-lightbox__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.sv-review-lightbox__thumb:hover { opacity: 0.85; }
.sv-review-lightbox__thumb.is-active { opacity: 1; border-color: var(--ev-accent); }

/* Mobile: arrows stay (as overlays) and swipe also navigates; just give the image a bit more room. */
@media (max-width: 640px) {
    .sv-review-lightbox__img { max-height: 60vh; }
    .sv-review-lightbox__quote { font-size: 1.2rem; }
}

@media (prefers-reduced-motion: reduce) {
    .sv-review-lightbox,
    .sv-photo-zoom__hint { transition: none; }
}

/* =========================================================================
   Google reviews block - desktop grid of three featured cards, mobile
   horizontal scroll-snap carousel. Same DOM, different layout.
   ========================================================================= */

.sv-reviews-header { text-align: center; margin-bottom: var(--ev-space-6); }
.sv-reviews-trust {
    color: var(--ev-muted);
    font-size: 1rem;
    max-width: 540px;
    margin: var(--ev-space-3) auto 0;
    line-height: 1.55;
}
.sv-reviews-trust-star { color: var(--ev-star); margin-left: 1px; }
.sv-reviews-trust-rating {
    font-family: var(--ev-font-display);
    font-size: 1.45em;
    font-weight: 500;
    line-height: 1;
    color: var(--ev-text-strong);
}

/* Mobile carousel mechanics. We deliberately do NOT use overflow-x: auto +
   scroll-snap here, because a native horizontal scroll container traps
   vertical touches on iOS Safari even with touch-action: pan-x, blocking
   page scroll. Instead the stage clips, the track positions itself with
   transform: translateX, and swipes are JS-driven. Vertical drags on the
   carousel are never claimed by any horizontal scroller because no such
   scroller exists, so page scroll works naturally. */
.sv-reviews-stage {
    position: relative;
    overflow: hidden;
    /* Height follows the active card (set by site.js) so a short text-only card doesn't get padded
       out to a photo card's height - no more big empty gap. */
    transition: height var(--ev-dur-base) var(--ev-ease);
}

.sv-reviews-track {
    display: flex;
    /* Cards keep their own height (a no-photo card stays short); the stage clips to the active one. */
    align-items: flex-start;
    gap: var(--ev-space-4);
    padding-left: var(--ev-space-4);
    padding-right: var(--ev-space-4);
    padding-bottom: var(--ev-space-4);
    transform: translateX(var(--reviews-track-offset, 0px));
    transition: transform 0.3s var(--ev-ease);
    will-change: transform;
    /* pan-y lets the browser handle vertical pans (= page scroll) and
       prevents it from initiating any horizontal pan of its own. Horizontal
       swipes are read in JS as touchmove deltas applied to the transform. */
    touch-action: pan-y;
}

.sv-reviews-slide {
    /* Slightly less than full width so the next card peeks on the right -
       the strongest possible visual signal that this is a carousel without
       drawing any extra chrome. */
    flex: 0 0 86%;
}

@media (min-width: 768px) {
    .sv-reviews-slide { flex: 0 0 calc(50% - var(--ev-space-3)); }
}

@media (min-width: 1024px) {
    .sv-reviews-stage { overflow: visible; }
    .sv-reviews-track {
        display: grid;
        grid-template-columns: repeat(3, 1fr);
        gap: var(--ev-space-5);
        padding-bottom: 0;
        /* stretch (not start) so all three cards in a row take the height of the tallest - reviews vary
           in length, and equal-height cards read as a clean set instead of a ragged staircase. Each card
           is a flex column whose bottom group (Google badge, plus the closing photo when present) is
           pushed to the bottom, so attributions and photo edges line up across the row. */
        align-items: stretch;
        transform: none;
        transition: none;
        touch-action: auto;
    }
    .sv-reviews-slide { flex: initial; }
}

.sv-review-card {
    /* Lifted one tier above the page surface so the card reads as a distinct
       plate against the espresso background, with a warm gold hairline and a
       soft warm halo instead of a hard border. */
    background: var(--ev-surface-2);
    border: 1px solid var(--ev-hairline-gold);
    border-radius: var(--ev-radius-lg);
    padding: var(--ev-space-6);
    display: flex;
    flex-direction: column;
    gap: var(--ev-space-4);
    box-shadow: var(--ev-elev-review);
    transition: border-color var(--ev-dur-base) var(--ev-ease),
                box-shadow var(--ev-dur-base) var(--ev-ease),
                transform var(--ev-dur-base) var(--ev-ease);
}
.sv-review-card:hover {
    border-color: var(--ev-border-strong);
    box-shadow: var(--ev-elev-review-strong);
}

/* The couple's still on a review card - a 3:2 band feathered into the card so it emerges from the
   dark (same language as the hero). Only present on reviews that have a photo. */
.sv-review-card__photo {
    position: relative;
    aspect-ratio: 3 / 2;
    /* The photo sits at the BOTTOM of the card on every breakpoint, after the review - leading with the
       icon, name, stars and review keeps it reading as a review with a closing still, not a photo with
       a caption. (order:90 places it last in the flex column; the review parts default to order 0.) */
    order: 90;
    margin-top: var(--ev-space-3);
}
.sv-review-card__img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    /* Symmetric soft feather so it melts whether it sits above or below the text. */
    -webkit-mask-image:
        linear-gradient(to bottom, transparent 0%, rgba(0,0,0,0.5) 7%, #000 17%, #000 83%, rgba(0,0,0,0.5) 93%, transparent 100%),
        linear-gradient(to right, transparent 0%, rgba(0,0,0,0.5) 6%, #000 15%, #000 85%, rgba(0,0,0,0.5) 94%, transparent 100%);
    -webkit-mask-composite: source-in;
    mask-image:
        linear-gradient(to bottom, transparent 0%, rgba(0,0,0,0.5) 7%, #000 17%, #000 83%, rgba(0,0,0,0.5) 93%, transparent 100%),
        linear-gradient(to right, transparent 0%, rgba(0,0,0,0.5) 6%, #000 15%, #000 85%, rgba(0,0,0,0.5) 94%, transparent 100%);
    mask-composite: intersect;
}
/* With a photo as the closing element, don't let the Google badge's bottom-stick (margin-top:auto,
   used for equal-height text cards) leave a gap above the photo - let the review flow into it. This is
   the right behaviour for the content-sized mobile carousel, where each card hugs its own content. */
.sv-review-card--has-photo .sv-review-google { margin-top: 0; }
/* Desktop 3-up grid only: the track is stretched to equal height, so re-enable the badge's bottom-stick
   on photo cards too. The badge (and the photo grouped right under it) drop to the bottom of the card,
   absorbing the extra height as a clean gap above the attribution. Without this, a short review's slack
   would open up below the photo instead. Source-ordered after the rule above so it wins at >=1024px. */
@media (min-width: 1024px) {
    .sv-review-card--has-photo .sv-review-google { margin-top: auto; }
}

.sv-review-head { display: flex; gap: var(--ev-space-3); align-items: center; }
.sv-review-avatar {
    width: 48px;
    height: 48px;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-weight: 600;
    color: var(--ev-google-g-fg);
    font-family: var(--ev-font-body);
    overflow: hidden;
    flex-shrink: 0;
    font-size: 1.15rem;
    background: var(--sv-avatar-bg, var(--ev-accent));
}
.sv-review-avatar img { width: 100%; height: 100%; object-fit: cover; }

.sv-review-meta { display: flex; flex-direction: column; line-height: 1.3; }
.sv-review-name { font-weight: 600; color: var(--ev-text); font-size: 1.125rem; letter-spacing: 0.005em; }
.sv-review-sub { color: var(--ev-muted); font-size: 0.875rem; margin-top: 2px; }

.sv-review-row { display: flex; align-items: center; gap: var(--ev-space-3); flex-wrap: wrap; }
.sv-stars { color: var(--ev-star); letter-spacing: 2px; font-size: 1rem; }
.sv-review-time { color: var(--ev-muted); font-size: 0.875rem; }

/* Per-card pull-quote: a serif highlight line shown on every review card (mobile carousel and the
   desktop 3-up grid), so each supporting review reads as a mini featured card under the big hero. */
.sv-review-quote {
    font-family: var(--ev-font-display);
    font-style: italic;
    font-weight: 400;
    font-size: 1.25rem;
    line-height: 1.34;
    color: var(--ev-ivory);
    text-wrap: pretty;
    margin: 0;
}

.sv-review-text {
    color: var(--ev-text);
    font-size: 1.0625rem;
    line-height: 1.7;
    margin: 0;
    white-space: pre-line;
}
@media (min-width: 1024px) { .sv-review-text { font-size: 1.125rem; } }

/* CSS line-clamp caps card height; site.js removes the clamp on Read more. */
.sv-review-text--clamped {
    display: -webkit-box;
    -webkit-line-clamp: 6;
    line-clamp: 6;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.sv-review-readmore {
    align-self: flex-start;
    background: none;
    border: 0;
    padding: 0;
    margin: calc(var(--ev-space-2) * -1) 0 0;
    color: var(--ev-accent);
    font: inherit;
    font-size: 1rem;
    font-weight: 500;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    min-height: var(--ev-touch);
}
.sv-review-readmore:hover { color: var(--ev-accent-hover); }
.sv-review-readmore:focus-visible {
    outline: 2px solid var(--ev-accent);
    outline-offset: 4px;
    border-radius: 4px;
}
.sv-review-readmore[hidden] { display: none; }
.sv-review-readmore[aria-expanded="true"] i { transform: rotate(180deg); }
.sv-review-readmore i { transition: transform var(--ev-dur-fast) var(--ev-ease); }

.sv-review-google {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    color: var(--ev-muted);
    font-size: 0.8125rem;
    margin-top: auto;
    letter-spacing: 0.02em;
}

/* Google G badge - the conic gradient is Google brand identity, kept as-is. */
.sv-review-google__badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    font-family: var(--ev-font-google-badge);
    font-weight: 700;
    font-size: 13px;
    line-height: 1;
    background: conic-gradient(from 0deg,
        var(--ev-google-blue)   0deg   90deg,
        var(--ev-google-red)   90deg  180deg,
        var(--ev-google-yellow) 180deg 270deg,
        var(--ev-google-green)  270deg 360deg);
    position: relative;
}
.sv-review-google__badge::after {
    content: "G";
    position: absolute;
    inset: 2px;
    background: var(--ev-surface);
    border-radius: 50%;
    color: var(--ev-google-g-fg);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 11px;
    font-weight: 700;
}

/* Desktop: the first review is the big hero above, so hide its duplicate card in the grid. Hide
   reviews past the first grid row until "Show all" sets is-expanded. */
@media (min-width: 1024px) {
    .sv-reviews-slide--first { display: none; }
    .sv-reviews-slide--extra { display: none; }
    .sv-reviews-track.is-expanded .sv-reviews-slide--extra { display: flex; }
}

/* Dot pagination - one dot per review, above the carousel. The active dot
   stretches into a gold pill. Hidden at desktop where the grid layout shows
   everything at once and pagination is irrelevant. */
.sv-reviews-dots {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 6px;
    margin: var(--ev-space-2) var(--ev-space-5) var(--ev-space-4);
}
.sv-reviews-dots span {
    width: 6px;
    height: 6px;
    border-radius: var(--ev-radius-pill);
    background: var(--ev-accent-tint-2);
    transition: background var(--ev-dur-base) var(--ev-ease),
                width var(--ev-dur-base) var(--ev-ease);
}
.sv-reviews-dots span.active {
    background: var(--ev-accent);
    width: 22px;
}

/* Lightbox-style overlay arrows. Sit on top of the active card vertically
   centered, fade in only when the user is interacting with the carousel
   (.is-active class added by site.js on touch / scroll, auto-removed after
   3 seconds). Hidden on desktop where the grid layout has no scroll axis. */
.sv-reviews-arrow {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 44px;
    height: 44px;
    border-radius: 50%;
    background: rgba(14, 8, 6, 0.78);
    -webkit-backdrop-filter: blur(8px);
    backdrop-filter: blur(8px);
    border: 1px solid var(--ev-border-strong);
    color: var(--ev-accent);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    box-shadow: var(--ev-elev-2);
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--ev-dur-base) var(--ev-ease),
                transform var(--ev-dur-base) var(--ev-ease),
                background var(--ev-dur-base) var(--ev-ease),
                border-color var(--ev-dur-base) var(--ev-ease);
    z-index: 5;
    cursor: pointer;
    font-size: 0.875rem;
}
.sv-reviews-arrow--prev { left: 8px; }
.sv-reviews-arrow--next { right: 8px; }
.sv-reviews-arrow:hover { background: rgba(14, 8, 6, 0.92); border-color: var(--ev-accent); }
.sv-reviews-arrow:active { transform: translateY(-50%) scale(0.92); }
.sv-reviews-arrow:focus { outline: none; }
.sv-reviews-arrow:focus-visible { outline: 2px solid var(--ev-accent); outline-offset: 2px; }
/* Kill the rectangular tap highlight (WebKit paints it on the button's bounding
   box, which looks square behind the round arrow). The :active scale below
   already provides press feedback. touch-action: manipulation lets vertical
   drags on the arrow bubble up to the page so the user can still scroll the
   page when their finger happens to land on an arrow. */
.sv-reviews-arrow {
    -webkit-tap-highlight-color: transparent;
    touch-action: manipulation;
}
.sv-reviews-stage.is-active .sv-reviews-arrow {
    opacity: 1;
    pointer-events: auto;
}
/* !important: this rule fights the .is-active selector above. Both target the
   same element when the carousel is being interacted with, but a disabled
   arrow must always read as disabled. */
.sv-reviews-stage.is-active .sv-reviews-arrow:disabled {
    opacity: 0.30 !important;
    pointer-events: none;
}

@media (min-width: 1024px) {
    .sv-reviews-dots,
    .sv-reviews-arrow { display: none; }
}

.sv-reviews-cta {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--ev-space-3);
    margin-top: var(--ev-space-7);
}
@media (min-width: 640px) {
    .sv-reviews-cta { flex-direction: row; justify-content: center; gap: var(--ev-space-4); }
}

/* Mobile/tablet: swipe is the affordance; the desktop Show all toggle is hidden. */
@media (max-width: 1023.98px) { .sv-reviews-toggle { display: none; } }

.sv-reviews-toggle[aria-expanded="true"] [data-sv-reviews-toggle-icon] { transform: rotate(180deg); }
.sv-reviews-toggle [data-sv-reviews-toggle-icon] { transition: transform var(--ev-dur-fast) var(--ev-ease); margin-left: 6px; }

.sv-reviews-google-cta { gap: 10px; }
.sv-reviews-google-cta .sv-review-google__badge { width: 18px; height: 18px; }
.sv-reviews-google-cta .sv-review-google__badge::after { font-size: 10px; }

/* =========================================================================
   Mobile (<768px): refine the carousel as a self-contained swipeable unit.
   Tighter card padding, a decorative quote glyph, a 5-line clamp on body
   text, and breathing room before the next section.
   ========================================================================= */
@media (max-width: 767.98px) {
    .sv-reviews { padding-bottom: var(--ev-space-9); }
    .sv-reviews-cta { margin-top: var(--ev-space-6); }

    .sv-reviews-slide { flex: 0 0 84%; scroll-snap-align: center; }

    .sv-review-card {
        position: relative;
        padding: 26px 24px 22px;
        overflow: hidden;
        gap: 16px;
    }
    /* A text-only card sizes to its content (the carousel height adapts per card), so no empty gap.
       Keep a gentle floor so a one-line review still feels like a card. */
    .sv-review-card:not(.sv-review-card--has-photo) { min-height: 220px; }

    /* Photo cards: the still bleeds to the full card width at the bottom for a punchy, immersive close,
       feathered only at the TOP so it melts up out of the review (sides/bottom meet the card's rounded
       edge, which clips them). */
    .sv-review-card--has-photo .sv-review-card__photo {
        margin: var(--ev-space-3) -24px -22px;
    }
    .sv-review-card--has-photo .sv-review-card__img {
        -webkit-mask-image: linear-gradient(to bottom, transparent 0%, rgba(0,0,0,0.5) 7%, #000 18%, #000 100%);
        -webkit-mask-composite: source-over;
        mask-image: linear-gradient(to bottom, transparent 0%, rgba(0,0,0,0.5) 7%, #000 18%, #000 100%);
        mask-composite: add;
    }
    /* Decorative oversized quote glyph, watermarked behind the header. Suppressed when the card has a
       photo (it would sit on top of the image). */
    .sv-review-card::before {
        content: "\201C";
        position: absolute;
        top: 12px;
        right: 18px;
        font: italic 500 64px/1 var(--ev-font-display);
        color: var(--ev-accent);
        opacity: 0.14;
        pointer-events: none;
    }
    .sv-review-card--has-photo::before { content: none; }

    .sv-review-avatar { width: 42px; height: 42px; }
    .sv-review-name { font-size: 1rem; font-weight: 650; }
    .sv-review-sub { font-size: 0.72rem; letter-spacing: 0.02em; }
    .sv-review-row { gap: 10px; }
    .sv-stars { font-size: 0.82rem; letter-spacing: 1.5px; }
    .sv-review-time { font-size: 0.72rem; letter-spacing: 0.02em; }

    .sv-review-text {
        font-size: 0.94rem;
        line-height: 1.72;
        text-wrap: pretty;
    }
    .sv-review-text--clamped {
        -webkit-line-clamp: 5;
        line-clamp: 5;
    }

    .sv-review-readmore {
        font-size: 0.78rem;
        letter-spacing: 0.04em;
        gap: 5px;
        min-height: 0;
        border-bottom: 1px solid var(--ev-accent-tint-2);
        padding: 0 0 2px;
        margin: 0;
    }
    .sv-review-readmore:hover { border-bottom-color: var(--ev-accent); }
    .sv-review-readmore i { font-size: 9px; }

    .sv-review-google {
        margin-top: auto;
        padding-top: 12px;
        border-top: 1px solid var(--ev-hairline-gold);
        font-size: 0.7rem;
        font-weight: 500;
        letter-spacing: 0.10em;
        text-transform: uppercase;
    }

    /* Cards with a photo: the photo sits at the bottom (after the review), so let the card flow
       naturally top-to-bottom and size to content - no auto gap pushing the badge down, and the
       photo is the closing note. */
    .sv-review-card--has-photo { min-height: 0; }
    .sv-review-card--has-photo .sv-review-google { margin-top: 0; }
}

/* ===== SpicyVisionLanding/wwwroot/css/components/_pricing.css ===== */
/* =========================================================================
   Pricing - editorial "Investment" treatment.
   Gold sine-wave divider + printed-menu card with optional Most Popular
   ribbon. CTA is a deliberately rectangular outline button (radius 2px).
   ========================================================================= */

.sv-pricing { text-align: center; }

.sv-pricing-header {
    max-width: 720px;
    margin: 0 auto var(--ev-space-5);
}

.sv-pricing-eyebrow {
    font-family: var(--ev-font-display);
    font-style: italic;
    font-weight: 400;
    color: var(--ev-accent);
    font-size: clamp(2.25rem, 4.5vw, 3rem);
    line-height: 1;
    margin: 0 0 var(--ev-space-3);
}

.sv-pricing-title {
    font-family: var(--ev-font-display);
    font-weight: 500;
    font-size: var(--ev-fs-h2);
    color: var(--ev-text);
    margin: 0 0 var(--ev-space-4);
    letter-spacing: 0.01em;
}

.sv-pricing-tagline {
    color: var(--ev-muted);
    font-size: 1.0625rem;
    line-height: 1.6;
    margin: 0;
}

/* Curved gold sine-wave divider */
.sv-pricing-divider {
    max-width: 620px;
    margin: var(--ev-space-5) auto var(--ev-space-7);
    padding: 0 var(--ev-space-4);
}
.sv-pricing-divider svg {
    width: 100%;
    height: 14px;
    display: block;
    overflow: visible;
}
.sv-pricing-divider path {
    fill: none;
    stroke: var(--ev-accent);
    stroke-width: 1;
    opacity: 0.7;
}

.sv-pricing-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--ev-space-5);
    padding-top: var(--ev-space-3); /* room for the ribbon tail */
    max-width: var(--ev-max-w-pricing);
    margin: 0 auto;
}
@media (min-width: 768px) {
    .sv-pricing-grid {
        grid-template-columns: repeat(2, 1fr);
        gap: var(--ev-space-5);
    }
}
/* From 1024px, let the grid adapt to card count: 3 cards fit in one row
   (~370px each), 2 cards share the row a bit wider, 4+ cards wrap evenly.
   The wider max-width gives the 3-up layout breathing room without
   stretching the 2-up case beyond what the cards can fill comfortably. */
@media (min-width: 1024px) {
    .sv-pricing-grid {
        grid-template-columns: repeat(auto-fit, minmax(290px, 1fr));
        max-width: 1200px;
    }
}

.sv-pricing-footnote {
    color: var(--ev-muted);
    font-style: italic;
    margin: var(--ev-space-7) auto 0;
    max-width: 640px;
    font-size: 1rem;
}

.sv-pricing-card {
    position: relative;
    background: linear-gradient(180deg, rgba(36, 24, 17, 0.75), rgba(26, 18, 14, 0.55));
    border: 1px solid rgba(201, 163, 116, 0.25);
    border-radius: var(--ev-radius-sm);
    padding: var(--ev-space-8) var(--ev-space-6) var(--ev-space-7);
    display: flex;
    flex-direction: column;
    gap: var(--ev-space-4);
    text-align: center;
    overflow: hidden;
    transition: border-color var(--ev-dur-base) var(--ev-ease),
                transform var(--ev-dur-base) var(--ev-ease);
}
.sv-pricing-card:hover {
    border-color: var(--ev-accent);
    transform: translateY(-3px);
}

.sv-pricing-card--featured {
    border-color: var(--ev-accent);
    box-shadow: var(--ev-elev-featured);
}

.sv-pricing-ribbon {
    /* Fixed-width centred banner that crosses the card's top-left corner.

       Geometry: the card has overflow:hidden, so the visible portion of the
       ribbon is the slice where the rotated band intersects the card's
       top-left corner. That visible slice is bounded by the line y = -x + 2M
       (slope -1 through the rotation centre M,M) and is symmetric only when
       the rotation centre sits on the diagonal x = y. With a 200px wide,
       ~24px tall element, the centre is at (left + 100, top + 12), so a
       symmetric crop requires  left = top - 88. We pick top:32 -> left:-56
       so the centre lands at (44, 44) and "MOST POPULAR" reads centred
       inside the corner regardless of card width. */
    position: absolute;
    top: 32px;
    left: -56px;
    width: 200px;
    transform: rotate(-45deg);
    transform-origin: center;
    background: var(--ev-accent);
    color: var(--ev-text-on-accent);
    text-align: center;
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    padding: 5px 0;
    box-shadow: var(--ev-elev-pricing-ribbon);
    white-space: nowrap;
    z-index: 1;
}

.sv-pricing-card-title {
    font-family: var(--ev-font-display);
    font-weight: 500;
    font-size: clamp(1.5rem, 2.5vw, 1.75rem);
    line-height: 1.15;
    color: var(--ev-text);
    margin: 0;
}

.sv-pricing-card-price {
    margin: 0;
    color: var(--ev-text);
    font-size: 1.25rem;
    letter-spacing: 0.01em;
}
.sv-pricing-card-price span {
    font-family: var(--ev-font-display);
    font-weight: 500;
    color: var(--ev-accent);
    font-size: 1.5rem;
    margin-left: 0.25rem;
}

/* Optional value-proposition sentence between the price and the gem
   divider. Muted cream so it does not compete with the gold price line
   above or the feature list below. */
.sv-pricing-card-desc {
    margin: 0;
    color: var(--ev-muted);
    font-size: 0.9375rem;
    line-height: 1.5;
    text-wrap: pretty;
    max-width: 32ch;
    margin-inline: auto;
}

/* Gold hairline with centre gem - divider between price and features. */
.sv-pricing-card-sep {
    display: flex;
    align-items: center;
    gap: var(--ev-space-3);
    margin: var(--ev-space-2) auto;
    width: 80%;
    color: var(--ev-accent);
    opacity: 0.8;
}
.sv-pricing-card-sep span {
    flex: 1;
    height: 1px;
    background: linear-gradient(90deg, transparent, var(--ev-accent), transparent);
}
.sv-pricing-card-sep i { font-size: 0.75rem; }

/* The list block sizes to its widest bullet (fit-content) and is centered
   inside the card via margin-inline:auto. All <li> children share that
   single left edge, so the bullets line up vertically even though the
   block as a whole sits centered in the card. */
.sv-pricing-card-features {
    list-style: none;
    padding: 0;
    margin: 0 auto;
    width: fit-content;
    max-width: 100%;
    display: flex;
    flex-direction: column;
    gap: var(--ev-space-3);
    color: var(--ev-text);
    font-size: 1rem;
    line-height: 1.5;
    text-align: left;
}
.sv-pricing-card-features li {
    position: relative;
    padding-left: 1.25rem;
}
.sv-pricing-card-features li::before {
    content: "·";
    position: absolute;
    left: 0.35rem;
    top: -0.1rem;
    color: var(--ev-accent);
    font-size: 1.25rem;
    line-height: 1;
}

.sv-pricing-card-note {
    color: var(--ev-muted);
    font-size: 0.9375rem;
    font-style: italic;
    margin: 0;
}
.sv-pricing-card-note strong {
    color: var(--ev-text);
    font-style: normal;
}

/* INQUIRE button - deliberate printed-menu rectangle (radius 2px) with
   heavy letter-spacing and an outline-fill hover swap. */
.sv-pricing-card-cta {
    display: inline-block;
    margin: var(--ev-space-4) auto 0;
    padding: 0.9rem 2.5rem;
    border: 1px solid var(--ev-accent);
    border-radius: var(--ev-radius-xs);
    color: var(--ev-accent);
    font-family: var(--ev-font-body);
    font-weight: 500;
    font-size: 0.875rem;
    letter-spacing: var(--ev-track-eyebrow);
    text-transform: uppercase;
    text-decoration: none;
    min-height: var(--ev-touch);
    min-width: 180px;
    text-align: center;
    line-height: 1.6;
    transition: background var(--ev-dur-fast) var(--ev-ease),
                color var(--ev-dur-fast) var(--ev-ease);
}
.sv-pricing-card-cta:hover {
    background: var(--ev-accent);
    color: var(--ev-text-on-accent);
}
.sv-body a.sv-pricing-card-cta { color: var(--ev-accent); }
.sv-body a.sv-pricing-card-cta:hover { color: var(--ev-text-on-accent); }

/* ===== SpicyVisionLanding/wwwroot/css/components/_callus.css ===== */
/* =========================================================================
   Call-us CTA section - tonal shift to surface with a soft gold radial glow.
   Lives between regular sections to mark the conversion moment.
   ========================================================================= */

.sv-callus {
    background: linear-gradient(180deg, var(--ev-surface) 0%, var(--ev-bg) 100%);
    padding: var(--ev-space-9) var(--ev-space-4);
    text-align: center;
    border-top: 1px solid var(--ev-border);
    border-bottom: 1px solid var(--ev-border);
}
.sv-callus h2 {
    font-size: var(--ev-fs-h1);
    margin: 0 0 var(--ev-space-4);
}
.sv-callus-phone {
    display: inline-block;
    font-family: var(--ev-font-display);
    font-size: clamp(2rem, 8vw, 4rem);
    color: var(--ev-accent);
    letter-spacing: 0.02em;
    margin-top: var(--ev-space-3);
}

/* Lead copy under the headline - reads as a single quiet paragraph. */
.sv-callus-lead {
    color: var(--ev-muted);
    max-width: 560px;
    margin: 0 auto;
}

/* Phone icon optical sizing inside the giant marquee number. */
.sv-callus-phone i.fa-phone {
    font-size: 0.6em;
    margin-right: 0.3em;
}

/* ===== SpicyVisionLanding/wwwroot/css/components/_related-services.css ===== */
/* =========================================================================
   Related services strip - curated cross-links at the bottom of each
   landing page. Flex + wrap + centre so 1-2 cards centre cleanly instead
   of left-hugging like a 3-col grid would.
   ========================================================================= */

.sv-services-grid {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--ev-space-3);
}
@media (min-width: 1024px) {
    .sv-services-grid { gap: var(--ev-space-4); }
}

.sv-service-card {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--ev-space-3);
    padding: var(--ev-space-4) var(--ev-space-5);
    background: var(--ev-surface);
    border: 1px solid var(--ev-border);
    border-radius: var(--ev-radius);
    color: var(--ev-text);
    text-decoration: none;
    min-height: var(--ev-touch);
    flex: 1 1 260px;
    max-width: 360px;
    transition: border-color var(--ev-dur-fast) var(--ev-ease),
                transform var(--ev-dur-fast) var(--ev-ease),
                background var(--ev-dur-fast) var(--ev-ease);
}
.sv-service-card:hover {
    border-color: var(--ev-accent);
    transform: translateY(-2px);
    background: rgba(201, 163, 116, 0.06);
}
.sv-service-name {
    font-family: var(--ev-font-display);
    font-weight: 500;
    font-size: 1.125rem;
    letter-spacing: 0.01em;
}
.sv-service-arrow {
    color: var(--ev-accent);
    font-size: 0.875rem;
    transition: transform var(--ev-dur-fast) var(--ev-ease);
}
.sv-service-card:hover .sv-service-arrow { transform: translateX(3px); }

/* Modifier: the .sv-section-kicker that sits above this grid sometimes needs
   extra breathing room. Captured here so the partial does not need an inline
   style. */
.sv-related-services-kicker { margin-bottom: var(--ev-space-6); }

/* /services hub page - the full-listing variant. */
.sv-services-page {
    padding: var(--ev-space-8) 0 var(--ev-space-9);
}
.sv-services-header {
    text-align: center;
    margin-bottom: var(--ev-space-8);
}
.sv-services-title {
    font-family: var(--ev-font-display);
    font-size: clamp(2rem, 4vw, 3rem);
    margin: var(--ev-space-2) 0 var(--ev-space-3);
}
.sv-services-sub {
    color: var(--ev-muted);
    max-width: 42rem;
    margin: 0 auto;
}
.sv-services-empty {
    text-align: center;
    color: var(--ev-muted);
    padding: var(--ev-space-8) 0;
}
.sv-service-card--detailed {
    align-items: center;
    padding: var(--ev-space-5) var(--ev-space-5);
    min-height: 5rem;
}
.sv-service-card-body {
    display: flex;
    flex-direction: column;
    gap: var(--ev-space-1);
    flex: 1;
}
.sv-service-desc {
    color: var(--ev-muted);
    font-size: 0.9rem;
    line-height: 1.45;
}
.sv-service-icon {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 3rem;
    height: 3rem;
    border-radius: var(--ev-radius-pill);
    background: rgba(201, 163, 116, 0.10);
    border: 1px solid rgba(201, 163, 116, 0.22);
    color: var(--ev-accent);
    font-size: 1.25rem;
    transition: background var(--ev-dur-fast) var(--ev-ease),
                border-color var(--ev-dur-fast) var(--ev-ease),
                transform var(--ev-dur-fast) var(--ev-ease);
}
.sv-service-card:hover .sv-service-icon {
    background: rgba(201, 163, 116, 0.18);
    border-color: var(--ev-accent);
    transform: scale(1.05);
}
.sv-service-card--detailed .sv-service-name {
    font-size: 1.25rem;
}
@media (min-width: 1024px) {
    .sv-service-card--detailed .sv-service-icon {
        width: 3.25rem;
        height: 3.25rem;
        font-size: 1.35rem;
    }
}

/* ===== SpicyVisionLanding/wwwroot/css/components/_cross-sell.css ===== */
/* =========================================================================
   Cross-sell - one premium editorial panel per related service. Calm, never
   an ad banner: the same warm --ev-surface, gold hairline, and soft review-card
   halo used elsewhere.

   Mobile leads with the text so the reader knows what the section is before
   any image appears: eyebrow, headline, body, THEN a slightly inset image,
   THEN the ghost button. The image is held back a little - shorter, inset, and
   rounded - so it introduces rather than dominates.

   Desktop relaxes into a two-column split: the image runs full-height down one
   side while the text sits centered on the other. --reverse swaps the image to
   the other side for visual rhythm across stacked panels.
   ========================================================================= */

.sv-crosssell-stack {
    display: flex;
    flex-direction: column;
    gap: var(--ev-space-6);
}

.sv-crosssell {
    display: grid;
    grid-template-columns: 1fr;
    grid-template-areas:
        "intro"
        "media"
        "cta";
    background: var(--ev-surface);
    border: 1px solid var(--ev-hairline-gold);
    border-radius: var(--ev-radius-lg);
    box-shadow: var(--ev-elev-review);
    overflow: hidden;
}

/* No image resolved - drop the media row so the text and button stack cleanly. */
.sv-crosssell--text-only {
    grid-template-areas:
        "intro"
        "cta";
}

.sv-crosssell-intro {
    grid-area: intro;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--ev-space-3);
    padding: var(--ev-space-6) var(--ev-space-5) 0;
}

.sv-crosssell-eyebrow {
    margin: 0;
    color: var(--ev-accent);
    font-family: var(--ev-font-body);
    font-size: var(--ev-fs-eyebrow);
    font-weight: 500;
    letter-spacing: var(--ev-track-eyebrow);
    text-transform: uppercase;
}

.sv-crosssell-heading {
    margin: 0;
    font-family: var(--ev-font-display);
    font-size: var(--ev-fs-h2);
    line-height: var(--ev-lh-snug);
    color: var(--ev-text-strong);
}

.sv-crosssell-text {
    margin: 0;
    color: var(--ev-muted);
    line-height: var(--ev-lh-body);
    max-width: 56ch;
}

/* Inset, shorter and rounded on mobile so the image stays a supporting note.
   The top margin is the breathing room between the paragraph and the image. */
.sv-crosssell-media {
    grid-area: media;
    display: block;
    min-height: 200px;
    margin: var(--ev-space-5) var(--ev-space-5) 0;
    border-radius: var(--ev-radius);
    background-image: var(--sv-crosssell-image);
    background-size: cover;
    background-position: center;
    overflow: hidden;
    transition: transform var(--ev-dur-base) var(--ev-ease);
}
.sv-crosssell:hover .sv-crosssell-media { transform: scale(1.02); }

.sv-crosssell-cta {
    grid-area: cta;
    justify-self: start;
    margin: var(--ev-space-5) var(--ev-space-5) var(--ev-space-6);
}
.sv-crosssell-cta i {
    margin-left: 0.5em;
    font-size: 0.8em;
    transition: transform var(--ev-dur-fast) var(--ev-ease);
}
.sv-crosssell-cta:hover i { transform: translateX(3px); }

@media (min-width: 768px) {
    /* Image full-height on one side; the text group (intro + button) is centered
       in the other column by the flexible spacer rows, with a guaranteed minimum
       of breathing room top and bottom if the copy ever runs long. */
    .sv-crosssell {
        grid-template-columns: 1fr 1fr;
        grid-template-rows: minmax(var(--ev-space-7), 1fr) auto auto minmax(var(--ev-space-7), 1fr);
        grid-template-areas:
            "media ."
            "media intro"
            "media cta"
            "media .";
        align-items: stretch;
    }
    .sv-crosssell--reverse {
        grid-template-areas:
            ". media"
            "intro media"
            "cta media"
            ". media";
    }

    .sv-crosssell-media {
        min-height: 400px;
        margin: 0;
        border-radius: 0;
    }

    .sv-crosssell-intro { padding: 0 var(--ev-space-7); }
    .sv-crosssell-cta { margin: var(--ev-space-5) var(--ev-space-7) 0; }

    /* No image resolved - the panel is a single centered text block instead of
       a lopsided half-empty split. */
    .sv-crosssell--text-only {
        grid-template-columns: 1fr;
        grid-template-rows: auto auto;
        grid-template-areas:
            "intro"
            "cta";
    }
    .sv-crosssell--text-only .sv-crosssell-intro {
        align-items: center;
        text-align: center;
        max-width: var(--ev-max-w-prose);
        margin: 0 auto;
        padding-top: var(--ev-space-8);
    }
    .sv-crosssell--text-only .sv-crosssell-cta {
        justify-self: center;
        margin: var(--ev-space-5) auto var(--ev-space-8);
    }
    .sv-crosssell--text-only .sv-crosssell-text { max-width: 60ch; }
}

/* ===== SpicyVisionLanding/wwwroot/css/components/_contact.css ===== */
/* =========================================================================
   Contact - dedicated /contact page + the inline contact block embedded
   at the bottom of Index / Niche pages.
   ========================================================================= */

.sv-contact-submit {
    align-self: flex-start;
    min-width: 220px;
}
.sv-contact-submit:disabled {
    opacity: 0.7;
    cursor: progress;
}

.sv-contact-smallprint {
    font-size: 0.8rem;
    color: var(--ev-muted);
    margin: 0;
}
.sv-contact-smallprint a {
    color: var(--ev-muted);
    border-bottom: 1px dotted var(--ev-muted);
}
.sv-contact-smallprint a:hover {
    color: var(--ev-accent);
    border-bottom-color: var(--ev-accent);
}

.sv-contact-page {
    padding: var(--ev-space-7) 0 var(--ev-space-9);
}
.sv-contact-inner {
    max-width: var(--ev-max-w-contact);
    margin: 0 auto;
    padding: 0 var(--ev-space-5);
}
@media (min-width: 768px) {
    .sv-contact-inner { padding: 0 var(--ev-space-6); }
}

.sv-contact-success {
    text-align: center;
    background: rgba(201, 163, 116, 0.06);
    border: 1px solid rgba(201, 163, 116, 0.3);
    border-radius: var(--ev-radius-lg);
    padding: var(--ev-space-7) var(--ev-space-5);
    color: var(--ev-text);
}
.sv-contact-success i {
    font-size: 3rem;
    color: var(--ev-accent);
    margin-bottom: var(--ev-space-4);
    display: block;
}
.sv-contact-success h2 { margin: 0 0 var(--ev-space-3); }
.sv-contact-success p { color: var(--ev-muted); margin: 0 0 var(--ev-space-5); }

/* Embedded contact block (Index / Niche bottom) */
.sv-contact-block {
    padding: var(--ev-space-8) 0;
    background:
        radial-gradient(ellipse 80% 60% at 50% 0%, rgba(201, 163, 116, 0.08), transparent 70%),
        var(--ev-surface);
    border-top: 1px solid var(--ev-border);
}
@media (min-width: 1024px) {
    .sv-contact-block { padding: var(--ev-space-9) 0; }
}
.sv-contact-block-inner {
    max-width: var(--ev-max-w-contact);
    margin: 0 auto;
    padding: 0 var(--ev-space-5);
}
@media (min-width: 768px) {
    .sv-contact-block-inner { padding: 0 var(--ev-space-6); }
}
.sv-contact-block-title {
    text-align: center;
    font-size: var(--ev-fs-h1);
    margin: 0 0 var(--ev-space-3);
}
.sv-contact-block-sub {
    text-align: center;
    color: var(--ev-muted);
    margin: 0 auto var(--ev-space-6);
    max-width: 560px;
}
.sv-contact-block-fallback {
    text-align: center;
    color: var(--ev-muted);
    padding: var(--ev-space-5);
    border: 1px dashed var(--ev-border);
    border-radius: var(--ev-radius);
}

/* Confirmation dialog - a fixed overlay shown by contact-form.js after a
   successful submit, shared by both contact forms. It floats above the page
   (z-index clears the sticky CTA / topbar, same as the video modal), so a
   successful submit moves nothing underneath: no scroll, no reflow. */
.sv-contact-modal {
    position: fixed;
    inset: 0;
    z-index: 3000;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--ev-space-5);
    background: var(--ev-overlay-deep);
    opacity: 0;
    transition: opacity var(--ev-dur-fast) var(--ev-ease);
}
/* Author display:flex would otherwise beat the [hidden] UA rule. */
.sv-contact-modal[hidden] { display: none; }
.sv-contact-modal.is-open { opacity: 1; }

.sv-contact-modal-backdrop {
    position: absolute;
    inset: 0;
    cursor: pointer;
}
.sv-contact-modal-dialog {
    position: relative;
    z-index: 1;
    width: 100%;
    max-width: 440px;
    text-align: center;
    background: var(--ev-surface);
    border: 1px solid var(--ev-border);
    border-radius: var(--ev-radius-lg);
    padding: var(--ev-space-7) var(--ev-space-6);
    box-shadow: var(--ev-elev-modal);
}
.sv-contact-modal-icon {
    font-size: 3rem;
    color: var(--ev-accent);
    display: block;
    margin-bottom: var(--ev-space-4);
}
.sv-contact-modal-title { margin: 0 0 var(--ev-space-3); }
.sv-contact-modal-text { color: var(--ev-muted); margin: 0 0 var(--ev-space-5); }
.sv-contact-modal-close { min-width: 180px; }

@media (prefers-reduced-motion: reduce) {
    .sv-contact-modal { transition: none; }
}

/* Sending state: a spinner shows while the submit is in flight, then the dialog
   flips to the success or error state in place. */
.sv-contact-modal-spinner {
    display: inline-block;
    width: 2.5rem;
    height: 2.5rem;
    margin-bottom: var(--ev-space-4);
    border: 3px solid var(--ev-border);
    border-top-color: var(--ev-accent);
    border-radius: 50%;
    animation: sv-contact-spin 0.8s linear infinite;
}
@keyframes sv-contact-spin {
    to { transform: rotate(360deg); }
}
.sv-contact-modal-icon--error { color: var(--ev-danger); }

@media (prefers-reduced-motion: reduce) {
    .sv-contact-modal-spinner { animation-duration: 1.6s; }
}

/* ===== SpicyVisionLanding/wwwroot/css/components/_legal.css ===== */
/* =========================================================================
   Legal pages (Terms, Privacy) - editorial long-form reading layout.
   The article body is admin-rendered HTML, so prose styling targets
   descendants of .sv-legal-body rather than markup the .cshtml controls.
   ========================================================================= */

.sv-legal {
    padding: var(--ev-space-7) 0 var(--ev-space-9);
}
.sv-legal-inner {
    max-width: var(--ev-max-w-prose);
    margin: 0 auto;
    padding: 0 var(--ev-space-4);
}
@media (min-width: 768px) {
    .sv-legal-inner { padding: 0 var(--ev-space-6); }
}

.sv-legal-meta {
    text-align: center;
    color: var(--ev-muted);
    font-size: 0.8125rem;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    margin: 0 0 var(--ev-space-6);
}

.sv-legal-article {
    padding: var(--ev-space-5) 0;
}
.sv-legal-article + .sv-legal-article {
    border-top: 1px solid rgba(201, 163, 116, 0.15);
}

.sv-legal-article-head {
    display: flex;
    align-items: baseline;
    gap: var(--ev-space-3);
    margin: 0 0 var(--ev-space-4);
}
.sv-legal-article-num {
    font-family: var(--ev-font-display);
    font-style: italic;
    font-weight: 400;
    color: var(--ev-accent);
    font-size: 1.75rem;
    line-height: 1;
    flex-shrink: 0;
    min-width: 2.25rem;
}
.sv-legal-article h2 {
    font-family: var(--ev-font-display);
    font-size: 1.5rem;
    font-weight: 500;
    margin: 0;
    color: var(--ev-text);
    letter-spacing: 0.01em;
    line-height: 1.2;
}
@media (min-width: 768px) {
    .sv-legal-article-num { font-size: 2.25rem; min-width: 3rem; }
    .sv-legal-article h2 { font-size: 1.875rem; }
}

.sv-legal-body {
    color: var(--ev-text);
    line-height: var(--ev-lh-prose);
    font-size: 1.0625rem;
}
.sv-legal-body > *:first-child { margin-top: 0; }
.sv-legal-body > *:last-child { margin-bottom: 0; }
.sv-legal-body p { margin: 0 0 var(--ev-space-4); }
.sv-legal-body strong { color: var(--ev-accent); font-weight: 500; }
.sv-legal-body em { color: var(--ev-muted); font-style: italic; }

.sv-legal-body a {
    color: var(--ev-accent);
    border-bottom: 1px solid rgba(201, 163, 116, 0.4);
    transition: border-color var(--ev-dur-fast) var(--ev-ease),
                color var(--ev-dur-fast) var(--ev-ease);
}
.sv-legal-body a:hover {
    color: var(--ev-accent-hover);
    border-bottom-color: var(--ev-accent);
}

.sv-legal-body ul,
.sv-legal-body ol {
    padding-left: 1.5rem;
    margin: 0 0 var(--ev-space-4);
}
.sv-legal-body li {
    margin-bottom: var(--ev-space-2);
    padding-left: var(--ev-space-1);
}
.sv-legal-body li::marker { color: var(--ev-accent); }

.sv-legal-body h3,
.sv-legal-body h4 {
    font-family: var(--ev-font-display);
    font-weight: 500;
    color: var(--ev-text);
    margin: var(--ev-space-5) 0 var(--ev-space-2);
    font-size: 1.25rem;
}
.sv-legal-body blockquote {
    margin: var(--ev-space-4) 0;
    padding: var(--ev-space-3) var(--ev-space-5);
    border-left: 2px solid var(--ev-accent);
    color: var(--ev-muted);
    font-style: italic;
}

.sv-legal-footnote {
    margin-top: var(--ev-space-7);
    padding: var(--ev-space-5) var(--ev-space-5);
    background: rgba(201, 163, 116, 0.06);
    border: 1px solid rgba(201, 163, 116, 0.25);
    border-radius: var(--ev-radius);
    text-align: center;
    color: var(--ev-muted);
    font-style: italic;
    font-size: 1rem;
    line-height: 1.7;
}
.sv-legal-footnote a {
    color: var(--ev-accent);
    border-bottom: 1px solid rgba(201, 163, 116, 0.4);
    transition: border-color var(--ev-dur-fast) var(--ev-ease),
                color var(--ev-dur-fast) var(--ev-ease);
}
.sv-legal-footnote a:hover {
    color: var(--ev-accent-hover);
    border-bottom-color: var(--ev-accent);
}

/* ===== SpicyVisionLanding/wwwroot/css/components/_footer.css ===== */
/* =========================================================================
   Footer - dark espresso floor, gold hairlines between tiers.
   ========================================================================= */

.sv-footer {
    background: var(--ev-bg-deep);
    color: var(--ev-muted);
    /* Top padding trimmed one step (64px -> 48px) so the footer reads a touch
       less tall without redesigning it. Bottom stays at space-6. */
    padding: var(--ev-space-7) var(--ev-space-4) var(--ev-space-6);
    text-align: center;
    border-top: 1px solid var(--ev-border);
}

/* Same .sv-body a override trick the topbar brand uses - the global anchor
   colour (gold) was winning on specificity otherwise. */
.sv-body a.sv-footer-brand {
    font-family: var(--ev-font-display);
    font-size: 1.75rem;
    /* Matches the topbar wordmark - cream-white instead of the warm body
       cream so it never reads as gold-tinted next to the actual accent. */
    color: var(--ev-text-strong);
    text-decoration: none;
    display: inline-block;
    margin-bottom: var(--ev-space-2);
}
.sv-body a.sv-footer-brand:hover { color: var(--ev-accent); }

.sv-footer-tagline {
    color: var(--ev-muted);
    font-size: 0.9375rem;
    margin: 0 0 var(--ev-space-4);
}

/* Phone + contact row. Only rendered on the delivery page, which has no
   marketing top bar - so it's the one place a desktop visitor needs an
   explicit way to reach out from the footer. */
.sv-footer-contact {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--ev-space-4) var(--ev-space-6);
    margin: var(--ev-space-4) 0;
    font-size: 0.95rem;
}
.sv-footer-contact a {
    color: var(--ev-text);
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    min-height: var(--ev-touch);
    padding: 0.5rem 0;
}
.sv-footer-contact a:hover { color: var(--ev-accent); }
.sv-footer-contact i { color: var(--ev-accent); }

.sv-footer-links {
    display: flex;
    flex-wrap: wrap;
    gap: var(--ev-space-4) var(--ev-space-5);
    justify-content: center;
    margin: var(--ev-space-4) 0;
}
.sv-footer-links a {
    min-height: var(--ev-touch);
    display: inline-flex;
    align-items: center;
    padding: 0.25rem 0.5rem;
    color: var(--ev-muted);
    font-size: 0.95rem;
}
.sv-footer-links a:hover { color: var(--ev-accent); }

/* On phones the default 24px column gap plus the per-link padding pushes
   the six site links over three rows. Tighten the column gap and shrink
   the font slightly so they collapse to two rows of three. Touch target
   stays at --ev-touch via the link's own min-height; the row gap also
   shrinks so the two rows sit visually together as one block. */
@media (max-width: 767.98px) {
    .sv-footer-links {
        gap: var(--ev-space-2) var(--ev-space-3);
        margin: var(--ev-space-4) 0 var(--ev-space-3);
    }
    .sv-footer-links a {
        padding: 0.25rem 0.35rem;
        font-size: 0.875rem;
    }

    /* The delivery footer's phone + contact row exists for desktop recipients,
       who have no marketing top bar to reach out from. On mobile the page's
       sticky "Call now" CTA already covers that, so the row is redundant. */
    .sv-footer-contact {
        display: none;
    }
}

.sv-footer-socials {
    display: flex;
    gap: var(--ev-space-4);
    justify-content: center;
    margin: var(--ev-space-4) 0;
    font-size: 1.5rem;
}
.sv-footer-socials a {
    width: var(--ev-touch);
    height: var(--ev-touch);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--ev-border);
    border-radius: 50%;
    color: var(--ev-muted);
    transition: border-color var(--ev-dur-fast) var(--ev-ease),
                color var(--ev-dur-fast) var(--ev-ease),
                background var(--ev-dur-fast) var(--ev-ease);
}
.sv-footer-socials a:hover {
    border-color: var(--ev-accent);
    color: var(--ev-accent);
    background: rgba(201, 163, 116, 0.06);
}

.sv-footer-copyright {
    color: var(--ev-muted-faint);
    font-size: 0.8125rem;
    margin-top: var(--ev-space-5);
    padding-top: var(--ev-space-4);
    border-top: 1px solid var(--ev-border);
}

/* ===== SpicyVisionLanding/wwwroot/css/components/_sticky-cta.css ===== */
/* =========================================================================
   Sticky mobile Call bar - full-width gold strip pinned to the bottom
   edge with iOS safe-area inset accounted for. Hidden above 768px.
   The text colour is forced because the global .sv-body a link rule would
   otherwise paint the label gold against the gold bar.
   ========================================================================= */

.sv-sticky-mobile-cta {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    min-height: var(--ev-sticky-cta-h);
    background: var(--ev-accent);
    /* !important - the .sv-body a rule sets every link to --ev-accent (gold),
       which would make the label invisible on the gold pill. The override
       has to stick for both the rest state and the active/hover states. */
    color: var(--ev-text-on-accent) !important;
    /* Body font, not the Cormorant display face. On the gold ground a serif
       at this size reads as cursive/decorative on phones and the digits in
       particular felt unreadable; the body sans keeps the label and number
       crisp, matched, and obviously interactive. */
    font-family: var(--ev-font-body);
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--ev-space-3);
    font-weight: 600;
    font-size: 1.0625rem;
    letter-spacing: 0.01em;
    text-decoration: none;
    z-index: 1000;
    box-shadow: var(--ev-elev-sticky);
    padding: 0.75rem 1rem;
    padding-bottom: max(0.75rem, env(safe-area-inset-bottom));
    /* Slide + fade for the scroll-driven reveal (see the .is-visible state and
       site.js). transform is GPU-cheap; will-change keeps the slide smooth. */
    transition: transform var(--ev-dur-base) var(--ev-ease),
                opacity var(--ev-dur-base) var(--ev-ease);
    will-change: transform;
}

/* Scroll-driven reveal.
   The bar should NOT cover the hero CTAs while the user is at the top of the
   page; it slides up only once the hero has scrolled away (site.js toggles
   .is-visible via an IntersectionObserver on the hero).

   It is hidden by default ONLY when JS is present (.sv-has-js, set on <html>
   before first paint), so there is no load-time flash of the bar sliding away,
   and so a no-JS / JS-error visitor still gets the bar (it just stays put).
   The reveal logic only ever ADDS visibility. */
.sv-has-js .sv-sticky-mobile-cta {
    transform: translateY(120%);
    opacity: 0;
    pointer-events: none;
}
.sv-has-js .sv-sticky-mobile-cta.is-visible {
    transform: translateY(0);
    opacity: 1;
    pointer-events: auto;
}

@media (prefers-reduced-motion: reduce) {
    .sv-sticky-mobile-cta { transition: none; }
}
.sv-sticky-mobile-cta:hover,
.sv-sticky-mobile-cta:active {
    background: var(--ev-accent-hover);
    color: var(--ev-text-on-accent) !important;
}

.sv-sticky-mobile-cta__label {
    font-weight: 600;
    letter-spacing: 0.01em;
}

/* Hairline pipe between label and phone number. A real character (|)
   carries the body font's metrics so its weight matches the surrounding
   text; an ::before bar would float visually and need separate tuning. */
.sv-sticky-mobile-cta__sep {
    display: inline-block;
    width: 1px;
    height: 1.1em;
    background: currentColor;
    opacity: 0.35;
}

.sv-sticky-mobile-cta__phone {
    /* Same body sans as the label, matched weight. Tabular lining digits
       keep the number's columns even - matters for a 10-digit string that
       sits right next to a word, so the eye reads them as a single phrase
       rather than as digits running into characters of different widths. */
    font-family: var(--ev-font-body);
    font-size: 1.0625rem;
    font-weight: 600;
    letter-spacing: 0.01em;
    font-variant-numeric: lining-nums tabular-nums;
}
@media (min-width: 768px) {
    .sv-sticky-mobile-cta { display: none; }
}


/* ===== SpicyVisionLanding/wwwroot/css/components/_toast.css ===== */
/* =========================================================================
   Toast - desktop tel: clipboard feedback. Sits above the sticky CTA on
   mobile so it never collides with the Call bar.
   ========================================================================= */

.sv-toast {
    position: fixed;
    bottom: calc(var(--ev-space-5) + env(safe-area-inset-bottom, 0px));
    left: 50%;
    transform: translate(-50%, 16px);
    background: var(--ev-text);
    color: var(--ev-bg);
    padding: 0.75rem 1.25rem;
    border-radius: var(--ev-radius);
    font-size: 0.9375rem;
    font-weight: 500;
    box-shadow: var(--ev-elev-toast);
    opacity: 0;
    transition: opacity var(--ev-dur-base) var(--ev-ease),
                transform var(--ev-dur-base) var(--ev-ease);
    z-index: 2100;
    pointer-events: none;
    max-width: calc(100vw - 2rem);
}
.sv-toast.is-visible { opacity: 1; transform: translate(-50%, 0); }

@media (max-width: 767.98px) {
    .sv-toast {
        bottom: calc(var(--ev-sticky-cta-h) + var(--ev-space-3) + env(safe-area-inset-bottom, 0px));
    }
}

/* ===== SpicyVisionLanding/wwwroot/css/components/_flatpickr.css ===== */
/* =========================================================================
   Flatpickr - third-party date picker overrides.
   The library injects its own CSS at much higher specificity, so every
   override in this file needs !important. Don't add !important elsewhere;
   it lives here because there is no other way to retheme Flatpickr.
   ========================================================================= */

.flatpickr-calendar {
    background: var(--ev-surface-2) !important;
    border: 1px solid var(--ev-border) !important;
    box-shadow: var(--ev-elev-flatpickr) !important;
    border-radius: var(--ev-radius) !important;
    font-family: var(--ev-font-body) !important;
}
.flatpickr-calendar.arrowTop::before,
.flatpickr-calendar.arrowTop::after { border-bottom-color: var(--ev-surface-2) !important; }
.flatpickr-calendar.arrowBottom::before,
.flatpickr-calendar.arrowBottom::after { border-top-color: var(--ev-surface-2) !important; }

.flatpickr-months .flatpickr-month,
.flatpickr-current-month,
.flatpickr-weekdays,
span.flatpickr-weekday {
    color: var(--ev-text) !important;
    background: transparent !important;
    fill: var(--ev-text) !important;
}

.flatpickr-current-month .flatpickr-monthDropdown-months,
.flatpickr-current-month input.cur-year { color: var(--ev-text) !important; }
.flatpickr-current-month .flatpickr-monthDropdown-months .flatpickr-monthDropdown-month {
    background: var(--ev-surface-2) !important;
}

.flatpickr-months .flatpickr-prev-month,
.flatpickr-months .flatpickr-next-month {
    color: var(--ev-accent) !important;
    fill: var(--ev-accent) !important;
}
.flatpickr-months .flatpickr-prev-month:hover svg,
.flatpickr-months .flatpickr-next-month:hover svg { fill: var(--ev-accent-hover) !important; }

.flatpickr-day {
    color: var(--ev-text) !important;
    border-radius: var(--ev-radius-day) !important;
}
.flatpickr-day:hover,
.flatpickr-day:focus {
    background: rgba(201, 163, 116, 0.18) !important;
    border-color: transparent !important;
}
.flatpickr-day.today {
    border-color: var(--ev-accent) !important;
    color: var(--ev-accent) !important;
}
.flatpickr-day.today:hover {
    background: var(--ev-accent) !important;
    color: var(--ev-text-on-accent) !important;
}
.flatpickr-day.selected,
.flatpickr-day.selected:hover,
.flatpickr-day.startRange,
.flatpickr-day.endRange {
    background: var(--ev-accent) !important;
    border-color: var(--ev-accent) !important;
    color: var(--ev-text-on-accent) !important;
}
.flatpickr-day.flatpickr-disabled,
.flatpickr-day.prevMonthDay,
.flatpickr-day.nextMonthDay {
    color: var(--ev-muted) !important;
    opacity: 0.4;
}

/* ===== SpicyVisionLanding/wwwroot/css/components/_delivery.css ===== */
/* =========================================================================
   Client Delivery Portal (/delivery/{token}) - photo/video download page.
   ========================================================================= */

/* Brand bar - a full-width clickable door back to the portfolio, sitting above
   the album title on every delivery state (valid, deactivated, not-found). The
   brand is always reachable, never a dead caption. */
.sv-deliv-brandbar {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 18px 0;
    border-bottom: 1px solid var(--ev-hairline-gold-faint);
    background: var(--ev-glass-deliv-brandbar);
}
.sv-deliv-brandbar a {
    display: inline-flex;
    align-items: center;
    gap: var(--ev-space-3);
    text-decoration: none;
    transition: opacity var(--ev-dur-fast) var(--ev-ease);
}
.sv-deliv-brandbar a:hover { opacity: 0.85; }
.sv-deliv-brandbar__pre {
    font-size: 0.6875rem;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    color: var(--ev-muted-faint);
}
.sv-deliv-brandbar__mark {
    font-family: var(--ev-font-display);
    font-weight: 500;
    font-size: 1.5rem;
    /* Gold foil, not cream: the wordmark recedes so the couple's name stays
       the hero. --ev-foil is a lighter gilt than the --ev-accent arrow. */
    color: var(--ev-foil);
    letter-spacing: 0.01em;
}
.sv-deliv-brandbar__chev {
    color: var(--ev-accent);
    font-size: 0.6875rem;
}

.delivery-page {
    padding: var(--ev-space-6) 0 var(--ev-space-9);
}

.delivery-container {
    max-width: 1200px;
    margin: 0 auto;
    padding: 0 var(--ev-space-4);
}

/* Album title block. The "Delivered by Emran Visuals" identity sits once in
   the header now, so the page opens straight on the gallery name - large,
   centered, cinematic. */
.delivery-banner {
    text-align: center;
    margin-bottom: var(--ev-space-5);
}

.delivery-title {
    font-family: var(--ev-font-display);
    font-weight: 500;
    color: var(--ev-text);
    font-size: var(--ev-fs-h1);
    margin: 0;
    text-align: center;
}

/* Hero banner (optional, per album). One uploaded image serves every screen:
   a short, wide cinematic strip that feathers into the page on all four edges,
   so the photo reads like a vignette melting into the background rather than a
   hard-edged slab. The same file serves desktop and mobile (cover-cropped from
   the same centre-ish point) and the social share preview, so the admin uploads
   only one. The image URL arrives as --delivery-hero-img from the page. */
.delivery-hero {
    position: relative;
    /* Full-bleed, but capped at the same 120rem page rail as #main-content and
       the brand bar above - NOT raw 100vw. Without the cap, on monitors wider
       than ~1920px the banner ran clear to the screen edges while the brand bar
       stopped at the cap, so the two looked mismatched. min() keeps it
       edge-to-edge up to 1920px and matched to the brand bar's gutters beyond.
       Margin-based bleed (not transform) so it doesn't fight .sv-fade, whose
       .is-visible state sets transform:none at higher specificity and would
       otherwise wipe the centering the moment the fade-in fires. */
    width: min(100vw, 120rem);
    margin-left: calc(50% - min(50vw, 60rem));
    /* Desktop: a thin 7:1 cinematic strip - a branded header band, NOT a half-screen
       hero that buries the gallery. Fixed 7:1, so a matching 7:1 upload shows EXACTLY
       as cropped (no width-dependent trimming), and the 120rem width cap holds the
       height to ~275px at most, so it never grows into a wall on a wide monitor.
       Mobile switches this to a 2:1 band below. */
    aspect-ratio: 7 / 1;
    display: flex;
    align-items: flex-end;
    justify-content: center;
    /* Pull the banner up to sit right under the brand bar - cancels the page's
       top padding so there's no empty gap above the photo. Scoped to the hero, so
       the plain-title and error states keep their normal top spacing. */
    margin-top: calc(-1 * var(--ev-space-6));
    margin-bottom: var(--ev-space-6);
}
/* The photo is its own layer so the feather mask never touches the title.
   Desktop: it fills the hero (absolute). Two intersected linear masks - a pixel
   is opaque only where BOTH gradients are opaque. The vertical gradient gives a
   SOLID (hard) top edge that sits crisply under the brand bar, then a long fade
   at the bottom so the overlaid title flows out of it; the horizontal gradient
   softens the left/right edges (and so rounds off the top corners). */
.delivery-hero-media {
    position: absolute;
    inset: 0;
    z-index: 0;
    background-image: var(--delivery-hero-img);
    background-size: cover;
    /* Favour the upper part of the frame (where faces sit in a portrait) so a
       short strip crops toward the feet, never decapitating the subject. */
    background-position: center 28%;
    background-repeat: no-repeat;
    -webkit-mask-image:
        linear-gradient(to right, transparent 0%, #000 13%, #000 87%, transparent 100%),
        linear-gradient(to bottom, #000 0%, #000 60%, transparent 100%);
    -webkit-mask-composite: source-in;
    mask-image:
        linear-gradient(to right, transparent 0%, #000 13%, #000 87%, transparent 100%),
        linear-gradient(to bottom, #000 0%, #000 60%, transparent 100%);
    mask-composite: intersect;
    pointer-events: none;
}
/* A faint lower-third haze, NOT a dark band: just enough to ground the title area
   and help the very bottom melt into the page, while the photo still reads clearly
   through it. The real text backing is the pool below, anchored to the title. Its
   own layer above the photo, below the title; side-feathered on desktop to match
   the photo's edges (the mobile rule drops that to match the full-width photo). */
.delivery-hero::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 0;
    background: linear-gradient(180deg,
        transparent 42%,
        var(--ev-overlay-faint) 72%,
        transparent 100%);
    -webkit-mask-image: linear-gradient(to right, transparent 0%, #000 13%, #000 87%, transparent 100%);
    mask-image: linear-gradient(to right, transparent 0%, #000 13%, #000 87%, transparent 100%);
    pointer-events: none;
}
.delivery-hero-inner {
    position: relative;
    z-index: 1;
    text-align: center;
    padding: 0 var(--ev-space-4) var(--ev-space-6);
}
/* The title's dark pool: a soft elliptical wash that HUGS the name - dark behind
   the letters and reaching just above their top edge (so it never blends into the
   photo) and a touch below, then feathering to nothing on every side so the photo
   still shows AROUND the title. The name floats on the image; it does not sit on a
   band. Anchored to the title box (no top padding; the bottom padding is offset out
   via the bottom value) so it tracks the text on one line or two, desktop or mobile. */
.delivery-hero-inner::before {
    content: "";
    position: absolute;
    top: -0.55em;
    left: 3%;
    right: 3%;
    bottom: calc(var(--ev-space-5) - 0.25em);
    /* A solid dark shape, then HEAVILY blurred so every edge dissolves into a soft
       gaussian falloff - there is no gradient ring or hard line anywhere, just a
       pool that melts into the photo. Covers the title block (rounded rect, not an
       ellipse, so the ends of a wide line stay backed) and reaches just above the
       letters' top edge so it never blends into the image. */
    background: rgba(0, 0, 0, 0.72);
    border-radius: 44px;
    filter: blur(26px);
    z-index: -1;
    pointer-events: none;
}
.delivery-hero-title {
    text-shadow: 0 1px 3px rgba(0, 0, 0, 0.9), 0 0 20px rgba(0, 0, 0, 0.5);
}

@media (max-width: 767px) {
    /* Mobile = desktop: the photo fills the band and the title overlays it on the
       readability scrim (inherited from the base rule), never stacked below. The
       band is taller so more of the couple shows, the top feather is light, and
       the page's top padding is trimmed, so the photo sits right under the brand
       bar with no wasted gap above. Two overrides here: a shorter banner with a
       top/bottom-only photo feather (the desktop side feather ate the couple on a
       narrow width), and a full-width scrim to match it. The overlaid title and
       shadow come from the desktop rules unchanged. */
    /* The hero is pulled up under the brand bar by the base negative margin, so
       there's no empty gap. SOLID (hard) top edge - same as desktop, so the two
       match; only the bottom fades, gradually, into the page and the overlaid
       title. No side feather here (it ate into the couple on a narrow width). A
       thin fixed 2:1 strip (wider than tall, like the desktop 7:1 but less extreme),
       so the dedicated mobile crop shows exactly as uploaded; the photo also swaps
       to the mobile image, which falls back to the desktop one when no mobile crop
       is set. */
    .delivery-hero { aspect-ratio: 2 / 1; }
    .delivery-hero-media {
        background-image: var(--delivery-hero-img-mobile);
        -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 52%, transparent 100%);
        -webkit-mask-composite: source-over;
        mask-image: linear-gradient(to bottom, #000 0%, #000 52%, transparent 100%);
        mask-composite: add;
    }
    /* The photo runs edge-to-edge on mobile (no side feather), so the haze does
       too. It's a touch stronger than desktop (a phone is held in bright daylight)
       but still only a gentle lower-third wash - the title's own pool does the
       heavy lifting, so this stays light enough that the photo reads through it and
       the name still floats rather than sitting on a band. */
    .delivery-hero::after {
        -webkit-mask-image: none;
        mask-image: none;
        background: linear-gradient(180deg,
            transparent 30%,
            var(--ev-overlay-soft) 70%,
            transparent 100%);
    }
}

/* Gallery */
.delivery-gallery {
    margin: var(--ev-space-5) 0 var(--ev-space-7);
}
/* Progressive loading: justifiedGallery lays the grid out instantly from each
   image's width/height attributes (waitThumbnailsLoad:false in delivery.js) and
   the browser lazy-loads thumbnails as they scroll into view. Until a tile's
   thumbnail has painted it shows a soft placeholder + a gold spinner, so a long
   wedding day's 800 photos fill in tile-by-tile instead of blocking on the whole
   set. delivery.js adds .is-loaded to each tile when its image loads. */
.delivery-gallery .gallery-item {
    cursor: pointer;
    background-color: var(--ev-surface-2);
}
/* Force full opacity over justifiedGallery's 0.1 entry fade-in so the spinner
   and placeholder are clearly visible while the thumbnail is still loading. */
.delivery-gallery.justified-gallery > .gallery-item { opacity: 1; }
.delivery-gallery .gallery-item::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 26px;
    height: 26px;
    margin: -13px 0 0 -13px;
    border-radius: 50%;
    border: 3px solid rgba(201, 163, 116, 0.25);
    border-top-color: var(--ev-accent);
    animation: delivery-thumb-spin 0.8s linear infinite;
    pointer-events: none;
}
.delivery-gallery .gallery-item.is-loaded { background-color: transparent; }
.delivery-gallery .gallery-item.is-loaded::after { display: none; }
.delivery-gallery .gallery-item img {
    border-radius: var(--ev-radius);
    transition: transform var(--ev-dur-base) var(--ev-ease),
                opacity var(--ev-dur-base) var(--ev-ease);
}
/* Reveal the thumbnail as soon as it loads, even if the layout plugin's own
   fade-in doesn't fire for a lazily-loaded tile (safety net). */
.delivery-gallery .gallery-item.is-loaded img { opacity: 1; }
.delivery-gallery .gallery-item:hover img {
    transform: scale(1.02);
    opacity: 0.9;
}
@keyframes delivery-thumb-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
    .delivery-gallery .gallery-item::after { animation-duration: 1.6s; }
}

/* Big-image loading spinner in the photo lightbox. lightGallery ships a small
   loading.gif behind each slide; this is a clearer, on-brand gold ring shown
   over the current slide until the full-resolution original (up to ~45MP, a few
   seconds to fetch) has loaded. lightGallery adds .lg-complete to the slide once
   it's loaded, which hides the ring. Scoped to the delivery gallery via the
   addClass set in delivery.js so other galleries are untouched. */
.lg-outer.lg-ev-loading .lg-item.lg-current:not(.lg-complete)::before {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 54px;
    height: 54px;
    margin: -27px 0 0 -27px;
    border-radius: 50%;
    border: 4px solid rgba(255, 255, 255, 0.25);
    border-top-color: var(--ev-accent);
    animation: delivery-thumb-spin 0.8s linear infinite;
    z-index: 1080;
    pointer-events: none;
}

/* Infinite-scroll trigger row under the gallery. delivery.js watches it and
   pulls the next page of photos as it nears the viewport, so a big album streams
   in instead of loading every tile at once. The gold spinner shows while a page
   is in flight; delivery.js removes the row once the last photo has loaded. */
.delivery-gallery-sentinel {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 56px;
    margin: var(--ev-space-3) 0 var(--ev-space-5);
}
.delivery-gallery-spinner {
    width: 30px;
    height: 30px;
    border-radius: 50%;
    border: 3px solid rgba(201, 163, 116, 0.25);
    border-top-color: var(--ev-accent);
    animation: delivery-thumb-spin 0.8s linear infinite;
}
@media (prefers-reduced-motion: reduce) {
    .delivery-gallery-spinner { animation-duration: 1.6s; }
}

/* "How to save" hint. Only useful on phones (where download routes through the
   share sheet); hidden on desktop, where a plain download is obvious. Calm and
   muted, centered under the gallery. */
.delivery-save-hint { display: none; }
@media (hover: none) and (pointer: coarse) {
    .delivery-save-hint {
        display: block;
        text-align: center;
        max-width: 540px;
        margin: calc(-1 * var(--ev-space-4)) auto var(--ev-space-6);
        color: var(--ev-muted);
        font-size: 0.85rem;
        line-height: 1.5;
    }
    .delivery-save-hint i { color: var(--ev-accent); margin-right: 0.35rem; }
    .delivery-save-hint strong { color: var(--ev-text); font-weight: 600; }
}

/* "Preparing your photo" overlay shown while the Share button downloads the
   image before opening the OS share sheet (a few seconds on a large photo), so
   the tap never looks frozen. delivery.js toggles .is-visible. */
.delivery-share-spinner {
    position: fixed;
    inset: 0;
    z-index: 1200;          /* above the lightbox */
    display: none;
    align-items: center;
    justify-content: center;
    background: rgba(0, 0, 0, 0.6);
}
.delivery-share-spinner.is-visible { display: flex; }
.delivery-share-spinner-box {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--ev-space-3);
    padding: var(--ev-space-5) var(--ev-space-6);
    border-radius: var(--ev-radius);
    background: var(--ev-surface);
    color: var(--ev-text);
    font-size: 0.9rem;
}
.delivery-share-spinner-ring {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    border: 4px solid rgba(255, 255, 255, 0.25);
    border-top-color: var(--ev-accent);
    animation: delivery-share-spin 0.8s linear infinite;
}
@keyframes delivery-share-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
    .delivery-share-spinner-ring { animation-duration: 1.6s; }
}

/* Download buttons */
.download-all-section {
    text-align: center;
    margin: var(--ev-space-5) 0;
}
/* Spacing modifiers - replace inline style="margin: ..." overrides on the
   delivery page so the .cshtml stays free of inline rules. */
.download-all-section--tight { margin: var(--ev-space-3) 0 var(--ev-space-4); }
.download-all-section--top   { margin-top: var(--ev-space-7); }

/* Primary download + secondary Share on one centered row. Wraps on narrow
   screens, where the download button goes full width (rule below) and Share
   drops beneath it, auto-width and centered - never a second card. */
.download-all-actions {
    display: flex;
    /* Stretch so the outlined Share always matches the gold download button's
       height on every screen, not just on mobile - otherwise the taller gold
       button sat higher than Share on desktop. */
    align-items: stretch;
    justify-content: center;
    gap: var(--ev-space-3);
    flex-wrap: wrap;
}

/* Scoped under .sv-body so the dark text colour beats the global
   `.sv-body a { color: var(--ev-accent) }` rule (specificity 0,1,1).
   Without this, the gold link text sits on a gold background and
   becomes invisible. Same trick used for .sv-btn-primary. */
.sv-body a.download-all-btn,
.download-all-btn {
    display: inline-flex;
    align-items: center;
    gap: var(--ev-space-2);
    /* Toned down a notch: clear and easy to find, but no longer the loudest
       thing on the page. Slightly smaller type/padding and a softer shadow
       give it room to breathe against the surrounding gallery. */
    padding: 0.85rem 2rem;
    border-radius: var(--ev-radius);
    /* A soft top-lit gradient plus an inner highlight and a warm glow give the
       primary button a tactile, premium feel - dimensional and inviting rather
       than a flat, shouting slab. Sentence case keeps the tone calm. */
    background: linear-gradient(180deg, var(--ev-accent-hover), var(--ev-accent));
    border: 1px solid var(--ev-accent);
    color: var(--ev-text-on-accent);
    font-family: var(--ev-font-body);
    font-weight: 600;
    font-size: 1rem;
    letter-spacing: normal;
    text-decoration: none;
    white-space: nowrap;
    box-shadow: 0 6px 18px rgba(201, 163, 116, 0.28),
                inset 0 1px 0 rgba(255, 255, 255, 0.22);
    transition: transform var(--ev-dur-fast) var(--ev-ease),
                box-shadow var(--ev-dur-fast) var(--ev-ease),
                filter var(--ev-dur-fast) var(--ev-ease);
}
.sv-body a.download-all-btn:hover,
.download-all-btn:hover {
    filter: brightness(1.04);
    color: var(--ev-text-on-accent);
    transform: translateY(-1px);
    box-shadow: 0 10px 26px rgba(201, 163, 116, 0.38),
                inset 0 1px 0 rgba(255, 255, 255, 0.26);
}

@media (max-width: 576px) {
    /* Keep both actions on one row to save vertical space: the gold download
       button flexes to fill the width with its label on a single line, the
       outlined Share stays compact beside it (never full width).
       Pin both to the same height: zero the vertical padding and line-height so
       the 48px min-height governs each one exactly, and stretch the row so they
       can't diverge. Without this the gold button's taller line box made it sit
       higher than the outlined Share. */
    .download-all-actions { flex-wrap: nowrap; gap: var(--ev-space-2); align-items: stretch; }
    .download-all-btn {
        flex: 1 1 auto;
        min-width: 0;
        min-height: var(--ev-touch);
        justify-content: center;
        padding: 0 1.1rem;
        font-size: 0.95rem;
        line-height: 1;
    }
    .delivery-share-btn {
        flex: 0 0 auto;
        min-height: var(--ev-touch);
        padding: 0 1.05rem;
        line-height: 1;
    }
    /* Bleed the tag strip out to the screen edges so it reads as a banner. */
    .delivery-tag-reminder {
        margin-left: calc(-1 * var(--ev-space-4));
        margin-right: calc(-1 * var(--ev-space-4));
    }
}

.download-size-hint {
    color: var(--ev-muted);
    font-size: 0.9rem;
    margin-top: var(--ev-space-2);
}

/* A full-width banner strip under the actions, not a rounded card - a quiet
   left-to-right band with a softly tinted background and hairline edges, so the
   tag request reads clearly without shouting. The social icons group together
   and the text wraps to its own line on narrow screens, keeping it balanced. */
.delivery-tag-reminder {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-wrap: nowrap;
    gap: 0.6rem;
    text-align: left;
    color: var(--ev-text);
    font-size: 0.85rem;
    line-height: 1.45;
    margin: var(--ev-space-5) 0 0;
    padding: 0.7rem var(--ev-space-4);
    background: var(--ev-accent-tint);
    border-top: 1px solid var(--ev-border);
    border-bottom: 1px solid var(--ev-border);
}
/* Icons stay a fixed block on the left; the text shrinks and wraps beside them
   so the whole thing reads as two rows (icons + two-line text), not three. */
.delivery-tag-icons {
    display: inline-flex;
    align-items: center;
    flex: 0 0 auto;
    gap: 0.65rem;
    font-size: 1.2rem;
}
/* The icons link to the real Instagram/Facebook profiles (new tab). Render the
   glyphs themselves in branded gold - no circle, no fill - so they sit clean on
   the espresso page instead of the garish Instagram-gradient / Facebook-blue.
   A subtle lift + brighten on hover signals they're clickable. */
.delivery-tag-icons a {
    display: inline-flex;
    line-height: 0;
    color: var(--ev-accent);
    text-decoration: none;
    transition: transform var(--ev-dur-fast) var(--ev-ease),
                opacity var(--ev-dur-fast) var(--ev-ease);
}
.delivery-tag-icons a:hover { transform: translateY(-1px); opacity: 0.8; }
.delivery-tag-text {
    flex: 0 1 auto;
    min-width: 0;
}
.delivery-tag-reminder strong { color: var(--ev-accent); font-weight: 600; }

/* Quiet opt-in shown inside a subfolder: "download only this folder". Subdued
   on purpose - a small text link, not a second button - so the primary
   "Download everything" stays the obvious action and this never competes. */
.download-folder-only {
    margin-top: var(--ev-space-3);
}
.sv-body a.download-folder-only-link,
.download-folder-only-link {
    display: inline-flex;
    align-items: center;
    gap: var(--ev-space-2);
    color: var(--ev-muted);
    font-size: 0.85rem;
    text-decoration: none;
    border-bottom: 1px solid transparent;
    transition: color var(--ev-dur-fast) var(--ev-ease),
                border-color var(--ev-dur-fast) var(--ev-ease);
}
.download-folder-only-link i { color: var(--ev-accent); }
.sv-body a.download-folder-only-link:hover,
.download-folder-only-link:hover {
    color: var(--ev-accent);
    border-bottom-color: var(--ev-accent);
}

/* Quiet line under the Download buttons that names the expiry date (or
   says "doesn't expire"). Muted, italicised, no border or background -
   it's reference info, not a call to action. The expired / near-expiry
   alerts up top stay loud for the cases where timing actually matters. */
.download-expiry-info {
    color: var(--ev-muted);
    font-size: 0.85rem;
    font-style: italic;
    text-align: center;
    margin: var(--ev-space-3) auto var(--ev-space-5);
    opacity: 0.85;
}
.download-expiry-info .fa-clock { color: var(--ev-accent); margin-right: 0.35rem; }

/* Same specificity bump as .download-all-btn. The folder/per-video
   button uses the body text colour on a transparent background, and
   the global `.sv-body a` rule was repainting it accent-gold. */
.sv-body a.download-folder-btn,
.download-folder-btn {
    display: inline-flex;
    align-items: center;
    gap: var(--ev-space-2);
    min-height: var(--ev-touch);
    padding: 0.6rem 1.25rem;
    border-radius: var(--ev-radius);
    background: transparent;
    border: 1px solid var(--ev-border);
    color: var(--ev-text);
    font-size: 0.95rem;
    text-decoration: none;
    transition: border-color var(--ev-dur-fast) var(--ev-ease),
                color var(--ev-dur-fast) var(--ev-ease);
}
.sv-body a.download-folder-btn:hover,
.download-folder-btn:hover {
    border-color: var(--ev-accent);
    color: var(--ev-accent);
}

/* Alerts */
.delivery-alert {
    padding: var(--ev-space-4) var(--ev-space-5);
    border-radius: var(--ev-radius);
    text-align: center;
    margin: var(--ev-space-5) auto;
    max-width: 720px;
    border: 1px solid transparent;
}
.delivery-alert-warning {
    background: rgba(201, 163, 116, 0.08);
    border-color: rgba(201, 163, 116, 0.35);
    color: var(--ev-accent);
}
.delivery-alert-danger {
    background: rgba(208, 133, 133, 0.08);
    border-color: rgba(208, 133, 133, 0.40);
    color: var(--ev-danger);
}

/* Video lightbox. A video tile in the media grid opens this overlay player,
   matching the photo lightbox so the whole gallery feels like one viewer.
   delivery.js toggles the hidden attribute and fills in source + download. */
.delivery-video-modal {
    position: fixed;
    inset: 0;
    z-index: 1000;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--ev-space-4);
}
.delivery-video-modal[hidden] { display: none; }
.delivery-video-modal-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, 0.85);
}
.delivery-video-modal-inner {
    position: relative;
    z-index: 1;
    width: min(960px, 100%);
    background: var(--ev-surface);
    border: 1px solid var(--ev-border);
    border-radius: var(--ev-radius-lg);
    padding: var(--ev-space-4);
    box-shadow: 0 24px 60px rgba(0, 0, 0, 0.5);
}
.delivery-video-modal-close {
    position: absolute;
    top: -14px;
    right: -14px;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    border: 1px solid var(--ev-border);
    background: var(--ev-surface);
    color: var(--ev-text);
    cursor: pointer;
    font-size: 1.1rem;
    line-height: 1;
    transition: color var(--ev-dur-fast) var(--ev-ease), border-color var(--ev-dur-fast) var(--ev-ease);
}
.delivery-video-modal-close:hover { color: var(--ev-accent); border-color: var(--ev-accent); }
/* Stage wraps the player so the buffering pill can sit over its top corner. */
.delivery-video-modal-stage {
    position: relative;
}
.delivery-video-modal-player {
    width: 100%;
    max-height: 70vh;
    border-radius: var(--ev-radius);
    background: var(--ev-overlay-bg);
    display: block;
}

/* "Buffering" pill, top-left over the video. Shown on EVERY device identically -
   no browser/OS detection. It's a labelled corner chip, not a centre ring, so it
   never reads as a duplicate of the browser's own native spinner, and it stays
   legible even where the native indicator is faint. delivery.js toggles `hidden`. */
.delivery-video-buffering {
    position: absolute;
    top: var(--ev-space-3);
    left: var(--ev-space-3);
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.35rem 0.7rem;
    border-radius: 999px;
    background: rgba(0, 0, 0, 0.7);
    color: #fff;
    font-size: 0.8rem;
    letter-spacing: 0.02em;
    pointer-events: none; /* never blocks the video controls */
}
.delivery-video-buffering[hidden] { display: none; }
.delivery-video-buffering-dot {
    width: 14px;
    height: 14px;
    border-radius: 50%;
    border: 2px solid rgba(255, 255, 255, 0.3);
    border-top-color: var(--ev-accent);
    animation: delivery-video-spin 0.8s linear infinite;
}
@keyframes delivery-video-spin {
    to { transform: rotate(360deg); }
}
@media (prefers-reduced-motion: reduce) {
    .delivery-video-buffering-dot { animation-duration: 1.6s; }
}
.delivery-video-modal-meta {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: var(--ev-space-3);
    margin-top: var(--ev-space-3);
    flex-wrap: wrap;
    color: var(--ev-muted);
    font-size: 0.9rem;
}
.delivery-video-modal-name {
    font-weight: 500;
    color: var(--ev-text);
    word-break: break-word;
}

/* Rotate-to-watch hint, shown under the video. A landscape film squeezed
   into a portrait phone is the work fighting itself, so we point the viewer
   at the better view: turn the phone and the film fills the screen. Only
   surfaced on touch phones held upright - pointless on desktop, and once the
   phone IS landscape the point is made, so it hides itself there. */
.delivery-video-rotate-hint {
    display: none;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    margin: var(--ev-space-3) 0 0;
    color: var(--ev-text);
    font-size: 0.9rem;
    line-height: 1.5;
    text-align: center;
}
.delivery-video-rotate-hint i {
    color: var(--ev-accent);
}
@media (pointer: coarse) and (orientation: portrait) {
    .delivery-video-rotate-hint { display: flex; }
}

/* On phones, stop squeezing the film: let the player run edge to edge so a
   landscape clip uses the whole screen width instead of sitting inside the
   card's padding. The card chrome (rounded corners, padding) stays on desktop
   where there is room to breathe. */
@media (max-width: 576px) {
    .delivery-video-modal {
        padding: 0;
        align-items: stretch;
    }
    .delivery-video-modal-inner {
        width: 100%;
        border-radius: 0;
        border-left: 0;
        border-right: 0;
        padding: var(--ev-space-3) 0 var(--ev-space-4);
        display: flex;
        flex-direction: column;
        justify-content: center;
        min-height: 100%;
    }
    .delivery-video-modal-player {
        border-radius: 0;
        max-height: 78vh;
    }
    /* Re-inset just the text rows so the copy and the close button keep a
       comfortable margin even though the video itself is full-bleed. */
    .delivery-video-modal-meta,
    .delivery-video-note,
    .delivery-video-rotate-hint {
        padding-left: var(--ev-space-4);
        padding-right: var(--ev-space-4);
    }
    .delivery-video-modal-close {
        top: var(--ev-space-3);
        right: var(--ev-space-3);
    }
}
/* When the phone IS turned sideways, give the film the full height too. */
@media (pointer: coarse) and (orientation: landscape) {
    .delivery-video-modal-player {
        max-height: 92vh;
    }
}

/* Expired / not-found card */
.delivery-expired-card {
    max-width: 540px;
    margin: var(--ev-space-8) auto;
    padding: var(--ev-space-8) var(--ev-space-6);
    background: var(--ev-surface);
    border: 1px solid var(--ev-border);
    border-radius: var(--ev-radius-lg);
    text-align: center;
}
.delivery-expired-card h3 {
    font-family: var(--ev-font-display);
    color: var(--ev-text);
    margin-top: var(--ev-space-3);
}
.delivery-expired-card p { color: var(--ev-muted); }

/* The expired-card clock icon - sized via FA fa-3x; the colour binds to the
   accent so the .cshtml can drop its inline style. */
.delivery-expired-card .fa-clock { color: var(--ev-accent); }

/* Folder dividers (legacy - kept so the selector still matches if any
   delivery page still emits them; the new drill-down layout uses
   .delivery-breadcrumb and .delivery-folder-grid instead). */
.delivery-folder-divider {
    margin: var(--ev-space-7) 0 var(--ev-space-4);
    padding-bottom: var(--ev-space-3);
    border-bottom: 1px solid var(--ev-border);
}
.delivery-folder-divider h4 {
    font-family: var(--ev-font-display);
    font-weight: 500;
    color: var(--ev-accent);
    font-size: 1.5rem;
    margin: 0;
    text-align: center;
}

/* Breadcrumb shown when the recipient has drilled below the visible root.
   Centered beneath the title and deliberately quiet - a path bar, not
   navigation chrome. Centering keeps the page symmetric; the old
   left-aligned version pulled the eye off-axis. */
.delivery-breadcrumb {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
    margin: var(--ev-space-2) 0 var(--ev-space-5);
    font-size: 0.85rem;
    color: var(--ev-muted);
}
.sv-body a.delivery-breadcrumb-link,
.delivery-breadcrumb-link {
    color: var(--ev-accent);
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.2rem 0.4rem;
    border-radius: var(--ev-radius);
    transition: background var(--ev-dur-fast) var(--ev-ease);
}
.sv-body a.delivery-breadcrumb-link:hover,
.delivery-breadcrumb-link:hover {
    background: rgba(201, 163, 116, 0.08);
    color: var(--ev-accent-hover);
}
.delivery-breadcrumb-sep {
    color: var(--ev-border);
}
.delivery-breadcrumb-current {
    color: var(--ev-text);
    font-weight: 500;
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
}
.delivery-breadcrumb-current .fa-folder-open { color: var(--ev-accent); }

/* Unified media grid: folders and videos as equal-sized cards in one centered,
   responsive grid. auto-fit + a max track width keep one or two cards centered
   instead of stranded at the left, and justify-content:center balances a short
   final row. Folder cards drill in; video cards open the lightbox player. */
.delivery-media-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 280px));
    justify-content: center;
    gap: var(--ev-space-4);
    margin: var(--ev-space-5) auto var(--ev-space-6);
    max-width: 1000px;
}
.sv-body a.delivery-media-card,
.delivery-media-card {
    display: flex;
    flex-direction: column;
    background: var(--ev-surface);
    border: 1px solid var(--ev-border);
    border-radius: var(--ev-radius);
    overflow: hidden;
    color: var(--ev-text);
    text-decoration: none;
    text-align: left;
    padding: 0;
    cursor: pointer;
    transition: transform var(--ev-dur-fast) var(--ev-ease),
                border-color var(--ev-dur-fast) var(--ev-ease),
                box-shadow var(--ev-dur-fast) var(--ev-ease);
}
.sv-body a.delivery-media-card:hover,
.delivery-media-card:hover {
    transform: translateY(-2px);
    border-color: var(--ev-accent);
    box-shadow: var(--ev-elev-cta-button);
    color: var(--ev-text);
}
.delivery-media-card-cover {
    position: relative;
    aspect-ratio: 4 / 3;
    background: var(--ev-overlay-bg);
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--ev-accent);
    overflow: hidden;
}
.delivery-media-card-cover img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}
.delivery-media-card-placeholder { font-size: 2.5rem; color: var(--ev-accent); }

/* First-frame video poster: sits over the placeholder icon and fills the cover
   like the photo thumbnails do. It's a muted, non-interactive <video> showing
   only its opening frame; clicks pass through to the card button beneath. */
.delivery-media-card-poster {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    pointer-events: none;
}

/* Small type badge top-left so folder vs video is readable at a glance. */
.delivery-media-card-badge {
    position: absolute;
    top: var(--ev-space-2);
    left: var(--ev-space-2);
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.2rem 0.5rem;
    border-radius: 999px;
    background: rgba(0, 0, 0, 0.55);
    color: #fff;
    font-size: 0.7rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}
.delivery-media-card-badge i { color: var(--ev-accent); }

/* Play glyph centred on a video cover. */
.delivery-media-card-play {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 56px;
    height: 56px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(0, 0, 0, 0.55);
    color: #fff;
    font-size: 1.2rem;
    transition: background var(--ev-dur-fast) var(--ev-ease), transform var(--ev-dur-fast) var(--ev-ease);
}
.delivery-media-card-play i { margin-left: 3px; } /* optical centering of the triangle */
.delivery-media-card--video:hover .delivery-media-card-play {
    background: var(--ev-accent);
    transform: translate(-50%, -50%) scale(1.08);
}
.delivery-media-card-body {
    padding: var(--ev-space-3) var(--ev-space-4);
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}
.delivery-media-card-name {
    font-family: var(--ev-font-display);
    font-weight: 500;
    font-size: 1.1rem;
    color: var(--ev-text);
    line-height: 1.25;
    word-break: break-word;
}
.delivery-media-card-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.85rem;
    font-size: 0.85rem;
    color: var(--ev-muted);
}
.delivery-media-card-meta i { color: var(--ev-accent); margin-right: 0.25rem; }
.delivery-media-card-meta .delivery-media-card-size {
    margin-left: auto;
    font-variant-numeric: tabular-nums;
}

/* Empty folder state. Shown when a folder has no direct files AND no
   subfolders to drill into - happens for empty new folders the admin
   hasn't filled yet. */
.delivery-empty-folder {
    text-align: center;
    color: var(--ev-muted);
    padding: var(--ev-space-7) 0;
}
.delivery-empty-folder i { color: var(--ev-accent); margin-bottom: var(--ev-space-3); }

/* Quiet note under the video meta row. Tells the client that what plays
   is the original master and that downloading is the answer when their
   network can't keep up - without fighting the player for attention.
   Muted body colour, italic, tight type, no border or background. */
.delivery-video-note {
    margin: var(--ev-space-3) 0 0;
    color: var(--ev-muted);
    font-size: 0.85rem;
    font-style: italic;
    line-height: 1.55;
    text-align: center;
    opacity: 0.85;
}

/* Soft conversion strip at the bottom of the delivery page. The point
   is to let the client's friends discover Emran Visuals when they
   receive a shared link, without turning the delivery page into a
   sales pitch. Quiet, brand-aligned, easy to skip. */
.delivery-promo {
    margin-top: var(--ev-space-8);
    padding: var(--ev-space-6) var(--ev-space-5);
    background: var(--ev-surface);
    border: 1px solid var(--ev-border);
    border-radius: var(--ev-radius-lg);
    text-align: center;
}
.delivery-promo-title {
    font-family: var(--ev-font-display);
    font-weight: 500;
    color: var(--ev-text);
    font-size: 1.6rem;
    margin: 0 0 var(--ev-space-3);
}
.delivery-promo-copy {
    color: var(--ev-muted);
    max-width: 640px;
    margin: 0 auto var(--ev-space-4);
    line-height: 1.7;
}
.delivery-promo-actions {
    display: flex;
    justify-content: center;
    gap: var(--ev-space-3);
    flex-wrap: wrap;
}
/* The referral CTAs reuse the global .sv-btn, which is uppercase site-wide.
   On the delivery page we want the same calm sentence case as the download
   button so they read as a warm invitation, not a shouting label. Scoped here
   so other pages keep their existing button style. */
.delivery-promo-actions .sv-btn {
    text-transform: none;
    letter-spacing: normal;
}

/* Download-started notice. Calm technical feedback, not a banner: the
   browser's own download bar carries the real percentage, so this is just a
   quiet reassurance that a long transfer is running. Styled in a muted smoky
   steel blue - the one cool tone on the page - so it reads as a neutral system
   message and sits apart from the warm gold without shouting. Compact: one
   line, narrow, soft. delivery.js toggles .is-visible on a download click. */
.delivery-download-notice {
    display: none;
    align-items: center;
    gap: var(--ev-space-3);
    max-width: 460px;
    margin: 0 auto var(--ev-space-5);
    padding: var(--ev-space-3) var(--ev-space-4);
    background: var(--ev-info-surface);
    border: 1px solid var(--ev-info-border);
    border-left: 3px solid var(--ev-info-accent);
    border-radius: var(--ev-radius);
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
    color: var(--ev-info);
    text-align: left;
    font-size: 0.85rem;
    line-height: 1.5;
}
.delivery-download-notice.is-visible {
    display: flex;
}
.delivery-download-notice > .fa-circle-down {
    color: var(--ev-info);
    font-size: 1.05rem;
    flex-shrink: 0;
}
.delivery-download-notice-text strong {
    color: var(--ev-text-strong);
    font-weight: 500;
}
.delivery-download-notice-dismiss {
    margin-left: auto;
    flex-shrink: 0;
    background: none;
    border: none;
    color: var(--ev-info);
    cursor: pointer;
    font-size: 1rem;
    line-height: 1;
    padding: 0.15rem 0.25rem;
    opacity: 0.7;
    transition: color var(--ev-dur-fast) var(--ev-ease), opacity var(--ev-dur-fast) var(--ev-ease);
}
.delivery-download-notice-dismiss:hover {
    color: var(--ev-info);
    opacity: 1;
}

.delivery-download-notice-body {
    flex: 1;
    min-width: 0;
}
.delivery-download-notice-text {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
}
.delivery-download-notice-sub {
    color: var(--ev-info);
    opacity: 0.8;
}

/* =========================================================================
   Secondary Share button (the outlined ghost action beside the download CTA).
   Restrained on purpose - quiet, brand-aligned, never a loud banner.
   ========================================================================= */

/* Quiet outlined action. Specificity bump (.sv-body a.…) so the global gold
   link colour doesn't win. Kept generic so the Share button can reuse it. */
.sv-body a.delivery-ghost-btn,
.delivery-ghost-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    min-height: var(--ev-touch);
    padding: 0.5rem 1rem;
    border-radius: var(--ev-radius);
    background: transparent;
    border: 1px solid var(--ev-border);
    color: var(--ev-text);
    font-family: var(--ev-font-body);
    font-size: 0.9rem;
    text-decoration: none;
    cursor: pointer;
    transition: border-color var(--ev-dur-fast) var(--ev-ease),
                color var(--ev-dur-fast) var(--ev-ease),
                background var(--ev-dur-fast) var(--ev-ease);
}
.sv-body a.delivery-ghost-btn:hover,
.delivery-ghost-btn:hover {
    border-color: var(--ev-accent);
    color: var(--ev-accent);
    background: var(--ev-accent-tint);
}
.delivery-ghost-btn i { color: var(--ev-accent); }
.delivery-ghost-btn.is-copied { border-color: var(--ev-accent); color: var(--ev-accent); }

/* Share pairs with the sentence-case download button beside it: same calm
   casing, no shouting uppercase. The outlined ghost base keeps it clearly
   secondary to the gold-filled primary. */
.delivery-share-btn {
    text-transform: none;
    letter-spacing: normal;
}

/* =========================================================================
   Request files (upload box). The reverse of delivery: when the album/folder
   link has uploads enabled, the recipient sees this panel and can send files
   IN. A calm gold-trimmed card with one big tap target, sized for a thumb on a
   phone first. delivery.js wires the picker, drag-drop, and per-file progress.
   ========================================================================= */
.delivery-upload {
    margin: var(--ev-space-5) auto var(--ev-space-6);
    max-width: 720px;
    padding: var(--ev-space-6) var(--ev-space-5);
    background: var(--ev-surface);
    border: 1px solid var(--ev-border-strong);
    border-radius: var(--ev-radius-lg);
    text-align: center;
}
.delivery-upload-icon {
    color: var(--ev-accent);
    font-size: 2rem;
    margin-bottom: var(--ev-space-3);
}
.delivery-upload-title {
    font-family: var(--ev-font-display);
    font-weight: 500;
    color: var(--ev-text);
    font-size: 1.6rem;
    margin: 0 0 var(--ev-space-2);
    line-height: 1.25;
}
.delivery-upload-sub {
    color: var(--ev-muted);
    max-width: 520px;
    margin: 0 auto var(--ev-space-5);
    line-height: 1.6;
    font-size: 0.95rem;
}

/* Optional name field. Reuses the site form primitives but constrained and
   centered so it reads as one quiet line above the dropzone. */
.delivery-upload-name {
    max-width: 360px;
    margin: 0 auto var(--ev-space-4);
    text-align: left;
}

/* The big drop target is a <label> wrapping a hidden file input, so a tap
   anywhere opens the native picker (and the phone's "Photo Library / Files"
   chooser). Dashed gold hairline that warms on hover/drag. */
.delivery-dropzone {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--ev-space-2);
    min-height: 220px;
    padding: var(--ev-space-7) var(--ev-space-5);
    /* No dashed outline - that reads like a default file input and cheapens
       the page. Instead a soft solid gold hairline on a gently warm panel,
       with a faint inner pool of light at the centre, so the drop target
       feels like an elegant inset space. The gold only comes forward on
       hover/drag. */
    border: 1px solid var(--ev-hairline-gold);
    border-radius: var(--ev-radius-lg);
    background:
        radial-gradient(120% 90% at 50% 36%, rgba(201, 163, 116, 0.10), transparent 72%),
        var(--ev-surface-2);
    box-shadow: 0 1px 0 rgba(255, 255, 255, 0.03) inset, 0 12px 32px rgba(0, 0, 0, 0.22);
    color: var(--ev-text);
    cursor: pointer;
    transition: border-color var(--ev-dur-fast) var(--ev-ease),
                background var(--ev-dur-fast) var(--ev-ease),
                box-shadow var(--ev-dur-fast) var(--ev-ease),
                transform var(--ev-dur-fast) var(--ev-ease);
}
.delivery-dropzone:hover {
    border-color: var(--ev-accent);
    background:
        radial-gradient(120% 90% at 50% 36%, rgba(201, 163, 116, 0.16), transparent 72%),
        var(--ev-surface-2);
    box-shadow: 0 0 0 1px var(--ev-hairline-gold) inset, 0 14px 36px rgba(201, 163, 116, 0.16);
}
.delivery-dropzone.is-dragover {
    border-color: var(--ev-accent-hover);
    background:
        radial-gradient(120% 90% at 50% 36%, rgba(201, 163, 116, 0.22), transparent 70%),
        var(--ev-surface-2);
    box-shadow: 0 0 0 1px var(--ev-accent) inset, 0 16px 40px rgba(201, 163, 116, 0.24);
    transform: scale(1.01);
}
.delivery-dropzone > i { color: var(--ev-accent); font-size: 1.6rem; }
.delivery-dropzone-main {
    font-family: var(--ev-font-display);
    font-size: 1.15rem;
    color: var(--ev-text);
}
.delivery-dropzone-sub { color: var(--ev-muted); font-size: 0.85rem; }

/* The queued/uploading file list. Each row is a small grid: file name + live
   percent on the top line, the progress bar across the middle, and the
   bytes-done / time-left readout beneath. On finish the percent becomes a
   check, the bar collapses, and the readout reads "Sent". */
.delivery-upload-list {
    list-style: none;
    margin: var(--ev-space-4) 0 0;
    padding: 0;
    text-align: left;
    display: flex;
    flex-direction: column;
    gap: var(--ev-space-2);
}
.delivery-upload-item {
    display: grid;
    grid-template-columns: 1fr auto auto;
    grid-template-areas:
        "name pct cancel"
        "bar  bar bar"
        "eta  eta eta";
    align-items: center;
    column-gap: var(--ev-space-3);
    row-gap: 0.4rem;
    padding: var(--ev-space-3) var(--ev-space-4);
    background: var(--ev-surface-2);
    border: 1px solid var(--ev-border);
    border-radius: var(--ev-radius);
}
/* The cancel (x) button, shown while a file is queued or uploading and removed
   on finish / error. Quiet by default, danger-tinted on hover. */
/* Icon + the word "Cancel" so a recipient never has to guess what the X does.
   Outlined pill makes it read as a button, not decoration. */
.delivery-upload-item-cancel {
    grid-area: cancel;
    justify-self: end;
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    background: none;
    border: 1px solid var(--ev-border);
    cursor: pointer;
    color: var(--ev-muted);
    font-size: 0.8rem;
    line-height: 1;
    padding: 0.3rem 0.6rem;
    border-radius: var(--ev-radius);
    transition: color var(--ev-dur-fast) var(--ev-ease),
                border-color var(--ev-dur-fast) var(--ev-ease),
                background var(--ev-dur-fast) var(--ev-ease);
}
.delivery-upload-item-cancel:hover {
    color: var(--ev-danger);
    border-color: var(--ev-danger);
    background: var(--ev-accent-tint);
}
.delivery-upload-item.is-cancelled .delivery-upload-item-pct,
.delivery-upload-item.is-cancelled .delivery-upload-item-eta { color: var(--ev-muted); }
.delivery-upload-item.is-cancelled .delivery-upload-bar { display: none; }
.delivery-upload-item-name {
    grid-area: name;
    color: var(--ev-text);
    font-size: 0.9rem;
    word-break: break-word;
}
.delivery-upload-item-size { color: var(--ev-muted); font-size: 0.8rem; }
.delivery-upload-item-pct {
    grid-area: pct;
    justify-self: end;
    min-width: 2.5rem;
    text-align: right;
    font-variant-numeric: tabular-nums;
    font-weight: 600;
    font-size: 0.9rem;
    color: var(--ev-accent);
}
.delivery-upload-item.is-done .delivery-upload-item-pct { color: var(--ev-success); }
.delivery-upload-item.is-error .delivery-upload-item-pct { color: var(--ev-danger); }
.delivery-upload-item.is-error .delivery-upload-item-name { color: var(--ev-danger); }
.delivery-upload-item-eta {
    grid-area: eta;
    color: var(--ev-muted);
    font-size: 0.78rem;
    font-variant-numeric: tabular-nums;
}
.delivery-upload-item.is-error .delivery-upload-item-eta { color: var(--ev-danger); }

.delivery-upload-bar {
    grid-area: bar;
    height: 6px;
    border-radius: var(--ev-radius-pill);
    background: var(--ev-border);
    overflow: hidden;
}
.delivery-upload-item.is-done .delivery-upload-bar,
.delivery-upload-item.is-error .delivery-upload-bar { display: none; }
.delivery-upload-bar-fill {
    /* MUST be block: it's a <span>, and width/height are ignored on an inline
       box - that's why an earlier build showed a percentage but no moving bar. */
    display: block;
    height: 100%;
    width: 0;
    border-radius: var(--ev-radius-pill);
    background: var(--ev-accent);
    transition: width var(--ev-dur-base) var(--ev-ease);
}
/* Animated sheen while a file is in flight, so the bar reads as live and
   moving rather than a static block at a fixed width. */
.delivery-upload-item.is-uploading .delivery-upload-bar-fill {
    background-image: linear-gradient(90deg, var(--ev-accent-dim), var(--ev-accent-hover), var(--ev-accent-dim));
    background-size: 200% 100%;
    animation: delivery-upload-sheen 1.1s linear infinite;
}
@keyframes delivery-upload-sheen { to { background-position: -200% 0; } }
@media (prefers-reduced-motion: reduce) {
    .delivery-upload-item.is-uploading .delivery-upload-bar-fill { animation: none; }
}

/* Quiet portfolio nudge under the upload box. In receive mode the gallery and
   referral card are hidden, so this is the one door from a drop-box link back
   into the actual work - it keeps the page feeling like Emran Visuals' site,
   not a faceless uploader. */
.delivery-upload-portfolio {
    text-align: center;
    margin-top: var(--ev-space-6);
}
.delivery-upload-portfolio-copy {
    color: var(--ev-muted);
    margin: 0 0 var(--ev-space-3);
    font-size: 0.95rem;
}

/* Final thank-you, swapped in by delivery.js once every file has landed. */
.delivery-upload-done {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--ev-space-2);
    margin-top: var(--ev-space-4);
    color: var(--ev-success);
    font-size: 0.95rem;
}
.delivery-upload-done[hidden] { display: none; }
.delivery-upload-done i { font-size: 1.1rem; }

/* "Keep this tab open" reminder. Shown by delivery.js only while a file is in
   flight (hidden on load), styled as a calm info toast, not an alarm. */
.delivery-upload-keepopen {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--ev-space-2);
    margin-top: var(--ev-space-4);
    padding: var(--ev-space-2) var(--ev-space-4);
    border-radius: var(--ev-radius);
    background: var(--ev-info-surface);
    border: 1px solid var(--ev-info-border);
    color: var(--ev-info);
    font-size: 0.85rem;
}
.delivery-upload-keepopen[hidden] { display: none; }
.delivery-upload-keepopen i { color: var(--ev-info); }

/* ==========================================================================
   Phone zip-warning dialog. Shown only on a handheld when the client taps a
   "Download everything / this folder" button (the photos in a zip land in
   Files, not the camera roll). Desktop never sees it.
   ========================================================================== */
.delivery-modal-overlay {
    position: fixed;
    inset: 0;
    z-index: 1300;
    display: none;
    align-items: center;
    justify-content: center;
    padding: var(--ev-space-4);
    background: rgba(0, 0, 0, 0.6);
}
.delivery-modal-overlay.is-visible { display: flex; }
.delivery-modal {
    width: 100%;
    max-width: 440px;
    background: var(--ev-surface);
    color: var(--ev-text);
    border-radius: var(--ev-radius);
    border: 1px solid var(--ev-hairline-gold-faint);
    padding: var(--ev-space-5);
    box-shadow: 0 18px 50px rgba(0, 0, 0, 0.45);
}
.delivery-modal-title {
    font-family: var(--ev-font-display);
    font-size: 1.15rem;
    margin: 0 0 var(--ev-space-3);
    color: var(--ev-text);
}
.delivery-modal-body {
    font-family: var(--ev-font-body);
    font-size: 0.92rem;
    line-height: 1.5;
    margin: 0 0 var(--ev-space-4);
    color: var(--ev-text);
}
.delivery-modal-actions { display: flex; flex-direction: column; gap: var(--ev-space-2); }
.delivery-modal-primary {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    min-height: var(--ev-touch);
    padding: 0.7rem 1.25rem;
    border-radius: var(--ev-radius);
    background: linear-gradient(180deg, var(--ev-accent-hover), var(--ev-accent));
    border: 1px solid var(--ev-accent);
    color: var(--ev-text-on-accent);
    font-family: var(--ev-font-body);
    font-weight: 600;
    font-size: 0.95rem;
    cursor: pointer;
}
.delivery-modal-cancel {
    min-height: var(--ev-touch);
    padding: 0.5rem 1rem;
    border-radius: var(--ev-radius);
    background: transparent;
    border: none;
    color: var(--ev-muted-faint);
    font-family: var(--ev-font-body);
    font-size: 0.9rem;
    cursor: pointer;
}

/* ===== SpicyVisionLanding/wwwroot/css/components/_storybook.css ===== */
/* =========================================================================
   Storybook - the venue-story feature blog (index + article).

   Two public pages share this file:
     /Blog                  - the Storybook index (compact hero, featured
                              split-card, 2-column story river, pager / mobile
                              load-more).
     /Blog/{slug}-{id}      - the Storybook article (breadcrumb, centered
                              header, full-bleed hero, drop-cap prose, optional
                              film facade, inquiry CTA, "More from the Storybook").

   The article body is admin-composed HTML (the blog service emits
   <section class="blog-block"> with figures, side-by-side rows, and video
   blocks), so prose rules target those descendant classes rather than markup
   the .cshtml controls directly.

   Colours, fonts, radii, shadows, and motion come from --ev-* tokens. The
   editorial type scale (large serif display sizes) is expressed in rem to
   match the design reference, the same way pages/_blog.css does.
   ========================================================================= */

/* ---------- Shared bits ---------- */
.sv-storybook {
    background: var(--ev-bg);
    color: var(--ev-text);
}

.sv-storybook-eyebrow {
    font-size: 0.75rem;
    font-weight: 500;
    letter-spacing: 0.3em;
    text-transform: uppercase;
    color: var(--ev-accent);
    margin: 0 0 var(--ev-space-4);
}

/* Dot-ornament divider under the index hero. */
.sv-storybook-dot {
    display: flex;
    align-items: center;
    gap: 14px;
    max-width: 1200px;
    margin: var(--ev-space-5) auto var(--ev-space-7);
    padding: 0 40px;
}
.sv-storybook-dot::before,
.sv-storybook-dot::after {
    content: "";
    flex: 1;
    height: 1px;
    background: linear-gradient(90deg, transparent, var(--ev-border-strong), transparent);
}
.sv-storybook-dot i {
    color: var(--ev-accent);
    opacity: 0.7;
    font-size: 0.5rem;
}

/* =========================================================================
   INDEX
   ========================================================================= */
.sv-storybook-hero {
    text-align: center;
    padding: 72px 40px var(--ev-space-7);
}
.sv-storybook-hero h1 {
    font-family: var(--ev-font-display);
    font-weight: 500;
    font-size: 3.25rem;
    line-height: 1.08;
    letter-spacing: var(--ev-track-display);
    margin: 0 0 var(--ev-space-4);
    text-wrap: balance;
}
.sv-storybook-hero-sub {
    color: var(--ev-muted);
    font-size: 1.0625rem;
    line-height: 1.6;
    max-width: 52ch;
    margin: 0 auto;
    text-wrap: pretty;
}

.sv-storybook-wrap {
    max-width: 1200px;
    margin: 0 auto;
    padding: 0 40px var(--ev-space-8);
}

.sv-storybook-empty {
    text-align: center;
    color: var(--ev-muted);
    padding: var(--ev-space-8) 0;
}

/* Featured (lead) story - split card. */
.sv-sb-feature {
    display: grid;
    grid-template-columns: 1.3fr 1fr;
    border: 1px solid var(--ev-border);
    border-radius: var(--ev-radius-lg);
    overflow: hidden;
    margin-bottom: var(--ev-space-7);
    background: var(--ev-surface);
    text-decoration: none;
    color: inherit;
    transition: border-color var(--ev-dur-base) var(--ev-ease),
                box-shadow var(--ev-dur-base) var(--ev-ease);
}
.sv-sb-feature:hover {
    border-color: var(--ev-border-strong);
    box-shadow: var(--ev-elev-1);
}
.sv-sb-feature-img {
    position: relative;
    display: block;
    min-height: 420px;
    background-color: var(--ev-surface-2);
    background-size: cover;
    background-position: center;
}
.sv-sb-feature-body {
    padding: var(--ev-space-7) 44px;
    display: flex;
    flex-direction: column;
    justify-content: center;
}
.sv-sb-feature-title {
    font-family: var(--ev-font-display);
    font-weight: 500;
    font-size: 2.375rem;
    line-height: 1.1;
    letter-spacing: var(--ev-track-display);
    margin: 0 0 var(--ev-space-4);
    color: var(--ev-text);
}
.sv-sb-feature .sv-sb-excerpt {
    font-size: 1rem;
    margin-bottom: var(--ev-space-5);
}

/* Story river - 2-col cards. */
.sv-storybook-river {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 36px var(--ev-space-6);
}
.sv-sb-card {
    display: flex;
    flex-direction: column;
    text-decoration: none;
    color: inherit;
}
.sv-sb-card-img {
    position: relative;
    display: block;
    aspect-ratio: 3 / 2;
    border-radius: var(--ev-radius);
    overflow: hidden;
    background-color: var(--ev-surface-2);
    background-size: cover;
    background-position: center;
    margin-bottom: var(--ev-space-4);
    border: 1px solid var(--ev-border);
    transition: transform var(--ev-dur-slow) var(--ev-ease);
}
.sv-sb-card:hover .sv-sb-card-img {
    transform: scale(1.015);
}
.sv-sb-card-title {
    font-family: var(--ev-font-display);
    font-weight: 500;
    font-size: 1.625rem;
    line-height: 1.14;
    margin: 0 0 var(--ev-space-3);
    color: var(--ev-text);
    transition: color var(--ev-dur-fast) var(--ev-ease);
}
.sv-sb-card:hover .sv-sb-card-title {
    color: var(--ev-accent);
}
.sv-sb-card .sv-sb-excerpt {
    font-size: 0.90625rem;
}
.sv-sb-card .sv-sb-meta {
    margin-top: auto;
}

/* Shared card pieces. */
.sv-sb-badge {
    position: absolute;
    top: 14px;
    left: 14px;
    background: rgba(14, 8, 6, 0.78);
    color: var(--ev-accent);
    border: 1px solid var(--ev-border-strong);
    border-radius: var(--ev-radius-pill);
    padding: 5px 12px;
    font-size: 0.65625rem;
    font-weight: 500;
    letter-spacing: 0.16em;
    text-transform: uppercase;
}
.sv-sb-feature .sv-sb-badge {
    top: 18px;
    left: 18px;
    padding: 6px 14px;
    font-size: 0.6875rem;
    letter-spacing: 0.18em;
}
.sv-sb-kicker {
    display: block;
    font-size: 0.65625rem;
    font-weight: 500;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--ev-accent);
    margin: 0 0 var(--ev-space-3);
}
.sv-sb-feature .sv-sb-kicker {
    font-size: 0.6875rem;
    letter-spacing: 0.22em;
    margin-bottom: var(--ev-space-4);
}
.sv-sb-excerpt {
    display: block;
    color: var(--ev-muted);
    line-height: 1.6;
    margin: 0 0 var(--ev-space-4);
}
.sv-sb-meta {
    display: flex;
    align-items: center;
    gap: var(--ev-space-3);
    color: var(--ev-muted-faint);
    font-size: 0.6875rem;
    letter-spacing: 0.05em;
    text-transform: uppercase;
}
.sv-sb-feature .sv-sb-meta {
    font-size: 0.75rem;
}
.sv-sb-meta .sep {
    width: 3px;
    height: 3px;
    border-radius: var(--ev-radius-pill);
    background: var(--ev-accent);
    opacity: 0.6;
}
.sv-sb-readlink {
    display: inline-flex;
    align-items: center;
    gap: var(--ev-space-2);
    margin-top: var(--ev-space-5);
    color: var(--ev-accent);
    font-size: 0.75rem;
    font-weight: 500;
    letter-spacing: 0.18em;
    text-transform: uppercase;
}
.sv-sb-feature:hover .sv-sb-readlink i {
    transform: translateX(3px);
}
.sv-sb-readlink i {
    transition: transform var(--ev-dur-fast) var(--ev-ease);
}

/* Pagination - desktop numbered pager. */
.sv-storybook-pager {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: var(--ev-space-2);
    margin-top: var(--ev-space-8);
    flex-wrap: wrap;
}
.sv-storybook-pager a {
    min-width: 40px;
    height: 40px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--ev-border);
    border-radius: var(--ev-radius);
    color: var(--ev-text);
    font-size: 0.875rem;
    font-family: var(--ev-font-display);
    text-decoration: none;
    transition: border-color var(--ev-dur-fast) var(--ev-ease),
                color var(--ev-dur-fast) var(--ev-ease);
}
.sv-storybook-pager a:hover {
    border-color: var(--ev-border-strong);
    color: var(--ev-accent);
}
.sv-storybook-pager a.active {
    border-color: var(--ev-accent);
    color: var(--ev-accent);
}
.sv-storybook-pager a.nav {
    letter-spacing: 0.1em;
    font-family: var(--ev-font-body);
    font-size: 0.75rem;
    text-transform: uppercase;
    padding: 0 var(--ev-space-4);
}

/* The numbered pager is the single pagination control on every viewport -
   server-rendered, crawlable URLs, and it keeps the footer reachable (each
   page is its own page load, not an in-place append). On phones it just wraps
   and tightens up. */
@media (max-width: 767.98px) {
    .sv-storybook-pager { gap: 6px; margin-top: var(--ev-space-7); }
    .sv-storybook-pager a { min-width: 38px; height: 38px; }
    .sv-storybook-pager a.nav { padding: 0 var(--ev-space-3); }
}

/* =========================================================================
   ARTICLE
   ========================================================================= */
.sv-storybook-crumb {
    max-width: 1100px;
    margin: 0 auto;
    padding: var(--ev-space-5) 40px 0;
    display: flex;
    align-items: center;
    gap: var(--ev-space-3);
    font-size: 0.75rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--ev-muted-faint);
}
.sv-storybook-crumb a {
    color: var(--ev-muted);
    text-decoration: none;
    transition: color var(--ev-dur-fast) var(--ev-ease);
}
.sv-storybook-crumb a:hover { color: var(--ev-accent); }
.sv-storybook-crumb .sep {
    color: var(--ev-accent);
    opacity: 0.55;
    font-size: 0.625rem;
}
.sv-storybook-crumb .here {
    color: var(--ev-text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.sv-storybook-head {
    max-width: var(--ev-max-w-blog);
    margin: 0 auto;
    padding: var(--ev-space-7) 40px var(--ev-space-6);
    text-align: center;
}
.sv-storybook-head-kicker {
    font-size: 0.75rem;
    font-weight: 500;
    letter-spacing: 0.24em;
    text-transform: uppercase;
    color: var(--ev-accent);
    margin: 0 0 var(--ev-space-4);
}
.sv-storybook-head h1 {
    font-family: var(--ev-font-display);
    font-weight: 500;
    font-size: 3.25rem;
    line-height: 1.08;
    letter-spacing: var(--ev-track-display);
    margin: 0 0 var(--ev-space-5);
    text-wrap: balance;
    color: var(--ev-text);
}
.sv-storybook-head-meta {
    display: inline-flex;
    align-items: center;
    gap: var(--ev-space-3);
    color: var(--ev-muted-faint);
    font-size: 0.75rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    flex-wrap: wrap;
    justify-content: center;
}
.sv-storybook-head-meta .sep {
    width: 3px;
    height: 3px;
    border-radius: var(--ev-radius-pill);
    background: var(--ev-accent);
    opacity: 0.6;
}

/* Full-bleed hero image. */
.sv-storybook-hero-img {
    width: 100%;
    height: 540px;
    background-color: var(--ev-surface-2);
    background-size: cover;
    background-position: center;
    margin-top: var(--ev-space-3);
}

/* ---------- Article body: typed blocks ---------- */
/* Each block is a direct child of .sv-storybook-article-body and owns its own
   width: text blocks sit in a 720px column, media blocks break wider. */
.sv-storybook-article-body {
    padding-top: var(--ev-space-7);
}
.sv-storybook-article-body > * {
    margin-bottom: var(--ev-space-6);
}
.sv-storybook-article-body > *:last-child {
    margin-bottom: 0;
}

/* Text blocks (Paragraph, DropCapParagraph, Heading) */
.sv-storybook-body {
    max-width: 720px;
    margin-left: auto;
    margin-right: auto;
    padding: 0 40px;
}
.sv-storybook-body p {
    font-size: var(--ev-fs-body);
    line-height: 1.8;
    color: var(--ev-text);
    margin: 0 0 var(--ev-space-5);
}
.sv-storybook-body p:last-child { margin-bottom: 0; }
.sv-storybook-body h2 {
    font-family: var(--ev-font-display);
    font-weight: 500;
    font-size: 2rem;
    line-height: 1.15;
    margin: 0;
    color: var(--ev-text);
}
.sv-storybook-body a {
    color: var(--ev-accent);
    border-bottom: 1px solid var(--ev-border-strong);
    text-decoration: none;
    transition: color var(--ev-dur-fast) var(--ev-ease);
}
.sv-storybook-body a:hover { color: var(--ev-accent-hover); }
.sv-storybook-body ul,
.sv-storybook-body ol {
    padding-left: var(--ev-space-5);
    margin: 0 0 var(--ev-space-5);
    color: var(--ev-text);
    line-height: 1.8;
}
.sv-storybook-body li { margin-bottom: var(--ev-space-2); }

/* Drop cap - applied to a specific block, never via :first-of-type (which
   would break when the first block is an image). */
.sv-storybook-dropcap p:first-of-type::first-letter {
    float: left;
    font-family: var(--ev-font-display);
    font-weight: 500;
    font-size: 4.75rem;
    line-height: 0.78;
    padding: 6px 12px 0 0;
    color: var(--ev-accent);
}

/* Pull quote */
.sv-storybook-quote {
    max-width: 760px;
    margin-left: auto;
    margin-right: auto;
    padding: 0 40px;
    text-align: center;
}
.sv-storybook-quote .sv-quote-mark {
    display: block;
    color: var(--ev-accent);
    font-size: 3.25rem;
    font-family: var(--ev-font-display);
    line-height: 0.5;
    margin-bottom: var(--ev-space-3);
}
.sv-storybook-quote blockquote {
    font-family: var(--ev-font-display);
    font-style: italic;
    font-weight: 400;
    font-size: 1.9rem;
    line-height: 1.35;
    color: var(--ev-ivory);
    margin: 0;
    text-wrap: pretty;
    border: 0;
    padding: 0;
}

/* Full-width image (figure / figure.bleed) */
.sv-storybook-figure {
    max-width: 720px;
    margin-left: auto;
    margin-right: auto;
    padding: 0 40px;
}
.sv-storybook-figure img {
    width: 100%;
    border-radius: var(--ev-radius);
    display: block;
}

/* Shared caption styling */
.sv-storybook-figure figcaption,
.sv-storybook-duo-wrap figcaption,
.sv-storybook-zig-media figcaption {
    color: var(--ev-muted-faint);
    font-size: 0.75rem;
    letter-spacing: 0.04em;
    margin-top: var(--ev-space-3);
    text-align: center;
    font-style: italic;
}

/* Two images side by side */
.sv-storybook-duo-wrap {
    max-width: 1040px;
    margin-left: auto;
    margin-right: auto;
    padding: 0 40px;
}
.sv-storybook-duo {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--ev-space-4);
}
.sv-storybook-duo img {
    width: 100%;
    aspect-ratio: 4 / 5;
    object-fit: cover;
    border-radius: var(--ev-radius);
    display: block;
}

/* Zigzag image + text feature */
.sv-storybook-zig {
    max-width: 1100px;
    margin-left: auto;
    margin-right: auto;
    padding: 0 40px;
    display: grid;
    grid-template-columns: 1.15fr 0.85fr;
    gap: 44px;
    align-items: center;
}
.sv-storybook-zig--flip { grid-template-columns: 0.85fr 1.15fr; }
.sv-storybook-zig--flip .sv-storybook-zig-media { order: 2; }
.sv-storybook-zig--flip .sv-storybook-zig-text { order: 1; }
.sv-storybook-zig-media img {
    width: 100%;
    aspect-ratio: 4 / 3;
    object-fit: cover;
    border-radius: var(--ev-radius);
    border: 1px solid var(--ev-border);
    display: block;
}
.sv-storybook-zig-text .k {
    font-size: 0.7rem;
    font-weight: 500;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--ev-accent);
    margin: 0 0 var(--ev-space-3);
}
.sv-storybook-zig-text h3 {
    font-family: var(--ev-font-display);
    font-weight: 500;
    font-size: 1.9rem;
    line-height: 1.14;
    margin: 0 0 var(--ev-space-3);
    color: var(--ev-text);
}
.sv-storybook-zig-text p {
    font-size: 1rem;
    line-height: 1.7;
    color: var(--ev-muted);
    margin: 0 0 var(--ev-space-3);
}
.sv-storybook-zig-text p:last-child { margin-bottom: 0; }

.sv-storybook-empty-note {
    color: var(--ev-muted);
    font-style: italic;
}

/* Full-bleed image breaks past the text column on desktop. An explicit width
   wider than the 720px column is what lets it bleed; margin + transform
   re-centre it. */
@media (min-width: 768px) {
    .sv-storybook-figure.bleed {
        max-width: min(1040px, calc(100vw - var(--ev-space-9)));
        padding: 0;
    }
}

/* ---------- End-of-article film facade ---------- */
.sv-storybook-film {
    max-width: 1040px;
    margin: var(--ev-space-2) auto var(--ev-space-6);
    padding: 0 40px;
}
.sv-storybook-film-frame {
    border-radius: var(--ev-radius-lg);
    overflow: hidden;
    border: 1px solid var(--ev-border);
    background: var(--ev-surface);
}
.sv-storybook-film-cap {
    color: var(--ev-muted-faint);
    font-size: 0.75rem;
    letter-spacing: 0.04em;
    margin-top: var(--ev-space-3);
    text-align: center;
    font-style: italic;
}

/* ---------- Inquiry CTA ---------- */
.sv-storybook-cta {
    max-width: 720px;
    margin: var(--ev-space-7) auto 0;
    padding: var(--ev-space-8) 40px var(--ev-space-8);
    text-align: center;
    border-top: 1px solid var(--ev-hairline-gold);
}
.sv-storybook-cta-kicker {
    font-size: 0.75rem;
    font-weight: 500;
    letter-spacing: 0.24em;
    text-transform: uppercase;
    color: var(--ev-accent);
    margin: 0 0 var(--ev-space-3);
}
.sv-storybook-cta h2 {
    font-family: var(--ev-font-display);
    font-weight: 500;
    font-size: 2.125rem;
    line-height: 1.12;
    margin: 0 0 var(--ev-space-3);
    color: var(--ev-text);
}
.sv-storybook-cta-line {
    color: var(--ev-muted);
    font-size: 1rem;
    line-height: 1.6;
    max-width: 46ch;
    margin: 0 auto var(--ev-space-5);
}
.sv-storybook-cta-btn {
    display: inline-flex;
    align-items: center;
    gap: var(--ev-space-2);
    padding: 15px var(--ev-space-6);
    background: var(--ev-accent);
    color: var(--ev-text-on-accent);
    border-radius: var(--ev-radius);
    font-weight: 500;
    font-size: 0.8125rem;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    text-decoration: none;
    transition: background var(--ev-dur-fast) var(--ev-ease);
}
.sv-storybook-cta-btn:hover { background: var(--ev-accent-hover); }
.sv-storybook-cta-btn i { font-size: 0.6875rem; }
/* The global ".sv-body a" rule colours every link gold; this button has its
   text directly inside the anchor, so without a stronger selector it renders
   gold-on-gold (invisible). Win on specificity for the colour only. */
.sv-storybook .sv-storybook-cta-btn { color: var(--ev-text-on-accent); }

/* ---------- More from the Storybook ---------- */
.sv-storybook-more {
    max-width: 1100px;
    margin: 0 auto;
    padding: var(--ev-space-8) 40px var(--ev-space-5);
}
.sv-storybook-more-head {
    text-align: center;
    margin-bottom: var(--ev-space-6);
}
.sv-storybook-more-head .sv-storybook-eyebrow {
    margin-bottom: var(--ev-space-3);
}
.sv-storybook-more-head h2 {
    font-family: var(--ev-font-display);
    font-weight: 500;
    font-size: 1.875rem;
    margin: 0;
    color: var(--ev-text);
}
.sv-storybook-more-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--ev-space-6);
}
.sv-sb-mini {
    display: block;
    text-decoration: none;
    color: inherit;
}
.sv-sb-mini-img {
    display: block;
    aspect-ratio: 3 / 2;
    border-radius: var(--ev-radius);
    background-color: var(--ev-surface-2);
    background-size: cover;
    background-position: center;
    margin-bottom: var(--ev-space-3);
    border: 1px solid var(--ev-border);
    transition: transform var(--ev-dur-slow) var(--ev-ease);
}
.sv-sb-mini:hover .sv-sb-mini-img { transform: scale(1.015); }
.sv-sb-mini-title {
    display: block;
    font-family: var(--ev-font-display);
    font-weight: 500;
    font-size: 1.3125rem;
    line-height: 1.16;
    margin: 0;
    color: var(--ev-text);
    transition: color var(--ev-dur-fast) var(--ev-ease);
}
.sv-sb-mini:hover .sv-sb-mini-title { color: var(--ev-accent); }

/* =========================================================================
   RESPONSIVE (mobile)
   ========================================================================= */
@media (max-width: 767.98px) {
    .sv-storybook-hero { padding: var(--ev-space-7) var(--ev-space-5) var(--ev-space-6); }
    .sv-storybook-hero h1 { font-size: 2.125rem; }
    .sv-storybook-hero-sub { font-size: 0.875rem; max-width: 100%; }
    .sv-storybook-dot { padding: 0 var(--ev-space-5); margin: var(--ev-space-4) auto var(--ev-space-6); }
    .sv-storybook-wrap { padding: 0 var(--ev-space-5) var(--ev-space-7); }

    /* Featured collapses to a normal full-width card. */
    .sv-sb-feature { grid-template-columns: 1fr; }
    .sv-sb-feature-img { min-height: 0; aspect-ratio: 4 / 3; }
    .sv-sb-feature-body { padding: var(--ev-space-6) var(--ev-space-5); }
    .sv-sb-feature-title { font-size: 1.75rem; }

    .sv-storybook-river { grid-template-columns: 1fr; gap: var(--ev-space-6); }

    /* Article */
    .sv-storybook-crumb { padding: var(--ev-space-4) var(--ev-space-5) 0; font-size: 0.6875rem; }
    .sv-sb-hide-m { display: none; }
    .sv-storybook-head { padding: var(--ev-space-5) var(--ev-space-5); }
    .sv-storybook-head h1 { font-size: 2rem; }
    .sv-storybook-hero-img { height: 300px; }
    .sv-storybook-article-body { padding-top: var(--ev-space-6); }
    .sv-storybook-body { padding: 0 var(--ev-space-5); }
    .sv-storybook-body p { font-size: var(--ev-fs-small); line-height: 1.75; }
    .sv-storybook-body h2 { font-size: 1.5625rem; }
    .sv-storybook-quote { padding: 0 var(--ev-space-5); }
    .sv-storybook-quote blockquote { font-size: 1.4375rem; }
    .sv-storybook-dropcap p:first-of-type::first-letter { font-size: 3.75rem; }
    .sv-storybook-figure { padding: 0 var(--ev-space-5); }
    .sv-storybook-duo-wrap { padding: 0 var(--ev-space-5); }
    .sv-storybook-duo { grid-template-columns: 1fr; }
    .sv-storybook-zig, .sv-storybook-zig--flip { grid-template-columns: 1fr; gap: var(--ev-space-4); padding: 0 var(--ev-space-5); }
    .sv-storybook-zig--flip .sv-storybook-zig-media { order: 1; }
    .sv-storybook-zig--flip .sv-storybook-zig-text { order: 2; }
    .sv-storybook-zig-text h3 { font-size: 1.55rem; }
    .sv-storybook-film { padding: 0 var(--ev-space-5); }
    .sv-storybook-cta { padding: var(--ev-space-7) var(--ev-space-5) var(--ev-space-7); }
    .sv-storybook-cta h2 { font-size: 1.6875rem; }
    .sv-storybook-more { padding: var(--ev-space-7) var(--ev-space-5) var(--ev-space-4); }
    .sv-storybook-more-grid { grid-template-columns: 1fr; gap: var(--ev-space-6); }
}


/* Page glue */
/* ===== SpicyVisionLanding/wwwroot/css/pages/_client-albums.css ===== */
/* =========================================================================
   Client Albums page (/client-album-index and /client-album/{id}).
   Shared between the index and the detail page; both render the same album
   primitives. lightGallery on the detail page moves DOM into a separate
   lightbox layer, so CSS isolation would lose scope attributes.
   ========================================================================= */

.sv-albums-page {
    padding-bottom: var(--ev-space-9);
}

/* Header / filter strip */
.sv-albums-header {
    text-align: center;
    padding: var(--ev-space-8) var(--ev-space-4) var(--ev-space-6);
}
.sv-albums-title {
    font-family: var(--ev-font-display);
    font-style: italic;
    font-weight: 500;
    font-size: clamp(2.25rem, 5vw, 3.25rem);
    color: var(--ev-text);
    margin: 0 0 var(--ev-space-2);
    line-height: 1.1;
}
.sv-albums-sub {
    color: var(--ev-muted);
    max-width: 620px;
    margin: 0 auto var(--ev-space-5);
    font-size: 1.0625rem;
}

.sv-albums-filters {
    display: flex;
    flex-wrap: wrap;
    gap: var(--ev-space-2) var(--ev-space-3);
    justify-content: center;
    margin-top: var(--ev-space-4);
}
.sv-albums-filter {
    display: inline-flex;
    align-items: center;
    min-height: var(--ev-touch);
    padding: 0 1.25rem;
    border-radius: var(--ev-radius-pill);
    border: 1px solid var(--ev-border);
    color: var(--ev-text);
    font-size: 0.9375rem;
    letter-spacing: 0.02em;
    text-decoration: none;
    transition: border-color var(--ev-dur-fast) var(--ev-ease),
                color var(--ev-dur-fast) var(--ev-ease),
                background var(--ev-dur-fast) var(--ev-ease);
}
.sv-albums-filter:hover,
.sv-albums-filter.active {
    border-color: var(--ev-accent);
    color: var(--ev-accent);
    background: rgba(201, 163, 116, 0.08);
}

/* Divider with center ornament */
.sv-albums-divider {
    max-width: var(--ev-max-w);
    margin: 0 auto var(--ev-space-6);
    padding: 0 var(--ev-space-4);
    display: flex;
    align-items: center;
    gap: var(--ev-space-4);
}
/* Detail page tightens the gap so the video enters the viewport sooner. */
.sv-album-hero + .sv-albums-divider {
    margin-bottom: var(--ev-space-4);
}
.sv-albums-divider::before,
.sv-albums-divider::after {
    content: "";
    flex: 1;
    height: 1px;
    background: linear-gradient(90deg, transparent, rgba(201, 163, 116, 0.35), transparent);
}
.sv-albums-divider i {
    color: var(--ev-accent);
    opacity: 0.6;
    font-size: 0.75rem;
}

/* Albums grid */
.sv-albums-grid {
    max-width: var(--ev-max-w);
    margin: 0 auto;
    padding: 0 var(--ev-space-4);
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--ev-space-6) var(--ev-space-5);
}
@media (min-width: 640px)  { .sv-albums-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) { .sv-albums-grid { grid-template-columns: repeat(3, 1fr); } }

.sv-album-card {
    display: block;
    text-decoration: none;
    color: inherit;
    transition: transform var(--ev-dur-base) var(--ev-ease);
}
.sv-album-card:hover { transform: translateY(-4px); }

.sv-album-cover {
    position: relative;
    aspect-ratio: 4 / 3;
    overflow: hidden;
    border-radius: var(--ev-radius);
    background: var(--ev-surface);
    margin-bottom: var(--ev-space-3);
    border: 1px solid var(--ev-border);
}
.sv-album-cover img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    transition: transform var(--ev-dur-slow) var(--ev-ease);
}
.sv-album-card:hover .sv-album-cover img { transform: scale(1.04); }
.sv-album-cover iframe {
    width: 100%;
    height: 100%;
    display: block;
    border: 0;
}

.sv-album-title {
    font-family: var(--ev-font-display);
    font-weight: 500;
    font-size: 1.5rem;
    color: var(--ev-text);
    margin: 0 0 var(--ev-space-1);
    text-align: center;
    line-height: 1.2;
}
.sv-album-card:hover .sv-album-title { color: var(--ev-accent); }

.sv-album-meta {
    color: var(--ev-muted);
    font-size: 0.9375rem;
    text-align: center;
    letter-spacing: 0.02em;
    margin: 0;
}

/* CTA under grid */
.sv-albums-cta {
    text-align: center;
    margin-top: var(--ev-space-8);
}

/* Empty state */
.sv-albums-empty {
    text-align: center;
    color: var(--ev-muted);
    padding: var(--ev-space-7) var(--ev-space-4);
    max-width: 520px;
    margin: 0 auto;
}

/* Single album page - tight vertical rhythm so the hero video enters the
   viewport with minimal scroll. */
.sv-album-hero {
    text-align: center;
    padding: var(--ev-space-5) var(--ev-space-4) var(--ev-space-4);
}
.sv-album-hero-title {
    font-family: var(--ev-font-display);
    font-style: italic;
    font-weight: 500;
    font-size: clamp(2rem, 5vw, 3.25rem);
    color: var(--ev-text);
    margin: 0 0 var(--ev-space-2);
    line-height: 1.1;
}
.sv-album-hero-sub {
    color: var(--ev-muted);
    font-size: 1.0625rem;
    margin: 0;
    letter-spacing: 0.02em;
}

.sv-album-back {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    color: var(--ev-accent);
    text-decoration: none;
    font-size: 0.95rem;
    margin-bottom: var(--ev-space-4);
    min-height: var(--ev-touch);
}
.sv-album-back:hover {
    color: var(--ev-accent-hover);
    text-decoration: underline;
}

/* Top spacing variant used on the detail page header container - replaces
   the inline padding-top that used to live on .sv-container. */
.sv-album-container-top { padding-top: var(--ev-space-5); }

/* Brick masonry album gallery - CSS columns preserve native aspect ratio. */
.sv-album-gallery {
    max-width: var(--ev-max-w);
    margin: 0 auto;
    padding: 0 var(--ev-space-4);
    column-count: 1;
    column-gap: var(--ev-space-3);
}
@media (min-width: 640px)  { .sv-album-gallery { column-count: 2; } }
@media (min-width: 1024px) {
    .sv-album-gallery {
        column-count: 3;
        column-gap: var(--ev-space-4);
    }
}

.sv-album-gallery a.gallery-item {
    display: block;
    break-inside: avoid;
    -webkit-column-break-inside: avoid;
    position: relative;
    overflow: hidden;
    border-radius: var(--ev-radius);
    background: var(--ev-surface);
    border: 1px solid var(--ev-border);
    margin-bottom: var(--ev-space-3);
}
@media (min-width: 1024px) {
    .sv-album-gallery a.gallery-item { margin-bottom: var(--ev-space-4); }
}

.sv-album-gallery img {
    width: 100%;
    height: auto;
    display: block;
    transition: transform var(--ev-dur-slow) var(--ev-ease),
                opacity var(--ev-dur-base) var(--ev-ease);
}
.sv-album-gallery a.gallery-item:hover img {
    transform: scale(1.04);
    opacity: 0.95;
}

/* Films section inside a client album. */
.sv-album-films {
    max-width: var(--ev-max-w);
    margin: 0 auto var(--ev-space-6);
    padding: 0 var(--ev-space-4);
}
@media (min-width: 1024px) {
    .sv-album-films { margin-bottom: var(--ev-space-7); }
}


