/* =========================================================
   LocalBusiness — shared front-end styles
   Notes:
   - This file is loaded by BOTH the public site and the admin /
     business / user dashboards. New rules added for the 2026 public
     redesign are scoped under either the public-only classes
     (.lb-public-nav, .lb-secondary-nav, .lb-hero, .lb-deal-card, etc.)
     OR the public-main wrapper, so the admin/dashboard look is left
     untouched.
   ========================================================= */

:root {
    --lb-primary: #ff5722;
    --lb-primary-dark: #e64a19;
    --lb-primary-soft: #fff3ed;
    --lb-secondary: #1e293b;
    --lb-accent: #facc15;
    --lb-success: #16a34a;
    --lb-light: #f8fafc;
    --lb-muted: #64748b;
    --lb-border: #e2e8f0;
    --lb-radius: 12px;
    --lb-radius-sm: 8px;
    --lb-shadow: 0 4px 16px rgba(15, 23, 42, .06);
    --lb-shadow-md: 0 10px 30px rgba(15, 23, 42, .08);
    --lb-shadow-lg: 0 18px 50px rgba(15, 23, 42, .12);
    --lb-fast: 160ms ease;
    --lb-med: 280ms cubic-bezier(.22, .61, .36, 1);
}

body {
    font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
    background: #f6f7fb;
    color: #1f2937;
    font-size: 14.5px;
}
a { color: var(--lb-primary); }
a:hover { color: var(--lb-primary-dark); }

