/* Shared design tokens + layout used by every page: the "app shell",
   big-button drill-down menus, and the listing-card list view all build on this. */

:root {
    --primary-blue: #005f73;
    /* The front page's header/nav blue - moved here (from css/home.css) so
       every page resolves the same value. Previously only home.css defined
       this, so account.css's `var(--header-blue, var(--primary-blue))`
       silently fell back to --primary-blue on /help/, /login/, /account/,
       rendering a visibly different blue there than on the home page. */
    --header-blue: #1E6091;
    --accent-blue: #94d2bd;
    --bg-beige: #eeeee0;
    --card-white: rgba(255, 255, 255, 0.3);
    --text-dark: #333333;
    --text-light: #666666;
    --danger: #ff6b6b;
    --shadow: 0 4px 6px rgba(0,0,0,0.1);
    --radius: 12px;
}

* {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

body {
    min-height: 100vh;
    display: flex;
    justify-content: center;
    align-items: flex-start;
    padding: 0;
    max-width: 800px;
    margin-left: auto;
    margin-right: auto; 
}

/* Mobile-first app container */
.app-container {
    background: rgba(255,255,255,0.2);
    width: 100%;
    /* Never shorter than the viewport itself - a page with little content
       (an empty bundle, a short account page) used to stop at 800px flat,
       which left the sticky .bottom-nav sitting wherever that short
       content ended instead of the actual screen bottom on any viewport
       taller than 800px, with the body's own background gradient showing
       in the gap below it. max() keeps the old 800px floor for short/
       desktop-window viewports while guaranteeing at least a full 100dvh
       everywhere else. The plain 800px line is a fallback for browsers
       without dvh support - min-height: max(..., 100dvh) is simply
       invalid (and ignored) there, not silently 0. */
    min-height: 800px;
    min-height: max(800px, 100dvh);
    overflow: hidden;
    position: relative;
    display: flex;
    flex-direction: column;
    transition-duration: 0.75s;
}

section#featured-section {
    border-radius: var(--radius);
    background: white;
    padding: 15px;    
}

/* Header */
header {
    padding: 20px;
    text-align: center;
    background: var(--card-white);
}

.back-link {
    display: inline-block;
    color: var(--primary-blue);
    text-decoration: none;
    font-weight: 600;
    font-size: 0.9rem;
    margin-bottom: 10px;
}

h1 {
    color: var(--primary-blue);
    font-size: 1.8rem;
    font-weight: 800;
    letter-spacing: 1px;
    margin-bottom: 5px;
    text-transform: uppercase;
}

h1 span {
    color: var(--primary-blue);
}

.subtitle {
    color: var(--text-light);
    font-size: 0.9rem;
    margin-bottom: 15px;
}

/* Search Bar */
.search-container {
    padding: 0 20px 20px 20px;
}

.search-input {
    width: 100%;
    padding: 12px 15px;
    border-radius: 25px;
    border: 1px solid #ddd;
    background: #f9f9f9;
    font-size: 1rem;
    color: var(--text-dark);
    outline: none;
    transition: border-color 0.3s;
}

.search-input:focus {
    border-color: var(--primary-blue);
    background: #fff;
}

/* The pre-tap stand-in (js/home.js's header comment on why this exists) -
   same box, but its text reads as a placeholder (muted, not real input
   text) since a div has no native placeholder of its own. */
.search-input-fake {
    color: var(--text-light);
    cursor: text;
    user-select: none;
}

/* !important: must always win over a component's own display rule
   regardless of cascade order - see css/listing.css's .hidden for why. */
.hidden {
    display: none !important;
}

/* Live search results (js/search.js), shown in place of the nav grid /
   content section while the search box has text in it. */
.search-results {
    padding: 0 20px 20px 20px;
}

.search-group-label {
    font-size: 0.75rem;
    color: var(--text-light);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    font-weight: 700;
    margin: 12px 0 8px 0;
}
.search-group-label:first-child {
    margin-top: 0;
}

.search-result {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: 12px 14px;
    margin-bottom: 8px;
    border-radius: var(--radius);
    text-decoration: none;
    box-shadow: 0 2px 4px rgba(0,0,0,0.05);
}

.search-result-category {
    background: var(--primary-blue);
    color: white;
}
.search-result-category .search-result-kind {
    color: rgba(255,255,255,0.8);
}

