:root {
    --hf-bg: #f7f7f9;
    --hf-panel: #ffffff;
    --hf-panel-alt: #f0f0f3;
    --hf-border: #e3e3e8;
    --hf-text: #17181c;
    --hf-text-muted: #6b6c75;
    --hf-accent: #4f6df5;
    --hf-accent-hover: #3d59e0;
    --hf-success: #1eae63;
    --hf-warning: #c8790a;
    --hf-danger: #e0473f;
    --hf-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
}

:root[data-theme="dark"] {
    --hf-bg: #121317;
    --hf-panel: #1a1b20;
    --hf-panel-alt: #202127;
    --hf-border: #2c2d34;
    --hf-text: #f2f2f4;
    --hf-text-muted: #93949e;
    --hf-accent: #6c8cff;
    --hf-accent-hover: #8aa2ff;
    --hf-success: #3ddc84;
    --hf-warning: #ffb547;
    --hf-danger: #ff6b6b;
    --hf-shadow: 0 8px 28px rgba(0, 0, 0, 0.45);
}

* { box-sizing: border-box; }

/* Shared outline icon sprite (public/assets/icons/icons.svg) — see the
   matching rule + comment in admin.css; same icons, same behavior, used
   on the frontend too (search, theme, basemap, list/map view). */
.hf-icon {
    width: 1em;
    height: 1em;
    display: inline-block;
    vertical-align: -0.125em;
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
}
.hf-icon-inline { margin-right: 4px; }


/* Browsers don't inherit the page font into form controls by default (each
   uses its own OS/browser UI font) — that's why the Google Font setting
   used to reach body text but not the search box, filter dropdowns, the
   Mekkah/Madinah tab buttons, the theme toggle, or the list/map switch. */
button, input, select, textarea { font-family: inherit; }

html, body {
    height: 100%;
    margin: 0;
}

body {
    font-family: var(--hf-font, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif);
    background: var(--hf-bg);
    color: var(--hf-text);
}

a { color: var(--hf-accent); }

.hf-app {
    display: flex;
    flex-direction: column;
    height: 100vh; /* fallback for browsers without dvh support */
    height: 100dvh;
}

.hf-header {
    display: flex;
    align-items: center;
    gap: 16px;
    padding: 12px 20px;
    background: var(--hf-panel);
    border-bottom: 1px solid var(--hf-border);
    flex-wrap: wrap;
}

.hf-header .hf-logo { font-weight: 800; font-size: 16px; white-space: nowrap; }
/* Caps whatever size logo file gets uploaded (Pengaturan Situs has no
   restriction on the source image's own pixel dimensions) so it can
   never force the header row wider than intended — width:auto keeps it
   proportional, it just can't exceed either bound. */
/* v1.26.0: height now comes from the admin setting (Pengaturan Situs ->
   Tinggi Logo, --hf-logo-h-desktop); width follows the image's own ratio.
   max-width:100% + object-fit:contain keep it from ever overflowing its
   container (it just shrinks instead of stretching/distorting). */
.hf-header .hf-logo img {
    height: var(--hf-logo-h-desktop, 28px);
    width: auto;
    max-width: 100%;
    object-fit: contain;
    object-position: left center;
    vertical-align: middle;
}
.hf-header-row1 { display: contents; }