/* ---------- Buttons ---------- */
.btn-primary,
.bg-primary {
    background-color: var(--lb-primary) !important;
    border-color: var(--lb-primary) !important;
}
.btn-primary:hover { background-color: var(--lb-primary-dark) !important; border-color: var(--lb-primary-dark) !important; }
.text-primary { color: var(--lb-primary) !important; }
.btn-outline-primary { color: var(--lb-primary); border-color: var(--lb-primary); }
.btn-outline-primary:hover { background-color: var(--lb-primary); color: #fff; }

/* ---------- Cards ---------- */
.card {
    border: 1px solid var(--lb-border);
    border-radius: var(--lb-radius);
    box-shadow: var(--lb-shadow);
}
.card-header {
    background: #fff;
    border-bottom: 1px solid var(--lb-border);
    font-weight: 600;
}

.navbar-brand b { color: var(--lb-primary); }
.navbar.bg-white { box-shadow: 0 1px 3px rgba(0, 0, 0, .04); }

/* =========================================================
   PUBLIC-ONLY 2026 redesign
   Scoped under .public-main or specific public-* classes so admin
   panels keep their existing look.
   ========================================================= */

.public-main { background: #f6f7fb; }

/* ---------- Top bar (public site) ---------- */
.lb-public-nav {
    background: #fff;
    border-bottom: 1px solid var(--lb-border);
    box-shadow: 0 1px 3px rgba(15, 23, 42, .04);
    position: sticky;
    top: 0;
    z-index: 1030;
}
.lb-public-nav .navbar-brand {
    font-size: 22px;
    font-weight: 800;
    letter-spacing: -.02em;
    color: var(--lb-secondary);
}
.lb-public-nav .navbar-brand b { color: var(--lb-primary); }
.lb-public-nav .lb-search {
    flex: 1;
    max-width: 460px;
}
.lb-public-nav .lb-search .form-control {
    border-right: 0;
    border-color: var(--lb-border);
}
.lb-public-nav .lb-search .btn { border-color: var(--lb-border); }

.lb-icon-btn {
    width: 42px; height: 42px;
    border-radius: 10px;
    display: inline-flex;
    align-items: center; justify-content: center;
    border: 1px solid var(--lb-border);
    background: #fff;
    color: var(--lb-secondary);
    transition: var(--lb-fast);
}
.lb-icon-btn:hover { color: var(--lb-primary); border-color: var(--lb-primary); }

/* The account dropdown carries grouped Login/Register headers for guests, which
   need more room than Bootstrap's default menu width. */
.lb-account-menu { min-width: 220px; }

/* ---------- Secondary nav (under top bar, desktop only) ---------- */
.lb-secondary-nav {
    background: #fff;
    border-bottom: 1px solid var(--lb-border);
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}
.lb-secondary-nav .container {
    display: flex;
    gap: 4px;
    padding-top: 6px;
    padding-bottom: 6px;
}
.lb-secondary-nav a {
    flex: 0 0 auto;
    padding: 10px 16px;
    border-radius: var(--lb-radius-sm);
    font-weight: 500;
    color: var(--lb-secondary);
    text-decoration: none;
    transition: var(--lb-fast);
    white-space: nowrap;
    font-size: 14.5px;
}
.lb-secondary-nav a:hover {
    color: var(--lb-primary);
    background: var(--lb-primary-soft);
}
.lb-secondary-nav a.active {
    color: var(--lb-primary);
    background: var(--lb-primary-soft);
}

/* ---------- Off-canvas mobile menu ---------- */
.lb-offcanvas {
    background: #fff;
}
.lb-offcanvas .offcanvas-header {
    border-bottom: 1px solid var(--lb-border);
    padding: 16px 20px;
}
.lb-offcanvas .offcanvas-body { padding: 8px; }
.lb-offcanvas .lb-mobile-menu a {
    display: flex; align-items: center;
    gap: 12px;
    padding: 14px 16px;
    border-radius: var(--lb-radius-sm);
    color: var(--lb-secondary);
    font-weight: 500;
    text-decoration: none;
    transition: var(--lb-fast);
}
.lb-offcanvas .lb-mobile-menu a:hover,
.lb-offcanvas .lb-mobile-menu a.active {
    background: var(--lb-primary-soft);
    color: var(--lb-primary);
}
.lb-offcanvas .lb-mobile-menu a i { font-size: 20px; width: 22px; text-align: center; }
.lb-offcanvas .lb-mobile-cta { padding: 16px; border-top: 1px solid var(--lb-border); }

/* ---------- Fixed mobile top strip: search + category icons ----------
   The wrapper pins directly beneath the sticky brand strip (which is 64px tall
   + 1px border). On scroll, search + categories stay put so they are always one
   tap away. z-index sits just under the brand strip (1030) and above content.
   Only shown on phones/tablets — the d-lg-none class hides it >= 992px. */
.lb-mobile-topbar {
    position: sticky;
    top: 64px;                 /* height of .lb-public-nav's min-height:64px row */
    z-index: 1029;
    background: #fff;
    border-bottom: 1px solid var(--lb-border);
    box-shadow: 0 2px 6px rgba(15, 23, 42, .05);
}

/* ---------- Mobile search row (inside the fixed top strip) ---------- */
.lb-mobile-search {
    padding: 10px 0;
    background: #fff;
}
.lb-mobile-search-field { position: relative; }
.lb-mobile-search-field > i {
    position: absolute;
    left: 16px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--lb-muted);
    font-size: 17px;
    pointer-events: none;
}
.lb-mobile-search-field .form-control {
    height: 46px;
    /* Room for the icon on the left; pill shape to match the mock-up. */
    padding-left: 44px;
    border-radius: 999px;
    border-color: var(--lb-border);
    background: #fff;
    font-size: 15px;
}
.lb-mobile-search-field .form-control:focus {
    border-color: var(--lb-primary);
    box-shadow: 0 0 0 3px rgba(255, 87, 34, .12);
}

/* ---------- Mobile category strip (inside the fixed top strip) ----------
   A compact, horizontally-scrollable row of category icons — the same idea as
   the home page's "Browse by category" strip, but slimmer so it fits in the
   pinned top bar. Scrolls sideways; scrollbar hidden. */
.lb-mobile-cats {
    padding: 6px 0 8px;
    background: #fff;
    border-top: 1px solid var(--lb-border);
}
.lb-mobile-cat-track {
    display: flex;
    gap: 14px;
    overflow-x: auto;
    scroll-behavior: smooth;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    -ms-overflow-style: none;
    padding: 2px 2px 0;
}
.lb-mobile-cat-track::-webkit-scrollbar { display: none; }
.lb-mobile-cat-item {
    flex: 0 0 auto;
    width: 76px;
    min-height: 82px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 5px;
    padding: 8px 6px;
    /* Soft pastel tile; colour from cat_tile_style() per category. */
    background: linear-gradient(160deg, var(--cat-bg, #eef2f7) 0%, var(--cat-bg2, #e4e9f0) 100%);
    border: 1px solid rgba(15, 23, 42, .05);
    border-radius: 14px;
    text-decoration: none;
    color: #1f2937;
}
.lb-mobile-cat-icon {
    width: 34px;
    height: 34px;
    border-radius: 10px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 19px;
    color: #334155;
    background: transparent;
    overflow: hidden;
    flex-shrink: 0;
}
/* Admin-uploaded category icon image fills the mobile icon badge. */
.lb-mobile-cat-icon-img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    border-radius: 9px;
}
/* Currently-open category: ring + bolder label. */
.lb-mobile-cat-item.active {
    box-shadow: 0 0 0 2px var(--lb-primary, #ff5722) inset;
}
.lb-mobile-cat-item.active .lb-mobile-cat-name { font-weight: 800; }
.lb-mobile-cat-name {
    font-size: 10px;
    font-weight: 700;
    line-height: 1.15;
    text-align: center;
    color: #263142;
    /* Allow up to two lines (reserve the height so the row stays even). */
    min-height: 2.3em;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* ---------- Home banner slider ---------- */
.lb-banner-wrap { padding: 16px 0 4px; }

.lb-banner-slider { position: relative; }

.lb-banner-track {
    display: flex;
    gap: 0;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scroll-behavior: smooth;
    border-radius: 14px;
    /* Hide the scrollbar: the arrows and dots are the affordance here, and a
       visible bar under a banner looks like a rendering fault. */
    scrollbar-width: none;
    -ms-overflow-style: none;
}
.lb-banner-track::-webkit-scrollbar { display: none; }

.lb-banner-slide {
    /* Exactly one slide per view, at every breakpoint. */
    flex: 0 0 100%;
    min-width: 100%;
    scroll-snap-align: start;
}
.lb-banner-slide > a { display: block; }
.lb-banner-slide picture { display: block; }

.lb-banner-slide img {
    display: block;
    width: 100%;
    /* A fixed ratio keeps every slide the same height, so the page does not
       jump as the track scrolls between differently proportioned artwork.
       object-fit covers the box and centres the crop. */
    aspect-ratio: 1600 / 450;
    object-fit: cover;
    object-position: center;
    border-radius: 14px;
    background: #eef0f5;
}

.lb-banner-arrow {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 38px; height: 38px;
    border-radius: 50%;
    border: 1px solid var(--lb-border);
    background: rgba(255, 255, 255, .92);
    color: var(--lb-secondary);
    display: inline-flex;
    align-items: center; justify-content: center;
    box-shadow: 0 2px 8px rgba(15, 23, 42, .12);
    transition: var(--lb-fast);
    z-index: 2;
}
.lb-banner-arrow:hover { color: var(--lb-primary); border-color: var(--lb-primary); }
.lb-banner-prev { left: 10px; }
.lb-banner-next { right: 10px; }

.lb-banner-dots {
    display: flex;
    justify-content: center;
    gap: 7px;
    margin-top: 10px;
}
.lb-banner-dots button {
    width: 8px; height: 8px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: #cbd5e1;
    transition: var(--lb-fast);
}
.lb-banner-dots button.active {
    background: var(--lb-primary);
    /* Widened rather than enlarged, so the row's height never shifts. */
    width: 20px;
    border-radius: 999px;
}

@media (max-width: 767.98px) {
    .lb-banner-wrap { padding: 4px 0 0; }
    .lb-banner-track { border-radius: 12px; }
    .lb-banner-slide img { border-radius: 12px; }

    /* Phones, WITH a dedicated mobile image: the artwork was authored for this
       shape, so a taller box is safe and fills the screen properly.
       `.has-mobile` is set server-side because <picture> swaps the resource,
       NOT the CSS box — a media query alone cannot tell the two cases apart. */
    .lb-banner-slide.has-mobile img { aspect-ratio: 16 / 10; }

    /* Phones, WITHOUT one: keep the wide desktop ratio. It renders short, but
       every pixel of the artwork is visible. Cropping to a squarer box here
       would discard ~50% of a 1600x450 banner's width and take the headline
       with it, which is the exact failure a mobile upload exists to avoid. */
    .lb-banner-slide:not(.has-mobile) img { aspect-ratio: 1600 / 450; }

    /* Arrows are redundant next to a swipe, and they cover the artwork. */
    .lb-banner-arrow { display: none; }
}

/* ---------- Home posters (admin "Add Poster") ---------- */
/* Poster 1: a single static banner below "Browse by category". */
.lb-poster-wrap { padding: 10px 0; }
.lb-poster-static { display: block; }
.lb-poster-static picture { display: block; }
.lb-poster-static img {
    display: block;
    width: 100%;
    aspect-ratio: 1600 / 450;
    object-fit: cover;
    object-position: center;
    border-radius: 14px;
    background: #eef0f5;
}
/* Poster 2: a slider, reuses .lb-banner-* mechanics; just its own spacing. */
.lb-poster-slider-wrap { padding: 18px 0 6px; }

@media (max-width: 767.98px) {
    .lb-poster-static img { border-radius: 12px; }
    .lb-poster-static:not(.has-mobile) img,
    .lb-poster-wrap .has-mobile img { /* keep it visible on phones */ }
}

/* ---------- Home tag buttons (admin "Add Tag") ---------- */
.lb-tagbtn-wrap { padding: 4px 0 18px; }
.lb-tagbtn-row {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    justify-content: center;
}
.lb-tagbtn {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 9px 16px;
    border-radius: 999px;
    border: 1px solid var(--lb-border);
    background: #fff;
    color: var(--lb-secondary);
    font-weight: 600;
    font-size: 14px;
    text-decoration: none;
    box-shadow: 0 1px 3px rgba(15, 23, 42, .06);
    transition: var(--lb-fast);
}
.lb-tagbtn i { color: var(--lb-primary); font-size: 16px; }
.lb-tagbtn:hover {
    border-color: var(--lb-primary);
    color: var(--lb-primary);
    transform: translateY(-2px);
    box-shadow: 0 4px 12px rgba(15, 23, 42, .12);
}
.lb-tagbtn:hover i { color: var(--lb-primary); }
/* A non-clickable tag reads the same but does not lift or change on hover. */
.lb-tagbtn-plain { cursor: default; }
.lb-tagbtn-plain:hover { transform: none; box-shadow: 0 1px 3px rgba(15, 23, 42, .06); border-color: var(--lb-border); color: var(--lb-secondary); }

/* ---------- Home display-ad row (was inside the hero) ---------- */
.lb-home-ad-wrap { padding: 14px 0 0; }
.lb-home-ad-wrap :is(img, iframe) { max-width: 100%; }

/* ---------- Fixed mobile bottom bar (floating pill, raised centre CTA) ----------
   Redesigned as a detached, rounded "pill" bar floating above the screen edge
   with a soft shadow, and a raised circular call-to-action in the centre slot
   (Expert Consult) that lifts above the bar like a native app's action button. */
.lb-bottom-nav {
    position: fixed;
    left: 12px; right: 12px;
    /* Float above the very bottom edge, clearing the iOS home indicator. */
    bottom: calc(10px + env(safe-area-inset-bottom, 0px));
    z-index: 1035;
    display: flex;
    background: #fff;
    border: 1px solid var(--lb-border);
    border-radius: 20px;
    box-shadow: 0 6px 22px rgba(15, 23, 42, .16);
    /* Room for the raised centre CTA to sit above the bar without clipping. */
    overflow: visible;
}
.lb-bottom-nav a {
    flex: 1 1 0;
    min-width: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 3px;
    padding: 7px 2px 6px;
    text-decoration: none;
    color: var(--lb-muted);
    /* 10px, not 11px: five slots leave ~78px each on a 390px phone. */
    font-size: 10px;
    font-weight: 600;
    line-height: 1.1;
    transition: var(--lb-fast);
    /* Sized for the tallest slot (icon row + two label lines), and top-aligned
       rather than centred: with justify-content:center a one-line label sits
       lower than the first line of a two-line one, so they would not share a
       baseline — which is the whole point of the fixed icon row below. */
    justify-content: flex-start;
    min-height: 66px;
}
/* All icons reserve the same 26px row, including the taller CTA circle below,
   so the labels below them stay on one baseline. */
.lb-bottom-nav a i {
    font-size: 19px;
    line-height: 1;
    height: 26px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.lb-bottom-nav a span {
    max-width: 100%;
    /* Wrap instead of ellipsis: with five slots a two-word label like
       "Expert Consult" cannot fit one line, and "Expert Con…" reads worse than
       two short lines. Clamped to 2 so the bar height stays predictable. */
    white-space: normal;
    text-align: center;
    line-height: 1.15;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* Expert Consult is the bar's call to action, not a peer tab: accent colour at
   all times (even when another page is active) and a filled circle behind the
   icon so it reads as a button. */
.lb-bottom-nav a.lb-bn-cta { color: var(--lb-primary); }
/* Raised circular action button: a bigger accent circle that lifts above the
   bar with a white ring, so it reads as the primary action. */
.lb-bottom-nav a.lb-bn-cta i {
    width: 52px;
    height: 52px;
    margin-top: -24px;           /* lift above the bar's top edge */
    margin-bottom: 2px;
    border-radius: 50%;
    background: var(--lb-primary);
    color: #fff;
    font-size: 22px;
    border: 4px solid #fff;      /* white ring separates it from the bar */
    box-shadow: 0 6px 14px rgba(255, 87, 34, .40);
}
/* The CTA is accent-coloured at all times, so colour alone cannot show that you
   are ON it. A stronger ring gives the active state a second signal. */
.lb-bottom-nav a.lb-bn-cta.active i {
    box-shadow: 0 0 0 3px rgba(255, 87, 34, .28), 0 6px 14px rgba(255, 87, 34, .40);
}
.lb-bottom-nav a.lb-bn-cta.active span { text-decoration: underline; }
.lb-bottom-nav a.active,
.lb-bottom-nav a:active { color: var(--lb-primary); }

/* NOTE: the clearance that stops this bar covering the footer lives further
   down, immediately AFTER `footer.lb-footer`'s own rule. It cannot live here:
   a media query adds no specificity, so the later `padding` shorthand on
   `footer.lb-footer` would reset `padding-bottom` and the reservation would
   silently do nothing. */

/* ---------- Hero ---------- */
.lb-hero {
    background:
        radial-gradient(circle at 80% -10%, rgba(255, 255, 255, .15), transparent 50%),
        linear-gradient(135deg, var(--lb-primary) 0%, #f97316 60%, #fb923c 100%);
    color: #fff;
    padding: 60px 0 70px;
    position: relative;
    overflow: hidden;
}
.lb-hero::before {
    content: "";
    position: absolute;
    inset: 0;
    background-image:
        radial-gradient(circle at 10% 80%, rgba(255, 255, 255, .12), transparent 40%);
    pointer-events: none;
}
.lb-hero h1 {
    font-weight: 800;
    letter-spacing: -.02em;
    font-size: clamp(28px, 4vw, 44px);
    line-height: 1.15;
    text-shadow: 0 2px 8px rgba(0, 0, 0, .06);
}
.lb-hero p { font-size: 17px; opacity: .95; }
.lb-hero .form-control { height: 48px; border-radius: 8px; border: 0; }
.lb-hero .btn { height: 48px; }

/* Compact page-header band: a title + one line of copy, no more. Used on
   listing/landing pages (deals, vaayu-setu, expert-consult) where the hero is
   a heading strip, not a call to action, so it should not eat the fold. */
.lb-hero.lb-hero-slim { padding: 22px 0; }
.lb-hero.lb-hero-slim h1 { font-size: clamp(20px, 3vw, 26px); margin-bottom: 2px; }
.lb-hero.lb-hero-slim p  { font-size: 14px; margin-bottom: 0; }
@media (max-width: 767.98px) {
    .lb-hero.lb-hero-slim { padding: 18px 0; }
}

/* ---------- Section headings ---------- */
.lb-section { padding: 56px 0; }
.lb-section-head {
    display: flex;
    align-items: end;
    justify-content: space-between;
    margin-bottom: 22px;
    flex-wrap: wrap;
    gap: 12px;
}
.lb-section-head h3 {
    font-weight: 800;
    letter-spacing: -.02em;
    margin: 0;
    font-size: clamp(20px, 2.4vw, 26px);
}
.lb-section-head .lb-eyebrow {
    color: var(--lb-primary);
    font-weight: 600;
    text-transform: uppercase;
    font-size: 12px;
    letter-spacing: .12em;
}
.lb-view-all {
    color: var(--lb-primary);
    font-weight: 600;
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    gap: 6px;
}
.lb-view-all:hover { color: var(--lb-primary-dark); }

/* ---------- Horizontal carousel (CSS scroll-snap) ---------- */
.lb-carousel {
    position: relative;
}
.lb-carousel-track {
    display: flex;
    gap: 18px;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    scroll-behavior: smooth;
    padding: 4px 4px 18px 4px;     /* room for shadow + scrollbar gap */
    scrollbar-width: thin;
}
.lb-carousel-track > * {
    flex: 0 0 280px;
    scroll-snap-align: start;
}
@media (min-width: 768px) {
    .lb-carousel-track > * { flex: 0 0 300px; }
}
.lb-carousel-arrow {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 44px; height: 44px;
    border-radius: 50%;
    background: #fff;
    border: 1px solid var(--lb-border);
    box-shadow: var(--lb-shadow);
    color: var(--lb-secondary);
    display: inline-flex;
    align-items: center; justify-content: center;
    cursor: pointer;
    z-index: 4;
    transition: var(--lb-fast);
}
.lb-carousel-arrow:hover { color: var(--lb-primary); border-color: var(--lb-primary); }
.lb-carousel-arrow.lb-prev { left: -22px; }
.lb-carousel-arrow.lb-next { right: -22px; }
.lb-carousel-arrow:disabled,
.lb-carousel-arrow.is-disabled { opacity: .4; cursor: default; }
@media (max-width: 767px) {
    .lb-carousel-arrow { display: none; }
}

/* ---------- Carousel: exact-fit variant (.lb-carousel-fit) ----------
   The base carousel sizes slides in fixed pixels, so how many you see depends
   on the viewport. This variant instead divides the track so exactly N slides
   fill the width: 2 on mobile, 4 from the md breakpoint up. The subtracted
   figure is the total gap between the visible slides — (N-1) x 18px — because
   `gap` is added BETWEEN flex items and would otherwise push the last one out
   of view. */
.lb-carousel-fit .lb-carousel-track > * {
    flex: 0 0 calc((100% - 18px) / 2);          /* 2 up: one 18px gap */
    scroll-snap-align: start;
}
@media (min-width: 768px) {
    .lb-carousel-fit .lb-carousel-track > * {
        flex: 0 0 calc((100% - 54px) / 4);      /* 4 up: three 18px gaps */
    }
}
/* Arrows stay available on mobile here — swipe still works, but this variant is
   used for sections where the arrows are the discoverable affordance. They move
   inside the track because the base -22px position would sit off-screen on a
   phone, and shrink so they cover as little of the card as possible. */
@media (max-width: 767.98px) {
    .lb-carousel-fit .lb-carousel-arrow {
        display: inline-flex;
        width: 32px; height: 32px;
        background: rgba(255, 255, 255, .93);
    }
    .lb-carousel-fit .lb-carousel-arrow.lb-prev { left: 2px; }
    .lb-carousel-fit .lb-carousel-arrow.lb-next { right: 2px; }
    /* Nothing to page through when everything already fits. */
    .lb-carousel-fit .lb-carousel-arrow.is-disabled { display: none; }
}

/* ---------- Deal card (public new variant) ---------- */
.lb-deal-card {
    border: 1px solid #e6e9ef;
    border-radius: 16px;
    background: #fff;
    overflow: hidden;
    transition: transform var(--lb-med), box-shadow var(--lb-med), border-color var(--lb-med);
    height: 100%;
    display: flex; flex-direction: column;
    /* Soft resting shadow so cards read as raised even before hover. */
    box-shadow: 0 2px 10px rgba(15, 23, 42, .06);
}
.lb-deal-card:hover {
    transform: translateY(-5px);
    box-shadow: 0 14px 30px rgba(15, 23, 42, .14);
    border-color: #d9dee7;
}
.lb-deal-card .lb-deal-img {
    aspect-ratio: 4/3;
    object-fit: cover;
    width: 100%;
    background: #f1f5f9;
    display: block;
}
.lb-deal-card .lb-deal-body { padding: 14px; flex: 1; display: flex; flex-direction: column; }
.lb-deal-card .lb-deal-biz {
    font-size: 12px;
    color: var(--lb-muted);
    margin-bottom: 6px;
    display: flex; align-items: center; gap: 6px;
}
.lb-deal-card .lb-deal-biz img { width: 18px; height: 18px; border-radius: 50%; object-fit: cover; }
.lb-deal-card .lb-deal-title {
    font-weight: 600;
    color: #0f172a;
    line-height: 1.3;
    margin-bottom: 10px;
    min-height: 38px;
    font-size: 15px;
}
.lb-deal-card .lb-price-strike { text-decoration: line-through; color: #94a3b8; font-size: 13px; }
.lb-deal-card .lb-price-now    { color: var(--lb-primary); font-weight: 700; font-size: 18px; }
.lb-deal-card .lb-deal-badge {
    position: absolute;
    top: 10px; left: 10px;
    background: var(--lb-primary);
    color: #fff;
    padding: 4px 10px;
    border-radius: 6px;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: .02em;
}

/* ---------- Business card ---------- */
.lb-biz-card {
    border: 1px solid #e6e9ef;
    border-radius: 16px;
    background: #fff;
    overflow: hidden;
    transition: transform var(--lb-med), box-shadow var(--lb-med), border-color var(--lb-med);
    text-decoration: none;
    color: inherit;
    display: block;
    height: 100%;
    box-shadow: 0 2px 10px rgba(15, 23, 42, .06);
}
.lb-biz-card:hover { transform: translateY(-5px); box-shadow: 0 14px 30px rgba(15, 23, 42, .14); border-color: #d9dee7; color: inherit; }
.lb-biz-card .lb-biz-cover {
    aspect-ratio: 16/8;
    background: linear-gradient(135deg, #1e293b 0%, #475569 100%) center/cover no-repeat;
}
.lb-biz-card .lb-biz-body {
    padding: 16px;
    position: relative;
}
.lb-biz-card .lb-biz-logo {
    width: 56px; height: 56px;
    border-radius: 12px;
    border: 3px solid #fff;
    object-fit: cover;
    background: #fff;
    box-shadow: var(--lb-shadow);
    margin-top: -42px;
    margin-bottom: 8px;
}
.lb-biz-card h6 { font-weight: 700; margin-bottom: 4px; line-height: 1.25; }
.lb-biz-card .lb-biz-meta { color: var(--lb-muted); font-size: 13px; }

/* ---------- Classified card ---------- */
.lb-classified-card {
    border: 1px solid var(--lb-border);
    border-radius: var(--lb-radius);
    background: #fff;
    overflow: hidden;
    transition: var(--lb-med);
    text-decoration: none;
    color: inherit;
    height: 100%;
    display: flex; flex-direction: column;
}
.lb-classified-card:hover { transform: translateY(-3px); box-shadow: var(--lb-shadow-md); color: inherit; }
.lb-classified-card .lb-classified-img {
    aspect-ratio: 4/3;
    object-fit: cover;
    width: 100%;
    background: #f1f5f9;
}
.lb-classified-card .lb-classified-body { padding: 14px; flex: 1; display: flex; flex-direction: column; }
.lb-classified-card .lb-classified-cat {
    font-size: 12px;
    color: var(--lb-muted);
    margin-bottom: 6px;
    text-transform: uppercase;
    letter-spacing: .04em;
}
.lb-classified-card h6 { font-weight: 600; margin-bottom: 8px; min-height: 38px; }
.lb-classified-card .lb-classified-foot { display: flex; justify-content: space-between; align-items: center; }

/* ---------- Donation card (public preview = title + photo + category) ---------- */
.lb-donation-card {
    border: 1px solid var(--lb-border);
    border-radius: var(--lb-radius);
    background: #fff;
    overflow: hidden;
    text-decoration: none;
    color: inherit;
    display: block;
    height: 100%;
    transition: var(--lb-med);
}
.lb-donation-card:hover { transform: translateY(-3px); box-shadow: var(--lb-shadow-md); color: inherit; }
.lb-donation-card .lb-don-img {
    aspect-ratio: 4/3;
    object-fit: cover;
    width: 100%;
    background: #f1f5f9;
}
.lb-donation-card .lb-don-img-fallback {
    aspect-ratio: 4/3;
    background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);
    display: flex; align-items: center; justify-content: center;
}
.lb-donation-card .lb-don-img-fallback i {
    font-size: 60px;
    color: var(--lb-primary);
    opacity: .8;
}
.lb-donation-card .lb-don-body { padding: 14px; }
.lb-donation-card .lb-don-cat {
    display: inline-block;
    font-size: 11px;
    color: var(--lb-primary);
    background: var(--lb-primary-soft);
    padding: 3px 10px;
    border-radius: 999px;
    font-weight: 600;
    margin-bottom: 8px;
    text-transform: uppercase;
    letter-spacing: .05em;
}
.lb-donation-card h6 { font-weight: 600; margin: 0; line-height: 1.3; }

/* ---------- Status pills (kept for compatibility) ---------- */
.pill { display: inline-block; padding: 3px 10px; border-radius: 999px; font-size: 12px; font-weight: 500; }
.pill-active   { background: #dcfce7; color: #166534; }
.pill-pending  { background: #fef3c7; color: #92400e; }
.pill-rejected { background: #fee2e2; color: #991b1b; }
.pill-expired  { background: #f1f5f9; color: #475569; }
.pill-redeemed { background: #dbeafe; color: #1e40af; }

/* ---------- Reward-type badge (deals) ---------- */
.reward-badge {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 3px 10px;
    border-radius: 999px;
    font-size: 12px;
    font-weight: 600;
    line-height: 1.4;
    white-space: nowrap;
}
.reward-badge.no-rewards { background: #f1f5f9; color: #475569; }
.reward-badge i { font-size: 12px; }

/* Rewards badge — bigger, golden pill with a coin, per the reference design.
   A warm gold gradient with a dark-amber label reads as "cashback/coins". */
.reward-badge.rewards {
    padding: 6px 14px;
    font-size: 13.5px;
    font-weight: 700;
    color: #7a4b00;
    background: linear-gradient(135deg, #fde68a 0%, #f6c945 55%, #e5a800 100%);
    border: 1px solid #e0a900;
    box-shadow: 0 2px 6px rgba(224, 169, 0, .35);
}
/* The coin icon: a filled golden disc so it reads as a coin even at a glance. */
.reward-badge.rewards i {
    font-size: 17px;
    line-height: 1;
    color: #b8860b;                 /* dark goldenrod coin */
    text-shadow: 0 1px 0 rgba(255, 255, 255, .5);
}

/* ---------- Loyalty pill (existing) ---------- */
.loyalty-pill {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 4px 12px; border-radius: 999px; font-size: 12px; font-weight: 600;
}
.loyalty-bronze   { background: #fef3c7; color: #92400e; }
.loyalty-silver   { background: #e2e8f0; color: #475569; }
.loyalty-gold     { background: #fde68a; color: #78350f; }
.loyalty-platinum { background: #c7d2fe; color: #3730a3; }

/* ---------- Existing classes kept intact (admin/dashboard rely on them) ---------- */
.deal-card .deal-img {
    aspect-ratio: 4/3;
    object-fit: cover;
    width: 100%;
    border-top-left-radius: var(--lb-radius);
    border-top-right-radius: var(--lb-radius);
}
.deal-card .deal-badge {
    position: absolute;
    top: 10px; left: 10px;
    background: var(--lb-primary);
    color: #fff;
    padding: 4px 10px;
    border-radius: 4px;
    font-size: 12px;
    font-weight: 600;
}
.deal-card .deal-title { font-weight: 600; color: #0f172a; line-height: 1.3; min-height: 38px; }
.deal-card .price-strike { text-decoration: line-through; color: #94a3b8; }
.deal-card .price-now    { color: var(--lb-primary); font-weight: 700; font-size: 18px; }

/* old hero kept for any non-redesigned page that still references it */
:not(.public-main) .lb-hero { padding: 60px 0 80px; }

.cat-tile {
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    background: #fff; border: 1px solid var(--lb-border); border-radius: 12px;
    padding: 22px 10px; text-align: center; transition: .2s; height: 100%;
    text-decoration: none; color: var(--lb-secondary);
}
.cat-tile:hover { transform: translateY(-2px); box-shadow: var(--lb-shadow); border-color: var(--lb-primary); }
.cat-tile i { font-size: 30px; color: var(--lb-primary); margin-bottom: 8px; }

/* business profile bits used by business.php detail */
.biz-cover { height: 240px; background: #1e293b center/cover no-repeat; border-radius: 14px; }
.biz-logo  { width: 110px; height: 110px; border-radius: 16px; border: 4px solid #fff; object-fit: cover; margin-top: -55px; background: #fff; }
.rating-stars { color: var(--lb-accent); }

/* coupon */
.coupon-card {
    background: #fff; border: 2px dashed var(--lb-primary);
    border-radius: 14px; padding: 22px;
    position: relative; overflow: hidden;
}
.coupon-card .code {
    font-family: 'Courier New', monospace;
    font-weight: 700; font-size: 22px; letter-spacing: 2px;
    color: var(--lb-primary);
}

/* utility */
.text-muted-2 { color: var(--lb-muted); }
.divider { height: 1px; background: var(--lb-border); margin: 18px 0; }
.shadow-sm-x { box-shadow: 0 1px 3px rgba(15, 23, 42, .05); }

/* ---------- Footer ---------- */
footer.lb-footer {
    background: var(--lb-secondary);
    color: #cbd5e1;
    padding: 56px 0 24px;
    margin-top: 60px;
}
footer.lb-footer h6 { color: #fff; font-weight: 700; margin-bottom: 16px; letter-spacing: -.01em; }
footer.lb-footer a  { color: #94a3b8; text-decoration: none; display: block; padding: 4px 0; transition: var(--lb-fast); }
footer.lb-footer a:hover { color: #fff; }
footer.lb-footer .copy { border-top: 1px solid #334155; margin-top: 30px; padding-top: 18px; font-size: 13px; color: #94a3b8; }

/* Clearance for the fixed mobile bottom bar (.lb-bottom-nav).
   MUST stay after the `footer.lb-footer` shorthand above: same specificity, so
   source order decides, and declaring it earlier makes it a no-op that lets the
   bar cover the copyright and Terms/Privacy row. */
@media (max-width: 991.98px) {
    /* Floating bar: ~66px tall + 10px float gap + a little breathing room. */
    footer.lb-footer { padding-bottom: calc(96px + env(safe-area-inset-bottom, 0px)); }
}

/* ---------- Mobile tweaks ---------- */
@media (max-width: 767.98px) {
    .lb-hero { padding: 40px 0 50px; }
    .lb-section { padding: 40px 0; }
}

/* Reduce motion on user request */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after { transition: none !important; animation: none !important; scroll-behavior: auto !important; }
}


/* =========================================================
   HOME: DEAL CATEGORY STRIP
   Horizontal scroller of category icons, showing only the
   categories that currently have at least one live deal.
   Scroll-snap gives native touch/swipe; the arrows are for
   mouse users and disable themselves at either end.
   ========================================================= */
.lb-cat-strip-wrap {
    padding: 36px 0 8px;
}
.lb-cat-strip {
    position: relative;
}
.lb-cat-track {
    display: flex;
    gap: 14px;
    overflow-x: auto;
    scroll-snap-type: x proximity;
    scroll-behavior: smooth;
    padding: 4px 2px 14px;
    /* Hide the scrollbar but keep the element scrollable. */
    scrollbar-width: none;
    -ms-overflow-style: none;
}
.lb-cat-track::-webkit-scrollbar { display: none; }

.lb-cat-item {
    flex: 0 0 auto;
    scroll-snap-align: start;
    width: 118px;
    min-height: 118px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 10px;
    padding: 14px 10px;
    /* Soft pastel tile; colour comes from cat_tile_style() per category. */
    background: linear-gradient(160deg, var(--cat-bg, #eef2f7) 0%, var(--cat-bg2, #e4e9f0) 100%);
    border: 1px solid rgba(15, 23, 42, .05);
    border-radius: 18px;
    text-decoration: none;
    color: #1f2937;
    transition: transform .18s ease, box-shadow .18s ease;
}
.lb-cat-item:hover {
    transform: translateY(-4px);
    box-shadow: 0 10px 24px rgba(15, 23, 42, .12);
    color: #1f2937;
}
.lb-cat-icon {
    width: 50px;
    height: 50px;
    border-radius: 14px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 26px;
    color: #334155;
    background: transparent;
    overflow: hidden;
    flex-shrink: 0;
}
/* Admin-uploaded category icon image fills the icon badge. */
.lb-cat-icon-img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    border-radius: 12px;
}
/* Currently-open category on a filtered listing: a clear ring. */
.lb-cat-item.active {
    box-shadow: 0 0 0 2px var(--lb-primary, #ff5722) inset, 0 6px 18px rgba(255, 87, 34, .15);
}
.lb-cat-item.active .lb-cat-name { font-weight: 800; }
.lb-cat-name {
    font-size: 13px;
    font-weight: 700;
    line-height: 1.2;
    text-align: center;
    color: #263142;
    /* Always allow (and reserve space for) two lines so the row height is even. */
    min-height: 2.4em;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    align-items: center;
}

.lb-cat-arrow {
    position: absolute;
    top: 38%;
    transform: translateY(-50%);
    z-index: 3;
    width: 36px;
    height: 36px;
    border-radius: 50%;
    border: 1px solid var(--lb-border, #e2e8f0);
    background: #fff;
    color: var(--lb-secondary, #0f172a);
    box-shadow: 0 4px 12px rgba(15, 23, 42, .10);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: opacity .18s ease;
}
.lb-cat-prev { left: -8px; }
.lb-cat-next { right: -8px; }
.lb-cat-arrow.is-disabled {
    opacity: 0;
    pointer-events: none;
}
/* Arrows are pointless on touch devices, which swipe instead. */
@media (max-width: 767.98px) {
    .lb-cat-arrow { display: none; }
    .lb-cat-item  { width: 108px; min-height: 110px; }
}

/* Lettered-circle fallback used where a business logo would normally sit. */
.lb-biz-logo-initial {
    display: inline-flex;
    padding: 0;
    background: none;
    border: none;
}


/* ==========================================================================
   Deals without a photo
   --------------------------------------------------------------------------
   Deal images used to be mandatory, so every card rendered a bare <img> and a
   deal saved without one produced a browser broken-image icon. Photos are now
   optional, so DealEngine::imageHtml() emits this placeholder instead: a tinted
   panel carrying the discount (or the deal's initial) plus a tag glyph.

   The hue comes from a hash of the deal title, so two adjacent cards without
   photos do not look like a rendering fault, and the same deal keeps the same
   colour on every page.
   ========================================================================== */
.lb-deal-ph {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  /* Soft near-white panel, matching the branded no-image tile design. */
  background: linear-gradient(135deg, #f7f8fa 0%, #eef1f5 100%);
  border-bottom: 1px solid rgba(0, 0, 0, .05);
}

/* Faint tag glyph in the corner, so the tile reads as a deal rather than a
   missing image. Deep red matches the branded no-image tile design. */
.lb-deal-ph {
  --lb-deal-ph-ink: #8f1d1d;
}
.lb-deal-ph-icon {
  position: absolute;
  right: -8px;
  bottom: -14px;
  font-size: 76px;
  line-height: 1;
  color: var(--lb-deal-ph-ink);
  opacity: .10;
  pointer-events: none;
}

.lb-deal-ph-body {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  line-height: 1.15;
  width: 100%;
  padding: 12px 14px;
}

/* Company Name — the prominent line, in a deep red. */
.lb-deal-ph-company {
  font-size: clamp(20px, 4.4vw, 30px);
  font-weight: 800;
  letter-spacing: -.5px;
  color: var(--lb-deal-ph-ink);
  text-transform: uppercase;
  word-break: break-word;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Deal Title — smaller, muted, spaced, beneath the company name. */
.lb-deal-ph-deal {
  margin-top: 8px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: #9aa3af;
  word-break: break-word;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* The business-profile and business-panel cards use .deal-img, which is a
   different box; keep the placeholder honouring whatever height it sets. */
.deal-img.lb-deal-ph { min-height: 120px; }

/* Coupon-view thumbnail. A fixed 120x90 box used by user/coupon-view.php.
   Sized the same whether it is an <img> (deal/subcategory/category image) or
   the generated .lb-deal-ph branded tile, so the fallback never distorts the
   layout. */
.lb-coupon-view-img {
  width: 120px;
  height: 90px;
  object-fit: cover;
  border-radius: 8px;
  flex-shrink: 0;
}
.lb-coupon-view-img.lb-deal-ph { min-height: 0; }
.lb-coupon-view-img.lb-deal-ph .lb-deal-ph-company { font-size: 13px; letter-spacing: 0; }
.lb-coupon-view-img.lb-deal-ph .lb-deal-ph-deal    { display: none; }
.lb-coupon-view-img.lb-deal-ph .lb-deal-ph-icon    { font-size: 40px; }

@media (max-width: 575.98px) {
  .lb-deal-ph-icon    { font-size: 60px; }
  .lb-deal-ph-company { font-size: clamp(16px, 6vw, 22px); }
  .lb-deal-ph-deal    { font-size: 11px; letter-spacing: 1.5px; }
}

/* ==========================================================================
   Reward Deals highlight
   --------------------------------------------------------------------------
   Reward Deals sort ahead of No-Rewards deals. Position alone is too subtle to
   notice on a grid, so they also carry a warm ring and a corner flash. Applied
   via DealEngine::cardClass(), which returns nothing when the admin turns
   `reward_deals_first` off — so switching the feature off removes the visual
   treatment too, rather than leaving a highlight that no longer means anything.
   ========================================================================== */
.lb-deal-card.lb-deal-reward {
  border-color: rgba(255, 87, 34, .38);
  box-shadow: 0 2px 10px rgba(255, 87, 34, .10);
}

.lb-deal-card.lb-deal-reward:hover {
  border-color: rgba(255, 87, 34, .6);
  box-shadow: 0 8px 22px rgba(255, 87, 34, .18);
}

/* Thin accent bar along the top edge of a reward deal card. */
.lb-deal-card.lb-deal-reward::after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  background: linear-gradient(90deg, #ff5722, #ff9800);
  z-index: 2;
  pointer-events: none;
}


/* Deal detail hero when no photo was uploaded — same generated tile as the
   listings, but at hero proportions instead of a card thumbnail. */
.lb-deal-hero-ph {
  aspect-ratio: 16 / 10;
  min-height: 220px;
}
.lb-deal-hero-ph .lb-deal-ph-icon    { font-size: 160px; right: -20px; bottom: -30px; }
.lb-deal-hero-ph .lb-deal-ph-company { font-size: clamp(34px, 6vw, 56px); }
.lb-deal-hero-ph .lb-deal-ph-deal    { font-size: 15px; letter-spacing: 4px; margin-top: 12px; }

@media (max-width: 575.98px) {
  .lb-deal-hero-ph .lb-deal-ph-icon    { font-size: 110px; }
  .lb-deal-hero-ph .lb-deal-ph-company { font-size: clamp(26px, 8vw, 40px); }
}

/* ==========================================================================
   Ticket-style deal card (.lb-tkt) — home page Top Deals / Deals / Recently
   viewed. Rendered by DealEngine::ticketCardHtml().

   Structure:
     a.lb-tkt            link + drop shadow (filter, so it follows the cut shape)
       .lb-tkt-shape     mask: scalloped top & bottom edges
         .lb-tkt-media   paper + inset photo; mask: upper half of side notches
         .lb-tkt-body    paper + dashed frame; mask: lower half of side notches

   The shape is cut with CSS masks instead of being painted in the section's
   background colour, so the card looks right on any background.
   ========================================================================== */
.lb-tkt {
  --tkt-paper:  #f5f3ee;
  --tkt-pad:    12px;   /* inset of photo / frame from the card edge */
  --tkt-notch:  11px;   /* side notch radius */
  --tkt-scal-r: 5px;    /* scallop radius on top/bottom edges */
  --tkt-scal-p: 16px;   /* scallop spacing */
  --tkt-cta:    #e4580d;
  --tkt-cta-hover: #c94a08;

  display: flex;
  flex: 1 1 auto;
  width: 100%;
  height: 100%;
  color: inherit;
  text-decoration: none;
  /* A hairline outline + soft shadow that hug the cut-out shape. */
  filter: drop-shadow(0 0 1px rgba(15, 23, 42, .28)) drop-shadow(0 6px 12px rgba(15, 23, 42, .08));
  transition: transform var(--lb-fast), filter var(--lb-fast);
}
.lb-tkt:hover {
  color: inherit;
  transform: translateY(-3px);
  filter: drop-shadow(0 0 1px rgba(15, 23, 42, .32)) drop-shadow(0 12px 20px rgba(15, 23, 42, .14));
}
.lb-tkt:focus-visible {
  outline: 3px solid var(--tkt-cta);
  outline-offset: 4px;
  border-radius: 6px;
}

/* Scalloped (perforated) top and bottom edges. Two half-height layers, each
   with a row of circular bites, overlap by 2% so there is no seam. */
.lb-tkt-shape {
  display: flex;
  flex-direction: column;
  width: 100%;
  -webkit-mask:
    radial-gradient(circle at 50% 0,    #0000 var(--tkt-scal-r), #000 calc(var(--tkt-scal-r) + .5px)) top    / var(--tkt-scal-p) 51% repeat-x,
    radial-gradient(circle at 50% 100%, #0000 var(--tkt-scal-r), #000 calc(var(--tkt-scal-r) + .5px)) bottom / var(--tkt-scal-p) 51% repeat-x;
          mask:
    radial-gradient(circle at 50% 0,    #0000 var(--tkt-scal-r), #000 calc(var(--tkt-scal-r) + .5px)) top    / var(--tkt-scal-p) 51% repeat-x,
    radial-gradient(circle at 50% 100%, #0000 var(--tkt-scal-r), #000 calc(var(--tkt-scal-r) + .5px)) bottom / var(--tkt-scal-p) 51% repeat-x;
}

/* ---- Photo half ---- */
.lb-tkt-media {
  position: relative;
  display: block;
  background: var(--tkt-paper);
  padding: var(--tkt-pad) var(--tkt-pad) calc(var(--tkt-pad) + 8px);
  /* Upper half of the two side notches (circle centred on the bottom edge). */
  -webkit-mask:
    radial-gradient(circle at 0 100%,    #0000 var(--tkt-notch), #000 calc(var(--tkt-notch) + .5px)) left  / 51% 100% no-repeat,
    radial-gradient(circle at 100% 100%, #0000 var(--tkt-notch), #000 calc(var(--tkt-notch) + .5px)) right / 51% 100% no-repeat;
          mask:
    radial-gradient(circle at 0 100%,    #0000 var(--tkt-notch), #000 calc(var(--tkt-notch) + .5px)) left  / 51% 100% no-repeat,
    radial-gradient(circle at 100% 100%, #0000 var(--tkt-notch), #000 calc(var(--tkt-notch) + .5px)) right / 51% 100% no-repeat;
}
.lb-tkt-img {
  display: block;
  width: 100%;
  aspect-ratio: 8 / 7;
  object-fit: cover;
  border-radius: 10px;
  background: #e9e6df;
}
/* No-photo fallback tile (DealEngine::imageHtml) inside the ticket. It must stay
   a centred flex box (the generic .lb-tkt-img rule above sets display:block),
   and gets top padding so the Rewards pill never sits on the company name. */
.lb-tkt-img.lb-deal-ph {
  display: flex;
  border-bottom: 0;
  padding-top: 34px;
  background: linear-gradient(135deg, #fbfaf7 0%, #ece8df 100%);
}
.lb-tkt .lb-deal-ph-company { font-size: clamp(15px, 3.4vw, 22px); }
.lb-tkt .lb-deal-ph-deal    { font-size: 10px; letter-spacing: 1.2px; }

/* Rewards / No Rewards pill over the photo. */
.lb-tkt-badge {
  position: absolute;
  top: calc(var(--tkt-pad) + 10px);
  left: calc(var(--tkt-pad) + 10px);
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 14px 4px 5px;
  border-radius: 999px;
  font-size: 14px;
  font-weight: 700;
  line-height: 1.2;
  white-space: nowrap;
  box-shadow: 0 2px 6px rgba(0, 0, 0, .25), inset 0 1px 0 rgba(255, 255, 255, .55);
}
.lb-tkt-coin {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  font-size: 12px;
  flex-shrink: 0;
}
.lb-tkt-badge.is-reward {
  color: #5b3a00;
  background: linear-gradient(180deg, #ffe680 0%, #fcc419 55%, #f0a500 100%);
  border: 1px solid #c98a00;
}
.lb-tkt-badge.is-reward .lb-tkt-coin {
  color: #7a4d00;
  background: radial-gradient(circle at 35% 30%, #fff3b0 0%, #f7c331 55%, #d99400 100%);
  border: 1px solid #b07800;
}
.lb-tkt-badge.is-plain {
  color: #334155;
  background: linear-gradient(180deg, #ffffff 0%, #e2e8f0 60%, #cbd5e1 100%);
  border: 1px solid #94a3b8;
}
.lb-tkt-badge.is-plain .lb-tkt-coin {
  color: #475569;
  background: radial-gradient(circle at 35% 30%, #ffffff 0%, #e2e8f0 60%, #b6c2d1 100%);
  border: 1px solid #94a3b8;
}

/* ---- Text half ---- */
.lb-tkt-body {
  position: relative;
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  background: var(--tkt-paper);
  padding: 16px calc(var(--tkt-pad) + 12px) calc(var(--tkt-pad) + 12px);
  /* Lower half of the side notches (circle centred on the top edge). */
  -webkit-mask:
    radial-gradient(circle at 0 0,    #0000 var(--tkt-notch), #000 calc(var(--tkt-notch) + .5px)) left  / 51% 100% no-repeat,
    radial-gradient(circle at 100% 0, #0000 var(--tkt-notch), #000 calc(var(--tkt-notch) + .5px)) right / 51% 100% no-repeat;
          mask:
    radial-gradient(circle at 0 0,    #0000 var(--tkt-notch), #000 calc(var(--tkt-notch) + .5px)) left  / 51% 100% no-repeat,
    radial-gradient(circle at 100% 0, #0000 var(--tkt-notch), #000 calc(var(--tkt-notch) + .5px)) right / 51% 100% no-repeat;
}
/* Dashed perforation frame: its top line runs notch-to-notch. */
.lb-tkt-body::before {
  content: "";
  position: absolute;
  top: 0;
  left: var(--tkt-notch);
  right: var(--tkt-notch);
  bottom: var(--tkt-pad);
  border: 1.5px dashed rgba(51, 65, 85, .55);
  border-radius: 0 0 10px 10px;
  pointer-events: none;
}
.lb-tkt-biz {
  position: relative;
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  margin-bottom: 8px;
}
.lb-tkt-avatar {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  object-fit: cover;
  flex-shrink: 0;
}
.lb-tkt-biz-name {
  font-size: 15px;
  color: #64748b;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.lb-tkt-title {
  position: relative;
  font-size: 18px;
  font-weight: 800;
  line-height: 1.25;
  color: #111827;
  min-height: 2.5em;   /* two lines, so a row of cards stays aligned */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  word-break: break-word;
}
/* The CTA is pushed to the bottom so every card in a row lines up. */
.lb-tkt-title { margin-bottom: 18px; }   /* minimum gap above the CTA */
.lb-tkt-btn {
  position: relative;
  display: block;
  margin-top: auto;
  padding: 11px 10px;
  border-radius: 8px;
  background: var(--tkt-cta);
  color: #fff;
  font-size: 16px;
  font-weight: 600;
  text-align: center;
  box-shadow: 0 2px 0 rgba(0, 0, 0, .08);
  transition: background var(--lb-fast);
}
.lb-tkt:hover .lb-tkt-btn { background: var(--tkt-cta-hover); }

/* Grid cells holding a ticket stretch it to the row height. */
.lb-tkt-col { display: flex; }

/* ---- Phones: two cards per row, so everything scales down ---- */
@media (max-width: 575.98px) {
  .lb-tkt {
    --tkt-pad:    7px;
    --tkt-notch:  8px;
    --tkt-scal-r: 3.5px;
    --tkt-scal-p: 11px;
  }
  .lb-tkt-media { padding-bottom: calc(var(--tkt-pad) + 6px); }
  .lb-tkt-img   { border-radius: 7px; }
  .lb-tkt-img.lb-deal-ph { padding-top: 24px; }
  .lb-tkt-badge {
    top: calc(var(--tkt-pad) + 6px);
    left: calc(var(--tkt-pad) + 6px);
    gap: 4px;
    padding: 2px 9px 2px 3px;
    font-size: 11px;
  }
  .lb-tkt-coin  { width: 16px; height: 16px; font-size: 9px; }
  .lb-tkt-body  { padding: 10px calc(var(--tkt-pad) + 8px) calc(var(--tkt-pad) + 8px); }
  .lb-tkt-body::before { border-radius: 0 0 7px 7px; }
  .lb-tkt-biz   { gap: 6px; margin-bottom: 6px; }
  .lb-tkt-avatar { width: 20px !important; height: 20px !important; font-size: 11px !important; }
  .lb-tkt-biz-name { font-size: 12px; }
  .lb-tkt-title { font-size: 14px; margin-bottom: 12px; }
  .lb-tkt-btn   { font-size: 13px; padding: 8px 6px; border-radius: 6px; }
}

/* ==========================================================================
   Featured business card (.lb-fbiz-card) — home page "Featured Businesses".
   Cover photo, overlapping round logo, name + verified tick, category,
   star rating + reviews, location, a Call / WhatsApp / Directions row, and a
   full-width dark-green "Visit Shop" button. Multiple action links, so the
   card is a <div> (not one big <a>) to avoid nested anchors.
   ========================================================================== */
.lb-fbiz-card {
  --fbiz-ink:   #0f5c4d;   /* deep teal-green for the Visit Shop button */
  --fbiz-ink-h: #0b4a3e;
  display: flex;
  flex-direction: column;
  height: 100%;
  background: #fff;
  border: 1px solid #e6e9ef;
  border-radius: 16px;
  overflow: hidden;
  box-shadow: 0 2px 10px rgba(15, 23, 42, .06);
  transition: transform var(--lb-med), box-shadow var(--lb-med), border-color var(--lb-med);
}
.lb-fbiz-card:hover {
  transform: translateY(-5px);
  box-shadow: 0 14px 30px rgba(15, 23, 42, .14);
  border-color: #d9dee7;
}

/* Cover with the logo overlapping its top-left corner. */
.lb-fbiz-covwrap { position: relative; }
.lb-fbiz-coverlink { display: block; text-decoration: none; }
.lb-fbiz-cover {
  display: block;
  aspect-ratio: 16 / 9;
  background: linear-gradient(135deg, #1e293b 0%, #475569 100%) center/cover no-repeat;
}
.lb-fbiz-logolink {
  position: absolute;
  left: 12px;
  bottom: -26px;
  z-index: 2;
  line-height: 0;
  text-decoration: none;
}
.lb-fbiz-logo {
  width: 56px;
  height: 56px;
  border-radius: 50%;
  border: 3px solid #fff;
  object-fit: cover;
  background: #fff;
  box-shadow: 0 2px 8px rgba(15, 23, 42, .18);
  display: block;
}
.lb-fbiz-logo-initial { display: inline-flex; padding: 0; }
.lb-fbiz-logo-initial .lb-avatar-initial {
  width: 50px !important;
  height: 50px !important;
  border-radius: 50% !important;
}

.lb-fbiz-body {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  padding: 34px 16px 16px;   /* top room for the overlapping logo */
}
.lb-fbiz-name {
  display: block;
  font-weight: 700;
  font-size: 18px;
  line-height: 1.2;
  color: #0f172a;
  text-decoration: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.lb-fbiz-name:hover { color: var(--lb-primary); }
.lb-fbiz-verified {
  color: #16a34a;
  font-size: 14px;
  margin-left: 4px;
  vertical-align: baseline;
}
.lb-fbiz-cat {
  font-size: 13px;
  color: #64748b;
  margin-top: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.lb-fbiz-rating {
  font-size: 14px;
  color: #0f172a;
  margin-top: 10px;
  margin-bottom: 6px;
}
.lb-fbiz-rating .bi-star-fill { color: var(--lb-accent); }
.lb-fbiz-rating .text-muted { font-weight: 400; }

.lb-fbiz-loc {
  font-size: 13px;
  color: #475569;
  margin-bottom: 12px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.lb-fbiz-loc .bi { color: #ef4444; margin-right: 2px; }

/* Call / WhatsApp / Directions row. */
.lb-fbiz-actions {
  display: flex;
  gap: 6px;
  padding-top: 12px;
  margin-bottom: 14px;
  border-top: 1px solid #eef1f5;
}
.lb-fbiz-act {
  flex: 1 1 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
  min-width: 0;
  padding: 4px 2px;
  font-size: 13px;
  font-weight: 500;
  color: #334155;
  text-decoration: none;
  border-radius: 6px;
  transition: color var(--lb-fast), background var(--lb-fast);
}
.lb-fbiz-act span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.lb-fbiz-act .bi { color: var(--fbiz-ink); font-size: 15px; }
.lb-fbiz-act:hover { color: var(--fbiz-ink); background: #f1f5f4; }

/* Full-width dark-green CTA. */
.lb-fbiz-visit {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin-top: auto;
  padding: 11px 12px;
  background: var(--fbiz-ink);
  color: #fff;
  font-weight: 600;
  font-size: 15px;
  text-decoration: none;
  border-radius: 10px;
  transition: background var(--lb-fast);
}
.lb-fbiz-visit:hover { background: var(--fbiz-ink-h); color: #fff; }

/* Phones: two cards per row, so shrink the logo, labels and paddings. */
@media (max-width: 575.98px) {
  .lb-fbiz-body { padding: 30px 11px 12px; }
  .lb-fbiz-logolink { left: 10px; bottom: -22px; }
  .lb-fbiz-logo { width: 48px; height: 48px; }
  .lb-fbiz-logo-initial .lb-avatar-initial { width: 42px !important; height: 42px !important; }
  .lb-fbiz-name { font-size: 15px; }
  .lb-fbiz-cat, .lb-fbiz-rating, .lb-fbiz-loc { font-size: 12px; }
  .lb-fbiz-actions { gap: 2px; }
  .lb-fbiz-act { font-size: 11px; padding: 4px 1px; gap: 3px; }
  .lb-fbiz-act .bi { font-size: 13px; }
  .lb-fbiz-visit { font-size: 13px; padding: 9px 10px; }
}

/* ==========================================================================
   Auto-hide navigation bars on scroll (mobile only).
   app.js toggles body.lb-nav-hidden on scroll-down and removes it on scroll-up.
   The top brand strip + the sticky mobile search/category strip slide up out of
   view; the floating bottom nav slides down out of view. Desktop is untouched
   (the rules live inside the mobile media query), and users who prefer reduced
   motion get no transition.
   ========================================================================== */
@media (max-width: 991.98px) {
  .lb-public-nav,
  .lb-mobile-topbar {
    transition: transform .28s ease;
    will-change: transform;
  }
  .lb-bottom-nav {
    transition: transform .28s ease;
    will-change: transform;
  }
  /* Scrolled down: tuck the bars away. The two sticky top bars must move as
     one unit — the search strip is pinned at top:64px (below the brand bar),
     so translating it by only its own -100% leaves it partly on screen. We
     push the brand bar fully up by its own height, and the strip up by its own
     height PLUS that 64px offset, so both clear the viewport together and the
     strip is shown in full when the bars return. */
  body.lb-nav-hidden .lb-public-nav {
    transform: translateY(-100%);
  }
  body.lb-nav-hidden .lb-mobile-topbar {
    transform: translateY(calc(-100% - 64px));
  }
  body.lb-nav-hidden .lb-bottom-nav {
    /* Clear the 10px float gap + shadow below the viewport. */
    transform: translateY(calc(100% + 24px));
  }
}
@media (prefers-reduced-motion: reduce) {
  .lb-public-nav,
  .lb-mobile-topbar,
  .lb-bottom-nav { transition: none; }
}

/* ==========================================================================
   Mobile filter drawer + inline "Filters" button.
   Listing pages (deals / search / free-ads / vaayu-setu) wrap their filter
   block in a Bootstrap responsive offcanvas (.offcanvas-md / .offcanvas-lg):
   it slides in from the right on mobile and renders inline on desktop. A plain
   button at the TOP of the page (filter_toggle_button()) opens it — it is in
   normal flow, so it does NOT float and does NOT hide on scroll.
   ========================================================================== */

/* Breadcrumb row with the "Filters" button pinned to the right. The button is
   in normal flow (not floating / not auto-hiding) and is auto-width. The flex
   layout is declared here too (not only via Bootstrap utility classes) so the
   button stays on the right regardless. */
.lb-filter-bar {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: nowrap;
    margin-bottom: 12px;
}
.lb-filter-bar-left {
    flex: 1 1 auto;
    min-width: 0;                               /* let a long breadcrumb truncate */
}
.lb-filter-bar-left .breadcrumb { flex-wrap: wrap; margin-bottom: 0; }
.lb-filter-top-btn {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 8px 16px;
    border-radius: 8px;
    font-weight: 600;
    font-size: 14px;
    white-space: nowrap;
}
.lb-filter-top-btn i { font-size: 15px; }

/* When the filter block is an inline (static) offcanvas on desktop, strip the
   drawer chrome so it looks like a normal in-page block. Bootstrap's
   responsive offcanvas already removes the fixed positioning above the
   breakpoint; we just hide the drawer-only header there. */
.lb-filter-drawer .offcanvas-header { border-bottom: 1px solid var(--lb-border); }
@media (min-width: 768px) {
    .offcanvas-md.lb-filter-drawer .offcanvas-header { display: none; }
    .offcanvas-md.lb-filter-drawer .offcanvas-body   { padding: 0; }
}
@media (min-width: 992px) {
    .offcanvas-lg.lb-filter-drawer .offcanvas-header { display: none; }
    .offcanvas-lg.lb-filter-drawer .offcanvas-body   { padding: 0; }
}
/* The drawer panel on mobile: comfortable width + scrollable body. */
.lb-filter-drawer.offcanvas-md,
.lb-filter-drawer.offcanvas-lg {
    --bs-offcanvas-width: min(88vw, 360px);
}

/* ==========================================================================
   Star-rating radio input (business profile "write a review" form).
   Radios are rendered high-to-low (5..1) so the CSS sibling selector can light
   up the hovered/checked star and all those to its left.
   ========================================================================== */
.lb-star-input {
    display: inline-flex;
    flex-direction: row-reverse;
    gap: 2px;
    font-size: 26px;
    line-height: 1;
}
.lb-star-input input { position: absolute; opacity: 0; width: 0; height: 0; }
.lb-star-input label {
    color: #cbd5e1;
    cursor: pointer;
    transition: color .12s ease;
    margin: 0;
}
.lb-star-input label:hover,
.lb-star-input label:hover ~ label,
.lb-star-input input:checked ~ label {
    color: var(--lb-accent, #facc15);
}