.search-result-entry {
    background: var(--card-white);
    color: var(--text-dark);
    border: 1px solid #eee;
}
.search-result-entry .search-result-kind {
    color: var(--text-light);
}

.search-result-title {
    font-weight: 600;
    font-size: 0.95rem;
}

.search-result-kind {
    font-size: 0.75rem;
    text-align: right;
    white-space: nowrap;
}

.search-empty {
    color: var(--text-light);
    font-size: 0.9rem;
    padding: 10px 0;
}

/* Big-button drill-down navigation grid, used on the home page and every
   category / sub-category menu so the drill-down pattern stays consistent. */
.nav-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 15px;
    padding: 0 20px 25px 20px;
}

.nav-item {
    background: var(--bg-beige);
    border-radius: var(--radius);
    padding: 20px 10px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-decoration: none;
    color: var(--primary-blue);
    transition: transform 0.2s, background-color 0.2s;
    box-shadow: 0 2px 4px rgba(0,0,0,0.05);
    aspect-ratio: 1 / 0.8;
}

.nav-item:active {
    transform: scale(0.98);
    background: #e0e0d0;
}

.nav-icon {
    font-size: 2rem;
    margin-bottom: 10px;
    display: block;
}

.nav-label {
    font-weight: 600;
    font-size: 0.9rem;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    text-align: center;
}

/* Horizontal-scroll edge-fade affordance - shared by every horizontal
   scroller site-wide (category pills, carousels, filter chips: see
   js/hscroll.js). Layered on top of each container's own "peek" (a
   partially-cut-off card/pill is already a real, industry-standard scroll
   cue - scroll-snap + fixed card widths, unchanged here) as a second,
   more discoverable one: whichever edge still has scrollable content past
   it fades toward transparent via mask-image, and disappears once that
   edge is actually reached (.at-start/.at-end, toggled by hscroll.js). */
.h-scroll {
    mask-image: linear-gradient(to right, transparent 0, black 24px, black calc(100% - 24px), transparent 100%);
    -webkit-mask-image: linear-gradient(to right, transparent 0, black 24px, black calc(100% - 24px), transparent 100%);
}
.h-scroll.at-start {
    mask-image: linear-gradient(to right, black calc(100% - 24px), transparent 100%);
    -webkit-mask-image: linear-gradient(to right, black calc(100% - 24px), transparent 100%);
}
.h-scroll.at-end {
    mask-image: linear-gradient(to right, transparent 0, black 24px);
    -webkit-mask-image: linear-gradient(to right, transparent 0, black 24px);
}
.h-scroll.at-start.at-end {
    mask-image: none;
    -webkit-mask-image: none;
}

/* Location diagnostics indicator (js/locationPing.js) - only ever created
   while the current admin has switched diagnostics logging on for
   themselves (see server/data/userStore.js's locationDiagnosticsEnabled);
   invisible to everyone else. `position: fixed` against the viewport (not
   .app-container, unlike .bottom-nav below) since it needs to float above
   whatever page content is on screen regardless of scroll, and top-right
   specifically to stay clear of the bottom nav bar. Solid while
   diagnostics is on; .pulsing briefly outlines it in an expanding ring
   each time a ping actually reaches the backend (js/locationPing.js's
   pulseIndicator), so it doubles as "this is working" feedback rather
   than just a static "this is on" light. */
.locdiag-indicator {
    position: fixed;
    top: 10px;
    right: 10px;
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background: #2a7d2a;
    box-shadow: 0 0 0 2px rgba(255,255,255,0.85);
    z-index: 500;
    cursor: pointer;
}
.locdiag-indicator::after {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: 50%;
    background: #2a7d2a;
    opacity: 0;
}
.locdiag-indicator.pulsing::after {
    animation: locdiag-pulse 1s ease-out;
}
@keyframes locdiag-pulse {
    0% { opacity: 0.6; transform: scale(1); }
    100% { opacity: 0; transform: scale(3); }
}

/* Bottom tab bar (server/views/partials/bottom-nav.ejs) - a child of
   .app-container (not the viewport), so `sticky` keeps it pinned to the
   bottom of the screen while scrolling *without* ever floating past the
   card's own bottom edge the way `fixed` did - .app-container above doesn't
   stretch to fill the viewport, so a `fixed` element anchored past it left
   a gap of background gradient showing between the card and the nav. Was
   home-page-only CSS (css/home.css) until the nav itself became shared
   across pages. */