.hf-tabs { display: flex; gap: 6px; background: var(--hf-panel-alt); border-radius: 999px; padding: 4px; }
.hf-tab {
    padding: 7px 16px;
    border-radius: 999px;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    color: var(--hf-text-muted);
    border: none;
    background: transparent;
}
.hf-tab.active { background: var(--hf-accent); color: #fff; }

.hf-search {
    flex: 1;
    min-width: 160px;
    padding: 8px 14px;
    border-radius: 999px;
    border: 1px solid var(--hf-border);
    background: var(--hf-panel-alt);
    color: var(--hf-text);
    font-size: 13px;
}

/* Desktop: this wrapper and the icon/close buttons inside it are purely a
   mobile concern (see the @media block) — display:contents keeps .hf-search
   and .hf-filters flowing as direct flex children of .hf-header exactly
   like before, and the two buttons stay hidden. */
.hf-searchbar-row { display: contents; }
.hf-search-icon-btn, .hf-search-close-btn { display: none; }

/* v1.33.2 (desktop): the search box gets a small (x) inside its right edge that
   clears the whole query in one click; it only shows while there is text.
   The wrapper takes over the flex sizing .hf-search had in the header. On the
   phone the wrapper is display:contents (see the @media block), the input
   behaves exactly as before and the existing close button does the clearing. */
.hf-search-wrap { position: relative; flex: 1; min-width: 160px; display: flex; align-items: center; }
.hf-search-wrap .hf-search { width: 100%; min-width: 0; padding-right: 34px; }
.hf-search-clear-btn {
    display: none;
    position: absolute;
    right: 7px; top: 50%;
    transform: translateY(-50%);
    width: 22px; height: 22px;
    align-items: center; justify-content: center;
    padding: 0;
    border: none; border-radius: 999px;
    background: transparent;
    color: var(--hf-text-muted);
    cursor: pointer;
}
.hf-search-clear-btn:hover { background: var(--hf-border); color: var(--hf-text); }
.hf-search-clear-btn .hf-icon { width: 14px; height: 14px; }
.hf-search-wrap.has-value .hf-search-clear-btn { display: flex; }

.hf-filters { display: flex; gap: 8px; align-items: center; }
.hf-filters select {
    padding: 7px 10px;
    border-radius: 8px;
    border: 1px solid var(--hf-border);
    background: var(--hf-panel-alt);
    color: var(--hf-text);
    font-size: 13px;
}

.hf-theme-toggle {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    border: 1px solid var(--hf-border);
    background: var(--hf-panel-alt);
    color: var(--hf-text);
    border-radius: 999px;
    width: 34px; height: 34px;
    cursor: pointer;
    font-size: 15px;
}

.hf-body { flex: 1; display: flex; overflow: hidden; }

.hf-sidebar {
    width: 380px;
    min-width: 320px;
    background: var(--hf-panel);
    border-right: 1px solid var(--hf-border);
    overflow-y: auto;
}

.hf-map { flex: 1; position: relative; }
#hf-map-el { width: 100%; height: 100%; }

.hf-basemap-toggle {
    position: absolute;
    top: 10px; right: 10px;
    z-index: 1000;
    border: 1px solid var(--hf-border);
    background: var(--hf-panel-alt);
    color: var(--hf-text);
    border-radius: 6px;
    padding: 6px 10px;
    font-size: 13px;
    cursor: pointer;
    box-shadow: 0 1px 4px rgba(0,0,0,.15);
}

.hf-list-count { padding: 12px 16px 4px; font-size: 12px; color: var(--hf-text-muted); }

.hf-card-item {
    display: flex;
    gap: 12px;
    padding: 12px 16px;
    border-bottom: 1px solid var(--hf-border);
    cursor: pointer;
}
.hf-card-item:hover, .hf-card-item.active { background: var(--hf-panel-alt); }

.hf-card-thumb {
    width: 64px; height: 64px;
    border-radius: 10px;
    object-fit: cover;
    background: var(--hf-panel-alt);
    flex-shrink: 0;
}

.hf-card-info { flex: 1; min-width: 0; }
.hf-card-name { font-weight: var(--hf-heading-weight, 700); font-size: var(--hf-heading-size, 14px); margin: 0 0 4px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.hf-card-meta { font-size: var(--hf-body-size, 12px); font-weight: var(--hf-body-weight, 400); color: var(--hf-text-muted); display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.hf-card-links { margin-top: 8px; display: flex; gap: 6px; flex-wrap: wrap; }

/* Same look as the popup's link buttons (.hf-popup-link-btn), scaled down
   to fit the card sidebar. Label width is FIXED (not content-based) so
   that when two buttons sit side by side, their right edges (the chevron)
   line up regardless of which label is shorter. Sized for "Traveloka" —
   the longest of the three platform labels used on this site — at 11px;
   a longer label elsewhere would ellipsize rather than push the button
   wider and break the alignment. */
.hf-card-link-btn {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 5px 7px;
    border-radius: 7px;
    background: var(--hf-panel-alt);
    border: 1px solid var(--hf-border);
    text-decoration: none;
    color: var(--hf-text);
    font-size: var(--hf-link-font-mobile, 11px);
    font-weight: 600;
    line-height: 1.2;
}
.hf-card-link-btn:hover { border-color: var(--hf-accent); }
.hf-card-link-btn img {
    width: 16px; height: 16px; border-radius: 4px; object-fit: cover; flex-shrink: 0;
}
.hf-card-link-btn .hf-popup-link-fallback {
    width: 16px; height: 16px; font-size: 10px;
}
.hf-card-link-btn .hf-ota-generic-icon { width: 16px; height: 16px; flex-shrink: 0; color: var(--hf-text-muted); }
.hf-card-link-btn .hf-ota-generic-icon svg { width: 100%; height: 100%; display: block; }
.hf-card-link-label {
    width: var(--hf-link-label-w-mobile, 62px); /* v1.33.11: was a fixed 46px; set in Pengaturan Situs */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.hf-card-link-btn .hf-popup-link-arrow { font-size: 13px; line-height: 1; }

/* v1.33.2: look of the card link buttons, set in Pengaturan Situs (Tombol Link di
   Kartu Daftar): label slot width and font size, separately for the phone (the
   base rules above, --hf-link-*-mobile) and for desktop (this block, --hf-link-*-desktop).
   The slot stays a FIXED width so the chevrons of buttons side by side stay aligned;
   a label longer than the slot is ellipsized. */
@media (min-width: 761px) {
    .hf-card-link-btn { font-size: var(--hf-link-font-desktop, 11px); }
    .hf-card-link-label { width: var(--hf-link-label-w-desktop, 58px); }
}

.hf-badge { display: inline-block; padding: 1px 8px; border-radius: 999px; font-size: var(--hf-tag-size, 11px); font-weight: var(--hf-tag-weight, 700); }
.hf-badge-easy { background: rgba(30, 174, 99, 0.15); color: var(--hf-success); }
.hf-badge-medium { background: rgba(200, 121, 10, 0.15); color: var(--hf-warning); }
.hf-badge-hard { background: rgba(224, 71, 63, 0.15); color: var(--hf-danger); }
.hf-badge-unclassified { background: rgba(107, 108, 117, 0.15); color: var(--hf-text-muted); }

/* Price-group tag: background + text color are set inline from the admin's
   price-group table (e.g. "< 1,5 juta / malam"). */
.hf-price-tag {
    display: inline-block;
    padding: 1px 8px;
    border-radius: 999px;
    font-size: var(--hf-tag-size, 11px);
    font-weight: var(--hf-tag-weight, 700);
    white-space: nowrap;
}

.hf-empty { padding: 40px 20px; text-align: center; color: var(--hf-text-muted); font-size: 13px; }

.hf-hint-banner {
    margin: 0 16px 12px;
    padding: 10px 14px;
    border-radius: 10px;
    background: rgba(79, 109, 245, 0.1);
    border: 1px solid rgba(79, 109, 245, 0.3);
    font-size: 13px;
    color: var(--hf-text);
}
.hf-hint-banner-btn {
    background: var(--hf-accent);
    color: #fff;
    border: none;
    border-radius: 999px;
    padding: 3px 12px;
    font-size: 12px;
    font-weight: 600;
    cursor: pointer;
    margin-left: 4px;
}
.hf-hint-banner-btn:hover { background: var(--hf-accent-hover); }

/* Map markers. Both looks get their color inline from the price group, so
   the selected state must NOT change the color (it would hide the price
   group) — it enlarges and outlines instead. */
.hf-map-pin {
    position: relative;
    background: var(--hf-panel);
    border: 2px solid var(--hf-accent);
    border-radius: 999px;
    padding: 4px 10px;
    font-size: 12px;
    font-weight: 700;
    color: var(--hf-text);
    box-shadow: var(--hf-shadow);
    white-space: nowrap;
    transition: transform 0.12s ease;
}
.hf-map-pin.active { transform: scale(1.12); box-shadow: 0 0 0 3px #fff, var(--hf-shadow); }

/* Teardrop ("air drop") pin: the tip sits exactly on the coordinate. */
.hf-drop-pin {
    position: relative;
    width: 30px;
    height: 40px;
    cursor: pointer;
    transform-origin: 50% 100%;
    transition: transform 0.12s ease;
}
/* v1.32.0: the drop shadow lives on the <svg> (it used to be on the whole pin)
   so the pulse ring below is not given a dark shadow of its own. Same look. */
.hf-drop-pin svg {
    display: block;
    width: 100%;
    height: 100%;
    filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.35));
}
.hf-drop-pin.active { transform: scale(1.3); }

.hf-view-switch { display: none; }

@media (max-width: 760px) {
    .hf-header {
        flex-wrap: nowrap;
        flex-direction: column;
        align-items: stretch;
        gap: 8px;
        padding: 10px 14px;
    }
    .hf-header-row1 { display: flex; align-items: center; gap: 10px; }
    /* v1.26.0: the logo now takes all leftover row width (and is the part
       that shrinks on a very narrow phone), while the Mekkah/Madinah pill
       hugs its two buttons — before, the pill itself stretched, which left
       an empty gap on its right when the Madinah tab was active. The
       theme button keeps flex-shrink:0, so it can still never be pushed
       off-screen. */
    .hf-header-row1 .hf-logo {
        flex: 1 1 0;
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
    }
    .hf-header .hf-logo img { height: var(--hf-logo-h-mobile, 34px); }
    .hf-header-row1 .hf-tabs { flex: 0 0 auto; }
    /* ~15% smaller than desktop (padding 7px 16px / font 13px) — frees up
       room for the theme toggle next to it so it stops getting clipped. */
    .hf-header-row1 .hf-tab {
        padding: 6px 13px;
        font-size: 11px;
        white-space: nowrap;
    }

    /* Opsi B: icon + 3 dropdown dalam 1 baris; tap icon -> baris ini
       berubah total jadi input pencarian penuh + tombol ✕ (lihat JS
       initSearchToggle di frontend.js untuk toggle class body.hf-search-active). */
    .hf-searchbar-row { display: flex; align-items: center; gap: 8px; }

    .hf-search-icon-btn {
        display: flex;
        align-items: center;
        justify-content: center;
        flex-shrink: 0;
        width: 34px; height: 34px;
        border-radius: 999px;
        border: 1px solid var(--hf-border);
        background: var(--hf-panel-alt);
        color: var(--hf-text);
        font-size: 15px;
        cursor: pointer;
    }
    .hf-search-close-btn {
        display: none;
        flex-shrink: 0;
        align-items: center;
        justify-content: center;
        width: 30px; height: 30px;
        border-radius: 999px;
        border: 1px solid var(--hf-border);
        background: transparent;
        color: var(--hf-text-muted);
        font-size: 14px;
        cursor: pointer;
    }

    .hf-search-wrap { display: contents; }
    .hf-search-clear-btn, .hf-search-wrap.has-value .hf-search-clear-btn { display: none; }
    .hf-search { display: none; }

    .hf-filters {
        flex: 1;
        min-width: 0;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
        padding-bottom: 2px;
        /* Scrolling itself stays on (needed if the 3 dropdowns don't all
           fit) — this only hides the track some browsers draw for it, so
           it reads as a clean row instead of a visible scrollbar line. */
        scrollbar-width: none;
        -ms-overflow-style: none;
    }
    .hf-filters::-webkit-scrollbar { display: none; }
    .hf-filters select { flex-shrink: 0; }

    /* Active state — search icon was tapped */
    body.hf-search-active .hf-search-icon-btn { display: none; }
    body.hf-search-active .hf-filters { display: none; }
    body.hf-search-active .hf-search-close-btn { display: flex; }
    body.hf-search-active .hf-search {
        display: block;
        flex: 1;
        min-width: 0;
        width: auto;
    }

    .hf-body { position: relative; }
    .hf-sidebar, .hf-map { width: 100%; height: 100%; }
    .hf-sidebar { display: none; border-right: none; }
    body.hf-view-map .hf-sidebar { display: none; }
    body.hf-view-list .hf-sidebar { display: block; }
    body.hf-view-list .hf-map { display: none; }
    body.hf-view-map .hf-map { display: block; }

    .hf-view-switch {
        display: flex;
        position: absolute;
        bottom: 18px;
        left: 50%;
        transform: translateX(-50%);
        z-index: 500;
        background: var(--hf-panel);
        border: 1px solid var(--hf-border);
        border-radius: 999px;
        padding: 4px;
        box-shadow: var(--hf-shadow);
        gap: 2px;
    }
    .hf-view-switch button {
        border: none;
        background: transparent;
        color: var(--hf-text-muted);
        font-size: 13px;
        font-weight: 700;
        padding: 8px 18px;
        border-radius: 999px;
        cursor: pointer;
    }
    .hf-view-switch button.active { background: var(--hf-accent); color: #fff; }
}

/* ---------------------------------------------------------------------
   Map popup (hotel detail card on marker click) — mirrors MMPU's popup
   card design: photo, title, distance, badges, then link buttons.
   --------------------------------------------------------------------- */
.leaflet-popup.hf-popup-wrap .leaflet-popup-content-wrapper {
    /* --hf-popup-opacity-pct / --hf-popup-blur are set from admin settings
       (Task 3) via JS at page load; the 100%/none fallbacks here mean the
       popup still looks exactly as before if that JS never runs. */
    background: color-mix(in srgb, var(--hf-panel) var(--hf-popup-opacity-pct, 100%), transparent);
    -webkit-backdrop-filter: var(--hf-popup-blur, none);
    backdrop-filter: var(--hf-popup-blur, none);
    color: var(--hf-text);
    border-radius: var(--hf-radius, 12px);
    box-shadow: var(--hf-shadow);
    padding: 0;
    overflow: hidden;
}
.leaflet-popup.hf-popup-wrap .leaflet-popup-content { margin: 0; width: 240px !important; }
.leaflet-popup.hf-popup-wrap .leaflet-popup-tip {
    background: color-mix(in srgb, var(--hf-panel) var(--hf-popup-opacity-pct, 100%), transparent);
}
.leaflet-popup.hf-popup-wrap .leaflet-popup-close-button {
    /* Circular backdrop so the button stays visible regardless of what's
       behind it (the hotel photo, which can be any color) — a fixed
       neutral dark scrim works on any photo, rather than a theme color
       that could itself blend into some photos. */
    top: 6px;
    right: 6px;
    width: 24px;
    height: 24px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background-color: rgba(0, 0, 0, 0.45);
    color: #ffffff;
    padding: 0;
    /* v1.28.0: the "×" is no longer a text glyph (its vertical position
       depended on the font — it sat low in the circle, worse with a custom
       Google Font). Leaflet's <span>×</span> is hidden and a white SVG X is
       drawn as the button's background, so it is centred whatever the font. */
    font-size: 0;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2.6' stroke-linecap='round'%3E%3Cline x1='6' y1='6' x2='18' y2='18'/%3E%3Cline x1='18' y1='6' x2='6' y2='18'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: center;
    background-size: 12px 12px;
}
.leaflet-popup.hf-popup-wrap .leaflet-popup-close-button span { display: none; }
.leaflet-popup.hf-popup-wrap .leaflet-popup-close-button:hover { background-color: rgba(0, 0, 0, 0.65); }

.hf-popup-photo { width: 100%; height: 130px; object-fit: cover; display: block; }
.hf-popup-body { padding: 12px 14px 14px; }
.hf-popup-title { font-weight: var(--hf-heading-weight, 700); font-size: var(--hf-heading-size, 14px); margin-bottom: 4px; }
.hf-popup-distance { font-size: var(--hf-body-size, 12px); font-weight: var(--hf-body-weight, 400); color: var(--hf-text-muted); margin-bottom: 8px; display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; }
/* Distance + walking/driving tag (v1.33.19). Icons are inline SVG (currentColor), so they follow the theme. */
.hf-dist-text, .hf-travel-tag { display: inline-flex; align-items: center; gap: 4px; }
.hf-travel-tag { white-space: nowrap; }
.hf-ic { width: 15px; height: 15px; flex: 0 0 auto; }
.hf-popup-badges { display: flex; align-items: center; flex-wrap: wrap; gap: 6px 8px; margin-bottom: 10px; }

.hf-popup-links { display: flex; flex-direction: column; gap: 6px; }
.hf-popup-link-btn {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 10px;
    border-radius: 8px;
    background: var(--hf-panel-alt);
    border: 1px solid var(--hf-border);
    text-decoration: none;
    color: var(--hf-text);
    font-size: 13px;
    font-weight: 600;
}
.hf-popup-link-btn:hover { border-color: var(--hf-accent); }
.hf-popup-link-btn img { width: 20px; height: 20px; border-radius: 4px; object-fit: cover; flex-shrink: 0; }
.hf-popup-link-fallback {
    width: 20px; height: 20px; border-radius: 4px; flex-shrink: 0;
    background: var(--hf-accent); color: #fff; font-size: 11px; font-weight: 700;
    display: flex; align-items: center; justify-content: center;
}
.hf-popup-link-btn .hf-ota-generic-icon { width: 20px; height: 20px; flex-shrink: 0; color: var(--hf-text-muted); }
.hf-popup-link-btn .hf-ota-generic-icon svg { width: 100%; height: 100%; display: block; }
.hf-popup-link-btn span:nth-child(2) { flex: 1; }
.hf-popup-link-arrow { color: var(--hf-text-muted); }

/* ---- Kartu Info (v1.29.0) -------------------------------------------------
   One slot in the hotel list holding 1..N admin-written HTML cards as a
   horizontal scroll-snap carousel. Height is set from JS to match the hotel
   cards (102px is only the fallback). Colours come from the theme variables,
   so it follows light/dark mode. */
.hf-infocard {
    position: relative;
    height: 102px;
    box-sizing: border-box;
    border-bottom: 1px solid var(--hf-border);
    background: var(--hf-panel-alt);
    overflow: hidden;
}
.hf-infocard-track {
    display: flex;
    height: 100%;
    overflow-x: auto;
    overflow-y: hidden;
    scroll-snap-type: x mandatory;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
}
.hf-infocard-track::-webkit-scrollbar { display: none; }
.hf-infocard-slide {
    flex: 0 0 100%;
    box-sizing: border-box;
    height: 100%;
    overflow-y: auto;
    scroll-snap-align: start;
    scrollbar-width: thin;
    padding: 10px 16px;
}
.hf-infocard-multi .hf-infocard-slide { padding: 10px 40px 20px; }
.hf-infocard-content {
    font-size: var(--hf-body-size, 12px);
    line-height: 1.5;
    color: var(--hf-text);
    overflow-wrap: anywhere;
}
.hf-infocard-content a { color: var(--hf-accent); }
.hf-infocard-content img { max-width: 100%; height: auto; }

.hf-infocard-nav {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    z-index: 2;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    padding: 0;
    border: none;
    border-radius: 50%;
    background: rgba(0, 0, 0, 0.35);
    color: #ffffff;
    cursor: pointer;
}
.hf-infocard-nav:hover { background: rgba(0, 0, 0, 0.55); }
.hf-infocard-nav[hidden] { display: none; }
.hf-infocard-nav .hf-icon { width: 16px; height: 16px; }
.hf-infocard-prev { left: 6px; }
.hf-infocard-next { right: 6px; }

.hf-infocard-dots {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 4px;
    z-index: 2;
    display: flex;
    justify-content: center;
    gap: 6px;
    pointer-events: none;
}
.hf-infocard-dot {
    width: 6px;
    height: 6px;
    padding: 0;
    border: none;
    border-radius: 50%;
    background: var(--hf-text-muted);
    opacity: 0.45;
    cursor: pointer;
    pointer-events: auto;
}
.hf-infocard-dot.active { background: var(--hf-accent); opacity: 1; }

/* ---- Pulse ring on the active pin (v1.32.0) --------------------------------
   Enabled by body.hf-pin-pulse (Pengaturan Peta). Keyed off the pin's .active
   class, which is NOT removed when the popup closes, so the ring keeps pulsing
   until another pin is selected. Colour = the pin's price-group colour, passed
   in as --hf-pin-color. Teardrop pin: two staggered rings centred on the tip
   (the hotel's real coordinate). Price-label pin: a ring growing out from the
   pill. Both sit behind the pin (z-index -1 inside the pin's own stacking
   context, which .active creates through its scale transform). */
.hf-pin-pulse .hf-drop-pin.active::before,
.hf-pin-pulse .hf-drop-pin.active::after {
    content: "";
    position: absolute;
    left: 50%;
    bottom: 0;
    width: 26px;
    height: 26px;
    margin-left: -13px;
    margin-bottom: -13px;
    box-sizing: border-box;
    border: 2px solid var(--hf-pin-color, #4f6df5);
    border-radius: 50%;
    pointer-events: none;
    z-index: -1;
    opacity: 0;
    transform: scale(0.3);
    animation: hf-pin-pulse var(--hf-pulse-dur, 1.8s) ease-out infinite;
}
.hf-pin-pulse .hf-drop-pin.active::after { animation-delay: calc(var(--hf-pulse-dur, 1.8s) / 2); }

.hf-pin-pulse .hf-map-pin.active::before {
    content: "";
    position: absolute;
    inset: -2px;
    box-sizing: border-box;
    border: 2px solid var(--hf-pin-color, #4f6df5);
    border-radius: 999px;
    pointer-events: none;
    z-index: -1;
    opacity: 0;
    animation: hf-pill-pulse var(--hf-pulse-dur, 1.8s) ease-out infinite;
}

@keyframes hf-pin-pulse {
    0%   { transform: scale(0.3); opacity: 0.75; }
    100% { transform: scale(2.4); opacity: 0; }
}
@keyframes hf-pill-pulse {
    0%   { transform: scale(1); opacity: 0.75; }
    100% { transform: scale(1.7, 2.2); opacity: 0; }
}

/* "Reduce motion" visitors: one still ring instead of the pulse. */
@media (prefers-reduced-motion: reduce) {
    .hf-pin-pulse .hf-drop-pin.active::before,
    .hf-pin-pulse .hf-map-pin.active::before {
        animation: none;
        opacity: 0.6;
    }
    .hf-pin-pulse .hf-drop-pin.active::before { transform: scale(1.5); }
    .hf-pin-pulse .hf-drop-pin.active::after { display: none; }
}

/* ---- Pulse ring on the connector line's end point (v1.33.1) ------------------
   Same switch as the pin pulse (body.hf-pin-pulse, Pengaturan Peta). The ring is
   an HTML marker (zero-size divIcon) centred on the end dot; --hf-end-d is the
   dot's diameter, --hf-pin-color the line colour (both set from JS). Shown once
   the line has finished drawing and kept while the line stays on the map. */
.hf-end-pulse { pointer-events: none; }
/* v1.37.3: label on the line's midpoint: two stacked lines (distance, then walking/driving estimate), centred
   on the point (zero-size divIcon). Hidden while the line on screen is shorter than the label (JS adds -hidden;
   visibility keeps it measurable). */
.hf-line-label { pointer-events: none; }
.hf-line-label-hidden { visibility: hidden; }
.hf-line-label-text {
    position: absolute;
    left: 0;
    top: 0;
    transform: translate(-50%, -50%);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1px;
    white-space: nowrap;
    padding: 3px 8px;
    border-radius: 10px;
    background: rgba(255, 255, 255, 0.95);
    color: #111827;
    line-height: 1.25;
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);
}
.hf-line-label-text .hf-ic { width: 12px; height: 12px; }
.hf-ll-dist, .hf-ll-time { display: inline-flex; align-items: center; gap: 3px; }
.hf-ll-dist { font-size: 11px; font-weight: 700; }
.hf-ll-time { font-size: 10px; font-weight: 600; color: #4b5563; }
.hf-end-pulse-ring {
    position: absolute;
    left: 0;
    top: 0;
    width: var(--hf-end-d, 24px);
    height: var(--hf-end-d, 24px);
    margin: calc(var(--hf-end-d, 24px) / -2) 0 0 calc(var(--hf-end-d, 24px) / -2);
    pointer-events: none;
}
.hf-pin-pulse .hf-end-pulse-ring::before,
.hf-pin-pulse .hf-end-pulse-ring::after {
    content: "";
    position: absolute;
    inset: 0;
    box-sizing: border-box;
    border: 2px solid var(--hf-pin-color, #2563eb);
    border-radius: 50%;
    opacity: 0;
    animation: hf-end-pulse var(--hf-pulse-dur, 1.8s) ease-out infinite;
}
.hf-pin-pulse .hf-end-pulse-ring::after { animation-delay: calc(var(--hf-pulse-dur, 1.8s) / 2); }
@keyframes hf-end-pulse {
    0%   { transform: scale(1); opacity: 0.8; }
    100% { transform: scale(2.6); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
    .hf-pin-pulse .hf-end-pulse-ring::before { animation: none; opacity: 0.6; transform: scale(1.5); }
    .hf-pin-pulse .hf-end-pulse-ring::after { display: none; }
}

/* v1.33.4: first-visit guide (tour.js). Four dark panels surround the spotlighted
   control (so a tap on the dark area closes the hint while a tap on the control
   itself still works), plus a ring and a speech bubble with an arrow. */
.hf-tour { position: fixed; inset: 0; z-index: 100000; pointer-events: none; }
.hf-tour-dim { position: fixed; background: transparent; pointer-events: auto; } /* invisible click/touch catchers */
.hf-tour-ring {
    position: fixed;
    /* The border-radius is set by tour.js to match the spotlighted control. The dark
       veil is this element's huge outer shadow, so the "hole" has rounded corners
       too (v1.33.5). The 1px part is the faint, translucent light-grey hairline. */
    border-radius: 14px;
    box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.28), 0 0 0 100vmax rgba(8, 9, 12, 0.62);
    pointer-events: none;
}
.hf-tour-bubble {
    position: fixed;
    box-sizing: border-box;
    padding: 14px 16px 12px;
    border-radius: 14px;
    background: var(--hf-panel);
    color: var(--hf-text);
    border: 1px solid var(--hf-border);
    box-shadow: var(--hf-shadow);
    font-size: 14px;
    line-height: 1.45;
    pointer-events: auto;
}
.hf-tour-text { margin-bottom: 12px; }
.hf-tour-actions { display: flex; justify-content: flex-end; align-items: center; gap: 8px; }
.hf-tour-btn {
    border: none;
    border-radius: 999px;
    padding: 7px 16px;
    font-size: 13px;
    font-weight: 700;
    cursor: pointer;
    font-family: inherit;
}
.hf-tour-ok { background: var(--hf-accent); color: #fff; }
.hf-tour-ok:hover { background: var(--hf-accent-hover); }
.hf-tour-skip { background: transparent; color: var(--hf-text-muted); }
.hf-tour-skip:hover { color: var(--hf-text); }
.hf-tour-arrow {
    position: absolute;
    width: 14px; height: 14px;
    background: var(--hf-panel);
    border: 1px solid var(--hf-border);
    transform: rotate(45deg);
}
.hf-tour-below .hf-tour-arrow { top: -8px; border-right: none; border-bottom: none; }
.hf-tour-above .hf-tour-arrow { bottom: -8px; border-left: none; border-top: none; }
@media (prefers-reduced-motion: no-preference) {
    .hf-tour { opacity: 0; transition: opacity 0.25s ease; }
    .hf-tour.hf-tour-in { opacity: 1; }
}

/* v1.33.9: the search hint (e.g. "1 hotel ditemukan di Madinah") mirrored as a
   card over the map, because on a phone the list panel holding the normal banner
   is hidden while the map is showing. Phone + map view only; sits above the
   List/Peta switch so it never covers the zoom control or the basemap button. */
.hf-map-hint { display: none; }
@media (max-width: 760px) {
    body.hf-view-map .hf-map-hint.has-hint {
        display: flex;
        align-items: center;
        gap: 8px;
        position: absolute;
        left: 10px; right: 10px;
        bottom: 72px;
        z-index: 1000;
        padding: 10px 10px 10px 14px;
        border-radius: 12px;
        background: var(--hf-panel);
        color: var(--hf-text);
        border: 1px solid var(--hf-border);
        box-shadow: var(--hf-shadow);
        font-size: 13px;
    }
}
.hf-map-hint-text { flex: 1; min-width: 0; }
.hf-map-hint .hf-hint-banner-btn { flex-shrink: 0; margin-left: 0; }
.hf-map-hint-text strong { font-weight: 700; }
/* Same look as the hotel popup's close button: a 24px dark translucent circle
   with a white SVG X (centred whatever the font). */
.hf-map-hint-close {
    flex-shrink: 0;
    width: 24px; height: 24px;
    padding: 0;
    border: none;
    border-radius: 50%;
    background-color: rgba(0, 0, 0, 0.45);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2.6' stroke-linecap='round'%3E%3Cline x1='6' y1='6' x2='18' y2='18'/%3E%3Cline x1='18' y1='6' x2='6' y2='18'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: center;
    background-size: 14px 14px;
    cursor: pointer;
}
.hf-map-hint-close:hover { background-color: rgba(0, 0, 0, 0.65); }

/* ---- Hide the other pins while a popup is open (v1.33.13) -------------------
   Switched on by Pengaturan Peta ("Sembunyikan pin hotel lain saat popup
   terbuka"); frontend.js adds .hf-pin-hidden to every pin except the one whose
   popup is open. Fade out (transition) and back in (keyframes), and hidden pins
   cannot be clicked. No transition is put on visible pins: that would override
   Leaflet's own transform transition during zoom animations. */
.leaflet-marker-icon.hf-pin-hidden {
    opacity: 0;
    visibility: hidden;
    pointer-events: none !important;
    transition: opacity 0.2s ease, visibility 0s linear 0.2s;
}
.leaflet-marker-icon.hf-pin-fadein {
    animation: hf-pin-fadein 0.2s ease var(--hf-pin-delay, 0ms) 1 backwards;
}
@keyframes hf-pin-fadein { from { opacity: 0; } to { opacity: 1; } }
/* v1.33.14: the pin itself also bounces up once as it comes back. It is the
   inner element that moves (Leaflet positions the wrapper with transform). The
   pin tip stays the anchor: it rises, dips slightly past its rest position, then
   settles. Pins that were hidden are never the active one, so this does not
   collide with the .active scale. */
.leaflet-marker-icon.hf-pin-fadein .hf-marker-el {
    /* v1.33.15: --hf-pin-delay staggers the pins; "backwards" keeps a pin at its
       first keyframe (invisible / at rest) while it waits for its turn. */
    animation: hf-pin-bounce 0.45s cubic-bezier(0.3, 0.7, 0.4, 1) var(--hf-pin-delay, 0ms) 1 backwards;
}
.leaflet-marker-icon.hf-pin-fadein.hf-pin-fadeonly .hf-marker-el { animation: none; }
@keyframes hf-pin-bounce {
    0%   { transform: translateY(0); }
    40%  { transform: translateY(-10px); }
    70%  { transform: translateY(2px); }
    100% { transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
    .leaflet-marker-icon.hf-pin-hidden { transition: none; }
    .leaflet-marker-icon.hf-pin-fadein { animation: none; }
    .leaflet-marker-icon.hf-pin-fadein .hf-marker-el { animation: none; }
}