.bottom-nav {
    position: sticky;
    bottom: 0;
    background: var(--header-blue);
    display: flex;
    justify-content: space-around;
    padding: 8px 0 calc(8px + env(safe-area-inset-bottom, 0px)) 0;
    box-shadow: 0 -2px 10px rgba(0,0,0,0.15);
    z-index: 20;
    border: none;
    /* The other half of the .app-container min-height fix above: as a flex
       child with nothing else claiming the extra space, this pulls all of
       it into its own top margin - pinning the nav to the bottom of a
       short page (screen bottom) automatically, while `position: sticky`
       above keeps it pinned to the screen bottom while scrolling a page
       whose real content runs taller than the screen. Together: never
       higher than the screen's bottom edge, whichever is lower of the two
       actually applies. */
    margin-top: auto;
}

.bottom-nav-item {
    background: none;
    border: none;
    color: rgba(255,255,255,0.7);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    text-decoration: none;
    font: inherit;
    cursor: pointer;
    padding: 4px 12px;
}

.bottom-nav-item.active { color: white; }
.bottom-nav-icon { font-size: 1.2rem; }
.bottom-nav-label { font-size: 0.65rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; }

/* Content Section (home page widgets) */
.content-section {
    background: #f8f9fa;
    border-top-left-radius: 25px;
    border-top-right-radius: 25px;
    padding: 25px 20px;
    flex-grow: 1;
    margin-top: auto;
}

.section-title {
    font-size: 0.85rem;
    color: var(--text-light);
    text-transform: uppercase;
    letter-spacing: 1px;
    margin-bottom: 15px;
    font-weight: 700;
}

.spacer {
    height: 20px;
}

/* Home page widgets */
.weather-widget {
    background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
    border-radius: var(--radius);
    padding: 20px;
    color: white;
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 20px;
    box-shadow: 0 4px 10px rgba(79, 172, 254, 0.4);
}

.weather-temp { font-size: 2.5rem; font-weight: 700; }
.weather-desc { font-size: 1.1rem; font-weight: 500; text-transform: capitalize; }
.weather-icon { font-size: 3rem; }

.event-widget {
    background: var(--card-white);
    border-radius: var(--radius);
    padding: 20px;
    box-shadow: var(--shadow);
    border-left: 5px solid var(--danger);
}

.event-label { font-size: 0.75rem; color: var(--danger); font-weight: 700; text-transform: uppercase; margin-bottom: 5px; display: block; }
.event-title { font-size: 1.2rem; color: var(--text-dark); font-weight: 700; margin-bottom: 5px; }
.event-status { font-size: 0.9rem; color: var(--text-light); display: flex; align-items: center; gap: 5px; }

.live-dot {
    height: 8px;
    width: 8px;
    background: var(--danger);
    border-radius: 50%;
    display: inline-block;
    animation: pulse 1.5s infinite;
}

@keyframes pulse { 0% { opacity: 1; } 50% { opacity: 0.4; } 100% { opacity: 1; } }

/* ---- Shared blue page header (partials/page-header.ejs) ---- */

/* The front page's blue block, reproduced for every non-themeable page
   (category, section, help, login, menu, account). The circular caret
   back button mirrors the venue/detail card's floating control so the
   two designs read as one. */
.page-header {
    background: var(--header-blue);
    color: white;
    padding: 18px 16px 16px;
    display: flex;
    align-items: flex-start;
    gap: 12px;
    border-radius: 0 0 16px 16px;
    margin-bottom: 15px;
}
.page-header-text { flex: 1;
    min-width: 0;
    align-self: center;
}

.page-header-title {
    color: white;
    font-size: 1.3rem;
    margin: 2px 0 2px;
    letter-spacing: 0.3px;
    align-self: center;
}
.page-header-subtitle {
    font-size: 0.85rem;
    opacity: 0.88;
    margin: 0;
    color: white;
}
.page-back-btn {
    flex: none;
    width: 38px;
    height: 38px;
    margin-top: 2px;
    border-radius: 50%;
    border: none;
    background: rgba(255, 255, 255, 0.22);
    color: white;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    text-decoration: none;
}
.page-back-btn:hover { background: rgba(255, 255, 255, 0.38); }

/* On a themeable hero (bundle pages) the same circle but dark, so it
   reads over any hero image/colour - same treatment as the detail
   card's floating button. */
.page-back-btn.on-hero {
    background: rgba(0, 0, 0, 0.55);
}

/* The sort-toggle now lives inside the blue header (listing/section
   pages) - its white pill buttons stay legible on the blue. */
.page-header .sort-toggle .sort-toggle-btn {
    background: rgba(255, 255, 255, 0.18);
    color: white;
    border-color: rgba(255, 255, 255, 0.35);
}
.page-header .sort-toggle .sort-toggle-btn.active {
    background: white;
    color: var(--header-blue);
}

/* ---- Bottom-nav "Me" button (js/me-menu.js) ---- */

/* .bottom-nav-item styles were written for anchors; the Me button is a
   real button. */
button.bottom-nav-item {
    background: none;
    border: none;
    cursor: pointer;
    font: inherit;
}
/* Subtle logged-in indicator: a small dot on the person icon once a
   session exists. */
#bottom-nav-me.logged-in .bottom-nav-icon {
    position: relative;
}
#bottom-nav-me.logged-in .bottom-nav-icon::after {
    content: '';
    position: absolute;
    right: -1px;
    bottom: 1px;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: #2ea44f;
    border: 1.5px solid #fff;
}

/* The Me modal. */
#me-dialog {
    border: none;
    border-radius: 16px;
    padding: 0;
    max-width: 300px;
    width: calc(100% - 48px);
    box-shadow: 0 8px 30px rgba(0, 0, 0, 0.25);
    /* Above the Leaflet map (panes 400, controls 800) - same rule as the
       inline edit modals. */
    z-index: 1100;
}
#me-dialog::backdrop { background: rgba(0, 0, 0, 0.35); }
#me-dialog .me-modal { padding: 14px; }
#me-dialog .me-modal-title {
    margin: 0 0 4px;
    font-size: 0.95rem;
    color: #445;
}
#me-dialog .me-modal-sub {
    margin: 0 0 8px;
    font-size: 0.8rem;
    color: #889;
}
.me-item {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 10px 8px;
    border: none;
    background: none;
    text-align: left;
    font: inherit;
    font-size: 0.95rem;
    color: #223;
    border-radius: 8px;
    cursor: pointer;
    text-decoration: none;
    box-sizing: border-box;
}
.me-item:hover { background: rgba(0, 95, 115, 0.08); }

/* Small legal/licensing note under upload forms (account + admin) */
.legal-note { font-size: 0.75rem; color: var(--text-light, #667); line-height: 1.5; margin: 8px 0 0; }
.legal-note a { color: var(--primary-blue, #1E6091); text-decoration: underline; }

/* Emoji thumbs for imageless listings (js/thumbEmoji.js) - big and
   centered on a neutral panel; here so the home page's favourite/nearby
   cards get it too (they don't load listing.css). */
.thumb-emoji {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100%;
    font-size: 3.5rem;
    line-height: 1;
    text-align: center;
}
.thumb-emoji-lg { font-size: 10rem; }
.thumbnail.no-photo, .favorite-card-thumb.no-photo, .nearby-card-thumb.no-photo {
    background: transparent;
}
.detail-thumbnail-plain {
    background: linear-gradient(135deg, #f4f7f9, #e9eef3);
}

/* Colour controls with the ✕ unset affordance (theme modal + admin
   theme editor) - white is a REAL colour; only the ✕ marks "unset". */
.theme-colour-wrap {
    position: relative;
    display: inline-block;
}
.theme-colour-wrap input[type="color"] { display: block; }
.theme-clear {
    position: absolute;
    top: -6px;
    right: -6px;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    border: none;
    background: #e2e8ef;
    color: #5b6b7c;
    font-size: 9px;
    line-height: 1;
    cursor: pointer;
    display: none;
    align-items: center;
    justify-content: center;
    padding: 0;
}
.theme-colour-wrap:hover .theme-clear { display: flex; }
input.theme-unset { opacity: 0.45; }
input.theme-unset + .theme-clear { display: flex; }


/* Fix for leaflet z-index issue (map appearing above everything else)
   Uses high specificity to override class from external css file */
.leaflet-pane.leaflet-map-pane {
    z-index: 10;
}
